Commit ce2d6368 authored by Nicolas Bertrand's avatar Nicolas Bertrand Committed by Jean-Baptiste Kempf

DCP: new access-demux module for DigitalCinemaPackage

This version only support 1-Reel-per-track DCPs, like trailers
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent af3bbd8f
...@@ -12,6 +12,7 @@ Access: ...@@ -12,6 +12,7 @@ Access:
New schemes for implicit (ftps) and explicit (ftpes) modes. New schemes for implicit (ftps) and explicit (ftpes) modes.
* MMS audio selection on split audio streams * MMS audio selection on split audio streams
* Blu-Ray: support overlays, preferred menu languages, region selection and navigation * Blu-Ray: support overlays, preferred menu languages, region selection and navigation
* Digital Cinema Package support
Decoder: Decoder:
* Partial support for Voxware MetaSound * Partial support for Voxware MetaSound
......
...@@ -1917,6 +1917,23 @@ then ...@@ -1917,6 +1917,23 @@ then
fi fi
AM_CONDITIONAL(HAVE_AVFOUNDATION, [test "${have_avfoundation}" != "no"]) AM_CONDITIONAL(HAVE_AVFOUNDATION, [test "${have_avfoundation}" != "no"])
dnl
dnl DCP plugin (using asdcplib)
dnl
AC_ARG_ENABLE(dcp,
AS_HELP_STRING([--enable-dcp],[Digital Cinema Package support using asdcplib (default auto)]))
have_asdcp="no"
AS_IF([test "x${enable_dcp}" != "no"], [
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS( [[AS@&t@_DCP.h]],
[have_asdcp="yes"],
[AS_IF( [test "x${enable_dcp}" = "yes"],
[AC_MSG_ERROR( [ ASDCP library cannot be found (needed for dcp module). Either use --enable-dcp=no or install asdcp library: http://www.cinecert.com/asdcplib/download/] )])
])
AC_LANG_POP(C++)
])
AM_CONDITIONAL(HAVE_ASDCP, [test "${have_asdcp}" != "no"])
dnl dnl
dnl Demux plugins dnl Demux plugins
dnl dnl
......
...@@ -75,6 +75,7 @@ $Id$ ...@@ -75,6 +75,7 @@ $Id$
* dbus: D-Bus control interface * dbus: D-Bus control interface
* dbus_screensaver: preventing the computer from suspending * dbus_screensaver: preventing the computer from suspending
* dc1394: IIDC (DCAM) FireWire input module * dc1394: IIDC (DCAM) FireWire input module
* dcp: Digital Cinema Package input
* ddummy: dummy decoder * ddummy: dummy decoder
* decklink: input module to read from a Blackmagic SDI card * decklink: input module to read from a Blackmagic SDI card
* decklinkoutput: output module to write to Blackmagic SDI card * decklinkoutput: output module to write to Blackmagic SDI card
......
...@@ -9,6 +9,13 @@ AM_CPPFLAGS += -I$(srcdir)/access ...@@ -9,6 +9,13 @@ AM_CPPFLAGS += -I$(srcdir)/access
libattachment_plugin_la_SOURCES = access/attachment.c libattachment_plugin_la_SOURCES = access/attachment.c
access_LTLIBRARIES += libattachment_plugin.la access_LTLIBRARIES += libattachment_plugin.la
libdcp_plugin_la_SOURCES = access/dcp/dcpparser.h access/dcp/dcp.cpp access/dcp/dcpparser.cpp
libdcp_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
libdcp_plugin_la_LIBADD = $(AM_LIBADD) -lasdcp
if HAVE_ASDCP
access_LTLIBRARIES += libdcp_plugin.la
endif
libfilesystem_plugin_la_SOURCES = access/fs.h access/file.c access/directory.c access/fs.c libfilesystem_plugin_la_SOURCES = access/fs.h access/file.c access/directory.c access/fs.c
libfilesystem_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) libfilesystem_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
if HAVE_WIN32 if HAVE_WIN32
......
/*****************************************************************************
* Copyright (C) 2012-2013 VLC authors and VideoLAN
*
* Authors:
* Nicolas Bertrand <nico@isf.cc>
* Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
* Jean-Baptiste Kempf <jb@videolan.org>
* Pierre Villard <pierre dot villard dot fr at gmail dot com>
* Claire Etienne
* Aurélie Sbinné
* Samuel Kerjose
* Julien Puyobro
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* @file dcp.cpp
* @brief DCP access-demux module for Digital Cinema Packages using asdcp library
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define __STDC_CONSTANT_MACROS 1
/* VLC core API headers */
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_plugin.h>
#include <vlc_xml.h>
#include <vlc_url.h>
#include <vlc_aout.h>
/* ASDCP headers */
#include <AS_DCP.h>
#include "dcpparser.h"
using namespace ASDCP;
using namespace std;
#define FRAME_BUFFER_SIZE 1302083 /* maximum frame length, in bytes, after
"Digital Cinema System Specification Version 1.2
with Errata as of 30 August 2012" */
/* Forward declarations */
static int Open( vlc_object_t * );
static void Close( vlc_object_t * );
/* Module descriptor */
vlc_module_begin()
set_shortname( N_( "DCP" ) )
add_shortcut( "dcp" )
set_description( N_( "Digital Cinema Package module" ) )
set_capability( "access_demux", 0 )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACCESS )
set_callbacks( Open, Close )
vlc_module_end()
//! Kind of MXF MEDIA TYPE
typedef enum MxfMedia_t {
MXF_UNKNOWN = 0,
MXF_PICTURE,
MXF_AUDIO,
} MxfMedia_t;
/* ASDCP library (version 1.10.48) can handle files having one of the following Essence Types, as defined in AS_DCP.h:
ESS_UNKNOWN, // the file is not a supported AS-DCP essence container
ESS_MPEG2_VES, // the file contains an MPEG video elementary stream
ESS_JPEG_2000, // the file contains one or more JPEG 2000 codestreams
ESS_PCM_24b_48k, // the file contains one or more PCM audio pairs
ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs
ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources
ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic).
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.
*/
class demux_sys_t
{
public:
/* ASDCP Picture Essence Type */
EssenceType_t PictureEssType;
/* ASDCP Video MXF Reader */
union
{
/* JPEG2000 essence type */
JP2K::MXFReader *p_PicMXFReader;
/* JPEG2000 stereoscopic essence type */
JP2K::MXFSReader *p_PicMXFSReader;
/* MPEG2 essence type */
MPEG2::MXFReader *p_VideoMXFReader;
};
/* ASDCP Audio MXF Reader */
PCM::MXFReader *p_AudioMXFReader;
/* audio buffer size */
uint32_t i_audio_buffer;
/* elementary streams */
es_out_id_t *p_video_es;
es_out_id_t *p_audio_es;
/* DCP object */
dcp_t *p_dcp;
/* current frame number */
uint32_t frame_no;
/* frame rate */
unsigned int frame_rate_num;
unsigned int frame_rate_denom;
/* total number of frames */
uint32_t frames_total;
uint8_t i_chans_to_reorder; /* do we need channel reordering */
uint8_t pi_chan_table[AOUT_CHAN_MAX];
uint8_t i_channels;
mtime_t i_pts;
demux_sys_t():
PictureEssType ( ESS_UNKNOWN ),
p_PicMXFReader( NULL ),
p_AudioMXFReader( NULL ),
p_video_es( NULL ),
p_audio_es( NULL ),
p_dcp( NULL ),
frame_no( 0 ) {};
~demux_sys_t()
{
switch ( PictureEssType )
{
case ESS_UNKNOWN:
break;
case ESS_JPEG_2000:
delete p_PicMXFReader;
break;
case ESS_JPEG_2000_S:
delete p_PicMXFSReader;
break;
case ESS_MPEG2_VES:
delete p_VideoMXFReader;
break;
default:
break;
}
delete p_AudioMXFReader;
delete p_dcp;
}
};
/*TODO: basic correlation between SMPTE S428-3/S429-2
* Real sound is more complex with case of left/right surround, ...
* and hearing impaired/Narration channels */
/* 1 channel: mono */
static const uint32_t i_channels_1[] =
{ AOUT_CHAN_LEFT, 0 };
/* 2 channels: stereo */
static const uint32_t i_channels_2[]=
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 };
/* 4 channels */
static const uint32_t i_channels_4[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
AOUT_CHAN_LFE, 0 };
/* 6 channels: 5.1 */
static const uint32_t i_channels_6[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
0 };
/* 7 channels: 6.1 */
static const uint32_t i_channels_7[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
AOUT_CHAN_REARCENTER, 0 };
/* 8 channels: 7.1 */
static const uint32_t i_channels_8[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
/* 9 channels; 8.1 */
static const uint32_t i_channels_9[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_REARCENTER, 0 };
static const uint32_t *pi_channels_aout [] =
{ NULL,
i_channels_1,
i_channels_2,
NULL,
i_channels_4,
NULL,
i_channels_6,
i_channels_7,
i_channels_8,
i_channels_9 };
static const unsigned i_channel_mask[] =
{ 0,
AOUT_CHAN_LEFT,
AOUT_CHANS_STEREO,
0,
AOUT_CHANS_3_1,
0,
AOUT_CHANS_5_1,
AOUT_CHANS_6_1_MIDDLE,
AOUT_CHANS_7_1,
AOUT_CHANS_8_1 };
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );
int dcpInit ( demux_t *p_demux );
int parseXML ( demux_t * p_demux );
static inline void fillVideoFmt(
video_format_t * fmt, unsigned int width, unsigned int height,
unsigned int frame_rate_num, unsigned int frame_rate_denom );
void CloseDcpAndMxf( demux_t *p_demux );
/*****************************************************************************
* Open: module init function
*****************************************************************************/
static int Open( vlc_object_t *obj )
{
demux_t *p_demux = ( demux_t* ) obj;
demux_sys_t *p_sys;
es_format_t video_format, audio_format;
int retval;
if( !p_demux->psz_file )
return VLC_EGENERIC;
p_sys = new ( nothrow ) demux_sys_t();
if( unlikely( p_sys == NULL ) ) {
return VLC_ENOMEM;
}
p_demux->p_sys = p_sys;
/* Allocate DCP object */
dcp_t *p_dcp = new ( nothrow ) dcp_t;
if( unlikely( p_dcp == NULL ) ) {
delete p_sys;
return VLC_ENOMEM;
}
p_sys->p_dcp = p_dcp;
/* handle the DCP directory, saving the paths for audio and video file, returning error if unsuccessful */
if( ( retval = dcpInit( p_demux ) ) )
goto error;
/* Open video file */
EssenceType( p_sys->p_dcp->videofile.c_str(), p_sys->PictureEssType );
switch( p_sys->PictureEssType )
{
case ESS_UNKNOWN:
msg_Err( p_demux, "The file %s is not a supported AS_DCP essence container", p_sys->p_dcp->videofile.c_str() );
retval = VLC_EGENERIC;
goto error;
case ESS_JPEG_2000:
case ESS_JPEG_2000_S: {
JP2K::PictureDescriptor PicDesc;
if (p_sys->PictureEssType == ESS_JPEG_2000_S) { /* 3D JPEG2000 */
JP2K::MXFSReader * p_PicMXFSReader = new ( nothrow ) JP2K::MXFSReader();
if( !p_PicMXFSReader) {
retval = VLC_ENOMEM;
goto error;
}
if( !ASDCP_SUCCESS( p_PicMXFSReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
msg_Err( p_demux, "File %s could not be opened with ASDCP", p_sys->p_dcp->videofile.c_str() );
retval = VLC_EGENERIC;
delete p_PicMXFSReader;
goto error;
}
p_PicMXFSReader->FillPictureDescriptor( PicDesc );
p_sys->p_PicMXFSReader = p_PicMXFSReader;
} else { /* 2D JPEG2000 */
JP2K::MXFReader *p_PicMXFReader = new ( nothrow ) JP2K::MXFReader();
if( !p_PicMXFReader ) {
retval = VLC_ENOMEM;
goto error;
}
if( !ASDCP_SUCCESS( p_PicMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
msg_Err( p_demux, "File %s could not be opened with ASDCP",
p_sys->p_dcp->videofile.c_str() );
retval = VLC_EGENERIC;
delete p_PicMXFReader;
goto error;
}
p_PicMXFReader->FillPictureDescriptor( PicDesc );
p_sys->p_PicMXFReader = p_PicMXFReader;
}
es_format_Init( &video_format, VIDEO_ES, VLC_CODEC_JPEG2000 );
fillVideoFmt( &video_format.video, PicDesc.StoredWidth, PicDesc.StoredHeight,
PicDesc.EditRate.Numerator, PicDesc.EditRate.Denominator );
p_sys->frame_rate_num = PicDesc.EditRate.Numerator;
p_sys->frame_rate_denom = PicDesc.EditRate.Denominator;
p_sys->frames_total = PicDesc.ContainerDuration;
break;
}
case ESS_MPEG2_VES: {
MPEG2::MXFReader *p_VideoMXFReader = p_sys->p_VideoMXFReader = new ( nothrow ) MPEG2::MXFReader();
MPEG2::VideoDescriptor VideoDesc;
if( !p_VideoMXFReader ) {
retval = VLC_ENOMEM;
goto error;
}
if( !ASDCP_SUCCESS( p_VideoMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
msg_Err( p_demux, "File %s could not be opened with ASDCP", p_sys->p_dcp->videofile.c_str() );
retval = VLC_EGENERIC;
goto error;
}
p_VideoMXFReader->FillVideoDescriptor( VideoDesc );
es_format_Init( &video_format, VIDEO_ES, VLC_CODEC_MPGV );
fillVideoFmt( &video_format.video, VideoDesc.StoredWidth, VideoDesc.StoredHeight,
VideoDesc.EditRate.Numerator, VideoDesc.EditRate.Denominator );
p_sys->frame_rate_num = VideoDesc.EditRate.Numerator;
p_sys->frame_rate_denom = VideoDesc.EditRate.Denominator;
p_sys->frames_total = VideoDesc.ContainerDuration;
break;
}
default:
msg_Err( p_demux, "Unrecognized video format" );
retval = VLC_EGENERIC;
goto error;
}
if ( (p_sys->frame_rate_num == 0) || (p_sys->frame_rate_denom == 0) ) {
msg_Err(p_demux, "Invalid frame rate (%i/%i)",
p_sys->frame_rate_num, p_sys->frame_rate_denom);
retval = VLC_EGENERIC;
goto error;
}
if( ( p_sys->p_video_es = es_out_Add( p_demux->out, &video_format ) ) == NULL ) {
msg_Err( p_demux, "Failed to add video es" );
retval = VLC_EGENERIC;
goto error;
}
/* Open audio file */
EssenceType_t AudioEssType;
EssenceType( p_sys->p_dcp->audiofile.c_str(), AudioEssType );
if ( (AudioEssType == ESS_PCM_24b_48k) || (AudioEssType == ESS_PCM_24b_96k) ) {
PCM::MXFReader *p_AudioMXFReader = new ( nothrow ) PCM::MXFReader();
PCM::AudioDescriptor AudioDesc;
if( !p_AudioMXFReader ) {
retval = VLC_ENOMEM;
goto error;
}
if( !ASDCP_SUCCESS( p_AudioMXFReader->OpenRead( p_sys->p_dcp->audiofile.c_str() ) ) ) {
msg_Err( p_demux, "File %s could not be opened with ASDCP",
p_sys->p_dcp->audiofile.c_str() );
retval = VLC_EGENERIC;
delete p_AudioMXFReader;
goto error;
}
p_AudioMXFReader->FillAudioDescriptor( AudioDesc );
if ( (AudioDesc.ChannelCount >= sizeof(pi_channels_aout)/sizeof(uint32_t *))
|| (pi_channels_aout[AudioDesc.ChannelCount] == NULL) ){
msg_Err(p_demux, " DCP module does not support %i channels",
AudioDesc.ChannelCount);
retval = VLC_EGENERIC;
delete p_AudioMXFReader;
goto error;
} else {
es_format_Init( &audio_format, AUDIO_ES, VLC_CODEC_S24L );
if( AudioDesc.AudioSamplingRate.Denominator != 0 )
audio_format.audio.i_rate =
AudioDesc.AudioSamplingRate.Numerator
/ AudioDesc.AudioSamplingRate.Denominator;
else if ( AudioEssType == ESS_PCM_24b_96k )
audio_format.audio.i_rate = 96000;
else
audio_format.audio.i_rate = 48000;
p_sys->i_audio_buffer = PCM::CalcFrameBufferSize(AudioDesc);
if (p_sys->i_audio_buffer == 0) {
msg_Err( p_demux, "Failed to get audio buffer size" );
retval = VLC_EGENERIC;
delete p_AudioMXFReader;
goto error;
}
audio_format.audio.i_bitspersample = AudioDesc.QuantizationBits;
audio_format.audio.i_blockalign = AudioDesc.BlockAlign;
audio_format.audio.i_channels =
p_sys->i_channels = AudioDesc.ChannelCount;
/* Manage channel orders */
p_sys->i_chans_to_reorder = aout_CheckChannelReorder(
pi_channels_aout[AudioDesc.ChannelCount], NULL,
i_channel_mask[AudioDesc.ChannelCount], p_sys->pi_chan_table );
if( ( p_sys->p_audio_es = es_out_Add( p_demux->out, &audio_format ) ) == NULL ) {
msg_Err( p_demux, "Failed to add audio es" );
retval = VLC_EGENERIC;
delete p_AudioMXFReader;
goto error;
}
p_sys->p_AudioMXFReader = p_AudioMXFReader;
}
} else {
msg_Err( p_demux, "The file %s is not a supported AS_DCP essence container",
p_sys->p_dcp->audiofile.c_str() );
retval = VLC_EGENERIC;
goto error;
}
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
return VLC_SUCCESS;
error:
CloseDcpAndMxf( p_demux );
return retval;
}
/*****************************************************************************
* Close: module destroy function
*****************************************************************************/
static inline void Close( vlc_object_t *obj )
{
demux_t *p_demux = ( demux_t* ) obj;
CloseDcpAndMxf( p_demux );
}
/*****************************************************************************
* Demux: DCP Demuxing function
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
block_t *p_video_frame = NULL, *p_audio_frame = NULL;
uint32_t i = p_sys->frame_no;
PCM::FrameBuffer AudioFrameBuff( p_sys->i_audio_buffer);
if( i == p_sys->frames_total )
return 0;
/* video frame */
switch( p_sys->PictureEssType )
{
case ESS_JPEG_2000:
case ESS_JPEG_2000_S:{
JP2K::FrameBuffer PicFrameBuff(FRAME_BUFFER_SIZE);
if ( ( p_video_frame = block_Alloc( FRAME_BUFFER_SIZE )) == NULL )
goto error;
if ( ! ASDCP_SUCCESS(
PicFrameBuff.SetData(p_video_frame->p_buffer, FRAME_BUFFER_SIZE)) )
goto error_asdcp;
if ( p_sys->PictureEssType == ESS_JPEG_2000_S ) {
if ( ! ASDCP_SUCCESS(
p_sys->p_PicMXFSReader->ReadFrame(i + p_sys->p_dcp->i_video_entry, JP2K::SP_LEFT, PicFrameBuff, 0, 0)) ) {
PicFrameBuff.SetData(0,0);
goto error_asdcp;
}
} else {
if ( ! ASDCP_SUCCESS(
p_sys->p_PicMXFReader->ReadFrame(i + p_sys->p_dcp->i_video_entry, PicFrameBuff, 0, 0)) ) {
PicFrameBuff.SetData(0,0);
goto error_asdcp;
}
}
p_video_frame->i_buffer = PicFrameBuff.Size();
break;
}
case ESS_MPEG2_VES: {
MPEG2::FrameBuffer VideoFrameBuff(FRAME_BUFFER_SIZE);
if ( ( p_video_frame = block_Alloc( FRAME_BUFFER_SIZE )) == NULL )
goto error;
if ( ! ASDCP_SUCCESS(
VideoFrameBuff.SetData(p_video_frame->p_buffer, FRAME_BUFFER_SIZE)) )
goto error_asdcp;
if ( ! ASDCP_SUCCESS(
p_sys->p_VideoMXFReader->ReadFrame(i + p_sys->p_dcp->i_video_entry, VideoFrameBuff, 0, 0)) ) {
VideoFrameBuff.SetData(0,0);
goto error_asdcp;
}
p_video_frame->i_buffer = VideoFrameBuff.Size();
break;
}
default:
msg_Err( p_demux, "Unrecognized video format" );
goto error;
}
p_video_frame->i_length = CLOCK_FREQ * p_sys->frame_rate_denom / p_sys->frame_rate_num;
p_video_frame->i_pts = CLOCK_FREQ * p_sys->frame_no * p_sys->frame_rate_denom / p_sys->frame_rate_num;
/* audio frame */
if ( ( p_audio_frame = block_Alloc( p_sys->i_audio_buffer )) == NULL ) {
goto error;
}
if ( ! ASDCP_SUCCESS(
AudioFrameBuff.SetData(p_audio_frame->p_buffer, p_sys->i_audio_buffer)) ) {
goto error_asdcp;
}
if ( ! ASDCP_SUCCESS(
p_sys->p_AudioMXFReader->ReadFrame(i + p_sys->p_dcp->i_audio_entry, AudioFrameBuff, 0, 0)) ) {
AudioFrameBuff.SetData(0,0);
goto error_asdcp;
}
if( p_sys->i_chans_to_reorder )
aout_ChannelReorder( p_audio_frame->p_buffer, p_audio_frame->i_buffer,
p_sys->i_channels,
p_sys->pi_chan_table, VLC_CODEC_S24L );
p_audio_frame->i_buffer = AudioFrameBuff.Size();
p_audio_frame->i_length = CLOCK_FREQ * p_sys->frame_rate_denom / p_sys->frame_rate_num;
p_audio_frame->i_pts = CLOCK_FREQ * p_sys->frame_no * p_sys->frame_rate_denom / p_sys->frame_rate_num;
/* Video is the main pts */
if ( p_audio_frame->i_pts != p_video_frame->i_pts ) {
msg_Err( p_demux, "Audio and video frame pts are not in sync" );
}
p_sys->i_pts = p_video_frame->i_pts;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pts );
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 );
p_sys->frame_no++;
return 1;
error_asdcp:
msg_Err( p_demux, "Couldn't read frame with ASDCP");
error:
if (p_video_frame)
block_Release(p_video_frame);
if (p_audio_frame)
block_Release(p_audio_frame);
return -1;
}
/*****************************************************************************
* Control: handle the controls
*****************************************************************************/
static int Control( demux_t *p_demux, int query, va_list args )
{
double f,*pf;
bool *pb;
int64_t *pi64, i64;
demux_sys_t *p_sys = p_demux->p_sys;
switch ( query )
{
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_CONTROL_PACE:
pb = ( bool* ) va_arg ( args, bool* );
*pb = true;
break;
case DEMUX_CAN_SEEK:
pb = (bool *)va_arg( args, bool * );
if( p_sys->PictureEssType != ESS_MPEG2_VES )
*pb = true;
else
*pb = false;
break;
case DEMUX_GET_POSITION:
pf = ( double* ) va_arg ( args, double* );
if( p_sys->frames_total != 0 )
*pf = (double) p_sys->frame_no / (double) p_sys->frames_total;
else {
msg_Warn( p_demux, "Total number of frames is 0" );
*pf = 0.0;
}
break;
case DEMUX_SET_POSITION:
f = ( double ) va_arg ( args, double );
p_sys->frame_no = (int) ( f * p_sys->frames_total );
break;
case DEMUX_GET_LENGTH:
pi64 = ( int64_t* ) va_arg ( args, int64_t* );
*pi64 = ( p_sys->frames_total * p_sys->frame_rate_denom / p_sys->frame_rate_num ) * CLOCK_FREQ;
break;
case DEMUX_GET_TIME:
pi64 = ( int64_t* ) va_arg ( args, int64_t* );
*pi64 = p_sys->i_pts >= 0 ? p_sys->i_pts : 0;
break;
case DEMUX_SET_TIME:
i64 = ( int64_t ) va_arg ( args, int64_t );
msg_Warn( p_demux, "DEMUX_SET_TIME" );
p_sys->frame_no = i64 * p_sys->frame_rate_num / ( CLOCK_FREQ * p_sys->frame_rate_denom );
p_sys->i_pts= i64;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pts);
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, ( mtime_t ) i64 );
break;
default:
msg_Warn( p_demux, "Unknown query %d in DCP Control", query );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* Low-level functions : string manipulation, free function, etc
*****************************************************************************/
/**
* Function to fill video_format_t fields for an elementary stream
* @param fmt video format structure
* @param width picture width
* @param height picture height
* @param frame_rate_num video frame rate numerator
* @param frame_rate_denom video frame rate denominator
*/
static inline void fillVideoFmt( video_format_t * fmt, unsigned int width, unsigned int height, unsigned int frame_rate_num, unsigned int frame_rate_denom )
{
fmt->i_width = width;
fmt->i_height = height;
/* As input are square pixels let VLC or decoder fix SAR, origin,
* and visible area */
fmt->i_frame_rate = frame_rate_num;
fmt->i_frame_rate_base = frame_rate_denom;
}
/**
* Function to free memory in case of error or when closing the module
* @param p_demux DCP access-demux
*/
void CloseDcpAndMxf( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
/* close the files */
switch( p_sys->PictureEssType )
{
case ESS_UNKNOWN:
break;
case ESS_JPEG_2000:
if( p_sys->p_PicMXFReader )
p_sys->p_PicMXFReader->Close();
break;
case ESS_JPEG_2000_S:
if( p_sys->p_PicMXFSReader )
p_sys->p_PicMXFSReader->Close();
break;
case ESS_MPEG2_VES:
if( p_sys->p_VideoMXFReader )
p_sys->p_VideoMXFReader->Close();
break;
default:
break;
}
if( p_sys->p_AudioMXFReader )
p_sys->p_AudioMXFReader->Close();
delete( p_demux->p_sys );
}
/*****************************************************************************
* DCP init
*****************************************************************************/
/**
* Function to handle the operations with the DCP directory.
* @param p_demux Demux pointer.
* @return Integer according to the success or not of the process.
*/
int dcpInit ( demux_t *p_demux )
{
int retval;
demux_sys_t *p_sys = p_demux->p_sys;
dcp_t *p_dcp = p_sys->p_dcp;
p_dcp->path = p_demux->psz_file;
/* Add a '/' in end of path if needed */
if ( *(p_dcp->path).rbegin() != '/')
p_dcp->path.append( "/" );
/* Parsing XML files to get audio and video files */
msg_Dbg( p_demux, "parsing XML files..." );
if( ( retval = parseXML( p_demux ) ) )
return retval;
msg_Dbg(p_demux, "parsing XML files done");
#ifndef NDEBUG
msg_Dbg( p_demux, "path = %s", p_sys->p_dcp->path.c_str() );
msg_Dbg( p_demux, "video = %s", p_sys->p_dcp->videofile.c_str() );
msg_Dbg( p_demux, "audio = %s", p_sys->p_dcp->audiofile.c_str() );
#endif
return VLC_SUCCESS;
}
/*****************************************************************************
* functions for XML parsing
*****************************************************************************/
/**
* Function to retrieve the path to the ASSETMAP file.
* @param p_demux DCP access_demux.
*/
static string assetmapPath( demux_t * p_demux )
{
DIR *dir = NULL;
struct dirent *ent = NULL;
dcp_t *p_dcp = p_demux->p_sys->p_dcp;
string result;
if( ( dir = opendir (p_dcp->path.c_str() ) ) != NULL )
{
/* print all the files and directories within directory */
while( ( ent = readdir ( dir ) ) != NULL )
{
if( strcasecmp( "assetmap", ent->d_name ) == 0 || strcasecmp( "assetmap.xml", ent->d_name ) == 0 )
{
/* copy of "path" in "res" */
result = p_dcp->path;
result.append( ent->d_name );
break;
}
}
closedir( dir );
}
else
msg_Err( p_demux, "Could not open the directory: %s", p_dcp->path.c_str() );
/* if no assetmap file */
if( result.empty() )
msg_Err( p_demux, "No ASSETMAP found in the directory: %s", p_dcp->path.c_str() );
return result;
}
/**
* Function which parses XML files in DCP directory in order to get video and audio files
* @param p_demux Demux pointer.
* @return Integer according to the success or not of the operation
*/
int parseXML ( demux_t * p_demux )
{
int retval;
string assetmap_path = assetmapPath( p_demux );
/* We get the ASSETMAP file path */
if( assetmap_path.empty() )
return VLC_EGENERIC;
/* We parse the ASSETMAP File in order to get CPL File path, PKL File path
and to store UID/Path of all files in DCP directory (except ASSETMAP file) */
AssetMap *assetmap = new (nothrow) AssetMap( p_demux, assetmap_path, p_demux->p_sys->p_dcp );
if( ( retval = assetmap->Parse() ) )
return retval;
delete assetmap;
return VLC_SUCCESS; /* TODO : perform checking on XML parsing */
}
/*****************************************************************************
* Copyright (C) 2013 VLC authors and VideoLAN
*
* Authors:
* Nicolas Bertrand <nico@isf.cc>
* Jean-Baptiste Kempf <jb@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* @file dcpparser.cpp
* @brief Parsing of DCP XML files
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* VLC core API headers */
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_xml.h>
#include <vlc_url.h>
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include "dcpparser.h"
using namespace std;
static int ReadNextNode(xml_reader_t *p_xmlReader, string& p_node) {
const char * c_node;
int i;
i = xml_ReaderNextNode( p_xmlReader, &c_node );
p_node = c_node;
return i;
}
static int ReadEndNode( xml_reader_t *p_xmlReader, string p_node,
int p_type, string &s_value) {
string node;
if ( xml_ReaderIsEmptyElement( p_xmlReader) )
return 0;
if (p_type != XML_READER_STARTELEM)
return -1;
if (ReadNextNode(p_xmlReader, node) == XML_READER_TEXT)
{
s_value = node;
if((ReadNextNode(p_xmlReader, node) == XML_READER_ENDELEM) &&
node == p_node)
return 0;
}
return -1;
}
typedef enum {
CHUNK_UNKNOWN = 0,
CHUNK_PATH,
CHUNK_VOL_INDEX,
CHUNK_OFFSET,
CHUNK_LENGTH
} ChunkTag_t;
typedef enum {
ASSET_UNKNOWN = 0,
ASSET_ID,
ASSET_ANNOTATION_TEXT,
ASSET_PACKING_LIST,
ASSET_CHUNK_LIST,
ASSET_HASH,
ASSET_SIZE,
ASSET_TYPE,
ASSET_ORIGINAL_FILENAME
} AssetTag_t;
static const string g_asset_names[] = {
"Id",
"AnnotationText",
"PackingList",
"ChunkList",
"Hash",
"Size",
"Type",
"OriginalFileName"
};
typedef enum {
PKL_UNKNOWN = 0,
PKL_ID,
PKL_ISSUE_DATE,
PKL_ISSUER,
PKL_CREATOR,
PKL_ASSET_LIST,
PKL_ANNOTATION_TEXT, /* start of optional tags */
PKL_ICON_ID,
PKL_GROUP_ID,
PKL_SIGNER,
PKL_SIGNATURE,
} PKLTag_t;
typedef enum {
CPL_UNKNOWN = 0,
CPL_ID,
CPL_ANNOTATION_TEXT, /* optional */
CPL_ICON_ID, /* optional */
CPL_ISSUE_DATE,
CPL_ISSUER, /* optional */
CPL_CREATOR, /* optional */
CPL_CONTENT_TITLE,
CPL_CONTENT_KIND,
CPL_CONTENT_VERSION, /* not optional, but not always present*/
CPL_RATING_LIST, /* not handled */
CPL_REEL_LIST,
CPL_SIGNER, /* optional - not handled */
CPL_SIGNATURE /* optional - not handled */
} CPLTag_t;
class ChunkList: public std::list<Chunk> {
public :
ChunkList();
~ChunkList();
};
/*
* Chunk Class
*/
int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
string node;
int type;
string s_value;
static const string names[] = {"Path", "VolumeIndex", "Offset",
"Length"};
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "Chunk")
return -1;
/* loop on Chunks Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
{
ChunkTag_t chunk_tag = CHUNK_UNKNOWN;
for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
if( node == names[i-1]) {
chunk_tag = i;
if ( ReadEndNode(p_xmlReader, node, type, s_value))
return -1;
switch (chunk_tag) {
case CHUNK_PATH:
this->s_path = s_value;
break;
case CHUNK_VOL_INDEX:
this->i_vol_index = atoi(s_value.c_str());
break;
case CHUNK_OFFSET:
this->i_offset = atoi(s_value.c_str());
break;
case CHUNK_LENGTH:
this->i_length = atoi(s_value.c_str());
break;
case CHUNK_UNKNOWN:
default:
break;
}
/* break the for loop as a tag is found*/
break;
}
}
if(chunk_tag == CHUNK_UNKNOWN)
return -1;
break;
}
case XML_READER_TEXT:
s_value = node;
if (unlikely(node.empty()))
return -1;
break;
case XML_READER_ENDELEM:
/* Verify if we reach Chuk endelem */
if ( node == p_node) {
/* verify path */
if ( this->s_path.empty() ) {
msg_Err(this->p_demux, "Chunk::Parse No path found");
return -1;
}
if ( this->i_vol_index != 1 ) {
msg_Err(this->p_demux, "Only one VOLINDEX suported. Patch welcome.");
return -1;
}
/* end of chunk tag parse */
return 0;
}
break;
}
}
return -1;
}
/*
* AssetMap Class
*/
AssetMap::~AssetMap() { }
int AssetMap::Parse ( )
{
int type = 0;
string node;
CPL *cpl;
Reel *reel;
PKL *pkl;
AssetList *_p_asset_list = NULL;
vector<string> pklfiles;
/* init XML parser */
if( this->OpenXml() ) {
msg_Err( p_demux, "Failed to initialize Assetmap XML parser" );
return -1;
}
/* reading ASSETMAP file to get the asset_list */
msg_Dbg( p_demux, "reading ASSETMAP file..." );
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
_p_asset_list = new (nothrow) AssetList();
if ( unlikely(_p_asset_list == NULL) ) {
this->CloseXml();
return -1;
}
p_dcp->p_asset_list = _p_asset_list;
if (this->ParseAssetList(p_xmlReader, node, type )) {
this->CloseXml();
return -1;
}
/* asset list found so break*/
break;
}
}
/* Look for PKLs path */
if ( (_p_asset_list == NULL) || (_p_asset_list->size() == 0) ) {
msg_Err( p_demux, "Asset list empty" );
this->CloseXml();
return -1;
}
for (AssetList::iterator iter = _p_asset_list->begin();
iter != _p_asset_list->end() ; ++iter) {
string s_filepath;
s_filepath = (*iter)->getPath();
if (s_filepath.empty()) {
msg_Err( p_demux, "No path element for asset" );
continue;
}
/* set an absolute file path */
s_filepath = p_dcp->path + s_filepath;
/* case of packing list */
if ((*iter)->isPackingList()) {
pklfiles.push_back( s_filepath );
}
}
/* TODO: case of only on PKL managed.
* Future work needed for managing severals
* the PKL vector will be used to select the required PKL */
if( (pklfiles.size() == 0) || (pklfiles[0].empty()) )
{
msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
this->CloseXml();
return -1;
}
/* Create the first PKL */
pkl = new (nothrow) PKL(p_demux, pklfiles[0], _p_asset_list, p_dcp->path);
if ( unlikely(pkl == NULL) ) {
this->CloseXml();
return -1;
}
if (pkl->Parse()) {
delete pkl;
this->CloseXml();
return -1;
}
p_dcp->pkls.push_back( pkl );
/* Now, CPL */
if ( pkl->FindCPLs() <= 0 ) {
msg_Err(p_demux, " No CPL found");
this->CloseXml();
return -1;
}
/* TODO: Only one CPL managed.
* Future work needed for managing severals
*/
cpl = pkl->getCPL(0);
if( cpl == NULL ) {
msg_Err(p_demux, " No CPL found");
this->CloseXml();
return -1;
}
if ( cpl->Parse() ) {
this->CloseXml();
return -1;
}
/*TODO : case of 1st reel only managed */
reel = cpl->getReel(0);
Asset *asset;
/* Get picture */
asset = reel->getTrack(TRACK_PICTURE);
msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
p_dcp->videofile = p_dcp->path + asset->getPath();
p_dcp->i_video_entry = asset->getEntryPoint();
/* Get audio */
asset = reel->getTrack(TRACK_SOUND);
msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
p_dcp->audiofile = p_dcp->path + asset->getPath();
p_dcp->i_audio_entry = asset->getEntryPoint();
/* free memory */
this->CloseXml();
return VLC_SUCCESS;
}
/*
* Asset Class
*/
Asset::~Asset() {}
int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
{
string node;
int type;
string s_value;
const string s_root_node = "Asset";
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != s_root_node)
return -1;
/* loop on Assets Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
{
AssetTag_t _tag = ASSET_UNKNOWN;
for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
if( node == g_asset_names[i-1]) {
_tag = i;
switch(_tag) {
/* Case of complex nodes */
case ASSET_PACKING_LIST:
/* case of <PackinkList/> tag, bur not compliant with SMPTE-429-9 2007*/
if (xml_ReaderIsEmptyElement( p_xmlReader))
this->b_is_packing_list = true;
else if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
if ( s_value == "true" )
this->b_is_packing_list = true;
break;
case ASSET_CHUNK_LIST:
if ( this->parseChunkList(p_xmlReader, node, type ) )
return -1;
this->s_path = this->chunk_vec[0].getPath();
break;
case ASSET_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->s_id = s_value;
break;
case ASSET_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->s_annotation = s_value;
break;
case ASSET_ORIGINAL_FILENAME:
case ASSET_HASH:
case ASSET_TYPE:
case ASSET_SIZE:
/* Asset tags not in AssetMap */
break;
}
/* break the for loop as a tag is found*/
break;
}
}
if( _tag == ASSET_UNKNOWN )
return -1;
break;
}
case XML_READER_TEXT:
return -1;
case XML_READER_ENDELEM:
if ( node != s_root_node) {
msg_Err(this->p_demux,
"Something goes wrong in Asset parsing on Assetmap (node %s)", node.c_str());
return -1;
} else {
/*Check Presence of Id and Chunklist */
if ( this->s_id.empty() ) {
msg_Err(this->p_demux, " No Id element found in Asset");
return -1;
}
if ( this->s_path.empty() ) {
msg_Err(this->p_demux, " No path element found in Asset");
return -1;
}
return 0;
}
break;
}
}
return -1;
}
int Asset::ParsePKL( xml_reader_t *p_xmlReader)
{
string node;
int type;
string s_value;
const string s_root_node = "Asset";
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
{
AssetTag_t _tag = ASSET_UNKNOWN;
for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
if( node == g_asset_names[i-1]) {
_tag = i;
switch(_tag) {
case ASSET_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
if ( this->s_annotation.empty() )
this->s_annotation = s_value;
else
this->s_annotation = this->s_annotation + "--" + s_value;
break;
case ASSET_HASH:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->s_hash = s_value;
break;
case ASSET_SIZE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->ui_size = atol(s_value.c_str());
break;
case ASSET_TYPE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->s_type = s_value;
break;
case ASSET_ORIGINAL_FILENAME:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
return -1;
this->s_original_filename = s_value;
break;
case ASSET_ID: /* already verified */
case ASSET_PACKING_LIST:
case ASSET_CHUNK_LIST:
/* Asset tags not in PKL */
break;
}
/* break the for loop as a tag is found*/
break;
}
}
if( _tag == ASSET_UNKNOWN )
return -1;
break;
}
case XML_READER_TEXT:
return -1;
case XML_READER_ENDELEM:
if ( node != s_root_node) {
msg_Err(this->p_demux,
"Something goes wrong in Asset parsing on PKL (node %s)", node.c_str());
return -1;
} else {
/* Verify that mandatory attributes are filled */
if (this->s_hash.empty()) {
msg_Err(this->p_demux,"Asset Hash tag invalid");
return -1;
}
if (this->ui_size == 0) {
msg_Err(this->p_demux,"Asset Size tag invalid");
return -1;
}
if (this->s_type.empty()) {
msg_Err(this->p_demux,"Asset Type tag invalid");
return -1;
}
return 0;
}
break;
}
}
return -1;
}
void Asset::Dump()
{
msg_Dbg(this->p_demux,"Id = %s", this->s_id.c_str());
msg_Dbg(this->p_demux,"Path = %s", this->s_path.c_str());
msg_Dbg(this->p_demux,"Is PKL = %s", this->b_is_packing_list ? "True" : "False");
msg_Dbg(this->p_demux,"Hash = %s", this->s_hash.c_str());
msg_Dbg(this->p_demux,"Size = %i", this->ui_size);
msg_Dbg(this->p_demux,"Type = %s", this->s_type.c_str());
msg_Dbg(this->p_demux,"OrignalFileName = %s", this->s_original_filename.c_str());
msg_Dbg(this->p_demux,"AnnotationText = %s", this->s_annotation.c_str());
}
int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
{
string node;
int type;
string s_value;
std::vector<Chunk> chunk_vec;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "ChunkList" )
return -1;
/* loop on Assets Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
{
Chunk chunk(this->p_demux);
if (node != "Chunk" )
return -1;
if ( chunk.Parse(p_xmlReader, node, type) )
return -1;
chunk_vec.push_back(chunk);
break;
}
case XML_READER_ENDELEM:
if ( node == p_node) {
if (chunk_vec.size() != 1 ) {
msg_Err(this->p_demux, "chunklist of size greater than one not supported");
return -1;
}
this->chunk_vec = chunk_vec;
return 0;
}
break;
}
}
return -1;
}
int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
{
string node;
int type;
Asset *asset;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "AssetList" )
return -1;
/* loop on AssetList nodes */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
if (node != "Asset" )
return -1;
asset = new (nothrow) Asset(this->p_demux);
if ( unlikely(asset == NULL) )
return -1;
if (asset->Parse(p_xmlReader, node, type)){
delete asset;
return -1;
}
p_dcp->p_asset_list->push_back(asset);
break;
case XML_READER_ENDELEM:
if (node == p_node )
return 0;
break;
default:
case XML_READER_TEXT:
msg_Err(this->p_demux, "Error parsing AssetList in AssetMap");
return -1;
}
}
return -1;
}
Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
{
AssetList::iterator index = asset_list->begin() ;
for (index = asset_list->begin(); index != asset_list->end(); ++index)
if ((*index)->getId() == p_id )
return *index;
return NULL;
}
/*
* XmlFile Class
*/
XmlFile::~XmlFile() {}
int XmlFile::OpenXml()
{
char *psz_uri;
this->p_xml = xml_Create( this->p_demux );
if (! this->p_xml) {
return -1;
}
psz_uri = vlc_path2uri( this->s_path.c_str(), "file" );
this->p_stream = stream_UrlNew(this->p_demux, psz_uri );
free(psz_uri);
if( ! this->p_stream ) {
xml_Delete(this->p_xml );
return -1;
}
this->p_xmlReader = xml_ReaderCreate( this->p_xml, this->p_stream);
if( ! this->p_xmlReader ) {
stream_Delete( this->p_stream );
xml_Delete(this->p_xml );
return -1;
}
return 0;
}
void XmlFile::CloseXml() {
if( this->p_stream )
stream_Delete( this->p_stream );
if( this->p_xmlReader )
xml_ReaderDelete( this->p_xmlReader );
if( this->p_xml )
xml_Delete( this->p_xml );
}
/*
* PKL Class
*/
PKL::PKL(demux_t * p_demux, string s_path, AssetList *_asset_list, string s):
XmlFile(p_demux, s_path),
asset_list(_asset_list), s_dcp_path(s)
{
type = XML_PKL;
}
PKL::~PKL() {
vlc_delete_all(vec_cpl);
}
int PKL::Parse()
{
string node;
int type;
string s_value;
const string s_root_node = "PackingList";
static const string names[] = {
"Id",
"IssueDate",
"Issuer",
"Creator",
"AssetList",
"AnnotationText",
"IconId",
"GroupId",
"Signer",
"ds:Signature"
};
if (this->OpenXml())
return -1;
/* read 1st node and verify that is a PKL*/
if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) &&
(node == s_root_node) ) ) {
msg_Err( this->p_demux, "Not a valid XML Packing List");
goto error;
}
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM: {
PKLTag_t _tag = PKL_UNKNOWN;
for(PKLTag_t i = PKL_ID; i <= PKL_SIGNATURE; i = PKLTag_t(i+1)) {
if( node == names[i-1]) {
_tag = i;
switch (_tag) {
/* case for parsing non terminal nodes */
case PKL_ASSET_LIST:
if ( this->ParseAssetList(node, type) )
goto error;
break;
case PKL_SIGNER:
if ( this->ParseSigner(node, type) )
goto error;
break;
case PKL_SIGNATURE:
if ( this->ParseSignature(node, type) )
goto error;
break;
/* Parse simple/end nodes */
case PKL_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_id = s_value;
break;
case PKL_ISSUE_DATE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_issue_date = s_value;
break;
case PKL_ISSUER:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_issuer = s_value;
break;
case PKL_CREATOR:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_creator = s_value;
break;
case PKL_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_annotation = s_value;
break;
case PKL_ICON_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_icon_id = s_value;
break;
case PKL_GROUP_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_group_id = s_value;
break;
}
/* break the for loop as a tag is found*/
break;
}
}
if( _tag == PKL_UNKNOWN )
goto error;
break;
}
case XML_READER_TEXT:
goto error;
case XML_READER_ENDELEM:
if ( node != s_root_node) {
msg_Err(this->p_demux,
"Something goes wrong in PKL parsing (node %s)", node.c_str());
goto error;
}
break;
}
}
/* TODO verify presence of mandatory fields*/
/* Close PKL XML*/
this->CloseXml();
return 0;
error:
msg_Err( this->p_demux, "PKL parsing failed");
this->CloseXml();
return -1;
}
int PKL::FindCPLs()
{
if ( this->vec_cpl.size() != 0 ) {
msg_Err(this->p_demux, "CPLs already checked");
return -1;
}
for (AssetList::iterator index = this->asset_list->begin();
index != this->asset_list->end(); ++index) {
Asset *asset = *index;
if ( asset->getType().find("text/xml") == string::npos) {
/* not an xml file */
continue;
}
CPL *cpl = new (nothrow) CPL(this->p_demux,
this->s_dcp_path + asset->getPath(),
this->asset_list);
if ( unlikely(cpl == NULL) )
return -1;
if ( cpl->IsCPL() )
/* CPL Found */
this->vec_cpl.push_back(cpl);
else
delete cpl;
}
return this->vec_cpl.size();
}
int PKL::ParseAssetList(string p_node, int p_type) {
string node;
int type;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "AssetList")
return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
if( node =="Asset") {
if ( this->ParseAsset(node, type) )
return -1;
} else
return -1;
break;
case XML_READER_ENDELEM:
if ( node == p_node) {
/* parse of chunklist finished */
goto end;
}
break;
}
}
end:
return 0;
}
int PKL::ParseAsset(string p_node, int p_type) {
string node;
int type;
string s_value;
Asset *asset = NULL;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "Asset")
return -1;
/* 1st node shall be Id" */
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
return -1;
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
if (type == XML_READER_TEXT) {
s_value = node;
if (unlikely(node.empty()))
return -1;
}
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
if (type == XML_READER_ENDELEM) {
asset = AssetMap::getAssetById(this->asset_list, s_value);
if (asset == NULL)
return -1;
}
if (asset == NULL)
return -1;
if ( asset->ParsePKL(this->p_xmlReader) )
return -1;
return 0;
}
int PKL::ParseSigner(string p_node, int p_type)
{
string node;
int type;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "Signer")
return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just parse until end of Signer node */
if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0;
}
msg_Err(this->p_demux, "Parse of Signer finished bad");
return -1;
}
int PKL::ParseSignature(string p_node, int p_type)
{
string node;
int type;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "ds:Signature")
return -1;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just parse until end of Signature node */
if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0;
}
msg_Err(this->p_demux, "Parse of Signature finished bad");
return -1;
}
/*
* Reel Class
*/
int Reel::Parse(string p_node, int p_type) {
string node;
int type;
string s_value;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "Reel")
return -1;
while (( type = ReadNextNode(this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
if (node =="Id") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
this->s_id = s_value;
} else if (node == "AnnotationText") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
this->s_annotation = s_value;
} else if ( node =="AssetList" ) {
if (this->ParseAssetList(node, type))
return -1;
} else {
/* unknown tag */
msg_Err(this->p_demux, "Reel::Parse, unknown tag:%s", node.c_str());
return -1;
}
break;
case XML_READER_TEXT:
/* Error */
msg_Err(this->p_demux, "Reel parsing error");
return -1;
case XML_READER_ENDELEM:
/* verify correctness of end node */
if ( node == p_node) {
/* TODO : verify Reel id */
return 0;
}
break;
}
}
return -1;
}
Asset * Reel::getTrack(TrackType_t e_track)
{
switch (e_track) {
case TRACK_PICTURE:
return this->p_picture_track;
case TRACK_SOUND:
return this->p_sound_track;
case TRACK_SUBTITLE:
return this->p_subtitle_track;
case TRACK_UNKNOWN:
default:
break;
}
return NULL;
}
int Reel::ParseAssetList(string p_node, int p_type) {
string node;
int type;
string s_value;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "AssetList")
return -1;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM:
if (node =="MainPicture") {
if ( this->ParseAsset(node, type, TRACK_PICTURE) )
return -1;
} else if (node =="MainSound") {
if ( this->ParseAsset(node, type, TRACK_SOUND) )
return -1;
} else if (node =="MainSubtitle") {
if ( this->ParseAsset(node, type, TRACK_SUBTITLE) )
return -1;
} else {
/* unknown tag */
msg_Err(this->p_demux, "Reel::ParseAssetList, unknown tag:%s", node.c_str());
return -1;
}
break;
case XML_READER_TEXT:
/* Parsing error */
msg_Err(this->p_demux, "AssetList parsing error");
return -1;
case XML_READER_ENDELEM:
/* verify correctness of end node */
if ( node == p_node) {
/* TODO : verify id */
return 0;
}
break;
}
}
return -1;
}
int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
string node;
int type;
string s_value;
bool b_stop_parse = false;
Asset *asset = NULL;
if (p_type != XML_READER_STARTELEM)
return -1;
/* 1st node shall be Id */
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
return -1;
if ( ReadEndNode(this->p_xmlReader, node, type, s_value) )
return -1;
asset = AssetMap::getAssetById(this->p_asset_list, s_value);
if (asset == NULL)
return -1;
while( (! b_stop_parse) &&
(( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) ) {
switch (type) {
case XML_READER_STARTELEM:
if (node =="EditRate") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else if (node == "AnnotationText") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
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()));
} else if (node == "EntryPoint") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
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()));
} else if (node == "KeyId") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else if (node == "Hash") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else if (node == "FrameRate") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else if (node == "ScreenAspectRatio") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else if (node == "Language") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1;
} else {
/* unknown tag */
msg_Err(this->p_demux, "Reel::ParseAsset unknown tag:%s", node.c_str());
return -1;
}
break;
case XML_READER_TEXT:
/* impossible */
return -1;
break;
case XML_READER_ENDELEM:
/* verify correctness of end node */
if ( node == p_node) {
/* TODO : verify id */
b_stop_parse = true;
}
}
}
/* store by track */
switch (e_track) {
case TRACK_PICTURE:
this->p_picture_track = asset;
break;
case TRACK_SOUND:
this->p_sound_track = asset;
break;
case TRACK_SUBTITLE:
this->p_subtitle_track = asset;
break;
case TRACK_UNKNOWN:
default:
break;
}
return 0;
}
/*
* CPL Class
*/
CPL::CPL(demux_t * p_demux, string s_path, AssetList *_asset_list)
: XmlFile(p_demux, s_path), asset_list( _asset_list)
{
string node;
int type;
this->type = XML_UNKNOWN;
if (this->OpenXml()) {
msg_Err(this->p_demux, "Failed to open CPL XML file");
return;
}
/* read 1st node and verify that is a CPL */
if ( (type = ReadNextNode(p_xmlReader, node)) > 0) {
if ( (type == XML_READER_STARTELEM) && (node == "CompositionPlaylist") ) {
this->type = XML_CPL;
}
}
/* close xml */
this->CloseXml();
};
CPL::~CPL() {
vlc_delete_all(vec_reel);
}
int CPL::Parse()
{
string node;
int type;
string s_value;
const string s_root_node = "CompositionPlaylist";
static const string names[] = {
"Id",
"AnnotationText",
"IconId",
"IssueDate",
"Issuer",
"Creator",
"ContentTitleText",
"ContentKind",
"ContentVersion",
"RatingList",
"ReelList",
"Signer",
"ds:Signature"
};
if (this->OpenXml())
return -1;
/* read 1st node and verify that is a CPL*/
if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) &&
(node == s_root_node) ) ) {
msg_Err( this->p_demux, "Not a valid XML Packing List");
goto error;
}
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM: {
CPLTag_t _tag = CPL_UNKNOWN;
for(CPLTag_t i = CPL_ID; i <= CPL_SIGNATURE; i = CPLTag_t(i+1)) {
if( node == names[i-1]) {
_tag = i;
switch (_tag) {
/* case for parsing non terminal nodes */
case CPL_REEL_LIST:
if ( this->ParseReelList(node, type) )
goto error;
break;
case CPL_CONTENT_VERSION:
case CPL_SIGNER:
case CPL_SIGNATURE:
case CPL_RATING_LIST:
if ( this->DummyParse(node,type) )
goto error;
break;
/* Parse simple/end nodes */
case CPL_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_id = s_value;
break;
case CPL_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_annotation = s_value;
break;
case CPL_ICON_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_icon_id = s_value;
break;
case CPL_ISSUE_DATE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_issue_date= s_value;
break;
case CPL_ISSUER:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_issuer = s_value;
break;
case CPL_CREATOR:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_creator = s_value;
break;
case CPL_CONTENT_TITLE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_content_title = s_value;
break;
case CPL_CONTENT_KIND:
if ( ReadEndNode(p_xmlReader, node, type, s_value) )
goto error;
this->s_content_kind = s_value;
break;
}
/* break the for loop as a tag is found*/
break;
}
}
if( _tag == CPL_UNKNOWN )
goto error;
break;
}
case XML_READER_TEXT:
goto error;
case XML_READER_ENDELEM:
if ( node != s_root_node) {
msg_Err(this->p_demux,
"Something goes wrong in CKL parsing (node %s)", node.c_str());
goto error;
}
break;
}
}
/* TODO verify presence of mandatory fields*/
/* Close CPL XML*/
this->CloseXml();
return 0;
error:
this->CloseXml();
return -1;
}
int CPL::ParseReelList(string p_node, int p_type) {
string node;
int type;
if (p_type != XML_READER_STARTELEM)
return -1;
if( p_node != "ReelList")
return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) {
case XML_READER_STARTELEM: {
Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
if ( unlikely(p_reel == NULL) )
return -1;
if( node =="Reel") {
if ( p_reel->Parse(node, type) ) {
delete p_reel;
return -1;
}
} else {
delete p_reel;
return -1;
}
this->vec_reel.push_back(p_reel);
break;
}
case XML_READER_TEXT:
/* impossible */
break;
case XML_READER_ENDELEM:
if ( node == p_node)
return 0;
break;
}
}
return -1;
}
int CPL::DummyParse(string p_node, int p_type)
{
string node;
int type;
if (p_type != XML_READER_STARTELEM)
return -1;
if (xml_ReaderIsEmptyElement( this->p_xmlReader))
return 0;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just pase until end of input node */
if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0;
}
return -1;
}
/*****************************************************************************
* Copyright (C) 2013 VLC authors and VideoLAN
*
* Authors: Nicolas Bertrand <nico@isf.cc>
* Jean-Baptiste Kempf <jb@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* @file dcpparser.h
* @brief Parse DCP XML files
*/
#ifndef _DCPPARSER_H
#define _DCPPARSER_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* VLC core API headers */
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_plugin.h>
#include <iostream>
#include <string>
#include <list>
#include <vector>
using namespace std;
typedef enum {
TRACK_UNKNOWN = 0,
TRACK_PICTURE,
TRACK_SOUND,
TRACK_SUBTITLE
} TrackType_t;
typedef enum {
XML_UNKNOWN = 0,
XML_ASSETMAP,
XML_CPL,
XML_PKL,
XML_SUB,
} XmlType_t;
class Asset;
class AssetList: public std::list<Asset *> {};
class PKL;
/* This struct stores the most important information about the DCP */
struct dcp_t
{
string path; /* Path to DCP directory */
vector<PKL *> pkls;
AssetList *p_asset_list;
string videofile; /* Picture file name */
string audiofile; /* Sound file name */
int i_video_entry; /* Picture entry point */
int i_audio_entry; /* Sound entry point */
dcp_t():
p_asset_list(NULL),
i_video_entry(0),
i_audio_entry(0) {};
~dcp_t( ) {
vlc_delete_all(pkls);
if ( p_asset_list != NULL ) {
vlc_delete_all(*p_asset_list);
delete(p_asset_list);
}
}
};
class XmlFile
{
public:
XmlFile( demux_t * p_demux, string s_path):
p_demux(p_demux), s_path(s_path),
p_stream(NULL),
p_xml(NULL),
p_xmlReader(NULL),
type(XML_UNKNOWN) {}
virtual ~XmlFile( );
virtual int Parse() = 0;
bool IsCPL() { return type == XML_CPL; }
protected:
demux_t *p_demux;
string s_path;
stream_t *p_stream;
xml_t *p_xml;
xml_reader_t *p_xmlReader;
int OpenXml();
void CloseXml();
XmlType_t type;
};
class Chunk {
public:
Chunk(demux_t * demux):
i_vol_index(1), i_offset(0), i_length(0),
p_demux(demux) {};
int Parse(xml_reader_t *p_xmlReader, string p_node, int p_type);
string getPath() { return this->s_path; };
private:
string s_path;
int i_vol_index;
int i_offset;
int i_length;
demux_t *p_demux;
};
class Asset {
public:
/* Constructor */
Asset (demux_t * demux):
b_is_packing_list(false), ui_size(0),
i_intrisic_duration(0), i_entry_point(0), i_duration(0),
p_demux(demux) {}
virtual ~Asset() ;
void setId(string p_string ) { this->s_id = p_string; };
void setPath(string p_string) { this->s_path = p_string; };
void setAnnotation(string p_string) {
if (this->s_annotation.empty())
this->s_annotation = p_string;
else
this->s_annotation = this->s_annotation + "--" + p_string;
};
void setPackingList(bool p_bool) { this->s_path = p_bool; };
void setEntryPoint(int i_val) { this->i_entry_point = i_val; };
void setDuration (int i_val) { this->i_duration = i_val; };
void setIntrinsicDuration (int i_val) { this->i_intrisic_duration = i_val; };
string getId() const { return this->s_id; } ;
string getPath() const { return this->s_path; };
string getType() const { return this->s_type; };
int getEntryPoint() const { return this->i_entry_point; };
int getDuration() const { return this->i_duration; };
int getIntrinsicDuration() const { return this->i_intrisic_duration; };
bool isPackingList() const { return this->b_is_packing_list; };
int Parse( xml_reader_t *p_xmlReader, string node, int type);
int ParsePKL( xml_reader_t *p_xmlReader);
// TODO: remove
void Dump();
private:
string s_id;
string s_path;
string s_annotation;
bool b_is_packing_list;
string s_hash;
uint32_t ui_size;
string s_type;
string s_original_filename;
TrackType_t e_track_type;
string s_edit_rate;
int i_intrisic_duration;
int i_entry_point;
int i_duration;
/* encryption attribute */
string s_key_id;
/* Picture attributes */
string s_frame_rate;
string s_screen_aspect_ratio;
/* sound and subtitle */
string s_language;
demux_t *p_demux;
std::vector<Chunk> chunk_vec;
int parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type);
};
class Reel
{
public:
Reel(demux_t * demux, AssetList *asset_list, xml_reader_t *xmlReader)
: p_asset_list(asset_list), p_xmlReader(xmlReader), p_demux(demux)
{};
int Parse(string p_node, int p_type);
Asset * getTrack(TrackType_t e_track);
private:
AssetList *p_asset_list;
xml_reader_t *p_xmlReader;
demux_t *p_demux;
string s_id;
string s_annotation;
Asset *p_picture_track;
Asset *p_sound_track;
Asset *p_subtitle_track;
int ParseAssetList(string p_node, int p_type);
int ParseAsset(string p_node, int p_type, TrackType_t e_track);
};
class CPL : public XmlFile
{
public:
CPL(demux_t *, string, AssetList*);
~CPL();
virtual int Parse();
Reel *getReel(int pos) { return this->vec_reel[pos]; } ;
private :
AssetList *asset_list;
string s_id;
string s_annotation;
string s_icon_id;
string s_issue_date;
string s_issuer;
string s_creator;
string s_content_title;
string s_content_kind;
/* TODO: ContentVersion, RatingList, signer and signature */
std::vector<Reel *> vec_reel;
int DummyParse(string p_node, int p_type);
int ParseReelList(string p_node, int p_type);
};
class PKL : public XmlFile
{
public:
PKL ( demux_t * p_demux, string s_path, AssetList *asset_list,
string s_dcp_path);
~PKL();
virtual int Parse();
int FindCPLs();
CPL *getCPL(int pos) { return this->vec_cpl[pos]; };
private:
AssetList *asset_list;
string s_id;
string s_annotation;
string s_issue_date;
string s_issuer;
string s_creator;
string s_icon_id;
string s_group_id;
string s_dcp_path;
std::vector<CPL *> vec_cpl;
int ParseAssetList(string p_node, int p_type);
int ParseAsset(string p_node, int p_type);
int ParseSigner(string p_node, int p_type);
int ParseSignature(string p_node, int p_type);
};
class AssetMap : public XmlFile {
public:
AssetMap( demux_t * p_demux, string s_path, dcp_t *_p_dcp)
: XmlFile( p_demux, s_path ), p_dcp( _p_dcp) {};
~AssetMap();
static Asset * getAssetById(AssetList*, const string p_id);
virtual int Parse();
private:
dcp_t *p_dcp;
int ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type);
};
#endif /* _DCPPARSER_H */
...@@ -185,6 +185,7 @@ modules/access/bd/bd.c ...@@ -185,6 +185,7 @@ modules/access/bd/bd.c
modules/access/bluray.c modules/access/bluray.c
modules/access/cdda.c modules/access/cdda.c
modules/access/dc1394.c modules/access/dc1394.c
modules/access/dcp/dcp.cpp
modules/access/decklink.cpp modules/access/decklink.cpp
modules/access/directory.c modules/access/directory.c
modules/access/dshow/crossbar.cpp modules/access/dshow/crossbar.cpp
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment