/* * InfoWin.cpp - Media Player for the Haiku Operating System * * Copyright (C) 2006 Marcus Overhagen * Copyright 2015 Axel Dörfler * * Released under the terms of the MIT license. */ #include "InfoWin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Controller.h" #include "ControllerObserver.h" #include "PlaylistItem.h" #define MIN_WIDTH 500 #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "MediaPlayer-InfoWin" class IconView : public BView { public: IconView(const char* name, int32 iconSize); virtual ~IconView(); status_t SetIcon(const PlaylistItem* item); status_t SetIcon(const char* mimeType); void SetGenericIcon(); virtual void GetPreferredSize(float* _width, float* _height); virtual void AttachedToWindow(); virtual void Draw(BRect updateRect); private: BBitmap* fIconBitmap; }; IconView::IconView(const char* name, int32 iconSize) : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), fIconBitmap(NULL) { fIconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), B_RGBA32); SetExplicitMaxSize(PreferredSize()); } IconView::~IconView() { delete fIconBitmap; } status_t IconView::SetIcon(const PlaylistItem* item) { return item->GetIcon(fIconBitmap, B_LARGE_ICON); } status_t IconView::SetIcon(const char* mimeTypeString) { if (!mimeTypeString) return B_BAD_VALUE; // get type icon BMimeType mimeType(mimeTypeString); status_t status = mimeType.GetIcon(fIconBitmap, B_LARGE_ICON); // get supertype icon if (status != B_OK) { BMimeType superType; status = mimeType.GetSupertype(&superType); if (status == B_OK) status = superType.GetIcon(fIconBitmap, B_LARGE_ICON); } return status; } void IconView::SetGenericIcon() { // get default icon BMimeType genericType(B_FILE_MIME_TYPE); if (genericType.GetIcon(fIconBitmap, B_LARGE_ICON) != B_OK) { // clear bitmap uint8 transparent = 0; if (fIconBitmap->ColorSpace() == B_CMAP8) transparent = B_TRANSPARENT_MAGIC_CMAP8; memset(fIconBitmap->Bits(), transparent, fIconBitmap->BitsLength()); } } void IconView::GetPreferredSize(float* _width, float* _height) { if (_width != NULL) { *_width = fIconBitmap->Bounds().Width() + 2 * be_control_look->DefaultItemSpacing(); } if (_height != NULL) { *_height = fIconBitmap->Bounds().Height() + 2 * be_control_look->DefaultItemSpacing(); } } void IconView::AttachedToWindow() { if (Parent() != NULL) SetViewColor(Parent()->ViewColor()); else SetViewUIColor(B_PANEL_BACKGROUND_COLOR); } void IconView::Draw(BRect updateRect) { BRect rect(Bounds()); if (fIconBitmap != NULL) { // Draw bitmap centered within the view SetDrawingMode(B_OP_ALPHA); DrawBitmap(fIconBitmap, BPoint(rect.left + (rect.Width() - fIconBitmap->Bounds().Width()) / 2, rect.top + (rect.Height() - fIconBitmap->Bounds().Height()) / 2)); } } // #pragma mark - InfoWin::InfoWin(BPoint leftTop, Controller* controller) : BWindow(BRect(leftTop.x, leftTop.y, leftTop.x + MIN_WIDTH - 1, leftTop.y + 300), B_TRANSLATE("File info"), B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE), fController(controller), fControllerObserver(new ControllerObserver(this, OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES | OBSERVE_STAT_CHANGES)) { fIconView = new IconView("background", B_LARGE_ICON); fFilenameView = _CreateInfo("filename"); BFont bigFont(be_plain_font); bigFont.SetSize(bigFont.Size() * 1.5f); fFilenameView->SetFont(&bigFont); // Create info views BStringView* containerLabel = _CreateLabel("containerLabel", B_TRANSLATE("Container")); fContainerInfo = _CreateInfo("container"); fVideoSeparator = _CreateSeparator(); fVideoLabel = _CreateLabel("videoLabel", B_TRANSLATE("Video")); fVideoFormatInfo = _CreateInfo("videoFormat"); fVideoConfigInfo = _CreateInfo("videoConfig"); fDisplayModeLabel = _CreateLabel("displayModeLabel", B_TRANSLATE("Display mode")); fDisplayModeInfo = _CreateInfo("displayMode"); fAudioSeparator = _CreateSeparator(); fAudioLabel = _CreateLabel("audioLabel", B_TRANSLATE("Audio")); fAudioFormatInfo = _CreateInfo("audioFormat"); fAudioConfigInfo = _CreateInfo("audioConfig"); BStringView* durationLabel = _CreateLabel("durationLabel", B_TRANSLATE("Duration")); fDurationInfo = _CreateInfo("duration"); BStringView* locationLabel = _CreateLabel("locationLabel", B_TRANSLATE("Location")); fLocationInfo = _CreateInfo("location"); fCopyrightSeparator = _CreateSeparator(); fCopyrightLabel = _CreateLabel("copyrightLabel", B_TRANSLATE("Copyright")); fCopyrightInfo = _CreateInfo("copyright"); BLayoutBuilder::Group<>(this, B_VERTICAL) .SetInsets(B_USE_DEFAULT_SPACING) .AddGroup(B_HORIZONTAL) .Add(fIconView, 0) .Add(fFilenameView, 1) .End() .AddGrid(2, 13) .Add(containerLabel, 0, 0) .Add(fContainerInfo, 1, 0) .Add(fVideoSeparator, 0, 1) .Add(fVideoLabel, 0, 2) .Add(fVideoFormatInfo, 1, 2) .Add(fVideoConfigInfo, 1, 3) .Add(fDisplayModeLabel, 0, 4) .Add(fDisplayModeInfo, 1, 4) .Add(fAudioSeparator, 0, 5) .Add(fAudioLabel, 0, 6) .Add(fAudioFormatInfo, 1, 6) .Add(fAudioConfigInfo, 1, 7) .Add(_CreateSeparator(), 0, 8) .Add(durationLabel, 0, 9) .Add(fDurationInfo, 1, 9) .Add(_CreateSeparator(), 0, 10) .Add(locationLabel, 0, 11) .Add(fLocationInfo, 1, 11) .Add(fCopyrightSeparator, 0, 12) .Add(fCopyrightLabel, 0, 12) .Add(fCopyrightInfo, 1, 12) .SetColumnWeight(0, 0) .SetColumnWeight(1, 1) .SetSpacing(B_USE_DEFAULT_SPACING, 0) .SetExplicitMinSize(BSize(MIN_WIDTH, B_SIZE_UNSET)); fController->AddListener(fControllerObserver); Update(); UpdateSizeLimits(); // Move window on screen if needed BScreen screen(this); if (screen.Frame().bottom < Frame().bottom) MoveBy(0, screen.Frame().bottom - Frame().bottom); if (screen.Frame().right < Frame().right) MoveBy(0, screen.Frame().right - Frame().right); Show(); } InfoWin::~InfoWin() { fController->RemoveListener(fControllerObserver); delete fControllerObserver; } void InfoWin::MessageReceived(BMessage* msg) { switch (msg->what) { case MSG_CONTROLLER_FILE_FINISHED: break; case MSG_CONTROLLER_FILE_CHANGED: Update(INFO_ALL); break; case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: Update(INFO_VIDEO | INFO_STATS); break; case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: Update(INFO_AUDIO | INFO_STATS); break; case MSG_CONTROLLER_VIDEO_STATS_CHANGED: case MSG_CONTROLLER_AUDIO_STATS_CHANGED: Update(INFO_STATS); break; default: BWindow::MessageReceived(msg); break; } } bool InfoWin::QuitRequested() { Hide(); return false; } void InfoWin::Pulse() { if (IsHidden()) return; Update(INFO_STATS); } // #pragma mark - void InfoWin::Update(uint32 which) { if (!fController->Lock()) return; if ((which & INFO_FILE) != 0) _UpdateFile(); // video track format information if ((which & INFO_VIDEO) != 0) _UpdateVideo(); // audio track format information if ((which & INFO_AUDIO) != 0) _UpdateAudio(); // statistics if ((which & INFO_STATS) != 0) { _UpdateDuration(); // TODO: demux/video/audio/... perfs (Kb/info) } if ((which & INFO_TRANSPORT) != 0) { // Transport protocol info (file, http, rtsp, ...) } if ((which & INFO_COPYRIGHT)!=0) _UpdateCopyright(); fController->Unlock(); } void InfoWin::_UpdateFile() { bool iconSet = false; if (fController->HasFile()) { const PlaylistItem* item = fController->Item(); iconSet = fIconView->SetIcon(item) == B_OK; media_file_format fileFormat; status_t status = fController->GetFileFormatInfo(&fileFormat); if (status == B_OK) { fContainerInfo->SetText(fileFormat.pretty_name); if (!iconSet) iconSet = fIconView->SetIcon(fileFormat.mime_type) == B_OK; } else fContainerInfo->SetText(strerror(status)); BString info; if (fController->GetLocation(&info) != B_OK) info = B_TRANSLATE(""); fLocationInfo->SetText(info.String()); fLocationInfo->SetToolTip(info.String()); if (fController->GetName(&info) != B_OK || info.IsEmpty()) info = B_TRANSLATE(""); fFilenameView->SetText(info.String()); fFilenameView->SetToolTip(info.String()); } else { fFilenameView->SetText(B_TRANSLATE("")); fContainerInfo->SetText("-"); fLocationInfo->SetText("-"); } if (!iconSet) fIconView->SetGenericIcon(); } void InfoWin::_UpdateVideo() { bool visible = fController->VideoTrackCount() > 0; if (visible) { BString info; media_format format; media_raw_video_format videoFormat = {}; status_t status = fController->GetEncodedVideoFormat(&format); if (status != B_OK) { info << "(" << strerror(status) << ")\n"; } else if (format.type == B_MEDIA_ENCODED_VIDEO) { videoFormat = format.u.encoded_video.output; media_codec_info mci; status = fController->GetVideoCodecInfo(&mci); if (status != B_OK) { if (format.user_data_type == B_CODEC_TYPE_INFO) { info << (char *)format.user_data << " " << B_TRANSLATE("(not supported)"); } else info = strerror(status); } else info << mci.pretty_name; //<< "(" << mci.short_name << ")"; } else if (format.type == B_MEDIA_RAW_VIDEO) { videoFormat = format.u.raw_video; info << B_TRANSLATE("raw video"); } else info << B_TRANSLATE("unknown format"); fVideoFormatInfo->SetText(info.String()); info.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRIu32 " × %" B_PRIu32, "The '×' is the Unicode multiplication sign U+00D7"), format.Width(), format.Height()); // encoded has output as 1st field... char fpsString[20]; snprintf(fpsString, sizeof(fpsString), B_TRANSLATE("%.3f fps"), videoFormat.field_rate); info << ", " << fpsString; fVideoConfigInfo->SetText(info.String()); if (fController->IsOverlayActive()) fDisplayModeInfo->SetText(B_TRANSLATE("Overlay")); else fDisplayModeInfo->SetText(B_TRANSLATE("DrawBitmap")); } fVideoSeparator->SetVisible(visible); _SetVisible(fVideoLabel, visible); _SetVisible(fVideoFormatInfo, visible); _SetVisible(fVideoConfigInfo, visible); _SetVisible(fDisplayModeLabel, visible); _SetVisible(fDisplayModeInfo, visible); } void InfoWin::_UpdateAudio() { bool visible = fController->AudioTrackCount() > 0; if (visible) { BString info; media_format format; media_raw_audio_format audioFormat = {}; status_t status = fController->GetEncodedAudioFormat(&format); if (status != B_OK) { info << "(" << strerror(status) << ")\n"; } else if (format.type == B_MEDIA_ENCODED_AUDIO) { audioFormat = format.u.encoded_audio.output; media_codec_info mci; status = fController->GetAudioCodecInfo(&mci); if (status != B_OK) { if (format.user_data_type == B_CODEC_TYPE_INFO) { info << (char *)format.user_data << " " << B_TRANSLATE("(not supported)"); } else info = strerror(status); } else info = mci.pretty_name; } else if (format.type == B_MEDIA_RAW_AUDIO) { audioFormat = format.u.raw_audio; info = B_TRANSLATE("raw audio"); } else info = B_TRANSLATE("unknown format"); fAudioFormatInfo->SetText(info.String()); int bitsPerSample = 8 * (audioFormat.format & media_raw_audio_format::B_AUDIO_SIZE_MASK); uint32 channelCount = audioFormat.channel_count; float sr = audioFormat.frame_rate; info.Truncate(0); if (bitsPerSample > 0) { char bitString[20]; snprintf(bitString, sizeof(bitString), B_TRANSLATE("%d Bit"), bitsPerSample); info << bitString << " "; } static BStringFormat channelFormat(B_TRANSLATE( "{0, plural, =1{Mono} =2{Stereo} other{# Channels}}")); channelFormat.Format(info, channelCount); info << ", "; if (sr > 0.0) { char rateString[20]; snprintf(rateString, sizeof(rateString), B_TRANSLATE("%.3f kHz"), sr / 1000); info << rateString; } else { BString rateString = B_TRANSLATE("%d kHz"); rateString.ReplaceFirst("%d", "??"); info << rateString; } if (format.type == B_MEDIA_ENCODED_AUDIO) { float br = format.u.encoded_audio.bit_rate; char string[20] = ""; if (br > 0.0) info << ", " << string_for_rate(br, string, sizeof(string)); } fAudioConfigInfo->SetText(info.String()); } fAudioSeparator->SetVisible(visible); _SetVisible(fAudioLabel, visible); _SetVisible(fAudioFormatInfo, visible); _SetVisible(fAudioConfigInfo, visible); } void InfoWin::_UpdateDuration() { if (!fController->HasFile()) { fDurationInfo->SetText("-"); return; } BString info; bigtime_t d = fController->TimeDuration() / 1000; bigtime_t v = d / (3600 * 1000); d = d % (3600 * 1000); bool hours = v > 0; if (hours) info << v << ":"; v = d / (60 * 1000); d = d % (60 * 1000); info << v << ":"; v = d / 1000; if (v < 10) info << '0'; info << v; if (hours) info << " " << B_TRANSLATE_COMMENT("h", "Hours"); else info << " " << B_TRANSLATE_COMMENT("min", "Minutes"); fDurationInfo->SetText(info.String()); } void InfoWin::_UpdateCopyright() { BString info; bool visible = fController->HasFile() && fController->GetCopyright(&info) == B_OK && !info.IsEmpty(); if (visible) fCopyrightInfo->SetText(info.String()); fCopyrightSeparator->SetVisible(visible); _SetVisible(fCopyrightLabel, visible); _SetVisible(fCopyrightInfo, visible); } // #pragma mark - BStringView* InfoWin::_CreateLabel(const char* name, const char* label) { static const rgb_color kLabelColor = tint_color( ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_3_TINT); BStringView* view = new BStringView(name, label); view->SetAlignment(B_ALIGN_RIGHT); view->SetHighColor(kLabelColor); return view; } BStringView* InfoWin::_CreateInfo(const char* name) { BStringView* view = new BStringView(name, ""); view->SetExplicitMinSize(BSize(200, B_SIZE_UNSET)); view->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); view->SetTruncation(B_TRUNCATE_SMART); return view; } BLayoutItem* InfoWin::_CreateSeparator() { return BSpaceLayoutItem::CreateVerticalStrut( be_control_look->ComposeSpacing(B_USE_HALF_ITEM_SPACING)); } void InfoWin::_SetVisible(BView* view, bool visible) { bool hidden = view->IsHidden(view); if (hidden && visible) view->Show(); else if (!hidden && !visible) view->Hide(); }