[vlc-devel] [PATCHv12] dcp: Creation of access-demux module for DCP
Nicolas Bertrand
nicoinattendu at gmail.com
Tue Dec 10 14:31:18 CET 2013
This version only support 1-Reel-per-track DCPs, like trailers
---
configure.ac | 17 +
modules/access/Makefile.am | 7 +
modules/access/dcp/dcp.cpp | 819 +++++++++++++++++++++++
modules/access/dcp/dcpparser.cpp | 1356 ++++++++++++++++++++++++++++++++++++++
modules/access/dcp/dcpparser.h | 299 +++++++++
5 files changed, 2498 insertions(+)
create mode 100644 modules/access/dcp/dcp.cpp
create mode 100644 modules/access/dcp/dcpparser.cpp
create mode 100644 modules/access/dcp/dcpparser.h
diff --git a/configure.ac b/configure.ac
index 5f7ed24..e6951dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1918,6 +1918,23 @@ fi
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 at _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-dev.pub.cinecert.com/asdcplib/download/ ] )])
+ ])
+ AC_LANG_POP(C++)
+])
+AM_CONDITIONAL(HAVE_ASDCP, [test "${have_asdcp}" != "no"])
+
+dnl
dnl Demux plugins
dnl
diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index 6b48799..27193a1 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -9,6 +9,13 @@ AM_CPPFLAGS += -I$(srcdir)/access
libattachment_plugin_la_SOURCES = access/attachment.c
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_CPPFLAGS = $(AM_CPPFLAGS)
if HAVE_WIN32
diff --git a/modules/access/dcp/dcp.cpp b/modules/access/dcp/dcp.cpp
new file mode 100644
index 0000000..1ae54ee
--- /dev/null
+++ b/modules/access/dcp/dcp.cpp
@@ -0,0 +1,819 @@
+/*****************************************************************************
+ * Copyright (C) 2012-2013 VLC authors and VideoLAN
+ *
+ * Authors:
+ * Nicolas Bertrand <nico at isf.cc>
+ * Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
+ * Jean-Baptiste Kempf <jb at 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 */
+#define NB_AUDIO_CONFIGS 9
+
+/* 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 };
+
+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 > NB_AUDIO_CONFIGS) {
+ msg_Err(p_demux, " DCP module support not support %i channels",
+ AudioDesc.ChannelCount);
+ delete p_AudioMXFReader;
+ } 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* ); *pf = 0.0;
+ 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 */
+}
diff --git a/modules/access/dcp/dcpparser.cpp b/modules/access/dcp/dcpparser.cpp
new file mode 100644
index 0000000..86e65ed
--- /dev/null
+++ b/modules/access/dcp/dcpparser.cpp
@@ -0,0 +1,1356 @@
+/*****************************************************************************
+ * Copyright (C) 2013 VLC authors and VideoLAN
+ *
+ * Authors:
+ * Nicolas Bertrand <nico at isf.cc>
+ * Jean-Baptiste Kempf <jb at 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;
+ }
+ }
+ }
+
+
+ /* 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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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;
+}
diff --git a/modules/access/dcp/dcpparser.h b/modules/access/dcp/dcpparser.h
new file mode 100644
index 0000000..9384d75
--- /dev/null
+++ b/modules/access/dcp/dcpparser.h
@@ -0,0 +1,299 @@
+/*****************************************************************************
+ * Copyright (C) 2013 VLC authors and VideoLAN
+ *
+ * Authors: Nicolas Bertrand <nico at isf.cc>
+ * Jean-Baptiste Kempf <jb at 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( ) {
+ vlc_delete_all(pkls);
+ 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 */
--
1.7.9.5
More information about the vlc-devel
mailing list