Author: stippi Date: 2010-09-27 17:26:41 +0200 (Mon, 27 Sep 2010) New Revision: 38827 Changeset: http://dev.haiku-os.org/changeset/38827 Added: haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.cpp haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.h haiku/trunk/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp haiku/trunk/src/apps/mediaplayer/supplier/SubTitlesSRT.h haiku/trunk/src/apps/mediaplayer/support/StackBlurFilter.cpp haiku/trunk/src/apps/mediaplayer/support/StackBlurFilter.h Modified: haiku/trunk/src/apps/mediaplayer/Controller.cpp haiku/trunk/src/apps/mediaplayer/Controller.h haiku/trunk/src/apps/mediaplayer/ControllerObserver.cpp haiku/trunk/src/apps/mediaplayer/ControllerObserver.h haiku/trunk/src/apps/mediaplayer/Jamfile haiku/trunk/src/apps/mediaplayer/MainWin.cpp haiku/trunk/src/apps/mediaplayer/MainWin.h haiku/trunk/src/apps/mediaplayer/VideoView.cpp haiku/trunk/src/apps/mediaplayer/VideoView.h haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.cpp haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.h haiku/trunk/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h haiku/trunk/src/apps/mediaplayer/supplier/TrackSupplier.h haiku/trunk/src/apps/mediaplayer/support/FileReadWrite.cpp Log: * Added basic support for SRT subtitle files. It only works if the SRT file is placed alongside the current playlist item under the same name (sans extension). The name of the language is taken from the file which needs to be separated by a dot (should be improved). * Instead of the black outline, subtitles have a nice drop shadow now, which is easier on the eyes somehow. Modified: haiku/trunk/src/apps/mediaplayer/Controller.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/Controller.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/Controller.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -47,6 +47,7 @@ #include "ProxyAudioSupplier.h" #include "ProxyVideoSupplier.h" #include "TrackSupplier.h" +#include "SubTitles.h" #include "VideoTrackSupplier.h" using std::nothrow; @@ -72,6 +73,7 @@ void Controller::Listener::FileChanged(PlaylistItem* item, status_t result) {} void Controller::Listener::VideoTrackChanged(int32) {} void Controller::Listener::AudioTrackChanged(int32) {} +void Controller::Listener::SubTitleTrackChanged(int32) {} void Controller::Listener::VideoStatsChanged() {} void Controller::Listener::AudioStatsChanged() {} void Controller::Listener::PlaybackStateChanged(uint32) {} @@ -104,6 +106,8 @@ fAudioSupplier(new ProxyAudioSupplier(this)), fVideoTrackSupplier(NULL), fAudioTrackSupplier(NULL), + fSubTitles(NULL), + fSubTitlesIndex(-1), fCurrentFrame(0), fDuration(0), @@ -248,6 +252,8 @@ fVideoTrackSupplier = NULL; fAudioTrackSupplier = NULL; + fSubTitles = NULL; + fSubTitlesIndex = -1; fCurrentFrame = 0; fDuration = 0; @@ -433,6 +439,17 @@ } +int +Controller::SubTitleTrackCount() +{ + BAutolock _(this); + + if (fTrackSupplier != NULL) + return fTrackSupplier->CountSubTitleTracks(); + return 0; +} + + status_t Controller::SelectAudioTrack(int n) { @@ -527,6 +544,58 @@ } +status_t +Controller::SelectSubTitleTrack(int n) +{ + BAutolock _(this); + + if (fTrackSupplier == NULL) + return B_NO_INIT; + + fSubTitlesIndex = n; + fSubTitles = fTrackSupplier->SubTitleTrackForIndex(n); + + const SubTitle* subTitle = NULL; + if (fSubTitles != NULL) + subTitle = fSubTitles->SubTitleAt(_TimePosition()); + if (subTitle != NULL) + fVideoView->SetSubTitle(subTitle->text.String()); + else + fVideoView->SetSubTitle(NULL); + + _NotifySubTitleTrackChanged(n); + return B_OK; +} + + +int +Controller::CurrentSubTitleTrack() +{ + BAutolock _(this); + + if (fSubTitles == NULL) + return -1; + + return fSubTitlesIndex; +} + + +const char* +Controller::SubTitleTrackName(int n) +{ + BAutolock _(this); + + if (fTrackSupplier == NULL) + return NULL; + + const SubTitles* subTitles = fTrackSupplier->SubTitleTrackForIndex(n); + if (subTitles == NULL) + return NULL; + + return subTitles->Name(); +} + + // #pragma mark - @@ -1015,6 +1084,18 @@ void +Controller::_NotifySubTitleTrackChanged(int32 index) const +{ + BList listeners(fListeners); + int32 count = listeners.CountItems(); + for (int32 i = 0; i < count; i++) { + Listener* listener = (Listener*)listeners.ItemAtFast(i); + listener->SubTitleTrackChanged(index); + } +} + + +void Controller::_NotifyVideoStatsChanged() const { BList listeners(fListeners); @@ -1136,7 +1217,16 @@ Controller::NotifyCurrentFrameChanged(int64 frame) const { fCurrentFrame = frame; - _NotifyPositionChanged((float)_TimePosition() / fDuration); + bigtime_t timePosition = _TimePosition(); + _NotifyPositionChanged((float)timePosition / fDuration); + + if (fSubTitles != NULL) { + const SubTitle* subTitle = fSubTitles->SubTitleAt(timePosition); + if (subTitle != NULL) + fVideoView->SetSubTitle(subTitle->text.String()); + else + fVideoView->SetSubTitle(NULL); + } } Modified: haiku/trunk/src/apps/mediaplayer/Controller.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/Controller.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/Controller.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -40,6 +40,7 @@ class PlaylistItem; class ProxyAudioSupplier; class ProxyVideoSupplier; +class SubTitles; class VideoTrackSupplier; class VideoView; @@ -57,6 +58,7 @@ virtual void VideoTrackChanged(int32 index); virtual void AudioTrackChanged(int32 index); + virtual void SubTitleTrackChanged(int32 index); virtual void VideoStatsChanged(); virtual void AudioStatsChanged(); @@ -93,12 +95,16 @@ int AudioTrackCount(); int VideoTrackCount(); + int SubTitleTrackCount(); status_t SelectAudioTrack(int n); int CurrentAudioTrack(); int AudioTrackChannelCount(); status_t SelectVideoTrack(int n); int CurrentVideoTrack(); + status_t SelectSubTitleTrack(int n); + int CurrentSubTitleTrack(); + const char* SubTitleTrackName(int n); void Stop(); void Play(); @@ -158,6 +164,7 @@ void _NotifyFileFinished() const; void _NotifyVideoTrackChanged(int32 index) const; void _NotifyAudioTrackChanged(int32 index) const; + void _NotifySubTitleTrackChanged(int32 index) const; void _NotifyVideoStatsChanged() const; void _NotifyAudioStatsChanged() const; @@ -196,6 +203,8 @@ ProxyAudioSupplier* fAudioSupplier; VideoTrackSupplier* fVideoTrackSupplier; AudioTrackSupplier* fAudioTrackSupplier; + const SubTitles* fSubTitles; + int32 fSubTitlesIndex; mutable int64 fCurrentFrame; bigtime_t fDuration; Modified: haiku/trunk/src/apps/mediaplayer/ControllerObserver.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/ControllerObserver.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/ControllerObserver.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -84,6 +84,19 @@ void +ControllerObserver::SubTitleTrackChanged(int32 index) +{ + if (!(fObserveFlags & OBSERVE_TRACK_CHANGES)) + return; + + BMessage message(MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED); + message.AddInt32("index", index); + + DeliverMessage(message); +} + + +void ControllerObserver::VideoStatsChanged() { if (!(fObserveFlags & OBSERVE_STAT_CHANGES)) Modified: haiku/trunk/src/apps/mediaplayer/ControllerObserver.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/ControllerObserver.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/ControllerObserver.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -19,6 +19,7 @@ MSG_CONTROLLER_VIDEO_TRACK_CHANGED = 'cnvt', MSG_CONTROLLER_AUDIO_TRACK_CHANGED = 'cnat', + MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED = 'cnst', MSG_CONTROLLER_VIDEO_STATS_CHANGED = 'cnvs', MSG_CONTROLLER_AUDIO_STATS_CHANGED = 'cnas', @@ -55,6 +56,7 @@ virtual void VideoTrackChanged(int32 index); virtual void AudioTrackChanged(int32 index); + virtual void SubTitleTrackChanged(int32 index); virtual void VideoStatsChanged(); virtual void AudioStatsChanged(); Modified: haiku/trunk/src/apps/mediaplayer/Jamfile =================================================================== --- haiku/trunk/src/apps/mediaplayer/Jamfile 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/Jamfile 2010-09-27 15:26:41 UTC (rev 38827) @@ -3,6 +3,7 @@ # for BRecentItems UsePublicHeaders [ FDirName be_apps Tracker ] ; UsePrivateHeaders interface shared ; +UseLibraryHeaders agg ; # source directories local sourceDirs = @@ -82,6 +83,8 @@ ProxyAudioSupplier.cpp ProxyVideoSupplier.cpp TrackSupplier.cpp + SubTitles.cpp + SubTitlesSRT.cpp VideoTrackSupplier.cpp # support @@ -99,6 +102,7 @@ Notifier.cpp RWLocker.cpp SettingsMessage.cpp + StackBlurFilter.cpp # . Controller.cpp Modified: haiku/trunk/src/apps/mediaplayer/MainWin.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/MainWin.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/MainWin.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -95,10 +95,12 @@ M_ASPECT_37_20, M_ASPECT_47_20, - M_SELECT_AUDIO_TRACK = 0x00000800, - M_SELECT_AUDIO_TRACK_END = 0x00000fff, - M_SELECT_VIDEO_TRACK = 0x00010000, - M_SELECT_VIDEO_TRACK_END = 0x000fffff, + M_SELECT_AUDIO_TRACK = 0x00000800, + M_SELECT_AUDIO_TRACK_END = 0x00000fff, + M_SELECT_VIDEO_TRACK = 0x00010000, + M_SELECT_VIDEO_TRACK_END = 0x00010fff, + M_SELECT_SUB_TITLE_TRACK = 0x00020000, + M_SELECT_SUB_TITLE_TRACK_END = 0x00020fff, M_SET_RATING, @@ -680,6 +682,22 @@ } break; } + case MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED: + { + int32 index; + if (msg->FindInt32("index", &index) == B_OK) { + int32 i = 0; + while (BMenuItem* item = fSubTitleTrackMenu->ItemAt(i)) { + BMessage* message = item->Message(); + if (message != NULL) { + item->SetMarked((int32)message->what + - M_SELECT_SUB_TITLE_TRACK == index); + } + i++; + } + } + break; + } case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: { uint32 state; @@ -971,6 +989,12 @@ fController->SelectVideoTrack(msg->what - M_SELECT_VIDEO_TRACK); break; } + if ((int32)msg->what >= M_SELECT_SUB_TITLE_TRACK - 1 + && msg->what <= M_SELECT_SUB_TITLE_TRACK_END) { + fController->SelectSubTitleTrack((int32)msg->what + - M_SELECT_SUB_TITLE_TRACK); + break; + } // let BWindow handle the rest BWindow::MessageReceived(msg); } @@ -1329,7 +1353,7 @@ { // printf("MainWin::_SetupWindow\n"); // Populate the track menus - _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu); + _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu, fSubTitleTrackMenu); _UpdateAudioChannelCount(fController->CurrentAudioTrack()); fVideoMenu->SetEnabled(fHasVideo); @@ -1382,6 +1406,7 @@ fVideoAspectMenu = new BMenu("Aspect ratio"); fAudioTrackMenu = new BMenu("Track"); fVideoTrackMenu = new BMenu("Track"); + fSubTitleTrackMenu = new BMenu("Subtitles"); fAttributesMenu = new BMenu("Attributes"); fMenuBar->AddItem(fFileMenu); @@ -1446,6 +1471,7 @@ fAudioMenu->AddItem(fAudioTrackMenu); fVideoMenu->AddItem(fVideoTrackMenu); + fVideoMenu->AddItem(fSubTitleTrackMenu); fVideoMenu->AddSeparatorItem(); BMessage* resizeMessage = new BMessage(M_VIEW_SIZE); resizeMessage->AddInt32("size", 50); @@ -1544,10 +1570,12 @@ void -MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu) +MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu, + BMenu* subTitleTrackMenu) { audioTrackMenu->RemoveItems(0, audioTrackMenu->CountItems(), true); videoTrackMenu->RemoveItems(0, videoTrackMenu->CountItems(), true); + subTitleTrackMenu->RemoveItems(0, subTitleTrackMenu->CountItems(), true); char s[100]; @@ -1590,6 +1618,33 @@ videoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); videoTrackMenu->ItemAt(0)->SetMarked(true); } + + count = fController->SubTitleTrackCount(); + if (count > 0) { + current = fController->CurrentSubTitleTrack(); + BMenuItem* item = new BMenuItem("Off", + new BMessage(M_SELECT_SUB_TITLE_TRACK - 1)); + subTitleTrackMenu->AddItem(item); + item->SetMarked(current == -1); + + subTitleTrackMenu->AddSeparatorItem(); + + for (int i = 0; i < count; i++) { + const char* name = fController->SubTitleTrackName(i); + if (name != NULL) + snprintf(s, sizeof(s), "%s", name); + else + snprintf(s, sizeof(s), "Track %d", i + 1); + item = new BMenuItem(s, + new BMessage(M_SELECT_SUB_TITLE_TRACK + i)); + item->SetMarked(i == current); + subTitleTrackMenu->AddItem(item); + } + } else { + subTitleTrackMenu->AddItem(new BMenuItem("none", + new BMessage(M_DUMMY))); + subTitleTrackMenu->ItemAt(0)->SetMarked(true); + } } @@ -1945,10 +2000,12 @@ // Add track selector menus BMenu* audioTrackMenu = new BMenu("Audio track"); BMenu* videoTrackMenu = new BMenu("Video track"); - _SetupTrackMenus(audioTrackMenu, videoTrackMenu); + BMenu* subTitleTrackMenu = new BMenu("Subtitles"); + _SetupTrackMenus(audioTrackMenu, videoTrackMenu, subTitleTrackMenu); audioTrackMenu->SetTargetForItems(this); videoTrackMenu->SetTargetForItems(this); + subTitleTrackMenu->SetTargetForItems(this); menu->AddItem(item = new BMenuItem(audioTrackMenu)); item->SetEnabled(fHasAudio); @@ -1956,6 +2013,9 @@ menu->AddItem(item = new BMenuItem(videoTrackMenu)); item->SetEnabled(fHasVideo); + menu->AddItem(item = new BMenuItem(subTitleTrackMenu)); + item->SetEnabled(fHasVideo); + menu->AddSeparatorItem(); menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); Modified: haiku/trunk/src/apps/mediaplayer/MainWin.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/MainWin.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/MainWin.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -89,7 +89,8 @@ void _CreateMenu(); void _SetupVideoAspectItems(BMenu* menu); void _SetupTrackMenus(BMenu* audioTrackMenu, - BMenu* videoTrackMenu); + BMenu* videoTrackMenu, + BMenu* subTitleTrackMenu); void _UpdateAudioChannelCount(int32 audioTrackIndex); void _GetMinimumWindowSize(int& width, @@ -152,6 +153,7 @@ BMenu* fVideoAspectMenu; BMenu* fAudioTrackMenu; BMenu* fVideoTrackMenu; + BMenu* fSubTitleTrackMenu; BMenuItem* fNoInterfaceMenuItem; BMenu* fAttributesMenu; BMenu* fRatingMenu; Modified: haiku/trunk/src/apps/mediaplayer/VideoView.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/VideoView.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/VideoView.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -290,15 +290,20 @@ void -VideoView::SetSubtitle(const char* text) +VideoView::SetSubTitle(const char* text) { if (text == NULL || text[0] == '\0') { fHasSubtitle = false; - return; + } else { + fHasSubtitle = true; + fSubtitleBitmap->SetText(text); } - - fHasSubtitle = true; - fSubtitleBitmap->SetText(text); + // TODO: Make smarter and invalidate only previous subtitle bitmap + // region; + if (!fIsPlaying && LockLooper()) { + Invalidate(); + UnlockLooper(); + } } Modified: haiku/trunk/src/apps/mediaplayer/VideoView.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/VideoView.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/VideoView.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -51,7 +51,7 @@ void SetFullscreen(bool fullScreen); void SetVideoFrame(const BRect& frame); - void SetSubtitle(const char* text); + void SetSubTitle(const char* text); private: void _DrawBitmap(const BBitmap* bitmap); Modified: haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -11,7 +11,9 @@ #include <Bitmap.h> #include <TextView.h> +#include "StackBlurFilter.h" + SubtitleBitmap::SubtitleBitmap() : fBitmap(NULL), @@ -77,11 +79,15 @@ delete fBitmap; - BRect bounds = _InsertText(); + BRect bounds; + float outlineRadius; + _InsertText(bounds, outlineRadius); fBitmap = new BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32); memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); + StackBlurFilter filter; + if (fBitmap->Lock()) { fBitmap->AddChild(fShadowTextView); fShadowTextView->ResizeTo(bounds.Width(), bounds.Height()); @@ -97,8 +103,11 @@ fShadowTextView->Sync(); fShadowTextView->RemoveSelf(); + filter.Filter(fBitmap, outlineRadius * 2); + fBitmap->AddChild(fTextView); fTextView->ResizeTo(bounds.Width(), bounds.Height()); + fTextView->MoveTo(-outlineRadius / 2, -outlineRadius / 2); fTextView->SetViewColor(0, 0, 0, 0); fTextView->SetDrawingMode(B_OP_ALPHA); @@ -273,12 +282,12 @@ } -BRect -SubtitleBitmap::_InsertText() +void +SubtitleBitmap::_InsertText(BRect& textRect, float& outlineRadius) { BFont font(be_plain_font); - float fontSize = ceilf((fVideoBounds.Width() * 0.9) / 35); - float falseBoldWidth = ceilf(fontSize / 28.0); + float fontSize = ceilf((fVideoBounds.Width() * 0.9) / 36); + outlineRadius = ceilf(fontSize / 28.0); font.SetSize(fontSize); rgb_color shadow; @@ -293,8 +302,8 @@ color.blue = 255; color.alpha = 240; - BRect textRect = fVideoBounds; - textRect.OffsetBy(falseBoldWidth, falseBoldWidth); + textRect = fVideoBounds; + textRect.OffsetBy(outlineRadius, outlineRadius); fTextView->SetText(NULL); fTextView->SetFontAndColor(&font, B_FONT_ALL, &color); @@ -302,7 +311,7 @@ fTextView->Insert(" "); parse_text(fText, fTextView, font, color, true); - font.SetFalseBoldWidth(falseBoldWidth); + font.SetFalseBoldWidth(outlineRadius); fShadowTextView->SetText(NULL); fShadowTextView->SetFontAndColor(&font, B_FONT_ALL, &shadow); @@ -316,9 +325,16 @@ fShadowTextView->SetTextRect(textRect); textRect = fTextView->TextRect(); - textRect.InsetBy(-falseBoldWidth, -falseBoldWidth); + textRect.InsetBy(-outlineRadius, -outlineRadius); textRect.OffsetTo(B_ORIGIN); - return textRect; + + // Make sure the text rect really finishes behind the last line. + // We don't want any accidental extra space. + textRect.bottom = outlineRadius; + int32 lineCount = fTextView->CountLines(); + for (int32 i = 0; i < lineCount; i++) + textRect.bottom += fTextView->LineHeight(i); + textRect.bottom += outlineRadius; } Modified: haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/interface/SubtitleBitmap.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -26,7 +26,8 @@ private: void _GenerateBitmap(); - BRect _InsertText(); + void _InsertText(BRect& bounds, + float& outlineRadius); private: BBitmap* fBitmap; Modified: haiku/trunk/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -16,6 +16,7 @@ #include <Path.h> #include "MediaFileTrackSupplier.h" +#include "SubTitlesSRT.h" static const char* kPathKey = "path"; @@ -346,11 +347,65 @@ BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRef); if (mediaFile == NULL) return NULL; - TrackSupplier* supplier + MediaFileTrackSupplier* supplier = new(std::nothrow) MediaFileTrackSupplier(mediaFile); - if (supplier == NULL) + if (supplier == NULL) { delete mediaFile; + return NULL; + } + // Search for subtitle files in the same folder + // TODO: Error checking + BEntry entry(&fRef, true); + + char originalName[B_FILE_NAME_LENGTH]; + entry.GetName(originalName); + BString nameWithoutExtension(originalName); + int32 extension = nameWithoutExtension.FindLast('.'); + if (extension > 0) + nameWithoutExtension.Truncate(extension); + + BPath path; + entry.GetPath(&path); + path.GetParent(&path); + BDirectory directory(path.Path()); + while (directory.GetNextEntry(&entry) == B_OK) { + char name[B_FILE_NAME_LENGTH]; + if (entry.GetName(name) != B_OK) + continue; + BString nameString(name); + if (nameString == originalName) + continue; + if (nameString.IFindFirst(nameWithoutExtension) < 0) + continue; + + BFile file(&entry, B_READ_ONLY); + if (file.InitCheck() != B_OK) + continue; + + int32 pos = nameString.FindLast('.'); + if (pos < 0) + continue; + + BString extensionString(nameString.String() + pos + 1); + extensionString.ToLower(); + + BString language = "default"; + if (pos > 1) { + int32 end = pos; + while (pos > 0 && *(nameString.String() + pos - 1) != '.') + pos--; + language.SetTo(nameString.String() + pos, end - pos); + } + + if (extensionString == "srt") { + SubTitles* subTitles + = new(std::nothrow) SubTitlesSRT(&file, language.String()); + if (subTitles != NULL && !supplier->AddSubTitles(subTitles)) + delete subTitles; + } + } + return supplier; } Modified: haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -69,7 +69,9 @@ { delete fMediaFile; // BMediaFile destructor will call ReleaseAllTracks() -} + for (int32 i = fSubTitleTracks.CountItems() - 1; i >= 0; i--) + delete reinterpret_cast<SubTitles*>(fSubTitleTracks.ItemAtFast(i)); + } status_t @@ -115,6 +117,13 @@ } +int32 +MediaFileTrackSupplier::CountSubTitleTracks() +{ + return fSubTitleTracks.CountItems(); +} + + status_t MediaFileTrackSupplier::GetAudioMetaData(int32 index, BMessage* metaData) { @@ -166,3 +175,17 @@ return supplier; } + +const SubTitles* +MediaFileTrackSupplier::SubTitleTrackForIndex(int32 index) +{ + return reinterpret_cast<SubTitles*>(fSubTitleTracks.ItemAt(index)); +} + + +bool +MediaFileTrackSupplier::AddSubTitles(SubTitles* subTitles) +{ + return fSubTitleTracks.AddItem(subTitles); +} + Modified: haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h 2010-09-27 09:43:55 UTC (rev 38826) +++ haiku/trunk/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -28,6 +28,7 @@ virtual int32 CountAudioTracks(); virtual int32 CountVideoTracks(); + virtual int32 CountSubTitleTracks(); virtual status_t GetAudioMetaData(int32 index, BMessage* metaData); @@ -36,11 +37,15 @@ virtual AudioTrackSupplier* CreateAudioTrackForIndex(int32 index); virtual VideoTrackSupplier* CreateVideoTrackForIndex(int32 index); + virtual const SubTitles* SubTitleTrackForIndex(int32 index); + bool AddSubTitles(SubTitles* subTitles); + private: BMediaFile* fMediaFile; BList fAudioTracks; BList fVideoTracks; + BList fSubTitleTracks; }; Added: haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.cpp (rev 0) +++ haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -0,0 +1,17 @@ +/* + * Copyright 2010, Stephan Aßmus <superstippi@xxxxxx>. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "SubTitles.h" + + +SubTitles::SubTitles() +{ +} + + +SubTitles::~SubTitles() +{ +} Added: haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.h =================================================================== --- haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.h (rev 0) +++ haiku/trunk/src/apps/mediaplayer/supplier/SubTitles.h 2010-09-27 15:26:41 UTC (rev 38827) @@ -0,0 +1,34 @@ +/* + * Copyright 2010, Stephan Aßmus <superstippi@xxxxxx>. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef SUB_TITLES_H +#define SUB_TITLES_H + + +#include <Point.h> +#include <String.h> + + +class BFile; + + +struct SubTitle { + BString text; + BPoint placement; + bigtime_t startTime; + bigtime_t duration; +}; + + +class SubTitles { +public: + SubTitles(); + virtual ~SubTitles(); + + virtual const char* Name() const = 0; + virtual const SubTitle* SubTitleAt(bigtime_t time) const = 0; +}; + + +#endif //SUB_TITLES_H Added: haiku/trunk/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp =================================================================== --- haiku/trunk/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp (rev 0) +++ haiku/trunk/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp 2010-09-27 15:26:41 UTC (rev 38827) @@ -0,0 +1,181 @@ +/* + * Copyright 2010, Stephan Aßmus <superstippi@xxxxxx>. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "SubTitlesSRT.h" + +#include <new> + +#include <stdlib.h> + +#include <File.h> + +#include "FileReadWrite.h" + + +SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name) + : + SubTitles(), + fName(name), + fSubTitles(64) +{ + if (file == NULL) + return; + if (file->InitCheck() != B_OK) + return; + + FileReadWrite lineProvider(file); + BString line; + enum { + EXPECT_SEQUENCE_NUMBER = 0, + EXPECT_TIME_CODE, + EXPECT_TEXT + }; + + SubTitle subTitle; + int32 lastSequenceNumber = 0; + int32 currentLine = 0; + + int32 state = EXPECT_SEQUENCE_NUMBER; + while (lineProvider.Next(line)) { + line.RemoveAll("\n"); + line.RemoveAll("\r"); + switch (state) { + case EXPECT_SEQUENCE_NUMBER: + { + line.Trim(); + int32 sequenceNumber = atoi(line.String()); + if (sequenceNumber != lastSequenceNumber + 1) { + fprintf(stderr, "Warning: Wrong sequence number in SRT " + "file: %ld, expected: %ld, line %ld\n", sequenceNumber, + lastSequenceNumber + 1, currentLine); + } + state = EXPECT_TIME_CODE; + lastSequenceNumber = sequenceNumber; + break; + } + + case EXPECT_TIME_CODE: + { + line.Trim(); + int32 separatorPos = line.FindFirst(" --> "); + if (separatorPos < 0) { + fprintf(stderr, "Error: Time code expected on line %ld, " + "got '%s'\n", currentLine, line.String()); + return; + } + BString timeCode(line.String(), separatorPos); + if (separatorPos != 12) { + fprintf(stderr, "Warning: Time code broken on line %ld " + "(%s)?\n", currentLine, timeCode.String()); + } + int hours; + int minutes; + int seconds; + int milliSeconds; + if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, + &seconds, &milliSeconds) != 4) { + fprintf(stderr, "Error: Failed to parse start time on " + "line %ld\n", currentLine); + return; + } + subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL + + (bigtime_t)minutes * 60 * 1000000LL + + (bigtime_t)seconds * 1000000LL + + (bigtime_t)milliSeconds * 1000; + + int32 endTimePos = separatorPos + 5; + timeCode.SetTo(line.String() + endTimePos); + if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, + &seconds, &milliSeconds) != 4) { + fprintf(stderr, "Error: Failed to parse end time on " + "line %ld\n", currentLine); + return; + } + bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL + + (bigtime_t)minutes * 60 * 1000000LL + + (bigtime_t)seconds * 1000000LL + + (bigtime_t)milliSeconds * 1000; + + subTitle.duration = endTime - subTitle.startTime; + + state = EXPECT_TEXT; + break; + } + + case EXPECT_TEXT: + if (line.Length() == 0) { + int32 index = _IndexFor(subTitle.startTime); + SubTitle* clone = new(std::nothrow) SubTitle(subTitle); + if (clone == NULL || !fSubTitles.AddItem(clone, index)) { + delete clone; + return; + } + subTitle.text = ""; + subTitle.placement = BPoint(-1, -1); + subTitle.startTime = 0; + subTitle.duration = 0; + + state = EXPECT_SEQUENCE_NUMBER; + } else + subTitle.text << line << '\n'; + break; + } + line.SetTo(""); + currentLine++; + } +} + + +SubTitlesSRT::~SubTitlesSRT() +{ + for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--) + delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i)); +} + + +const char* +SubTitlesSRT::Name() const +{ + return fName.String(); +} + + +const SubTitle* +SubTitlesSRT::SubTitleAt(bigtime_t time) const +{ + int32 index = _IndexFor(time); + SubTitle* subTitle + = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index)); + if (subTitle != NULL && subTitle->startTime > time) [... truncated: 793 lines follow ...]