[vlc-devel] [PATCH] dcp: Creation of access-demux module for DCP (using asdcplib)

Nicolas Bertrand nicoinattendu at gmail.com
Fri Jul 12 22:37:18 CEST 2013


From: Simona-Marinela Prodea <simonamarinela.prodea at etu.enseeiht.fr>

---
 configure.ac              |   17 +
 modules/access/Modules.am |    7 +
 modules/access/dcp.cpp    | 1174 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1198 insertions(+)
 create mode 100644 modules/access/dcp.cpp

diff --git a/configure.ac b/configure.ac
index c8e3f31..37830de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1962,6 +1962,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 "${enable_dcp}" != "no"], [
+  AC_LANG_PUSH(C++)
+  AC_CHECK_HEADERS( [[AS@&t at _DCP.h]], 
+    [have_asdcp="yes"],
+    [AS_IF( [test "${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/Modules.am b/modules/access/Modules.am
index b10118d..5c9fb35 100644
--- a/modules/access/Modules.am
+++ b/modules/access/Modules.am
@@ -24,6 +24,13 @@ 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)
+libdcp_plugin_la_LIBADD = $(AM_LIBADD) -lasdcp
+if HAVE_ASDCP
+libvlc_LTLIBRARIES += libdcp_plugin.la
+endif
+
 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..726aaea
--- /dev/null
+++ b/modules/access/dcp.cpp
@@ -0,0 +1,1174 @@
+/*****************************************************************************
+ * 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 <iostream>
+#include <string>
+#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>
+#include <vlc_xml.h>
+#include <vlc_url.h>
+#include <vlc_stream.h>
+
+/* ASDCP headers */
+#include <AS_DCP.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_( "DCP access-demux module" ) )
+    set_capability( "access_demux", 0 )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_ACCESS )
+    set_callbacks( Open, Close )
+vlc_module_end()
+
+//! File structure class
+class file_t
+{
+ public:
+    string id;
+    string path;
+};
+
+//! DCP class
+/*! This class stores the most important information about the DCP */
+class dcp_t
+{
+ public:
+    string path;             /*!< Path to DCP directory */
+    char * assetmapURI;      /*!< ASSETMAP XML file URI  - will be allocated with malloc in vlc_path2uri */
+    char * cplfileURI;       /*!< CPL XML file URI - will be allocated with malloc in vlc_path2uri */
+    string pklfile;          /*!< PKL XML file name */
+    string videofile;        /*!< Video file name */
+    string audiofile;        /*!< Audio file name */
+    int nb_files;            /*!< Number of files defined in ASSETMAP */
+    file_t *files[20];       /*!< Files defined in ASSETMAP : we assume that there are less than 20 files */
+
+    dcp_t():
+        assetmapURI( NULL ),
+        cplfileURI( NULL ),
+        nb_files( 0 )
+    {
+        for( int i=0; i < 20; i++ )
+            files[i] = NULL;
+    }
+    ~dcp_t()
+    {
+        free( assetmapURI );
+        free( cplfileURI );
+        for( int i=0; i < 20; i++ )
+            delete files[i];
+    }
+};
+
+/* 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;
+
+    demux_sys_t():
+        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 )
+    {
+        PictureEssType = ESS_UNKNOWN;
+    }
+    ~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;
+        if( p_video_es )
+            es_out_Del( p_out, p_video_es );
+        if( p_audio_es )
+            es_out_Del( p_out, p_audio_es );
+        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 );
+int assetmapPath( demux_t * p_demux );
+int parserAssetmapXML ( demux_t * p_demux );
+int parserCplXML ( demux_t * p_demux );
+bool endsWith( const char * str, const char * suffix );
+int initXMLParser( demux_t *p_demux, char * filePath, stream_t **p_stream, xml_t **p_xml, xml_reader_t **p_xmlReader );
+void fillVideoFmt( video_format_t * fmt, unsigned int width, unsigned int height, unsigned int frame_rate_num, unsigned int frame_rate_denom );
+void freeMemXML( xml_reader_t *p_xml_reader, xml_t *p_xml, stream_t *p_stream );
+void freeMem( demux_t *p_demux );
+
+static int Open( vlc_object_t *obj )
+{
+    demux_t *p_demux = ( demux_t* ) obj;
+    demux_sys_t *p_sys = NULL;
+    es_format_t video_format, audio_format;
+    vlc_fourcc_t fcc;
+    int retval;
+
+    if( !p_demux->psz_file )
+        return VLC_EGENERIC;
+
+    /* handle the DCP directory, saving the paths for audio and video file, returning error if unsuccessful */
+    if( ( retval = dcpInit( p_demux ) ) )
+    {
+        freeMem( p_demux );
+        return retval;
+    }
+
+    p_sys = p_demux->p_sys;
+
+    /***************************************************
+     ***************** 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() );
+            freeMem( p_demux );
+            return VLC_EGENERIC;
+        case ESS_JPEG_2000:
+        {
+            fcc = VLC_FOURCC( 'J', 'P', '2', 'K' );
+            JP2K::MXFReader *p_PicMXFReader = p_sys->p_PicMXFReader = new ( nothrow ) JP2K::MXFReader();
+            JP2K::PictureDescriptor *p_PicDesc = p_sys->p_PicDesc = new ( nothrow ) JP2K::PictureDescriptor();
+
+            if( !p_PicMXFReader || !p_PicDesc )
+            {
+                msg_Err( p_demux, "Could not alloc object for handling mxf" );
+                freeMem( p_demux );
+                return VLC_ENOMEM;
+            }
+            Result_t result = p_PicMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() );
+            if( !ASDCP_SUCCESS( result ) )
+            {
+                msg_Err( p_demux, "File %s could not be opened with asdcp", p_sys->p_dcp->videofile.c_str() );
+                freeMem( p_demux );
+                return VLC_EGENERIC;
+            }
+
+            p_PicMXFReader->FillPictureDescriptor( *p_PicDesc );
+
+            es_format_Init( &video_format, VIDEO_ES, fcc );
+            fillVideoFmt( &video_format.video, p_PicDesc->StoredWidth, p_PicDesc->StoredHeight, p_PicDesc->EditRate.Numerator, p_PicDesc->EditRate.Denominator );
+
+            p_sys->p_PicMXFReader = p_PicMXFReader;
+            p_sys->p_PicDesc = p_PicDesc;
+            p_sys->frame_no = 0;
+            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;
+
+            break;
+        }
+        case ESS_JPEG_2000_S:
+        {
+            fcc = VLC_FOURCC( 'J', 'P', '2', 'K' );
+            JP2K::MXFSReader *p_PicMXFSReader = p_sys->p_PicMXFSReader = new ( nothrow ) JP2K::MXFSReader();
+            JP2K::PictureDescriptor *p_PicDesc = p_sys->p_PicDesc = new ( nothrow ) JP2K::PictureDescriptor();
+
+            if( !p_PicMXFSReader || !p_PicDesc )
+            {
+                msg_Err( p_demux, "Could not alloc object for handling mxf" );
+                freeMem( p_demux );
+                return VLC_ENOMEM;
+            }
+
+            Result_t result = p_PicMXFSReader->OpenRead( p_sys->p_dcp->videofile.c_str() );
+            if( !ASDCP_SUCCESS( result ) )
+            {
+                msg_Err( p_demux, "File %s could not be opened with asdcp", p_sys->p_dcp->videofile.c_str() );
+                freeMem( p_demux );
+                return VLC_EGENERIC;
+            }
+
+            p_PicMXFSReader->FillPictureDescriptor( *p_PicDesc );
+
+            es_format_Init( &video_format, VIDEO_ES, fcc );
+            fillVideoFmt( &video_format.video, p_PicDesc->StoredWidth, p_PicDesc->StoredHeight, p_PicDesc->EditRate.Numerator, p_PicDesc->EditRate.Denominator );
+
+            p_sys->p_PicMXFSReader = p_PicMXFSReader;
+            p_sys->p_PicDesc = p_PicDesc;
+            p_sys->frame_no = 0;
+            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;
+
+            break;
+        }
+        case ESS_MPEG2_VES:
+        {
+            fcc = VLC_FOURCC( 'm', 'p', 'g', 'v' );
+            MPEG2::MXFReader *p_VideoMXFReader = p_sys->p_VideoMXFReader = new ( nothrow ) MPEG2::MXFReader();
+            MPEG2::VideoDescriptor *p_VideoDesc = p_sys->p_VideoDesc = new ( nothrow ) MPEG2::VideoDescriptor();
+
+            if( !p_VideoMXFReader || !p_VideoDesc )
+            {
+                msg_Err( p_demux, "Could not alloc object for handling mxf" );
+                freeMem( p_demux );
+                return VLC_ENOMEM;
+            }
+
+            Result_t result = p_VideoMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() );
+            if( !ASDCP_SUCCESS( result ) )
+            {
+                msg_Err( p_demux, "File %s could not be opened with asdcp", p_sys->p_dcp->videofile.c_str() );
+                freeMem( p_demux );
+                return VLC_EGENERIC;
+            }
+
+            p_VideoMXFReader->FillVideoDescriptor( *p_VideoDesc );
+
+            es_format_Init( &video_format, VIDEO_ES, fcc );
+            fillVideoFmt( &video_format.video, p_VideoDesc->StoredWidth, p_VideoDesc->StoredHeight, p_VideoDesc->EditRate.Numerator, p_VideoDesc->EditRate.Denominator );
+
+            p_sys->p_VideoMXFReader = p_VideoMXFReader;
+            p_sys->p_VideoDesc = p_VideoDesc;
+            p_sys->frame_no = 0;
+            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;
+
+            break;
+        }
+        default:
+            msg_Err( p_demux, "Unrecognized video format" );
+            freeMem( p_demux );
+            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" );
+        freeMem( p_demux );
+        return VLC_EGENERIC;
+    }
+
+    /***************************************************
+     ***************** open audio file *****************
+     ***************************************************/
+    EssenceType_t AudioEssType;
+    EssenceType( p_sys->p_dcp->audiofile.c_str(), AudioEssType );
+    switch( AudioEssType )
+    {
+        case ESS_UNKNOWN:
+            msg_Err( p_demux, "The file %s is not a supported AS_DCP essence container", p_sys->p_dcp->audiofile.c_str() );
+            freeMem( p_demux );
+            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, "Unrecognized audio format" );
+            freeMem( p_demux );
+            return VLC_EGENERIC;
+    }
+
+    PCM::MXFReader *p_AudioMXFReader = p_sys->p_AudioMXFReader = new ( nothrow ) PCM::MXFReader();
+    PCM::AudioDescriptor *p_AudioDesc = p_sys->p_AudioDesc = new ( nothrow ) PCM::AudioDescriptor();
+
+    if( !p_AudioMXFReader || !p_AudioDesc )
+    {
+        msg_Err( p_demux, "Could not alloc object for handling mxf" );
+        freeMem( p_demux );
+        return VLC_ENOMEM;
+    }
+
+    Result_t result = p_AudioMXFReader->OpenRead( p_sys->p_dcp->audiofile.c_str() );
+    if( !ASDCP_SUCCESS( result ) )
+    {
+        msg_Err( p_demux, "File %s could not be opened with asdcp", p_sys->p_dcp->audiofile.c_str() );
+        freeMem( p_demux );
+        return VLC_EGENERIC;
+    }
+
+    p_AudioMXFReader->FillAudioDescriptor( *p_AudioDesc );
+
+    es_format_Init( &audio_format, AUDIO_ES, fcc );
+    if( p_AudioDesc->AudioSamplingRate.Denominator != 0 )
+        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" );
+        freeMem( p_demux );
+        return VLC_EGENERIC;
+    }
+
+    p_sys->p_out = p_demux->out;
+    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;
+    freeMem( p_demux );
+}
+
+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;
+    mtime_t pts = 0;
+
+    if( i == p_sys->frames_total )
+        return VLC_SUCCESS;
+
+    /* video frame */
+    switch( p_sys->PictureEssType )
+    {
+        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_video_frame = block_Alloc( PicFrameBuff.Size() ) ) != NULL )
+                memcpy( p_video_frame->p_buffer, PicFrameBuff.Data(), PicFrameBuff.Size() );
+            else
+            {
+                msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+                return VLC_ENOMEM;
+            }
+
+            p_video_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_video_frame = block_Alloc( PicFrameBuff.Size() ) ) != NULL )
+                memcpy( p_video_frame->p_buffer, PicFrameBuff.Data(), PicFrameBuff.Size() );
+            else
+            {
+                msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+                return VLC_ENOMEM;
+            }
+
+            p_video_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_video_frame = block_Alloc( VideoFrameBuff.Size() ) ) != NULL )
+                memcpy( p_video_frame->p_buffer, VideoFrameBuff.Data(), VideoFrameBuff.Size() );
+            else
+            {
+                msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+                return VLC_ENOMEM;
+            }
+
+            p_video_frame->i_buffer = VideoFrameBuff.Size();
+            break;
+        }
+        default:
+            msg_Err( p_demux, "Unrecognized video format" );
+            return VLC_EGENERIC;
+    }
+
+    if( p_sys->frame_rate_num != 0 )
+    {
+        p_video_frame->i_length = 1000000 * p_sys->frame_rate_denom / p_sys->frame_rate_num;
+        p_video_frame->i_pts = 1000000 * p_sys->frame_no * p_sys->frame_rate_denom / p_sys->frame_rate_num;
+    }
+    else
+    {
+        msg_Err( p_demux, "Frame rate 0");
+        return VLC_EBADVAR;
+    }
+
+    /* audio frame */
+    PCM::MXFReader *p_AudioMXFReader = p_sys->p_AudioMXFReader;
+    PCM::FrameBuffer AudioFrameBuff( PCM::CalcFrameBufferSize( *p_sys->p_AudioDesc ) );
+
+    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_audio_frame = block_Alloc( AudioFrameBuff.Size() ) ) != NULL )
+        memcpy( p_audio_frame->p_buffer, AudioFrameBuff.Data(), AudioFrameBuff.Size() );
+    else
+    {
+        msg_Err( p_demux, "Couldn't alloc block for elementary stream" );
+        return VLC_ENOMEM;
+    }
+
+    p_audio_frame->i_buffer = AudioFrameBuff.Size();
+    if( p_sys->frame_rate_num != 0 )
+    {
+        p_audio_frame->i_length = 1000000 * p_sys->frame_rate_denom / p_sys->frame_rate_num;
+        p_audio_frame->i_pts = 1000000 * p_sys->frame_no * p_sys->frame_rate_denom / p_sys->frame_rate_num;
+    }
+    else
+    {
+        msg_Err( p_demux, "Frame rate 0");
+        return VLC_EBADVAR;
+    }
+
+    if( p_video_frame->i_pts < p_audio_frame->i_pts )
+        pts = p_video_frame->i_pts;
+    else
+        pts = p_audio_frame->i_pts;
+    es_out_Control( p_demux->out, ES_OUT_SET_PCR, 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;
+}
+
+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;
+            return VLC_SUCCESS;
+        case DEMUX_SET_PAUSE_STATE:
+            return VLC_SUCCESS;
+        case DEMUX_CAN_SEEK:
+            if( p_sys->PictureEssType != ESS_MPEG2_VES )
+            {
+                pb = (bool *)va_arg( args, bool * );
+                *pb = true;
+                return VLC_SUCCESS;
+            }
+            else
+            {
+                msg_Warn( p_demux, "Unknown query %d in DCP Control", query );
+                return VLC_EGENERIC;
+            }
+        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_Err( p_demux, "Total number of frames is 0" );
+                break;
+            }
+            return VLC_SUCCESS;
+        case DEMUX_SET_POSITION:
+            f = ( double ) va_arg ( args, double );
+            p_sys->frame_no = (int) ( f * p_sys->frames_total );
+            return VLC_SUCCESS;
+        case DEMUX_GET_LENGTH:
+            pi64 = ( int64_t* ) va_arg ( args, int64_t* );
+            if( p_sys->frame_rate_num != 0 )
+                *pi64 = (int) ( ( p_sys->frames_total * p_sys->frame_rate_denom / p_sys->frame_rate_num ) * 1000000 );
+            else
+            {
+                msg_Err( p_demux, "Frame rate is 0" );
+                return VLC_EBADVAR;
+            }
+            return VLC_SUCCESS;
+        case DEMUX_GET_TIME:
+            pi64 = ( int64_t* ) va_arg ( args, int64_t* );
+            if( p_sys->frame_rate_num != 0 )
+                *pi64 = (int) ( ( p_sys->frame_no *p_sys->frame_rate_denom / p_sys->frame_rate_num ) * 1000000 );
+            else
+            {
+                msg_Err( p_demux, "Frame rate is 0" );
+                return VLC_EBADVAR;
+            }
+            return VLC_SUCCESS;
+        case DEMUX_SET_TIME:
+            i64 = ( int64_t ) va_arg ( args, int64_t );
+            if( p_sys->frame_rate_num != 0 )
+            {
+                p_sys->frame_no = i64 * p_sys->frame_rate_num / ( 1000000 * p_sys->frame_rate_denom );
+                es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, ( mtime_t ) i64 );
+            }
+            else
+            {
+                msg_Err( p_demux, "Frame rate is 0" );
+                return VLC_EBADVAR;
+            }
+            return VLC_SUCCESS;
+        default:
+            msg_Warn( 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 )
+{
+    int retval;
+    /* Allocate internal state */
+    demux_sys_t *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 ) )
+        return VLC_ENOMEM;
+
+    p_sys->p_dcp = p_dcp;
+
+    p_dcp->path = p_demux->psz_file;
+    /* We check if there is a "/" at the end of the path */
+    if( !endsWith( p_demux->psz_file, "/" ) )
+    {
+        try
+        {
+            p_dcp->path.append( "/" );
+        }
+        catch(...)
+        {
+            msg_Err( p_demux, "String handling failed" );
+            return VLC_EGENERIC;
+        }
+    }
+
+    /* Parsing XML files to get audio and video files */
+    msg_Dbg( p_demux, "parsing XML files..." );
+    if( ( retval = parserXML( p_demux ) ) )
+        return retval;
+    msg_Dbg(p_demux, "parsing XML files done");
+
+    msg_Dbg( p_demux, "path = %s", p_sys->p_dcp->path.c_str() );
+    msg_Dbg( p_demux, "ASSETMAP = %s", p_sys->p_dcp->assetmapURI );
+    msg_Dbg( p_demux, "CPL = %s", p_sys->p_dcp->cplfileURI );
+    msg_Dbg( p_demux, "PKL = %s", p_sys->p_dcp->pklfile.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() );
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * 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 )
+{
+    int retval;
+    /* We get the ASSETMAP file path */
+    if( assetmapPath( p_demux ) )
+        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) */
+    if( ( retval = parserAssetmapXML( p_demux ) ) )
+        return retval;
+
+    /* We parse the CPL File : in order to retrieve video and audio files
+     according to UID of files described in CPL XML file */
+    if( ( retval = parserCplXML( p_demux ) ) )
+        return retval;
+
+    return VLC_SUCCESS; /* TODO : perform checking on XML parsing */
+}
+
+/**
+ * Function to retrieve the path to the ASSETMAP file.
+ * @param p_demux DCP access_demux.
+ */
+int assetmapPath( demux_t * p_demux )
+{
+    DIR *dir = NULL;
+    struct dirent *ent = NULL;
+    string result;
+
+    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 )
+            {
+                /* copy of "path" in "res" */
+                result = p_demux->psz_file;
+                try
+                {
+                    result.append( ent->d_name );
+                }
+                catch(...)
+                {
+                    msg_Err( p_demux, "String handling failed" );
+                    return VLC_EGENERIC;
+                }
+                break;
+            }
+        }
+        closedir( dir );
+    }
+    else
+    {
+        msg_Err( p_demux, "Could not open the directory: %s", p_demux->psz_file );
+        return VLC_EGENERIC;
+    }
+    /* if no assetmap file */
+    if( result.empty() )
+    {
+        msg_Err( p_demux, "No ASSETMAP found in the directory: %s", p_demux->psz_file );
+        return VLC_EGENERIC;
+    }
+
+    /* We convert the ASSETMAP path into URI (transformation needed later, when reading with XML Reader) 
+     * and copy it into p_dcp structure */
+    p_demux->p_sys->p_dcp->assetmapURI = vlc_path2uri( result.c_str(), "file" );
+    if( unlikely( p_demux->p_sys->p_dcp->assetmapURI == NULL ) )
+        return VLC_ENOMEM;
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * 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.
+ */
+int parserAssetmapXML ( demux_t * p_demux )
+{
+    dcp_t *p_dcp = p_demux->p_sys->p_dcp;
+    int type = 0;
+    const char* node = NULL;
+    int packinglist = 0;
+    int file_number = 0;
+    int asset_list = 0;
+    string filepath;
+    stream_t *p_stream = NULL;
+    xml_t *p_xml = NULL;
+    xml_reader_t *p_xmlReader = NULL;
+
+    /* init XML parser */
+    if( initXMLParser( p_demux, p_dcp->assetmapURI, &p_stream, &p_xml, &p_xmlReader ) )
+    {
+        msg_Err( p_demux, "Failed to initialize XML parser" );
+        goto error;
+    }
+
+    /* reading XML file ( node by node ) */
+    msg_Dbg( p_demux, "reading ASSETMAP file..." );
+    while( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) > 0 )
+    {
+        /* This condition determines if we are in AssetList part or not */
+        if( ( type == XML_READER_STARTELEM ) && ( strcasecmp( node, "AssetList" ) == 0 ) )
+        {
+            asset_list = 1;
+            continue;
+        }
+
+        /* When we are in AssetList part */
+        if( asset_list == 1 )
+        {
+            /* Set the UID of the file */
+            if( ( type == XML_READER_STARTELEM ) && ( strcasecmp( node, "Id" ) == 0 ) )
+            {
+                if( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) != XML_READER_TEXT )
+                {
+                    msg_Err( p_demux, "Id missing in ASSETMAP!" );
+                    goto error;
+                }
+                else
+                {
+                    p_dcp->files[ file_number ] = new ( nothrow ) file_t;
+                    if( !p_dcp->files[ file_number ] )
+                    {
+                        msg_Err( p_demux, "Could not alloc variable." );
+                        goto error;
+                    }
+                    if( node != NULL )
+                        p_dcp->files[file_number]->id = node;
+                    continue;
+                }
+            }
+
+            /* 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( ( type == XML_READER_STARTELEM ) && ( strcasecmp ( node, "PackingList" ) == 0 ) )
+            {
+                packinglist = 1; /* next path info will be for PKL file */
+                continue;
+            }
+
+            /* Set the Path of the file */
+            /* If the file is an XML file, it is the PKL file or the CPL file */
+            if( ( type == XML_READER_STARTELEM ) && ( strcasecmp( node, "Path" ) == 0 ) )
+            {
+                if( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) != XML_READER_TEXT )
+                {
+                    msg_Err( p_demux, "Path missing in ASSETMAP" );
+                    goto error;
+                }
+                else
+                {
+                    filepath = p_dcp->path + node;
+                    if( endsWith( node, ".xml" ) )
+                    {
+                        if( packinglist == 1 )
+                        {
+                            /* it is PKL file name */
+                            p_dcp->pklfile = filepath;
+                            p_dcp->files[file_number]->path = filepath;
+                            packinglist = 0;
+                        }
+                        else
+                        {
+                            /* it is CPL file name */
+                            p_dcp->cplfileURI = vlc_path2uri( filepath.c_str(), "file" );
+                            p_dcp->files[file_number]->path = filepath;
+                        }
+                    }
+                    else
+                    {
+                        /* it is an other file (MXF in theory) */
+                        p_dcp->files[file_number]->path = filepath;
+                    }
+                    file_number++;
+                }
+            }
+        }
+    }
+    if( asset_list == 0 )
+    {
+        msg_Err( p_demux, "Could not find AssetList tag in ASSETMAP" );
+        goto error;
+    }
+
+    if( !p_dcp->cplfileURI )
+    {
+        msg_Err( p_demux, "Could not find CPL file in ASSETMAP" );
+        goto error;
+    }
+    if( p_dcp->pklfile.empty() )
+    {
+        msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
+        goto error;
+    }
+
+    /* Set the number of files described in ASSETMAP file */
+    p_dcp->nb_files = file_number;
+
+    /* free memory */
+    freeMemXML( p_xmlReader, p_xml, p_stream );
+    return VLC_SUCCESS;
+
+error:
+    freeMemXML( p_xmlReader, p_xml, p_stream );
+    return VLC_EGENERIC;
+}
+
+/**
+ * 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.
+ */
+int parserCplXML ( demux_t * p_demux )
+{
+    dcp_t *p_dcp = p_demux->p_sys->p_dcp;
+    int type = 0;
+    bool found = false;
+    const char *node = NULL;
+    stream_t *p_stream = NULL;
+    xml_t *p_xml = NULL;
+    xml_reader_t *p_xmlReader = NULL;
+
+    /* init XML reader */
+    if( initXMLParser( p_demux, p_dcp->cplfileURI, &p_stream, &p_xml, &p_xmlReader ) )
+    {
+        msg_Err( p_demux, "Failed to initialize XML parser" );
+        goto error;
+    }
+
+    /* Read XML file node by node */
+    msg_Dbg( p_demux, "reading CPL file..." );
+    while( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) > 0 )
+    {
+        /* MainPicture data */
+        if( ( type == XML_READER_STARTELEM) && ( strcasecmp( node, "MainPicture" ) == 0 || strcasecmp( node, "MainStereoscopicPicture" ) == 0 ) )
+        {
+            /* searching for the Id of the picture file */
+            found = false;
+            while( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) > 0 )
+            {
+                if( ( type == XML_READER_STARTELEM ) && ( strcasecmp( node, "Id" ) == 0 ) )
+                {
+                    if( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) != XML_READER_TEXT )
+                    {
+                        msg_Err( p_demux, "MainPicture Id missing in CPL!" );
+                        goto error;
+                    }
+                    else
+                    {
+                        for( int i = 0; i < p_dcp->nb_files; i++ )
+                        {
+                            if( p_dcp->files[i]->id.compare( node ) == 0 )
+                            {
+                                p_dcp->videofile = p_dcp->files[i]->path;
+                                found = true;
+                                break;
+                            }
+                        }
+                        if( found )
+                            break;
+                    }
+                }
+            }
+        }
+
+        /* MainSound data */
+        if( ( type == XML_READER_STARTELEM) && ( strcasecmp( node, "MainSound" ) == 0 ) )
+        {
+            /* searching for the Id of the audio file */
+            found = false;
+            while( ( type = xml_ReaderNextNode( p_xmlReader, &node ) ) > 0 )
+            {
+                if( ( type == XML_READER_STARTELEM ) && ( strcasecmp( node, "Id" ) == 0 ) )
+                {
+                    if( ( type = xml_ReaderNextNode( p_xmlReader, &node ) )  != XML_READER_TEXT )
+                    {
+                        msg_Err( p_demux, "MainSound Id missing in CPL!" );
+                        goto error;
+                    }
+                    else
+                    {
+                        for( int i = 0; i < p_dcp->nb_files; i++ )
+                        {
+                            if( p_dcp->files[i]->id.compare( node ) == 0 )
+                            {
+                                p_dcp->audiofile = p_dcp->files[i]->path;
+                                found = true;
+                                break;
+                            }
+                        }
+                        if( found )
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+    if( p_dcp->videofile.empty() )
+    {
+        msg_Err( p_demux, "Could not find video file in ASSETMAP" );
+        goto error;
+    }
+
+    if( p_dcp->audiofile.empty() )
+    {
+        msg_Err( p_demux, "Could not find audio file in ASSETMAP" );
+        goto error;
+    }
+
+    /* free memory */
+    freeMemXML( p_xmlReader, p_xml, p_stream );
+    return VLC_SUCCESS;
+
+error:
+    freeMemXML( p_xmlReader, p_xml, p_stream );
+    return VLC_EGENERIC;
+}
+
+/**** Low-level functions : string manipulation, free function, etc ****/
+
+/**
+ * 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, false otherwise
+ */
+bool endsWith( const char * str, const char * suffix )
+{
+    if( !str || !suffix )
+        return false;
+    size_t lenstr = strlen( str );
+    size_t lensuffix = strlen( suffix );
+    if( lensuffix >  lenstr )
+        return false;
+    if( strncasecmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0 )
+        return true;
+    else
+        return false;
+}
+
+/**
+ * Function to create XML parser - using VLC libxml2 wrapper
+ * @param p_stream stream used to access XML file
+ * @param p_xml object used to create XML Reader
+ * @param p_xmlReader object used to read XML file
+ */
+int initXMLParser( demux_t *p_demux, char *filePath, stream_t **p_stream, xml_t **p_xml, xml_reader_t **p_xmlReader )
+{
+    *p_xml = xml_Create( p_demux );
+    if( ! *p_xml )
+        return VLC_EGENERIC;
+
+    /* create the stream to read from */
+    *p_stream = stream_UrlNew( p_demux, filePath );
+    if( ! *p_stream )
+        return VLC_EGENERIC;
+
+    /* init XML reader */
+    *p_xmlReader = xml_ReaderCreate( *p_xml, *p_stream );
+    if( ! *p_xmlReader )
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * 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 parserAssetmapXML function
+ * @param reader xmlTextReaderPtr object
+ */
+void freeMemXML( xml_reader_t *p_xmlReader, xml_t *p_xml, stream_t *p_stream )
+{
+    if( p_xmlReader )
+        xml_ReaderDelete( p_xmlReader );
+    if( p_stream )
+        stream_Delete( p_stream );
+    if( p_xml )
+        xml_Delete( p_xml );
+}
+
+/**
+ * Function to free memory in case of error or when closing the module
+ * @param p_demux DCP access-demux
+ */
+void freeMem( 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 );
+}
-- 
1.7.9.5





More information about the vlc-devel mailing list