2010-01-30 22:26:47 +00:00
|
|
|
/*
|
|
|
|
* This file is part of MPlayer.
|
|
|
|
*
|
2013-07-12 20:11:08 +00:00
|
|
|
* Original author: Benjamin Zores
|
|
|
|
*
|
2010-01-30 22:26:47 +00:00
|
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2012-02-19 13:15:41 +00:00
|
|
|
#include "talloc.h"
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/mp_msg.h"
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2007-12-02 13:13:02 +00:00
|
|
|
#include <libgen.h>
|
2007-11-16 21:32:09 +00:00
|
|
|
#include <errno.h>
|
|
|
|
|
2005-10-03 14:29:01 +00:00
|
|
|
#define FIRST_AC3_AID 128
|
|
|
|
#define FIRST_DTS_AID 136
|
|
|
|
#define FIRST_MPG_AID 0
|
|
|
|
#define FIRST_PCM_AID 160
|
|
|
|
|
2005-05-19 19:50:39 +00:00
|
|
|
#include "stream.h"
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/m_option.h"
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2006-06-11 21:29:00 +00:00
|
|
|
#include "stream_dvd.h"
|
2007-08-04 21:59:26 +00:00
|
|
|
#include "stream_dvd_common.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "demux/demux.h"
|
2006-06-11 21:29:00 +00:00
|
|
|
|
2008-07-04 15:54:45 +00:00
|
|
|
static char* dvd_device_current;
|
2005-05-19 19:50:39 +00:00
|
|
|
int dvd_angle=1;
|
|
|
|
|
2005-12-28 12:34:20 +00:00
|
|
|
#define LIBDVDREAD_VERSION(maj,min,micro) ((maj)*10000 + (min)*100 + (micro))
|
2005-05-19 19:50:39 +00:00
|
|
|
/*
|
|
|
|
* Try to autodetect the libdvd-0.9.0 library
|
|
|
|
* (0.9.0 removed the <dvdread/dvd_udf.h> header, and moved the two defines
|
|
|
|
* DVD_VIDEO_LB_LEN and MAX_UDF_FILE_NAME_LEN from it to
|
|
|
|
* <dvdread/dvd_reader.h>)
|
|
|
|
*/
|
2005-12-28 12:34:20 +00:00
|
|
|
#ifndef DVDREAD_VERSION
|
2005-05-19 19:50:39 +00:00
|
|
|
#if defined(DVD_VIDEO_LB_LEN) && defined(MAX_UDF_FILE_NAME_LEN)
|
2005-12-28 12:34:20 +00:00
|
|
|
#define DVDREAD_VERSION LIBDVDREAD_VERSION(0,9,0)
|
2005-05-19 19:50:39 +00:00
|
|
|
#else
|
2005-12-28 12:34:20 +00:00
|
|
|
#define DVDREAD_VERSION LIBDVDREAD_VERSION(0,8,0)
|
|
|
|
#endif
|
2005-05-19 19:50:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
static const dvd_priv_t stream_priv_dflts = {
|
|
|
|
.cfg_title = 1,
|
2005-05-19 19:50:39 +00:00
|
|
|
};
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
#define OPT_BASE_STRUCT dvd_priv_t
|
2005-05-19 19:50:39 +00:00
|
|
|
/// URL definition
|
2007-12-02 21:37:08 +00:00
|
|
|
static const m_option_t stream_opts_fields[] = {
|
2013-08-02 15:02:34 +00:00
|
|
|
OPT_INTRANGE("title", cfg_title, 0, 1, 99),
|
|
|
|
OPT_STRING("device", cfg_device, 0),
|
|
|
|
{0}
|
2005-05-19 19:50:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int dvd_chapter_from_cell(dvd_priv_t* dvd,int title,int cell)
|
|
|
|
{
|
|
|
|
pgc_t * cur_pgc;
|
|
|
|
ptt_info_t* ptt;
|
|
|
|
int chapter = cell;
|
|
|
|
int pgc_id,pgn;
|
|
|
|
if(title < 0 || cell < 0){
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* for most DVD's chapter == cell */
|
|
|
|
/* but there are more complecated cases... */
|
|
|
|
if(chapter >= dvd->vmg_file->tt_srpt->title[title].nr_of_ptts) {
|
|
|
|
chapter = dvd->vmg_file->tt_srpt->title[title].nr_of_ptts-1;
|
|
|
|
}
|
|
|
|
title = dvd->tt_srpt->title[title].vts_ttn-1;
|
|
|
|
ptt = dvd->vts_file->vts_ptt_srpt->title[title].ptt;
|
|
|
|
while(chapter >= 0) {
|
|
|
|
pgc_id = ptt[chapter].pgcn;
|
|
|
|
pgn = ptt[chapter].pgn;
|
|
|
|
cur_pgc = dvd->vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
|
|
|
|
if(cell >= cur_pgc->program_map[pgn-1]-1) {
|
|
|
|
return chapter;
|
|
|
|
}
|
|
|
|
--chapter;
|
|
|
|
}
|
|
|
|
/* didn't find a chapter ??? */
|
|
|
|
return chapter;
|
|
|
|
}
|
|
|
|
|
2012-02-19 13:15:41 +00:00
|
|
|
static int dvd_lang_from_aid(stream_t *stream, int id) {
|
2006-10-11 17:25:46 +00:00
|
|
|
dvd_priv_t *d;
|
2006-10-11 20:53:33 +00:00
|
|
|
int i;
|
2006-10-11 17:25:46 +00:00
|
|
|
if (!stream) return 0;
|
|
|
|
d = stream->priv;
|
|
|
|
if (!d) return 0;
|
|
|
|
for(i=0;i<d->nr_of_channels;i++) {
|
|
|
|
if(d->audio_streams[i].id==id)
|
|
|
|
return d->audio_streams[i].language;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-18 14:32:40 +00:00
|
|
|
int dvd_aid_from_lang(stream_t *stream, char **lang) {
|
2005-05-19 19:50:39 +00:00
|
|
|
dvd_priv_t *d=stream->priv;
|
|
|
|
int code,i;
|
2011-02-18 14:32:40 +00:00
|
|
|
for (int n = 0; lang[n]; n++) {
|
|
|
|
code = lang[n][1] | (lang[n][0] << 8);
|
2005-05-19 19:50:39 +00:00
|
|
|
for(i=0;i<d->nr_of_channels;i++) {
|
|
|
|
if(d->audio_streams[i].language==code) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_INFO,"Selected DVD audio channel: %d language: %c%c\n",
|
2011-02-18 14:32:40 +00:00
|
|
|
d->audio_streams[i].id, lang[n][0], lang[n][1]);
|
2005-05-19 19:50:39 +00:00
|
|
|
return d->audio_streams[i].id;
|
|
|
|
}
|
|
|
|
//printf("%X != %X (%c%c)\n",code,d->audio_streams[i].language,lang[0],lang[1]);
|
|
|
|
}
|
|
|
|
}
|
2011-02-18 14:32:40 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_WARN,"No matching DVD audio language found!\n");
|
2005-05-19 19:50:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dvd_number_of_subs(stream_t *stream) {
|
2007-06-24 08:14:10 +00:00
|
|
|
int i;
|
|
|
|
int maxid = -1;
|
2005-05-19 19:50:39 +00:00
|
|
|
dvd_priv_t *d;
|
|
|
|
if (!stream) return -1;
|
|
|
|
d = stream->priv;
|
|
|
|
if (!d) return -1;
|
2007-06-24 08:14:10 +00:00
|
|
|
for (i = 0; i < d->nr_of_subtitles; i++)
|
|
|
|
if (d->subtitles[i].id > maxid) maxid = d->subtitles[i].id;
|
|
|
|
return maxid + 1;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 13:15:41 +00:00
|
|
|
static int dvd_lang_from_sid(stream_t *stream, int id) {
|
2007-06-24 08:14:10 +00:00
|
|
|
int i;
|
2005-05-19 19:50:39 +00:00
|
|
|
dvd_priv_t *d;
|
|
|
|
if (!stream) return 0;
|
|
|
|
d = stream->priv;
|
|
|
|
if (!d) return 0;
|
2007-06-24 08:14:10 +00:00
|
|
|
for (i = 0; i < d->nr_of_subtitles; i++)
|
|
|
|
if (d->subtitles[i].id == id && d->subtitles[i].language) return d->subtitles[i].language;
|
|
|
|
return 0;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2011-02-18 14:32:40 +00:00
|
|
|
int dvd_sid_from_lang(stream_t *stream, char **lang) {
|
2005-05-19 19:50:39 +00:00
|
|
|
dvd_priv_t *d=stream->priv;
|
|
|
|
int code,i;
|
2011-02-18 14:32:40 +00:00
|
|
|
for (int n = 0; lang[n]; n++) {
|
|
|
|
code = lang[n][1] | (lang[n][0] << 8);
|
2005-05-19 19:50:39 +00:00
|
|
|
for(i=0;i<d->nr_of_subtitles;i++) {
|
|
|
|
if(d->subtitles[i].language==code) {
|
2011-02-18 14:32:40 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_INFO,"Selected DVD subtitle channel: %d language: %c%c\n", i, lang[n][0], lang[n][1]);
|
2007-06-24 08:14:10 +00:00
|
|
|
return d->subtitles[i].id;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_WARN,"No matching DVD subtitle language found!\n");
|
2005-05-19 19:50:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvd_next_cell(dvd_priv_t *d) {
|
|
|
|
int next_cell=d->cur_cell;
|
|
|
|
|
2005-05-22 22:35:44 +00:00
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG2, "dvd_next_cell: next1=0x%X \n",next_cell);
|
2005-05-19 19:50:39 +00:00
|
|
|
if( d->cur_pgc->cell_playback[ next_cell ].block_type == BLOCK_TYPE_ANGLE_BLOCK ) {
|
|
|
|
while(next_cell<d->last_cell) {
|
|
|
|
if( d->cur_pgc->cell_playback[next_cell].block_mode == BLOCK_MODE_LAST_CELL )
|
|
|
|
break;
|
|
|
|
++next_cell;
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 22:35:44 +00:00
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG2, "dvd_next_cell: next2=0x%X \n",next_cell);
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
++next_cell;
|
2009-07-06 23:26:13 +00:00
|
|
|
if(next_cell>=d->last_cell)
|
2005-05-19 19:50:39 +00:00
|
|
|
return -1; // EOF
|
|
|
|
if(d->cur_pgc->cell_playback[next_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK ) {
|
2012-12-22 09:00:00 +00:00
|
|
|
next_cell+=dvd_angle-1;
|
2009-07-06 23:26:13 +00:00
|
|
|
if(next_cell>=d->last_cell)
|
2005-05-19 19:50:39 +00:00
|
|
|
return -1; // EOF
|
|
|
|
}
|
2005-05-22 22:35:44 +00:00
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG2, "dvd_next_cell: next3=0x%X \n",next_cell);
|
2005-05-19 19:50:39 +00:00
|
|
|
return next_cell;
|
|
|
|
}
|
|
|
|
|
2010-05-29 14:15:55 +00:00
|
|
|
static int dvd_read_sector(dvd_priv_t *d, unsigned char *data)
|
|
|
|
{
|
2005-05-19 19:50:39 +00:00
|
|
|
int len;
|
|
|
|
|
|
|
|
if(d->packs_left==0) {
|
|
|
|
/**
|
|
|
|
* If we're not at the end of this cell, we can determine the next
|
|
|
|
* VOBU to display using the VOBU_SRI information section of the
|
|
|
|
* DSI. Using this value correctly follows the current angle,
|
|
|
|
* avoiding the doubled scenes in The Matrix, and makes our life
|
|
|
|
* really happy.
|
|
|
|
*
|
|
|
|
* Otherwise, we set our next address past the end of this cell to
|
|
|
|
* force the code above to go to the next cell in the program.
|
|
|
|
*/
|
|
|
|
if(d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
|
|
|
|
d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn + ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
|
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG2, "Navi new pos=0x%X \n",d->cur_pack);
|
|
|
|
} else {
|
|
|
|
// end of cell! find next cell!
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "--- END OF CELL !!! ---\n");
|
|
|
|
d->cur_pack=d->cell_last_pack+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
read_next:
|
|
|
|
if(d->cur_pack>d->cell_last_pack) {
|
|
|
|
// end of cell!
|
|
|
|
int next=dvd_next_cell(d);
|
|
|
|
if(next>=0) {
|
|
|
|
d->cur_cell=next;
|
2009-07-06 23:26:13 +00:00
|
|
|
// if( d->cur_pgc->cell_playback[d->cur_cell].block_type
|
2012-12-22 09:00:00 +00:00
|
|
|
// == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle-1;
|
2005-05-19 19:50:39 +00:00
|
|
|
d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
|
|
|
|
d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "DVD next cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack);
|
2009-07-06 23:26:13 +00:00
|
|
|
} else
|
2005-05-19 19:50:39 +00:00
|
|
|
return -1; // EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
len = DVDReadBlocks(d->title, d->cur_pack, 1, data);
|
2008-12-31 14:21:50 +00:00
|
|
|
// only == 0 should indicate an error, but some dvdread version are buggy when used with dvdcss
|
|
|
|
if(len <= 0) return -1; //error
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF &&
|
|
|
|
data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF) {
|
|
|
|
// found a Navi packet!!!
|
2005-12-28 12:34:20 +00:00
|
|
|
#if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0)
|
2005-05-19 19:50:39 +00:00
|
|
|
navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]));
|
|
|
|
#else
|
|
|
|
navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t));
|
|
|
|
#endif
|
|
|
|
if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ) {
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "Invalid NAVI packet! lba=0x%X navi=0x%X \n",
|
|
|
|
d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn);
|
|
|
|
} else {
|
|
|
|
// process!
|
|
|
|
d->packs_left = d->dsi_pack.dsi_gi.vobu_ea;
|
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG2, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left);
|
|
|
|
//navPrint_DSI(&d->dsi_pack);
|
|
|
|
mp_msg(MSGT_DVD,MSGL_DBG3,"\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d->cur_cell,
|
|
|
|
d->dsi_pack.dsi_gi.vobu_c_idn,d->dsi_pack.dsi_gi.vobu_vob_idn,
|
|
|
|
d->cur_pgc->cell_position[d->cur_cell].cell_nr,
|
|
|
|
d->cur_pgc->cell_position[d->cur_cell].vob_id_nr);
|
|
|
|
|
|
|
|
if(d->angle_seek) {
|
|
|
|
int i,skip=0;
|
|
|
|
for(i=0;i<9;i++) // check if all values zero:
|
|
|
|
if((skip=d->dsi_pack.sml_agli.data[i].address)!=0) break;
|
2008-01-05 10:48:17 +00:00
|
|
|
if(skip && skip!=0x7fffffff) {
|
2005-05-19 19:50:39 +00:00
|
|
|
// sml_agli table has valid data (at least one non-zero):
|
|
|
|
d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+
|
2012-12-22 09:00:00 +00:00
|
|
|
d->dsi_pack.sml_agli.data[dvd_angle-1].address;
|
2005-05-19 19:50:39 +00:00
|
|
|
d->angle_seek=0;
|
2008-01-05 10:48:17 +00:00
|
|
|
d->cur_pack--;
|
2005-05-19 19:50:39 +00:00
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d->cur_pack);
|
|
|
|
} else {
|
|
|
|
// check if we're in the right cell, jump otherwise:
|
|
|
|
if( (d->dsi_pack.dsi_gi.vobu_c_idn==d->cur_pgc->cell_position[d->cur_cell].cell_nr) &&
|
|
|
|
(d->dsi_pack.dsi_gi.vobu_vob_idn==d->cur_pgc->cell_position[d->cur_cell].vob_id_nr) ){
|
|
|
|
d->angle_seek=0;
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "Angle-seek synced by cell/vob IDN search! \n");
|
|
|
|
} else {
|
|
|
|
// wrong angle, skip this vobu:
|
|
|
|
d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+
|
|
|
|
d->dsi_pack.dsi_gi.vobu_ea;
|
|
|
|
d->angle_seek=2; // DEBUG
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++d->cur_pack;
|
|
|
|
goto read_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
++d->cur_pack;
|
|
|
|
if(d->packs_left>=0) --d->packs_left;
|
|
|
|
|
|
|
|
if(d->angle_seek) {
|
|
|
|
if(d->angle_seek==2) mp_msg(MSGT_DVD,MSGL_V, "!!! warning! reading packet while angle_seek !!!\n");
|
|
|
|
goto read_next; // searching for Navi packet
|
|
|
|
}
|
|
|
|
|
|
|
|
return d->cur_pack-1;
|
|
|
|
}
|
|
|
|
|
2010-05-29 14:15:55 +00:00
|
|
|
static void dvd_seek(dvd_priv_t *d, int pos)
|
|
|
|
{
|
2005-05-19 19:50:39 +00:00
|
|
|
d->packs_left=-1;
|
|
|
|
d->cur_pack=pos;
|
|
|
|
|
|
|
|
// check if we stay in current cell (speedup things, and avoid angle skip)
|
|
|
|
if(d->cur_pack>d->cell_last_pack ||
|
|
|
|
d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector) {
|
|
|
|
|
|
|
|
// ok, cell change, find the right cell!
|
2010-08-01 22:53:09 +00:00
|
|
|
cell_playback_t *cell;
|
|
|
|
for(d->cur_cell=0; d->cur_cell < d->cur_pgc->nr_of_cells; d->cur_cell++) {
|
|
|
|
cell = &(d->cur_pgc->cell_playback[d->cur_cell]);
|
|
|
|
if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
|
|
|
|
continue;
|
|
|
|
d->cell_last_pack=cell->last_sector;
|
|
|
|
if(d->cur_pack<cell->first_sector) {
|
|
|
|
d->cur_pack=cell->first_sector;
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(d->cur_pack<=d->cell_last_pack) break; // ok, we find it! :)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n",
|
|
|
|
d->cur_pack,d->cur_cell,d->cur_pgc->cell_playback[ d->cur_cell ].first_sector,d->cell_last_pack);
|
|
|
|
|
|
|
|
// if we're in interleaved multi-angle cell, find the right angle chain!
|
|
|
|
// (read Navi block, and use the seamless angle jump table)
|
|
|
|
d->angle_seek=1;
|
|
|
|
}
|
|
|
|
|
2010-05-29 14:15:55 +00:00
|
|
|
static void dvd_close(dvd_priv_t *d)
|
|
|
|
{
|
2005-05-19 19:50:39 +00:00
|
|
|
ifoClose(d->vts_file);
|
|
|
|
ifoClose(d->vmg_file);
|
|
|
|
DVDCloseFile(d->title);
|
|
|
|
DVDClose(d->dvd);
|
2008-07-04 15:54:45 +00:00
|
|
|
dvd_set_speed(dvd_device_current, -1); /* -1 => restore default */
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2010-11-06 19:35:34 +00:00
|
|
|
static int fill_buffer(stream_t *s, char *buf, int len)
|
2005-05-19 19:50:39 +00:00
|
|
|
{
|
2012-11-18 19:46:12 +00:00
|
|
|
int64_t pos;
|
2010-11-06 19:35:34 +00:00
|
|
|
if (len < 2048)
|
|
|
|
return -1;
|
|
|
|
pos = dvd_read_sector(s->priv, buf);
|
2010-11-06 19:30:48 +00:00
|
|
|
if (pos < 0)
|
|
|
|
return -1;
|
2013-08-22 16:23:33 +00:00
|
|
|
// dvd_read_sector() sometimes internally skips disk-level blocks
|
2010-11-06 19:30:48 +00:00
|
|
|
s->pos = 2048*(pos - 1);
|
|
|
|
return 2048; // full sector
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2012-11-18 19:46:12 +00:00
|
|
|
static int seek(stream_t *s, int64_t newpos) {
|
2013-08-22 16:23:33 +00:00
|
|
|
dvd_seek(s->priv,newpos/2048);
|
2005-05-19 19:50:39 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stream_dvd_close(stream_t *s) {
|
|
|
|
dvd_close(s->priv);
|
|
|
|
}
|
|
|
|
|
2006-04-14 13:03:05 +00:00
|
|
|
static int mp_get_titleset_length(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
|
2006-02-27 21:11:28 +00:00
|
|
|
{
|
|
|
|
int vts_ttn; ///< title number within video title set
|
|
|
|
int pgc_no; ///< program chain number
|
|
|
|
int msec; ///< time length in milliseconds
|
|
|
|
|
2006-03-11 23:29:02 +00:00
|
|
|
msec=0;
|
2006-02-27 21:11:28 +00:00
|
|
|
if(!vts_file || !tt_srpt)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(vts_file->vtsi_mat && vts_file->vts_pgcit)
|
|
|
|
{
|
|
|
|
vts_ttn = tt_srpt->title[title_no].vts_ttn - 1;
|
|
|
|
pgc_no = vts_file->vts_ptt_srpt->title[vts_ttn].ptt[0].pgcn - 1;
|
2007-08-04 21:59:26 +00:00
|
|
|
msec = mp_dvdtimetomsec(&vts_file->vts_pgcit->pgci_srp[pgc_no].pgc->playback_time);
|
2006-02-27 21:11:28 +00:00
|
|
|
}
|
|
|
|
return msec;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mp_describe_titleset(dvd_reader_t *dvd, tt_srpt_t *tt_srpt, int vts_no)
|
|
|
|
{
|
|
|
|
ifo_handle_t *vts_file;
|
|
|
|
int title_no, msec=0;
|
|
|
|
|
|
|
|
vts_file = ifoOpen(dvd, vts_no);
|
|
|
|
if(!vts_file)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(!vts_file->vtsi_mat || !vts_file->vts_pgcit)
|
2006-04-12 11:19:49 +00:00
|
|
|
{
|
|
|
|
ifoClose(vts_file);
|
2006-02-27 21:11:28 +00:00
|
|
|
return 0;
|
2006-04-12 11:19:49 +00:00
|
|
|
}
|
2006-02-27 21:11:28 +00:00
|
|
|
|
|
|
|
for(title_no = 0; title_no < tt_srpt->nr_of_srpts; title_no++)
|
|
|
|
{
|
2006-04-10 16:16:33 +00:00
|
|
|
if (tt_srpt->title[title_no].title_set_nr != vts_no)
|
|
|
|
continue;
|
2006-04-14 13:03:05 +00:00
|
|
|
msec = mp_get_titleset_length(vts_file, tt_srpt, title_no);
|
2006-04-24 07:20:34 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_TITLE_%d_LENGTH=%d.%03d\n", title_no + 1, msec / 1000, msec % 1000);
|
2006-02-27 21:11:28 +00:00
|
|
|
}
|
|
|
|
ifoClose(vts_file);
|
2006-04-12 11:19:49 +00:00
|
|
|
return 1;
|
2006-02-27 21:11:28 +00:00
|
|
|
}
|
|
|
|
|
2010-08-01 22:48:01 +00:00
|
|
|
static int get_num_chapter(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
|
|
|
|
{
|
|
|
|
if(!vts_file || !tt_srpt)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(title_no < 0 || title_no >= tt_srpt->nr_of_srpts)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// map global title to vts title
|
|
|
|
title_no = tt_srpt->title[title_no].vts_ttn - 1;
|
|
|
|
|
|
|
|
if(title_no < 0 || title_no >= vts_file->vts_ptt_srpt->nr_of_srpts)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts;
|
|
|
|
}
|
|
|
|
|
2006-08-18 19:05:37 +00:00
|
|
|
static int seek_to_chapter(stream_t *stream, ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no, int chapter)
|
|
|
|
{
|
2010-08-01 22:51:15 +00:00
|
|
|
dvd_priv_t *d = stream->priv;
|
2006-08-18 19:05:37 +00:00
|
|
|
ptt_info_t ptt;
|
|
|
|
pgc_t *pgc;
|
2012-11-18 19:46:12 +00:00
|
|
|
int64_t pos;
|
2006-08-18 19:05:37 +00:00
|
|
|
|
|
|
|
if(!vts_file || !tt_srpt)
|
|
|
|
return 0;
|
|
|
|
|
2009-01-01 11:46:34 +00:00
|
|
|
if(title_no < 0 || title_no >= tt_srpt->nr_of_srpts)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// map global title to vts title
|
|
|
|
title_no = tt_srpt->title[title_no].vts_ttn - 1;
|
|
|
|
|
|
|
|
if(title_no < 0 || title_no >= vts_file->vts_ptt_srpt->nr_of_srpts)
|
|
|
|
return 0;
|
|
|
|
|
2006-08-19 10:45:17 +00:00
|
|
|
if(chapter < 0 || chapter > vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts-1) //no such chapter
|
2006-08-18 19:05:37 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
ptt = vts_file->vts_ptt_srpt->title[title_no].ptt[chapter];
|
|
|
|
pgc = vts_file->vts_pgcit->pgci_srp[ptt.pgcn-1].pgc;
|
|
|
|
|
2010-08-01 22:51:15 +00:00
|
|
|
d->cur_cell = pgc->program_map[ptt.pgn - 1] - 1;
|
|
|
|
if(pgc->cell_playback[d->cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK)
|
2012-12-22 09:00:00 +00:00
|
|
|
d->cur_cell += dvd_angle-1;
|
2010-08-01 22:51:15 +00:00
|
|
|
d->cur_pack = pgc->cell_playback[d->cur_cell].first_sector;
|
|
|
|
d->cell_last_pack = pgc->cell_playback[d->cur_cell].last_sector;
|
|
|
|
|
|
|
|
d->packs_left = -1;
|
|
|
|
d->angle_seek = 0;
|
|
|
|
|
2012-11-18 19:46:12 +00:00
|
|
|
pos = (int64_t) d->cur_pack * 2048;
|
2012-12-07 15:44:53 +00:00
|
|
|
stream_seek(stream, pos);
|
|
|
|
|
2006-08-18 19:05:37 +00:00
|
|
|
mp_msg(MSGT_OPEN,MSGL_V,"\r\nSTREAM_DVD, seeked to chapter: %d, cell: %u, pos: %"PRIu64"\n",
|
2010-08-01 22:51:15 +00:00
|
|
|
chapter, d->cur_pack, pos);
|
2006-08-18 19:05:37 +00:00
|
|
|
|
|
|
|
return chapter;
|
|
|
|
}
|
|
|
|
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
// p: in=chapter number, out=PTS
|
|
|
|
static int get_chapter_time(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no, double *p)
|
2006-11-10 00:36:39 +00:00
|
|
|
{
|
2010-08-01 22:48:01 +00:00
|
|
|
unsigned int i, cell, last_cell;
|
2010-10-27 14:57:52 +00:00
|
|
|
unsigned int t=0;
|
2010-08-01 22:48:01 +00:00
|
|
|
ptt_info_t *ptt;
|
|
|
|
pgc_t *pgc;
|
2006-11-10 00:36:39 +00:00
|
|
|
|
2010-08-01 22:48:01 +00:00
|
|
|
title_no = tt_srpt->title[title_no].vts_ttn - 1;
|
|
|
|
if(vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts < 2)
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
return 0;
|
2010-08-01 22:48:01 +00:00
|
|
|
ptt = vts_file->vts_ptt_srpt->title[title_no].ptt;
|
2006-11-10 00:36:39 +00:00
|
|
|
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
int cur = 0;
|
2010-08-01 22:48:01 +00:00
|
|
|
for(i=0; i<vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts; i++)
|
2006-11-10 00:36:39 +00:00
|
|
|
{
|
2010-08-01 22:48:01 +00:00
|
|
|
pgc = vts_file->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
|
|
|
|
cell = pgc->program_map[ptt[i].pgn-1]; //here the cell is 1-based
|
|
|
|
if(ptt[i].pgn<pgc->nr_of_programs)
|
|
|
|
last_cell = pgc->program_map[ptt[i].pgn];
|
|
|
|
else
|
|
|
|
last_cell = 0;
|
2013-09-14 18:15:23 +00:00
|
|
|
while (cell < last_cell) {
|
2006-11-10 00:36:39 +00:00
|
|
|
if(!(pgc->cell_playback[cell-1].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
|
|
|
|
pgc->cell_playback[cell-1].block_mode != BLOCK_MODE_FIRST_CELL)
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
) {
|
|
|
|
if (cur == *p) {
|
|
|
|
*p = t / 1000.0;
|
|
|
|
return 1;
|
|
|
|
}
|
2007-08-04 21:59:26 +00:00
|
|
|
t += mp_dvdtimetomsec(&pgc->cell_playback[cell-1].playback_time);
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
cur++;
|
|
|
|
}
|
2006-11-10 00:36:39 +00:00
|
|
|
cell++;
|
2013-09-14 18:15:23 +00:00
|
|
|
}
|
2006-11-10 00:36:39 +00:00
|
|
|
}
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void list_chapters(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
|
|
|
|
{
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "CHAPTERS: ");
|
|
|
|
for (int n = 0; ; n++) {
|
|
|
|
double p = n;
|
|
|
|
int r;
|
|
|
|
r = get_chapter_time(vts_file, tt_srpt, title_no, &p);
|
|
|
|
if (!r)
|
|
|
|
break;
|
|
|
|
int t = p * 1000;
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02d:%02d:%02d.%03d,", t/3600000, (t/60000)%60, (t/1000)%60, t%1000);
|
|
|
|
}
|
2006-11-10 00:36:39 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "\n");
|
|
|
|
}
|
|
|
|
|
2006-11-07 22:49:56 +00:00
|
|
|
static double dvd_get_current_time(stream_t *stream, int cell)
|
|
|
|
{
|
|
|
|
int i, tm;
|
|
|
|
dvd_priv_t *d = stream->priv;
|
|
|
|
|
|
|
|
tm=0;
|
2011-06-12 11:10:13 +00:00
|
|
|
if(cell < 0) cell=d->cur_cell;
|
|
|
|
for(i=0; i<cell; i++) {
|
2006-11-07 22:49:56 +00:00
|
|
|
if(d->cur_pgc->cell_playback[i].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
|
|
|
|
d->cur_pgc->cell_playback[i].block_mode != BLOCK_MODE_FIRST_CELL
|
|
|
|
)
|
|
|
|
continue;
|
|
|
|
tm += d->cell_times_table[i];
|
|
|
|
}
|
2007-08-04 21:59:26 +00:00
|
|
|
tm += mp_dvdtimetomsec(&d->dsi_pack.dsi_gi.c_eltm);
|
2006-11-07 22:49:56 +00:00
|
|
|
|
|
|
|
return (double)tm/1000.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvd_seek_to_time(stream_t *stream, ifo_handle_t *vts_file, double sec)
|
|
|
|
{
|
|
|
|
unsigned int i, j, k, timeunit, ac_time, tmap_sector=0, cell_sector=0, vobu_sector=0;
|
2007-05-28 08:42:35 +00:00
|
|
|
int t=0;
|
2006-11-07 22:49:56 +00:00
|
|
|
double tm, duration;
|
2012-11-18 19:46:12 +00:00
|
|
|
int64_t pos = -1;
|
2006-11-07 22:49:56 +00:00
|
|
|
dvd_priv_t *d = stream->priv;
|
|
|
|
vts_tmapt_t *vts_tmapt = vts_file->vts_tmapt;
|
|
|
|
|
|
|
|
if(!vts_file->vts_tmapt || sec < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
duration = (double) mp_get_titleset_length(d->vts_file, d->tt_srpt, d->cur_title-1) / 1000.0f;
|
|
|
|
if(sec > duration)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
i=d->cur_pgc_idx;
|
|
|
|
timeunit = vts_tmapt->tmap[i].tmu;
|
|
|
|
for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
|
|
|
|
ac_time = timeunit * (j + 1);
|
|
|
|
if(ac_time >= sec)
|
|
|
|
break;
|
|
|
|
tmap_sector = vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff;
|
|
|
|
}
|
|
|
|
//search enclosing cell
|
|
|
|
for(i=0; i<d->cur_pgc->nr_of_cells; i++) {
|
|
|
|
if(tmap_sector >= d->cur_pgc->cell_playback[i].first_sector && tmap_sector <= d->cur_pgc->cell_playback[i].last_sector) {
|
|
|
|
cell_sector = d->cur_pgc->cell_playback[i].first_sector;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-18 19:46:12 +00:00
|
|
|
pos = ((int64_t)cell_sector)<<11;
|
2006-11-07 22:49:56 +00:00
|
|
|
stream_seek(stream, pos);
|
|
|
|
do {
|
|
|
|
stream_skip(stream, 2048);
|
2007-08-04 21:59:26 +00:00
|
|
|
t = mp_dvdtimetomsec(&d->dsi_pack.dsi_gi.c_eltm);
|
2006-11-07 22:49:56 +00:00
|
|
|
} while(!t);
|
2011-06-12 11:10:13 +00:00
|
|
|
tm = dvd_get_current_time(stream, -1);
|
2006-11-07 22:49:56 +00:00
|
|
|
|
2012-11-18 19:46:12 +00:00
|
|
|
pos = ((int64_t)tmap_sector)<<11;
|
2006-11-07 22:49:56 +00:00
|
|
|
stream_seek(stream, pos);
|
|
|
|
//now get current time in terms of the cell+cell time offset
|
|
|
|
memset(&d->dsi_pack.dsi_gi.c_eltm, 0, sizeof(dvd_time_t));
|
|
|
|
while(tm <= sec) {
|
|
|
|
if(!stream_skip(stream, 2048))
|
|
|
|
break;
|
2011-06-12 11:10:13 +00:00
|
|
|
tm = dvd_get_current_time(stream, -1);
|
2006-11-07 22:49:56 +00:00
|
|
|
};
|
|
|
|
tmap_sector = stream->pos >> 11;
|
|
|
|
|
|
|
|
//search closest VOBU sector
|
|
|
|
k=(vts_file->vts_vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4; //entries in the vobu admap
|
|
|
|
for(i=1; i<k; i++) {
|
|
|
|
if(vts_file->vts_vobu_admap->vobu_start_sectors[i] > tmap_sector)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vobu_sector = vts_file->vts_vobu_admap->vobu_start_sectors[i-1];
|
2012-11-18 19:46:12 +00:00
|
|
|
pos = ((int64_t)vobu_sector) << 11;
|
2006-11-07 22:49:56 +00:00
|
|
|
stream_seek(stream, pos);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-07-06 23:26:13 +00:00
|
|
|
static int control(stream_t *stream,int cmd,void* arg)
|
2006-02-27 21:11:28 +00:00
|
|
|
{
|
2006-08-18 19:05:37 +00:00
|
|
|
dvd_priv_t *d = stream->priv;
|
2009-07-06 23:26:13 +00:00
|
|
|
switch(cmd)
|
2006-02-27 21:11:28 +00:00
|
|
|
{
|
|
|
|
case STREAM_CTRL_GET_TIME_LENGTH:
|
|
|
|
{
|
2006-11-12 17:19:56 +00:00
|
|
|
*((double *)arg) = (double) mp_get_titleset_length(d->vts_file, d->tt_srpt, d->cur_title-1)/1000.0;
|
2006-02-27 21:11:28 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2013-05-03 18:11:46 +00:00
|
|
|
case STREAM_CTRL_GET_START_TIME:
|
|
|
|
{
|
|
|
|
*((double *)arg) = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2011-12-31 12:20:08 +00:00
|
|
|
case STREAM_CTRL_GET_NUM_TITLES:
|
|
|
|
{
|
|
|
|
*((unsigned int *)arg) = d->vmg_file->tt_srpt->nr_of_srpts;
|
|
|
|
return 1;
|
|
|
|
}
|
2006-08-21 19:13:36 +00:00
|
|
|
case STREAM_CTRL_GET_NUM_CHAPTERS:
|
|
|
|
{
|
2010-08-01 22:48:01 +00:00
|
|
|
int r;
|
|
|
|
r = get_num_chapter(d->vts_file, d->tt_srpt, d->cur_title-1);
|
|
|
|
if(! r) return STREAM_UNSUPPORTED;
|
|
|
|
*((unsigned int *)arg) = r;
|
2006-08-21 19:13:36 +00:00
|
|
|
return 1;
|
|
|
|
}
|
stream: report chapter times, use time seeks for DVD chapters
Allow the stream layer to report chapter times. Extend stream_dvd to do
this. I'm not 100% sure whether the re-used code is bug-free (because it
was used for slave-mode and/or debugging only).
MAke the frontend do time-based seeks when switching DVD chapters. I'm
not sure if there's a real reason STREAM_CTRL_SEEK_TO_CHAPTER exists
(maybe/hopefully not), but we will see.
Note that querying chapter times in demuxer_chapter_time() with the new
STREAM_CTRL_GET_CHAPTER_TIME could be excessively slow, especially with
the cache enabled. The frontend likes to query chapter times very often.
Additionally, stream_dvd uses some sort of quadratic algorithm to list
times for all chapters. For this reason, we try to query all chapters on
start (after the demuxer is opened), and add the chapters to the demuxer
chapter list. demuxer_chapter_time() will get the time from that list,
instead of asking the stream layer over and over again.
This assumes stream_dvd knows the list of chapters at the start, and
also that the list of chapters never changes during playback. This
seems to be true, and the only exception, switching DVD titles, is not
supported at runtime (and doesn't need to be supported).
2013-05-03 23:20:39 +00:00
|
|
|
case STREAM_CTRL_GET_CHAPTER_TIME:
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
r = get_chapter_time(d->vts_file, d->tt_srpt, d->cur_title-1, (double *)arg);
|
|
|
|
if(! r) return STREAM_UNSUPPORTED;
|
|
|
|
return 1;
|
|
|
|
}
|
2006-08-18 19:05:37 +00:00
|
|
|
case STREAM_CTRL_SEEK_TO_CHAPTER:
|
|
|
|
{
|
2006-11-11 13:02:39 +00:00
|
|
|
int r;
|
|
|
|
r = seek_to_chapter(stream, d->vts_file, d->tt_srpt, d->cur_title-1, *((unsigned int *)arg));
|
2007-08-28 22:38:45 +00:00
|
|
|
if(! r) return STREAM_UNSUPPORTED;
|
2006-08-18 19:05:37 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2012-10-21 14:56:50 +00:00
|
|
|
case STREAM_CTRL_GET_CURRENT_TITLE:
|
|
|
|
{
|
|
|
|
*((unsigned int *)arg) = d->cur_title - 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2006-08-18 19:05:37 +00:00
|
|
|
case STREAM_CTRL_GET_CURRENT_CHAPTER:
|
|
|
|
{
|
|
|
|
*((unsigned int *)arg) = dvd_chapter_from_cell(d, d->cur_title-1, d->cur_cell);
|
|
|
|
return 1;
|
|
|
|
}
|
2006-11-07 22:49:56 +00:00
|
|
|
case STREAM_CTRL_GET_CURRENT_TIME:
|
|
|
|
{
|
2006-11-11 13:02:39 +00:00
|
|
|
double tm;
|
2011-06-12 11:10:13 +00:00
|
|
|
tm = dvd_get_current_time(stream, -1);
|
2006-11-07 22:49:56 +00:00
|
|
|
if(tm != -1) {
|
2006-11-12 17:19:56 +00:00
|
|
|
*((double *)arg) = tm;
|
2006-11-07 22:49:56 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case STREAM_CTRL_SEEK_TO_TIME:
|
|
|
|
{
|
|
|
|
if(dvd_seek_to_time(stream, d->vts_file, *((double*)arg)))
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
}
|
2007-09-01 15:51:41 +00:00
|
|
|
case STREAM_CTRL_GET_ASPECT_RATIO:
|
|
|
|
{
|
|
|
|
*((double *)arg) = !d->vts_file->vtsi_mat->vts_video_attr.display_aspect_ratio ? 4.0/3.0 : 16.0/9.0;
|
|
|
|
return 1;
|
|
|
|
}
|
2008-01-05 14:22:05 +00:00
|
|
|
case STREAM_CTRL_GET_NUM_ANGLES:
|
|
|
|
{
|
|
|
|
*((int *)arg) = d->vmg_file->tt_srpt->title[dvd_title].nr_of_angles;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case STREAM_CTRL_GET_ANGLE:
|
|
|
|
{
|
2012-12-22 09:00:00 +00:00
|
|
|
*((int *)arg) = dvd_angle;
|
2008-01-05 14:22:05 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case STREAM_CTRL_SET_ANGLE:
|
|
|
|
{
|
|
|
|
int ang = *((int *)arg);
|
|
|
|
if(ang>d->vmg_file->tt_srpt->title[dvd_title].nr_of_angles || ang<=0)
|
|
|
|
break;
|
2012-12-22 09:00:00 +00:00
|
|
|
dvd_angle = ang;
|
2008-01-05 14:22:05 +00:00
|
|
|
d->angle_seek = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2012-02-19 13:15:41 +00:00
|
|
|
case STREAM_CTRL_GET_LANG:
|
|
|
|
{
|
|
|
|
struct stream_lang_req *req = arg;
|
|
|
|
int lang = 0;
|
|
|
|
switch(req->type) {
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
case STREAM_AUDIO:
|
2012-02-19 13:15:41 +00:00
|
|
|
lang = dvd_lang_from_aid(stream, req->id);
|
|
|
|
break;
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
case STREAM_SUB:
|
2012-02-19 13:15:41 +00:00
|
|
|
lang = dvd_lang_from_sid(stream, req->id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!lang)
|
|
|
|
break;
|
2012-11-18 23:22:43 +00:00
|
|
|
snprintf(req->name, sizeof(req->name), "%c%c", lang >> 8, lang);
|
2012-02-19 13:15:41 +00:00
|
|
|
return STREAM_OK;
|
|
|
|
}
|
2013-05-03 17:52:28 +00:00
|
|
|
case STREAM_CTRL_MANAGES_TIMELINE:
|
|
|
|
return STREAM_OK;
|
2013-06-04 23:59:04 +00:00
|
|
|
case STREAM_CTRL_GET_DVD_INFO:
|
|
|
|
{
|
|
|
|
struct stream_dvd_info_req *req = arg;
|
|
|
|
memset(req, 0, sizeof(*req));
|
|
|
|
req->num_subs = dvd_number_of_subs(stream);
|
|
|
|
memcpy(req->palette, d->cur_pgc->palette, sizeof(req->palette));
|
|
|
|
return STREAM_OK;
|
|
|
|
}
|
2006-02-27 21:11:28 +00:00
|
|
|
}
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2006-02-27 21:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
static int open_s(stream_t *stream, int mode)
|
2013-07-11 19:10:42 +00:00
|
|
|
{
|
2006-11-07 22:49:56 +00:00
|
|
|
int k;
|
2013-08-02 15:02:34 +00:00
|
|
|
dvd_priv_t *d = stream->priv;
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2007-11-30 19:54:30 +00:00
|
|
|
mp_msg(MSGT_OPEN,MSGL_V,"URL: %s\n", stream->url);
|
2013-08-02 15:02:34 +00:00
|
|
|
dvd_title = d->cfg_title;
|
2005-05-19 19:50:39 +00:00
|
|
|
if(1){
|
|
|
|
//int ret,ret2;
|
|
|
|
int ttn,pgc_id,pgn;
|
|
|
|
dvd_reader_t *dvd;
|
|
|
|
dvd_file_t *title;
|
|
|
|
ifo_handle_t *vmg_file;
|
|
|
|
tt_srpt_t *tt_srpt;
|
|
|
|
ifo_handle_t *vts_file;
|
2007-02-15 15:54:06 +00:00
|
|
|
pgc_t *pgc;
|
2005-05-19 19:50:39 +00:00
|
|
|
/**
|
|
|
|
* Open the disc.
|
|
|
|
*/
|
2013-08-02 15:02:34 +00:00
|
|
|
if(d->cfg_device)
|
|
|
|
dvd_device_current = d->cfg_device;
|
2008-07-04 15:54:45 +00:00
|
|
|
else if(dvd_device)
|
|
|
|
dvd_device_current = dvd_device;
|
|
|
|
else
|
2008-07-06 18:04:14 +00:00
|
|
|
dvd_device_current = DEFAULT_DVD_DEVICE;
|
2008-07-04 15:54:45 +00:00
|
|
|
dvd_set_speed(dvd_device_current, dvd_speed);
|
2007-12-11 23:52:55 +00:00
|
|
|
#if defined(__APPLE__) || defined(__DARWIN__)
|
2005-05-19 19:50:39 +00:00
|
|
|
/* Dynamic DVD drive selection on Darwin */
|
2008-07-04 15:54:45 +00:00
|
|
|
if(!strcmp(dvd_device_current, "/dev/rdiskN")) {
|
2005-05-19 19:50:39 +00:00
|
|
|
int i;
|
2008-07-04 15:54:45 +00:00
|
|
|
size_t len = strlen(dvd_device_current)+1;
|
2007-03-03 13:16:31 +00:00
|
|
|
char *temp_device = malloc(len);
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
for (i = 1; i < 10; i++) {
|
2007-03-03 13:16:31 +00:00
|
|
|
snprintf(temp_device, len, "/dev/rdisk%d", i);
|
2005-05-19 19:50:39 +00:00
|
|
|
dvd = DVDOpen(temp_device);
|
|
|
|
if(!dvd) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR,"Couldn't open DVD device: %s (%s)\n",temp_device, strerror(errno));
|
2005-05-19 19:50:39 +00:00
|
|
|
} else {
|
2006-01-27 03:07:13 +00:00
|
|
|
#if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4)
|
2006-12-23 23:13:11 +00:00
|
|
|
dvd_file_t *dvdfile = DVDOpenFile(dvd,dvd_title,DVD_READ_INFO_FILE);
|
|
|
|
if(!dvdfile) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR,"Couldn't open DVD device: %s (%s)\n",temp_device, strerror(errno));
|
2006-01-27 03:07:13 +00:00
|
|
|
DVDClose(dvd);
|
2006-12-23 23:13:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
DVDCloseFile(dvdfile);
|
2006-01-27 03:07:13 +00:00
|
|
|
#endif
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-12-23 23:13:11 +00:00
|
|
|
free(temp_device);
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
if(!dvd) {
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
} else
|
2007-12-11 23:52:55 +00:00
|
|
|
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
2005-05-19 19:50:39 +00:00
|
|
|
{
|
2008-07-04 15:54:45 +00:00
|
|
|
dvd = DVDOpen(dvd_device_current);
|
2005-05-19 19:50:39 +00:00
|
|
|
if(!dvd) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR,"Couldn't open DVD device: %s (%s)\n",dvd_device_current, strerror(errno));
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-19 14:52:50 +00:00
|
|
|
mp_msg(MSGT_OPEN,MSGL_V,"Reading disc structure, please wait...\n");
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the video manager to find out the information about the titles on
|
|
|
|
* this disc.
|
|
|
|
*/
|
|
|
|
vmg_file = ifoOpen(dvd, 0);
|
|
|
|
if(!vmg_file) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR, "Can't open VMG info!\n");
|
2005-05-19 19:50:39 +00:00
|
|
|
DVDClose( dvd );
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
tt_srpt = vmg_file->tt_srpt;
|
2006-04-24 07:20:34 +00:00
|
|
|
if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
|
2005-09-22 07:40:25 +00:00
|
|
|
{
|
|
|
|
int title_no; ///< title number
|
2006-04-24 07:20:34 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLES=%d\n", tt_srpt->nr_of_srpts);
|
2005-09-22 07:40:25 +00:00
|
|
|
for (title_no = 0; title_no < tt_srpt->nr_of_srpts; title_no++)
|
|
|
|
{
|
2006-04-24 07:20:34 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLE_%d_CHAPTERS=%d\n", title_no + 1, tt_srpt->title[title_no].nr_of_ptts);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLE_%d_ANGLES=%d\n", title_no + 1, tt_srpt->title[title_no].nr_of_angles);
|
2005-09-22 07:40:25 +00:00
|
|
|
}
|
2006-04-24 07:20:34 +00:00
|
|
|
}
|
|
|
|
if (mp_msg_test(MSGT_IDENTIFY, MSGL_V))
|
|
|
|
{
|
2008-08-30 11:21:11 +00:00
|
|
|
char volid[32];
|
2006-04-24 07:20:34 +00:00
|
|
|
unsigned char discid [16]; ///< disk ID, a 128 bit MD5 sum
|
|
|
|
int vts_no; ///< video title set number
|
2005-09-22 07:40:25 +00:00
|
|
|
for (vts_no = 1; vts_no <= vmg_file->vts_atrt->nr_of_vtss; vts_no++)
|
2006-02-27 21:11:28 +00:00
|
|
|
mp_describe_titleset(dvd, tt_srpt, vts_no);
|
2005-09-22 07:40:25 +00:00
|
|
|
if (DVDDiscID(dvd, discid) >= 0)
|
|
|
|
{
|
|
|
|
int i;
|
2007-03-04 08:49:24 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_DISC_ID=");
|
2005-09-22 07:40:25 +00:00
|
|
|
for (i = 0; i < 16; i ++)
|
2007-03-04 08:49:24 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_V, "%02X", discid[i]);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_V, "\n");
|
2005-09-22 07:40:25 +00:00
|
|
|
}
|
2008-09-16 21:30:04 +00:00
|
|
|
if (DVDUDFVolumeInfo(dvd, volid, sizeof(volid), NULL, 0) >= 0 || DVDISOVolumeInfo(dvd, volid, sizeof(volid), NULL, 0) >= 0)
|
2008-08-30 11:21:11 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_VOLUME_ID=%s\n", volid);
|
2005-09-22 07:40:25 +00:00
|
|
|
}
|
2005-05-19 19:50:39 +00:00
|
|
|
/**
|
|
|
|
* Make sure our title number is valid.
|
|
|
|
*/
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS, "There are %d titles on this DVD.\n", tt_srpt->nr_of_srpts );
|
2005-05-19 19:50:39 +00:00
|
|
|
if(dvd_title < 1 || dvd_title > tt_srpt->nr_of_srpts) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR, "Invalid DVD title number: %d\n", dvd_title);
|
2005-05-19 19:50:39 +00:00
|
|
|
ifoClose( vmg_file );
|
|
|
|
DVDClose( dvd );
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
2006-10-06 19:06:59 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_CURRENT_TITLE=%d\n", dvd_title);
|
2005-05-19 19:50:39 +00:00
|
|
|
--dvd_title; // remap 1.. -> 0..
|
|
|
|
/**
|
|
|
|
* Make sure the angle number is valid for this title.
|
|
|
|
*/
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS, "There are %d angles in this DVD title.\n", tt_srpt->title[dvd_title].nr_of_angles);
|
2005-05-19 19:50:39 +00:00
|
|
|
if(dvd_angle<1 || dvd_angle>tt_srpt->title[dvd_title].nr_of_angles) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR, "Invalid DVD angle number: %d\n", dvd_angle);
|
2007-08-27 21:10:41 +00:00
|
|
|
goto fail;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
2005-10-03 14:29:01 +00:00
|
|
|
|
|
|
|
ttn = tt_srpt->title[dvd_title].vts_ttn - 1;
|
2005-05-19 19:50:39 +00:00
|
|
|
/**
|
|
|
|
* Load the VTS information for the title set our title is in.
|
|
|
|
*/
|
|
|
|
vts_file = ifoOpen( dvd, tt_srpt->title[dvd_title].title_set_nr );
|
|
|
|
if(!vts_file) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR, "Cannot open the IFO file for DVD title %d.\n", tt_srpt->title[dvd_title].title_set_nr );
|
2007-08-27 21:10:41 +00:00
|
|
|
goto fail;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* We've got enough info, time to open the title set data.
|
|
|
|
*/
|
|
|
|
title = DVDOpenFile(dvd, tt_srpt->title[dvd_title].title_set_nr, DVD_READ_TITLE_VOBS);
|
|
|
|
if(!title) {
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_ERR, "Cannot open title VOBS (VTS_%02d_1.VOB).\n", tt_srpt->title[dvd_title].title_set_nr);
|
2005-05-19 19:50:39 +00:00
|
|
|
ifoClose( vts_file );
|
2007-08-27 21:10:41 +00:00
|
|
|
goto fail;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2006-08-19 14:52:50 +00:00
|
|
|
mp_msg(MSGT_OPEN,MSGL_V, "DVD successfully opened.\n");
|
2005-05-19 19:50:39 +00:00
|
|
|
// store data
|
|
|
|
d->dvd=dvd;
|
|
|
|
d->title=title;
|
|
|
|
d->vmg_file=vmg_file;
|
|
|
|
d->tt_srpt=tt_srpt;
|
|
|
|
d->vts_file=vts_file;
|
2006-02-27 21:11:28 +00:00
|
|
|
d->cur_title = dvd_title+1;
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2007-02-15 15:54:06 +00:00
|
|
|
pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[ttn].pgc : NULL;
|
2005-05-19 19:50:39 +00:00
|
|
|
/**
|
|
|
|
* Check number of audio channels and types
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
d->nr_of_channels=0;
|
|
|
|
if(vts_file->vts_pgcit) {
|
|
|
|
int i;
|
|
|
|
for(i=0;i<8;i++)
|
2007-02-15 15:54:06 +00:00
|
|
|
if(pgc->audio_control[i] & 0x8000) {
|
2005-05-19 19:50:39 +00:00
|
|
|
audio_attr_t * audio = &vts_file->vtsi_mat->vts_audio_attr[i];
|
|
|
|
int language = 0;
|
|
|
|
char tmp[] = "unknown";
|
2007-02-15 15:54:06 +00:00
|
|
|
stream_language_t *audio_stream = &d->audio_streams[d->nr_of_channels];
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
if(audio->lang_type == 1) {
|
|
|
|
language=audio->lang_code;
|
|
|
|
tmp[0]=language>>8;
|
|
|
|
tmp[1]=language&0xff;
|
|
|
|
tmp[2]=0;
|
|
|
|
}
|
|
|
|
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->language=language;
|
|
|
|
audio_stream->id=pgc->audio_control[i] >> 8 & 7;
|
2005-05-19 19:50:39 +00:00
|
|
|
switch(audio->audio_format) {
|
|
|
|
case 0: // ac3
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->id+=FIRST_AC3_AID;
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
case 6: // dts
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->id+=FIRST_DTS_AID;
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
case 2: // mpeg layer 1/2/3
|
|
|
|
case 3: // mpeg2 ext
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->id+=FIRST_MPG_AID;
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
case 4: // lpcm
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->id+=FIRST_PCM_AID;
|
2005-05-19 19:50:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->type=audio->audio_format;
|
2005-05-19 19:50:39 +00:00
|
|
|
// Pontscho: to my mind, tha channels:
|
|
|
|
// 1 - stereo
|
|
|
|
// 5 - 5.1
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->channels=audio->channels;
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS,"audio stream: %d format: %s (%s) language: %s aid: %d.\n",
|
2005-05-19 19:50:39 +00:00
|
|
|
d->nr_of_channels,
|
|
|
|
dvd_audio_stream_types[ audio->audio_format ],
|
|
|
|
dvd_audio_stream_channels[ audio->channels ],
|
|
|
|
tmp,
|
2007-02-15 15:54:06 +00:00
|
|
|
audio_stream->id
|
2005-05-19 19:50:39 +00:00
|
|
|
);
|
2007-02-15 15:54:06 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_ID=%d\n", audio_stream->id);
|
2006-04-24 07:20:34 +00:00
|
|
|
if(language && tmp[0])
|
2007-02-15 15:54:06 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_LANG=%s\n", audio_stream->id, tmp);
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
d->nr_of_channels++;
|
|
|
|
}
|
|
|
|
}
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS,"number of audio channels on disk: %d.\n",d->nr_of_channels );
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check number of subtitles and language
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
d->nr_of_subtitles=0;
|
|
|
|
for(i=0;i<32;i++)
|
2007-02-15 15:54:06 +00:00
|
|
|
if(pgc->subp_control[i] & 0x80000000) {
|
2005-05-19 19:50:39 +00:00
|
|
|
subp_attr_t * subtitle = &vts_file->vtsi_mat->vts_subp_attr[i];
|
2005-10-03 14:29:01 +00:00
|
|
|
video_attr_t *video = &vts_file->vtsi_mat->vts_video_attr;
|
2005-05-19 19:50:39 +00:00
|
|
|
int language = 0;
|
|
|
|
char tmp[] = "unknown";
|
2007-02-15 15:54:06 +00:00
|
|
|
stream_language_t *sub_stream = &d->subtitles[d->nr_of_subtitles];
|
2005-05-19 19:50:39 +00:00
|
|
|
|
|
|
|
if(subtitle->type == 1) {
|
|
|
|
language=subtitle->lang_code;
|
|
|
|
tmp[0]=language>>8;
|
|
|
|
tmp[1]=language&0xff;
|
|
|
|
tmp[2]=0;
|
|
|
|
}
|
|
|
|
|
2007-02-15 15:54:06 +00:00
|
|
|
sub_stream->language=language;
|
|
|
|
sub_stream->id=d->nr_of_subtitles;
|
2005-10-03 14:29:01 +00:00
|
|
|
if(video->display_aspect_ratio == 0) /* 4:3 */
|
2007-02-15 15:54:06 +00:00
|
|
|
sub_stream->id = pgc->subp_control[i] >> 24 & 31;
|
2005-10-03 14:29:01 +00:00
|
|
|
else if(video->display_aspect_ratio == 3) /* 16:9 */
|
2007-02-15 15:54:06 +00:00
|
|
|
sub_stream->id = pgc->subp_control[i] >> 8 & 31;
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS,"subtitle ( sid ): %d language: %s\n", sub_stream->id, tmp);
|
2007-06-24 08:14:10 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SUBTITLE_ID=%d\n", sub_stream->id);
|
2006-04-24 07:20:34 +00:00
|
|
|
if(language && tmp[0])
|
2007-06-24 08:14:10 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sub_stream->id, tmp);
|
2005-05-19 19:50:39 +00:00
|
|
|
d->nr_of_subtitles++;
|
|
|
|
}
|
2009-07-06 22:15:02 +00:00
|
|
|
mp_tmsg(MSGT_OPEN,MSGL_STATUS,"number of subtitles on disk: %d\n",d->nr_of_subtitles);
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine which program chain we want to watch. This is based on the
|
|
|
|
* chapter number.
|
|
|
|
*/
|
2008-02-11 22:03:34 +00:00
|
|
|
pgc_id = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgcn; // local
|
|
|
|
pgn = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgn; // local
|
2006-11-07 22:49:56 +00:00
|
|
|
d->cur_pgc_idx = pgc_id-1;
|
2005-05-19 19:50:39 +00:00
|
|
|
d->cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
|
|
|
|
d->cur_cell = d->cur_pgc->program_map[pgn-1] - 1; // start playback here
|
|
|
|
d->packs_left=-1; // for Navi stuff
|
|
|
|
d->angle_seek=0;
|
|
|
|
d->last_cell=d->cur_pgc->nr_of_cells;
|
|
|
|
|
2009-07-06 23:26:13 +00:00
|
|
|
if(d->cur_pgc->cell_playback[d->cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK )
|
2012-12-22 09:00:00 +00:00
|
|
|
d->cur_cell+=dvd_angle-1;
|
2005-05-19 19:50:39 +00:00
|
|
|
d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
|
|
|
|
d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
|
|
|
|
mp_msg(MSGT_DVD,MSGL_V, "DVD start cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack);
|
|
|
|
|
2006-11-07 22:49:56 +00:00
|
|
|
//assign cell_times_table
|
|
|
|
d->cell_times_table = malloc(sizeof(unsigned int) * d->cur_pgc->nr_of_cells);
|
|
|
|
if(d->cell_times_table == NULL)
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2006-11-07 22:49:56 +00:00
|
|
|
for(k=0; k<d->cur_pgc->nr_of_cells; k++)
|
2007-08-04 21:59:26 +00:00
|
|
|
d->cell_times_table[k] = mp_dvdtimetomsec(&d->cur_pgc->cell_playback[k].playback_time);
|
2010-08-01 22:48:01 +00:00
|
|
|
list_chapters(vts_file,tt_srpt,dvd_title);
|
2006-11-07 22:49:56 +00:00
|
|
|
|
2005-05-19 19:50:39 +00:00
|
|
|
// ... (unimplemented)
|
|
|
|
// return NULL;
|
|
|
|
stream->type = STREAMTYPE_DVD;
|
|
|
|
stream->sector_size = 2048;
|
2013-07-07 18:49:15 +00:00
|
|
|
stream->flags = MP_STREAM_SEEK;
|
2005-05-19 19:50:39 +00:00
|
|
|
stream->fill_buffer = fill_buffer;
|
|
|
|
stream->seek = seek;
|
2006-02-27 21:11:28 +00:00
|
|
|
stream->control = control;
|
2005-05-19 19:50:39 +00:00
|
|
|
stream->close = stream_dvd_close;
|
2012-11-18 19:46:12 +00:00
|
|
|
stream->start_pos = (int64_t)d->cur_pack*2048;
|
|
|
|
stream->end_pos = (int64_t)(d->cur_pgc->cell_playback[d->last_cell-1].last_sector)*2048;
|
2005-05-19 19:50:39 +00:00
|
|
|
mp_msg(MSGT_DVD,MSGL_V,"DVD start=%d end=%d \n",d->cur_pack,d->cur_pgc->cell_playback[d->last_cell-1].last_sector);
|
|
|
|
stream->priv = (void*)d;
|
|
|
|
return STREAM_OK;
|
2007-08-27 21:10:41 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
ifoClose(vmg_file);
|
|
|
|
DVDClose(dvd);
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
2012-10-11 00:04:08 +00:00
|
|
|
mp_tmsg(MSGT_DVD,MSGL_ERR,"mpv was compiled without DVD support, exiting.\n");
|
2007-08-28 22:38:45 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
2005-05-19 19:50:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
static int ifo_stream_open (stream_t *stream, int mode)
|
2007-12-02 13:13:02 +00:00
|
|
|
{
|
|
|
|
char* filename;
|
2013-08-02 15:02:34 +00:00
|
|
|
dvd_priv_t *priv = talloc_ptrtype(stream, priv);
|
|
|
|
stream->priv = priv;
|
|
|
|
*priv = stream_priv_dflts;
|
2007-12-02 13:13:02 +00:00
|
|
|
|
2013-08-02 15:03:30 +00:00
|
|
|
// "file://" prefix -> decode URL-style escapes
|
|
|
|
if (strlen(stream->url) > strlen(stream->path))
|
|
|
|
mp_url_unescape_inplace(stream->path);
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
int len = strlen(stream->path);
|
|
|
|
if (len < 4 || strcasecmp (stream->path + len - 4, ".ifo"))
|
2007-12-02 13:13:02 +00:00
|
|
|
return STREAM_UNSUPPORTED;
|
|
|
|
|
|
|
|
mp_msg(MSGT_DVD, MSGL_INFO, ".IFO detected. Redirecting to dvd://\n");
|
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
filename = strdup(basename(stream->path));
|
2007-12-02 13:13:02 +00:00
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
talloc_free(priv->cfg_device);
|
|
|
|
priv->cfg_device = talloc_strdup(NULL, dirname(stream->path));
|
2007-12-02 13:13:02 +00:00
|
|
|
if(!strncasecmp(filename,"vts_",4))
|
|
|
|
{
|
2013-08-02 15:02:34 +00:00
|
|
|
if(sscanf(filename+3, "_%02d_", &priv->cfg_title)!=1)
|
|
|
|
priv->cfg_title = 1;
|
2007-12-02 13:13:02 +00:00
|
|
|
}else
|
2013-08-02 15:02:34 +00:00
|
|
|
priv->cfg_title = 1;
|
2007-12-02 13:13:02 +00:00
|
|
|
|
|
|
|
free(filename);
|
2013-07-30 14:06:06 +00:00
|
|
|
stream->url=talloc_strdup(stream, "dvd://");
|
2007-12-02 13:13:02 +00:00
|
|
|
|
2013-08-02 15:02:34 +00:00
|
|
|
return open_s(stream, mode);
|
2007-12-02 13:13:02 +00:00
|
|
|
}
|
2005-05-19 19:50:39 +00:00
|
|
|
|
2007-12-02 13:22:53 +00:00
|
|
|
const stream_info_t stream_info_dvd = {
|
stream: fix url_options field, make protocols field not fixed length
The way the url_options field was handled was not entirely sane: it's
actually a flexible array member, so it points to garbage for streams
which do not initialize this member (it just points to the data right
after the struct, which is garbage in theory and practice). This was
not actually a problem, since the field is only used if priv_size is
set (due to how this stuff is used). But it doesn't allow setting
priv_size only, which might be useful in some cases.
Also, make the protocols array not a fixed size array. Most stream
implementations have only 1 protocol prefix, but stream_lavf.c has
over 10 (whitelists ffmpeg protocols). The high size of the fixed
size protocol array wastes space, and it is _still_ annoying to
add new prefixes to stream_lavf (have to bump the maximum length),
so make it arbitrary length.
The two changes (plus some more cosmetic changes) arte conflated into
one, because it was annoying going over all the stream implementations.
2013-08-25 20:49:27 +00:00
|
|
|
.name = "dvd",
|
|
|
|
.open = open_s,
|
|
|
|
.protocols = (const char*[]){ "dvd", NULL },
|
2013-08-02 15:02:34 +00:00
|
|
|
.priv_size = sizeof(dvd_priv_t),
|
|
|
|
.priv_defaults = &stream_priv_dflts,
|
|
|
|
.options = stream_opts_fields,
|
stream: fix url_options field, make protocols field not fixed length
The way the url_options field was handled was not entirely sane: it's
actually a flexible array member, so it points to garbage for streams
which do not initialize this member (it just points to the data right
after the struct, which is garbage in theory and practice). This was
not actually a problem, since the field is only used if priv_size is
set (due to how this stuff is used). But it doesn't allow setting
priv_size only, which might be useful in some cases.
Also, make the protocols array not a fixed size array. Most stream
implementations have only 1 protocol prefix, but stream_lavf.c has
over 10 (whitelists ffmpeg protocols). The high size of the fixed
size protocol array wastes space, and it is _still_ annoying to
add new prefixes to stream_lavf (have to bump the maximum length),
so make it arbitrary length.
The two changes (plus some more cosmetic changes) arte conflated into
one, because it was annoying going over all the stream implementations.
2013-08-25 20:49:27 +00:00
|
|
|
.url_options = (const char*[]){
|
|
|
|
"hostname=title",
|
|
|
|
"filename=device",
|
|
|
|
NULL
|
2013-08-02 15:02:34 +00:00
|
|
|
},
|
2005-05-19 19:50:39 +00:00
|
|
|
};
|
2007-12-02 13:13:02 +00:00
|
|
|
|
2007-12-02 13:22:53 +00:00
|
|
|
const stream_info_t stream_info_ifo = {
|
stream: fix url_options field, make protocols field not fixed length
The way the url_options field was handled was not entirely sane: it's
actually a flexible array member, so it points to garbage for streams
which do not initialize this member (it just points to the data right
after the struct, which is garbage in theory and practice). This was
not actually a problem, since the field is only used if priv_size is
set (due to how this stuff is used). But it doesn't allow setting
priv_size only, which might be useful in some cases.
Also, make the protocols array not a fixed size array. Most stream
implementations have only 1 protocol prefix, but stream_lavf.c has
over 10 (whitelists ffmpeg protocols). The high size of the fixed
size protocol array wastes space, and it is _still_ annoying to
add new prefixes to stream_lavf (have to bump the maximum length),
so make it arbitrary length.
The two changes (plus some more cosmetic changes) arte conflated into
one, because it was annoying going over all the stream implementations.
2013-08-25 20:49:27 +00:00
|
|
|
.name = "ifo",
|
|
|
|
.open = ifo_stream_open,
|
|
|
|
.protocols = (const char*[]){ "file", "", NULL },
|
2007-12-02 13:13:02 +00:00
|
|
|
};
|