[vlc-devel] [PATCH V5] dcp: Creation of access-demux module for DCP
Denis Charmet
typx at dinauz.org
Tue Oct 8 10:26:52 CEST 2013
Hi,
First, formal comment, you mix coding styles with your bracket
positions.
Comments inlined.
Le lundi 07 octobre 2013 à 04:06:48, Nicolas Bertrand a écrit :
> +//! File structure class
> +struct file_t
> +{
> + 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[MAX_DCP_FILES]; /*!< List of files in DCP directory */
Why not a file_t files[MAX_DCP_FILES]?
> + dcp_t():
> + assetmapURI( NULL ),
> + cplfileURI( NULL ),
> + nb_files( 0 )
> + {
> + for( int i=0; i < 20; i++ )
> + files[i] = NULL;
memset
> + }
> + ~dcp_t()
> + {
> + free( assetmapURI );
> + free( cplfileURI );
> + for( int i=0; i < 20; i++ )
> + delete files[i];
> + }
> +};
> +
> +class demux_sys_t
> +{
> + public:
> + /* ASDCP Picture Essence Type */
> + EssenceType_t PictureEssType;
> +
> + /* ASDCP Video MXF Reader */
> + union
> + {
> + /* JPEG2000 essence type */
> + JP2K::MXFReader *p_PicMXFReader;
> +
> + /* JPEG2000 stereoscopic essence type */
> + JP2K::MXFSReader *p_PicMXFSReader;
> +
> + /* MPEG2 essence type */
> + MPEG2::MXFReader *p_VideoMXFReader;
> + };
> +
> + /* ASDCP Picture Descriptor */
> + union
> + {
> + /* JPEG2000 and JPEG2000 stereoscopic essence types */
> + JP2K::PictureDescriptor *p_PicDesc;
> +
> + /* MPEG2 essence type */
> + MPEG2::VideoDescriptor *p_VideoDesc;
> + };
> +
> + /* ASDCP Audio MXF Reader */
> + PCM::MXFReader *p_AudioMXFReader;
> +
> + /* ASDCP Audio Descriptor */
> + PCM::AudioDescriptor *p_AudioDesc;
> +
> + /* elementary streams */
> + es_out_t *p_out;
> + es_out_id_t *p_video_es;
> + es_out_id_t *p_audio_es;
> +
> + /* DCP object */
> + dcp_t *p_dcp;
> +
> + /* current frame number */
> + uint32_t frame_no;
> +
> + /* frame rate */
> + unsigned int frame_rate_num;
> + unsigned int frame_rate_denom;
> +
> + /* total number of frames */
> + uint32_t frames_total;
> +
> + mtime_t i_pts;
> +
> + demux_sys_t():
> + 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;
Code duplication just use:
case ESS_JPEG_2000:
case ESS_JPEG_2000_S:
delete p_PicMXFReader;
delete p_PicDesc;
break;
> + case ESS_MPEG2_VES:
> + delete p_VideoMXFReader;
> + delete p_VideoDesc;
> + break;
> + default:
> + break;
> + }
> + delete p_AudioMXFReader;
> + delete p_AudioDesc;
> + delete p_dcp;
> + }
> +};
> +
> +/*****************************************************************************
> + * Local prototypes
> + *****************************************************************************/
> +
> +static int Demux( demux_t * );
> +static int Control( demux_t *, int, va_list );
> +
> +int dcpInit ( demux_t *p_demux );
> +int parseXML ( demux_t * p_demux );
> +void fillVideoFmt( video_format_t * fmt, unsigned int width, unsigned int height, unsigned int frame_rate_num, unsigned int frame_rate_denom );
> +void CloseDcpAndMxf( demux_t *p_demux );
> +
> +
> +
> +/*****************************************************************************
> + * Open: module init function
> + *****************************************************************************/
> +static int Open( vlc_object_t *obj )
> +{
> + demux_t *p_demux = ( demux_t* ) obj;
> + demux_sys_t *p_sys;
> + es_format_t video_format, audio_format;
> + vlc_fourcc_t fcc;
> + int retval;
> + JP2K::MXFReader *p_PicMXFReader;
> + JP2K::MXFSReader *p_PicMXFSReader;
> + JP2K::PictureDescriptor *p_PicDesc;
> + MPEG2::MXFReader *p_VideoMXFReader;
> + MPEG2::VideoDescriptor *p_VideoDesc;
> + PCM::MXFReader *p_AudioMXFReader;
> + PCM::AudioDescriptor *p_AudioDesc;
> +
> + 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 ) ) )
> + {
> + CloseDcpAndMxf( 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() );
> + goto error_generic;
> +
> + case ESS_JPEG_2000:
> + {
> + fcc = VLC_CODEC_JPEG2000;
> + p_PicMXFReader = p_sys->p_PicMXFReader = new ( nothrow ) JP2K::MXFReader();
> + p_PicDesc = p_sys->p_PicDesc = new ( nothrow ) JP2K::PictureDescriptor();
> +
> + if( !p_PicMXFReader || !p_PicDesc )
> + goto error_nomem;
> +
> + if( !ASDCP_SUCCESS( p_PicMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
> + msg_Err( p_demux, "File %s could not be opened with ASDCP",
> + p_sys->p_dcp->videofile.c_str() );
> + goto error_generic;
> + }
> +
> + 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;
this could belong to the constructor
> + 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_CODEC_JPEG2000;
> + p_PicMXFSReader = p_sys->p_PicMXFSReader = new ( nothrow ) JP2K::MXFSReader();
> + p_PicDesc = p_sys->p_PicDesc = new ( nothrow ) JP2K::PictureDescriptor();
> +
> + if( !p_PicMXFSReader || !p_PicDesc )
> + goto error_nomem;
> +
> + if( !ASDCP_SUCCESS( p_PicMXFSReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
> + msg_Err( p_demux, "File %s could not be opened with ASDCP", p_sys->p_dcp->videofile.c_str() );
> + goto error_generic;
> + }
> +
> + 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;
I think some of this code could be factorized but fair enough
> +
> + break;
> + }
> + case ESS_MPEG2_VES:
> + {
> + fcc = VLC_CODEC_MPGV;
> + p_VideoMXFReader = p_sys->p_VideoMXFReader = new ( nothrow ) MPEG2::MXFReader();
> + p_VideoDesc = p_sys->p_VideoDesc = new ( nothrow ) MPEG2::VideoDescriptor();
> +
> + if( !p_VideoMXFReader || !p_VideoDesc )
> + goto error_nomem;
> +
> + if( !ASDCP_SUCCESS( p_VideoMXFReader->OpenRead( p_sys->p_dcp->videofile.c_str() ) ) ) {
> + msg_Err( p_demux, "File %s could not be opened with ASDCP", p_sys->p_dcp->videofile.c_str() );
> + goto error_generic;
> + }
> +
> + 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" );
> + goto error_generic;
> + }
> +
> + if ( (p_sys->frame_rate_num == 0) || (p_sys->frame_rate_denom == 0) ) {
> + msg_Err(p_demux, "Invalid frame rate (%i/%i)",
> + p_sys->frame_rate_num, p_sys->frame_rate_denom);
> + goto error_generic;
> + }
> +
> + if( ( p_sys->p_video_es = es_out_Add( p_demux->out, &video_format ) ) == NULL ) {
> + msg_Err( p_demux, "Failed to add video es" );
> + goto error_generic;
> + }
> +
> + /* 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() );
> + goto error_generic;
> + case ESS_PCM_24b_48k:
> + case ESS_PCM_24b_96k:
> + fcc = VLC_CODEC_S24L;
> + break;
> + default:
> + msg_Err( p_demux, "Unrecognized audio format" );
> + goto error_generic;
> + }
> +
> + p_AudioMXFReader = p_sys->p_AudioMXFReader = new ( nothrow ) PCM::MXFReader();
> + p_AudioDesc = p_sys->p_AudioDesc = new ( nothrow ) PCM::AudioDescriptor();
> +
> + if( !p_AudioMXFReader || !p_AudioDesc )
> + goto error_nomem;
> +
> + if( !ASDCP_SUCCESS( p_AudioMXFReader->OpenRead( p_sys->p_dcp->audiofile.c_str() ) ) ) {
> + msg_Err( p_demux, "File %s could not be opened with ASDCP", p_sys->p_dcp->audiofile.c_str() );
> + goto error_generic;
> + }
> +
> + 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" );
> + goto error_generic;
> + }
> +
> + p_sys->p_out = p_demux->out;
> + p_demux->pf_demux = Demux;
> + p_demux->pf_control = Control;
> + p_demux->p_sys = p_sys;
useless
> +
> + return VLC_SUCCESS;
> +
> +error_generic:
> + CloseDcpAndMxf( p_demux );
> + return VLC_EGENERIC;
> +
> +error_nomem:
> + CloseDcpAndMxf( p_demux );
> + return VLC_ENOMEM;
using two labels doing the same thing just to change the return value
seems quite overkill. Use retval.
> +
> +}
> +
> +
> +/*****************************************************************************
> + * Close: module destroy function
> + *****************************************************************************/
> +static inline void Close( vlc_object_t *obj )
> +{
> + demux_t *p_demux = ( demux_t* ) obj;
> + CloseDcpAndMxf( p_demux );
> +}
> +
> +
> +
> +/*****************************************************************************
> + * Demux: DCP Demuxing function
> + *****************************************************************************/
> +static int Demux( demux_t *p_demux )
> +{
> + demux_sys_t *p_sys = p_demux->p_sys;
> + block_t *p_video_frame = NULL, *p_audio_frame = NULL;
> + uint32_t i = p_sys->frame_no;
> + mtime_t pts = 0;
> + uint32_t i_audio_buffer = PCM::CalcFrameBufferSize(*p_sys->p_AudioDesc);
> + JP2K::FrameBuffer PicFrameBuff(FRAME_BUFFER_SIZE);
> + MPEG2::FrameBuffer VideoFrameBuff(FRAME_BUFFER_SIZE);
> + PCM::FrameBuffer AudioFrameBuff(i_audio_buffer);
> +
> + if( i == p_sys->frames_total )
> + return 0;
> +
> + /* video frame */
> + switch( p_sys->PictureEssType )
> + {
The following could be factorized.
> + case ESS_JPEG_2000:
> + if ( ( p_video_frame = block_Alloc( FRAME_BUFFER_SIZE )) == NULL )
why allocating FRAME_BUFFER_SIZE and not PicFrameBuff.Size()?
> + goto error;
> +
> + if ( ! ASDCP_SUCCESS(
> + PicFrameBuff.SetData(p_video_frame->p_buffer, FRAME_BUFFER_SIZE)) )
> + goto error_asdcp;
> +
> + if ( ! ASDCP_SUCCESS(
> + p_sys->p_PicMXFReader->ReadFrame(i, PicFrameBuff, 0, 0)) ) {
> + PicFrameBuff.SetData(0,0);
> + goto error_asdcp;
> + }
> +
> + p_video_frame->i_buffer = PicFrameBuff.Size();
> + break;
> +
> + case ESS_JPEG_2000_S:
> + if ( ( p_video_frame = block_Alloc( FRAME_BUFFER_SIZE )) == NULL )
> + goto error;
> +
> + if ( ! ASDCP_SUCCESS(
> + PicFrameBuff.SetData(p_video_frame->p_buffer, FRAME_BUFFER_SIZE)) )
> + goto error_asdcp;
> +
> + if ( ! ASDCP_SUCCESS(
> + p_sys->p_PicMXFSReader->ReadFrame(i, JP2K::SP_LEFT, PicFrameBuff, 0, 0)) ) {
> + PicFrameBuff.SetData(0,0);
> + goto error_asdcp;
> +
> + p_video_frame->i_buffer = PicFrameBuff.Size();
> + break;
> + }
> + case ESS_MPEG2_VES:
> + {
> + if ( ( p_video_frame = block_Alloc( FRAME_BUFFER_SIZE )) == NULL )
> + goto error;
> +
> + if ( ! ASDCP_SUCCESS(
> + VideoFrameBuff.SetData(p_video_frame->p_buffer, FRAME_BUFFER_SIZE)) )
> + goto error_asdcp;
> +
> + if ( ! ASDCP_SUCCESS(
> + p_sys->p_VideoMXFReader->ReadFrame(i, VideoFrameBuff, 0, 0)) ) {
> + VideoFrameBuff.SetData(0,0);
> + goto error_asdcp;
> + }
> +
> + p_video_frame->i_buffer = VideoFrameBuff.Size();
> + break;
> + }
> + default:
> + msg_Err( p_demux, "Unrecognized video format" );
> + goto error;
> + }
> +
> + p_video_frame->i_length = 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;
> +
> + /* audio frame */
> + if ( ( p_audio_frame = block_Alloc( i_audio_buffer )) == NULL )
> + goto error;
leaks the videoblock
> +
> + if ( ! ASDCP_SUCCESS(
> + AudioFrameBuff.SetData(p_audio_frame->p_buffer, i_audio_buffer)) )
> + goto error_asdcp;
leaks
> +
> + if ( ! ASDCP_SUCCESS(
> + p_sys->p_AudioMXFReader->ReadFrame(i, AudioFrameBuff, 0, 0)) ) {
> + AudioFrameBuff.SetData(0,0);
> + goto error_asdcp;
leaks
> + }
> +
> + p_audio_frame->i_buffer = AudioFrameBuff.Size();
> + 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;
> +
> + if( p_video_frame->i_pts < p_audio_frame->i_pts )
> + pts = p_video_frame->i_pts;
> + else
> + pts = p_audio_frame->i_pts;
ternary operator or __MIN() macro
> + p_sys->i_pts = 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;
> +
> +error_asdcp:
> + msg_Err( p_demux, "Couldn't read frame with ASDCP");
> + return -1;
> +error:
> + return -1;
> +}
> +
> +/*****************************************************************************
> + * Control: handle the controls
> + *****************************************************************************/
> +static int Control( demux_t *p_demux, int query, va_list args )
> +{
> + double f,*pf;
> + bool *pb;
> + int64_t *pi64, i64;
> + demux_sys_t *p_sys = p_demux->p_sys;
> +
> + switch ( query )
> + {
> + case DEMUX_CAN_PAUSE:
> + case DEMUX_CAN_CONTROL_PACE:
> + pb = ( bool* ) va_arg ( args, bool* );
> + *pb = true;
> + break;
> +
> + case DEMUX_SET_PAUSE_STATE:
> + break;
> +
> + case DEMUX_CAN_SEEK:
> + pb = (bool *)va_arg( args, bool * );
> + if( p_sys->PictureEssType != ESS_MPEG2_VES )
> + *pb = true;
> + else
> + *pb = false;
> + break;
> +
> + case DEMUX_GET_POSITION:
> + pf = ( double* ) va_arg ( args, double* ); *pf = 0.0;
> + if( p_sys->frames_total != 0 )
> + *pf = (double) p_sys->frame_no / (double) p_sys->frames_total;
> + else
> + msg_Warn( p_demux, "Total number of frames is 0" );
> + break;
> +
> + case DEMUX_SET_POSITION:
> + f = ( double ) va_arg ( args, double );
> + p_sys->frame_no = (int) ( f * p_sys->frames_total );
> + break;
> +
> + case DEMUX_GET_LENGTH:
> + pi64 = ( int64_t* ) va_arg ( args, int64_t* );
> + *pi64 = (int) ( ( p_sys->frames_total * p_sys->frame_rate_denom / p_sys->frame_rate_num ) * 1000000 );
> + break;
> +
> + case DEMUX_GET_TIME:
> + pi64 = ( int64_t* ) va_arg ( args, int64_t* );
> + *pi64 = p_sys->i_pts >= 0 ? p_sys->i_pts : 0;
> + break;
> +
> + case DEMUX_SET_TIME:
> + i64 = ( int64_t ) va_arg ( args, int64_t );
> + msg_Warn( p_demux, "DEMUX_SET_TIME" );
> + p_sys->frame_no = i64 * p_sys->frame_rate_num / ( 1000000 * p_sys->frame_rate_denom );
> + p_sys->i_pts= i64;
> + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, ( mtime_t ) i64 );
> + break;
> + default:
> + msg_Warn( p_demux, "Unknown query %d in DCP Control", query );
> + return VLC_EGENERIC;
> + }
> +
> + return VLC_SUCCESS;
> +}
> +
> +
> +/*****************************************************************************
> + * Low-level functions : string manipulation, free function, etc
> + *****************************************************************************/
> +/**
> + * Function to 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 )
> +{
> + bool b_end_with_suffix = false;
> + size_t lenstr , lensuffix;
> +
> + if( !str || !suffix )
> + return b_end_with_suffix;
> +
> + lenstr = strlen( str );
> + lensuffix = strlen( suffix );
> + if ( (lensuffix <= lenstr)
> + && (strncasecmp(str + lenstr - lensuffix, suffix, lensuffix) == 0)
> + )
> + b_end_with_suffix = true;
useless branching
b_end_with_suffix = ( ( lensuffix <= lenstr ) &&
( !strncasecmp(str + lenstr - lensuffix, suffix, lensuffix) )
> + return b_end_with_suffix;
> +}
> +
> +
> +/**
> + * 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
> + */
> +static 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 CloseDcpAndMxf( demux_t *p_demux )
> +{
> + demux_sys_t *p_sys = p_demux->p_sys;
> + /* close the files */
> + switch( p_sys->PictureEssType )
segfaults if p_sys wasn't properly allocated in dcpInit()
> + {
> + case ESS_UNKNOWN:
> + break;
> + case ESS_JPEG_2000:
> + if( p_sys->p_PicMXFReader )
> + p_sys->p_PicMXFReader->Close();
> + break;
> + case ESS_JPEG_2000_S:
> + if( p_sys->p_PicMXFSReader )
> + p_sys->p_PicMXFSReader->Close();
> + break;
> + case ESS_MPEG2_VES:
> + if( p_sys->p_VideoMXFReader )
> + p_sys->p_VideoMXFReader->Close();
> + break;
> + default:
> + break;
> + }
> + if( p_sys->p_AudioMXFReader )
> + p_sys->p_AudioMXFReader->Close();
> +
> + delete( p_demux->p_sys );
> +}
> +
> +
> +/*****************************************************************************
> + * DCP init
> + *****************************************************************************/
> +
> +/**
> + * Function to handle the operations with the DCP directory.
> + * @param p_demux Demux pointer.
> + * @return Integer according to the success or not of the process.
> + */
> +int dcpInit ( demux_t *p_demux )
> +{
> + int retval;
> + /* 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 ) )
leaking p_sys
> + 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" );
leaks
> + return VLC_EGENERIC;
> + }
> + }
> +
> + /* Parsing XML files to get audio and video files */
> + msg_Dbg( p_demux, "parsing XML files..." );
> + if( ( retval = parseXML( p_demux ) ) )
leaks
> + 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;
> +}
> +
> +
> +/*****************************************************************************
> + * functions for XML parsing
> + *****************************************************************************/
> +
> +/**
> + * 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
> + */
> +static 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 )
leaks
> + return VLC_EGENERIC;
> +
> + /* init XML reader */
> + *p_xmlReader = xml_ReaderCreate( *p_xml, *p_stream );
> + if( ! *p_xmlReader )
leaks
> + return VLC_EGENERIC;
> +
> + return VLC_SUCCESS;
> +}
> +
> +/**
> + * Function to retrieve the path to the ASSETMAP file.
> + * @param p_demux DCP access_demux.
> + */
> +static 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" );
closedir
> + 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;
> + }
> +
> + /* convert the ASSETMAP path into URI */
> + 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 ] )
> + {
> + 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++;
I think this segfaults your file has Path before Id and leaks if the
file is corrupted and don't have a Path
> + }
> + }
> + }
> + }
> + 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 found a MXF File in DCP directory.
> + * Return True if file is found and update dcp strucure with
> + * filepath accodring MXD media type
> + * @param pp_dcp dcp sturcture (in/out)
> + * @param uuid UUID of MXF file to found
> + * @param MxfMedia Type of Media to found ans store
> + */
> +
> +static bool foundMxfFile(dcp_t **pp_dcp, const char *uuid, MxfMedia_t MxfMedia )
> +{
> + bool found = false;
> + int i;
> + dcp_t *p_dcp = *pp_dcp;
> + for( i = 0; i < p_dcp->nb_files; i++ ) {
> + if( p_dcp->files[i]->id.compare( uuid ) == 0 ) {
> + switch (MxfMedia) {
> + case MXF_PICTURE:
> + p_dcp->videofile = p_dcp->files[i]->path;
> + found = true;
> + break;
> + case MXF_AUDIO:
> + p_dcp->audiofile = p_dcp->files[i]->path;
> + found = true;
> + break;
> + case MXF_UNKNOWN:
> + default:
> + found = false;
useless
> + }
> + if (found)
> + break;
> + }
> + }
> + return found;
> +}
> +/**
> + * 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 parseCplXML ( demux_t * p_demux )
> +{
> + dcp_t *p_dcp = p_demux->p_sys->p_dcp;
> + int type = 0;
> + 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 */
> + 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))
> + &&
> + (foundMxfFile(&p_dcp, node, MXF_PICTURE)))
> + break;
> + else {
> + msg_Err( p_demux, "MainPicture Id missing in CPL!" );
> + goto error;
> + }
> + }
> + }
> + }
> +
> + /* MainSound data */
> + if( ( type == XML_READER_STARTELEM) && ( strcasecmp( node, "MainSound" ) == 0 ) )
> + {
> + /* searching for the Id of the audio file */
> + 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)
> + &&
> + (foundMxfFile(&p_dcp, node, MXF_AUDIO)))
> + break;
> + else {
> + msg_Err( p_demux, "MainSound Id missing in CPL!" );
> + goto error;
> + }
> + }
> + }
> + }
> + }
> +
> + 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;
> +}
> +
> +
> +/**
> + * Function which parses XML files in DCP directory in order to get video and audio files
> + * @param p_demux Demux pointer.
> + * @return Integer according to the success or not of the operation
> + */
> +int parseXML ( demux_t * p_demux )
> +{
> + int retval;
> + /* 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 = parseCplXML( p_demux ) ) )
> + return retval;
> +
> + return VLC_SUCCESS; /* TODO : perform checking on XML parsing */
> +}
Regards,
--
Denis Charmet - TypX
Le mauvais esprit est un art de vivre
More information about the vlc-devel
mailing list