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