diff --git a/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp b/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp index 4b1cafd7d0..9279fe19ee 100644 --- a/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp +++ b/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp @@ -190,7 +190,13 @@ status_t FilePlaylistItem::SetAttribute(const Attribute& attribute, const int64& value) { - return B_NOT_SUPPORTED; + switch (attribute) { + case ATTR_INT64_DURATION: + return _SetAttribute("Media:Length", B_INT64_TYPE, &value, + sizeof(int64)); + default: + return B_NOT_SUPPORTED; + } } @@ -198,7 +204,13 @@ status_t FilePlaylistItem::GetAttribute(const Attribute& attribute, int64& value) const { - return B_NOT_SUPPORTED; + switch (attribute) { + case ATTR_INT64_DURATION: + return _GetAttribute("Media:Length", B_INT64_TYPE, &value, + sizeof(int64)); + default: + return B_NOT_SUPPORTED; + } } @@ -407,7 +419,7 @@ FilePlaylistItem::_SetAttribute(const char* attrName, type_code type, status_t FilePlaylistItem::_GetAttribute(const char* attrName, type_code type, - void* data, size_t size) + void* data, size_t size) const { BEntry entry(&fRefs[0], true); BNode node(&entry); diff --git a/src/apps/mediaplayer/playlist/FilePlaylistItem.h b/src/apps/mediaplayer/playlist/FilePlaylistItem.h index a0c4da42fb..fb72f9e802 100644 --- a/src/apps/mediaplayer/playlist/FilePlaylistItem.h +++ b/src/apps/mediaplayer/playlist/FilePlaylistItem.h @@ -67,7 +67,7 @@ private: size_t size); status_t _GetAttribute(const char* attrName, type_code type, void* data, - size_t size); + size_t size) const; status_t _MoveIntoTrash(vector* refs, vector* namesInTrash); status_t _RestoreFromTrash(vector* refs, diff --git a/src/apps/mediaplayer/playlist/PlaylistItem.h b/src/apps/mediaplayer/playlist/PlaylistItem.h index b896df5b77..73de81aa31 100644 --- a/src/apps/mediaplayer/playlist/PlaylistItem.h +++ b/src/apps/mediaplayer/playlist/PlaylistItem.h @@ -49,11 +49,12 @@ public: ATTR_STRING_TITLE = 'titl', ATTR_STRING_AUDIO_BITRATE = 'abtr', ATTR_STRING_VIDEO_BITRATE = 'vbtr', - ATTR_STRING_DURATION = 'drtn', ATTR_INT32_TRACK = 'trck', ATTR_INT32_YEAR = 'year', - ATTR_INT32_RATING = 'rtng' + ATTR_INT32_RATING = 'rtng', + + ATTR_INT64_DURATION = 'drtn' } Attribute; virtual status_t SetAttribute(const Attribute& attribute, diff --git a/src/apps/mediaplayer/playlist/PlaylistWindow.cpp b/src/apps/mediaplayer/playlist/PlaylistWindow.cpp index 08ce627449..fbea517e45 100644 --- a/src/apps/mediaplayer/playlist/PlaylistWindow.cpp +++ b/src/apps/mediaplayer/playlist/PlaylistWindow.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,12 +33,16 @@ #include #include #include +#include +#include "AudioTrackSupplier.h" #include "CommandStack.h" +#include "DurationToString.h" #include "MainApp.h" #include "PlaylistListView.h" #include "RWLocker.h" - +#include "TrackSupplier.h" +#include "VideoTrackSupplier.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "MediaPlayer-PlaylistWindow" @@ -82,7 +88,8 @@ PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist, fPlaylist(playlist), fLocker(new RWLocker("command stack lock")), fCommandStack(new CommandStack(fLocker)), - fCommandStackListener(this) + fCommandStackListener(this), + fDurationListener(new DurationListener(*this)) { frame = Bounds(); @@ -90,6 +97,7 @@ PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist, // will adjust frame to account for menubar frame.right -= B_V_SCROLL_BAR_WIDTH; + frame.bottom -= B_H_SCROLL_BAR_HEIGHT; fListView = new PlaylistListView(frame, playlist, controller, fCommandStack); @@ -104,7 +112,23 @@ PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist, // make it so the frame of the menubar is also the frame of // the scroll bar (appears to be) scrollBar->MoveBy(0, -1); - scrollBar->ResizeBy(0, -(B_H_SCROLL_BAR_HEIGHT - 2)); + scrollBar->ResizeBy(0, 2); + } + + frame.top += frame.Height(); + frame.bottom += B_H_SCROLL_BAR_HEIGHT; + + fTotalDuration = new BStringView(frame, "fDuration", "", + B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT); + AddChild(fTotalDuration); + + _UpdateTotalDuration(0); + + { + BAutolock _(fPlaylist); + + _QueryInitialDurations(); + fPlaylist->AddListener(fDurationListener); } fCommandStack->AddListener(&fCommandStackListener); @@ -121,6 +145,9 @@ PlaylistWindow::~PlaylistWindow() fCommandStack->RemoveListener(&fCommandStackListener); delete fCommandStack; delete fLocker; + + fPlaylist->RemoveListener(fDurationListener); + BMessenger(fDurationListener).SendMessage(B_QUIT_REQUESTED); } @@ -422,3 +449,157 @@ PlaylistWindow::_SavePlaylist(BEntry& origEntry, BEntry& tempEntry, info.SetType("application/x-vnd.haiku-playlist"); } + +void +PlaylistWindow::_QueryInitialDurations() +{ + BAutolock lock(fPlaylist); + + BMessage addMessage(MSG_PLAYLIST_ITEM_ADDED); + for (int32 i = 0; i < fPlaylist->CountItems(); i++) { + addMessage.AddPointer("item", fPlaylist->ItemAt(i)); + addMessage.AddInt32("index", i); + } + + BMessenger(fDurationListener).SendMessage(&addMessage); +} + + +void +PlaylistWindow::_UpdateTotalDuration(bigtime_t duration) +{ + BAutolock lock(this); + + char buffer[64]; + duration /= 1000000; + duration_to_string(duration, buffer, sizeof(buffer)); + + BString text; + text.SetToFormat(B_TRANSLATE("Total duration : %s"), buffer); + + fTotalDuration->SetText(text.String()); +} + + +// #pragma mark - + + +PlaylistWindow::DurationListener::DurationListener(PlaylistWindow& parent) + : + PlaylistObserver(this), + fKnown(20, true), + fTotalDuration(0), + fParent(parent) +{ + Run(); +} + + +PlaylistWindow::DurationListener::~DurationListener() +{ +} + + +void +PlaylistWindow::DurationListener::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_PLAYLIST_ITEM_ADDED: + { + void* item; + int32 index; + + int32 currentItem = 0; + while (message->FindPointer("item", currentItem, &item) == B_OK + && message->FindInt32("index", currentItem, &index) == B_OK) { + _HandleItemAdded(static_cast(item), index); + ++currentItem; + } + + break; + } + + case MSG_PLAYLIST_ITEM_REMOVED: + { + int32 index; + + if (message->FindInt32("index", &index) == B_OK) { + _HandleItemRemoved(index); + } + + break; + } + + default: + BLooper::MessageReceived(message); + break; + } +} + + +bigtime_t +PlaylistWindow::DurationListener::TotalDuration() +{ + return fTotalDuration; +} + + +void +PlaylistWindow::DurationListener::_HandleItemAdded(PlaylistItem* item, + int32 index) +{ + bigtime_t duration = _DetermineItemDuration(item); + fTotalDuration += duration; + fParent._UpdateTotalDuration(fTotalDuration); + fKnown.AddItem(new bigtime_t(duration), index); +} + + +void +PlaylistWindow::DurationListener::_HandleItemRemoved(int32 index) +{ + bigtime_t* deleted = fKnown.RemoveItemAt(index); + fTotalDuration -= *deleted; + fParent._UpdateTotalDuration(fTotalDuration); + + delete deleted; +} + + +bigtime_t +PlaylistWindow::DurationListener::_DetermineItemDuration(PlaylistItem* item) +{ + bigtime_t duration; + if (item->GetAttribute(PlaylistItem::ATTR_INT64_DURATION, duration) == B_OK) + return duration; + + // We have to find out the duration ourselves + if (FilePlaylistItem* file = dynamic_cast(item)) { + // We are dealing with a file + BMediaFile mediaFile(&file->Ref()); + + if (mediaFile.InitCheck() != B_OK || mediaFile.CountTracks() < 1) + return 0; + + duration = mediaFile.TrackAt(0)->Duration(); + } else { + // Not a file, so fall back to the generic TrackSupplier solution + TrackSupplier* supplier = item->CreateTrackSupplier(); + + AudioTrackSupplier* au = supplier->CreateAudioTrackForIndex(0); + VideoTrackSupplier* vi = supplier->CreateVideoTrackForIndex(0); + + duration = max_c(au == NULL ? 0 : au->Duration(), + vi == NULL ? 0 : vi->Duration()); + + delete vi; + delete au; + delete supplier; + } + + // Store the duration for later use + item->SetAttribute(PlaylistItem::ATTR_INT64_DURATION, duration); + + return duration; +} + diff --git a/src/apps/mediaplayer/playlist/PlaylistWindow.h b/src/apps/mediaplayer/playlist/PlaylistWindow.h index 2372385d91..6b70e76c4e 100644 --- a/src/apps/mediaplayer/playlist/PlaylistWindow.h +++ b/src/apps/mediaplayer/playlist/PlaylistWindow.h @@ -11,8 +11,10 @@ #include +#include #include +#include "PlaylistObserver.h" #include "ListenerAdapter.h" @@ -21,11 +23,11 @@ class BMenuItem; class CommandStack; class Controller; class Notifier; -class Playlist; class PlaylistListView; class RWLocker; class BButton; class BFilePanel; +class BStringView; enum { @@ -53,12 +55,39 @@ public: virtual void MessageReceived(BMessage* message); private: + + class DurationListener : public PlaylistObserver, public BLooper { + public: + + DurationListener(PlaylistWindow& parent); + ~DurationListener(); + + void MessageReceived(BMessage* message); + + bigtime_t TotalDuration(); + + private: + void _HandleItemAdded(PlaylistItem* item, + int32 index); + void _HandleItemRemoved(int32 index); + bigtime_t _DetermineItemDuration(PlaylistItem* item); + + BObjectList + fKnown; + bigtime_t fTotalDuration; + PlaylistWindow& fParent; + }; + + friend class DurationListener; + void _CreateMenu(BRect& frame); void _ObjectChanged(const Notifier* object); void _SavePlaylist(const BMessage* filePanelMessage); void _SavePlaylist(const entry_ref& ref); void _SavePlaylist(BEntry& origEntry, BEntry& tempEntry, const char* finalName); + void _QueryInitialDurations(); + void _UpdateTotalDuration(bigtime_t duration); Playlist* fPlaylist; PlaylistListView* fListView; @@ -72,6 +101,9 @@ private: ListenerAdapter fCommandStackListener; entry_ref fSavedPlaylistRef; + + DurationListener* fDurationListener; + BStringView* fTotalDuration; };