2009-11-12 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / playlist.cpp
blob247141498c08b2f0f9f360f482c93e25c331f647
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * playlist.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
14 #include <expat.h>
15 #include <string.h>
16 #include <math.h>
17 #include <glib.h>
19 #include "playlist.h"
20 #include "clock.h"
21 #include "mediaelement.h"
22 #include "debug.h"
23 #include "media.h"
24 #include "mediaplayer.h"
27 * PlaylistParserInternal
30 PlaylistParserInternal::PlaylistParserInternal ()
32 parser = XML_ParserCreate (NULL);
33 bytes_read = 0;
34 reparse = false;
37 PlaylistParserInternal::~PlaylistParserInternal ()
39 XML_ParserFree (parser);
40 parser = NULL;
44 * PlaylistNode
47 PlaylistNode::PlaylistNode (PlaylistEntry *entry) : List::Node ()
49 if (entry)
50 entry->ref ();
51 this->entry = entry;
54 PlaylistNode::~PlaylistNode ()
56 if (entry) {
57 entry->unref ();
58 entry = NULL;
63 * PlaylistEntry
66 PlaylistEntry::PlaylistEntry (Playlist *parent)
67 : EventObject (Type::PLAYLISTENTRY, false)
69 LOG_PLAYLIST ("PlaylistEntry::PlaylistEntry (%p)\n", parent);
71 Init (parent);
72 g_return_if_fail (parent != NULL); // should ge a g_warn..., but glib 2.10 doesn't have g_warn.
75 PlaylistEntry::PlaylistEntry (Type::Kind kind, Playlist *parent)
76 : EventObject (kind, false)
78 LOG_PLAYLIST ("PlaylistEntry::PlaylistEntry (%p)\n", parent);
80 Init (parent);
81 g_return_if_fail (parent != NULL); // should ge a g_warn..., but glib 2.10 doesn't have g_warn.
85 PlaylistEntry::PlaylistEntry (Type::Kind kind)
86 : EventObject (kind, false)
88 LOG_PLAYLIST ("PlaylistEntry::PlaylistEntry ()\n");
90 Init (NULL);
93 void
94 PlaylistEntry::Dispose ()
96 LOG_PLAYLIST ("PlaylistEntry::Dispose () id: %i media: %i\n", GET_OBJ_ID (this), GET_OBJ_ID (media));
98 if (media) {
99 Media *tmp = media;
100 media = NULL;
101 tmp->RemoveSafeHandlers (this);
102 tmp->DisposeObject (tmp);
103 tmp->unref ();
106 delete source_name;
107 source_name = NULL;
108 g_free (full_source_name);
109 full_source_name = NULL;
111 delete base;
112 base = NULL;
113 g_free (title);
114 title = NULL;
115 g_free (author);
116 author = NULL;
117 g_free (abstract);
118 abstract = NULL;
119 g_free (copyright);
120 copyright = NULL;
121 g_free (info_target);
122 info_target = NULL;
123 g_free (info_url);
124 info_url = NULL;
126 parent = NULL;
127 if (params != NULL) {
128 g_hash_table_destroy (params);
129 params = NULL;
132 EventObject::Dispose ();
135 void
136 PlaylistEntry::Init (Playlist *parent)
138 // Parent might be null
139 this->parent = parent;
140 this->media = NULL;
141 source_name = NULL;
142 full_source_name = NULL;
143 start_time = 0;
144 duration = NULL;
145 play_when_available = false;
146 base = NULL;
147 title = NULL;
148 author = NULL;
149 abstract = NULL;
150 copyright = NULL;
151 info_target = NULL;
152 info_url = NULL;
153 client_skip = true;
154 is_live = false;
155 set_values = (PlaylistKind::Kind) 0;
156 opened = false;
157 params = NULL;
160 void
161 PlaylistEntry::Initialize (Media *media)
163 g_return_if_fail (media != NULL);
164 g_return_if_fail (this->media == NULL);
166 media->AddSafeHandler (Media::OpenCompletedEvent, OpenCompletedCallback, this);
167 media->AddSafeHandler (Media::OpeningEvent, OpeningCallback, this);
168 media->AddSafeHandler (Media::SeekingEvent, SeekingCallback, this);
169 media->AddSafeHandler (Media::SeekCompletedEvent, SeekCompletedCallback, this);
170 media->AddSafeHandler (Media::CurrentStateChangedEvent, CurrentStateChangedCallback, this);
171 media->AddSafeHandler (Media::DownloadProgressChangedEvent, DownloadProgressChangedCallback, this);
172 media->AddSafeHandler (Media::BufferingProgressChangedEvent, BufferingProgressChangedCallback, this);
173 media->AddSafeHandler (Media::MediaErrorEvent, MediaErrorCallback, this);
175 this->media = media;
176 this->media->ref ();
179 void
180 PlaylistEntry::InitializeWithStream (ManagedStreamCallbacks *callbacks)
182 Media *media;
183 ManagedStreamSource *source;
184 PlaylistRoot *root = GetRoot ();
186 g_return_if_fail (callbacks != NULL);
187 g_return_if_fail (root != NULL);
189 media = new Media (root);
190 Initialize (media);
192 source = new ManagedStreamSource (media, callbacks);
193 media->Initialize (source);
194 if (!media->HasReportedError ())
195 media->OpenAsync ();
196 media->unref ();
197 source->unref ();
200 void
201 PlaylistEntry::InitializeWithSource (IMediaSource *source)
203 Media *media;
204 PlaylistRoot *root = GetRoot ();
206 g_return_if_fail (source != NULL);
207 g_return_if_fail (root != NULL);
209 media = source->GetMediaReffed ();
211 g_return_if_fail (media != NULL);
213 Initialize (media);
215 media->Initialize (source);
216 if (!media->HasReportedError ())
217 media->OpenAsync ();
218 media->unref ();
221 void
222 PlaylistEntry::InitializeWithUri (const char *uri)
224 Media *media;
225 PlaylistRoot *root = GetRoot ();
227 g_return_if_fail (uri != NULL);
228 g_return_if_fail (root != NULL);
230 media = new Media (root);
231 Initialize (media);
232 media->Initialize (uri);
233 if (!media->HasReportedError ())
234 media->OpenAsync ();
235 media->unref ();
238 void
239 PlaylistEntry::InitializeWithDownloader (Downloader *dl, const char *PartName)
241 Media *media;
242 PlaylistRoot *root = GetRoot ();
244 g_return_if_fail (dl != NULL);
245 g_return_if_fail (root != NULL);
247 media = new Media (root);
248 Initialize (media);
249 media->Initialize (dl, PartName);
250 if (!media->HasReportedError ())
251 media->OpenAsync ();
252 media->unref ();
255 void
256 PlaylistEntry::InitializeWithDemuxer (IMediaDemuxer *demuxer)
258 Media *media;
259 PlaylistRoot *root = GetRoot ();
261 g_return_if_fail (demuxer != NULL);
262 g_return_if_fail (root != NULL);
264 media = demuxer->GetMediaReffed ();
266 g_return_if_fail (media != NULL);
268 Initialize (media);
269 media->Initialize (demuxer);
270 if (!media->HasReportedError ())
271 media->OpenAsync ();
272 media->unref ();
275 void
276 PlaylistEntry::OpeningHandler (Media *media, EventArgs *args)
278 PlaylistRoot *root = GetRoot ();
280 LOG_PLAYLIST ("PlaylistEntry::OpeningHandler (%p, %p)\n", media, args);
282 g_return_if_fail (root != NULL);
284 root->Emit (PlaylistRoot::OpeningEvent, args);
287 void
288 PlaylistEntry::OpenMediaPlayer ()
290 PlaylistRoot *root = GetRoot ();
291 MediaPlayer *mplayer;
293 g_return_if_fail (opened == true);
294 g_return_if_fail (root != NULL);
296 mplayer = GetMediaPlayer ();
297 g_return_if_fail (mplayer != NULL);
299 mplayer->Open (media, this);
301 root->Emit (PlaylistRoot::OpenCompletedEvent, NULL);
304 void
305 PlaylistEntry::OpenCompletedHandler (Media *media, EventArgs *args)
307 PlaylistRoot *root = GetRoot ();
308 IMediaDemuxer *demuxer;
309 Playlist *playlist;
311 LOG_PLAYLIST ("PlaylistEntry::OpenCompletedHandler (%p, %p)\n", media, args);
312 opened = true;
314 g_return_if_fail (media != NULL);
315 g_return_if_fail (root != NULL);
316 g_return_if_fail (parent != NULL);
318 demuxer = media->GetDemuxer ();
320 g_return_if_fail (demuxer != NULL);
322 LOG_PLAYLIST ("PlaylistEntry::OpenCompletedHandler (%p, %p) demuxer: %i %s\n", media, args, GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
324 if (demuxer->IsPlaylist ()) {
325 playlist = demuxer->GetPlaylist ();
327 g_return_if_fail (playlist != NULL);
328 g_return_if_fail (parent != NULL);
330 parent->ReplaceCurrentEntry (playlist);
331 playlist->Open ();
332 } else {
333 if (parent->GetCurrentEntry () == this) {
334 OpenMediaPlayer ();
335 } else {
336 LOG_PLAYLIST ("PlaylistEntry::OpenCompletedHandler (%p, %p): opened entry in advance, waiting for current entry to finish.\n", media, args);
342 void
343 PlaylistEntry::SeekingHandler (Media *media, EventArgs *args)
345 LOG_PLAYLIST ("PlaylistEntry::SeekingHandler (%p, %p)\n", media, args);
348 void
349 PlaylistEntry::SeekCompletedHandler (Media *media, EventArgs *args)
351 PlaylistRoot *root = GetRoot ();
353 LOG_PLAYLIST ("PlaylistEntry::SeekCompletedHandler (%p, %p)\n", media, args);
355 g_return_if_fail (root != NULL);
357 if (args)
358 args->ref ();
359 root->Emit (PlaylistRoot::SeekCompletedEvent, args);
362 void
363 PlaylistEntry::CurrentStateChangedHandler (Media *media, EventArgs *args)
365 LOG_PLAYLIST ("PlaylistEntry::CurrentStateChangedHandler (%p, %p)\n", media, args);
368 void
369 PlaylistEntry::MediaErrorHandler (Media *media, ErrorEventArgs *args)
371 LOG_PLAYLIST ("PlaylistEntry::MediaErrorHandler (%p, %p): %s '%s'\n", media, args, GetFullSourceName (), args ? args->GetErrorMessage() : "?");
373 g_return_if_fail (parent != NULL);
375 parent->OnEntryFailed (args);
378 void
379 PlaylistEntry::DownloadProgressChangedHandler (Media *media, EventArgs *args)
381 PlaylistRoot *root;
383 LOG_PLAYLIST ("PlaylistEntry::DownloadProgressChanged (%p, %p %.2f). Disposed: %i\n", media, args, args ? ((ProgressEventArgs *) args)->progress : -1.0, IsDisposed ());
385 if (IsDisposed ())
386 return;
388 root = GetRoot ();
390 g_return_if_fail (root != NULL);
392 if (args)
393 args->ref ();
394 root->Emit (PlaylistRoot::DownloadProgressChangedEvent, args);
397 void
398 PlaylistEntry::BufferingProgressChangedHandler (Media *media, EventArgs *args)
400 PlaylistRoot *root = GetRoot ();
402 LOG_PLAYLIST ("PlaylistEntry::BufferingProgressChanged (%p, %p) %.2f\n", media, args, args ? ((ProgressEventArgs *) args)->progress : -1.0);
404 if (root == NULL)
405 return; // this might happen if the media is still buffering and we're in the process of getting cleaned up
407 if (args)
408 args->ref ();
409 root->Emit (PlaylistRoot::BufferingProgressChangedEvent, args);
412 void
413 PlaylistEntry::Seek (guint64 pts)
415 LOG_PLAYLIST ("PlaylistEntry::Seek (%" G_GUINT64_FORMAT ")\n", pts);
417 g_return_if_fail (media != NULL);
419 media->SeekAsync (pts);
422 void
423 PlaylistEntry::AddParams (const char *name, const char *value)
425 char *uppername = g_ascii_strup (name, strlen (name));
426 if (!strcmp (uppername, "AUTHOR")) {
427 SetAuthor (value);
428 } else if (!strcmp (uppername, "ABSTRACT")) {
429 SetAbstract (value);
430 } else if (!strcmp (uppername, "TITLE")) {
431 SetTitle (value);
432 } else if (!strcmp (uppername, "COPYRIGHT")) {
433 SetCopyright (value);
434 } else if (!strcmp (uppername, "INFOTARGET")) {
435 SetInfoTarget (value);
436 } else if (!strcmp (uppername, "INFOURL")) {
437 SetInfoURL (value);
438 } else {
439 if (params == NULL)
440 params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
442 if (g_hash_table_lookup (params, uppername) == NULL) {
443 g_hash_table_insert (params, uppername, g_strdup (value));
444 uppername = NULL;
447 g_free (uppername);
450 Uri *
451 PlaylistEntry::GetBase ()
453 return base;
456 Uri *
457 PlaylistEntry::GetBaseInherited ()
459 if (base != NULL)
460 return base;
461 if (parent != NULL)
462 return parent->GetBaseInherited ();
463 return NULL;
466 void
467 PlaylistEntry::SetBase (Uri *base)
469 // TODO: Haven't been able to make BASE work with SL,
470 // which means that I haven't been able to confirm any behaviour.
471 if (!(set_values & PlaylistKind::Base)) {
472 this->base = base;
473 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Base);
474 } else {
475 delete base;
479 const char *
480 PlaylistEntry::GetTitle ()
482 return title;
485 void
486 PlaylistEntry::SetTitle (const char *title)
488 if (!(set_values & PlaylistKind::Title)) {
489 this->title = g_strdup (title);
490 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Title);
494 const char *
495 PlaylistEntry::GetAuthor ()
497 return author;
500 void PlaylistEntry::SetAuthor (const char *author)
502 if (!(set_values & PlaylistKind::Author)) {
503 this->author = g_strdup (author);
504 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Author);
508 const char *
509 PlaylistEntry::GetAbstract ()
511 return abstract;
514 void
515 PlaylistEntry::SetAbstract (const char *abstract)
517 if (!(set_values & PlaylistKind::Abstract)) {
518 this->abstract = g_strdup (abstract);
519 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Abstract);
523 const char *
524 PlaylistEntry::GetCopyright ()
526 return copyright;
529 void
530 PlaylistEntry::SetCopyright (const char *copyright)
532 if (!(set_values & PlaylistKind::Copyright)) {
533 this->copyright = g_strdup (copyright);
534 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Copyright);
538 Uri *
539 PlaylistEntry::GetSourceName ()
541 return source_name;
544 void
545 PlaylistEntry::SetSourceName (Uri *source_name)
547 if (this->source_name)
548 delete this->source_name;
549 this->source_name = source_name;
552 TimeSpan
553 PlaylistEntry::GetStartTime ()
555 return start_time;
558 void
559 PlaylistEntry::SetStartTime (TimeSpan start_time)
561 if (!(set_values & PlaylistKind::StartTime)) {
562 this->start_time = start_time;
563 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::StartTime);
567 Duration*
568 PlaylistEntry::GetDuration ()
570 return duration;
573 Duration *
574 PlaylistEntry::GetInheritedDuration ()
576 if (HasDuration ()) {
577 return GetDuration ();
578 } else if (parent != NULL) {
579 return parent->GetInheritedDuration ();
580 } else {
581 return NULL;
585 bool
586 PlaylistEntry::HasInheritedDuration ()
588 if (HasDuration ()) {
589 return true;
590 } else if (parent != NULL) {
591 return parent->HasInheritedDuration ();
592 } else {
593 return false;
597 void
598 PlaylistEntry::SetDuration (Duration *duration)
600 if (!(set_values & PlaylistKind::Duration)) {
601 this->duration = duration;
602 set_values = (PlaylistKind::Kind) (set_values | PlaylistKind::Duration);
606 const char *
607 PlaylistEntry::GetInfoTarget ()
609 return info_target;
612 void
613 PlaylistEntry::SetInfoTarget (const char *info_target)
615 g_free (this->info_target);
616 this->info_target = g_strdup (info_target);
619 const char *
620 PlaylistEntry::GetInfoURL ()
622 return info_url;
625 void
626 PlaylistEntry::SetInfoURL (const char *info_url)
628 g_free (this->info_url);
629 this->info_url = g_strdup (info_url);
632 bool
633 PlaylistEntry::GetClientSkip ()
635 return client_skip;
638 void
639 PlaylistEntry::SetClientSkip (bool value)
641 client_skip = value;
644 MediaElement *
645 PlaylistEntry::GetElement ()
647 g_return_val_if_fail (parent != NULL, NULL);
649 return parent->GetElement ();
652 void
653 PlaylistEntry::ClearMedia ()
655 g_return_if_fail (media != NULL);
656 media->unref ();
657 media = NULL;
660 MediaPlayer *
661 PlaylistEntry::GetMediaPlayer ()
663 PlaylistRoot *root = GetRoot ();
665 g_return_val_if_fail (root != NULL, NULL);
667 return root->GetMediaPlayer ();
670 static void
671 add_attribute (MediaAttributeCollection *attributes, const char *name, const char *attr)
673 if (!attr)
674 return;
676 MediaAttribute *attribute = new MediaAttribute ();
677 attribute->SetValue (attr);
678 attribute->SetName (name);
680 attributes->Add (attribute);
681 attribute->unref ();
684 static void
685 add_attribute_glib (const char *name, const char *value, MediaAttributeCollection *attributes)
687 add_attribute (attributes, name, value);
690 void
691 PlaylistEntry::PopulateMediaAttributes ()
693 LOG_PLAYLIST ("PlaylistEntry::PopulateMediaAttributes ()\n");
695 const char *abstract = NULL;
696 const char *author = NULL;
697 const char *copyright = NULL;
698 const char *title = NULL;
699 const char *infotarget = NULL;
700 const char *infourl = NULL;
701 const char *baseurl = NULL;
703 MediaElement *element = GetElement ();
704 PlaylistEntry *current = this;
705 MediaAttributeCollection *attributes;
707 g_return_if_fail (element != NULL);
709 if (!(attributes = element->GetAttributes ())) {
710 attributes = new MediaAttributeCollection ();
711 element->SetAttributes (attributes);
712 } else {
713 attributes->Clear ();
716 while (current != NULL) {
717 if (abstract == NULL)
718 abstract = current->GetAbstract ();
719 if (author == NULL)
720 author = current->GetAuthor ();
721 if (copyright == NULL)
722 copyright = current->GetCopyright ();
723 if (title == NULL)
724 title = current->GetTitle ();
725 if (infotarget == NULL)
726 infotarget = current->GetInfoTarget ();
727 if (infourl == NULL)
728 infourl = current->GetInfoURL ();
729 if (baseurl == NULL && current->GetBase () != NULL)
730 baseurl = current->GetBase ()->originalString;
732 current = current->GetParent ();
735 add_attribute (attributes, "ABSTRACT", abstract);
736 add_attribute (attributes, "AUTHOR", author);
737 add_attribute (attributes, "BaseURL", baseurl);
738 add_attribute (attributes, "COPYRIGHT", copyright);
739 add_attribute (attributes, "InfoTarget", infotarget);
740 add_attribute (attributes, "InfoURL", infourl);
741 add_attribute (attributes, "TITLE", title);
743 current = this;
744 while (current != NULL) {
745 if (current->params != NULL)
746 g_hash_table_foreach (current->params, (GHFunc) add_attribute_glib, attributes);
747 current = current->GetParent ();
751 const char *
752 PlaylistEntry::GetFullSourceName ()
755 * Now here we have some interesting semantics:
756 * - BASE has to be a complete url, with scheme and domain
757 * - BASE only matters up to the latest / (if no /, the entire BASE is used)
759 * Examples (numbered according to the test-playlist-with-base test in test/media/video)
761 * 01 localhost/dir/ + * = error
762 * 02 /dir/ + * = error
763 * 03 dir + * = error
764 * 04 http://localhost/dir/ + somefile = http://localhost/dir/somefile
765 * 05 http://localhost/dir + somefile = http://localhost/somefile
766 * 06 http://localhost + somefile = http://localhost/somefile
767 * 07 http://localhost/dir/ + /somefile = http://localhost/somefile
768 * 08 http://localhost/dir/ + dir2/somefile = http://localhost/dir/dir2/somefile
769 * 09 rtsp://localhost/ + somefile = http://localhost/somefile
770 * 10 mms://localhost/dir/ + somefile = mms://localhost/dir/somefile
771 * 11 http://localhost/?huh + somefile = http://localhost/somefile
772 * 12 http://localhost/#huh + somefile = http://localhost/somefile
773 * 13 httP://localhost/ + somefile = http://localhost/somefile
777 // TODO: url validation, however it should probably happen inside MediaElement when we set the source
779 if (full_source_name == NULL) {
780 Uri *base = GetBaseInherited ();
781 Uri *current = GetSourceName ();
782 Uri *result = NULL;
783 const char *pathsep;
784 char *base_path;
786 //printf ("PlaylistEntry::GetFullSourceName (), base: %s, current: %s\n", base ? base->ToString () : "NULL", current ? current->ToString () : "NULL");
788 if (current == NULL) {
789 return NULL;
790 } else if (current->GetHost () != NULL) {
791 //printf (" current host (%s) is something, scheme: %s\n", current->GetHost (), current->scheme);
792 result = current;
793 } else if (base != NULL) {
794 result = new Uri ();
795 result->scheme = g_strdup (base->GetScheme());
796 result->user = g_strdup (base->GetUser());
797 result->passwd = g_strdup (base->GetPasswd());
798 result->host = g_strdup (base->GetHost());
799 result->port = base->GetPort();
800 // we ignore the params, query and fragment values.
801 if (current->GetPath() != NULL && current->GetPath() [0] == '/') {
802 //printf (" current path is relative to root dir on host\n");
803 result->path = g_strdup (current->GetPath());
804 } else if (base->GetPath() == NULL) {
805 //printf (" base path is root dir on host\n");
806 result->path = g_strdup (current->GetPath());
807 } else {
808 pathsep = strrchr (base->GetPath(), '/');
809 if (pathsep != NULL) {
810 if ((size_t) (pathsep - base->GetPath() + 1) == strlen (base->GetPath())) {
811 //printf (" last character of base path (%s) is /\n", base->path);
812 result->path = g_strjoin (NULL, base->GetPath(), current->GetPath(), NULL);
813 } else {
814 //printf (" base path (%s) does not end with /, only copy path up to the last /\n", base->path);
815 base_path = g_strndup (base->GetPath(), pathsep - base->GetPath() + 1);
816 result->path = g_strjoin (NULL, base_path, current->GetPath(), NULL);
817 g_free (base_path);
819 } else {
820 //printf (" base path (%s) does not contain a /\n", base->path);
821 result->path = g_strjoin (NULL, base->GetPath(), "/", current->GetPath(), NULL);
824 } else {
825 //printf (" there's no base\n");
826 result = current;
829 full_source_name = result->ToString ();
831 //printf (" result: %s\n", full_source_name);
833 if (result != base && result != current)
834 delete result;
836 return full_source_name;
839 void
840 PlaylistEntry::Open ()
842 LOG_PLAYLIST ("PlaylistEntry::Open (), media = %p, FullSourceName = %s\n", media, GetFullSourceName ());
844 if (!media) {
845 g_return_if_fail (GetFullSourceName () != NULL);
846 InitializeWithUri (GetFullSourceName ());
847 } else if (opened) {
848 OpenMediaPlayer ();
849 } else {
850 media->OpenAsync ();
854 void
855 PlaylistEntry::Play ()
857 MediaPlayer *mplayer = GetMediaPlayer ();
858 PlaylistRoot *root = GetRoot ();
860 LOG_PLAYLIST ("PlaylistEntry::Play (), play_when_available: %s, media: %p, source name: %s\n", play_when_available ? "true" : "false", media, source_name ? source_name->ToString () : "NULL");
862 g_return_if_fail (media != NULL);
863 g_return_if_fail (mplayer != NULL);
864 g_return_if_fail (root != NULL);
866 media->PlayAsync ();
867 mplayer->Play ();
869 root->Emit (PlaylistRoot::PlayEvent);
872 void
873 PlaylistEntry::Pause ()
875 MediaPlayer *mplayer = GetMediaPlayer ();
876 PlaylistRoot *root = GetRoot ();
878 LOG_PLAYLIST ("PlaylistEntry::Pause ()\n");
880 g_return_if_fail (media != NULL);
881 g_return_if_fail (mplayer != NULL);
882 g_return_if_fail (root != NULL);
884 play_when_available = false;
885 media->PauseAsync ();
886 mplayer->Pause ();
888 root->Emit (PlaylistRoot::PauseEvent);
891 void
892 PlaylistEntry::Stop ()
894 LOG_PLAYLIST ("PlaylistEntry::Stop ()\n");
896 play_when_available = false;
897 if (media != NULL)
898 media->StopAsync ();
901 Media *
902 PlaylistEntry::GetMedia ()
904 return media;
907 bool
908 PlaylistEntry::IsSingleFile ()
910 return parent ? parent->IsSingleFile () : false;
913 PlaylistRoot *
914 PlaylistEntry::GetRoot ()
916 Playlist *pl;
918 if (IsDisposed ())
919 return NULL;
921 if (parent == NULL) {
922 g_return_val_if_fail (GetObjectType () == Type::PLAYLISTROOT, NULL);
923 return (PlaylistRoot *) this;
926 pl = parent;
928 while (pl->parent != NULL)
929 pl = pl->parent;
931 g_return_val_if_fail (pl->GetObjectType () == Type::PLAYLISTROOT, NULL);
933 return (PlaylistRoot *) pl;
937 * Playlist
940 Playlist::Playlist (Playlist *parent, IMediaSource *source)
941 : PlaylistEntry (Type::PLAYLIST, parent)
943 is_single_file = false;
944 waiting = false;
945 opened = false;
946 Init ();
947 this->source = source;
948 this->source->ref ();
951 Playlist::Playlist (Type::Kind kind)
952 : PlaylistEntry (kind)
954 LOG_PLAYLIST ("Playlist::Playlist ()\n");
955 is_single_file = true;
956 Init ();
958 AddEntry (new PlaylistEntry (this));
961 void
962 Playlist::Init ()
964 LOG_PLAYLIST ("Playlist::Init ()\n");
966 entries = new List ();
967 current_node = NULL;
968 source = NULL;
971 void
972 Playlist::Dispose ()
974 PlaylistNode *node;
975 PlaylistEntry *entry;
977 LOG_PLAYLIST ("Playlist::Dispose () id: %i\n", GET_OBJ_ID (this));
979 current_node = NULL;
981 if (entries != NULL) {
982 node = (PlaylistNode *) entries->First ();
983 while (node != NULL) {
984 entry = node->GetEntry ();
985 if (entry != NULL)
986 entry->Dispose ();
987 node = (PlaylistNode *) node->next;
989 delete entries;
990 entries = NULL;
993 if (source) {
994 source->unref ();
995 source = NULL;
998 PlaylistEntry::Dispose ();
1002 bool
1003 Playlist::IsCurrentEntryLastEntry ()
1005 PlaylistEntry *entry;
1006 Playlist *pl;
1008 if (entries->Last () == NULL)
1009 return false;
1011 if (current_node != entries->Last ())
1012 return false;
1014 entry = GetCurrentEntry ();
1016 if (!entry->IsPlaylist ())
1017 return true;
1019 pl = (Playlist *) entry;
1021 return pl->IsCurrentEntryLastEntry ();
1024 void
1025 Playlist::Open ()
1027 PlaylistEntry *current_entry;
1029 LOG_PLAYLIST ("Playlist::Open ()\n");
1031 current_node = (PlaylistNode *) entries->First ();
1033 current_entry = GetCurrentEntry ();
1035 while (current_entry && current_entry->HasDuration () && current_entry->GetDuration ()->HasTimeSpan() &&
1036 current_entry->GetDuration ()->GetTimeSpan () == 0) {
1037 LOG_PLAYLIST ("Playlist::Open (), current entry (%s) has zero duration, skipping it.\n", current_entry->GetSourceName ()->ToString ());
1038 current_node = (PlaylistNode *) current_node->next;
1039 current_entry = GetCurrentEntry ();
1042 if (current_entry)
1043 current_entry->Open ();
1045 opened = true;
1047 LOG_PLAYLIST ("Playlist::Open (): current node: %p, current entry: %p\n", current_entry, GetCurrentEntry ());
1050 bool
1051 Playlist::PlayNext ()
1053 PlaylistEntry *current_entry;
1054 MediaElement *element = GetElement ();
1055 PlaylistRoot *root = GetRoot ();
1057 LOG_PLAYLIST ("Playlist::PlayNext () current_node: %p\n", current_node);
1058 g_return_val_if_fail (root != NULL, false);
1060 if (!current_node)
1061 return false;
1063 SetWaiting (false);
1065 current_entry = GetCurrentEntry ();
1067 if (current_entry->HasDuration() && current_entry->GetDuration()->IsForever ()) {
1068 element->SetPlayRequested ();
1069 current_entry->Play ();
1070 return true;
1073 if (current_entry->IsPlaylist ()) {
1074 Playlist *current_playlist = (Playlist *) current_entry;
1075 if (current_playlist->PlayNext ())
1076 return true;
1079 if (current_node->next) {
1080 current_node = (PlaylistNode *) current_node->next;
1082 current_entry = GetCurrentEntry ();
1083 if (current_entry) {
1084 LOG_PLAYLIST ("Playlist::PlayNext () playing entry: %p %s\n", current_entry, current_entry->GetFullSourceName ());
1085 element->SetPlayRequested ();
1086 root->Emit (PlaylistRoot::EntryChangedEvent);
1087 current_entry->Open ();
1088 return true;
1092 LOG_PLAYLIST ("Playlist::PlayNext () current_node: %p, nothing to play (is root: %i)\n", current_node, GetObjectType () == Type::PLAYLISTROOT);
1094 if (GetObjectType () == Type::PLAYLISTROOT)
1095 root->Emit (PlaylistRoot::MediaEndedEvent);
1097 return false;
1100 void
1101 Playlist::OnEntryEnded ()
1103 LOG_PLAYLIST ("Playlist::OnEntryEnded ()\n");
1104 PlayNext ();
1107 void
1108 Playlist::OnEntryFailed (ErrorEventArgs *args)
1110 bool fatal = true;
1111 PlaylistRoot *root = GetRoot ();
1113 LOG_PLAYLIST ("Playlist::OnEntryFailed () extended_code: %i is_single_file: %i\n", args ? args->GetExtendedCode() : 0, is_single_file);
1115 g_return_if_fail (root != NULL);
1117 // media or playlist 404: fatal
1118 // invalid playlist (playlist parsing failed): fatal
1119 // invalid media (gif, swf): play next
1120 if (args == NULL) {
1121 fatal = true;
1122 } else {
1123 // check if we're in a playlist
1124 if (GetMedia () != NULL && GetMedia ()->GetDemuxer () != NULL && GetMedia ()->GetDemuxer ()->GetObjectType () == Type::ASXDEMUXER) {
1125 // we're a playlist
1126 if (args->GetExtendedCode() == MEDIA_UNKNOWN_CODEC) {
1127 fatal = false;
1128 } else {
1129 fatal = true;
1131 } else {
1132 // we're not a playlist
1133 fatal = true;
1137 if (fatal) {
1138 if (args)
1139 args->ref ();
1140 root->Emit (PlaylistRoot::MediaErrorEvent, args);
1141 } else {
1142 root->PlayNext ();
1146 void
1147 Playlist::Seek (guint64 pts)
1149 PlaylistEntry *current_entry;
1151 LOG_PLAYLIST ("Playlist::Seek (%" G_GUINT64_FORMAT ")\n", pts);
1153 current_entry = GetCurrentEntry ();
1155 g_return_if_fail (current_entry != NULL);
1157 current_entry->Seek (pts);
1160 void
1161 Playlist::Play ()
1163 PlaylistEntry *current_entry;
1165 LOG_PLAYLIST ("Playlist::Play ()\n");
1167 current_entry = GetCurrentEntry ();
1169 g_return_if_fail (current_entry != NULL);
1171 if (current_entry && current_entry->HasDuration () && current_entry->GetDuration () == 0) {
1172 LOG_PLAYLIST ("Playlist::Open (), current entry (%s) has zero duration, skipping it.\n", current_entry->GetSourceName ()->ToString ());
1173 OnEntryEnded ();
1174 } else {
1175 if (current_entry)
1176 current_entry->Play ();
1180 void
1181 Playlist::Pause ()
1183 PlaylistEntry *current_entry;
1185 LOG_PLAYLIST ("Playlist::Pause ()\n");
1187 current_entry = GetCurrentEntry ();
1189 g_return_if_fail (current_entry != NULL);
1191 current_entry->Pause ();
1194 void
1195 Playlist::Stop ()
1197 PlaylistNode *node;
1199 LOG_PLAYLIST ("Playlist::Stop ()\n");
1201 node = (PlaylistNode *) entries->First ();
1202 current_node = node; // reset to first node
1203 while (node != NULL) {
1204 node->GetEntry ()->Stop ();
1205 node = (PlaylistNode *) node->next;
1209 void
1210 Playlist::PopulateMediaAttributes ()
1212 PlaylistEntry *current_entry = GetCurrentEntry ();
1214 LOG_PLAYLIST ("Playlist::PopulateMediaAttributes ()\n");
1216 if (!current_entry)
1217 return;
1219 current_entry->PopulateMediaAttributes ();
1222 void
1223 Playlist::AddEntry (PlaylistEntry *entry)
1225 PlaylistNode *node;
1227 LOG_PLAYLIST ("Playlist::AddEntry (%p) Count: %i\n", entry, entries->Length ());
1229 node = new PlaylistNode (entry);
1230 entries->Append (node);
1231 entry->unref ();
1233 if (entries->Length () == 1) {
1234 g_return_if_fail (current_node == NULL);
1235 current_node = node;
1239 bool
1240 Playlist::ReplaceCurrentEntry (Playlist *pl)
1242 bool result;
1244 PlaylistEntry *current_entry = GetCurrentEntry ();
1246 LOG_PLAYLIST ("Playlist::ReplaceCurrentEntry (%p)\n", pl);
1248 // check for too nested playlist
1249 int counter = 0;
1250 PlaylistEntry *e = this;
1251 while (e != NULL && e->IsPlaylist ()) {
1252 if (e->GetObjectType () != Type::PLAYLISTROOT && e->GetMedia () != NULL && e->GetMedia ()->GetDemuxer () != NULL && e->GetMedia ()->GetDemuxer ()->GetObjectType () == Type::ASXDEMUXER)
1253 counter++;
1254 e = e->GetParent ();
1256 if (counter > 5) {
1257 ErrorEventArgs *args = new ErrorEventArgs (MediaError,
1258 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR"));
1259 OnEntryFailed (args);
1260 args->unref ();
1261 return false;
1265 if (current_entry->IsPlaylist ()) {
1266 result = ((Playlist *) current_entry)->ReplaceCurrentEntry (pl);
1267 } else {
1268 PlaylistNode *pln = new PlaylistNode (pl);
1269 pl->MergeWith (current_entry);
1270 entries->InsertBefore (pln, current_node);
1271 entries->Remove (current_node);
1272 pl->SetParent (this);
1273 current_node = pln;
1274 result = true;
1277 LOG_PLAYLIST ("Playlist::ReplaceCurrentEntrY (%p) [DONE]\n", pl);
1279 return result;
1282 void
1283 Playlist::MergeWith (PlaylistEntry *entry)
1285 LOG_PLAYLIST ("Playlist::MergeWith (%p)\n", entry);
1287 SetBase (entry->GetBase () ? new Uri (*entry->GetBase ()) : NULL);
1288 SetTitle (entry->GetTitle ());
1289 SetAuthor (entry->GetAuthor ());
1290 SetAbstract (entry->GetAbstract ());
1291 SetCopyright (entry->GetCopyright ());
1293 SetSourceName (entry->GetSourceName () ? new Uri (*entry->GetSourceName ()) : NULL);
1294 if (entry->HasDuration ())
1295 SetDuration (entry->GetDuration ());
1296 Initialize (entry->GetMedia ());
1297 entry->ClearMedia ();
1300 PlaylistEntry *
1301 Playlist::GetCurrentPlaylistEntry ()
1303 PlaylistEntry *result = NULL;
1305 if (current_node)
1306 result = current_node->GetEntry () ->GetCurrentPlaylistEntry ();
1308 return result;
1311 * PlaylistRoot
1314 PlaylistRoot::PlaylistRoot (MediaElement *element)
1315 : Playlist (Type::PLAYLISTROOT)
1317 this->element = element;
1319 mplayer = element->GetMediaPlayer ();
1320 mplayer->AddHandler (MediaPlayer::MediaEndedEvent, MediaEndedCallback, this);
1321 mplayer->AddHandler (MediaPlayer::BufferUnderflowEvent, BufferUnderflowCallback, this);
1322 mplayer->ref ();
1325 void
1326 PlaylistRoot::Dispose ()
1328 if (mplayer != NULL) {
1329 mplayer->RemoveAllHandlers (this);
1330 mplayer->unref ();
1331 mplayer = NULL;
1334 Playlist::Dispose ();
1337 bool
1338 PlaylistRoot::IsSingleFile ()
1340 PlaylistEntry *entry;
1342 if (GetCount () != 1)
1343 return false;
1345 entry = GetCurrentEntry ();
1346 if (entry == NULL)
1347 return false;
1349 if (entry->GetObjectType () == Type::PLAYLISTENTRY)
1350 return true;
1352 return entry->IsSingleFile ();
1355 #if DEBUG
1356 void
1357 PlaylistEntry::DumpInternal (int tabs)
1359 printf ("%*s%s %i\n", tabs, "", GetTypeName (), GET_OBJ_ID (this));
1360 tabs++;
1361 printf ("%*sParent: %p %s\n", tabs, "", parent, parent ? parent->GetTypeName () : NULL);
1362 printf ("%*sFullSourceName: %s\n", tabs, "", GetFullSourceName ());
1363 printf ("%*sDuration: %s %.2f seconds\n", tabs, "", HasDuration () ? "yes" : "no", HasDuration () ? GetDuration ()->ToSecondsFloat () : 0.0);
1364 printf ("%*sMedia: %i %s\n", tabs, "", GET_OBJ_ID (media), media ? "" : "(null)");
1365 if (media) {
1366 printf ("%*sUri: %s\n", tabs, "", media->GetUri ());
1367 printf ("%*sDemuxer: %i %s\n", tabs, "", GET_OBJ_ID (media->GetDemuxer ()), media->GetDemuxer () ? media->GetDemuxer ()->GetTypeName () : "N/A");
1368 printf ("%*sSource: %i %s\n", tabs, "", GET_OBJ_ID (media->GetSource ()), media->GetSource () ? media->GetSource ()->GetTypeName () : "N/A");
1373 void
1374 Playlist::DumpInternal (int tabs)
1376 PlaylistNode *node;
1378 PlaylistEntry::DumpInternal (tabs);
1379 printf ("%*s %i entries:\n", tabs, "", entries->Length ());
1380 node = (PlaylistNode *) entries->First ();
1381 while (node != NULL) {
1382 if (node == current_node)
1383 printf ("*%*s * CURRENT NODE *\n", tabs, "");
1384 node->GetEntry ()->DumpInternal (tabs + 2);
1385 node = (PlaylistNode *) node->next;
1388 void
1389 PlaylistRoot::Dump ()
1391 printf ("\n\nDUMP OF PLAYLIST\n\n");
1392 DumpInternal (0);
1393 printf ("\n\nDUMP OF PLAYLIST DONE\n\n");
1395 #endif
1397 void
1398 PlaylistRoot::SeekCallback (EventObject *obj)
1400 PlaylistRoot *playlist = (PlaylistRoot *) obj;
1401 PtsNode *pts_node;
1403 LOG_PLAYLIST ("PlaylistRoot::SeekCallback ()\n");
1405 if (playlist->IsDisposed ())
1406 return;
1408 pts_node = (PtsNode *) playlist->seeks.First ();
1409 if (pts_node != NULL) {
1410 playlist->seeks.Unlink (pts_node);
1411 playlist->Seek (pts_node->pts);
1412 delete pts_node;
1416 void
1417 PlaylistRoot::SeekAsync (guint64 pts)
1419 LOG_PLAYLIST ("PlaylistRoot::SeekAsync (%" G_GUINT64_FORMAT ")\n", pts);
1420 seeks.Append (new PtsNode (pts));
1421 AddTickCall (SeekCallback);
1424 void
1425 PlaylistRoot::PlayCallback (EventObject *obj)
1427 LOG_PLAYLIST ("Playlist::PlayCallback ()\n");
1429 PlaylistRoot *root = (PlaylistRoot *) obj;
1430 if (root->IsDisposed ())
1431 return;
1432 root->Play ();
1435 void
1436 PlaylistRoot::PlayAsync ()
1438 LOG_PLAYLIST ("Playlist::PlayAsync ()\n");
1439 AddTickCall (PlayCallback);
1442 void
1443 PlaylistRoot::PauseCallback (EventObject *obj)
1445 LOG_PLAYLIST ("Playlist::PauseCallback ()\n");
1447 PlaylistRoot *root = (PlaylistRoot *) obj;
1448 if (root->IsDisposed ())
1449 return;
1450 root->Pause ();
1453 void
1454 PlaylistRoot::PauseAsync ()
1456 LOG_PLAYLIST ("Playlist::PauseAsync ()\n");
1457 AddTickCall (PauseCallback);
1460 void
1461 PlaylistRoot::OpenCallback (EventObject *obj)
1463 LOG_PLAYLIST ("Playlist::OpenCallback ()\n");
1465 PlaylistRoot *root = (PlaylistRoot *) obj;
1466 if (root->IsDisposed ())
1467 return;
1468 root->Open ();
1471 void
1472 PlaylistRoot::OpenAsync ()
1474 LOG_PLAYLIST ("Playlist::OpenAsync ()\n");
1475 AddTickCall (OpenCallback);
1478 void
1479 PlaylistRoot::StopCallback (EventObject *obj)
1481 LOG_PLAYLIST ("Playlist::StopCallback ()\n");
1483 PlaylistRoot *root = (PlaylistRoot *) obj;
1484 if (root->IsDisposed ())
1485 return;
1486 root->Stop ();
1489 void
1490 PlaylistRoot::StopAsync ()
1492 LOG_PLAYLIST ("Playlist::StopAsync ()\n");
1493 AddTickCall (StopCallback);
1496 void
1497 PlaylistRoot::Stop ()
1499 MediaPlayer *mplayer;
1501 LOG_PLAYLIST ("PlaylistRoot::Stop ()\n");
1503 mplayer = GetMediaPlayer ();
1505 Playlist::Stop ();
1506 if (mplayer != NULL)
1507 mplayer->Stop ();
1508 // Stop is called async, and if we now emit Open async, we'd possibly not get events in the right order
1509 // example with user code:
1510 // Stop ();
1511 // Play ();
1512 // would end up like:
1513 // StopAsync (); -> enqueue Stop
1514 // PlayAsync (); -> enqueue Play
1515 // Stop is called, enqueue Open
1516 Open ();
1517 Emit (StopEvent); // we emit the event after enqueuing the Open request, do avoid funky side-effects of event emission.
1520 void
1521 PlaylistRoot::EmitBufferUnderflowEvent (EventObject *obj)
1523 PlaylistRoot *root = (PlaylistRoot *) obj;
1524 root->Emit (BufferUnderflowEvent);
1527 MediaPlayer *
1528 PlaylistRoot::GetMediaPlayer ()
1530 return mplayer;
1533 Media *
1534 PlaylistRoot::GetCurrentMedia ()
1536 PlaylistEntry *entry = GetCurrentEntry ();
1538 if (entry == NULL)
1539 return NULL;
1541 return entry->GetMedia ();
1544 MediaElement *
1545 PlaylistRoot::GetElement ()
1547 return element;
1550 void
1551 PlaylistRoot::MediaEndedHandler (MediaPlayer *mplayer, EventArgs *args)
1553 LOG_PLAYLIST ("PlaylistRoot::MediaEndedHandler (%p, %p)\n", mplayer, args);
1555 OnEntryEnded ();
1557 // Emit (MediaEndedEvent, args);
1560 void
1561 PlaylistRoot::BufferUnderflowHandler (MediaPlayer *mplayer, EventArgs *args)
1563 LOG_PLAYLIST ("PlaylistRoot::BufferUnderflowHandler (%p, %p)\n", mplayer, args);
1565 if (Surface::InMainThread ()) {
1566 EmitBufferUnderflowEvent (this);
1567 } else {
1568 AddTickCall (EmitBufferUnderflowEvent);
1573 * PlaylistParser
1576 PlaylistParser::PlaylistParser (PlaylistRoot *root, IMediaSource *source)
1578 this->root = root;
1579 this->source = source;
1580 this->internal = NULL;
1581 this->kind_stack = NULL;
1582 this->playlist = NULL;
1583 this->current_entry = NULL;
1584 this->current_text = NULL;
1585 this->error_args = NULL;
1588 void
1589 PlaylistParser::SetSource (IMediaSource *new_source)
1591 if (source)
1592 source->unref ();
1593 source = new_source;
1594 if (source)
1595 source->ref ();
1598 void
1599 PlaylistParser::Setup (XmlType type)
1601 playlist = NULL;
1602 current_entry = NULL;
1603 current_text = NULL;
1605 was_playlist = false;
1607 internal = new PlaylistParserInternal ();
1608 kind_stack = new List ();
1609 PushCurrentKind (PlaylistKind::Root);
1611 if (type == XML_TYPE_ASX3) {
1612 XML_SetUserData (internal->parser, this);
1613 XML_SetElementHandler (internal->parser, on_asx_start_element, on_asx_end_element);
1614 XML_SetCharacterDataHandler (internal->parser, on_asx_text);
1619 void
1620 PlaylistParser::Cleanup ()
1622 if (kind_stack) {
1623 kind_stack->Clear (true);
1624 delete kind_stack;
1625 kind_stack = NULL;
1627 delete internal;
1628 internal = NULL;
1629 if (playlist) {
1630 playlist->unref ();
1631 playlist = NULL;
1633 if (error_args) {
1634 error_args->unref ();
1635 error_args = NULL;
1639 PlaylistParser::~PlaylistParser ()
1641 Cleanup ();
1644 static bool
1645 str_match (const char *candidate, const char *tag)
1647 return g_ascii_strcasecmp (candidate, tag) == 0;
1650 void
1651 PlaylistParser::on_asx_start_element (gpointer user_data, const char *name, const char **attrs)
1653 ((PlaylistParser *) user_data)->OnASXStartElement (name, attrs);
1656 void
1657 PlaylistParser::on_asx_end_element (gpointer user_data, const char *name)
1659 ((PlaylistParser *) user_data)->OnASXEndElement (name);
1662 void
1663 PlaylistParser::on_asx_text (gpointer user_data, const char *data, int len)
1665 ((PlaylistParser *) user_data)->OnASXText (data, len);
1668 static bool
1669 is_all_whitespace (const char *str)
1671 if (str == NULL)
1672 return true;
1674 for (int i = 0; str [i] != 0; i++) {
1675 switch (str [i]) {
1676 case 10:
1677 case 13:
1678 case ' ':
1679 case '\t':
1680 break;
1681 default:
1682 return false;
1685 return true;
1689 // To make matters more interesting, the format of the VALUE attribute in the STARTTIME tag isn't
1690 // exactly the same as xaml's or javascript's TimeSpan format.
1692 // The time index, in hours, minutes, seconds, and hundredths of seconds.
1693 // [[hh]:mm]:ss.fract
1695 // The parser seems to stop if it finds a second dot, returnning whatever it had parsed
1696 // up till then
1698 // At most 4 digits of fract is read, the rest is ignored (even if it's not numbers).
1700 static bool
1701 parse_int (const char **pp, const char *end, int *result)
1703 const char *p = *pp;
1704 int res = 0;
1705 bool success = false;
1707 while (p <= end && g_ascii_isdigit (*p)) {
1708 res = res * 10 + *p - '0';
1709 p++;
1712 success = *pp != p;
1714 *pp = p;
1715 *result = res;
1717 return success;
1720 static bool
1721 duration_from_asx_str (PlaylistParser *parser, const char *str, Duration **res)
1723 const char *end = str + strlen (str);
1724 const char *p;
1726 int values [] = {0, 0, 0};
1727 int counter = 0;
1728 int hh = 0, mm = 0, ss = 0;
1729 int milliseconds = 0;
1730 int digits = 2;
1732 p = str;
1734 if (!g_ascii_isdigit (*p)) {
1735 parser->ParsingError (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 2210, "AG_E_INVALID_ARGUMENT")));
1736 return false;
1739 for (int i = 0; i < 3; i++) {
1740 if (!parse_int (&p, end, &values [i])) {
1741 parser->ParsingError (new ErrorEventArgs (MediaError,
1742 MoonError (MoonError::EXCEPTION, 2210, "AG_E_INVALID_ARGUMENT")));
1743 return false;
1745 counter++;
1746 if (*p != ':')
1747 break;
1748 p++;
1751 if (*p == '.') {
1752 p++;
1753 while (digits >= 0 && g_ascii_isdigit (*p)) {
1754 milliseconds += pow (10.0f, digits) * (*p - '0');
1755 p++;
1756 digits--;
1758 if (counter == 3 && *p != 0 && !g_ascii_isdigit (*p)) {
1759 parser->ParsingError (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 2210, "AG_E_INVALID_ARGUMENT")));
1760 return false;
1764 switch (counter) {
1765 case 1:
1766 ss = values [0];
1767 break;
1768 case 2:
1769 ss = values [1];
1770 mm = values [0];
1771 break;
1772 case 3:
1773 ss = values [2];
1774 mm = values [1];
1775 hh = values [0];
1776 break;
1777 default:
1778 parser->ParsingError (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 2210, "AG_E_INVALID_ARGUMENT")));
1779 return false;
1782 gint64 ms = ((hh * 3600) + (mm * 60) + ss) * 1000 + milliseconds;
1783 TimeSpan result = TimeSpan_FromPts (MilliSeconds_ToPts (ms));
1784 Duration *duration = new Duration (result);
1786 *res = duration;
1788 return true;
1791 void
1792 PlaylistParser::OnASXStartElement (const char *name, const char **attrs)
1794 PlaylistKind::Kind kind = StringToKind (name);
1795 Uri *uri = NULL;
1796 bool failed;
1798 LOG_PLAYLIST ("PlaylistParser::OnStartElement (%s, %p), kind = %d\n", name, attrs, kind);
1800 g_free (current_text);
1801 current_text = NULL;
1803 PushCurrentKind (kind);
1805 switch (kind) {
1806 case PlaylistKind::Abstract:
1807 if (attrs != NULL && attrs [0] != NULL)
1808 ParsingError (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1809 break;
1810 case PlaylistKind::Asx:
1811 // Here the kind stack should be: Root+Asx
1812 if (kind_stack->Length () != 2 || !AssertParentKind (PlaylistKind::Root)) {
1813 ParsingError (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
1814 return;
1817 playlist = new Playlist (root, source);
1819 for (int i = 0; attrs [i] != NULL; i += 2) {
1820 if (str_match (attrs [i], "VERSION")) {
1821 if (str_match (attrs [i+1], "3")) {
1822 playlist_version = 3;
1823 } else if (str_match (attrs [i+1], "3.0")) {
1824 playlist_version = 3;
1825 } else {
1826 ParsingError (new ErrorEventArgs (MediaError,
1827 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
1829 } else if (str_match (attrs [i], "BANNERBAR")) {
1830 ParsingError (new ErrorEventArgs (MediaError,
1831 MoonError (MoonError::EXCEPTION, 3007, "Unsupported ASX attribute")));
1832 } else if (str_match (attrs [i], "PREVIEWMODE")) {
1833 ParsingError (new ErrorEventArgs (MediaError,
1834 MoonError (MoonError::EXCEPTION, 3007, "Unsupported ASX attribute")));
1835 } else {
1836 ParsingError (new ErrorEventArgs (MediaError,
1837 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1840 break;
1841 case PlaylistKind::Author:
1842 if (attrs != NULL && attrs [0] != NULL)
1843 ParsingError (new ErrorEventArgs (MediaError,
1844 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1845 break;
1846 case PlaylistKind::Banner:
1847 if (attrs != NULL && attrs [0] != NULL)
1848 ParsingError (new ErrorEventArgs (MediaError,
1849 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1850 break;
1851 case PlaylistKind::Base:
1852 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
1853 break;
1854 for (int i = 0; attrs [i] != NULL; i += 2) {
1855 if (str_match (attrs [i], "HREF")) {
1856 // TODO: What do we do with this value?
1857 if (GetCurrentContent () != NULL) {
1858 failed = false;
1859 uri = new Uri ();
1860 if (!uri->Parse (attrs [i+1], true)) {
1861 failed = true;
1862 } else if (uri->GetScheme() == NULL) {
1863 failed = true;
1864 } else if (uri->IsScheme ("http") &&
1865 uri->IsScheme ("https") &&
1866 uri->IsScheme ("mms") &&
1867 uri->IsScheme ("rtsp") &&
1868 uri->IsScheme ("rstpt")) {
1869 failed = true;
1872 if (!failed) {
1873 GetCurrentContent ()->SetBase (uri);
1874 } else {
1875 delete uri;
1876 ParsingError (new ErrorEventArgs (MediaError,
1877 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
1879 uri = NULL;
1881 } else {
1882 ParsingError (new ErrorEventArgs (MediaError,
1883 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1884 break;
1887 break;
1888 case PlaylistKind::Copyright:
1889 if (attrs != NULL && attrs [0] != NULL)
1890 ParsingError (new ErrorEventArgs (MediaError,
1891 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1892 break;
1893 case PlaylistKind::Duration: {
1894 Duration *dur;
1895 for (int i = 0; attrs [i] != NULL; i += 2) {
1896 if (str_match (attrs [i], "VALUE")) {
1897 if (duration_from_asx_str (this, attrs [i+1], &dur)) {
1898 if (GetCurrentEntry () != NULL && GetParentKind () != PlaylistKind::Ref) {
1899 LOG_PLAYLIST ("PlaylistParser::OnStartElement (%s, %p), found VALUE/timespan = %f s\n", name, attrs, TimeSpan_ToSecondsFloat (dur->GetTimeSpan()));
1900 GetCurrentEntry ()->SetDuration (dur);
1903 } else {
1904 ParsingError (new ErrorEventArgs (MediaError,
1905 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1906 break;
1909 break;
1911 case PlaylistKind::Entry: {
1912 bool client_skip = true;
1913 for (int i = 0; attrs [i] != NULL; i += 2) {
1914 if (str_match (attrs [i], "CLIENTSKIP")) {
1915 // TODO: What do we do with this value?
1916 if (str_match (attrs [i+1], "YES")) {
1917 client_skip = true;
1918 } else if (str_match (attrs [i+1], "NO")) {
1919 client_skip = false;
1920 } else {
1921 ParsingError (new ErrorEventArgs (MediaError,
1922 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
1923 break;
1925 } else if (str_match (attrs [i], "SKIPIFREF")) {
1926 ParsingError (new ErrorEventArgs (MediaError,
1927 MoonError (MoonError::EXCEPTION, 3007, "Unsupported ASX attribute")));
1928 break;
1929 } else {
1930 ParsingError (new ErrorEventArgs (MediaError,
1931 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1932 break;
1935 PlaylistEntry *entry = new PlaylistEntry (playlist);
1936 entry->SetClientSkip (client_skip);
1937 playlist->AddEntry (entry);
1938 current_entry = entry;
1939 break;
1941 case PlaylistKind::EntryRef: {
1942 char *href = NULL;
1943 for (int i = 0; attrs [i] != NULL; i += 2) {
1944 if (str_match (attrs [i], "HREF")) {
1945 if (href == NULL)
1946 href = g_strdup (attrs [i+1]);
1947 // Docs says this attribute isn't unsupported, but an error is emitted.
1948 //} else if (str_match (attrs [i], "CLIENTBIND")) {
1949 // // TODO: What do we do with this value?
1950 } else {
1951 ParsingError (new ErrorEventArgs (MediaError,
1952 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1953 break;
1957 if (href) {
1958 uri = new Uri ();
1959 if (!uri->Parse (href)) {
1960 delete uri;
1961 uri = NULL;
1962 ParsingError (new ErrorEventArgs (MediaError,
1963 MoonError (MoonError::EXCEPTION, 1001, "AG_E_UNKNOWN_ERROR")));
1967 PlaylistEntry *entry = new PlaylistEntry (playlist);
1968 if (uri)
1969 entry->SetSourceName (uri);
1970 uri = NULL;
1971 playlist->AddEntry (entry);
1972 current_entry = entry;
1973 break;
1975 case PlaylistKind::LogUrl:
1976 if (attrs != NULL && attrs [0] != NULL)
1977 ParsingError (new ErrorEventArgs (MediaError,
1978 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1979 break;
1980 case PlaylistKind::MoreInfo:
1981 for (int i = 0; attrs [i] != NULL; i += 2) {
1982 if (str_match (attrs [i], "HREF")) {
1983 if (GetCurrentEntry () != NULL)
1984 GetCurrentEntry ()->SetInfoURL (attrs [i+1]);
1985 } else if (str_match (attrs [i], "TARGET")) {
1986 if (GetCurrentEntry () != NULL)
1987 GetCurrentEntry ()->SetInfoTarget (attrs [i+1]);
1988 } else {
1989 ParsingError (new ErrorEventArgs (MediaError,
1990 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
1991 break;
1994 break;
1995 case PlaylistKind::StartTime: {
1996 Duration *dur;
1997 for (int i = 0; attrs [i] != NULL; i += 2) {
1998 if (str_match (attrs [i], "VALUE")) {
1999 if (duration_from_asx_str (this, attrs [i+1], &dur) && dur->HasTimeSpan ()) {
2000 if (GetCurrentEntry () != NULL && GetParentKind () != PlaylistKind::Ref) {
2001 LOG_PLAYLIST ("PlaylistParser::OnStartElement (%s, %p), found VALUE/timespan = %f s\n", name, attrs, TimeSpan_ToSecondsFloat (dur->GetTimeSpan()));
2002 GetCurrentEntry ()->SetStartTime (dur->GetTimeSpan ());
2005 } else {
2006 ParsingError (new ErrorEventArgs (MediaError,
2007 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
2008 break;
2012 break;
2014 case PlaylistKind::Ref: {
2015 for (int i = 0; attrs [i] != NULL; i += 2) {
2016 if (str_match (attrs [i], "HREF")) {
2017 if (GetCurrentEntry () != NULL && GetCurrentEntry ()->GetSourceName () == NULL) {
2018 uri = new Uri ();
2019 if (uri->Parse (attrs [i+1])) {
2020 GetCurrentEntry ()->SetSourceName (uri);
2021 } else {
2022 delete uri;
2023 ParsingError (new ErrorEventArgs (MediaError,
2024 MoonError (MoonError::EXCEPTION, 1001, "AG_E_UNKNOWN_ERROR")));
2026 uri = NULL;
2028 } else {
2029 ParsingError (new ErrorEventArgs (MediaError,
2030 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
2031 break;
2034 break;
2036 case PlaylistKind::Param: {
2037 const char *name = NULL;
2038 const char *value = NULL;
2040 for (int i = 0; attrs [i] != NULL; i+= 2) {
2041 if (str_match (attrs [i], "name")) {
2042 name = attrs [i + 1];
2043 } else if (str_match (attrs [i], "value")) {
2044 value = attrs [i + 1];
2045 } else {
2046 ParsingError (new ErrorEventArgs (MediaError,
2047 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
2048 break;
2051 if (value != NULL && value [0] != 0 && name != NULL && name [0] != 0) {
2052 PlaylistEntry *entry = GetCurrentEntry ();
2053 if (entry == NULL)
2054 entry = playlist;
2055 if (entry == NULL)
2056 entry = root;
2057 entry->AddParams (name, value);
2058 } else {
2059 // TODO: add test
2061 break;
2063 case PlaylistKind::Title:
2064 if (attrs != NULL && attrs [0] != NULL)
2065 ParsingError (new ErrorEventArgs (MediaError,
2066 MoonError (MoonError::EXCEPTION, 3005, "Invalid ASX attribute")));
2067 break;
2068 case PlaylistKind::StartMarker:
2069 case PlaylistKind::EndMarker:
2070 case PlaylistKind::Repeat:
2071 case PlaylistKind::Event:
2072 ParsingError (new ErrorEventArgs (MediaError,
2073 MoonError (MoonError::EXCEPTION, 3006, "Unsupported ASX element")));
2074 break;
2075 case PlaylistKind::Root:
2076 case PlaylistKind::Unknown:
2077 default:
2078 LOG_PLAYLIST ("PlaylistParser::OnStartElement ('%s', %p): Unknown kind: %d\n", name, attrs, kind);
2079 ParsingError (new ErrorEventArgs (MediaError,
2080 MoonError (MoonError::EXCEPTION, 3004, "Invalid ASX element")));
2081 break;
2085 void
2086 PlaylistParser::OnASXEndElement (const char *name)
2088 PlaylistKind::Kind kind = GetCurrentKind ();
2089 Duration *dur;
2091 LOG_PLAYLIST ("PlaylistParser::OnEndElement (%s), GetCurrentKind (): %d, GetCurrentKind () to string: %s\n", name, kind, KindToString (kind));
2093 switch (kind) {
2094 case PlaylistKind::Abstract:
2095 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2096 break;
2097 if (GetCurrentContent () != NULL)
2098 GetCurrentContent ()->SetAbstract (current_text);
2099 break;
2100 case PlaylistKind::Author:
2101 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2102 break;
2103 if (GetCurrentContent () != NULL)
2104 GetCurrentContent ()->SetAuthor (current_text);
2105 break;
2106 case PlaylistKind::Base:
2107 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2108 break;
2109 break;
2110 case PlaylistKind::Copyright:
2111 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2112 break;
2113 if (GetCurrentContent () != NULL)
2114 GetCurrentContent ()->SetCopyright (current_text);
2115 break;
2116 case PlaylistKind::Duration:
2117 if (!AssertParentKind (PlaylistKind::Entry | PlaylistKind::Ref))
2118 break;
2119 if (current_text == NULL)
2120 break;
2121 duration_from_asx_str (this, current_text, &dur);
2122 if (GetCurrentEntry () != NULL)
2123 GetCurrentEntry ()->SetDuration (dur);
2124 break;
2125 case PlaylistKind::Entry:
2126 if (!AssertParentKind (PlaylistKind::Asx))
2127 break;
2128 if (!is_all_whitespace (current_text)) {
2129 ParsingError (new ErrorEventArgs (MediaError,
2130 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2132 break;
2133 case PlaylistKind::EntryRef:
2134 if (!AssertParentKind (PlaylistKind::Asx))
2135 break;
2136 break;
2137 case PlaylistKind::StartTime:
2138 if (!AssertParentKind (PlaylistKind::Entry | PlaylistKind::Ref))
2139 break;
2140 if (!is_all_whitespace (current_text)) {
2141 ParsingError (new ErrorEventArgs (MediaError,
2142 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2144 break;
2145 case PlaylistKind::Title:
2146 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2147 break;
2148 if (GetCurrentContent () != NULL)
2149 GetCurrentContent ()->SetTitle (current_text);
2150 break;
2151 case PlaylistKind::Asx:
2152 if (playlist_version == 3)
2153 was_playlist = true;
2154 if (!AssertParentKind (PlaylistKind::Root))
2155 break;
2156 break;
2157 case PlaylistKind::Ref:
2158 if (!AssertParentKind (PlaylistKind::Entry))
2159 break;
2160 if (!is_all_whitespace (current_text)) {
2161 ParsingError (new ErrorEventArgs (MediaError,
2162 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2164 break;
2165 case PlaylistKind::MoreInfo:
2166 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2167 break;
2168 if (!is_all_whitespace (current_text)) {
2169 ParsingError (new ErrorEventArgs (MediaError,
2170 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2172 break;
2173 case PlaylistKind::Param:
2174 if (!AssertParentKind (PlaylistKind::Asx | PlaylistKind::Entry))
2175 break;
2176 if (!is_all_whitespace (current_text)) {
2177 ParsingError (new ErrorEventArgs (MediaError,
2178 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2180 break;
2181 default:
2182 LOG_PLAYLIST ("PlaylistParser::OnEndElement ('%s'): Unknown kind %d.\n", name, kind);
2183 ParsingError (new ErrorEventArgs (MediaError,
2184 MoonError (MoonError::EXCEPTION, 3004, "Invalid ASX element")));
2185 break;
2188 if (current_text != NULL) {
2189 g_free (current_text);
2190 current_text = NULL;
2193 switch (GetCurrentKind ()) {
2194 case PlaylistKind::Entry:
2195 EndEntry ();
2196 break;
2197 default:
2198 break;
2200 PopCurrentKind ();
2203 void
2204 PlaylistParser::OnASXText (const char *text, int len)
2206 char *a = g_strndup (text, len);
2208 #if DEBUG
2209 char *p = g_strndup (text, len);
2210 for (int i = 0; p [i] != 0; i++)
2211 if (p [i] == 10 || p [i] == 13)
2212 p [i] = ' ';
2214 LOG_PLAYLIST ("PlaylistParser::OnText (%s, %d)\n", p, len);
2215 g_free (p);
2216 #endif
2218 if (current_text == NULL) {
2219 current_text = a;
2220 } else {
2221 char *b = g_strconcat (current_text, a, NULL);
2222 g_free (current_text);
2223 current_text = b;
2227 bool
2228 PlaylistParser::Is (IMediaSource *source, const char *asx_header)
2230 bool result = false;
2231 int asx_header_length = strlen (asx_header);
2232 unsigned char buffer [20];
2234 do {
2235 result = source->Peek ((guint8 *) buffer, asx_header_length);
2236 if (!result)
2237 goto cleanup;
2239 // skip any whitespace
2240 unsigned char c = buffer [0];
2241 switch (c) {
2242 case ' ':
2243 case '\t':
2244 case 10:
2245 case 13: {
2246 result = source->ReadAll ((guint8 *) buffer, 1);
2247 if (!result)
2248 goto cleanup;
2249 continue;
2251 case 0xef: {
2252 if (buffer [1] == 0xbb && buffer [2] == 0xbf) { // UTF-8 BOM: EF BB BF
2253 result = source->ReadAll ((guint8 *) buffer, 3);
2254 if (!result)
2255 goto cleanup;
2256 continue;
2258 // TODO: there might be other BOMs we should handle too
2259 // fall through
2261 default:
2262 result = !g_ascii_strncasecmp ((const char *) buffer, asx_header, asx_header_length);
2263 goto cleanup;
2265 } while (true);
2267 cleanup:
2269 source->Seek (0, SEEK_SET);
2271 return result;
2274 bool
2275 PlaylistParser::IsASX3 (IMediaSource *source)
2277 return Is (source, "<ASX");
2280 bool
2281 PlaylistParser::IsASX2 (IMediaSource *source)
2283 return Is (source, "[Reference]");
2286 bool
2287 PlaylistParser::ParseASX2 ()
2289 const int BUFFER_SIZE = 1024;
2290 int bytes_read;
2291 char buffer[BUFFER_SIZE];
2292 char *ref;
2293 char *mms_uri;
2294 GKeyFile *key_file;
2295 Uri *uri;
2297 playlist_version = 2;
2299 bytes_read = source->ReadSome (buffer, BUFFER_SIZE);
2300 if (bytes_read < 0) {
2301 LOG_PLAYLIST_WARN ("Could not read asx document for parsing.\n");
2302 return false;
2305 key_file = g_key_file_new ();
2306 if (!g_key_file_load_from_data (key_file, buffer, bytes_read,
2307 G_KEY_FILE_NONE, NULL)) {
2308 LOG_PLAYLIST_WARN ("Invalid asx2 document.\n");
2309 g_key_file_free (key_file);
2310 return false;
2313 ref = g_key_file_get_value (key_file, "Reference", "Ref1", NULL);
2314 if (ref == NULL) {
2315 LOG_PLAYLIST_WARN ("Could not find Ref1 entry in asx2 document.\n");
2316 g_key_file_free (key_file);
2317 return false;
2320 if (!g_str_has_prefix (ref, "http://") || !g_str_has_suffix (ref, "MSWMExt=.asf")) {
2321 LOG_PLAYLIST_WARN ("Could not find a valid uri within Ref1 entry in asx2 document.\n");
2322 g_free (ref);
2323 g_key_file_free (key_file);
2324 return false;
2327 mms_uri = g_strdup_printf ("mms://%s", strstr (ref, "http://") + strlen ("http://"));
2328 g_free (ref);
2329 g_key_file_free (key_file);
2332 playlist = new Playlist (root, source);
2334 PlaylistEntry *entry = new PlaylistEntry (playlist);
2335 uri = new Uri ();
2336 if (uri->Parse (mms_uri)) {
2337 entry->SetSourceName (uri);
2338 } else {
2339 delete uri;
2341 playlist->AddEntry (entry);
2342 current_entry = entry;
2344 return true;
2347 bool
2348 PlaylistParser::TryFixError (gint8 *current_buffer, int bytes_read)
2350 Media *media;
2352 if (XML_GetErrorCode (internal->parser) != XML_ERROR_INVALID_TOKEN)
2353 return false;
2355 int index = XML_GetErrorByteIndex (internal->parser);
2357 if (index > bytes_read)
2358 return false;
2360 LOG_PLAYLIST ("Attempting to fix invalid token error index: %d\n", index);
2362 // OK, so we are going to guess that we are in an attribute here and walk back
2363 // until we hit a control char that should be escaped.
2364 char * escape = NULL;
2365 while (index >= 0) {
2366 switch (current_buffer [index]) {
2367 case '&':
2368 escape = g_strdup ("&amp;");
2369 break;
2370 case '<':
2371 escape = g_strdup ("&lt;");
2372 break;
2373 case '>':
2374 escape = g_strdup ("&gt;");
2375 break;
2376 case '\"':
2377 break;
2379 if (escape)
2380 break;
2381 index--;
2384 if (!escape) {
2385 LOG_PLAYLIST_WARN ("Unable to find an invalid escape character to fix in ASX: %s.\n", current_buffer);
2386 g_free (escape);
2387 return false;
2390 int escape_len = strlen (escape);
2391 int new_size = source->GetSize () + escape_len - 1;
2392 int patched_size = internal->bytes_read + bytes_read + escape_len - 1;
2393 gint8 * new_buffer = (gint8 *) g_malloc (new_size);
2395 source->Seek (0, SEEK_SET);
2396 source->ReadSome (new_buffer, internal->bytes_read);
2398 memcpy (new_buffer + internal->bytes_read, current_buffer, index);
2399 memcpy (new_buffer + internal->bytes_read + index, escape, escape_len);
2400 memcpy (new_buffer + internal->bytes_read + index + escape_len, current_buffer + index + 1, bytes_read - index - 1);
2402 source->Seek (internal->bytes_read + bytes_read, SEEK_SET);
2403 source->ReadSome (new_buffer + patched_size, new_size - patched_size);
2405 media = source->GetMediaReffed ();
2407 MemorySource *reparse_source = new MemorySource (media, new_buffer, new_size);
2408 SetSource (reparse_source);
2409 reparse_source->unref ();
2411 internal->reparse = true;
2413 if (error_args) {
2414 // Clear out errors in the old buffer
2415 error_args->unref ();
2416 error_args = NULL;
2420 g_free (escape);
2422 if (media)
2423 media->unref ();
2425 return true;
2428 MediaResult
2429 PlaylistParser::Parse ()
2431 bool result;
2432 gint64 last_available_pos;
2433 gint64 size;
2435 LOG_PLAYLIST ("PlaylistParser::Parse ()\n");
2437 do {
2438 // Don't try to parse anything until we have all the data.
2439 if (internal != NULL)
2440 internal->reparse = false;
2441 size = source->GetSize ();
2442 last_available_pos = source->GetLastAvailablePosition ();
2443 if (size != -1 && last_available_pos != -1 && size != last_available_pos)
2444 return MEDIA_NOT_ENOUGH_DATA;
2446 if (this->IsASX2 (source)) {
2447 /* Parse as a asx2 mms file */
2448 Setup (XML_TYPE_NONE);
2449 result = this->ParseASX2 ();
2450 } else if (this->IsASX3 (source)) {
2451 Setup (XML_TYPE_ASX3);
2452 result = this->ParseASX3 ();
2453 } else {
2454 result = false;
2456 } while (result && internal->reparse);
2458 return result ? MEDIA_SUCCESS : MEDIA_FAIL;
2461 bool
2462 PlaylistParser::ParseASX3 ()
2464 int bytes_read;
2465 void *buffer;
2467 // asx documents don't tend to be very big, so there's no need for a big buffer
2468 const int BUFFER_SIZE = 1024;
2470 for (;;) {
2471 buffer = XML_GetBuffer(internal->parser, BUFFER_SIZE);
2472 if (buffer == NULL) {
2473 fprintf (stderr, "Could not allocate memory for asx document parsing.\n");
2474 return false;
2477 bytes_read = source->ReadSome (buffer, BUFFER_SIZE);
2478 if (bytes_read < 0) {
2479 fprintf (stderr, "Could not read asx document for parsing.\n");
2480 return false;
2483 if (!XML_ParseBuffer (internal->parser, bytes_read, bytes_read == 0)) {
2484 if (error_args != NULL)
2485 return false;
2487 switch (XML_GetErrorCode (internal->parser)) {
2488 case XML_ERROR_NO_ELEMENTS:
2489 ParsingError (new ErrorEventArgs (MediaError,
2490 MoonError (MoonError::EXCEPTION, 7000, "unexpected end of input")));
2491 return false;
2492 case XML_ERROR_DUPLICATE_ATTRIBUTE:
2493 ParsingError (new ErrorEventArgs (MediaError,
2494 MoonError (MoonError::EXCEPTION, 7031, "wfc: unique attribute spec")));
2495 return false;
2496 case XML_ERROR_INVALID_TOKEN:
2497 // save error args in case the error fixing fails (in which case we want this error, not the error the error fixing caused)
2498 error_args = new ErrorEventArgs (MediaError,
2499 MoonError (MoonError::EXCEPTION, 7007, "quote expected"));
2500 if (TryFixError ((gint8 *) buffer, bytes_read))
2501 return true;
2502 // fall through
2503 default:
2504 char *msg = g_strdup_printf ("%s %d (%d, %d)",
2505 XML_ErrorString (XML_GetErrorCode (internal->parser)), (int) XML_GetErrorCode (internal->parser),
2506 (int) XML_GetCurrentLineNumber (internal->parser), (int) XML_GetCurrentColumnNumber (internal->parser));
2507 ParsingError (new ErrorEventArgs (MediaError,
2508 MoonError (MoonError::EXCEPTION, 3000, msg)));
2509 g_free (msg);
2510 return false;
2514 if (bytes_read == 0)
2515 break;
2517 internal->bytes_read += bytes_read;
2520 return playlist != NULL;
2523 PlaylistEntry *
2524 PlaylistParser::GetCurrentContent ()
2526 if (current_entry != NULL)
2527 return current_entry;
2529 return playlist;
2532 PlaylistEntry *
2533 PlaylistParser::GetCurrentEntry ()
2535 return current_entry;
2538 void
2539 PlaylistParser::EndEntry ()
2541 this->current_entry = NULL;
2544 void
2545 PlaylistParser::PushCurrentKind (PlaylistKind::Kind kind)
2547 kind_stack->Append (new KindNode (kind));
2548 LOG_PLAYLIST ("PlaylistParser::Push (%d)\n", kind);
2551 void
2552 PlaylistParser::PopCurrentKind ()
2554 LOG_PLAYLIST ("PlaylistParser::PopCurrentKind (), current: %d\n", ((KindNode *)kind_stack->Last ())->kind);
2555 kind_stack->Remove (kind_stack->Last ());
2558 PlaylistKind::Kind
2559 PlaylistParser::GetCurrentKind ()
2561 KindNode *node = (KindNode *) kind_stack->Last ();
2562 return node->kind;
2565 PlaylistKind::Kind
2566 PlaylistParser::GetParentKind ()
2568 KindNode *node = (KindNode *) kind_stack->Last ()->prev;
2569 return node->kind;
2572 bool
2573 PlaylistParser::AssertParentKind (int kind)
2575 LOG_PLAYLIST ("PlaylistParser::AssertParentKind (%d), GetParentKind: %d, result: %d\n", kind, GetParentKind (), GetParentKind () & kind);
2577 if (GetParentKind () & kind)
2578 return true;
2580 ParsingError (new ErrorEventArgs (MediaError,
2581 MoonError (MoonError::EXCEPTION, 3008, "ASX parse error")));
2583 return false;
2586 void
2587 PlaylistParser::ParsingError (ErrorEventArgs *args)
2589 LOG_PLAYLIST ("PlaylistParser::ParsingError (%s)\n", args->GetErrorMessage());
2591 XML_StopParser (internal->parser, false);
2592 if (error_args) {
2593 if (args)
2594 args->unref ();
2595 return; // don't overwrite any previous errors.
2597 error_args = args; // don't ref, this method is called like this: ParsingError (new ErrorEventArgs (...));, so the caller gives us the ref he has
2601 PlaylistKind PlaylistParser::playlist_kinds [] = {
2602 /* ASX3 */
2603 PlaylistKind ("ABSTRACT", PlaylistKind::Abstract),
2604 PlaylistKind ("ASX", PlaylistKind::Asx),
2605 PlaylistKind ("ROOT", PlaylistKind::Root),
2606 PlaylistKind ("AUTHOR", PlaylistKind::Author),
2607 PlaylistKind ("BANNER", PlaylistKind::Banner),
2608 PlaylistKind ("BASE", PlaylistKind::Base),
2609 PlaylistKind ("COPYRIGHT", PlaylistKind::Copyright),
2610 PlaylistKind ("DURATION", PlaylistKind::Duration),
2611 PlaylistKind ("ENTRY", PlaylistKind::Entry),
2612 PlaylistKind ("ENTRYREF", PlaylistKind::EntryRef),
2613 PlaylistKind ("LOGURL", PlaylistKind::LogUrl),
2614 PlaylistKind ("MOREINFO", PlaylistKind::MoreInfo),
2615 PlaylistKind ("REF", PlaylistKind::Ref),
2616 PlaylistKind ("STARTTIME", PlaylistKind::StartTime),
2617 PlaylistKind ("TITLE", PlaylistKind::Title),
2618 PlaylistKind ("STARTMARKER", PlaylistKind::StartMarker),
2619 PlaylistKind ("REPEAT", PlaylistKind::Repeat),
2620 PlaylistKind ("ENDMARKER", PlaylistKind::EndMarker),
2621 PlaylistKind ("PARAM", PlaylistKind::Param),
2622 PlaylistKind ("EVENT", PlaylistKind::Event),
2624 PlaylistKind (NULL, PlaylistKind::Unknown)
2627 PlaylistKind::Kind
2628 PlaylistParser::StringToKind (const char *str)
2630 PlaylistKind::Kind kind = PlaylistKind::Unknown;
2632 for (int i = 0; playlist_kinds [i].str != NULL; i++) {
2633 if (str_match (str, playlist_kinds [i].str)) {
2634 kind = playlist_kinds [i].kind;
2635 break;
2639 LOG_PLAYLIST ("PlaylistParser::StringToKind ('%s') = %d\n", str, kind);
2641 return kind;
2644 const char *
2645 PlaylistParser::KindToString (PlaylistKind::Kind kind)
2647 const char *result = NULL;
2649 for (int i = 0; playlist_kinds [i].str != NULL; i++) {
2650 if (playlist_kinds [i].kind == kind) {
2651 result = playlist_kinds [i].str;
2652 break;
2656 LOG_PLAYLIST ("PlaylistParser::KindToString (%d) = '%s'\n", kind, result);
2658 return result;