You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1517 lines
51 KiB
1517 lines
51 KiB
/*****************************************************************************
|
|
* Copyright (C) 2013 VLC authors and VideoLAN
|
|
*
|
|
* Authors:
|
|
* Nicolas Bertrand <nico@isf.cc>
|
|
* Jean-Baptiste Kempf <jb@videolan.org>
|
|
* Guillaume Gonnaud
|
|
* Valentin Vetter <vvetter@outlook.com>
|
|
* Anthony Giniers
|
|
* Ludovic Hoareau
|
|
* Loukmane Dessai
|
|
* 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 dcpparser.cpp
|
|
* @brief Parsing of DCP XML files
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
/* VLC core API headers */
|
|
#include <vlc_common.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_xml.h>
|
|
#include <vlc_url.h>
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
#include "dcpparser.h"
|
|
|
|
using namespace std;
|
|
|
|
typedef enum {
|
|
CHUNK_UNKNOWN = 0,
|
|
CHUNK_PATH,
|
|
CHUNK_VOL_INDEX,
|
|
CHUNK_OFFSET,
|
|
CHUNK_LENGTH
|
|
} ChunkTag_t;
|
|
|
|
|
|
typedef enum {
|
|
ASSET_UNKNOWN = 0,
|
|
ASSET_ID,
|
|
ASSET_ANNOTATION_TEXT,
|
|
ASSET_PACKING_LIST,
|
|
ASSET_CHUNK_LIST,
|
|
ASSET_HASH,
|
|
ASSET_SIZE,
|
|
ASSET_TYPE,
|
|
ASSET_ORIGINAL_FILENAME
|
|
} AssetTag_t;
|
|
|
|
static const string g_asset_names[] = {
|
|
"Id",
|
|
"AnnotationText",
|
|
"PackingList",
|
|
"ChunkList",
|
|
"Hash",
|
|
"Size",
|
|
"Type",
|
|
"OriginalFileName"
|
|
};
|
|
|
|
|
|
typedef enum {
|
|
PKL_UNKNOWN = 0,
|
|
PKL_ID,
|
|
PKL_ISSUE_DATE,
|
|
PKL_ISSUER,
|
|
PKL_CREATOR,
|
|
PKL_ASSET_LIST,
|
|
PKL_ANNOTATION_TEXT, /* start of optional tags */
|
|
PKL_ICON_ID,
|
|
PKL_GROUP_ID,
|
|
PKL_SIGNER,
|
|
PKL_SIGNATURE,
|
|
} PKLTag_t;
|
|
|
|
typedef enum {
|
|
CPL_UNKNOWN = 0,
|
|
CPL_ID,
|
|
CPL_ANNOTATION_TEXT, /* optional */
|
|
CPL_ICON_ID, /* optional */
|
|
CPL_ISSUE_DATE,
|
|
CPL_ISSUER, /* optional */
|
|
CPL_CREATOR, /* optional */
|
|
CPL_CONTENT_TITLE,
|
|
CPL_CONTENT_KIND,
|
|
CPL_CONTENT_VERSION, /* not optional, but not always present*/
|
|
CPL_RATING_LIST, /* not handled */
|
|
CPL_REEL_LIST,
|
|
CPL_SIGNER, /* optional - not handled */
|
|
CPL_SIGNATURE /* optional - not handled */
|
|
} CPLTag_t;
|
|
|
|
|
|
class ChunkList: public std::list<Chunk> {
|
|
public :
|
|
ChunkList();
|
|
~ChunkList();
|
|
};
|
|
|
|
/*
|
|
* Chunk Class
|
|
*/
|
|
int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
static const string names[] = {"Path", "VolumeIndex", "Offset",
|
|
"Length"};
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "Chunk")
|
|
return -1;
|
|
/* loop on Chunks Node */
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
{
|
|
ChunkTag_t chunk_tag = CHUNK_UNKNOWN;
|
|
for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
|
|
if( node == names[i-1]) {
|
|
chunk_tag = i;
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
switch (chunk_tag) {
|
|
case CHUNK_PATH:
|
|
this->s_path = s_value;
|
|
break;
|
|
case CHUNK_VOL_INDEX:
|
|
this->i_vol_index = atoi(s_value.c_str());
|
|
break;
|
|
case CHUNK_OFFSET:
|
|
this->i_offset = atoi(s_value.c_str());
|
|
break;
|
|
case CHUNK_LENGTH:
|
|
this->i_length = atoi(s_value.c_str());
|
|
break;
|
|
case CHUNK_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
/* break the for loop as a tag is found*/
|
|
break;
|
|
}
|
|
}
|
|
if(chunk_tag == CHUNK_UNKNOWN)
|
|
return -1;
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
s_value = node;
|
|
if (unlikely(node.empty()))
|
|
return -1;
|
|
break;
|
|
case XML_READER_ENDELEM:
|
|
/* Verify if we reach Chuk endelem */
|
|
if ( node == p_node) {
|
|
/* verify path */
|
|
if ( this->s_path.empty() ) {
|
|
msg_Err(this->p_demux, "Chunk::Parse No path found");
|
|
return -1;
|
|
}
|
|
if ( this->i_vol_index != 1 ) {
|
|
msg_Err(this->p_demux, "Only one VOLINDEX supported. Patch welcome.");
|
|
return -1;
|
|
}
|
|
/* end of chunk tag parse */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
/*
|
|
* AssetMap Class
|
|
*/
|
|
|
|
AssetMap::~AssetMap() { }
|
|
|
|
int AssetMap::Parse ( )
|
|
{
|
|
int type = 0;
|
|
int retval;
|
|
int reel_nbr = 0;
|
|
int index = 0;
|
|
int sum_duration_vid = 0;
|
|
int sum_duration_aud = 0;
|
|
string node;
|
|
char *psz_kdm_path;
|
|
|
|
CPL *cpl;
|
|
Reel *reel;
|
|
PKL *pkl;
|
|
AssetList *_p_asset_list = NULL;
|
|
|
|
vector<string> pklfiles;
|
|
|
|
/* init XML parser */
|
|
if( this->OpenXml() ) {
|
|
msg_Err( p_demux, "Failed to initialize Assetmap XML parser" );
|
|
return -1;
|
|
}
|
|
|
|
/* reading ASSETMAP file to get the asset_list */
|
|
msg_Dbg( p_demux, "reading ASSETMAP file..." );
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
|
|
if( type == -1 )
|
|
{
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
|
|
_p_asset_list = new (nothrow) AssetList();
|
|
if ( unlikely(_p_asset_list == NULL) ) {
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
p_dcp->p_asset_list = _p_asset_list;
|
|
if (this->ParseAssetList(p_xmlReader, node, type )) {
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
/* asset list found so break*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Look for PKLs path */
|
|
if ( (_p_asset_list == NULL) || (_p_asset_list->size() == 0) ) {
|
|
msg_Err( p_demux, "Asset list empty" );
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
|
|
for (AssetList::iterator iter = _p_asset_list->begin();
|
|
iter != _p_asset_list->end() ; ++iter) {
|
|
string s_filepath;
|
|
s_filepath = (*iter)->getPath();
|
|
if (s_filepath.empty()) {
|
|
msg_Err( p_demux, "No path element for asset" );
|
|
continue;
|
|
}
|
|
/* set an absolute file path */
|
|
s_filepath = p_dcp->path + s_filepath;
|
|
|
|
/* case of packing list */
|
|
if ((*iter)->isPackingList()) {
|
|
pklfiles.push_back( s_filepath );
|
|
}
|
|
}
|
|
|
|
/* TODO: case of only on PKL managed.
|
|
* Future work needed for managing severals
|
|
* the PKL vector will be used to select the required PKL */
|
|
if( (pklfiles.size() == 0) || (pklfiles[0].empty()) )
|
|
{
|
|
msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
|
|
/* Create the first PKL */
|
|
pkl = new (nothrow) PKL(p_demux, pklfiles[0], _p_asset_list, p_dcp->path);
|
|
if ( unlikely(pkl == NULL) ) {
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
if (pkl->Parse()) {
|
|
delete pkl;
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
p_dcp->pkls.push_back( pkl );
|
|
|
|
/* Now, CPL */
|
|
if ( pkl->FindCPLs() <= 0 ) {
|
|
msg_Err(p_demux, " No CPL found");
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
/* TODO: Only one CPL managed.
|
|
* Future work needed for managing severals
|
|
*/
|
|
|
|
cpl = pkl->getCPL(0);
|
|
if( cpl == NULL ) {
|
|
msg_Err(p_demux, " No CPL found");
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
if ( cpl->Parse() ) {
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
|
|
/* KDM, if needed */
|
|
for( AssetList::iterator iter = _p_asset_list->begin(); iter != _p_asset_list->end(); ++iter )
|
|
if( ! (*iter)->getKeyId().empty() )
|
|
{
|
|
msg_Dbg( p_demux, "DCP is encrypted, searching KDM file...");
|
|
psz_kdm_path = var_InheritString( p_demux, "kdm" );
|
|
if( !psz_kdm_path || !*psz_kdm_path )
|
|
{
|
|
msg_Err( p_demux, "cryptographic key IDs found in CPL and no path to KDM given");
|
|
free( psz_kdm_path );
|
|
this->CloseXml();
|
|
return VLC_EGENERIC;
|
|
}
|
|
KDM p_kdm( p_demux, psz_kdm_path, p_dcp );
|
|
free( psz_kdm_path );
|
|
if( ( retval = p_kdm.Parse() ) )
|
|
{
|
|
this->CloseXml();
|
|
return retval;
|
|
}
|
|
break;
|
|
}
|
|
|
|
reel_nbr = cpl->getReelList().size();
|
|
for(index = 0; index != reel_nbr; ++index)
|
|
{
|
|
reel = cpl->getReel(index);
|
|
|
|
Asset *asset;
|
|
struct info_reel video;
|
|
struct info_reel audio;
|
|
|
|
/* Get picture */
|
|
asset = reel->getTrack(TRACK_PICTURE);
|
|
if (asset != NULL)
|
|
{
|
|
sum_duration_vid += asset->getDuration();
|
|
video.filename = p_dcp->path + asset->getPath();
|
|
video.i_entrypoint = asset->getEntryPoint();
|
|
video.i_duration = asset->getDuration();
|
|
video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
|
|
video.i_absolute_end = sum_duration_vid;
|
|
video.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
|
|
p_dcp->video_reels.push_back(video);
|
|
msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
|
|
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
|
|
}
|
|
/* Get audio */
|
|
asset = reel->getTrack(TRACK_SOUND);
|
|
if (asset != NULL)
|
|
{
|
|
/*if (!p_dcp->audio_reels.empty())
|
|
{
|
|
sum_duration_aud = 0;
|
|
for (int i = 0; i != p_dcp->audio_reels.size(); ++i)
|
|
{
|
|
sum_duration_aud += p_dcp->audio_reels(i).i_duration;
|
|
}
|
|
}*/
|
|
sum_duration_aud += asset->getDuration();
|
|
audio.filename = p_dcp->path + asset->getPath();
|
|
audio.i_entrypoint = asset->getEntryPoint();
|
|
audio.i_duration = asset->getDuration();
|
|
audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
|
|
audio.i_absolute_end = sum_duration_aud;
|
|
audio.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
|
|
p_dcp->audio_reels.push_back(audio);
|
|
msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
|
|
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
|
|
}
|
|
}
|
|
/* free memory */
|
|
this->CloseXml();
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Asset Class
|
|
*/
|
|
Asset::~Asset() {}
|
|
|
|
int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
const string s_root_node = "Asset";
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != s_root_node)
|
|
return -1;
|
|
/* loop on Assets Node */
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
{
|
|
AssetTag_t _tag = ASSET_UNKNOWN;
|
|
for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
|
|
if( node == g_asset_names[i-1]) {
|
|
_tag = i;
|
|
switch(_tag) {
|
|
/* Case of complex nodes */
|
|
case ASSET_PACKING_LIST:
|
|
/* case of <PackingList/> tag, bur not compliant with SMPTE-429-9 2007*/
|
|
if (xml_ReaderIsEmptyElement( p_xmlReader))
|
|
{
|
|
this->b_is_packing_list = true;
|
|
}
|
|
else if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
{
|
|
msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
|
|
return -1;
|
|
}
|
|
if ( s_value == "true" )
|
|
this->b_is_packing_list = true;
|
|
break;
|
|
case ASSET_CHUNK_LIST:
|
|
if ( this->parseChunkList(p_xmlReader, node, type ) )
|
|
{
|
|
msg_Err(this->p_demux, "Error parsing chunk list: %s", node.c_str());
|
|
return -1;
|
|
}
|
|
this->s_path = this->chunk_vec[0].getPath();
|
|
break;
|
|
case ASSET_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
{
|
|
msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
|
|
return -1;
|
|
}
|
|
this->s_id = s_value;
|
|
break;
|
|
case ASSET_ANNOTATION_TEXT:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
{
|
|
msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
|
|
return -1;
|
|
}
|
|
this->s_annotation = s_value;
|
|
break;
|
|
case ASSET_ORIGINAL_FILENAME:
|
|
case ASSET_HASH:
|
|
case ASSET_TYPE:
|
|
case ASSET_SIZE:
|
|
/* Asset tags not in AssetMap */
|
|
break;
|
|
default:
|
|
msg_Warn(this->p_demux, "Unknown ASSET_TAG: %i", _tag );
|
|
break;
|
|
}
|
|
/* break the for loop as a tag is found*/
|
|
break;
|
|
}
|
|
}
|
|
if( _tag == ASSET_UNKNOWN )
|
|
{
|
|
msg_Err(this->p_demux, "Unknown ASSET_TAG: %s", node.c_str());
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
msg_Err(this->p_demux, " Text element found in Asset");
|
|
return -1;
|
|
case XML_READER_ENDELEM:
|
|
if ( node != s_root_node) {
|
|
msg_Err(this->p_demux,
|
|
"Something goes wrong in Asset parsing on Assetmap (node %s)", node.c_str());
|
|
return -1;
|
|
} else {
|
|
/*Check Presence of Id and Chunklist */
|
|
if ( this->s_id.empty() ) {
|
|
msg_Err(this->p_demux, " No Id element found in Asset");
|
|
return -1;
|
|
}
|
|
if ( this->s_path.empty() ) {
|
|
msg_Err(this->p_demux, " No path element found in Asset");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
int Asset::ParsePKL( xml_reader_t *p_xmlReader)
|
|
{
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
const string s_root_node = "Asset";
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
{
|
|
AssetTag_t _tag = ASSET_UNKNOWN;
|
|
for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
|
|
if( node == g_asset_names[i-1]) {
|
|
_tag = i;
|
|
switch(_tag) {
|
|
case ASSET_ANNOTATION_TEXT:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
if ( this->s_annotation.empty() )
|
|
this->s_annotation = s_value;
|
|
else
|
|
this->s_annotation = this->s_annotation + "--" + s_value;
|
|
break;
|
|
case ASSET_HASH:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->s_hash = s_value;
|
|
break;
|
|
case ASSET_SIZE:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->ui_size = atol(s_value.c_str());
|
|
break;
|
|
case ASSET_TYPE:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->s_type = s_value;
|
|
break;
|
|
case ASSET_ORIGINAL_FILENAME:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->s_original_filename = s_value;
|
|
break;
|
|
case ASSET_ID: /* already verified */
|
|
case ASSET_PACKING_LIST:
|
|
case ASSET_CHUNK_LIST:
|
|
/* Asset tags not in PKL */
|
|
break;
|
|
default:
|
|
msg_Warn(this->p_demux, "Unknown ASSET_TAG: %i", _tag );
|
|
break;
|
|
}
|
|
/* break the for loop as a tag is found*/
|
|
break;
|
|
}
|
|
}
|
|
if( _tag == ASSET_UNKNOWN )
|
|
return -1;
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
return -1;
|
|
case XML_READER_ENDELEM:
|
|
if ( node != s_root_node) {
|
|
msg_Err(this->p_demux,
|
|
"Something goes wrong in Asset parsing on PKL (node %s)", node.c_str());
|
|
return -1;
|
|
} else {
|
|
/* Verify that mandatory attributes are filled */
|
|
if (this->s_hash.empty()) {
|
|
msg_Err(this->p_demux,"Asset Hash tag invalid");
|
|
return -1;
|
|
}
|
|
if (this->ui_size == 0) {
|
|
msg_Err(this->p_demux,"Asset Size tag invalid");
|
|
return -1;
|
|
}
|
|
if (this->s_type.empty()) {
|
|
msg_Err(this->p_demux,"Asset Type tag invalid");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void Asset::Dump()
|
|
{
|
|
msg_Dbg(this->p_demux,"Id = %s", this->s_id.c_str());
|
|
msg_Dbg(this->p_demux,"Path = %s", this->s_path.c_str());
|
|
msg_Dbg(this->p_demux,"Is PKL = %s", this->b_is_packing_list ? "True" : "False");
|
|
msg_Dbg(this->p_demux,"Hash = %s", this->s_hash.c_str());
|
|
msg_Dbg(this->p_demux,"Size = %i", this->ui_size);
|
|
msg_Dbg(this->p_demux,"Type = %s", this->s_type.c_str());
|
|
msg_Dbg(this->p_demux,"OrignalFileName = %s", this->s_original_filename.c_str());
|
|
msg_Dbg(this->p_demux,"AnnotationText = %s", this->s_annotation.c_str());
|
|
}
|
|
|
|
int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
std::vector<Chunk> chunk_vec;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "ChunkList" )
|
|
return -1;
|
|
/* loop on Assets Node */
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
{
|
|
Chunk chunk(this->p_demux);
|
|
if (node != "Chunk" )
|
|
return -1;
|
|
if ( chunk.Parse(p_xmlReader, node, type) )
|
|
return -1;
|
|
chunk_vec.push_back(chunk);
|
|
break;
|
|
}
|
|
case XML_READER_ENDELEM:
|
|
if ( node == p_node) {
|
|
if (chunk_vec.size() != 1 ) {
|
|
msg_Err(this->p_demux, "chunklist of size greater than one not supported");
|
|
return -1;
|
|
}
|
|
this->chunk_vec = chunk_vec;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
AESKey * Asset::getAESKeyById( AESKeyList* p_key_list, const string s_id )
|
|
{
|
|
/* return NULL if DCP is not encrypted */
|
|
if( !p_key_list || s_id.empty() )
|
|
return NULL;
|
|
|
|
for( AESKeyList::iterator index = p_key_list->begin(); index != p_key_list->end(); ++index )
|
|
if( (*index)->getKeyId() == s_id )
|
|
return *index;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
Asset *asset;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "AssetList" )
|
|
return -1;
|
|
/* loop on AssetList nodes */
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
if (node != "Asset" )
|
|
return -1;
|
|
asset = new (nothrow) Asset(this->p_demux);
|
|
if ( unlikely(asset == NULL) )
|
|
return -1;
|
|
if (asset->Parse(p_xmlReader, node, type)){
|
|
msg_Err(this->p_demux, "Error parsing Asset in AssetMap");
|
|
delete asset;
|
|
return -1;
|
|
}
|
|
p_dcp->p_asset_list->push_back(asset);
|
|
break;
|
|
|
|
case XML_READER_ENDELEM:
|
|
if (node == p_node )
|
|
return 0;
|
|
break;
|
|
default:
|
|
case XML_READER_TEXT:
|
|
msg_Err(this->p_demux, "Error parsing AssetList in AssetMap");
|
|
return -1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
|
|
{
|
|
AssetList::iterator index = asset_list->begin() ;
|
|
for (index = asset_list->begin(); index != asset_list->end(); ++index)
|
|
if ((*index)->getId() == p_id )
|
|
return *index;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* XmlFile Class
|
|
*/
|
|
XmlFile::~XmlFile() {}
|
|
|
|
int XmlFile::OpenXml()
|
|
{
|
|
char *psz_uri;
|
|
|
|
psz_uri = vlc_path2uri( this->s_path.c_str(), "file" );
|
|
this->p_stream = vlc_stream_NewURL(this->p_demux, psz_uri );
|
|
free(psz_uri);
|
|
if( ! this->p_stream ) {
|
|
return -1;
|
|
}
|
|
|
|
this->p_xmlReader = xml_ReaderCreate( this->p_demux, this->p_stream);
|
|
if( ! this->p_xmlReader ) {
|
|
vlc_stream_Delete( this->p_stream );
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int XmlFile::ReadNextNode( demux_t *p_demux, xml_reader_t *p_xmlReader, string& p_node )
|
|
{
|
|
const char * c_node;
|
|
int i = xml_ReaderNextNode( p_xmlReader, &c_node );
|
|
|
|
if( i <= XML_READER_NONE )
|
|
return i;
|
|
|
|
/* remove namespaces, if there are any */
|
|
string s_node = c_node;
|
|
size_t ui_pos = s_node.find( ":" );
|
|
|
|
if( ( i == XML_READER_STARTELEM || i == XML_READER_ENDELEM ) && ( ui_pos != string::npos ) )
|
|
{
|
|
try
|
|
{
|
|
p_node = s_node.substr( ui_pos + 1 );
|
|
}
|
|
catch( ... )
|
|
{
|
|
msg_Err( p_demux, "error while handling string" );
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
p_node = s_node;
|
|
|
|
return i;
|
|
}
|
|
|
|
int XmlFile::ReadEndNode( demux_t *p_demux, xml_reader_t *p_xmlReader, string p_node, int p_type, string &s_value)
|
|
{
|
|
string node;
|
|
|
|
if ( xml_ReaderIsEmptyElement( p_xmlReader) )
|
|
return 0;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
|
|
int n = XmlFile::ReadNextNode( p_demux, p_xmlReader, node );
|
|
if( n == XML_READER_TEXT )
|
|
{
|
|
s_value = node;
|
|
n = XmlFile::ReadNextNode( p_demux, p_xmlReader, node );
|
|
if( ( n == XML_READER_ENDELEM ) && node == p_node)
|
|
return 0;
|
|
}
|
|
return n == XML_READER_ENDELEM ? 0 : -1;
|
|
}
|
|
/*
|
|
* Reads first node in XML and returns
|
|
* 1 if XML is CPL,
|
|
* 0 if not
|
|
* -1 on error
|
|
*/
|
|
int XmlFile::isCPL()
|
|
{
|
|
string node;
|
|
int type, ret = 0;
|
|
|
|
if( this->OpenXml() )
|
|
{
|
|
msg_Err( this->p_demux, "Failed to open CPL XML file" );
|
|
return -1;
|
|
}
|
|
|
|
/* read 1st node and verify that is a CPL */
|
|
type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node );
|
|
if( type == -1 ) /* error */
|
|
ret = -1;
|
|
if( type == XML_READER_STARTELEM && node == "CompositionPlaylist" )
|
|
ret = 1;
|
|
|
|
/* close xml */
|
|
this->CloseXml();
|
|
return ret;
|
|
}
|
|
|
|
void XmlFile::CloseXml() {
|
|
if( this->p_stream )
|
|
vlc_stream_Delete( this->p_stream );
|
|
if( this->p_xmlReader )
|
|
xml_ReaderDelete( this->p_xmlReader );
|
|
}
|
|
|
|
/*
|
|
* PKL Class
|
|
*/
|
|
|
|
PKL::~PKL() {
|
|
vlc_delete_all(vec_cpl);
|
|
}
|
|
|
|
int PKL::Parse()
|
|
{
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
const string s_root_node = "PackingList";
|
|
|
|
static const string names[] = {
|
|
"Id",
|
|
"IssueDate",
|
|
"Issuer",
|
|
"Creator",
|
|
"AssetList",
|
|
"AnnotationText",
|
|
"IconId",
|
|
"GroupId",
|
|
"Signer",
|
|
"Signature"
|
|
};
|
|
|
|
if (this->OpenXml())
|
|
return -1;
|
|
|
|
/* read 1st node and verify that is a PKL*/
|
|
if ( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) &&
|
|
(node == s_root_node) ) ) {
|
|
msg_Err( this->p_demux, "Not a valid XML Packing List");
|
|
goto error;
|
|
}
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM: {
|
|
PKLTag_t _tag = PKL_UNKNOWN;
|
|
for(PKLTag_t i = PKL_ID; i <= PKL_SIGNATURE; i = PKLTag_t(i+1)) {
|
|
if( node == names[i-1]) {
|
|
_tag = i;
|
|
switch (_tag) {
|
|
/* case for parsing non terminal nodes */
|
|
case PKL_ASSET_LIST:
|
|
if ( this->ParseAssetList(node, type) )
|
|
goto error;
|
|
break;
|
|
case PKL_SIGNER:
|
|
if ( this->ParseSigner(node, type) )
|
|
goto error;
|
|
break;
|
|
case PKL_SIGNATURE:
|
|
if ( this->ParseSignature(node, type) )
|
|
goto error;
|
|
break;
|
|
/* Parse simple/end nodes */
|
|
case PKL_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_id = s_value;
|
|
break;
|
|
case PKL_ISSUE_DATE:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_issue_date = s_value;
|
|
break;
|
|
case PKL_ISSUER:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_issuer = s_value;
|
|
break;
|
|
case PKL_CREATOR:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_creator = s_value;
|
|
break;
|
|
case PKL_ANNOTATION_TEXT:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_annotation = s_value;
|
|
break;
|
|
case PKL_ICON_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_icon_id = s_value;
|
|
break;
|
|
case PKL_GROUP_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_group_id = s_value;
|
|
break;
|
|
default:
|
|
msg_Warn(this->p_demux, "Unknown PKG_TAG: %i", _tag );
|
|
break;
|
|
}
|
|
/* break the for loop as a tag is found*/
|
|
break;
|
|
}
|
|
}
|
|
if( _tag == PKL_UNKNOWN )
|
|
goto error;
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
case -1:
|
|
goto error;
|
|
case XML_READER_ENDELEM:
|
|
if ( node != s_root_node) {
|
|
msg_Err(this->p_demux,
|
|
"Something goes wrong in PKL parsing (node %s)", node.c_str());
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* TODO verify presence of mandatory fields*/
|
|
|
|
/* Close PKL XML*/
|
|
this->CloseXml();
|
|
return 0;
|
|
error:
|
|
msg_Err( this->p_demux, "PKL parsing failed");
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
|
|
int PKL::FindCPLs()
|
|
{
|
|
if ( this->vec_cpl.size() != 0 ) {
|
|
msg_Err(this->p_demux, "CPLs already checked");
|
|
return -1;
|
|
}
|
|
|
|
for (AssetList::iterator index = this->asset_list->begin();
|
|
index != this->asset_list->end(); ++index) {
|
|
Asset *asset = *index;
|
|
if ( asset->getType().find("text/xml") == string::npos) {
|
|
/* not an xml file */
|
|
continue;
|
|
}
|
|
|
|
CPL *cpl = new (nothrow) CPL(this->p_demux,
|
|
this->s_dcp_path + asset->getPath(),
|
|
this->asset_list);
|
|
if ( unlikely(cpl == NULL) )
|
|
return -1;
|
|
switch( cpl->isCPL() )
|
|
{
|
|
case 1:
|
|
/* CPL Found */
|
|
this->vec_cpl.push_back(cpl);
|
|
break;
|
|
case -1:
|
|
/* error */
|
|
delete cpl;
|
|
return -1;
|
|
case 0:
|
|
default:
|
|
delete cpl;
|
|
break;
|
|
}
|
|
}
|
|
return this->vec_cpl.size();
|
|
}
|
|
|
|
|
|
int PKL::ParseAssetList(string p_node, int p_type) {
|
|
string node;
|
|
int type;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "AssetList")
|
|
return -1;
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
if( node =="Asset") {
|
|
if ( this->ParseAsset(node, type) )
|
|
return -1;
|
|
} else
|
|
return -1;
|
|
break;
|
|
case XML_READER_ENDELEM:
|
|
if ( node == p_node) {
|
|
/* parse of chunklist finished */
|
|
goto end;
|
|
}
|
|
break;
|
|
case -1:
|
|
/* error */
|
|
return -1;
|
|
}
|
|
}
|
|
end:
|
|
return 0;
|
|
}
|
|
|
|
int PKL::ParseAsset(string p_node, int p_type) {
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
Asset *asset = NULL;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "Asset")
|
|
return -1;
|
|
|
|
/* 1st node shall be Id" */
|
|
if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) )
|
|
if ( ! ( ( type == XML_READER_STARTELEM ) && ( node == "Id" ) ) || type == -1 )
|
|
return -1;
|
|
if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) != -1 )
|
|
{
|
|
if( type == XML_READER_TEXT )
|
|
{
|
|
s_value = node;
|
|
if (unlikely(node.empty()))
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) != -1 )
|
|
{
|
|
if( type == XML_READER_ENDELEM )
|
|
{
|
|
asset = AssetMap::getAssetById(this->asset_list, s_value);
|
|
if (asset == NULL)
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
if (asset == NULL)
|
|
return -1;
|
|
if ( asset->ParsePKL(this->p_xmlReader) )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int PKL::ParseSigner(string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "Signer")
|
|
return -1;
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
/* TODO not implemented. Just parse until end of Signer node */
|
|
if ((node == p_node) && (type == XML_READER_ENDELEM))
|
|
return 0;
|
|
}
|
|
|
|
msg_Err(this->p_demux, "Parse of Signer finished bad");
|
|
return -1;
|
|
}
|
|
|
|
int PKL::ParseSignature(string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "Signature")
|
|
return -1;
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
/* TODO not implemented. Just parse until end of Signature node */
|
|
if ((node == p_node) && (type == XML_READER_ENDELEM))
|
|
return 0;
|
|
}
|
|
msg_Err(this->p_demux, "Parse of Signature finished bad");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Reel Class
|
|
*/
|
|
int Reel::Parse(string p_node, int p_type) {
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "Reel")
|
|
return -1;
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
if (node =="Id") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->s_id = s_value;
|
|
} else if (node == "AnnotationText") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
this->s_annotation = s_value;
|
|
} else if ( node =="AssetList" ) {
|
|
if (this->ParseAssetList(node, type))
|
|
return -1;
|
|
} else {
|
|
/* unknown tag */
|
|
msg_Err(this->p_demux, "Reel::Parse, unknown tag:%s", node.c_str());
|
|
return -1;
|
|
}
|
|
break;
|
|
case XML_READER_TEXT:
|
|
/* Error */
|
|
msg_Err(this->p_demux, "Reel parsing error");
|
|
return -1;
|
|
case XML_READER_ENDELEM:
|
|
/* verify correctness of end node */
|
|
if ( node == p_node) {
|
|
/* TODO : verify Reel id */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
Asset * Reel::getTrack(TrackType_t e_track)
|
|
{
|
|
switch (e_track) {
|
|
case TRACK_PICTURE:
|
|
return this->p_picture_track;
|
|
case TRACK_SOUND:
|
|
return this->p_sound_track;
|
|
case TRACK_SUBTITLE:
|
|
return this->p_subtitle_track;
|
|
case TRACK_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int Reel::ParseAssetList(string p_node, int p_type) {
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "AssetList")
|
|
return -1;
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
if (node =="MainPicture") {
|
|
if ( this->ParseAsset(node, type, TRACK_PICTURE) )
|
|
return -1;
|
|
} else if (node =="MainSound") {
|
|
if ( this->ParseAsset(node, type, TRACK_SOUND) )
|
|
return -1;
|
|
} else if (node =="MainSubtitle") {
|
|
if ( this->ParseAsset(node, type, TRACK_SUBTITLE) )
|
|
return -1;
|
|
} else {
|
|
/* unknown tag */
|
|
msg_Err(this->p_demux, "Reel::ParseAssetList, unknown tag:%s", node.c_str());
|
|
return -1;
|
|
}
|
|
break;
|
|
case XML_READER_TEXT:
|
|
/* Parsing error */
|
|
msg_Err(this->p_demux, "AssetList parsing error");
|
|
return -1;
|
|
case XML_READER_ENDELEM:
|
|
/* verify correctness of end node */
|
|
if ( node == p_node) {
|
|
/* TODO : verify id */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
bool b_stop_parse = false;
|
|
Asset *asset = NULL;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
|
|
/* 1st node shall be Id */
|
|
if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) )
|
|
if( ! ( ( type == XML_READER_STARTELEM ) && ( node == "Id" ) ) || type == -1 )
|
|
return -1;
|
|
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
|
|
asset = AssetMap::getAssetById(this->p_asset_list, s_value);
|
|
if (asset == NULL)
|
|
return -1;
|
|
|
|
while( (! b_stop_parse) &&
|
|
( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM:
|
|
if (node =="EditRate") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
} else if (node == "AnnotationText") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
asset->setAnnotation(s_value);
|
|
} else if (node == "IntrinsicDuration") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
asset->setIntrinsicDuration(atoi(s_value.c_str()));
|
|
} else if (node == "EntryPoint") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
asset->setEntryPoint(atoi(s_value.c_str()));
|
|
} else if (node == "Duration") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
asset->setDuration(atoi(s_value.c_str()));
|
|
} else if (node == "KeyId") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
asset->setKeyId( s_value );
|
|
} else if (node == "Hash") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
} else if (node == "FrameRate") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
} else if (node == "ScreenAspectRatio") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
} else if (node == "Language") {
|
|
if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
|
|
return -1;
|
|
} else {
|
|
/* unknown tag */
|
|
msg_Err(this->p_demux, "Reel::ParseAsset unknown tag:%s", node.c_str());
|
|
return -1;
|
|
}
|
|
break;
|
|
case XML_READER_TEXT:
|
|
case -1:
|
|
/* error */
|
|
return -1;
|
|
break;
|
|
case XML_READER_ENDELEM:
|
|
/* verify correctness of end node */
|
|
if ( node == p_node) {
|
|
/* TODO : verify id */
|
|
b_stop_parse = true;
|
|
}
|
|
}
|
|
}
|
|
/* store by track */
|
|
switch (e_track) {
|
|
case TRACK_PICTURE:
|
|
this->p_picture_track = asset;
|
|
break;
|
|
case TRACK_SOUND:
|
|
this->p_sound_track = asset;
|
|
break;
|
|
case TRACK_SUBTITLE:
|
|
this->p_subtitle_track = asset;
|
|
break;
|
|
case TRACK_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* CPL Class
|
|
*/
|
|
|
|
CPL::~CPL() {
|
|
vlc_delete_all(vec_reel);
|
|
}
|
|
|
|
int CPL::Parse()
|
|
{
|
|
string node;
|
|
int type;
|
|
string s_value;
|
|
const string s_root_node = "CompositionPlaylist";
|
|
|
|
static const string names[] = {
|
|
"Id",
|
|
"AnnotationText",
|
|
"IconId",
|
|
"IssueDate",
|
|
"Issuer",
|
|
"Creator",
|
|
"ContentTitleText",
|
|
"ContentKind",
|
|
"ContentVersion",
|
|
"RatingList",
|
|
"ReelList",
|
|
"Signer",
|
|
"Signature"
|
|
};
|
|
|
|
if (this->OpenXml())
|
|
return -1;
|
|
|
|
/* read 1st node and verify that is a CPL*/
|
|
if( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) &&
|
|
(node == s_root_node) ) ) {
|
|
msg_Err( this->p_demux, "Not a valid XML Packing List");
|
|
goto error;
|
|
}
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM: {
|
|
CPLTag_t _tag = CPL_UNKNOWN;
|
|
for(CPLTag_t i = CPL_ID; i <= CPL_SIGNATURE; i = CPLTag_t(i+1)) {
|
|
if( node == names[i-1]) {
|
|
_tag = i;
|
|
switch (_tag) {
|
|
/* case for parsing non terminal nodes */
|
|
case CPL_REEL_LIST:
|
|
if ( this->ParseReelList(node, type) )
|
|
goto error;
|
|
break;
|
|
case CPL_CONTENT_VERSION:
|
|
case CPL_SIGNER:
|
|
case CPL_SIGNATURE:
|
|
case CPL_RATING_LIST:
|
|
if ( this->DummyParse(node,type) )
|
|
goto error;
|
|
break;
|
|
/* Parse simple/end nodes */
|
|
case CPL_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_id = s_value;
|
|
break;
|
|
case CPL_ANNOTATION_TEXT:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_annotation = s_value;
|
|
break;
|
|
case CPL_ICON_ID:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_icon_id = s_value;
|
|
break;
|
|
case CPL_ISSUE_DATE:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_issue_date= s_value;
|
|
break;
|
|
case CPL_ISSUER:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_issuer = s_value;
|
|
break;
|
|
case CPL_CREATOR:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_creator = s_value;
|
|
break;
|
|
case CPL_CONTENT_TITLE:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_content_title = s_value;
|
|
break;
|
|
case CPL_CONTENT_KIND:
|
|
if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
|
|
goto error;
|
|
this->s_content_kind = s_value;
|
|
break;
|
|
default:
|
|
msg_Warn(this->p_demux, "Unknown CPL_TAG: %i", _tag );
|
|
break;
|
|
}
|
|
|
|
/* break the for loop as a tag is found*/
|
|
break;
|
|
}
|
|
}
|
|
if( _tag == CPL_UNKNOWN )
|
|
goto error;
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
case -1:
|
|
goto error;
|
|
case XML_READER_ENDELEM:
|
|
if ( node != s_root_node) {
|
|
msg_Err(this->p_demux,
|
|
"Something goes wrong in CKL parsing (node %s)", node.c_str());
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* TODO verify presence of mandatory fields*/
|
|
|
|
/* Close CPL XML*/
|
|
this->CloseXml();
|
|
return 0;
|
|
error:
|
|
this->CloseXml();
|
|
return -1;
|
|
}
|
|
|
|
int CPL::ParseReelList(string p_node, int p_type) {
|
|
string node;
|
|
int type;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
if( p_node != "ReelList")
|
|
return -1;
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
switch (type) {
|
|
case XML_READER_STARTELEM: {
|
|
Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
|
|
if ( unlikely(p_reel == NULL) )
|
|
return -1;
|
|
if( node =="Reel") {
|
|
if ( p_reel->Parse(node, type) ) {
|
|
delete p_reel;
|
|
return -1;
|
|
}
|
|
} else {
|
|
delete p_reel;
|
|
return -1;
|
|
}
|
|
this->vec_reel.push_back(p_reel);
|
|
|
|
break;
|
|
}
|
|
case XML_READER_TEXT:
|
|
/* impossible */
|
|
break;
|
|
case XML_READER_ENDELEM:
|
|
if ( node == p_node)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int CPL::DummyParse(string p_node, int p_type)
|
|
{
|
|
string node;
|
|
int type;
|
|
|
|
if (p_type != XML_READER_STARTELEM)
|
|
return -1;
|
|
|
|
if (xml_ReaderIsEmptyElement( this->p_xmlReader))
|
|
return 0;
|
|
|
|
while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
|
|
/* TODO not implemented. Just pase until end of input node */
|
|
if ((node == p_node) && (type == XML_READER_ENDELEM))
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|