DCP: new access-demux module for DigitalCinemaPackage
[vlc.git] / modules / access / dcp / dcpparser.cpp
1 /*****************************************************************************
2  * Copyright (C) 2013 VLC authors and VideoLAN
3  *
4  * Authors:
5  *          Nicolas Bertrand <nico@isf.cc>
6  *          Jean-Baptiste Kempf <jb@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /**
24  * @file dcpparser.cpp
25  * @brief Parsing of DCP XML files
26  */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 /* VLC core API headers */
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_xml.h>
35 #include <vlc_url.h>
36
37 #include <iostream>
38 #include <string>
39 #include <list>
40 #include <vector>
41
42 #include "dcpparser.h"
43
44 using namespace std;
45
46 static int ReadNextNode(xml_reader_t *p_xmlReader, string& p_node) {
47     const char * c_node;
48     int i;
49     i = xml_ReaderNextNode( p_xmlReader, &c_node );
50     p_node = c_node;
51     return i;
52 }
53
54 static int ReadEndNode( xml_reader_t *p_xmlReader, string p_node,
55                         int p_type, string &s_value) {
56     string node;
57
58     if ( xml_ReaderIsEmptyElement( p_xmlReader) )
59             return 0;
60
61     if (p_type != XML_READER_STARTELEM)
62         return -1;
63
64     if (ReadNextNode(p_xmlReader, node) == XML_READER_TEXT)
65     {
66         s_value = node;
67         if((ReadNextNode(p_xmlReader, node) == XML_READER_ENDELEM) &&
68                 node == p_node)
69             return 0;
70     }
71     return -1;
72 }
73
74 typedef enum {
75     CHUNK_UNKNOWN = 0,
76     CHUNK_PATH,
77     CHUNK_VOL_INDEX,
78     CHUNK_OFFSET,
79     CHUNK_LENGTH
80 } ChunkTag_t;
81
82
83 typedef enum {
84     ASSET_UNKNOWN = 0,
85     ASSET_ID,
86     ASSET_ANNOTATION_TEXT,
87     ASSET_PACKING_LIST,
88     ASSET_CHUNK_LIST,
89     ASSET_HASH,
90     ASSET_SIZE,
91     ASSET_TYPE,
92     ASSET_ORIGINAL_FILENAME
93 } AssetTag_t;
94
95 static const string g_asset_names[] = {
96     "Id",
97     "AnnotationText",
98     "PackingList",
99     "ChunkList",
100     "Hash",
101     "Size",
102     "Type",
103     "OriginalFileName"
104 };
105
106
107 typedef enum {
108     PKL_UNKNOWN = 0,
109     PKL_ID,
110     PKL_ISSUE_DATE,
111     PKL_ISSUER,
112     PKL_CREATOR,
113     PKL_ASSET_LIST,
114     PKL_ANNOTATION_TEXT, /* start of optional tags */
115     PKL_ICON_ID,
116     PKL_GROUP_ID,
117     PKL_SIGNER,
118     PKL_SIGNATURE,
119 } PKLTag_t;
120
121 typedef enum {
122     CPL_UNKNOWN = 0,
123     CPL_ID,
124     CPL_ANNOTATION_TEXT,        /* optional */
125     CPL_ICON_ID,                /* optional */
126     CPL_ISSUE_DATE,
127     CPL_ISSUER,                 /* optional */
128     CPL_CREATOR,                /* optional */
129     CPL_CONTENT_TITLE,
130     CPL_CONTENT_KIND,
131     CPL_CONTENT_VERSION,        /* not optional, but not always present*/
132     CPL_RATING_LIST,            /* not handled */
133     CPL_REEL_LIST,
134     CPL_SIGNER,                 /* optional - not handled */
135     CPL_SIGNATURE               /* optional - not handled */
136 } CPLTag_t;
137
138
139 class ChunkList: public std::list<Chunk> {
140 public :
141     ChunkList();
142     ~ChunkList();
143 };
144
145 /*
146  * Chunk Class
147  */
148 int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
149     string node;
150     int type;
151     string s_value;
152     static const string names[] = {"Path", "VolumeIndex", "Offset",
153                                "Length"};
154     if (p_type != XML_READER_STARTELEM)
155         return -1;
156     if( p_node != "Chunk")
157         return -1;
158     /* loop on Chunks Node */
159     while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
160         switch (type) {
161             case XML_READER_STARTELEM:
162             {
163                 ChunkTag_t chunk_tag = CHUNK_UNKNOWN;
164                 for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
165                     if( node == names[i-1]) {
166                         chunk_tag = i;
167                         if ( ReadEndNode(p_xmlReader, node, type, s_value))
168                             return -1;
169                         switch (chunk_tag) {
170                             case CHUNK_PATH:
171                                 this->s_path = s_value;
172                                 break;
173                             case CHUNK_VOL_INDEX:
174                                 this->i_vol_index = atoi(s_value.c_str());
175                                 break;
176                             case CHUNK_OFFSET:
177                                 this->i_offset = atoi(s_value.c_str());
178                                 break;
179                             case CHUNK_LENGTH:
180                                 this->i_length = atoi(s_value.c_str());
181                                 break;
182                             case CHUNK_UNKNOWN:
183                             default:
184                                 break;
185                         }
186                         /* break the for loop as a tag is found*/
187                         break;
188                     }
189                 }
190                 if(chunk_tag == CHUNK_UNKNOWN)
191                     return -1;
192                 break;
193             }
194             case XML_READER_TEXT:
195                 s_value = node;
196                 if (unlikely(node.empty()))
197                     return -1;
198                 break;
199             case XML_READER_ENDELEM:
200                 /* Verify if we reach Chuk endelem  */
201                 if ( node == p_node) {
202                     /* verify path */
203                     if ( this->s_path.empty() ) {
204                         msg_Err(this->p_demux, "Chunk::Parse No path found");
205                         return -1;
206                     }
207                     if ( this->i_vol_index != 1 ) {
208                         msg_Err(this->p_demux, "Only one VOLINDEX suported. Patch welcome.");
209                         return -1;
210                     }
211                     /* end of chunk tag parse */
212                     return 0;
213                 }
214                 break;
215         }
216     }
217     return -1;
218 }
219 /*
220  * AssetMap Class
221  */
222
223 AssetMap::~AssetMap() { }
224
225 int AssetMap::Parse ( )
226 {
227     int type = 0;
228     string node;
229
230     CPL  *cpl;
231     Reel *reel;
232     PKL  *pkl;
233     AssetList *_p_asset_list = NULL;
234
235     vector<string> pklfiles;
236
237     /* init XML parser */
238     if( this->OpenXml() ) {
239         msg_Err( p_demux, "Failed to initialize Assetmap XML parser" );
240         return -1;
241     }
242
243     /* reading ASSETMAP file to get the asset_list */
244     msg_Dbg( p_demux, "reading ASSETMAP file..." );
245     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
246         if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
247             _p_asset_list =  new (nothrow) AssetList();
248             if ( unlikely(_p_asset_list == NULL) ) {
249                 this->CloseXml();
250                 return -1;
251             }
252             p_dcp->p_asset_list = _p_asset_list;
253             if (this->ParseAssetList(p_xmlReader, node, type )) {
254                 this->CloseXml();
255                 return -1;
256             }
257             /* asset list found so break*/
258             break;
259         }
260     }
261
262
263     /* Look for PKLs path */
264     if ( (_p_asset_list == NULL) ||  (_p_asset_list->size() == 0) ) {
265         msg_Err( p_demux, "Asset list empty" );
266         this->CloseXml();
267         return -1;
268     }
269
270     for (AssetList::iterator iter = _p_asset_list->begin();
271             iter != _p_asset_list->end() ; ++iter) {
272         string s_filepath;
273         s_filepath = (*iter)->getPath();
274         if (s_filepath.empty()) {
275             msg_Err( p_demux, "No path element for asset" );
276             continue;
277         }
278         /* set an absolute file path */
279         s_filepath = p_dcp->path + s_filepath;
280
281         /* case of packing list */
282         if ((*iter)->isPackingList()) {
283             pklfiles.push_back( s_filepath );
284         }
285     }
286
287     /* TODO: case of only on PKL managed.
288      * Future work needed for managing severals
289      * the PKL vector will be used to select the required PKL  */
290     if( (pklfiles.size() == 0)  || (pklfiles[0].empty()) )
291     {
292         msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
293         this->CloseXml();
294         return -1;
295     }
296
297     /* Create the first PKL */
298     pkl = new (nothrow) PKL(p_demux, pklfiles[0], _p_asset_list, p_dcp->path);
299     if ( unlikely(pkl == NULL) ) {
300         this->CloseXml();
301         return -1;
302         }
303     if (pkl->Parse()) {
304         delete pkl;
305         this->CloseXml();
306         return -1;
307         }
308     p_dcp->pkls.push_back( pkl );
309
310     /* Now, CPL */
311     if ( pkl->FindCPLs() <= 0 ) {
312         msg_Err(p_demux, " No CPL found");
313         this->CloseXml();
314         return -1;
315     }
316     /* TODO: Only one CPL managed.
317      * Future work needed for managing severals
318      */
319
320     cpl = pkl->getCPL(0);
321     if( cpl == NULL ) {
322         msg_Err(p_demux, " No CPL found");
323         this->CloseXml();
324         return -1;
325     }
326     if ( cpl->Parse() ) {
327         this->CloseXml();
328         return -1;
329     }
330     /*TODO : case of 1st reel only managed */
331     reel = cpl->getReel(0);
332
333     Asset *asset;
334     /* Get picture */
335     asset = reel->getTrack(TRACK_PICTURE);
336     msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
337     msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
338     p_dcp->videofile = p_dcp->path + asset->getPath();
339     p_dcp->i_video_entry = asset->getEntryPoint();
340     /* Get audio */
341     asset = reel->getTrack(TRACK_SOUND);
342     msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
343     msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
344     p_dcp->audiofile = p_dcp->path + asset->getPath();
345     p_dcp->i_audio_entry = asset->getEntryPoint();
346
347     /* free memory */
348     this->CloseXml();
349     return VLC_SUCCESS;
350 }
351
352
353
354 /*
355  * Asset Class
356  */
357 Asset::~Asset() {}
358
359 int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
360 {
361     string node;
362     int type;
363     string s_value;
364     const string s_root_node = "Asset";
365
366     if (p_type != XML_READER_STARTELEM)
367         return -1;
368     if( p_node != s_root_node)
369         return -1;
370     /* loop on Assets Node */
371     while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
372         switch (type) {
373             case XML_READER_STARTELEM:
374                 {
375                     AssetTag_t _tag = ASSET_UNKNOWN;
376                     for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
377                         if( node == g_asset_names[i-1]) {
378                             _tag = i;
379                             switch(_tag) {
380                                 /* Case of complex nodes */
381                                 case ASSET_PACKING_LIST:
382                                     /* case of <PackinkList/> tag, bur not compliant with SMPTE-429-9 2007*/
383                                     if (xml_ReaderIsEmptyElement( p_xmlReader))
384                                         this->b_is_packing_list = true;
385                                     else if ( ReadEndNode(p_xmlReader, node, type, s_value) )
386                                         return -1;
387                                     if ( s_value == "true" )
388                                         this->b_is_packing_list = true;
389                                     break;
390                                 case ASSET_CHUNK_LIST:
391                                     if ( this->parseChunkList(p_xmlReader, node, type ) )
392                                         return -1;
393                                     this->s_path = this->chunk_vec[0].getPath();
394                                     break;
395                                 case ASSET_ID:
396                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
397                                         return -1;
398                                     this->s_id = s_value;
399                                     break;
400                                 case ASSET_ANNOTATION_TEXT:
401                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
402                                         return -1;
403                                     this->s_annotation = s_value;
404                                     break;
405                                 case ASSET_ORIGINAL_FILENAME:
406                                 case ASSET_HASH:
407                                 case ASSET_TYPE:
408                                 case ASSET_SIZE:
409                                     /* Asset tags not in AssetMap */
410                                     break;
411                             }
412                             /* break the for loop as a tag is found*/
413                             break;
414                         }
415                     }
416                     if( _tag == ASSET_UNKNOWN )
417                         return -1;
418                     break;
419                 }
420             case XML_READER_TEXT:
421                 return -1;
422             case XML_READER_ENDELEM:
423                 if ( node != s_root_node) {
424                     msg_Err(this->p_demux,
425                             "Something goes wrong in Asset parsing on Assetmap (node %s)", node.c_str());
426                     return -1;
427                 } else {
428                     /*Check Presence of Id and Chunklist */
429                     if ( this->s_id.empty() ) {
430                         msg_Err(this->p_demux, " No Id element found in Asset");
431                         return -1;
432                     }
433                     if ( this->s_path.empty() ) {
434                         msg_Err(this->p_demux, " No path element found in Asset");
435                         return -1;
436                     }
437                     return 0;
438                 }
439                 break;
440         }
441     }
442     return -1;
443 }
444
445
446
447 int Asset::ParsePKL( xml_reader_t *p_xmlReader)
448 {
449     string node;
450     int type;
451     string s_value;
452     const string s_root_node = "Asset";
453
454     while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
455         switch (type) {
456             case XML_READER_STARTELEM:
457                 {
458                     AssetTag_t _tag = ASSET_UNKNOWN;
459                     for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
460                         if( node == g_asset_names[i-1]) {
461                             _tag = i;
462                             switch(_tag) {
463                                 case ASSET_ANNOTATION_TEXT:
464                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
465                                         return -1;
466                                     if ( this->s_annotation.empty() )
467                                         this->s_annotation = s_value;
468                                     else
469                                         this->s_annotation = this->s_annotation + "--" + s_value;
470                                     break;
471                                 case ASSET_HASH:
472                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
473                                         return -1;
474                                     this->s_hash = s_value;
475                                     break;
476                                 case ASSET_SIZE:
477                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
478                                         return -1;
479                                     this->ui_size = atol(s_value.c_str());
480                                     break;
481                                 case ASSET_TYPE:
482                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
483                                         return -1;
484                                     this->s_type = s_value;
485                                     break;
486                                 case ASSET_ORIGINAL_FILENAME:
487                                     if ( ReadEndNode(p_xmlReader, node, type, s_value) )
488                                         return -1;
489                                     this->s_original_filename = s_value;
490                                     break;
491                                 case ASSET_ID: /* already verified */
492                                 case ASSET_PACKING_LIST:
493                                 case ASSET_CHUNK_LIST:
494                                     /* Asset tags not in PKL */
495                                     break;
496                             }
497                             /* break the for loop as a tag is found*/
498                             break;
499                         }
500                     }
501                     if( _tag == ASSET_UNKNOWN )
502                         return -1;
503                 break;
504             }
505             case XML_READER_TEXT:
506                 return -1;
507             case XML_READER_ENDELEM:
508                 if ( node != s_root_node) {
509                     msg_Err(this->p_demux,
510                             "Something goes wrong in Asset parsing on PKL (node %s)", node.c_str());
511                     return -1;
512                 } else {
513                     /* Verify that mandatory attributes are filled */
514                     if (this->s_hash.empty()) {
515                         msg_Err(this->p_demux,"Asset Hash tag invalid");
516                         return -1;
517                     }
518                     if (this->ui_size == 0) {
519                         msg_Err(this->p_demux,"Asset Size tag invalid");
520                         return -1;
521                     }
522                     if (this->s_type.empty()) {
523                         msg_Err(this->p_demux,"Asset Type tag invalid");
524                         return -1;
525                     }
526                     return 0;
527                 }
528                 break;
529         }
530
531     }
532
533     return -1;
534 }
535
536 void Asset::Dump()
537 {
538     msg_Dbg(this->p_demux,"Id              = %s", this->s_id.c_str());
539     msg_Dbg(this->p_demux,"Path            = %s", this->s_path.c_str());
540     msg_Dbg(this->p_demux,"Is PKL          = %s", this->b_is_packing_list ? "True" : "False");
541     msg_Dbg(this->p_demux,"Hash            = %s", this->s_hash.c_str());
542     msg_Dbg(this->p_demux,"Size            = %i", this->ui_size);
543     msg_Dbg(this->p_demux,"Type            = %s", this->s_type.c_str());
544     msg_Dbg(this->p_demux,"OrignalFileName = %s", this->s_original_filename.c_str());
545     msg_Dbg(this->p_demux,"AnnotationText  = %s", this->s_annotation.c_str());
546 }
547
548 int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
549 {
550     string node;
551     int type;
552     string s_value;
553     std::vector<Chunk> chunk_vec;
554
555     if (p_type != XML_READER_STARTELEM)
556         return -1;
557     if( p_node != "ChunkList" )
558         return -1;
559     /* loop on Assets Node */
560     while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
561          switch (type) {
562             case XML_READER_STARTELEM:
563                 {
564                     Chunk chunk(this->p_demux);
565                     if (node != "Chunk" )
566                         return -1;
567                     if ( chunk.Parse(p_xmlReader, node, type) )
568                         return -1;
569                     chunk_vec.push_back(chunk);
570                     break;
571                 }
572             case XML_READER_ENDELEM:
573                 if ( node == p_node) {
574                     if (chunk_vec.size() != 1 ) {
575                         msg_Err(this->p_demux, "chunklist of size greater than one not supported");
576                         return -1;
577                         }
578                     this->chunk_vec = chunk_vec;
579                     return 0;
580                 }
581                 break;
582         }
583     }
584     return -1;
585 }
586
587 int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
588 {
589     string node;
590     int type;
591     Asset *asset;
592
593     if (p_type != XML_READER_STARTELEM)
594         return -1;
595     if( p_node != "AssetList" )
596         return -1;
597     /* loop on AssetList nodes */
598     while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) {
599         switch (type) {
600             case XML_READER_STARTELEM:
601                 if (node != "Asset" )
602                     return -1;
603                 asset = new (nothrow) Asset(this->p_demux);
604                 if ( unlikely(asset == NULL) )
605                     return -1;
606                 if (asset->Parse(p_xmlReader, node, type)){
607                     delete asset;
608                     return -1;
609                 }
610                 p_dcp->p_asset_list->push_back(asset);
611                 break;
612
613             case XML_READER_ENDELEM:
614                 if (node == p_node )
615                     return 0;
616                 break;
617             default:
618             case XML_READER_TEXT:
619                 msg_Err(this->p_demux, "Error parsing AssetList in AssetMap");
620                 return -1;
621         }
622     }
623     return -1;
624 }
625
626 Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
627 {
628     AssetList::iterator index = asset_list->begin() ;
629     for (index = asset_list->begin(); index != asset_list->end(); ++index)
630         if ((*index)->getId() == p_id )
631             return *index;
632     return NULL;
633 }
634
635 /*
636  * XmlFile Class
637  */
638 XmlFile::~XmlFile() {}
639
640 int XmlFile::OpenXml()
641 {
642     char *psz_uri;
643
644     this->p_xml = xml_Create( this->p_demux );
645     if (! this->p_xml) {
646         return -1;
647     }
648     psz_uri = vlc_path2uri( this->s_path.c_str(), "file" );
649     this->p_stream = stream_UrlNew(this->p_demux, psz_uri );
650     free(psz_uri);
651     if( ! this->p_stream ) {
652         xml_Delete(this->p_xml );
653         return -1;
654     }
655
656     this->p_xmlReader = xml_ReaderCreate( this->p_xml, this->p_stream);
657     if( ! this->p_xmlReader ) {
658         stream_Delete( this->p_stream );
659         xml_Delete(this->p_xml );
660         return -1;
661     }
662     return 0;
663 }
664
665 void XmlFile::CloseXml() {
666     if( this->p_stream )
667         stream_Delete( this->p_stream );
668     if( this->p_xmlReader )
669         xml_ReaderDelete( this->p_xmlReader );
670     if( this->p_xml )
671         xml_Delete( this->p_xml );
672
673 }
674
675 /*
676  * PKL Class
677  */
678
679 PKL::PKL(demux_t * p_demux, string s_path, AssetList *_asset_list, string s):
680     XmlFile(p_demux, s_path),
681     asset_list(_asset_list), s_dcp_path(s)
682 {
683     type = XML_PKL;
684 }
685
686 PKL::~PKL() {
687     vlc_delete_all(vec_cpl);
688 }
689
690 int PKL::Parse()
691 {
692     string node;
693     int type;
694     string s_value;
695     const string s_root_node = "PackingList";
696
697     static const string names[] = {
698         "Id",
699         "IssueDate",
700         "Issuer",
701         "Creator",
702         "AssetList",
703         "AnnotationText",
704         "IconId",
705         "GroupId",
706         "Signer",
707         "ds:Signature"
708     };
709
710     if (this->OpenXml())
711         return -1;
712
713     /* read 1st node  and verify that is a PKL*/
714     if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) &&
715                 (node == s_root_node) ) ) {
716         msg_Err( this->p_demux, "Not a valid XML Packing List");
717         goto error;
718     }
719     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
720         switch (type) {
721             case XML_READER_STARTELEM: {
722                 PKLTag_t _tag = PKL_UNKNOWN;
723                 for(PKLTag_t i = PKL_ID; i <= PKL_SIGNATURE; i = PKLTag_t(i+1)) {
724                     if( node == names[i-1]) {
725                         _tag = i;
726                         switch (_tag) {
727                             /* case for parsing non terminal nodes */
728                             case PKL_ASSET_LIST:
729                                 if ( this->ParseAssetList(node, type) )
730                                     goto error;
731                                 break;
732                             case PKL_SIGNER:
733                                 if ( this->ParseSigner(node, type) )
734                                     goto error;
735                                 break;
736                             case PKL_SIGNATURE:
737                                 if ( this->ParseSignature(node, type) )
738                                     goto error;
739                                 break;
740                             /* Parse simple/end nodes */
741                             case PKL_ID:
742                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
743                                     goto error;
744                                 this->s_id = s_value;
745                                 break;
746                             case PKL_ISSUE_DATE:
747                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
748                                     goto error;
749                                 this->s_issue_date = s_value;
750                                 break;
751                             case PKL_ISSUER:
752                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
753                                     goto error;
754                                 this->s_issuer = s_value;
755                                 break;
756                             case PKL_CREATOR:
757                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
758                                     goto error;
759                                 this->s_creator = s_value;
760                                 break;
761                             case PKL_ANNOTATION_TEXT:
762                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
763                                     goto error;
764                                 this->s_annotation = s_value;
765                                 break;
766                             case PKL_ICON_ID:
767                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
768                                     goto error;
769                                 this->s_icon_id = s_value;
770                                 break;
771                             case PKL_GROUP_ID:
772                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
773                                     goto error;
774                                 this->s_group_id = s_value;
775                                 break;
776                         }
777                         /* break the for loop as a tag is found*/
778                         break;
779                     }
780                 }
781                 if( _tag == PKL_UNKNOWN )
782                     goto error;
783                 break;
784             }
785             case XML_READER_TEXT:
786                 goto error;
787             case XML_READER_ENDELEM:
788                 if ( node != s_root_node) {
789                     msg_Err(this->p_demux,
790                         "Something goes wrong in PKL parsing (node %s)", node.c_str());
791                     goto error;
792                 }
793                 break;
794         }
795     }
796     /* TODO verify presence of mandatory fields*/
797
798     /* Close PKL XML*/
799     this->CloseXml();
800     return 0;
801 error:
802     msg_Err( this->p_demux, "PKL parsing failed");
803     this->CloseXml();
804     return -1;
805 }
806
807 int PKL::FindCPLs()
808 {
809     if ( this->vec_cpl.size() != 0 ) {
810         msg_Err(this->p_demux, "CPLs already checked");
811         return -1;
812     }
813
814     for (AssetList::iterator index = this->asset_list->begin();
815             index != this->asset_list->end(); ++index) {
816         Asset *asset = *index;
817         if ( asset->getType().find("text/xml") == string::npos) {
818             /* not an xml file */
819             continue;
820         }
821
822         CPL *cpl = new (nothrow) CPL(this->p_demux,
823                       this->s_dcp_path + asset->getPath(),
824                       this->asset_list);
825         if ( unlikely(cpl == NULL) )
826                     return -1;
827         if ( cpl->IsCPL() )
828             /* CPL Found */
829             this->vec_cpl.push_back(cpl);
830         else
831             delete cpl;
832     }
833     return this->vec_cpl.size();
834 }
835
836
837 int PKL::ParseAssetList(string p_node, int p_type) {
838     string node;
839     int type;
840
841     if (p_type != XML_READER_STARTELEM)
842         return -1;
843     if( p_node != "AssetList")
844         return -1;
845     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
846         switch (type) {
847             case XML_READER_STARTELEM:
848                 if( node =="Asset") {
849                     if ( this->ParseAsset(node, type) )
850                         return -1;
851                 } else
852                     return -1;
853                 break;
854             case XML_READER_ENDELEM:
855                 if ( node == p_node) {
856                     /* parse of chunklist finished */
857                     goto end;
858                 }
859                 break;
860             }
861     }
862 end:
863     return 0;
864 }
865
866 int PKL::ParseAsset(string p_node, int p_type) {
867     string node;
868     int type;
869     string s_value;
870     Asset *asset = NULL;
871
872     if (p_type != XML_READER_STARTELEM)
873         return -1;
874     if( p_node != "Asset")
875         return -1;
876
877     /* 1st node shall be Id" */
878     if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
879         if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
880             return -1;
881     if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
882          if (type == XML_READER_TEXT) {
883             s_value = node;
884             if (unlikely(node.empty()))
885                 return -1;
886             }
887     if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
888         if (type == XML_READER_ENDELEM) {
889             asset = AssetMap::getAssetById(this->asset_list, s_value);
890             if (asset  == NULL)
891                 return -1;
892         }
893      if (asset == NULL)
894         return -1;
895      if ( asset->ParsePKL(this->p_xmlReader) )
896         return -1;
897     return 0;
898 }
899
900 int PKL::ParseSigner(string p_node, int p_type)
901 {
902     string node;
903     int type;
904
905     if (p_type != XML_READER_STARTELEM)
906         return -1;
907     if( p_node != "Signer")
908         return -1;
909
910     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
911         /* TODO not implemented. Just parse until end of Signer node */
912             if ((node == p_node) && (type = XML_READER_ENDELEM))
913                 return 0;
914     }
915
916     msg_Err(this->p_demux, "Parse of Signer finished bad");
917     return -1;
918 }
919
920 int PKL::ParseSignature(string p_node, int p_type)
921 {
922     string node;
923     int type;
924
925     if (p_type != XML_READER_STARTELEM)
926         return -1;
927     if( p_node != "ds:Signature")
928         return -1;
929
930     while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
931         /* TODO not implemented. Just parse until end of Signature node */
932             if ((node == p_node) && (type = XML_READER_ENDELEM))
933                 return 0;
934     }
935     msg_Err(this->p_demux, "Parse of Signature finished bad");
936     return -1;
937 }
938
939 /*
940  * Reel Class
941  */
942 int Reel::Parse(string p_node, int p_type) {
943     string node;
944     int type;
945     string s_value;
946
947     if (p_type != XML_READER_STARTELEM)
948         return -1;
949     if( p_node != "Reel")
950         return -1;
951
952     while (( type = ReadNextNode(this->p_xmlReader, node ) ) > 0 ) {
953         switch (type) {
954             case XML_READER_STARTELEM:
955                 if (node =="Id") {
956                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
957                         return -1;
958                     this->s_id = s_value;
959                 } else if (node == "AnnotationText") {
960                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
961                         return -1;
962                     this->s_annotation = s_value;
963                 } else if ( node =="AssetList" ) {
964                     if (this->ParseAssetList(node, type))
965                         return -1;
966                 } else {
967                     /* unknown tag */
968                     msg_Err(this->p_demux, "Reel::Parse, unknown tag:%s", node.c_str());
969                     return -1;
970                 }
971                 break;
972             case XML_READER_TEXT:
973                 /* Error */
974                 msg_Err(this->p_demux, "Reel parsing error");
975                 return -1;
976             case XML_READER_ENDELEM:
977                 /* verify correctness of end node */
978                 if ( node == p_node) {
979                     /* TODO : verify Reel id */
980                     return 0;
981                 }
982                 break;
983         }
984     }
985     return -1;
986 }
987
988
989 Asset * Reel::getTrack(TrackType_t e_track)
990 {
991     switch (e_track) {
992         case TRACK_PICTURE:
993             return this->p_picture_track;
994         case TRACK_SOUND:
995             return this->p_sound_track;
996         case TRACK_SUBTITLE:
997             return this->p_subtitle_track;
998         case TRACK_UNKNOWN:
999         default:
1000             break;
1001     }
1002     return NULL;
1003 }
1004
1005 int Reel::ParseAssetList(string p_node, int p_type) {
1006     string node;
1007     int type;
1008     string s_value;
1009
1010     if (p_type != XML_READER_STARTELEM)
1011         return -1;
1012     if( p_node != "AssetList")
1013         return -1;
1014
1015     while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 )  {
1016         switch (type) {
1017             case XML_READER_STARTELEM:
1018                 if (node =="MainPicture") {
1019                     if ( this->ParseAsset(node, type, TRACK_PICTURE) )
1020                         return -1;
1021                 } else if (node =="MainSound") {
1022                     if ( this->ParseAsset(node, type, TRACK_SOUND) )
1023                         return -1;
1024                 } else if (node =="MainSubtitle") {
1025                     if ( this->ParseAsset(node, type, TRACK_SUBTITLE) )
1026                         return -1;
1027                 } else {
1028                     /* unknown tag */
1029                     msg_Err(this->p_demux, "Reel::ParseAssetList, unknown tag:%s", node.c_str());
1030                     return -1;
1031                 }
1032                 break;
1033             case XML_READER_TEXT:
1034                 /* Parsing error */
1035                 msg_Err(this->p_demux, "AssetList parsing error");
1036                 return -1;
1037             case XML_READER_ENDELEM:
1038                 /* verify correctness of end node */
1039                 if ( node == p_node) {
1040                     /* TODO : verify id */
1041                     return 0;
1042                 }
1043                 break;
1044         }
1045     }
1046     return -1;
1047 }
1048
1049 int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
1050     string node;
1051     int type;
1052     string s_value;
1053     bool b_stop_parse = false;
1054     Asset *asset = NULL;
1055
1056     if (p_type != XML_READER_STARTELEM)
1057         return -1;
1058
1059     /* 1st node shall be Id */
1060     if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0)
1061         if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
1062             return -1;
1063
1064     if ( ReadEndNode(this->p_xmlReader, node, type, s_value) )
1065         return -1;
1066
1067     asset = AssetMap::getAssetById(this->p_asset_list, s_value);
1068     if (asset  == NULL)
1069         return -1;
1070
1071     while(  (! b_stop_parse) &&
1072             (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) ) {
1073         switch (type) {
1074             case XML_READER_STARTELEM:
1075                 if (node =="EditRate") {
1076                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1077                         return -1;
1078                 } else if (node == "AnnotationText") {
1079                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1080                         return -1;
1081                         asset->setAnnotation(s_value);
1082                 } else if (node == "IntrinsicDuration") {
1083                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1084                         return -1;
1085                         asset->setIntrinsicDuration(atoi(s_value.c_str()));
1086                 } else if (node == "EntryPoint") {
1087                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1088                         return -1;
1089                         asset->setEntryPoint(atoi(s_value.c_str()));
1090                 } else if (node == "Duration") {
1091                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1092                         return -1;
1093                         asset->setDuration(atoi(s_value.c_str()));
1094                 } else if (node == "KeyId") {
1095                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1096                         return -1;
1097                 } else if (node == "Hash") {
1098                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1099                         return -1;
1100                 } else if (node == "FrameRate") {
1101                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1102                         return -1;
1103                 } else if (node == "ScreenAspectRatio") {
1104                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1105                         return -1;
1106                 } else if (node == "Language") {
1107                     if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
1108                         return -1;
1109                 } else {
1110                     /* unknown tag */
1111                     msg_Err(this->p_demux, "Reel::ParseAsset unknown tag:%s", node.c_str());
1112                     return -1;
1113                 }
1114                 break;
1115             case XML_READER_TEXT:
1116                 /* impossible */
1117                 return -1;
1118                 break;
1119             case XML_READER_ENDELEM:
1120                 /* verify correctness of end node */
1121                 if ( node == p_node) {
1122                     /* TODO : verify id */
1123                     b_stop_parse = true;
1124                 }
1125         }
1126     }
1127     /* store by track */
1128     switch (e_track) {
1129         case TRACK_PICTURE:
1130             this->p_picture_track = asset;
1131             break;
1132         case TRACK_SOUND:
1133             this->p_sound_track = asset;
1134             break;
1135         case TRACK_SUBTITLE:
1136             this->p_subtitle_track = asset;
1137             break;
1138         case TRACK_UNKNOWN:
1139         default:
1140             break;
1141     }
1142     return 0;
1143 }
1144
1145 /*
1146  * CPL Class
1147  */
1148
1149 CPL::CPL(demux_t * p_demux, string s_path, AssetList *_asset_list)
1150     : XmlFile(p_demux, s_path), asset_list( _asset_list)
1151 {
1152     string node;
1153     int type;
1154
1155     this->type = XML_UNKNOWN;
1156
1157     if (this->OpenXml()) {
1158         msg_Err(this->p_demux, "Failed to open CPL XML file");
1159         return;
1160         }
1161
1162     /* read 1st node  and verify that is a CPL */
1163     if ( (type = ReadNextNode(p_xmlReader, node)) > 0) {
1164         if ( (type == XML_READER_STARTELEM) && (node == "CompositionPlaylist") ) {
1165             this->type = XML_CPL;
1166         }
1167     }
1168     /* close xml */
1169     this->CloseXml();
1170 };
1171
1172 CPL::~CPL() {
1173     vlc_delete_all(vec_reel);
1174 }
1175
1176 int CPL::Parse()
1177 {
1178     string node;
1179     int type;
1180     string s_value;
1181     const string s_root_node = "CompositionPlaylist";
1182
1183     static const string names[] = {
1184         "Id",
1185         "AnnotationText",
1186         "IconId",
1187         "IssueDate",
1188         "Issuer",
1189         "Creator",
1190         "ContentTitleText",
1191         "ContentKind",
1192         "ContentVersion",
1193         "RatingList",
1194         "ReelList",
1195         "Signer",
1196         "ds:Signature"
1197     };
1198
1199     if (this->OpenXml())
1200         return -1;
1201
1202     /* read 1st node  and verify that is a CPL*/
1203     if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) &&
1204                 (node == s_root_node) ) ) {
1205         msg_Err( this->p_demux, "Not a valid XML Packing List");
1206         goto error;
1207     }
1208
1209     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1210         switch (type) {
1211             case XML_READER_STARTELEM: {
1212                 CPLTag_t _tag = CPL_UNKNOWN;
1213                 for(CPLTag_t i = CPL_ID; i <= CPL_SIGNATURE; i = CPLTag_t(i+1)) {
1214                     if( node == names[i-1]) {
1215                         _tag = i;
1216                         switch (_tag) {
1217                             /* case for parsing non terminal nodes */
1218                             case CPL_REEL_LIST:
1219                                 if ( this->ParseReelList(node, type) )
1220                                     goto error;
1221                                 break;
1222                             case CPL_CONTENT_VERSION:
1223                             case CPL_SIGNER:
1224                             case CPL_SIGNATURE:
1225                             case CPL_RATING_LIST:
1226                                 if ( this->DummyParse(node,type) )
1227                                     goto error;
1228                                 break;
1229                                 /* Parse simple/end nodes */
1230                             case CPL_ID:
1231                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1232                                     goto error;
1233                                 this->s_id = s_value;
1234                                 break;
1235                             case CPL_ANNOTATION_TEXT:
1236                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1237                                     goto error;
1238                                 this->s_annotation = s_value;
1239                                 break;
1240                             case CPL_ICON_ID:
1241                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1242                                     goto error;
1243                                 this->s_icon_id = s_value;
1244                                 break;
1245                             case CPL_ISSUE_DATE:
1246                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1247                                     goto error;
1248                                 this->s_issue_date= s_value;
1249                                 break;
1250                             case CPL_ISSUER:
1251                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1252                                     goto error;
1253                                 this->s_issuer = s_value;
1254                                 break;
1255                             case CPL_CREATOR:
1256                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1257                                     goto error;
1258                                 this->s_creator = s_value;
1259                                 break;
1260                             case CPL_CONTENT_TITLE:
1261                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1262                                     goto error;
1263                                 this->s_content_title = s_value;
1264                                 break;
1265                             case CPL_CONTENT_KIND:
1266                                 if ( ReadEndNode(p_xmlReader, node, type, s_value) )
1267                                     goto error;
1268                                 this->s_content_kind = s_value;
1269                                 break;
1270                         }
1271
1272                         /* break the for loop as a tag is found*/
1273                         break;
1274                     }
1275                 }
1276                 if( _tag == CPL_UNKNOWN )
1277                     goto error;
1278                 break;
1279             }
1280             case XML_READER_TEXT:
1281                goto error;
1282             case XML_READER_ENDELEM:
1283                if ( node != s_root_node) {
1284                    msg_Err(this->p_demux,
1285                            "Something goes wrong in CKL parsing (node %s)", node.c_str());
1286                    goto error;
1287                }
1288                break;
1289         }
1290     }
1291
1292     /* TODO verify presence of mandatory fields*/
1293
1294     /* Close CPL XML*/
1295     this->CloseXml();
1296     return 0;
1297 error:
1298     this->CloseXml();
1299     return -1;
1300 }
1301
1302 int CPL::ParseReelList(string p_node, int p_type) {
1303     string node;
1304     int type;
1305
1306     if (p_type != XML_READER_STARTELEM)
1307         return -1;
1308     if( p_node != "ReelList")
1309         return -1;
1310     while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1311         switch (type) {
1312             case XML_READER_STARTELEM: {
1313                 Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
1314                 if ( unlikely(p_reel == NULL) )
1315                     return -1;
1316                 if( node =="Reel") {
1317                     if ( p_reel->Parse(node, type) ) {
1318                         delete p_reel;
1319                         return -1;
1320                     }
1321                 } else {
1322                     delete p_reel;
1323                     return -1;
1324                 }
1325                 this->vec_reel.push_back(p_reel);
1326
1327                 break;
1328             }
1329             case XML_READER_TEXT:
1330                 /* impossible */
1331                 break;
1332             case XML_READER_ENDELEM:
1333                 if ( node == p_node)
1334                     return 0;
1335                 break;
1336             }
1337     }
1338     return -1;
1339 }
1340
1341
1342 int CPL::DummyParse(string p_node, int p_type)
1343 {
1344     string node;
1345     int type;
1346
1347     if (p_type != XML_READER_STARTELEM)
1348         return -1;
1349
1350     if (xml_ReaderIsEmptyElement( this->p_xmlReader))
1351         return 0;
1352
1353     while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1354         /* TODO not implemented. Just pase until end of input node */
1355         if ((node == p_node) && (type = XML_READER_ENDELEM))
1356             return 0;
1357     }
1358
1359     return -1;
1360 }