1 /* 2 * InfoWin.cpp - Media Player for the Haiku Operating System 3 * 4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de> 5 * Copyright 2015 Axel Dörfler <axeld@pinc-software.de> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 23 #include "InfoWin.h" 24 25 #include <math.h> 26 #include <stdio.h> 27 #include <string.h> 28 29 #include <Bitmap.h> 30 #include <Catalog.h> 31 #include <ControlLook.h> 32 #include <Debug.h> 33 #include <LayoutBuilder.h> 34 #include <MediaDefs.h> 35 #include <MessageFormat.h> 36 #include <Mime.h> 37 #include <NodeInfo.h> 38 #include <Screen.h> 39 #include <String.h> 40 #include <StringForRate.h> 41 #include <StringView.h> 42 #include <TextView.h> 43 44 #include "Controller.h" 45 #include "ControllerObserver.h" 46 #include "PlaylistItem.h" 47 48 49 #define MIN_WIDTH 500 50 51 52 #undef B_TRANSLATION_CONTEXT 53 #define B_TRANSLATION_CONTEXT "MediaPlayer-InfoWin" 54 55 56 class IconView : public BView { 57 public: 58 IconView(const char* name, int32 iconSize); 59 virtual ~IconView(); 60 61 status_t SetIcon(const PlaylistItem* item); 62 status_t SetIcon(const char* mimeType); 63 void SetGenericIcon(); 64 65 virtual void GetPreferredSize(float* _width, float* _height); 66 virtual void AttachedToWindow(); 67 virtual void Draw(BRect updateRect); 68 69 private: 70 BBitmap* fIconBitmap; 71 }; 72 73 74 IconView::IconView(const char* name, int32 iconSize) 75 : 76 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 77 fIconBitmap(NULL) 78 { 79 fIconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 80 B_RGBA32); 81 SetExplicitMaxSize(PreferredSize()); 82 } 83 84 85 IconView::~IconView() 86 { 87 delete fIconBitmap; 88 } 89 90 91 status_t 92 IconView::SetIcon(const PlaylistItem* item) 93 { 94 return item->GetIcon(fIconBitmap, B_LARGE_ICON); 95 } 96 97 98 status_t 99 IconView::SetIcon(const char* mimeTypeString) 100 { 101 if (!mimeTypeString) 102 return B_BAD_VALUE; 103 104 // get type icon 105 BMimeType mimeType(mimeTypeString); 106 status_t status = mimeType.GetIcon(fIconBitmap, B_LARGE_ICON); 107 108 // get supertype icon 109 if (status != B_OK) { 110 BMimeType superType; 111 status = mimeType.GetSupertype(&superType); 112 if (status == B_OK) 113 status = superType.GetIcon(fIconBitmap, B_LARGE_ICON); 114 } 115 116 return status; 117 } 118 119 120 void 121 IconView::SetGenericIcon() 122 { 123 // get default icon 124 BMimeType genericType(B_FILE_MIME_TYPE); 125 if (genericType.GetIcon(fIconBitmap, B_LARGE_ICON) != B_OK) { 126 // clear bitmap 127 uint8 transparent = 0; 128 if (fIconBitmap->ColorSpace() == B_CMAP8) 129 transparent = B_TRANSPARENT_MAGIC_CMAP8; 130 131 memset(fIconBitmap->Bits(), transparent, fIconBitmap->BitsLength()); 132 } 133 } 134 135 136 void 137 IconView::GetPreferredSize(float* _width, float* _height) 138 { 139 if (_width != NULL) { 140 *_width = fIconBitmap->Bounds().Width() 141 + 2 * be_control_look->DefaultItemSpacing(); 142 } 143 if (_height != NULL) { 144 *_height = fIconBitmap->Bounds().Height() 145 + 2 * be_control_look->DefaultItemSpacing(); 146 } 147 } 148 149 150 void 151 IconView::AttachedToWindow() 152 { 153 if (Parent() != NULL) 154 SetViewColor(Parent()->ViewColor()); 155 else 156 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 157 } 158 159 160 void 161 IconView::Draw(BRect updateRect) 162 { 163 BRect rect(Bounds()); 164 165 if (fIconBitmap != NULL) { 166 // Draw bitmap centered within the view 167 SetDrawingMode(B_OP_ALPHA); 168 DrawBitmap(fIconBitmap, BPoint(rect.left 169 + (rect.Width() - fIconBitmap->Bounds().Width()) / 2, 170 rect.top + (rect.Height() - fIconBitmap->Bounds().Height()) / 2)); 171 } 172 } 173 174 175 // #pragma mark - 176 177 178 InfoWin::InfoWin(BPoint leftTop, Controller* controller) 179 : 180 BWindow(BRect(leftTop.x, leftTop.y, leftTop.x + MIN_WIDTH - 1, 181 leftTop.y + 300), B_TRANSLATE("File info"), B_TITLED_WINDOW, 182 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE), 183 fController(controller), 184 fControllerObserver(new ControllerObserver(this, 185 OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES | OBSERVE_STAT_CHANGES)) 186 { 187 fIconView = new IconView("background", B_LARGE_ICON); 188 189 fFilenameView = _CreateInfo("filename"); 190 BFont bigFont(be_plain_font); 191 bigFont.SetSize(bigFont.Size() * 1.5f); 192 fFilenameView->SetFont(&bigFont); 193 194 // Create info views 195 196 BStringView* containerLabel = _CreateLabel("containerLabel", 197 B_TRANSLATE("Container")); 198 fContainerInfo = _CreateInfo("container"); 199 200 fVideoSeparator = _CreateSeparator(); 201 fVideoLabel = _CreateLabel("videoLabel", B_TRANSLATE("Video")); 202 fVideoFormatInfo = _CreateInfo("videoFormat"); 203 fVideoConfigInfo = _CreateInfo("videoConfig"); 204 fDisplayModeLabel = _CreateLabel("displayModeLabel", 205 B_TRANSLATE("Display mode")); 206 fDisplayModeInfo = _CreateInfo("displayMode"); 207 208 fAudioSeparator = _CreateSeparator(); 209 fAudioLabel = _CreateLabel("audioLabel", B_TRANSLATE("Audio")); 210 fAudioFormatInfo = _CreateInfo("audioFormat"); 211 fAudioConfigInfo = _CreateInfo("audioConfig"); 212 213 BStringView* durationLabel = _CreateLabel("durationLabel", 214 B_TRANSLATE("Duration")); 215 fDurationInfo = _CreateInfo("duration"); 216 217 BStringView* locationLabel = _CreateLabel("locationLabel", 218 B_TRANSLATE("Location")); 219 fLocationInfo = _CreateInfo("location"); 220 221 fCopyrightSeparator = _CreateSeparator(); 222 fCopyrightLabel = _CreateLabel("copyrightLabel", B_TRANSLATE("Copyright")); 223 fCopyrightInfo = _CreateInfo("copyright"); 224 225 BLayoutBuilder::Group<>(this, B_VERTICAL) 226 .SetInsets(B_USE_DEFAULT_SPACING) 227 .AddGroup(B_HORIZONTAL) 228 .Add(fIconView, 0) 229 .Add(fFilenameView, 1) 230 .End() 231 .AddGrid(2, 13) 232 .Add(containerLabel, 0, 0) 233 .Add(fContainerInfo, 1, 0) 234 .Add(fVideoSeparator, 0, 1) 235 .Add(fVideoLabel, 0, 2) 236 .Add(fVideoFormatInfo, 1, 2) 237 .Add(fVideoConfigInfo, 1, 3) 238 .Add(fDisplayModeLabel, 0, 4) 239 .Add(fDisplayModeInfo, 1, 4) 240 .Add(fAudioSeparator, 0, 5) 241 .Add(fAudioLabel, 0, 6) 242 .Add(fAudioFormatInfo, 1, 6) 243 .Add(fAudioConfigInfo, 1, 7) 244 .Add(_CreateSeparator(), 0, 8) 245 .Add(durationLabel, 0, 9) 246 .Add(fDurationInfo, 1, 9) 247 .Add(_CreateSeparator(), 0, 10) 248 .Add(locationLabel, 0, 11) 249 .Add(fLocationInfo, 1, 11) 250 .Add(fCopyrightSeparator, 0, 12) 251 .Add(fCopyrightLabel, 0, 12) 252 .Add(fCopyrightInfo, 1, 12) 253 .SetColumnWeight(0, 0) 254 .SetColumnWeight(1, 1) 255 .SetSpacing(B_USE_DEFAULT_SPACING, 0) 256 .SetExplicitMinSize(BSize(MIN_WIDTH, B_SIZE_UNSET)); 257 258 fController->AddListener(fControllerObserver); 259 Update(); 260 261 UpdateSizeLimits(); 262 263 // Move window on screen if needed 264 BScreen screen(this); 265 if (screen.Frame().bottom < Frame().bottom) 266 MoveBy(0, screen.Frame().bottom - Frame().bottom); 267 if (screen.Frame().right < Frame().right) 268 MoveBy(0, screen.Frame().right - Frame().right); 269 270 Show(); 271 } 272 273 274 InfoWin::~InfoWin() 275 { 276 fController->RemoveListener(fControllerObserver); 277 delete fControllerObserver; 278 } 279 280 281 void 282 InfoWin::MessageReceived(BMessage* msg) 283 { 284 switch (msg->what) { 285 case MSG_CONTROLLER_FILE_FINISHED: 286 break; 287 case MSG_CONTROLLER_FILE_CHANGED: 288 Update(INFO_ALL); 289 break; 290 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: 291 Update(INFO_VIDEO | INFO_STATS); 292 break; 293 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: 294 Update(INFO_AUDIO | INFO_STATS); 295 break; 296 case MSG_CONTROLLER_VIDEO_STATS_CHANGED: 297 case MSG_CONTROLLER_AUDIO_STATS_CHANGED: 298 Update(INFO_STATS); 299 break; 300 default: 301 BWindow::MessageReceived(msg); 302 break; 303 } 304 } 305 306 307 bool 308 InfoWin::QuitRequested() 309 { 310 Hide(); 311 return false; 312 } 313 314 315 void 316 InfoWin::Pulse() 317 { 318 if (IsHidden()) 319 return; 320 Update(INFO_STATS); 321 } 322 323 324 // #pragma mark - 325 326 327 void 328 InfoWin::Update(uint32 which) 329 { 330 if (!fController->Lock()) 331 return; 332 333 if ((which & INFO_FILE) != 0) 334 _UpdateFile(); 335 336 // video track format information 337 if ((which & INFO_VIDEO) != 0) 338 _UpdateVideo(); 339 340 // audio track format information 341 if ((which & INFO_AUDIO) != 0) 342 _UpdateAudio(); 343 344 // statistics 345 if ((which & INFO_STATS) != 0) { 346 _UpdateDuration(); 347 // TODO: demux/video/audio/... perfs (Kb/info) 348 } 349 350 if ((which & INFO_TRANSPORT) != 0) { 351 // Transport protocol info (file, http, rtsp, ...) 352 } 353 354 if ((which & INFO_COPYRIGHT)!=0) 355 _UpdateCopyright(); 356 357 fController->Unlock(); 358 } 359 360 361 void 362 InfoWin::_UpdateFile() 363 { 364 bool iconSet = false; 365 if (fController->HasFile()) { 366 const PlaylistItem* item = fController->Item(); 367 iconSet = fIconView->SetIcon(item) == B_OK; 368 media_file_format fileFormat; 369 status_t status = fController->GetFileFormatInfo(&fileFormat); 370 if (status == B_OK) { 371 fContainerInfo->SetText(fileFormat.pretty_name); 372 if (!iconSet) 373 iconSet = fIconView->SetIcon(fileFormat.mime_type) == B_OK; 374 } else 375 fContainerInfo->SetText(strerror(status)); 376 377 BString info; 378 if (fController->GetLocation(&info) != B_OK) 379 info = B_TRANSLATE("<unknown>"); 380 fLocationInfo->SetText(info.String()); 381 fLocationInfo->SetToolTip(info.String()); 382 383 if (fController->GetName(&info) != B_OK || info.IsEmpty()) 384 info = B_TRANSLATE("<unnamed media>"); 385 fFilenameView->SetText(info.String()); 386 fFilenameView->SetToolTip(info.String()); 387 } else { 388 fFilenameView->SetText(B_TRANSLATE("<no media>")); 389 fContainerInfo->SetText("-"); 390 fLocationInfo->SetText("-"); 391 } 392 393 if (!iconSet) 394 fIconView->SetGenericIcon(); 395 } 396 397 398 void 399 InfoWin::_UpdateVideo() 400 { 401 bool visible = fController->VideoTrackCount() > 0; 402 if (visible) { 403 BString info; 404 media_format format; 405 media_raw_video_format videoFormat = {}; 406 status_t status = fController->GetEncodedVideoFormat(&format); 407 if (status != B_OK) { 408 info << "(" << strerror(status) << ")\n"; 409 } else if (format.type == B_MEDIA_ENCODED_VIDEO) { 410 videoFormat = format.u.encoded_video.output; 411 media_codec_info mci; 412 status = fController->GetVideoCodecInfo(&mci); 413 if (status != B_OK) { 414 if (format.user_data_type == B_CODEC_TYPE_INFO) { 415 info << (char *)format.user_data << " " 416 << B_TRANSLATE("(not supported)"); 417 } else 418 info = strerror(status); 419 } else 420 info << mci.pretty_name; //<< "(" << mci.short_name << ")"; 421 } else if (format.type == B_MEDIA_RAW_VIDEO) { 422 videoFormat = format.u.raw_video; 423 info << B_TRANSLATE("raw video"); 424 } else 425 info << B_TRANSLATE("unknown format"); 426 427 fVideoFormatInfo->SetText(info.String()); 428 429 info.SetToFormat("%" B_PRIu32 " x %" B_PRIu32, format.Width(), 430 format.Height()); 431 432 // encoded has output as 1st field... 433 char fpsString[20]; 434 snprintf(fpsString, sizeof(fpsString), B_TRANSLATE("%.3f fps"), 435 videoFormat.field_rate); 436 info << ", " << fpsString; 437 438 fVideoConfigInfo->SetText(info.String()); 439 440 if (fController->IsOverlayActive()) 441 fDisplayModeInfo->SetText(B_TRANSLATE("Overlay")); 442 else 443 fDisplayModeInfo->SetText(B_TRANSLATE("DrawBitmap")); 444 } 445 446 fVideoSeparator->SetVisible(visible); 447 _SetVisible(fVideoLabel, visible); 448 _SetVisible(fVideoFormatInfo, visible); 449 _SetVisible(fVideoConfigInfo, visible); 450 _SetVisible(fDisplayModeLabel, visible); 451 _SetVisible(fDisplayModeInfo, visible); 452 } 453 454 455 void 456 InfoWin::_UpdateAudio() 457 { 458 bool visible = fController->AudioTrackCount() > 0; 459 if (visible) { 460 BString info; 461 media_format format; 462 media_raw_audio_format audioFormat = {}; 463 464 status_t status = fController->GetEncodedAudioFormat(&format); 465 if (status != B_OK) { 466 info << "(" << strerror(status) << ")\n"; 467 } else if (format.type == B_MEDIA_ENCODED_AUDIO) { 468 audioFormat = format.u.encoded_audio.output; 469 media_codec_info mci; 470 status = fController->GetAudioCodecInfo(&mci); 471 if (status != B_OK) { 472 if (format.user_data_type == B_CODEC_TYPE_INFO) { 473 info << (char *)format.user_data << " " 474 << B_TRANSLATE("(not supported)"); 475 } else 476 info = strerror(status); 477 } else 478 info = mci.pretty_name; 479 } else if (format.type == B_MEDIA_RAW_AUDIO) { 480 audioFormat = format.u.raw_audio; 481 info = B_TRANSLATE("raw audio"); 482 } else 483 info = B_TRANSLATE("unknown format"); 484 485 fAudioFormatInfo->SetText(info.String()); 486 487 uint32 bitsPerSample = 8 * (audioFormat.format 488 & media_raw_audio_format::B_AUDIO_SIZE_MASK); 489 uint32 channelCount = audioFormat.channel_count; 490 float sr = audioFormat.frame_rate; 491 492 info.Truncate(0); 493 494 if (bitsPerSample > 0) { 495 char bitString[20]; 496 snprintf(bitString, sizeof(bitString), B_TRANSLATE("%d Bit"), 497 bitsPerSample); 498 info << bitString << " "; 499 } 500 501 static BMessageFormat channelFormat(B_TRANSLATE( 502 "{0, plural, =1{Mono} =2{Stereo} other{# Channels}}")); 503 channelFormat.Format(info, channelCount); 504 505 info << ", "; 506 if (sr > 0.0) { 507 char rateString[20]; 508 snprintf(rateString, sizeof(rateString), 509 B_TRANSLATE("%.3f kHz"), sr / 1000); 510 info << rateString; 511 } else { 512 BString rateString = B_TRANSLATE("%d kHz"); 513 rateString.ReplaceFirst("%d", "??"); 514 info << rateString; 515 } 516 if (format.type == B_MEDIA_ENCODED_AUDIO) { 517 float br = format.u.encoded_audio.bit_rate; 518 char string[20] = ""; 519 if (br > 0.0) 520 info << ", " << string_for_rate(br, string, sizeof(string)); 521 } 522 523 fAudioConfigInfo->SetText(info.String()); 524 } 525 526 fAudioSeparator->SetVisible(visible); 527 _SetVisible(fAudioLabel, visible); 528 _SetVisible(fAudioFormatInfo, visible); 529 _SetVisible(fAudioConfigInfo, visible); 530 } 531 532 533 void 534 InfoWin::_UpdateDuration() 535 { 536 if (!fController->HasFile()) { 537 fDurationInfo->SetText("-"); 538 return; 539 } 540 541 BString info; 542 543 bigtime_t d = fController->TimeDuration() / 1000; 544 bigtime_t v = d / (3600 * 1000); 545 d = d % (3600 * 1000); 546 bool hours = v > 0; 547 if (hours) 548 info << v << ":"; 549 v = d / (60 * 1000); 550 d = d % (60 * 1000); 551 info << v << ":"; 552 v = d / 1000; 553 if (v < 10) 554 info << '0'; 555 info << v; 556 if (hours) 557 info << " " << B_TRANSLATE_COMMENT("h", "Hours"); 558 else 559 info << " " << B_TRANSLATE_COMMENT("min", "Minutes"); 560 561 fDurationInfo->SetText(info.String()); 562 } 563 564 565 void 566 InfoWin::_UpdateCopyright() 567 { 568 BString info; 569 570 bool visible = fController->HasFile() 571 && fController->GetCopyright(&info) == B_OK && !info.IsEmpty(); 572 if (visible) 573 fCopyrightInfo->SetText(info.String()); 574 575 fCopyrightSeparator->SetVisible(visible); 576 _SetVisible(fCopyrightLabel, visible); 577 _SetVisible(fCopyrightInfo, visible); 578 } 579 580 581 // #pragma mark - 582 583 584 BStringView* 585 InfoWin::_CreateLabel(const char* name, const char* label) 586 { 587 static const rgb_color kLabelColor = tint_color( 588 ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_3_TINT); 589 590 BStringView* view = new BStringView(name, label); 591 view->SetAlignment(B_ALIGN_RIGHT); 592 view->SetHighColor(kLabelColor); 593 594 return view; 595 } 596 597 598 BStringView* 599 InfoWin::_CreateInfo(const char* name) 600 { 601 BStringView* view = new BStringView(name, ""); 602 view->SetExplicitMinSize(BSize(200, B_SIZE_UNSET)); 603 view->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 604 view->SetTruncation(B_TRUNCATE_SMART); 605 606 return view; 607 } 608 609 610 BLayoutItem* 611 InfoWin::_CreateSeparator() 612 { 613 return BSpaceLayoutItem::CreateVerticalStrut( 614 be_control_look->ComposeSpacing(B_USE_HALF_ITEM_SPACING)); 615 } 616 617 618 void 619 InfoWin::_SetVisible(BView* view, bool visible) 620 { 621 bool hidden = view->IsHidden(view); 622 if (hidden && visible) 623 view->Show(); 624 else if (!hidden && !visible) 625 view->Hide(); 626 } 627