demux: mkv: handle WAVE_FORMAT_MPEG_ADTS_AAC
[vlc.git] / modules / access / dcp / dcpparser.cpp
blob92a26f1a722e275fc56ccd0a19fcfab07e38ec64
1 /*****************************************************************************
2 * Copyright (C) 2013 VLC authors and VideoLAN
4 * Authors:
5 * Nicolas Bertrand <nico@isf.cc>
6 * Jean-Baptiste Kempf <jb@videolan.org>
7 * Guillaume Gonnaud
8 * Valentin Vetter <vvetter@outlook.com>
9 * Anthony Giniers
10 * Ludovic Hoareau
11 * Loukmane Dessai
12 * Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 /**
30 * @file dcpparser.cpp
31 * @brief Parsing of DCP XML files
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
37 /* VLC core API headers */
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_xml.h>
41 #include <vlc_url.h>
43 #include <iostream>
44 #include <string>
45 #include <list>
46 #include <vector>
48 #include "dcpparser.h"
50 using namespace std;
52 typedef enum {
53 CHUNK_UNKNOWN = 0,
54 CHUNK_PATH,
55 CHUNK_VOL_INDEX,
56 CHUNK_OFFSET,
57 CHUNK_LENGTH
58 } ChunkTag_t;
61 typedef enum {
62 ASSET_UNKNOWN = 0,
63 ASSET_ID,
64 ASSET_ANNOTATION_TEXT,
65 ASSET_PACKING_LIST,
66 ASSET_CHUNK_LIST,
67 ASSET_HASH,
68 ASSET_SIZE,
69 ASSET_TYPE,
70 ASSET_ORIGINAL_FILENAME
71 } AssetTag_t;
73 static const string g_asset_names[] = {
74 "Id",
75 "AnnotationText",
76 "PackingList",
77 "ChunkList",
78 "Hash",
79 "Size",
80 "Type",
81 "OriginalFileName"
85 typedef enum {
86 PKL_UNKNOWN = 0,
87 PKL_ID,
88 PKL_ISSUE_DATE,
89 PKL_ISSUER,
90 PKL_CREATOR,
91 PKL_ASSET_LIST,
92 PKL_ANNOTATION_TEXT, /* start of optional tags */
93 PKL_ICON_ID,
94 PKL_GROUP_ID,
95 PKL_SIGNER,
96 PKL_SIGNATURE,
97 } PKLTag_t;
99 typedef enum {
100 CPL_UNKNOWN = 0,
101 CPL_ID,
102 CPL_ANNOTATION_TEXT, /* optional */
103 CPL_ICON_ID, /* optional */
104 CPL_ISSUE_DATE,
105 CPL_ISSUER, /* optional */
106 CPL_CREATOR, /* optional */
107 CPL_CONTENT_TITLE,
108 CPL_CONTENT_KIND,
109 CPL_CONTENT_VERSION, /* not optional, but not always present*/
110 CPL_RATING_LIST, /* not handled */
111 CPL_REEL_LIST,
112 CPL_SIGNER, /* optional - not handled */
113 CPL_SIGNATURE /* optional - not handled */
114 } CPLTag_t;
117 class ChunkList: public std::list<Chunk> {
118 public :
119 ChunkList();
120 ~ChunkList();
124 * Chunk Class
126 int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
127 string node;
128 int type;
129 string s_value;
130 static const string names[] = {"Path", "VolumeIndex", "Offset",
131 "Length"};
132 if (p_type != XML_READER_STARTELEM)
133 return -1;
134 if( p_node != "Chunk")
135 return -1;
136 /* loop on Chunks Node */
137 while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
138 switch (type) {
139 case XML_READER_STARTELEM:
141 ChunkTag_t chunk_tag = CHUNK_UNKNOWN;
142 for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
143 if( node == names[i-1]) {
144 chunk_tag = i;
145 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
146 return -1;
147 switch (chunk_tag) {
148 case CHUNK_PATH:
149 this->s_path = s_value;
150 break;
151 case CHUNK_VOL_INDEX:
152 this->i_vol_index = atoi(s_value.c_str());
153 break;
154 case CHUNK_OFFSET:
155 this->i_offset = atoi(s_value.c_str());
156 break;
157 case CHUNK_LENGTH:
158 this->i_length = atoi(s_value.c_str());
159 break;
160 case CHUNK_UNKNOWN:
161 default:
162 break;
164 /* break the for loop as a tag is found*/
165 break;
168 if(chunk_tag == CHUNK_UNKNOWN)
169 return -1;
170 break;
172 case XML_READER_TEXT:
173 s_value = node;
174 if (unlikely(node.empty()))
175 return -1;
176 break;
177 case XML_READER_ENDELEM:
178 /* Verify if we reach Chuk endelem */
179 if ( node == p_node) {
180 /* verify path */
181 if ( this->s_path.empty() ) {
182 msg_Err(this->p_demux, "Chunk::Parse No path found");
183 return -1;
185 if ( this->i_vol_index != 1 ) {
186 msg_Err(this->p_demux, "Only one VOLINDEX supported. Patch welcome.");
187 return -1;
189 /* end of chunk tag parse */
190 return 0;
192 break;
195 return -1;
198 * AssetMap Class
201 AssetMap::~AssetMap() { }
203 int AssetMap::Parse ( )
205 int type = 0;
206 int retval;
207 int reel_nbr = 0;
208 int index = 0;
209 int sum_duration_vid = 0;
210 int sum_duration_aud = 0;
211 string node;
212 char *psz_kdm_path;
214 CPL *cpl;
215 Reel *reel;
216 PKL *pkl;
217 AssetList *_p_asset_list = NULL;
219 vector<string> pklfiles;
221 /* init XML parser */
222 if( this->OpenXml() ) {
223 msg_Err( p_demux, "Failed to initialize Assetmap XML parser" );
224 return -1;
227 /* reading ASSETMAP file to get the asset_list */
228 msg_Dbg( p_demux, "reading ASSETMAP file..." );
229 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
230 if( type == -1 )
232 this->CloseXml();
233 return -1;
235 if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
236 _p_asset_list = new (nothrow) AssetList();
237 if ( unlikely(_p_asset_list == NULL) ) {
238 this->CloseXml();
239 return -1;
241 p_dcp->p_asset_list = _p_asset_list;
242 if (this->ParseAssetList(p_xmlReader, node, type )) {
243 this->CloseXml();
244 return -1;
246 /* asset list found so break*/
247 break;
251 /* Look for PKLs path */
252 if ( (_p_asset_list == NULL) || (_p_asset_list->size() == 0) ) {
253 msg_Err( p_demux, "Asset list empty" );
254 this->CloseXml();
255 return -1;
258 for (AssetList::iterator iter = _p_asset_list->begin();
259 iter != _p_asset_list->end() ; ++iter) {
260 string s_filepath;
261 s_filepath = (*iter)->getPath();
262 if (s_filepath.empty()) {
263 msg_Err( p_demux, "No path element for asset" );
264 continue;
266 /* set an absolute file path */
267 s_filepath = p_dcp->path + s_filepath;
269 /* case of packing list */
270 if ((*iter)->isPackingList()) {
271 pklfiles.push_back( s_filepath );
275 /* TODO: case of only on PKL managed.
276 * Future work needed for managing severals
277 * the PKL vector will be used to select the required PKL */
278 if( (pklfiles.size() == 0) || (pklfiles[0].empty()) )
280 msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
281 this->CloseXml();
282 return -1;
285 /* Create the first PKL */
286 pkl = new (nothrow) PKL(p_demux, pklfiles[0], _p_asset_list, p_dcp->path);
287 if ( unlikely(pkl == NULL) ) {
288 this->CloseXml();
289 return -1;
291 if (pkl->Parse()) {
292 delete pkl;
293 this->CloseXml();
294 return -1;
296 p_dcp->pkls.push_back( pkl );
298 /* Now, CPL */
299 if ( pkl->FindCPLs() <= 0 ) {
300 msg_Err(p_demux, " No CPL found");
301 this->CloseXml();
302 return -1;
304 /* TODO: Only one CPL managed.
305 * Future work needed for managing severals
308 cpl = pkl->getCPL(0);
309 if( cpl == NULL ) {
310 msg_Err(p_demux, " No CPL found");
311 this->CloseXml();
312 return -1;
314 if ( cpl->Parse() ) {
315 this->CloseXml();
316 return -1;
319 /* KDM, if needed */
320 for( AssetList::iterator iter = _p_asset_list->begin(); iter != _p_asset_list->end(); ++iter )
321 if( ! (*iter)->getKeyId().empty() )
323 msg_Dbg( p_demux, "DCP is encrypted, searching KDM file...");
324 psz_kdm_path = var_InheritString( p_demux, "kdm" );
325 if( !psz_kdm_path || !*psz_kdm_path )
327 msg_Err( p_demux, "cryptographic key IDs found in CPL and no path to KDM given");
328 free( psz_kdm_path );
329 this->CloseXml();
330 return VLC_EGENERIC;
332 KDM p_kdm( p_demux, psz_kdm_path, p_dcp );
333 free( psz_kdm_path );
334 if( ( retval = p_kdm.Parse() ) )
336 this->CloseXml();
337 return retval;
339 break;
342 reel_nbr = cpl->getReelList().size();
343 for(index = 0; index != reel_nbr; ++index)
345 reel = cpl->getReel(index);
347 Asset *asset;
348 struct info_reel video;
349 struct info_reel audio;
351 /* Get picture */
352 asset = reel->getTrack(TRACK_PICTURE);
353 if (asset != NULL)
355 sum_duration_vid += asset->getDuration();
356 video.filename = p_dcp->path + asset->getPath();
357 video.i_entrypoint = asset->getEntryPoint();
358 video.i_duration = asset->getDuration();
359 video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
360 video.i_absolute_end = sum_duration_vid;
361 video.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
362 p_dcp->video_reels.push_back(video);
363 msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
364 msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
366 /* Get audio */
367 asset = reel->getTrack(TRACK_SOUND);
368 if (asset != NULL)
370 /*if (!p_dcp->audio_reels.empty())
372 sum_duration_aud = 0;
373 for (int i = 0; i != p_dcp->audio_reels.size(); ++i)
375 sum_duration_aud += p_dcp->audio_reels(i).i_duration;
378 sum_duration_aud += asset->getDuration();
379 audio.filename = p_dcp->path + asset->getPath();
380 audio.i_entrypoint = asset->getEntryPoint();
381 audio.i_duration = asset->getDuration();
382 audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
383 audio.i_absolute_end = sum_duration_aud;
384 audio.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
385 p_dcp->audio_reels.push_back(audio);
386 msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
387 msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
390 /* free memory */
391 this->CloseXml();
392 return VLC_SUCCESS;
398 * Asset Class
400 Asset::~Asset() {}
402 int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
404 string node;
405 int type;
406 string s_value;
407 const string s_root_node = "Asset";
409 if (p_type != XML_READER_STARTELEM)
410 return -1;
411 if( p_node != s_root_node)
412 return -1;
413 /* loop on Assets Node */
414 while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
415 switch (type) {
416 case XML_READER_STARTELEM:
418 AssetTag_t _tag = ASSET_UNKNOWN;
419 for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
420 if( node == g_asset_names[i-1]) {
421 _tag = i;
422 switch(_tag) {
423 /* Case of complex nodes */
424 case ASSET_PACKING_LIST:
425 /* case of <PackingList/> tag, bur not compliant with SMPTE-429-9 2007*/
426 if (xml_ReaderIsEmptyElement( p_xmlReader))
428 this->b_is_packing_list = true;
430 else if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
432 msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
433 return -1;
435 if ( s_value == "true" )
436 this->b_is_packing_list = true;
437 break;
438 case ASSET_CHUNK_LIST:
439 if ( this->parseChunkList(p_xmlReader, node, type ) )
441 msg_Err(this->p_demux, "Error parsing chunk list: %s", node.c_str());
442 return -1;
444 this->s_path = this->chunk_vec[0].getPath();
445 break;
446 case ASSET_ID:
447 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
449 msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
450 return -1;
452 this->s_id = s_value;
453 break;
454 case ASSET_ANNOTATION_TEXT:
455 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
457 msg_Err(this->p_demux, "Missing end node in %s", node.c_str());
458 return -1;
460 this->s_annotation = s_value;
461 break;
462 case ASSET_ORIGINAL_FILENAME:
463 case ASSET_HASH:
464 case ASSET_TYPE:
465 case ASSET_SIZE:
466 /* Asset tags not in AssetMap */
467 break;
468 default:
469 msg_Warn(this->p_demux, "Unknown ASSET_TAG: %i", _tag );
470 break;
472 /* break the for loop as a tag is found*/
473 break;
476 if( _tag == ASSET_UNKNOWN )
478 msg_Err(this->p_demux, "Unknown ASSET_TAG: %s", node.c_str());
479 return -1;
481 break;
483 case XML_READER_TEXT:
484 msg_Err(this->p_demux, " Text element found in Asset");
485 return -1;
486 case XML_READER_ENDELEM:
487 if ( node != s_root_node) {
488 msg_Err(this->p_demux,
489 "Something goes wrong in Asset parsing on Assetmap (node %s)", node.c_str());
490 return -1;
491 } else {
492 /*Check Presence of Id and Chunklist */
493 if ( this->s_id.empty() ) {
494 msg_Err(this->p_demux, " No Id element found in Asset");
495 return -1;
497 if ( this->s_path.empty() ) {
498 msg_Err(this->p_demux, " No path element found in Asset");
499 return -1;
501 return 0;
503 break;
506 return -1;
511 int Asset::ParsePKL( xml_reader_t *p_xmlReader)
513 string node;
514 int type;
515 string s_value;
516 const string s_root_node = "Asset";
518 while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
519 switch (type) {
520 case XML_READER_STARTELEM:
522 AssetTag_t _tag = ASSET_UNKNOWN;
523 for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
524 if( node == g_asset_names[i-1]) {
525 _tag = i;
526 switch(_tag) {
527 case ASSET_ANNOTATION_TEXT:
528 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
529 return -1;
530 if ( this->s_annotation.empty() )
531 this->s_annotation = s_value;
532 else
533 this->s_annotation = this->s_annotation + "--" + s_value;
534 break;
535 case ASSET_HASH:
536 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
537 return -1;
538 this->s_hash = s_value;
539 break;
540 case ASSET_SIZE:
541 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
542 return -1;
543 this->ui_size = atol(s_value.c_str());
544 break;
545 case ASSET_TYPE:
546 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
547 return -1;
548 this->s_type = s_value;
549 break;
550 case ASSET_ORIGINAL_FILENAME:
551 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
552 return -1;
553 this->s_original_filename = s_value;
554 break;
555 case ASSET_ID: /* already verified */
556 case ASSET_PACKING_LIST:
557 case ASSET_CHUNK_LIST:
558 /* Asset tags not in PKL */
559 break;
560 default:
561 msg_Warn(this->p_demux, "Unknow ASSET_TAG: %i", _tag );
562 break;
564 /* break the for loop as a tag is found*/
565 break;
568 if( _tag == ASSET_UNKNOWN )
569 return -1;
570 break;
572 case XML_READER_TEXT:
573 return -1;
574 case XML_READER_ENDELEM:
575 if ( node != s_root_node) {
576 msg_Err(this->p_demux,
577 "Something goes wrong in Asset parsing on PKL (node %s)", node.c_str());
578 return -1;
579 } else {
580 /* Verify that mandatory attributes are filled */
581 if (this->s_hash.empty()) {
582 msg_Err(this->p_demux,"Asset Hash tag invalid");
583 return -1;
585 if (this->ui_size == 0) {
586 msg_Err(this->p_demux,"Asset Size tag invalid");
587 return -1;
589 if (this->s_type.empty()) {
590 msg_Err(this->p_demux,"Asset Type tag invalid");
591 return -1;
593 return 0;
595 break;
600 return -1;
603 void Asset::Dump()
605 msg_Dbg(this->p_demux,"Id = %s", this->s_id.c_str());
606 msg_Dbg(this->p_demux,"Path = %s", this->s_path.c_str());
607 msg_Dbg(this->p_demux,"Is PKL = %s", this->b_is_packing_list ? "True" : "False");
608 msg_Dbg(this->p_demux,"Hash = %s", this->s_hash.c_str());
609 msg_Dbg(this->p_demux,"Size = %i", this->ui_size);
610 msg_Dbg(this->p_demux,"Type = %s", this->s_type.c_str());
611 msg_Dbg(this->p_demux,"OrignalFileName = %s", this->s_original_filename.c_str());
612 msg_Dbg(this->p_demux,"AnnotationText = %s", this->s_annotation.c_str());
615 int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
617 string node;
618 int type;
619 string s_value;
620 std::vector<Chunk> chunk_vec;
622 if (p_type != XML_READER_STARTELEM)
623 return -1;
624 if( p_node != "ChunkList" )
625 return -1;
626 /* loop on Assets Node */
627 while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
628 switch (type) {
629 case XML_READER_STARTELEM:
631 Chunk chunk(this->p_demux);
632 if (node != "Chunk" )
633 return -1;
634 if ( chunk.Parse(p_xmlReader, node, type) )
635 return -1;
636 chunk_vec.push_back(chunk);
637 break;
639 case XML_READER_ENDELEM:
640 if ( node == p_node) {
641 if (chunk_vec.size() != 1 ) {
642 msg_Err(this->p_demux, "chunklist of size greater than one not supported");
643 return -1;
645 this->chunk_vec = chunk_vec;
646 return 0;
648 break;
651 return -1;
654 AESKey * Asset::getAESKeyById( AESKeyList* p_key_list, const string s_id )
656 /* return NULL if DCP is not encrypted */
657 if( !p_key_list || s_id.empty() )
658 return NULL;
660 for( AESKeyList::iterator index = p_key_list->begin(); index != p_key_list->end(); ++index )
661 if( (*index)->getKeyId() == s_id )
662 return *index;
664 return NULL;
668 int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
670 string node;
671 int type;
672 Asset *asset;
674 if (p_type != XML_READER_STARTELEM)
675 return -1;
676 if( p_node != "AssetList" )
677 return -1;
678 /* loop on AssetList nodes */
679 while( ( type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node ) ) > 0 ) {
680 switch (type) {
681 case XML_READER_STARTELEM:
682 if (node != "Asset" )
683 return -1;
684 asset = new (nothrow) Asset(this->p_demux);
685 if ( unlikely(asset == NULL) )
686 return -1;
687 if (asset->Parse(p_xmlReader, node, type)){
688 msg_Err(this->p_demux, "Error parsing Asset in AssetMap");
689 delete asset;
690 return -1;
692 p_dcp->p_asset_list->push_back(asset);
693 break;
695 case XML_READER_ENDELEM:
696 if (node == p_node )
697 return 0;
698 break;
699 default:
700 case XML_READER_TEXT:
701 msg_Err(this->p_demux, "Error parsing AssetList in AssetMap");
702 return -1;
705 return -1;
708 Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
710 AssetList::iterator index = asset_list->begin() ;
711 for (index = asset_list->begin(); index != asset_list->end(); ++index)
712 if ((*index)->getId() == p_id )
713 return *index;
714 return NULL;
718 * XmlFile Class
720 XmlFile::~XmlFile() {}
722 int XmlFile::OpenXml()
724 char *psz_uri;
726 psz_uri = vlc_path2uri( this->s_path.c_str(), "file" );
727 this->p_stream = vlc_stream_NewURL(this->p_demux, psz_uri );
728 free(psz_uri);
729 if( ! this->p_stream ) {
730 return -1;
733 this->p_xmlReader = xml_ReaderCreate( this->p_demux, this->p_stream);
734 if( ! this->p_xmlReader ) {
735 vlc_stream_Delete( this->p_stream );
736 return -1;
738 return 0;
741 int XmlFile::ReadNextNode( demux_t *p_demux, xml_reader_t *p_xmlReader, string& p_node )
743 const char * c_node;
744 int i = xml_ReaderNextNode( p_xmlReader, &c_node );
746 /* remove namespaces, if there are any */
747 string s_node = c_node;
748 size_t ui_pos = s_node.find( ":" );
750 if( ( i == XML_READER_STARTELEM || i == XML_READER_ENDELEM ) && ( ui_pos != string::npos ) )
754 p_node = s_node.substr( ui_pos + 1 );
756 catch( ... )
758 msg_Err( p_demux, "error while handling string" );
759 return -1;
762 else
763 p_node = s_node;
765 return i;
768 int XmlFile::ReadEndNode( demux_t *p_demux, xml_reader_t *p_xmlReader, string p_node, int p_type, string &s_value)
770 string node;
772 if ( xml_ReaderIsEmptyElement( p_xmlReader) )
773 return 0;
775 if (p_type != XML_READER_STARTELEM)
776 return -1;
778 int n = XmlFile::ReadNextNode( p_demux, p_xmlReader, node );
779 if( n == XML_READER_TEXT )
781 s_value = node;
782 n = XmlFile::ReadNextNode( p_demux, p_xmlReader, node );
783 if( ( n == XML_READER_ENDELEM ) && node == p_node)
784 return 0;
786 return n == XML_READER_ENDELEM ? 0 : -1;
789 * Reads first node in XML and returns
790 * 1 if XML is CPL,
791 * 0 if not
792 * -1 on error
794 int XmlFile::isCPL()
796 string node;
797 int type, ret = 0;
799 if( this->OpenXml() )
801 msg_Err( this->p_demux, "Failed to open CPL XML file" );
802 return -1;
805 /* read 1st node and verify that is a CPL */
806 type = XmlFile::ReadNextNode( this->p_demux, p_xmlReader, node );
807 if( type == -1 ) /* error */
808 ret = -1;
809 if( type == XML_READER_STARTELEM && node == "CompositionPlaylist" )
810 ret = 1;
812 /* close xml */
813 this->CloseXml();
814 return ret;
817 void XmlFile::CloseXml() {
818 if( this->p_stream )
819 vlc_stream_Delete( this->p_stream );
820 if( this->p_xmlReader )
821 xml_ReaderDelete( this->p_xmlReader );
825 * PKL Class
828 PKL::~PKL() {
829 vlc_delete_all(vec_cpl);
832 int PKL::Parse()
834 string node;
835 int type;
836 string s_value;
837 const string s_root_node = "PackingList";
839 static const string names[] = {
840 "Id",
841 "IssueDate",
842 "Issuer",
843 "Creator",
844 "AssetList",
845 "AnnotationText",
846 "IconId",
847 "GroupId",
848 "Signer",
849 "Signature"
852 if (this->OpenXml())
853 return -1;
855 /* read 1st node and verify that is a PKL*/
856 if ( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) &&
857 (node == s_root_node) ) ) {
858 msg_Err( this->p_demux, "Not a valid XML Packing List");
859 goto error;
861 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
862 switch (type) {
863 case XML_READER_STARTELEM: {
864 PKLTag_t _tag = PKL_UNKNOWN;
865 for(PKLTag_t i = PKL_ID; i <= PKL_SIGNATURE; i = PKLTag_t(i+1)) {
866 if( node == names[i-1]) {
867 _tag = i;
868 switch (_tag) {
869 /* case for parsing non terminal nodes */
870 case PKL_ASSET_LIST:
871 if ( this->ParseAssetList(node, type) )
872 goto error;
873 break;
874 case PKL_SIGNER:
875 if ( this->ParseSigner(node, type) )
876 goto error;
877 break;
878 case PKL_SIGNATURE:
879 if ( this->ParseSignature(node, type) )
880 goto error;
881 break;
882 /* Parse simple/end nodes */
883 case PKL_ID:
884 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
885 goto error;
886 this->s_id = s_value;
887 break;
888 case PKL_ISSUE_DATE:
889 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
890 goto error;
891 this->s_issue_date = s_value;
892 break;
893 case PKL_ISSUER:
894 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
895 goto error;
896 this->s_issuer = s_value;
897 break;
898 case PKL_CREATOR:
899 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
900 goto error;
901 this->s_creator = s_value;
902 break;
903 case PKL_ANNOTATION_TEXT:
904 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
905 goto error;
906 this->s_annotation = s_value;
907 break;
908 case PKL_ICON_ID:
909 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
910 goto error;
911 this->s_icon_id = s_value;
912 break;
913 case PKL_GROUP_ID:
914 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
915 goto error;
916 this->s_group_id = s_value;
917 break;
918 default:
919 msg_Warn(this->p_demux, "Unknow PKG_TAG: %i", _tag );
920 break;
922 /* break the for loop as a tag is found*/
923 break;
926 if( _tag == PKL_UNKNOWN )
927 goto error;
928 break;
930 case XML_READER_TEXT:
931 case -1:
932 goto error;
933 case XML_READER_ENDELEM:
934 if ( node != s_root_node) {
935 msg_Err(this->p_demux,
936 "Something goes wrong in PKL parsing (node %s)", node.c_str());
937 goto error;
939 break;
942 /* TODO verify presence of mandatory fields*/
944 /* Close PKL XML*/
945 this->CloseXml();
946 return 0;
947 error:
948 msg_Err( this->p_demux, "PKL parsing failed");
949 this->CloseXml();
950 return -1;
953 int PKL::FindCPLs()
955 if ( this->vec_cpl.size() != 0 ) {
956 msg_Err(this->p_demux, "CPLs already checked");
957 return -1;
960 for (AssetList::iterator index = this->asset_list->begin();
961 index != this->asset_list->end(); ++index) {
962 Asset *asset = *index;
963 if ( asset->getType().find("text/xml") == string::npos) {
964 /* not an xml file */
965 continue;
968 CPL *cpl = new (nothrow) CPL(this->p_demux,
969 this->s_dcp_path + asset->getPath(),
970 this->asset_list);
971 if ( unlikely(cpl == NULL) )
972 return -1;
973 switch( cpl->isCPL() )
975 case 1:
976 /* CPL Found */
977 this->vec_cpl.push_back(cpl);
978 break;
979 case -1:
980 /* error */
981 return -1;
982 case 0:
983 default:
984 delete cpl;
985 break;
988 return this->vec_cpl.size();
992 int PKL::ParseAssetList(string p_node, int p_type) {
993 string node;
994 int type;
996 if (p_type != XML_READER_STARTELEM)
997 return -1;
998 if( p_node != "AssetList")
999 return -1;
1000 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
1001 switch (type) {
1002 case XML_READER_STARTELEM:
1003 if( node =="Asset") {
1004 if ( this->ParseAsset(node, type) )
1005 return -1;
1006 } else
1007 return -1;
1008 break;
1009 case XML_READER_ENDELEM:
1010 if ( node == p_node) {
1011 /* parse of chunklist finished */
1012 goto end;
1014 break;
1015 case -1:
1016 /* error */
1017 return -1;
1020 end:
1021 return 0;
1024 int PKL::ParseAsset(string p_node, int p_type) {
1025 string node;
1026 int type;
1027 string s_value;
1028 Asset *asset = NULL;
1030 if (p_type != XML_READER_STARTELEM)
1031 return -1;
1032 if( p_node != "Asset")
1033 return -1;
1035 /* 1st node shall be Id" */
1036 if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) )
1037 if ( ! ( ( type == XML_READER_STARTELEM ) && ( node == "Id" ) ) || type == -1 )
1038 return -1;
1039 if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) != -1 )
1041 if( type == XML_READER_TEXT )
1043 s_value = node;
1044 if (unlikely(node.empty()))
1045 return -1;
1048 else
1049 return -1;
1051 if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) != -1 )
1053 if( type == XML_READER_ENDELEM )
1055 asset = AssetMap::getAssetById(this->asset_list, s_value);
1056 if (asset == NULL)
1057 return -1;
1060 else
1061 return -1;
1063 if (asset == NULL)
1064 return -1;
1065 if ( asset->ParsePKL(this->p_xmlReader) )
1066 return -1;
1067 return 0;
1070 int PKL::ParseSigner(string p_node, int p_type)
1072 string node;
1073 int type;
1075 if (p_type != XML_READER_STARTELEM)
1076 return -1;
1077 if( p_node != "Signer")
1078 return -1;
1080 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1081 /* TODO not implemented. Just parse until end of Signer node */
1082 if ((node == p_node) && (type = XML_READER_ENDELEM))
1083 return 0;
1086 msg_Err(this->p_demux, "Parse of Signer finished bad");
1087 return -1;
1090 int PKL::ParseSignature(string p_node, int p_type)
1092 string node;
1093 int type;
1095 if (p_type != XML_READER_STARTELEM)
1096 return -1;
1097 if( p_node != "Signature")
1098 return -1;
1100 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1101 /* TODO not implemented. Just parse until end of Signature node */
1102 if ((node == p_node) && (type = XML_READER_ENDELEM))
1103 return 0;
1105 msg_Err(this->p_demux, "Parse of Signature finished bad");
1106 return -1;
1110 * Reel Class
1112 int Reel::Parse(string p_node, int p_type) {
1113 string node;
1114 int type;
1115 string s_value;
1117 if (p_type != XML_READER_STARTELEM)
1118 return -1;
1119 if( p_node != "Reel")
1120 return -1;
1122 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1123 switch (type) {
1124 case XML_READER_STARTELEM:
1125 if (node =="Id") {
1126 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1127 return -1;
1128 this->s_id = s_value;
1129 } else if (node == "AnnotationText") {
1130 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1131 return -1;
1132 this->s_annotation = s_value;
1133 } else if ( node =="AssetList" ) {
1134 if (this->ParseAssetList(node, type))
1135 return -1;
1136 } else {
1137 /* unknown tag */
1138 msg_Err(this->p_demux, "Reel::Parse, unknown tag:%s", node.c_str());
1139 return -1;
1141 break;
1142 case XML_READER_TEXT:
1143 /* Error */
1144 msg_Err(this->p_demux, "Reel parsing error");
1145 return -1;
1146 case XML_READER_ENDELEM:
1147 /* verify correctness of end node */
1148 if ( node == p_node) {
1149 /* TODO : verify Reel id */
1150 return 0;
1152 break;
1155 return -1;
1159 Asset * Reel::getTrack(TrackType_t e_track)
1161 switch (e_track) {
1162 case TRACK_PICTURE:
1163 return this->p_picture_track;
1164 case TRACK_SOUND:
1165 return this->p_sound_track;
1166 case TRACK_SUBTITLE:
1167 return this->p_subtitle_track;
1168 case TRACK_UNKNOWN:
1169 default:
1170 break;
1172 return NULL;
1175 int Reel::ParseAssetList(string p_node, int p_type) {
1176 string node;
1177 int type;
1178 string s_value;
1180 if (p_type != XML_READER_STARTELEM)
1181 return -1;
1182 if( p_node != "AssetList")
1183 return -1;
1185 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1186 switch (type) {
1187 case XML_READER_STARTELEM:
1188 if (node =="MainPicture") {
1189 if ( this->ParseAsset(node, type, TRACK_PICTURE) )
1190 return -1;
1191 } else if (node =="MainSound") {
1192 if ( this->ParseAsset(node, type, TRACK_SOUND) )
1193 return -1;
1194 } else if (node =="MainSubtitle") {
1195 if ( this->ParseAsset(node, type, TRACK_SUBTITLE) )
1196 return -1;
1197 } else {
1198 /* unknown tag */
1199 msg_Err(this->p_demux, "Reel::ParseAssetList, unknown tag:%s", node.c_str());
1200 return -1;
1202 break;
1203 case XML_READER_TEXT:
1204 /* Parsing error */
1205 msg_Err(this->p_demux, "AssetList parsing error");
1206 return -1;
1207 case XML_READER_ENDELEM:
1208 /* verify correctness of end node */
1209 if ( node == p_node) {
1210 /* TODO : verify id */
1211 return 0;
1213 break;
1216 return -1;
1219 int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
1220 string node;
1221 int type;
1222 string s_value;
1223 bool b_stop_parse = false;
1224 Asset *asset = NULL;
1226 if (p_type != XML_READER_STARTELEM)
1227 return -1;
1229 /* 1st node shall be Id */
1230 if( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) )
1231 if( ! ( ( type == XML_READER_STARTELEM ) && ( node == "Id" ) ) || type == -1 )
1232 return -1;
1234 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1235 return -1;
1237 asset = AssetMap::getAssetById(this->p_asset_list, s_value);
1238 if (asset == NULL)
1239 return -1;
1241 while( (! b_stop_parse) &&
1242 ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
1243 switch (type) {
1244 case XML_READER_STARTELEM:
1245 if (node =="EditRate") {
1246 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1247 return -1;
1248 } else if (node == "AnnotationText") {
1249 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1250 return -1;
1251 asset->setAnnotation(s_value);
1252 } else if (node == "IntrinsicDuration") {
1253 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1254 return -1;
1255 asset->setIntrinsicDuration(atoi(s_value.c_str()));
1256 } else if (node == "EntryPoint") {
1257 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1258 return -1;
1259 asset->setEntryPoint(atoi(s_value.c_str()));
1260 } else if (node == "Duration") {
1261 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1262 return -1;
1263 asset->setDuration(atoi(s_value.c_str()));
1264 } else if (node == "KeyId") {
1265 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1266 return -1;
1267 asset->setKeyId( s_value );
1268 } else if (node == "Hash") {
1269 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1270 return -1;
1271 } else if (node == "FrameRate") {
1272 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1273 return -1;
1274 } else if (node == "ScreenAspectRatio") {
1275 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1276 return -1;
1277 } else if (node == "Language") {
1278 if ( XmlFile::ReadEndNode( this->p_demux, this->p_xmlReader, node, type, s_value ) )
1279 return -1;
1280 } else {
1281 /* unknown tag */
1282 msg_Err(this->p_demux, "Reel::ParseAsset unknown tag:%s", node.c_str());
1283 return -1;
1285 break;
1286 case XML_READER_TEXT:
1287 case -1:
1288 /* error */
1289 return -1;
1290 break;
1291 case XML_READER_ENDELEM:
1292 /* verify correctness of end node */
1293 if ( node == p_node) {
1294 /* TODO : verify id */
1295 b_stop_parse = true;
1299 /* store by track */
1300 switch (e_track) {
1301 case TRACK_PICTURE:
1302 this->p_picture_track = asset;
1303 break;
1304 case TRACK_SOUND:
1305 this->p_sound_track = asset;
1306 break;
1307 case TRACK_SUBTITLE:
1308 this->p_subtitle_track = asset;
1309 break;
1310 case TRACK_UNKNOWN:
1311 default:
1312 break;
1314 return 0;
1318 * CPL Class
1321 CPL::~CPL() {
1322 vlc_delete_all(vec_reel);
1325 int CPL::Parse()
1327 string node;
1328 int type;
1329 string s_value;
1330 const string s_root_node = "CompositionPlaylist";
1332 static const string names[] = {
1333 "Id",
1334 "AnnotationText",
1335 "IconId",
1336 "IssueDate",
1337 "Issuer",
1338 "Creator",
1339 "ContentTitleText",
1340 "ContentKind",
1341 "ContentVersion",
1342 "RatingList",
1343 "ReelList",
1344 "Signer",
1345 "Signature"
1348 if (this->OpenXml())
1349 return -1;
1351 /* read 1st node and verify that is a CPL*/
1352 if( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) &&
1353 (node == s_root_node) ) ) {
1354 msg_Err( this->p_demux, "Not a valid XML Packing List");
1355 goto error;
1358 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) ) {
1359 switch (type) {
1360 case XML_READER_STARTELEM: {
1361 CPLTag_t _tag = CPL_UNKNOWN;
1362 for(CPLTag_t i = CPL_ID; i <= CPL_SIGNATURE; i = CPLTag_t(i+1)) {
1363 if( node == names[i-1]) {
1364 _tag = i;
1365 switch (_tag) {
1366 /* case for parsing non terminal nodes */
1367 case CPL_REEL_LIST:
1368 if ( this->ParseReelList(node, type) )
1369 goto error;
1370 break;
1371 case CPL_CONTENT_VERSION:
1372 case CPL_SIGNER:
1373 case CPL_SIGNATURE:
1374 case CPL_RATING_LIST:
1375 if ( this->DummyParse(node,type) )
1376 goto error;
1377 break;
1378 /* Parse simple/end nodes */
1379 case CPL_ID:
1380 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1381 goto error;
1382 this->s_id = s_value;
1383 break;
1384 case CPL_ANNOTATION_TEXT:
1385 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1386 goto error;
1387 this->s_annotation = s_value;
1388 break;
1389 case CPL_ICON_ID:
1390 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1391 goto error;
1392 this->s_icon_id = s_value;
1393 break;
1394 case CPL_ISSUE_DATE:
1395 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1396 goto error;
1397 this->s_issue_date= s_value;
1398 break;
1399 case CPL_ISSUER:
1400 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1401 goto error;
1402 this->s_issuer = s_value;
1403 break;
1404 case CPL_CREATOR:
1405 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1406 goto error;
1407 this->s_creator = s_value;
1408 break;
1409 case CPL_CONTENT_TITLE:
1410 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1411 goto error;
1412 this->s_content_title = s_value;
1413 break;
1414 case CPL_CONTENT_KIND:
1415 if ( XmlFile::ReadEndNode( this->p_demux, p_xmlReader, node, type, s_value ) )
1416 goto error;
1417 this->s_content_kind = s_value;
1418 break;
1419 default:
1420 msg_Warn(this->p_demux, "Unknow CPL_TAG: %i", _tag );
1421 break;
1424 /* break the for loop as a tag is found*/
1425 break;
1428 if( _tag == CPL_UNKNOWN )
1429 goto error;
1430 break;
1432 case XML_READER_TEXT:
1433 case -1:
1434 goto error;
1435 case XML_READER_ENDELEM:
1436 if ( node != s_root_node) {
1437 msg_Err(this->p_demux,
1438 "Something goes wrong in CKL parsing (node %s)", node.c_str());
1439 goto error;
1441 break;
1445 /* TODO verify presence of mandatory fields*/
1447 /* Close CPL XML*/
1448 this->CloseXml();
1449 return 0;
1450 error:
1451 this->CloseXml();
1452 return -1;
1455 int CPL::ParseReelList(string p_node, int p_type) {
1456 string node;
1457 int type;
1459 if (p_type != XML_READER_STARTELEM)
1460 return -1;
1461 if( p_node != "ReelList")
1462 return -1;
1463 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1464 switch (type) {
1465 case XML_READER_STARTELEM: {
1466 Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
1467 if ( unlikely(p_reel == NULL) )
1468 return -1;
1469 if( node =="Reel") {
1470 if ( p_reel->Parse(node, type) ) {
1471 delete p_reel;
1472 return -1;
1474 } else {
1475 delete p_reel;
1476 return -1;
1478 this->vec_reel.push_back(p_reel);
1480 break;
1482 case XML_READER_TEXT:
1483 /* impossible */
1484 break;
1485 case XML_READER_ENDELEM:
1486 if ( node == p_node)
1487 return 0;
1488 break;
1491 return -1;
1495 int CPL::DummyParse(string p_node, int p_type)
1497 string node;
1498 int type;
1500 if (p_type != XML_READER_STARTELEM)
1501 return -1;
1503 if (xml_ReaderIsEmptyElement( this->p_xmlReader))
1504 return 0;
1506 while( ( type = XmlFile::ReadNextNode( this->p_demux, this->p_xmlReader, node ) ) > 0 ) {
1507 /* TODO not implemented. Just pase until end of input node */
1508 if ((node == p_node) && (type = XML_READER_ENDELEM))
1509 return 0;
1512 return -1;