[vlc-devel] [PATCH] DCP: allow to read subtitles
Valentin Vetter
vvetter at outlook.com
Sat Mar 1 05:20:12 CET 2014
---
modules/access/dcp/dcp.cpp | 361 +++++++++++++++++++++++++++++++++++++-
modules/access/dcp/dcpparser.cpp | 64 +++++--
modules/access/dcp/dcpparser.h | 6 +-
3 files changed, 407 insertions(+), 24 deletions(-)
diff --git a/modules/access/dcp/dcp.cpp b/modules/access/dcp/dcp.cpp
index 7960c6e..3b17d05 100644
--- a/modules/access/dcp/dcp.cpp
+++ b/modules/access/dcp/dcp.cpp
@@ -48,6 +48,7 @@
#include <vlc_plugin.h>
#include <vlc_xml.h>
#include <vlc_url.h>
+#include <vlc_memory.h>
#include <vlc_aout.h>
/* ASDCP headers */
@@ -115,6 +116,60 @@ struct audioReader_t
The classes for handling these essence types are defined in AS_DCP.h and are different for each essence type, respectively. The demux_sys_t structure contains members for handling each of these essence types.
*/
+enum
+{
+ SUB_TYPE_UNKNOWN = -1,
+ SUB_TYPE_DCSUBTITLE
+};
+
+typedef struct
+{
+ int64_t i_start;
+ int64_t i_stop;
+
+ char *psz_text;
+} subtitle_t;
+
+typedef struct
+{
+ xml_t *xml;
+ xml_reader_t *xml_reader;
+ int count;
+} sub_xml_t;
+
+struct demux_sub_sys_t
+{
+ int i_type;
+ sub_xml_t sub_xml;
+ es_out_id_t *es;
+
+ int64_t i_next_demux_date;
+ int64_t i_microsecperframe;
+
+ char *psz_header;
+ int i_subtitle;
+ int i_subtitles;
+ subtitle_t *subtitle;
+
+ int64_t i_length;
+
+ struct
+ {
+ bool b_inited;
+
+ int i_comment;
+ int i_time_resolution;
+ int i_time_shift;
+ } jss;
+ struct
+ {
+ bool b_inited;
+
+ float f_total;
+ float f_factor;
+ } mpsub;
+};
+
class demux_sys_t
{
public:
@@ -127,12 +182,20 @@ class demux_sys_t
/* ASDCP Audio MXF Reader */
vector<audioReader_t> v_audioReader;
+ /* Subtitles information */
+ vector<demux_sub_sys_t*> v_sub_sys;
+
/* audio buffer size */
uint32_t i_audio_buffer;
/* elementary streams */
es_out_id_t *p_video_es;
es_out_id_t *p_audio_es;
+ es_out_id_t *p_sub_es;
+
+ /* subtitle parser */
+ stream_t *p_subtitle_parser;
+ stream_t *p_subtitle_stream;
/* DCP object */
dcp_t *p_dcp;
@@ -153,6 +216,9 @@ class demux_sys_t
/* current audio reel */
unsigned int i_audio_reel;
+ /* current subtitle reel */
+ unsigned int i_sub_reel;
+
uint8_t i_chans_to_reorder; /* do we need channel reordering */
uint8_t pi_chan_table[AOUT_CHAN_MAX];
uint8_t i_channels;
@@ -163,13 +229,16 @@ class demux_sys_t
PictureEssType ( ESS_UNKNOWN ),
v_videoReader(),
v_audioReader(),
+ v_sub_sys(),
p_video_es( NULL ),
p_audio_es( NULL ),
+ p_sub_es( NULL ),
p_dcp( NULL ),
frame_no( 0 ),
frames_total( 0 ),
i_video_reel( 0 ),
- i_audio_reel( 0 ) {};
+ i_audio_reel( 0 ),
+ i_sub_reel( 0 ) {};
~demux_sys_t()
{
@@ -281,6 +350,9 @@ static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );
int dcpInit ( demux_t *p_demux );
+int OpenSubtitle( demux_t *p_demux, const char *filePath, int );
+int SubtitleDemux( demux_t *, int );
+static int ParseDCSubtitle ( demux_t *, subtitle_t *, int, int );
int parseXML ( demux_t * p_demux );
static inline void fillVideoFmt(
video_format_t * fmt, unsigned int width, unsigned int height,
@@ -411,7 +483,6 @@ static int Open( vlc_object_t *obj )
break;
}
case ESS_MPEG2_VES: {
-
MPEG2::MXFReader *p_VideoMXFReader = new ( nothrow ) MPEG2::MXFReader();
videoReader_t videoReader;
@@ -533,7 +604,6 @@ static int Open( vlc_object_t *obj )
audioReader_t audioReader;
audioReader.p_AudioMXFReader = p_AudioMXFReader;
p_sys->v_audioReader.push_back( audioReader );
-
}
es_format_Init( &audio_format, AUDIO_ES, VLC_CODEC_S24L );
if( AudioDesc.AudioSamplingRate.Denominator != 0 )
@@ -573,9 +643,33 @@ static int Open( vlc_object_t *obj )
retval = VLC_EGENERIC;
goto error;
}
+
+ /* Open subtitle file */
+ if( p_sys->p_dcp->sub_reels.size() > 0 )
+ {
+ for ( uint8_t i = 0; i < ( p_sys->p_dcp->sub_reels.size() ); i++)
+ {
+ if ( retval = OpenSubtitle( p_demux, p_sys->p_dcp->sub_reels[i].filename.c_str(), i ) )
+ return retval;
+ }
+ }
+
+ es_format_t fmt;
+ es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
+ fmt.subs.psz_encoding = strdup( "UTF-8" );
+
+ if( ( p_demux->p_sys->p_sub_es = es_out_Add( p_demux->out, &fmt ) ) == NULL )
+ {
+ msg_Err( p_demux, "Failed to add subtitle es" );
+ free( p_sys );
+ return VLC_EGENERIC;
+ }
+
+ es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_demux->p_sys->p_sub_es );
+ es_format_Clean( &fmt );
+
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
- p_sys->frame_no = p_sys->p_dcp->video_reels[0].i_entrypoint;
return VLC_SUCCESS;
error:
@@ -631,6 +725,21 @@ static int Demux( demux_t *p_demux )
}
}
+ /* swaping subtitle reels */
+ if ( p_sys->frame_no == p_sys->p_dcp->sub_reels[p_sys->i_sub_reel].i_absolute_end )
+ {
+ if ( p_sys->i_sub_reel + 1 == p_sys->p_dcp->sub_reels.size() )
+ {
+ return 0;
+ }
+ else
+ {
+ p_sys->i_sub_reel++;
+ es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_demux->p_sys->p_sub_es );
+ }
+ }
+
+
/* video frame */
switch( p_sys->PictureEssType )
{
@@ -719,6 +828,9 @@ static int Demux( demux_t *p_demux )
es_out_Send( p_demux->out, p_sys->p_video_es, p_video_frame );
es_out_Send( p_demux->out, p_sys->p_audio_es, p_audio_frame );
+ if ( p_sys->v_sub_sys[p_sys->i_sub_reel] )
+ SubtitleDemux( p_demux, p_sys->i_sub_reel );
+
p_sys->frame_no++;
return 1;
@@ -743,6 +855,9 @@ static int Control( demux_t *p_demux, int query, va_list args )
int64_t *pi64, i64;
demux_sys_t *p_sys = p_demux->p_sys;
+ int i_reel = 0;
+ bool b_correct_reel = false;
+
switch ( query )
{
case DEMUX_CAN_PAUSE:
@@ -970,3 +1085,241 @@ int parseXML ( demux_t * p_demux )
delete assetmap;
return VLC_SUCCESS; /* TODO : perform checking on XML parsing */
}
+
+
+/*****************************************************************************
+ * functions for subtitles
+ *****************************************************************************/
+
+static int XmlLoad( demux_t * p_demux, stream_t *s, int sub_nb )
+{
+ demux_sub_sys_t *p_sys = p_demux->p_sys->v_sub_sys[sub_nb];
+ sub_xml_t *p_sub_xml = &p_sys->sub_xml;
+ xml_t *p_xml;
+ xml_reader_t *p_xml_reader;
+
+ /* Create XML parser */
+ p_xml = xml_Create( p_demux );
+ if( ! p_xml )
+ return VLC_EGENERIC;
+
+ /* Initialize XML reader */
+ p_xml_reader = xml_ReaderCreate( p_xml, s );
+ if( ! p_xml_reader )
+ return VLC_EGENERIC;
+ p_sub_xml->xml = p_xml;
+ p_sub_xml->xml_reader = p_xml_reader;
+ return VLC_SUCCESS;
+
+}
+
+static void XmlUnLoad ( demux_t *p_demux, int sub_nb )
+{
+ demux_sub_sys_t *p_sys = p_demux->p_sys->v_sub_sys[sub_nb];
+ sub_xml_t *p_sub_xml = &p_sys->sub_xml;
+ xml_ReaderDelete( p_sub_xml->xml_reader );
+ xml_Delete( p_sub_xml->xml );
+
+}
+
+static int ParseDCSubtitle( demux_t *p_demux, subtitle_t *p_subtitle, int i_idx, int sub_nb )
+{
+ demux_sub_sys_t *p_sys = p_demux->p_sys->v_sub_sys[sub_nb];
+ sub_xml_t *p_sub_xml = &p_sys->sub_xml;
+ xml_reader_t * p_xml_reader = p_sub_xml->xml_reader;
+ const char *node = NULL;
+ int type = 0;
+ bool b_subtitle_tag = false;
+ bool b_quit = false;
+ char *psz_text = NULL;
+ int i_len = 0;
+ while( ( b_quit == false ) && ( type = xml_ReaderNextNode( p_xml_reader, &node ) ) > 0 )
+ {
+ switch (type)
+ {
+ case XML_READER_STARTELEM:
+ if( strcmp( node, "Subtitle" ) == 0 ) {
+ const char *val, *attr;
+ int h1, m1, s1, d1;
+ b_subtitle_tag = true;
+ i_len = 0;
+ while( ( attr = xml_ReaderNextAttr( p_xml_reader, &val ) ) ) {
+ if( ( strcmp( attr, "TimeIn" ) == 0 ) ) {
+ if( sscanf( val, "%d:%d:%d:%d", &h1, &m1, &s1, &d1 ) ==4 ) {
+ p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
+ (int64_t)m1 * 60*1000 +
+ (int64_t)s1 * 1000 +
+ (int64_t)d1 ) * 1000;
+ }
+ }
+ if( ( strcmp(attr, "TimeOut" ) == 0 ) ) {
+ if( sscanf(val, "%d:%d:%d:%d", &h1, &m1, &s1, &d1) ==4 ) {
+ p_subtitle->i_stop = ( (int64_t)h1 * 3600*1000 +
+ (int64_t)m1 * 60*1000 +
+ (int64_t)s1 * 1000 +
+ (int64_t)d1 ) * 1000;
+ }
+ }
+ }
+ }
+ break;
+
+ case XML_READER_TEXT:
+ if( b_subtitle_tag ) {
+ if( i_len == 0 ) {
+ psz_text = strdup(node);
+ i_len = strlen(psz_text);
+ } else {
+ int i_old = i_len;
+ i_len = strlen(node);
+ psz_text = (char *) realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
+ strcat( psz_text, "\n" );
+ strcat( psz_text, node );
+ i_len = strlen(psz_text);
+ }
+ }
+ break;
+
+ case XML_READER_ENDELEM:
+ if( strcmp( node, "Subtitle" ) == 0 ) {
+ p_subtitle->psz_text = psz_text;
+ b_subtitle_tag = false ;
+ b_quit = true;
+ }
+ if( strcmp(node, "DCSubtitle") == 0 ) {
+ if( psz_text ) free(psz_text);
+ return VLC_EGENERIC;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+
+int OpenSubtitle( demux_t *p_demux, const char *filePath, int sub_nb )
+{
+ demux_sub_sys_t *p_sys ;
+ const char * s_url = vlc_path2uri( filePath, "file" );
+ stream_t *p_stream;
+ int ret;
+ int i_max;
+
+ /*TODO: Where put the free */
+ p_sys = new demux_sub_sys_t();
+ p_demux->p_sys->v_sub_sys.push_back( p_sys );
+ p_demux->p_sys->v_sub_sys[sub_nb] = p_sys = (demux_sub_sys_t *) malloc( sizeof( *p_sys ) );
+
+ if( p_sys == NULL )
+ return VLC_ENOMEM;
+
+ p_sys->psz_header = NULL;
+ p_sys->i_subtitle = 0;
+ p_sys->i_subtitles = 0;
+ p_sys->subtitle = NULL;
+ p_sys->i_microsecperframe = 40000;
+
+ p_sys->jss.b_inited = false;
+ p_sys->mpsub.b_inited = false;
+
+ /* HARD fix fps to 24 fps */
+ p_sys->i_microsecperframe = (int64_t)(1000000 / 24 );
+
+ p_sys->i_type = SUB_TYPE_DCSUBTITLE;
+
+ /* Load the whole file */
+ p_stream = stream_UrlNew( p_demux, s_url );
+ if( ! p_stream )
+ return VLC_EGENERIC;
+ if ( ret = XmlLoad( p_demux, p_stream, sub_nb ) ) {
+ msg_Warn( p_demux, "Failure on loading XML subfile" );
+ free( p_sys );
+ stream_Delete( p_stream );
+ return ret;
+ }
+
+ /* Parse it */
+ for( i_max = 0; ; )
+ {
+ if( p_sys->i_subtitles >= i_max )
+ {
+ i_max += 500;
+ if( !( p_sys->subtitle = (subtitle_t *)realloc_or_free( p_sys->subtitle,
+ sizeof(subtitle_t) * i_max ) ) )
+ {
+ XmlUnLoad( p_demux, sub_nb );
+ stream_Delete( p_stream );
+ free( p_sys );
+ return VLC_ENOMEM;
+ }
+ }
+
+ if( ParseDCSubtitle( p_demux, &p_sys->subtitle[p_sys->i_subtitles],
+ p_sys->i_subtitles, sub_nb ) )
+ break;
+
+ p_sys->i_subtitles++;
+ }
+
+ XmlUnLoad( p_demux, sub_nb );
+ stream_Delete(p_stream);
+
+ return VLC_SUCCESS;
+}
+
+int SubtitleDemux( demux_t *p_demux, int sub_nb )
+{
+ demux_sub_sys_t *p_sys = p_demux->p_sys->v_sub_sys[sub_nb];
+ int64_t i_maxdate;
+
+
+ if( p_sys->i_subtitle >= p_sys->i_subtitles )
+ return 0;
+
+ i_maxdate = p_sys->i_next_demux_date;
+
+ if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
+ {
+ /* Should not happen */
+ i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
+ }
+
+ while( p_sys->i_subtitle < p_sys->i_subtitles &&
+ p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
+ {
+ const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];
+
+ block_t *p_block;
+ int i_len = strlen( p_subtitle->psz_text ) + 1;
+
+ if( i_len <= 1 || p_subtitle->i_start < 0 )
+ {
+ p_sys->i_subtitle++;
+ continue;
+ }
+
+ if( ( p_block = block_Alloc( i_len ) ) == NULL )
+ {
+ p_sys->i_subtitle++;
+ continue;
+ }
+
+ p_block->i_dts =
+ p_block->i_pts = VLC_TS_0 + p_subtitle->i_start + p_demux->p_sys->p_dcp->sub_reels[sub_nb].i_correction;
+
+ if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
+ p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
+
+ memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );
+
+ es_out_Send( p_demux->out, p_demux->p_sys->p_sub_es, p_block );
+
+ p_sys->i_subtitle++;
+ }
+
+ p_sys->i_next_demux_date = 0;
+ return 1;
+}
diff --git a/modules/access/dcp/dcpparser.cpp b/modules/access/dcp/dcpparser.cpp
index cd42922..e68bc46 100644
--- a/modules/access/dcp/dcpparser.cpp
+++ b/modules/access/dcp/dcpparser.cpp
@@ -234,6 +234,8 @@ int AssetMap::Parse ( )
int index = 0;
int sum_duration_vid = 0;
int sum_duration_aud = 0;
+ int sum_duration_sub = 0;
+ int64_t i_conversion = (int64_t)(1000000 / 24);
string node;
CPL *cpl;
@@ -268,7 +270,6 @@ int AssetMap::Parse ( )
}
}
-
/* Look for PKLs path */
if ( (_p_asset_list == NULL) || (_p_asset_list->size() == 0) ) {
msg_Err( p_demux, "Asset list empty" );
@@ -345,6 +346,7 @@ int AssetMap::Parse ( )
Asset *asset;
struct info_reel video;
struct info_reel audio;
+ struct info_reel subtitles;
/* Get picture */
asset = reel->getTrack(TRACK_PICTURE);
@@ -356,32 +358,51 @@ int AssetMap::Parse ( )
video.i_duration = asset->getDuration();
video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
video.i_absolute_end = sum_duration_vid;
+ video.i_nb_reel = index;
p_dcp->video_reels.push_back(video);
msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
}
+
/* Get audio */
asset = reel->getTrack(TRACK_SOUND);
if (asset != NULL)
{
- /*if (!p_dcp->audio_reels.empty())
- {
- sum_duration_aud = 0;
- for (int i = 0; i != p_dcp->audio_reels.size(); ++i)
- {
- sum_duration_aud += p_dcp->audio_reels(i).i_duration;
- }
- }*/
sum_duration_aud += asset->getDuration();
audio.filename = p_dcp->path + asset->getPath();
audio.i_entrypoint = asset->getEntryPoint();
audio.i_duration = asset->getDuration();
audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
audio.i_absolute_end = sum_duration_aud;
+ audio.i_nb_reel = index;
p_dcp->audio_reels.push_back(audio);
msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
}
+
+ /* Get subtitle */
+ asset = reel->getTrack(TRACK_SUBTITLE);
+ if (asset != NULL)
+ {
+ sum_duration_sub += asset->getDuration();
+ subtitles.filename = p_dcp->path + asset->getPath();
+ subtitles.i_entrypoint = asset->getEntryPoint();
+ subtitles.i_duration = asset->getDuration();
+ /* Subtitle correction, not the same as for audio and video, unit is 10^(-6)*s */
+ if (index > 0 )
+ {
+ subtitles.i_correction = (p_dcp->video_reels[index-1].i_absolute_end + subtitles.i_entrypoint) * i_conversion;
+ }
+ else
+ {
+ subtitles.i_correction = (subtitles.i_entrypoint) * i_conversion;
+ }
+ subtitles.i_absolute_end = sum_duration_sub;
+ subtitles.i_nb_reel = index;
+ p_dcp->sub_reels.push_back(subtitles);
+ msg_Dbg( this->p_demux, "Subtitle Track: %s",asset->getPath().c_str());
+ msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
+ }
}
/* free memory */
this->CloseXml();
@@ -448,7 +469,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
/* Asset tags not in AssetMap */
break;
default:
- msg_Warn(this->p_demux, "Unknow ASSET_TAG: %i", _tag );
+ msg_Warn( this->p_demux, "Unknow ASSET_TAG: %i", _tag );
break;
}
/* break the for loop as a tag is found*/
@@ -482,6 +503,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
}
}
return -1;
+
}
@@ -536,7 +558,7 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader)
/* Asset tags not in PKL */
break;
default:
- msg_Warn(this->p_demux, "Unknow ASSET_TAG: %i", _tag );
+ msg_Warn( this->p_demux, "Unknow ASSET_TAG: %i", _tag );
break;
}
/* break the for loop as a tag is found*/
@@ -634,7 +656,6 @@ int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, in
string node;
int type;
Asset *asset;
-
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "AssetList" )
@@ -666,6 +687,7 @@ int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, in
}
}
return -1;
+
}
Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
@@ -819,7 +841,7 @@ int PKL::Parse()
this->s_group_id = s_value;
break;
default:
- msg_Warn(this->p_demux, "Unknow PKG_TAG: %i", _tag );
+ msg_Warn( this->p_demux, "Unknow PKG_TAG: %i", _tag );
break;
}
/* break the for loop as a tag is found*/
@@ -992,6 +1014,10 @@ int Reel::Parse(string p_node, int p_type) {
int type;
string s_value;
+ this->p_picture_track = NULL;
+ this->p_sound_track = NULL;
+ this->p_subtitle_track = NULL;
+
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "Reel")
@@ -1126,19 +1152,19 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
} else if (node == "AnnotationText") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
- asset->setAnnotation(s_value);
+ asset->setAnnotation(s_value);
} else if (node == "IntrinsicDuration") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
- asset->setIntrinsicDuration(atoi(s_value.c_str()));
+ asset->setIntrinsicDuration(atoi(s_value.c_str()));
} else if (node == "EntryPoint") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
- asset->setEntryPoint(atoi(s_value.c_str()));
+ asset->setEntryPoint(atoi(s_value.c_str()));
} else if (node == "Duration") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
- asset->setDuration(atoi(s_value.c_str()));
+ asset->setDuration(atoi(s_value.c_str()));
} else if (node == "KeyId") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
@@ -1187,6 +1213,7 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
default:
break;
}
+
return 0;
}
@@ -1316,7 +1343,7 @@ int CPL::Parse()
this->s_content_kind = s_value;
break;
default:
- msg_Warn(this->p_demux, "Unknow CPL_TAG: %i", _tag );
+ msg_Warn( this->p_demux, "Unknow CPL_TAG: %i", _tag );
break;
}
@@ -1386,6 +1413,7 @@ int CPL::ParseReelList(string p_node, int p_type) {
break;
}
}
+
return -1;
}
diff --git a/modules/access/dcp/dcpparser.h b/modules/access/dcp/dcpparser.h
index 32059f5..f02a520 100644
--- a/modules/access/dcp/dcpparser.h
+++ b/modules/access/dcp/dcpparser.h
@@ -72,11 +72,12 @@ class PKL;
/* This struct stores useful information about an MXF for demux() */
struct info_reel
{
+ int64_t i_correction; /* entrypoint - sum of previous durations */
string filename;
int i_entrypoint;
int i_duration;
- int i_correction; /* entrypoint - sum of previous durations */
- uint32_t i_absolute_end; /* correction + duration */
+ uint32_t i_absolute_end; /* correction + duration */
+ int i_nb_reel; /* number of the reel in the cpl reel list, start at 0 */
};
/* This struct stores the most important information about the DCP */
@@ -89,6 +90,7 @@ struct dcp_t
vector<info_reel> audio_reels;
vector<info_reel> video_reels;
+ vector<info_reel> sub_reels;
dcp_t():
p_asset_list(NULL) {};
--
1.7.9.5
More information about the vlc-devel
mailing list