[vlc-devel] [PATCHv10] dcp: Creation of access-demux module for DCP
Denis Charmet
typx at dinauz.org
Mon Dec 2 16:27:56 CET 2013
Hi,
Boy this is looooooong to review :)
Le lundi 02 décembre 2013 à 02:01:55, Nicolas Bertrand a écrit :
> This version only support 1-Reel-per-track DCPs, like trailers
>
> ---
> configure.ac | 17 +
> modules/access/Makefile.am | 7 +
> modules/access/dcp/dcp.cpp | 756 ++++++++++++++++++++++
> modules/access/dcp/dcpparser.cpp | 1312 ++++++++++++++++++++++++++++++++++++++
> modules/access/dcp/dcpparser.h | 293 +++++++++
> 5 files changed, 2385 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..40c3a55
> --- /dev/null
> +++ b/modules/access/dcp/dcp.cpp
> @@ -0,0 +1,756 @@
> +/*****************************************************************************
> + * 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>
> +
> +/* 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 Picture Descriptor */
> + union
> + {
> + /* JPEG2000 and JPEG2000 stereoscopic essence types */
> + JP2K::PictureDescriptor *p_PicDesc;
> +
> + /* MPEG2 essence type */
> + MPEG2::VideoDescriptor *p_VideoDesc;
> + };
> +
> + /* ASDCP Audio MXF Reader */
> + PCM::MXFReader *p_AudioMXFReader;
> +
> + /* ASDCP Audio Descriptor */
> + PCM::AudioDescriptor *p_AudioDesc;
> +
> + /* elementary streams */
> + es_out_t *p_out;
> + 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;
> +
> + mtime_t i_pts;
> +
> + demux_sys_t():
> + PictureEssType ( ESS_UNKNOWN ),
> + p_PicMXFReader( NULL ),
> + p_PicDesc( NULL ),
> + p_AudioMXFReader( NULL ),
> + p_AudioDesc( NULL ),
> + p_out( 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;
> + delete p_PicDesc;
> + break;
> + case ESS_JPEG_2000_S:
> + delete p_PicMXFSReader;
> + delete p_PicDesc;
> + break;
> + case ESS_MPEG2_VES:
> + delete p_VideoMXFReader;
> + delete p_VideoDesc;
> + break;
> + default:
> + break;
> + }
> + delete p_AudioMXFReader;
> + delete p_AudioDesc;
> + delete p_dcp;
> + }
> +};
> +
> +/*****************************************************************************
> + * 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 );
> +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 *p_PicDesc;
> +
> + p_PicDesc = p_sys->p_PicDesc = new ( nothrow ) JP2K::PictureDescriptor();
> + if ( !p_PicDesc ) {
> + retval = VLC_ENOMEM;
> + goto error;
> + }
> + if (p_sys->PictureEssType == ESS_JPEG_2000_S) { /* 3D JPEG2000 */
> + JP2K::MXFSReader *p_PicMXFSReader;
> + p_PicMXFSReader = p_sys->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;
> + goto error;
> + }
> +
> + p_PicMXFSReader->FillPictureDescriptor( *p_PicDesc );
> + } else { /* 2D JPEG2000 */
> + JP2K::MXFReader *p_PicMXFReader;
> + p_PicMXFReader = p_sys->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;
> + goto error;
> + }
> +
> + p_PicMXFReader->FillPictureDescriptor( *p_PicDesc );
> + }
> + es_format_Init( &video_format, VIDEO_ES, VLC_CODEC_JPEG2000 );
> + fillVideoFmt( &video_format.video, p_PicDesc->StoredWidth, p_PicDesc->StoredHeight, p_PicDesc->EditRate.Numerator, p_PicDesc->EditRate.Denominator );
> +
> + p_sys->p_PicDesc = p_PicDesc;
> + p_sys->frame_rate_num = p_PicDesc->EditRate.Numerator;
> + p_sys->frame_rate_denom = p_PicDesc->EditRate.Denominator;
> + p_sys->frames_total = p_PicDesc->ContainerDuration;
As previously stated if you don't use it afterward why keeping
p_PicDesc?
> + break;
> + case ESS_MPEG2_VES:
> + MPEG2::MXFReader *p_VideoMXFReader;
> + MPEG2::VideoDescriptor *p_VideoDesc;
> +
> + p_VideoMXFReader = p_sys->p_VideoMXFReader = new ( nothrow ) MPEG2::MXFReader();
> + p_VideoDesc = p_sys->p_VideoDesc = new ( nothrow ) MPEG2::VideoDescriptor();
> +
> + if( !p_VideoMXFReader || !p_VideoDesc ) {
> + 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( *p_VideoDesc );
> +
> + es_format_Init( &video_format, VIDEO_ES, VLC_CODEC_MPGV );
> + fillVideoFmt( &video_format.video, p_VideoDesc->StoredWidth, p_VideoDesc->StoredHeight, p_VideoDesc->EditRate.Numerator, p_VideoDesc->EditRate.Denominator );
> +
> + p_sys->frame_rate_num = p_VideoDesc->EditRate.Numerator;
> + p_sys->frame_rate_denom = p_VideoDesc->EditRate.Denominator;
> + p_sys->frames_total = p_VideoDesc->ContainerDuration;
You are leaking p_VideoDesc
> +
> + 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;
> + PCM::AudioDescriptor *p_AudioDesc;
> +
> + p_AudioMXFReader = p_sys->p_AudioMXFReader = new ( nothrow ) PCM::MXFReader();
> + p_AudioDesc = p_sys->p_AudioDesc = new ( nothrow ) PCM::AudioDescriptor();
> +
> + if( !p_AudioMXFReader || !p_AudioDesc ){
> + 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;
> + goto error;
> + }
> +
> + p_AudioMXFReader->FillAudioDescriptor( *p_AudioDesc );
> +
> + es_format_Init( &audio_format, AUDIO_ES, VLC_CODEC_S24L );
> + if( p_AudioDesc->AudioSamplingRate.Denominator != 0 )
> + audio_format.audio.i_rate =
> + p_AudioDesc->AudioSamplingRate.Numerator
> + / p_AudioDesc->AudioSamplingRate.Denominator;
> + else if ( AudioEssType == ESS_PCM_24b_96k )
> + audio_format.audio.i_rate = 96000;
> + else
> + audio_format.audio.i_rate = 48000;
> +
> + audio_format.audio.i_bitspersample = p_AudioDesc->QuantizationBits;
> + audio_format.audio.i_blockalign = p_AudioDesc->BlockAlign;
> + audio_format.audio.i_channels = p_AudioDesc->ChannelCount;
> +
> + 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;
> + goto error;
> + }
> + } 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_sys->p_out = p_demux->out;
> + 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;
> + uint32_t i_audio_buffer = PCM::CalcFrameBufferSize(*p_sys->p_AudioDesc);
Maybe it could be worth to do that only once in Open just like for
PicDesc and VideoDesc. Then you can free all the desc in Open when you
got the important variables.
Besides isn't there some kind of similar functions for video/pic
descriptors to avoid the max FRAME_BUFFER_SIZE?
> + PCM::FrameBuffer AudioFrameBuff(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( i_audio_buffer )) == NULL ) {
> + goto error;
> + }
> + if ( ! ASDCP_SUCCESS(
> + AudioFrameBuff.SetData(p_audio_frame->p_buffer, 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;
> + }
> +
> + 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_SET_PAUSE_STATE:
it takes a bool as va_arg, I assume that you don't use it.
> + break;
> +
> + case DEMUX_CAN_SEEK:
> + pb = (bool *)va_arg( args, bool * );
> + if( p_sys->PictureEssType != ESS_MPEG2_VES )
> + *pb = true;
> + else
> + *pb = false;
could be *pb = (p_sys->PictureEssType != ESS_MPEG2_VES);
> + 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" );
maybe put the *pf = 0.0; inside the else.
> + 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 = (int) ( ( p_sys->frames_total * p_sys->frame_rate_denom / p_sys->frame_rate_num ) * CLOCK_FREQ );
Don't cast an int64_t as an int you may lose data.
> + 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
> + */
> +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;
> + try
> + {
> + result.append( ent->d_name );
You use a try catch here but not in dcpInit, is there a reason?
> + }
> + catch(...)
> + {
> + msg_Err( p_demux, "String handling failed" );
> + closedir( dir );
> + return NULL;
> + }
> + 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 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..4416996
> --- /dev/null
> +++ b/modules/access/dcp/dcpparser.cpp
> @@ -0,0 +1,1312 @@
> +/*****************************************************************************
> + * 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 (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;
> +
> +
> +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_UNKNOWN; i < CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
<nitpicking>
I think that it would look better with:
for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
if( node == names[i-1]) {
chunk_tag = i;
...
switch(chunk_tag) {
...
</nitpicking>
> + if( node == names[i]) {
> + chunk_tag = ChunkTag_t(i+1);
> + 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;
> + }
> + }
> + }
> + 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 Chnuk tag parse */
Chunk
> + 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 );
> + }
> + }
> +
> + 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 );
You still maintain vectors for only one element, if you foresee future
use of more pkls please add a comment stating it.
Besides you don't use pkls outside of this function (putting
apart the destructor) so why keeping it?
> +
> + /* Now, CPL */
> + if ( pkl->FindCPLs() <= 0 ) {
> + msg_Err(p_demux, " No CPL found");
> + this->CloseXml();
> + return -1;
> + }
> + cpl = pkl->getCPL(0);
> + if( cpl == NULL ) {
> + msg_Err(p_demux, " No CPL found");
> + this->CloseXml();
> + return -1;
> + }
> + cpl->Parse();
no check?
> + reel = cpl->getReel(0);
no check this may crash if no Reel was found during Parse?
> +
> + 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 */
> + 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;
> + AssetTag_t asset_tag = ASSET_UNKNOWN;
> +
> + if (p_type != XML_READER_STARTELEM)
> + return -1;
> + if( p_node != "Asset")
> + return -1;
> + /* loop on Assets Node */
> + while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
> + switch (type) {
> + case XML_READER_STARTELEM:
> + if( node =="Id")
> + asset_tag = ASSET_ID;
> + else if( node =="AnnotationText")
> + asset_tag = ASSET_ANNOTATION_TEXT;
> + else if( node =="PackingList") {
> + asset_tag = 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( node =="ChunkList") {
> + asset_tag = ASSET_CHUNK_LIST;
> + if ( this->parseChunkList(p_xmlReader, node, type ) )
> + return -1;
> + this->s_path = this->chunk_vec[0].getPath();
> + } else {
> + asset_tag = ASSET_UNKNOWN;
> + return -1;
> + }
> + break;
> + case XML_READER_TEXT:
> + s_value = node;
> + if (unlikely(node.empty()))
> + return -1;
> + break;
> + case XML_READER_ENDELEM:
> + /*Check Presence of Id and Chunklist, when its present */
> + if ( node == p_node) {
> + if ( this->s_id.empty() ) {
> + //msg_Err(this->p_demux, " No Id element found in Asset");
> + return -1;
> + }
> + /* TODO: verification of Chunklist */
> + goto end;
> + }
> + switch (asset_tag) {
> + case ASSET_ID:
> + if( node != "Id" )
> + return -1;
> + this->s_id = s_value;
> + break;
> + case ASSET_PACKING_LIST:
> + if( node != "PackingList" )
> + return -1;
> + if ( s_value == "true" )
> + this->b_is_packing_list = true;
> + break;
> + case ASSET_ANNOTATION_TEXT:
> + if( node != "AnnotationText" )
> + return -1;
> + this->s_annotation = s_value;
> + break;
> + default:
> + //msg_Err(this->p_demux,"Unknow end attribute %s",s_value.c_str());
> + break;
> + }
> + }
> + }
> +end:
> + return 0;
> +}
> +
> +int Asset::ParsePKL( xml_reader_t *p_xmlReader)
> +{
> + string node;
> + int type;
> + string s_value;
> + AssetTag_t i_tag = ASSET_UNKNOWN;
> +
> + while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
> + switch (type) {
> + case XML_READER_STARTELEM:
<nitpicking> You could have used the string list trick too.
</nitpicking>
> + if( node =="AnnotationText")
> + i_tag = ASSET_ANNOTATION_TEXT;
> + else if( node =="Hash")
> + i_tag = ASSET_HASH;
> + else if( node =="Size")
> + i_tag = ASSET_SIZE;
> + else if( node =="Type")
> + i_tag = ASSET_TYPE;
> + else if( node =="OriginalFileName")
> + i_tag = ASSET_ORIGINAL_FILENAME;
> + else {
> + i_tag = ASSET_UNKNOWN;
> + return -1;
> + }
> + break;
> + case XML_READER_TEXT:
> + s_value = node;
> + if (unlikely(node.empty()))
> + return -1;
> + break;
> + case XML_READER_ENDELEM:
> + if (node == "Asset") {
> + /* Reach end node of asset */
> + 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;
> + }
> + goto end;
> + }
> +
> + switch (i_tag) {
> + case ASSET_ANNOTATION_TEXT:
> + if ( this->s_annotation.empty() )
> + this->s_annotation = s_value;
> + else
> + this->s_annotation = this->s_annotation + "--" + s_value;
> + break;
> + case ASSET_HASH:
> + this->s_hash = s_value;
> + break;
> + case ASSET_SIZE:
> + this->ui_size = atol(s_value.c_str());
> + break;
> + case ASSET_TYPE:
> + this->s_type = s_value;
> + break;
> + case ASSET_ORIGINAL_FILENAME:
> + this->s_original_filename = s_value;
> + break;
> + case ASSET_UNKNOWN:
> + default:
> + msg_Err(this->p_demux," Error parsing asset in PKL");
> + break;
> + }
> + break;
> + }
> + }
> +end:
> + return 0;
> +}
> +
> +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");
For now? Else you don't need a vector.
> + return -1;
> + }
> + this->chunk_vec = chunk_vec;
> + goto end;
return 0;
> + }
> + break;
> + }
> + }
> +end:
so if you finish the ReadNextNode loop without XML_READER_ENDELEM and
node == p_node you return 0? Looks suspicious
> + return 0;
> +}
> +
> +int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, string p_node, int p_type)
> +{
> + string node;
> + int type;
> + Asset *asset;
maybe a const string name = "AssetList" could avoid the repetition of
the "AssetList" in the START and END. Don't know if the compiler is
smart enough to see it's the same string and allocate it only once.
> +
> + 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 Asset(this->p_demux);
nothrow or try/catch
> + 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 == "AssetList" )
> + 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 );
you could free psz_uri here and avoid all the next free(psz_uri)
> + if( ! this->p_stream ) {
> + xml_Delete(this->p_xml );
> + free(psz_uri);
> + 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 );
> + free(psz_uri);
> + return -1;
> + }
> + free(psz_uri);
> + 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;
> + PKLTag_t i_tag = PKL_UNKNOWN;
> + if (this->OpenXml())
> + return -1;
> + /* read 1st node and verify that is a CPL*/
> + if ( (type = ReadNextNode(this->p_xmlReader, node)) > 0) {
> + if ( ! ((type == XML_READER_STARTELEM) && (node == "PackingList")) ) {
> + msg_Err( this->p_demux, "Not a valid XML Packing List");
> + goto error;
> + }
> + } else {
> + msg_Err( this->p_demux, "XML read failed");
> + goto error;
> + }
> + while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
> + switch (type) {
> + case XML_READER_STARTELEM:
the string list trick would make this smaller.
> + if( node =="Id")
> + i_tag = PKL_ID;
> + else if( node == "IssueDate")
> + i_tag = PKL_ISSUE_DATE;
> + else if( node == "Issuer")
> + i_tag = PKL_ISSUER;
> + else if( node == "Creator")
> + i_tag = PKL_CREATOR;
> + else if( node == "AssetList") {
> + i_tag = PKL_ASSET_LIST;
> + if ( this->ParseAssetList(node, type) )
> + goto error;
> + } else if( node == "AnnotationText")
> + i_tag = PKL_ANNOTATION_TEXT;
> + else if( node == "IconId")
> + i_tag = PKL_ICON_ID;
> + else if( node == "GroupId")
> + i_tag = PKL_GROUP_ID;
> + else if( node == "Signer") {
> + i_tag = PKL_SIGNER;
> + if ( this->ParseSigner(node, type) )
> + goto error;
> + } else if( node == "ds:Signature") {
> + i_tag = PKL_SIGNATURE;
> + if ( this->ParseSignature(node, type) )
> + goto error;
> + } else {
> + i_tag = PKL_UNKNOWN;
> + goto error;
> + }
> + break;
> + case XML_READER_TEXT:
> + s_value = node;
> + if (unlikely(node.empty()))
> + goto error;
> + break;
> +
> + case XML_READER_ENDELEM:
> + switch (i_tag) {
> + case PKL_ID:
> + if( node != "Id" )
> + goto error;
> + this->s_id = s_value;
> + break;
> + case PKL_ISSUE_DATE:
> + if( node != "IssueDate" )
> + goto error;
> + this->s_issue_date= s_value;
> + break;
> + case PKL_ISSUER:
> + if( node != "Issuer" )
> + goto error;
> + this->s_issuer = s_value;
> + break;
> + case PKL_CREATOR:
> + if( node != "Creator" )
> + goto error;
> + this->s_creator = s_value;
> + break;
> + case PKL_ASSET_LIST:
> + /* impossible */
> + break;
> + case PKL_ANNOTATION_TEXT:
> + if( node != "AnnotationText" )
> + goto error;
> + this->s_annotation = s_value;
> + break;
> + case PKL_ICON_ID:
> + if( node != "IconId" )
> + goto error;
> + this->s_icon_id = s_value;
> + break;
> + case PKL_GROUP_ID:
> + if( node != "GroupId" )
> + goto error;
> + this->s_group_id = s_value;
> + break;
> + case PKL_SIGNER:
> + case PKL_SIGNATURE:
> + /* impossible */
> + break;
> + case ASSET_UNKNOWN:
> + default:
> + goto error;
> + }
> + break;
> + }
> + }
> + /* TODO verify presence of mandatory fields*/
> +
> + /* Close PKL XML*/
> + this->CloseXml();
> + return 0;
> +error:
> + 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 =="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::Parse, 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 == "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::Parse, 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;
> +
> + if (this->OpenXml())
There is a *LOT* of OpenXml, why not simply give the reader of the
parent node? it would prevent you to parse the whole 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;
> + CPLTag_t i_tag = CPL_UNKNOWN;
> + if (this->OpenXml())
> + return -1;
> + /* read 1st node and verify that is a CPL*/
> + if ( (type = ReadNextNode(this->p_xmlReader, node)) > 0) {
> + if ( ! ((type == XML_READER_STARTELEM) && (node == "CompositionPlaylist")) ) {
> + msg_Err( this->p_demux, "Not a valid XML Composition Play List");
> + goto error;
> + }
> + } else {
> + msg_Err( this->p_demux, "XML read failed");
> + goto error;
> + }
> + while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
> + switch (type) {
> + case XML_READER_STARTELEM:
string list makes it smaller
> + if( node =="Id")
> + i_tag = CPL_ID;
> + else if( node == "AnnotationText")
> + i_tag = CPL_ANNOTATION_TEXT;
> + else if( node == "IconId")
> + i_tag = CPL_ICON_ID;
> + else if( node == "IssueDate")
> + i_tag = CPL_ISSUE_DATE;
> + else if( node == "Issuer")
> + i_tag = CPL_ISSUER;
> + else if( node == "Creator")
> + i_tag = CPL_CREATOR;
> + else if( node == "ContentTitleText")
> + i_tag = CPL_CONTENT_TITLE;
> + else if( node == "ContentKind")
> + i_tag = CPL_CONTENT_KIND;
> + else if( node == "ContentVersion") {
> + i_tag = CPL_CONTENT_VERSION;
> + if ( this->DummyParse(node,type) )
> + goto error;
> + }
> + else if( node == "RatingList") {
> + i_tag = CPL_RATING_LIST;
> + if ( this->DummyParse(node,type) )
> + goto error;
> + }
> + else if( node == "ReelList") {
> + i_tag = CPL_REEL_LIST;
> + if ( this->ParseReelList(node, type) )
> + goto error;
> + } else if( node == "Signer") {
> + i_tag = CPL_SIGNER;
> + if ( this->DummyParse(node, type) )
> + goto error;
> + } else if( node == "ds:Signature") {
> + i_tag = CPL_SIGNATURE;
> + if ( this->DummyParse(node, type) )
> + goto error;
> + } else {
> + i_tag = CPL_UNKNOWN;
> + goto error;
> + }
> + break;
> + case XML_READER_TEXT:
> + s_value = node;
> + if (unlikely(node.empty()))
> + goto error;
> + break;
> +
> + case XML_READER_ENDELEM:
And if you use the list you can check that
if( node == list[(int)i_tag - 1]) once and for all.
> + switch (i_tag) {
> + case CPL_ID:
> + if( node != "Id" )
> + goto error;
> + this->s_id = s_value;
> + break;
> + case CPL_ANNOTATION_TEXT:
> + if( node != "AnnotationText" )
> + goto error;
> + this->s_annotation = s_value;
> + break;
> + case CPL_ICON_ID:
> + if( node != "IconId" )
> + goto error;
> + this->s_icon_id = s_value;
> + break;
> + case CPL_ISSUE_DATE:
> + if( node != "IssueDate" )
> + goto error;
> + this->s_issue_date= s_value;
> + break;
> + case CPL_ISSUER:
> + if( node != "Issuer" )
> + goto error;
> + this->s_issuer = s_value;
> + break;
> + case CPL_CREATOR:
> + if( node != "Creator" )
> + goto error;
> + this->s_creator = s_value;
> + break;
> + case CPL_CONTENT_TITLE:
> + if( node != "ContentTitleText" )
> + goto error;
> + this->s_content_title = s_value;
> + break;
> + case CPL_CONTENT_KIND:
> + if( node != "ContentKind" )
> + goto error;
> + this->s_content_kind = s_value;
> + break;
> + case CPL_CONTENT_VERSION:
> + case CPL_RATING_LIST:
> + case CPL_REEL_LIST:
> + case CPL_SIGNER:
> + case CPL_SIGNATURE:
> + /* impossible */
> + break;
> + case ASSET_UNKNOWN:
> + default:
> + goto error;
> + }
> + break;
> + }
> + }
> + /* TODO verify presence of mandatory fields*/
> +
> + /* Close Ckl 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..0e101c5
> --- /dev/null
> +++ b/modules/access/dcp/dcpparser.h
> @@ -0,0 +1,293 @@
> +/*****************************************************************************
> + * 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 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, string p_node, int p_type);
> +};
> +#endif /* _DCPPARSER_H */
Regards,
--
Denis Charmet - TypX
Le mauvais esprit est un art de vivre
More information about the vlc-devel
mailing list