[vlc-devel] [RFC] dcp.cpp: Creation of access-demux module for DCP (using asdcplib)
Simona-Marinela Prodea
simona.marinela.prodea at gmail.com
Mon Jun 3 11:01:12 CEST 2013
The module only handles audio and video files in DCP, no subtitles yet. Is this form worthy of a patch?
---
modules/access/Modules.am | 5 +
modules/access/dcp.cpp | 1006 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1011 insertions(+)
create mode 100644 modules/access/dcp.cpp
diff --git a/modules/access/Modules.am b/modules/access/Modules.am
index a96e33c..254197a 100644
--- a/modules/access/Modules.am
+++ b/modules/access/Modules.am
@@ -24,6 +24,11 @@ libtimecode_plugin_la_SOURCES = timecode.c
libtimecode_plugin_la_CFLAGS = $(AM_CFLAGS)
libtimecode_plugin_la_LIBADD = $(AM_LIBADD)
+libdcp_plugin_la_SOURCES = dcp.cpp
+libdcp_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBXML2_CFLAGS)
+libdcp_plugin_la_LIBADD = $(AM_LIBADD) $(LIBXML2_LIBS) -lasdcp
+libvlc_LTLIBRARIES += libdcp_plugin.la
+
libzip_plugin_la_SOURCES = zip/zip.h zip/zipstream.c zip/zipaccess.c
libzip_plugin_la_CFLAGS = $(AM_CFLAGS) $(MINIZIP_CFLAGS)
libzip_plugin_la_LIBADD = $(AM_LIBADD) $(MINIZIP_LIBS)
diff --git a/modules/access/dcp.cpp b/modules/access/dcp.cpp
new file mode 100644
index 0000000..c814bab
--- /dev/null
+++ b/modules/access/dcp.cpp
@@ -0,0 +1,1006 @@
+/*****************************************************************************
+ * Copyright (C) 2012-2013 VLC authors and VideoLAN
+ *
+ * Authors: Claire Etienne
+ * Aurélie Sbinné
+ * Pierre Villard <pierre dot villard dot fr at gmail dot com>
+ * Samuel Kerjose
+ * Julien Puyobro
+ * Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
+ *
+ * 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
+
+#include <stdlib.h>
+#include <dirent.h> /* for managing the directory */
+
+/* VLC core API headers */
+#include <vlc_common.h>
+#include <vlc_input.h>
+#include <vlc_demux.h>
+#include <vlc_plugin.h>
+
+/* for use of libXML2 */
+#include <libxml/xmlreader.h>
+
+/* ASDCP headers */
+#include "AS_DCP.h"
+
+using namespace ASDCP;
+
+#define FRAME_BUFFER_SIZE 4 * 1024 * 1024
+
+/* 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_( "DCP access-demux module" ) )
+ set_capability( "access_demux", 100 ) /* score? */
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_ACCESS )
+ set_callbacks( Open, Close )
+vlc_module_end()
+
+//! File structure
+struct file_t
+{
+ char* id;
+ char* path;
+};
+
+//! DCP Structure
+/*! This structure stores the most important information about the DCP */
+struct dcp_t
+{
+ char *path; /*!< Path to DCP directory */
+ char *assetmapfile; /*!< ASSETMAP XML file */
+ char *pklfile; /*!< PKL XML file name */
+ char *cplfile; /*!< CPL XML file name */
+ char *videofile; /*!< Video file name */
+ char *audiofile; /*!< Audio file name */
+ int nb_files; /*!< Number of files defined in ASSETMAP */
+ file_t *files[50]; /*!< Files defined in ASSETMAP : we assume that there are less than 50 files */
+};
+
+/* ASDCP library 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.
+*/
+
+struct demux_sys_t
+{
+ /* ASDCP Picture Essence Type */
+ EssenceType_t VideoEssType;
+
+ /* ASDCP Video MXF Reader for JPEG2000 essence type */
+ JP2K::MXFReader *p_PicMXFReader;
+
+ /* ASDCP Video MXF Reader for JPEG2000 stereoscopic essence type */
+ JP2K::MXFSReader *p_PicMXFSReader;
+
+ /* ASDCP Video MXF Reader for MPEG2 essence type */
+ MPEG2::MXFReader *p_VideoMXFReader;
+
+ /* ASDCP Picture Descriptor ( for JPEG2000 and JPEG2000 stereoscopic )*/
+ JP2K::PictureDescriptor *p_PicDesc;
+
+ /* ASDCP Video Descriptor ( for MPEG2 ) */
+ MPEG2::VideoDescriptor *p_VideoDesc;
+
+ /* ASDCP Audio MXF Reader */
+ PCM::MXFReader *p_AudioMXFReader;
+
+ /* ASDCP Audio Descriptor */
+ PCM::AudioDescriptor *p_AudioDesc;
+
+ /* 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 */
+ double frame_rate;
+
+ /* total number of frames */
+ uint32_t frames_total;
+
+ demux_sys_t()
+ {
+ p_PicMXFReader = NULL;
+ p_PicMXFSReader = NULL;
+ p_PicDesc = NULL;
+ p_VideoMXFReader = NULL;
+ p_VideoDesc = NULL;
+ p_AudioMXFReader = NULL;
+ p_AudioDesc = NULL;
+ p_dcp = NULL;
+ }
+ ~demux_sys_t()
+ {
+ delete p_PicMXFReader;
+ delete p_PicMXFSReader;
+ delete p_PicDesc;
+ delete p_VideoMXFReader;
+ delete p_VideoDesc;
+ delete p_AudioMXFReader;
+ delete p_AudioDesc;
+ delete p_dcp;
+ }
+};
+
+static int Demux( demux_t * );
+static int Control( demux_t *, int, va_list );
+
+int dcpInit ( demux_t *p_demux );
+int parserXML ( demux_t * p_demux );
+void assetmapUri( demux_t * p_demux );
+void parserAssetmapXML ( demux_t * p_demux );
+void parserCplXML ( demux_t * p_demux );
+int endsWith( const char * str, const char * suffix );
+
+static int Open( vlc_object_t *obj )
+{
+ demux_t *p_demux = ( demux_t* ) obj;
+ demux_sys_t *p_sys = p_demux->p_sys = new demux_sys_t();
+ es_format_t video_format, audio_format;
+ vlc_fourcc_t fcc;
+
+ msg_Dbg( p_demux, "Opening DCP access-demux module" );
+
+ if( !p_demux->psz_file )
+ return VLC_EGENERIC;
+
+ /* handle the DCP directory, saving the paths for audio and video file */
+ dcpInit( p_demux );
+ p_sys = p_demux->p_sys;
+
+ /***************************************************
+ ***************** open video file *****************
+ ***************************************************/
+ msg_Dbg( p_demux, "Video file is : %s", p_sys->p_dcp->videofile );
+ EssenceType( p_sys->p_dcp->videofile, p_sys->VideoEssType );
+
+ switch( p_sys->VideoEssType )
+ {
+ case ESS_UNKNOWN:
+ msg_Err( p_demux, "The file %s is not a supported AS_DCP essence container. DCP cannot be read.");
+ return VLC_EGENERIC;
+ case ESS_JPEG_2000:
+ {
+ fcc = VLC_FOURCC( 'J', 'P', '2', 'K' );
+ JP2K::MXFReader *p_PicMXFReader = p_sys->p_PicMXFReader = new JP2K::MXFReader();
+ JP2K::PictureDescriptor *p_PicDesc = p_sys->p_PicDesc = new JP2K::PictureDescriptor();
+
+ Result_t result = p_PicMXFReader->OpenRead( p_sys->p_dcp->videofile );
+ if( ASDCP_SUCCESS( result ) )
+ msg_Dbg( p_demux, "File %s was successfully opened with asdcp", p_sys->p_dcp->videofile );
+ else
+ {
+ msg_Err( p_demux, "File %s could not be opened with asdcp!", p_sys->p_dcp->videofile );
+ return VLC_EGENERIC;
+ }
+
+ p_PicMXFReader->FillPictureDescriptor( *p_PicDesc );
+
+ msg_Dbg( p_demux, "PictureDescriptor: " );
+ msg_Dbg( p_demux, " EditRate: %d:%d", p_PicDesc->EditRate.Numerator, p_PicDesc->EditRate.Denominator );
+ msg_Dbg( p_demux, " ContainerDuration: %d", p_PicDesc->ContainerDuration );
+ msg_Dbg( p_demux, " SampleRate: %d:%d", p_PicDesc->SampleRate.Numerator, p_PicDesc->SampleRate.Denominator );
+ msg_Dbg( p_demux, " StoredWidth: %d", p_PicDesc->StoredWidth );
+ msg_Dbg( p_demux, " StoredHeight: %d", p_PicDesc->StoredHeight );
+ msg_Dbg( p_demux, " Rsize: %d", p_PicDesc->Rsize );
+ msg_Dbg( p_demux, " Xsize: %d", p_PicDesc->Xsize );
+ msg_Dbg( p_demux, " Ysize: %d", p_PicDesc->Ysize );
+ msg_Dbg( p_demux, " XOsize: %d", p_PicDesc->XOsize );
+ msg_Dbg( p_demux, " YOsize: %d", p_PicDesc->YOsize );
+ msg_Dbg( p_demux, " XTsize: %d", p_PicDesc->XTsize );
+ msg_Dbg( p_demux, " YTsize: %d", p_PicDesc->YTsize );
+ msg_Dbg( p_demux, " XTOsize: %d", p_PicDesc->XTOsize );
+ msg_Dbg( p_demux, " YTOsize: %d", p_PicDesc->YTOsize );
+ msg_Dbg( p_demux, " Csize: %d", p_PicDesc->Csize );
+ msg_Dbg( p_demux, " Aspect Ratio: %d:%d", p_PicDesc->AspectRatio.Numerator, p_PicDesc->AspectRatio.Denominator );
+ msg_Dbg( p_demux, " ImageComponents, CodingStyleDefault, QuantizationDefault - to see in asdcplib/src/AS_DCP.h" );
+
+ p_sys->p_PicMXFReader = p_PicMXFReader;
+ p_sys->p_PicDesc = p_PicDesc;
+ p_sys->frame_no = 0;
+ p_sys->frame_rate = p_PicDesc->EditRate.Numerator / p_PicDesc->EditRate.Denominator;
+ p_sys->frames_total = p_PicDesc->ContainerDuration;
+
+ es_format_Init( &video_format, VIDEO_ES, fcc );
+ video_format.video.i_width = p_PicDesc->StoredWidth;
+ video_format.video.i_height = p_PicDesc->StoredHeight;
+ /* As input are square pixels let VLC or decoder fix SAR, origin,
+ * and visible area */
+ video_format.video.i_frame_rate = p_PicDesc->EditRate.Numerator;
+ video_format.video.i_frame_rate_base = p_PicDesc->EditRate.Denominator;
+ break;
+ }
+ case ESS_JPEG_2000_S:
+ {
+ fcc = VLC_FOURCC( 'J', 'P', '2', 'K' );
+ JP2K::MXFSReader *p_PicMXFSReader = p_sys->p_PicMXFSReader = new JP2K::MXFSReader();
+ JP2K::PictureDescriptor *p_PicDesc = p_sys->p_PicDesc = new JP2K::PictureDescriptor();
+
+ Result_t result = p_PicMXFSReader->OpenRead( p_sys->p_dcp->videofile );
+ if( ASDCP_SUCCESS( result ) )
+ msg_Dbg( p_demux, "File %s was successfully opened with asdcp", p_sys->p_dcp->videofile );
+ else
+ {
+ msg_Err( p_demux, "File %s could not be opened with asdcp!", p_sys->p_dcp->videofile );
+ return VLC_EGENERIC;
+ }
+
+ p_PicMXFSReader->FillPictureDescriptor( *p_PicDesc );
+
+ msg_Dbg( p_demux, "PictureDescriptor: " );
+ msg_Dbg( p_demux, " EditRate: %d:%d", p_PicDesc->EditRate.Numerator, p_PicDesc->EditRate.Denominator );
+ msg_Dbg( p_demux, " ContainerDuration: %d", p_PicDesc->ContainerDuration );
+ msg_Dbg( p_demux, " SampleRate: %d:%d", p_PicDesc->SampleRate.Numerator, p_PicDesc->SampleRate.Denominator );
+ msg_Dbg( p_demux, " StoredWidth: %d", p_PicDesc->StoredWidth );
+ msg_Dbg( p_demux, " StoredHeight: %d", p_PicDesc->StoredHeight );
+ msg_Dbg( p_demux, " Rsize: %d", p_PicDesc->Rsize );
+ msg_Dbg( p_demux, " Xsize: %d", p_PicDesc->Xsize );
+ msg_Dbg( p_demux, " Ysize: %d", p_PicDesc->Ysize );
+ msg_Dbg( p_demux, " XOsize: %d", p_PicDesc->XOsize );
+ msg_Dbg( p_demux, " YOsize: %d", p_PicDesc->YOsize );
+ msg_Dbg( p_demux, " XTsize: %d", p_PicDesc->XTsize );
+ msg_Dbg( p_demux, " YTsize: %d", p_PicDesc->YTsize );
+ msg_Dbg( p_demux, " XTOsize: %d", p_PicDesc->XTOsize );
+ msg_Dbg( p_demux, " YTOsize: %d", p_PicDesc->YTOsize );
+ msg_Dbg( p_demux, " Csize: %d", p_PicDesc->Csize );
+ msg_Dbg( p_demux, " Aspect Ratio: %d:%d", p_PicDesc->AspectRatio.Numerator, p_PicDesc->AspectRatio.Denominator );
+ msg_Dbg( p_demux, " ImageComponents, CodingStyleDefault, QuantizationDefault - to see in asdcplib/src/AS_DCP.h" );
+
+ p_sys->p_PicMXFSReader = p_PicMXFSReader;
+ p_sys->p_PicDesc = p_PicDesc;
+ p_sys->frame_no = 0;
+ p_sys->frame_rate = p_PicDesc->EditRate.Numerator / p_PicDesc->EditRate.Denominator;
+ p_sys->frames_total = p_PicDesc->ContainerDuration;
+
+ es_format_Init( &video_format, VIDEO_ES, fcc );
+ video_format.video.i_width = p_PicDesc->StoredWidth;
+ video_format.video.i_height = p_PicDesc->StoredHeight;
+ /* As input are square pixels let VLC or decoder fix SAR, origin,
+ * and visible area */
+ video_format.video.i_frame_rate = p_PicDesc->EditRate.Numerator;
+ video_format.video.i_frame_rate_base = p_PicDesc->EditRate.Denominator;
+ break;
+ }
+ case ESS_MPEG2_VES:
+ {
+ fcc = VLC_FOURCC( 'm', 'p', 'g', 'v' );
+ MPEG2::MXFReader *p_VideoMXFReader = p_sys->p_VideoMXFReader = new MPEG2::MXFReader();
+ MPEG2::VideoDescriptor *p_VideoDesc = p_sys->p_VideoDesc = new MPEG2::VideoDescriptor();
+
+ Result_t result = p_VideoMXFReader->OpenRead( p_sys->p_dcp->videofile );
+ if( ASDCP_SUCCESS( result ) )
+ msg_Dbg( p_demux, "File %s was successfully opened with asdcp", p_sys->p_dcp->videofile );
+ else
+ {
+ msg_Err( p_demux, "File %s could not be opened with asdcp!", p_sys->p_dcp->videofile );
+ return VLC_EGENERIC;
+ }
+
+ p_VideoMXFReader->FillVideoDescriptor( *p_VideoDesc );
+
+ msg_Dbg( p_demux, "VideoDescriptor: " );
+ msg_Dbg( p_demux, " EditRate: %d:%d", p_VideoDesc->EditRate.Numerator, p_VideoDesc->EditRate.Denominator );
+ msg_Dbg( p_demux, " FrameRate: %d", p_VideoDesc->FrameRate );
+ msg_Dbg( p_demux, " SampleRate: %d:%d", p_VideoDesc->SampleRate.Numerator, p_VideoDesc->SampleRate.Denominator );
+ msg_Dbg( p_demux, " FrameLayout: %d", p_VideoDesc->FrameLayout );
+ msg_Dbg( p_demux, " StoredWidth: %d", p_VideoDesc->StoredWidth );
+ msg_Dbg( p_demux, " StoredHeight: %d", p_VideoDesc->StoredHeight );
+ msg_Dbg( p_demux, " Aspect Ratio: %d:%d", p_VideoDesc->AspectRatio.Numerator, p_VideoDesc->AspectRatio.Denominator );
+ msg_Dbg( p_demux, " ComponentDepth: %d", p_VideoDesc->ComponentDepth );
+ msg_Dbg( p_demux, " HorizontalSubsampling: %d", p_VideoDesc->HorizontalSubsampling );
+ msg_Dbg( p_demux, " VerticalSubsampling: %d", p_VideoDesc->VerticalSubsampling );
+ msg_Dbg( p_demux, " ColorSiting: %d", p_VideoDesc->ColorSiting );
+ msg_Dbg( p_demux, " CodedContentType: %d", p_VideoDesc->CodedContentType );
+ msg_Dbg( p_demux, " LowDelay: %s", p_VideoDesc->LowDelay ? "true" : "false" );
+ msg_Dbg( p_demux, " BitRate: %d", p_VideoDesc->BitRate );
+ msg_Dbg( p_demux, " ProfileAndLevel: %d", p_VideoDesc->ProfileAndLevel );
+ msg_Dbg( p_demux, " ContainerDuration: %d", p_VideoDesc->ContainerDuration );
+
+ p_sys->p_VideoMXFReader = p_VideoMXFReader;
+ p_sys->p_VideoDesc = p_VideoDesc;
+ p_sys->frame_no = 0;
+ p_sys->frame_rate = p_VideoDesc->EditRate.Numerator / p_VideoDesc->EditRate.Denominator;
+ p_sys->frames_total = p_VideoDesc->ContainerDuration;
+
+ es_format_Init( &video_format, VIDEO_ES, fcc );
+ video_format.video.i_width = p_VideoDesc->StoredWidth;
+ video_format.video.i_height = p_VideoDesc->StoredHeight;
+ /* As input are square pixels let VLC or decoder fix SAR, origin,
+ * and visible area */
+ video_format.video.i_frame_rate = p_VideoDesc->EditRate.Numerator;
+ video_format.video.i_frame_rate_base = p_VideoDesc->EditRate.Denominator;
+ break;
+ }
+ default:
+ msg_Err( p_demux, "Video file has an audio or subtitle format. DCP may be broken. Check ASSETMAP file." );
+ return VLC_EGENERIC;
+ }
+
+ if( ( p_sys->p_video_es = es_out_Add( p_demux->out, &video_format ) ) == NULL )
+ {
+ msg_Err( p_demux, "Failed to add video es" );
+ return VLC_EGENERIC;
+ }
+
+ /***************************************************
+ ***************** open audio file *****************
+ ***************************************************/
+ msg_Dbg( p_demux, "Audio file is : %s", p_sys->p_dcp->audiofile );
+ EssenceType_t AudioEssType;
+ EssenceType( p_sys->p_dcp->audiofile, AudioEssType );
+ switch( AudioEssType )
+ {
+ case ESS_UNKNOWN:
+ msg_Err( p_demux, "The file %s is not a supported AS_DCP essence container. DCP cannot be read.");
+ return VLC_EGENERIC;
+ case ESS_PCM_24b_48k:
+ case ESS_PCM_24b_96k:
+ fcc = VLC_FOURCC( 's', '2', '4', 'l' );
+ break;
+ default:
+ msg_Err( p_demux, "Audio file has a video or subtitle format. DCP may be broken. Check ASSETMAP file." );
+ return VLC_EGENERIC;
+ }
+
+ PCM::MXFReader *p_AudioMXFReader = p_sys->p_AudioMXFReader = new PCM::MXFReader();
+ PCM::AudioDescriptor *p_AudioDesc = p_sys->p_AudioDesc = new PCM::AudioDescriptor();
+
+ Result_t result = p_AudioMXFReader->OpenRead( p_sys->p_dcp->audiofile );
+ if( ASDCP_SUCCESS( result ) )
+ msg_Dbg( p_demux, "File %s was successfully opened with asdcp", p_sys->p_dcp->audiofile );
+ else
+ {
+ msg_Err( p_demux, "File %s could not be opened with asdcp!", p_sys->p_dcp->audiofile );
+ return VLC_EGENERIC;
+ }
+
+ p_AudioMXFReader->FillAudioDescriptor( *p_AudioDesc );
+
+ es_format_Init( &audio_format, AUDIO_ES, fcc );
+
+ msg_Dbg( p_demux, "AudioDescriptor: " );
+ msg_Dbg( p_demux, " EditRate: %d:%d", p_AudioDesc->EditRate.Numerator, p_AudioDesc->EditRate.Denominator );
+ msg_Dbg( p_demux, " AudioSamplingRate: %d:%d", p_AudioDesc->AudioSamplingRate.Numerator, p_AudioDesc->AudioSamplingRate.Denominator );
+ msg_Dbg( p_demux, " Locked: %d", p_AudioDesc->Locked );
+ msg_Dbg( p_demux, " ChannelCount: %d", p_AudioDesc->ChannelCount );
+ msg_Dbg( p_demux, " QuantizationBits: %d", p_AudioDesc->QuantizationBits );
+ msg_Dbg( p_demux, " BlockAlign: %d", p_AudioDesc->BlockAlign );
+ msg_Dbg( p_demux, " AvgBps: %d", p_AudioDesc->AvgBps );
+ msg_Dbg( p_demux, " LinkedTrackID: %d", p_AudioDesc->LinkedTrackID );
+ msg_Dbg( p_demux, " Containerduration: %d", p_AudioDesc->ContainerDuration );
+ msg_Dbg( p_demux, " ChannelFormat: %d", p_AudioDesc->ChannelFormat ); /* Does it print the right thing? */
+
+ audio_format.audio.i_rate = p_AudioDesc->AudioSamplingRate.Numerator / p_AudioDesc->AudioSamplingRate.Denominator;
+ audio_format.audio.i_bitspersample = p_AudioDesc->QuantizationBits;
+ audio_format.audio.i_blockalign = p_AudioDesc->BlockAlign;
+ audio_format.audio.i_channels = p_AudioDesc->ChannelCount;
+
+ p_sys->p_AudioMXFReader = p_AudioMXFReader;
+ p_sys->p_AudioDesc = p_AudioDesc;
+
+
+ if( ( p_sys->p_audio_es = es_out_Add( p_demux->out, &audio_format ) ) == NULL )
+ {
+ msg_Err( p_demux, "Failed to add audio es" );
+ return VLC_EGENERIC;
+ }
+
+ p_demux->pf_demux = Demux;
+ p_demux->pf_control = Control;
+ p_demux->p_sys = p_sys;
+
+ return VLC_SUCCESS;
+}
+
+static void Close( vlc_object_t *obj )
+{
+ demux_t *p_demux = ( demux_t* ) obj;
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ /* close the files */
+ if( p_sys->p_PicMXFReader )
+ p_sys->p_PicMXFReader->Close();
+ if( p_sys->p_PicMXFSReader )
+ p_sys->p_PicMXFSReader->Close();
+ if( p_sys->p_VideoMXFReader )
+ p_sys->p_VideoMXFReader->Close();
+ p_sys->p_AudioMXFReader->Close();
+
+ delete( p_demux->p_sys );
+ msg_Dbg( p_demux, "Closing DCP access-demux module" );
+}
+
+static int Demux( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ block_t *p_frame = NULL;
+ uint32_t i = p_sys->frame_no;
+
+ if( i == p_sys->frames_total )
+ return VLC_SUCCESS;
+
+ /* video frame */
+ switch( p_sys->VideoEssType )
+ {
+ case ESS_JPEG_2000:
+ {
+ JP2K::MXFReader *p_PicMXFReader = p_sys->p_PicMXFReader;
+ JP2K::FrameBuffer PicFrameBuff( FRAME_BUFFER_SIZE );
+
+ Result_t result = p_PicMXFReader->ReadFrame( i, PicFrameBuff, 0, 0 );
+ if( ! ASDCP_SUCCESS( result ) )
+ {
+ msg_Err( p_demux, "Couldn't read frame with ASDCP");
+ return VLC_EGENERIC;
+ }
+
+ if( ( p_frame = block_Alloc( PicFrameBuff.Size() ) ) != NULL )
+ memcpy( p_frame->p_buffer, PicFrameBuff.Data(), PicFrameBuff.Size() );
+ else
+ {
+ msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+ return VLC_ENOMEM;
+ }
+
+ p_frame->i_buffer = PicFrameBuff.Size();
+ break;
+ }
+ case ESS_JPEG_2000_S:
+ {
+ JP2K::MXFSReader *p_PicMXFReader = p_sys->p_PicMXFSReader;
+ JP2K::FrameBuffer PicFrameBuff( FRAME_BUFFER_SIZE );
+
+ Result_t result = p_PicMXFReader->ReadFrame( i, JP2K::SP_LEFT, PicFrameBuff, 0, 0 );
+ if( ! ASDCP_SUCCESS( result ) )
+ {
+ msg_Err( p_demux, "Couldn't read frame with ASDCP");
+ return VLC_EGENERIC;
+ }
+
+ if( ( p_frame = block_Alloc( PicFrameBuff.Size() ) ) != NULL )
+ memcpy( p_frame->p_buffer, PicFrameBuff.Data(), PicFrameBuff.Size() );
+ else
+ {
+ msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+ return VLC_ENOMEM;
+ }
+
+ p_frame->i_buffer = PicFrameBuff.Size();
+ break;
+ }
+ case ESS_MPEG2_VES:
+ {
+ MPEG2::MXFReader *p_VideoMXFReader = p_sys->p_VideoMXFReader;
+ MPEG2::FrameBuffer VideoFrameBuff( FRAME_BUFFER_SIZE );
+
+ Result_t result = p_VideoMXFReader->ReadFrame( i, VideoFrameBuff, 0, 0 );
+ if( ! ASDCP_SUCCESS( result ) )
+ {
+ msg_Err( p_demux, "Couldn't read frame with ASDCP");
+ return VLC_EGENERIC;
+ }
+
+ if( ( p_frame = block_Alloc( VideoFrameBuff.Size() ) ) != NULL )
+ memcpy( p_frame->p_buffer, VideoFrameBuff.Data(), VideoFrameBuff.Size() );
+ else
+ {
+ msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+ return VLC_ENOMEM;
+ }
+
+ p_frame->i_buffer = VideoFrameBuff.Size();
+ break;
+ }
+ default:
+ msg_Err( p_demux, "Video file has an audio or subtitle format. DCP may be broken. Check ASSETMAP file." );
+ return VLC_EGENERIC;
+ }
+
+ p_frame->i_flags |= BLOCK_FLAG_TYPE_I;
+ p_frame->i_length = 1000000 / p_sys->frame_rate;
+ p_frame->i_pts = 1000000 * p_sys->frame_no / p_sys->frame_rate;
+
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_frame->i_pts );
+ es_out_Send( p_demux->out, p_sys->p_video_es, p_frame );
+
+ /* audio frame */
+ PCM::MXFReader *p_AudioMXFReader = p_sys->p_AudioMXFReader;
+ PCM::FrameBuffer AudioFrameBuff( PCM::CalcFrameBufferSize( *p_sys->p_AudioDesc ) );
+ p_frame = NULL;
+
+ Result_t result = p_AudioMXFReader->ReadFrame( i, AudioFrameBuff, 0, 0 );
+ if( ! ASDCP_SUCCESS( result ) )
+ {
+ msg_Err( p_demux, "Couldn't read frame with ASDCP");
+ return VLC_EGENERIC;
+ }
+
+ if( ( p_frame = block_Alloc( AudioFrameBuff.Size() ) ) != NULL )
+ memcpy( p_frame->p_buffer, AudioFrameBuff.Data(), AudioFrameBuff.Size() );
+ else
+ {
+ msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+ return VLC_ENOMEM;
+ }
+
+ p_frame->i_buffer = AudioFrameBuff.Size();
+ p_frame->i_length = 1000000 / p_sys->frame_rate;
+ p_frame->i_pts = 1000000 * p_sys->frame_no / p_sys->frame_rate;
+
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_frame->i_pts );
+ es_out_Send( p_demux->out, p_sys->p_audio_es, p_frame );
+
+ p_sys->frame_no++;
+
+ return 1;
+}
+
+static int Control( demux_t *p_demux, int query, va_list args )
+{
+ double f,*pf;
+ bool *pb;
+ int64_t *pi64;
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ switch ( query )
+ {
+ case DEMUX_CAN_CONTROL_PACE:
+ pb = ( bool* ) va_arg ( args, bool* );
+ *pb = true;
+ return VLC_SUCCESS;
+ case DEMUX_GET_POSITION:
+ pf = ( double* ) va_arg ( args, double* ); *pf = 0.0;
+ *pf = ( p_sys->frame_no / p_sys->frame_rate ) / ( p_sys->frames_total / p_sys->frame_rate );
+ return VLC_SUCCESS;
+ case DEMUX_SET_POSITION:
+ f = ( double ) va_arg ( args, double );
+ p_sys->frame_no = f * p_sys->frames_total;
+ return VLC_SUCCESS;
+ case DEMUX_GET_LENGTH:
+ pi64 = ( int64_t* ) va_arg ( args, int64_t* );
+ *pi64 = ( p_sys->frames_total / p_sys->frame_rate ) * 1000000;
+ return VLC_SUCCESS;
+ case DEMUX_GET_TIME:
+ pi64 = ( int64_t* ) va_arg ( args, int64_t* );
+ *pi64 = ( p_sys->frame_no / p_sys->frame_rate ) * 1000000;
+ return VLC_SUCCESS;
+ case DEMUX_SET_TIME:
+ f = ( double ) va_arg ( args, double );
+ p_sys->frame_no = f * p_sys->frame_rate / 1000000;
+ return VLC_SUCCESS;
+ default:
+ msg_Err( p_demux, "Unknown query %d in DCP Control", query );
+ return VLC_EGENERIC;
+ }
+
+ return VLC_SUCCESS;
+}
+
+/**
+ * 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 )
+{
+ msg_Dbg( p_demux, "dcpInit() in DCP Module : START" );
+
+ /* Allocate internal state */
+ demux_sys_t *p_sys = new demux_sys_t();
+ if( unlikely( p_sys == NULL ) )
+ return VLC_ENOMEM;
+ p_demux->p_sys = p_sys;
+
+ /* Allocate DCP object */
+ msg_Dbg( p_demux, "dcpInit() in DCP Module : allocation DCP object" );
+ dcp_t *p_dcp = new dcp_t;
+ if( unlikely( p_dcp == NULL ) )
+ return VLC_ENOMEM;
+
+ p_sys->p_dcp = p_dcp;
+
+ p_dcp->path = new char[ strlen( p_demux->psz_file ) + 5 ];
+ /* We check if there is a "/" at the end of the path */
+ if( endsWith( p_demux->psz_file, "/" ) )
+ strcpy( p_dcp->path, p_demux->psz_file );
+ else
+ strcpy( p_dcp->path, strcat( p_demux->psz_file, "/" ) );
+
+ /* Parsing XML files to get audio and video files */
+ msg_Dbg( p_demux, "dcpInit() in DCP Module : start parsing XML files" );
+ if( !parserXML( p_demux ) )
+ goto error;
+ msg_Dbg(p_demux, "dcpInit() in DCP Module : parsing XML files done");
+
+ msg_Dbg( p_demux, "Path = %s", p_sys->p_dcp->path );
+ msg_Dbg( p_demux, "ASSETMAP = %s", p_sys->p_dcp->assetmapfile );
+ msg_Dbg( p_demux, "PKL = %s", p_sys->p_dcp->pklfile );
+ msg_Dbg( p_demux, "CPL = %s", p_sys->p_dcp->cplfile );
+ msg_Dbg( p_demux, "Video = %s", p_sys->p_dcp->videofile );
+ msg_Dbg( p_demux, "Audio = %s", p_sys->p_dcp->audiofile );
+
+ return VLC_SUCCESS;
+
+ error:
+ delete( p_demux->p_sys );
+ return VLC_EGENERIC;
+}
+
+/**
+ * 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 parserXML ( demux_t * p_demux )
+{
+ dcp_t *p_dcp = p_demux->p_sys->p_dcp;
+
+ msg_Dbg( p_demux, "parserXML() in DCP Module : START" );
+
+ /* We get the ASSETMAP file path */
+ assetmapUri( p_demux );
+
+ msg_Dbg( p_demux, "Path to ASSETMAP file = %s", p_dcp->assetmapfile );
+
+ /* 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) */
+ parserAssetmapXML( p_demux );
+
+ msg_Dbg( p_demux, "Path to CPL file = %s", p_dcp->cplfile );
+
+ /* We parse the CPL File : in order to retrieve video and audio files
+ according to UID of files described in CPL XML file */
+ parserCplXML( p_demux );
+
+ msg_Dbg( p_demux, "parserXML() in DCP Module : END" );
+
+ return 1; /* TODO : perform checking on XML parsing */
+}
+
+/**
+ * Function to retrieve the path to the ASSETMAP file.
+ * @param p_demux DCP access_demux.
+ */
+void assetmapUri( demux_t * p_demux )
+{
+ DIR *dir = NULL;
+ struct dirent *ent = NULL;
+ int b_found = 0;
+
+ char * result = new char[ strlen( p_demux->psz_file ) + 1 ];
+ char * result2 = NULL;
+
+ /* copy of "path" in "res" */
+ strcpy( result, p_demux->psz_file );
+
+ if( ( dir = opendir ( p_demux -> psz_file ) ) != 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 )
+ {
+ result2 = new char[ strlen( result ) + 1 ];
+ strcpy( result2, result );
+ delete result;
+ result = new char[ strlen( result2 ) + strlen( ent->d_name ) + 1 ];
+ strcpy( result, result2 );
+ strcat( result, ( const char * ) ent->d_name );
+ delete result2;
+ b_found = 1;
+ }
+ }
+ closedir( dir );
+ }
+ else
+ msg_Err( p_demux, "Could not open the directory : %s", p_demux -> psz_file );
+
+ /* if no assetmap file */
+ if( !b_found )
+ {
+ msg_Err( p_demux, "No ASSETMAP found in the directory : %s", p_demux -> psz_file );
+ result = NULL;
+ }
+
+
+ /* We check if we actually get the ASSETMAP file path */
+ if( result == NULL )
+ msg_Err( p_demux, "No ASSETMAP XML file found in the DCP directory" );
+
+ /* We copy the ASSETMAP file path in the DCP structure */
+ p_demux->p_sys->p_dcp->assetmapfile = new char[ strlen( result ) + 1 ];
+ strcpy( p_demux->p_sys->p_dcp->assetmapfile, result );
+
+ /* free memory */
+ delete result;
+}
+
+/**
+ * Function to parse the assetmap file in order to get CPL file path, PKL file path and
+ * to store UID/Path of files described in the ASSETMAP XML file.
+ * @param p_demux DCP access_demux.
+ */
+void parserAssetmapXML ( demux_t * p_demux )
+{
+ msg_Dbg( p_demux, "parserAssetmapXML() in DCP Module : START" );
+
+ /* variables */
+ dcp_t *p_dcp = p_demux->p_sys->p_dcp;
+ int node = 0;
+ int packinglist = 0;
+ int file_number = 0;
+ int asset_list = 0;
+
+ char * filepath = NULL;
+
+ /* init libxml2 */
+ LIBXML_TEST_VERSION;
+
+ /* init XML reader */
+ msg_Dbg( p_demux, "parserAssetmapXML() in DCP Module : loading XML reader with %s", p_dcp->assetmapfile );
+ xmlTextReaderPtr reader = xmlReaderForFile( ( const char * ) p_dcp->assetmapfile, NULL, 0 );
+
+ msg_Dbg( p_demux, "parserAssetmapXML() in DCP Module : reading XML file" );
+
+ /* reading XML file ( node by node ) */
+ node = xmlTextReaderRead( reader );
+ while( node == 1 )
+ {
+ /* Uncomment this line to print XML content node by node */
+ /* msg_Dbg(p_demux, "Name = %s, Value = %s", ( char * ) xmlTextReaderConstName( reader ), ( char * )xmlTextReaderConstValue( reader ) ); */
+
+ /* This condition determines if we are in AssetList part or not */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "AssetList" ) == 0 )
+ {
+ if( asset_list == 0 )
+ asset_list = 1;
+ else
+ asset_list = 0;
+ }
+
+ /* When we are in AssetList part */
+ if( asset_list == 1 )
+ {
+ /* Set the UID of the file */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "Id" ) == 0 )
+ {
+ xmlTextReaderRead( reader );
+ p_dcp->files[ file_number ] = new file_t;
+ p_dcp->files[ file_number ]->id = new char[ strlen( (char *)xmlTextReaderConstValue( reader ) ) + 1 ];
+ msg_Dbg( p_demux, "ID = %s", ( char* ) xmlTextReaderConstValue ( reader ) );
+ strcpy( p_dcp->files[file_number]->id, (char *)xmlTextReaderConstValue( reader ) );
+ xmlTextReaderRead( reader );
+ }
+
+ /* It determines if it is PKL File or not */
+ /* For PKL File, we have : <PackingList>true</PackingList> */
+ /* This tag seems to be used only for PKL file */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "PackingList" ) == 0 )
+ packinglist = 1; /* next path info will be for PKL file */
+
+ /* Set the Path of the file */
+ /* If the file is a XML file, it is the PKL file or the CPL file */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "Path" ) == 0 )
+ {
+ xmlTextReaderRead( reader );
+ msg_Dbg( p_demux, "Path = %s", ( char * ) xmlTextReaderConstValue( reader ) );
+ filepath = new char[ strlen( ( char* ) xmlTextReaderConstValue( reader ) ) + strlen( p_dcp->path ) + 1 ];
+ strcpy( filepath, p_dcp->path );
+ filepath = strcat( filepath, ( char * ) xmlTextReaderConstValue( reader ) );
+ msg_Dbg( p_demux, "filepath = %s", filepath );
+ if( endsWith( ( char * ) xmlTextReaderConstValue( reader ), ".xml" ) )
+ {
+ if( packinglist == 1 )
+ {
+ /* it is PKL file name */
+ p_dcp->pklfile = new char[ strlen( filepath ) + 1 ];
+ p_dcp->files[file_number]->path = new char[ strlen( filepath ) + 1 ];;
+ strcpy( p_dcp->pklfile, filepath );
+ strcpy( p_dcp->files[file_number]->path, filepath );
+ packinglist = 0;
+ }
+ else
+ {
+ /* it is CPL file name */
+ p_dcp->cplfile = new char[ strlen( filepath ) + 1 ];;
+ p_dcp->files[file_number]->path = new char[ strlen( filepath ) + 1 ];;
+ strcpy( p_dcp->cplfile, filepath );
+ strcpy( p_dcp->files[file_number]->path, filepath );
+ }
+ }
+ else
+ {
+ /* it is an other file (MXF in theory) */
+ p_dcp->files[file_number]->path = new char[ strlen( filepath ) + 1 ];;
+ strcpy( p_dcp->files[file_number]->path, filepath );
+ }
+
+ /* next node */
+ xmlTextReaderRead( reader );
+
+ /* next file */
+ file_number++;
+ }
+ }
+
+ /* next node */
+ node = xmlTextReaderRead( reader );
+ }
+
+ /* Set the number of files described in ASSETMAP file */
+ p_dcp->nb_files = file_number;
+
+ /* free memory */
+ xmlFreeTextReader( reader );
+ xmlCleanupParser();
+ xmlMemoryDump();
+ delete filepath;
+
+ msg_Dbg( p_demux, "parserAssetmapXML() in DCP Module : END" );
+}
+
+/**
+ * Function to parse the CPL file in order to retrieve UID of video and audio files.
+ * When it is done, we can determine path to video and audio files using information
+ * about files described in ASSETMAP file
+ * @param p_demux DCP access_demux.
+ */
+void parserCplXML ( demux_t * p_demux )
+{
+ msg_Dbg( p_demux, "parserCplXML() in DCP Module : START" );
+
+ /* variables */
+ dcp_t *p_dcp = p_demux->p_sys->p_dcp;
+ int incr = 0;
+ int node = 0;
+
+ msg_Dbg( p_demux, "Full path to CPL file : %s", p_dcp->cplfile );
+
+ /* init libxml2 */
+ LIBXML_TEST_VERSION;
+
+ /* init XML reader */
+ msg_Dbg( p_demux, "parserCplXML() in DCP Module : loading XML reader with %s", p_dcp->cplfile );
+ xmlTextReaderPtr reader = xmlReaderForFile( ( const char * ) p_dcp->cplfile, NULL, 0 );
+
+ msg_Dbg( p_demux, "parserCplXML() in DCP Module : reading XML file" );
+
+ /* Read XML file node by node */
+ node = xmlTextReaderRead( reader );
+ while( node == 1 )
+ {
+ /* This variable is used to prevent the confusing case : foo</MainPicture><MainSound>foo */
+ incr = 0;
+
+ /* Uncomment this line to print XML content node by node */
+ /* msg_Dbg( p_demux, "Name = %s, Value = %s", ( char * ) xmlTextReaderConstName( reader ), ( char * )xmlTextReaderConstValue( reader ) ); */
+
+ /* MainPicture data */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "MainPicture" ) == 0 || strcmp( ( char * ) xmlTextReaderConstName( reader ), "MainStereoscopicPicture" ) == 0 )
+ {
+ incr = 1;
+ node = xmlTextReaderRead( reader );
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "#text" ) == 0 )
+ {
+ node = xmlTextReaderRead( reader );
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "Id" ) == 0 )
+ {
+ node = xmlTextReaderRead( reader );
+ /* ID value contained in : ( char * ) xmlTextReaderConstValue( reader ) */
+ for( int i = 0; i < p_dcp->nb_files; i++ )
+ {
+ if( strcmp( p_dcp->files[i]->id,( char * ) xmlTextReaderConstValue( reader ) ) == 0 )
+ {
+ p_dcp->videofile = new char[ strlen( p_dcp->files[i]->path ) + 1 ];
+ strcpy( p_dcp->videofile, p_dcp->files[i]->path );
+ }
+ }
+ }
+ }
+ }
+
+ /* MainSound data */
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "MainSound" ) == 0 )
+ {
+ incr = 1;
+ node = xmlTextReaderRead( reader );
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "#text" ) == 0 )
+ {
+ node = xmlTextReaderRead( reader );
+ if( strcmp( ( char * ) xmlTextReaderConstName( reader ), "Id" ) == 0 )
+ {
+ node = xmlTextReaderRead( reader );
+ /* ID value contained in : ( char * ) xmlTextReaderConstValue( reader ) */
+ for( int i = 0; i < p_dcp->nb_files; i++ )
+ {
+ if( strcmp( p_dcp->files[i]->id,( char * ) xmlTextReaderConstValue( reader ) ) == 0 )
+ {
+ p_dcp->audiofile = new char[ strlen( p_dcp->files[i]->path ) + 1 ];
+ strcpy( p_dcp->audiofile, p_dcp->files[i]->path );
+ }
+ }
+ }
+ }
+ }
+
+ /* We do not go to the next node when we have </MainPicture><MainSound> */
+ if( incr == 0 )
+ node = xmlTextReaderRead( reader );
+ }
+
+ /* free memory */
+ xmlFreeTextReader( reader );
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ msg_Dbg( p_demux, "parserCplXML() in DCP Module : END" );
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+/////// ///////
+/////// Low-level functions : Strings manipulations, Free function ///////
+/////// ///////
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Function to check if a file name ends with a given suffix
+ * @param str File name
+ * @param suffix Suffix to look for
+ * @return true if the file name ends with the given suffix, fale otherwise
+ */
+int endsWith( const char * str, const char * suffix )
+{
+ if( !str || !suffix )
+ return 0;
+ size_t lenstr = strlen( str );
+ size_t lensuffix = strlen( suffix );
+ if( lensuffix > lenstr )
+ return 0;
+ return strncasecmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
+}
--
1.7.9.5
More information about the vlc-devel
mailing list