[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