1 /* 2 * InfoWin.cpp - Media Player for the Haiku Operating System 3 * 4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * version 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 */ 20 21 22 #include "InfoWin.h" 23 24 #include <math.h> 25 #include <stdio.h> 26 #include <string.h> 27 28 #include <Bitmap.h> 29 #include <Catalog.h> 30 #include <Debug.h> 31 #include <MediaDefs.h> 32 #include <MessageFormat.h> 33 #include <Mime.h> 34 #include <NodeInfo.h> 35 #include <String.h> 36 #include <StringForRate.h> 37 #include <StringView.h> 38 #include <TextView.h> 39 40 #include "Controller.h" 41 #include "ControllerObserver.h" 42 #include "PlaylistItem.h" 43 44 45 #define NAME "File info" 46 #define MIN_WIDTH 400 47 48 #define BASE_HEIGHT (32 + 32) 49 50 //const rgb_color kGreen = { 152, 203, 152, 255 }; 51 const rgb_color kRed = { 203, 152, 152, 255 }; 52 const rgb_color kBlue = { 0, 0, 220, 255 }; 53 const rgb_color kGreen = { 171, 221, 161, 255 }; 54 const rgb_color kBlack = { 0, 0, 0, 255 }; 55 56 #undef B_TRANSLATION_CONTEXT 57 #define B_TRANSLATION_CONTEXT "MediaPlayer-InfoWin" 58 59 // should later draw an icon 60 class InfoView : public BView { 61 public: 62 InfoView(BRect frame, const char* name, float divider); 63 virtual ~InfoView(); 64 virtual void Draw(BRect updateRect); 65 66 status_t SetIcon(const PlaylistItem* item); 67 status_t SetIcon(const char* mimeType); 68 void SetGenericIcon(); 69 70 private: 71 float fDivider; 72 BBitmap* fIconBitmap; 73 }; 74 75 76 InfoView::InfoView(BRect frame, const char *name, float divider) 77 : BView(frame, name, B_FOLLOW_ALL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 78 fDivider(divider), 79 fIconBitmap(NULL) 80 { 81 BRect rect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); 82 83 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 84 fIconBitmap = new BBitmap(rect, B_RGBA32); 85 #else 86 fIconBitmap = new BBitmap(rect, B_CMAP8); 87 #endif 88 } 89 90 91 InfoView::~InfoView() 92 { 93 delete fIconBitmap; 94 } 95 96 void 97 InfoView::Draw(BRect updateRect) 98 { 99 SetHighColor(kGreen); 100 BRect r(Bounds()); 101 r.right = r.left + fDivider; 102 FillRect(r); 103 104 if (fIconBitmap) { 105 float left = r.left + ( r.right - r.left ) / 2 - B_LARGE_ICON / 2; 106 SetDrawingMode(B_OP_ALPHA); 107 DrawBitmap(fIconBitmap, BPoint(left, r.top + B_LARGE_ICON / 2)); 108 } 109 110 SetHighColor(ui_color(B_DOCUMENT_TEXT_COLOR)); 111 r.left = r.right; 112 FillRect(r); 113 } 114 115 116 status_t 117 InfoView::SetIcon(const PlaylistItem* item) 118 { 119 return item->GetIcon(fIconBitmap, B_LARGE_ICON); 120 } 121 122 123 status_t 124 InfoView::SetIcon(const char* mimeTypeString) 125 { 126 if (!mimeTypeString) 127 return B_BAD_VALUE; 128 129 // get type icon 130 BMimeType mimeType(mimeTypeString); 131 status_t status = mimeType.GetIcon(fIconBitmap, B_LARGE_ICON); 132 133 // get supertype icon 134 if (status != B_OK) { 135 BMimeType superType; 136 status = mimeType.GetSupertype(&superType); 137 if (status == B_OK) 138 status = superType.GetIcon(fIconBitmap, B_LARGE_ICON); 139 } 140 141 return status; 142 } 143 144 145 void 146 InfoView::SetGenericIcon() 147 { 148 // get default icon 149 BMimeType genericType(B_FILE_MIME_TYPE); 150 if (genericType.GetIcon(fIconBitmap, B_LARGE_ICON) != B_OK) { 151 // clear bitmap 152 uint8 transparent = 0; 153 if (fIconBitmap->ColorSpace() == B_CMAP8) 154 transparent = B_TRANSPARENT_MAGIC_CMAP8; 155 156 memset(fIconBitmap->Bits(), transparent, fIconBitmap->BitsLength()); 157 } 158 } 159 160 161 // #pragma mark - 162 163 164 InfoWin::InfoWin(BPoint leftTop, Controller* controller) 165 : 166 BWindow(BRect(leftTop.x, leftTop.y, leftTop.x + MIN_WIDTH - 1, 167 leftTop.y + 300), B_TRANSLATE(NAME), B_TITLED_WINDOW, 168 B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_ZOOMABLE), 169 fController(controller), 170 fControllerObserver(new ControllerObserver(this, 171 OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES | OBSERVE_STAT_CHANGES)) 172 { 173 BRect rect = Bounds(); 174 175 // accomodate for big fonts 176 float div = max_c(2 * 32, be_plain_font->StringWidth( 177 B_TRANSLATE("Display Mode")) + 10); 178 179 fInfoView = new InfoView(rect, "background", div); 180 fInfoView->SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); 181 AddChild(fInfoView); 182 183 BFont bigFont(be_plain_font); 184 bigFont.SetSize(bigFont.Size() + 6); 185 font_height fh; 186 bigFont.GetHeight(&fh); 187 fFilenameView = new BStringView( 188 BRect(div + 10, 20, rect.right - 10, 20 + fh.ascent + 5), 189 "filename", ""); 190 AddChild(fFilenameView); 191 fFilenameView->SetFont(&bigFont); 192 fFilenameView->SetViewColor(fInfoView->ViewColor()); 193 fFilenameView->SetLowColor(fInfoView->ViewColor()); 194 #ifdef B_BEOS_VERSION_DANO /* maybe we should support that as well ? */ 195 fFilenameView->SetTruncation(B_TRUNCATE_END); 196 #endif 197 198 rect.top = BASE_HEIGHT; 199 200 BRect lr(rect); 201 BRect cr(rect); 202 lr.right = div - 1; 203 cr.left = div + 1; 204 BRect tr; 205 tr = lr.OffsetToCopy(0, 0).InsetByCopy(5, 1); 206 fLabelsView = new BTextView(lr, "labels", tr, B_FOLLOW_BOTTOM); 207 fLabelsView->SetViewColor(kGreen); 208 fLabelsView->SetAlignment(B_ALIGN_RIGHT); 209 fLabelsView->SetWordWrap(false); 210 AddChild(fLabelsView); 211 tr = cr.OffsetToCopy(0, 0).InsetByCopy(10, 1); 212 fContentsView = new BTextView(cr, "contents", tr, B_FOLLOW_BOTTOM); 213 fContentsView->SetWordWrap(false); 214 AddChild(fContentsView); 215 216 fLabelsView->MakeSelectable(); 217 fContentsView->MakeSelectable(); 218 219 fController->AddListener(fControllerObserver); 220 Update(); 221 222 Show(); 223 } 224 225 226 InfoWin::~InfoWin() 227 { 228 fController->RemoveListener(fControllerObserver); 229 delete fControllerObserver; 230 } 231 232 233 // #pragma mark - 234 235 236 void 237 InfoWin::FrameResized(float newWidth, float newHeight) 238 { 239 } 240 241 242 void 243 InfoWin::MessageReceived(BMessage* msg) 244 { 245 switch (msg->what) { 246 case MSG_CONTROLLER_FILE_FINISHED: 247 break; 248 case MSG_CONTROLLER_FILE_CHANGED: 249 Update(INFO_ALL); 250 break; 251 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: 252 Update(/*INFO_VIDEO | INFO_STATS*/INFO_ALL); 253 break; 254 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: 255 Update(/*INFO_AUDIO | INFO_STATS*/INFO_ALL); 256 break; 257 case MSG_CONTROLLER_VIDEO_STATS_CHANGED: 258 case MSG_CONTROLLER_AUDIO_STATS_CHANGED: 259 Update(/*INFO_STATS*/INFO_ALL); 260 break; 261 default: 262 BWindow::MessageReceived(msg); 263 break; 264 } 265 } 266 267 268 bool 269 InfoWin::QuitRequested() 270 { 271 Hide(); 272 return false; 273 } 274 275 276 void 277 InfoWin::Pulse() 278 { 279 if (IsHidden()) 280 return; 281 Update(INFO_STATS); 282 } 283 284 285 // #pragma mark - 286 287 288 void 289 InfoWin::ResizeToPreferred() 290 { 291 } 292 293 294 void 295 InfoWin::Update(uint32 which) 296 { 297 printf("InfoWin::Update(0x%08" B_PRIx32 ")\n", which); 298 fLabelsView->SetText(""); 299 fContentsView->SetText(""); 300 fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kBlue); 301 fLabelsView->Insert(" "); 302 fContentsView->SetFontAndColor(be_plain_font, B_FONT_ALL); 303 // fContentsView->Insert(""); 304 305 if (!fController->Lock()) 306 return; 307 308 fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kRed); 309 310 status_t err; 311 // video track format information 312 if ((which & INFO_VIDEO) && fController->VideoTrackCount() > 0) { 313 BString label = B_TRANSLATE("Video"); 314 fLabelsView->Insert(label << "\n\n\n"); 315 BString s; 316 media_format format; 317 media_raw_video_format videoFormat = {}; 318 err = fController->GetEncodedVideoFormat(&format); 319 if (err < B_OK) { 320 s << "(" << strerror(err) << ")\n"; 321 } else if (format.type == B_MEDIA_ENCODED_VIDEO) { 322 videoFormat = format.u.encoded_video.output; 323 media_codec_info mci; 324 err = fController->GetVideoCodecInfo(&mci); 325 if (err < B_OK) { 326 s << B_TRANSLATE("Haiku Media Kit: ") << strerror(err); 327 if (format.user_data_type == B_CODEC_TYPE_INFO) { 328 s << (char *)format.user_data << " " 329 << B_TRANSLATE("(not supported)"); 330 } 331 } else 332 s << mci.pretty_name; //<< "(" << mci.short_name << ")"; 333 } else if (format.type == B_MEDIA_RAW_VIDEO) { 334 videoFormat = format.u.raw_video; 335 s << B_TRANSLATE("raw video"); 336 } else 337 s << B_TRANSLATE("unknown format"); 338 s << "\n"; 339 s << format.Width() << " x " << format.Height(); 340 // encoded has output as 1st field... 341 char fpsString[20]; 342 snprintf(fpsString, sizeof(fpsString), B_TRANSLATE("%.3f fps"), 343 videoFormat.field_rate); 344 s << ", " << fpsString << "\n\n"; 345 fContentsView->Insert(s.String()); 346 } 347 348 // audio track format information 349 if ((which & INFO_AUDIO) && fController->AudioTrackCount() > 0) { 350 BString label = B_TRANSLATE("Audio"); 351 fLabelsView->Insert(label << "\n\n\n"); 352 BString s; 353 media_format format; 354 media_raw_audio_format audioFormat = {}; 355 err = fController->GetEncodedAudioFormat(&format); 356 //string_for_format(format, buf, sizeof(buf)); 357 //printf("%s\n", buf); 358 if (err < 0) { 359 s << "(" << strerror(err) << ")\n"; 360 } else if (format.type == B_MEDIA_ENCODED_AUDIO) { 361 audioFormat = format.u.encoded_audio.output; 362 media_codec_info mci; 363 err = fController->GetAudioCodecInfo(&mci); 364 if (err < 0) { 365 s << B_TRANSLATE("Haiku Media Kit: ") << strerror(err); 366 if (format.user_data_type == B_CODEC_TYPE_INFO) { 367 s << (char *)format.user_data << " " 368 << B_TRANSLATE("(not supported)"); 369 } 370 } else 371 s << mci.pretty_name; //<< "(" << mci.short_name << ")"; 372 } else if (format.type == B_MEDIA_RAW_AUDIO) { 373 audioFormat = format.u.raw_audio; 374 s << B_TRANSLATE("raw audio"); 375 } else 376 s << B_TRANSLATE("unknown format"); 377 s << "\n"; 378 uint32 bitsPerSample = 8 * (audioFormat.format 379 & media_raw_audio_format::B_AUDIO_SIZE_MASK); 380 uint32 channelCount = audioFormat.channel_count; 381 float sr = audioFormat.frame_rate; 382 383 if (bitsPerSample > 0) { 384 char bitString[20]; 385 snprintf(bitString, sizeof(bitString), B_TRANSLATE("%d Bit"), 386 bitsPerSample); 387 s << bitString << " "; 388 } 389 390 static BMessageFormat channelFormat(B_TRANSLATE( 391 "{0, plural, =1{Mono} =2{Stereo} other{# Channels}}")); 392 channelFormat.Format(s, channelCount); 393 394 s << ", "; 395 if (sr > 0.0) { 396 char rateString[20]; 397 snprintf(rateString, sizeof(rateString), 398 B_TRANSLATE("%.3f kHz"), sr / 1000); 399 s << rateString; 400 } else { 401 BString rateString = B_TRANSLATE("%d kHz"); 402 rateString.ReplaceFirst("%d", "??"); 403 s << rateString; 404 } 405 if (format.type == B_MEDIA_ENCODED_AUDIO) { 406 float br = format.u.encoded_audio.bit_rate; 407 char string[20] = ""; 408 if (br > 0.0) 409 s << ", " << string_for_rate(br, string, sizeof(string)); 410 } 411 s << "\n\n"; 412 fContentsView->Insert(s.String()); 413 } 414 415 // statistics 416 if ((which & INFO_STATS) && fController->HasFile()) { 417 BString label = B_TRANSLATE("Duration"); 418 fLabelsView->Insert(label << "\n"); 419 BString s; 420 bigtime_t d = fController->TimeDuration(); 421 bigtime_t v; 422 423 //s << d << "µs; "; 424 425 d /= 1000; 426 427 v = d / (3600 * 1000); 428 d = d % (3600 * 1000); 429 bool hours = v > 0; 430 if (hours) 431 s << v << ":"; 432 v = d / (60 * 1000); 433 d = d % (60 * 1000); 434 s << v << ":"; 435 v = d / 1000; 436 if (v < 10) 437 s << '0'; 438 s << v; 439 if (hours) 440 s << " " << B_TRANSLATE_COMMENT("h", "Hours"); 441 else 442 s << " " << B_TRANSLATE_COMMENT("min", "Minutes"); 443 s << "\n"; 444 fContentsView->Insert(s.String()); 445 // TODO: demux/video/audio/... perfs (Kb/s) 446 447 BString content = B_TRANSLATE("Display mode"); 448 fLabelsView->Insert(content << "\n"); 449 if (fController->IsOverlayActive()) { 450 content = B_TRANSLATE("Overlay"); 451 fContentsView->Insert(content << "\n"); 452 } else { 453 content = B_TRANSLATE("DrawBitmap"); 454 fContentsView->Insert(content << "\n"); 455 } 456 457 fLabelsView->Insert("\n"); 458 fContentsView->Insert("\n"); 459 } 460 461 if (which & INFO_TRANSPORT) { 462 // Transport protocol info (file, http, rtsp, ...) 463 } 464 465 if (which & INFO_FILE) { 466 bool iconSet = false; 467 if (fController->HasFile()) { 468 const PlaylistItem* item = fController->Item(); 469 iconSet = fInfoView->SetIcon(item) == B_OK; 470 media_file_format fileFormat; 471 BString s; 472 if (fController->GetFileFormatInfo(&fileFormat) == B_OK) { 473 BString label = B_TRANSLATE("Container"); 474 fLabelsView->Insert(label << "\n"); 475 s << fileFormat.pretty_name; 476 s << "\n"; 477 fContentsView->Insert(s.String()); 478 if (!iconSet) 479 iconSet = fInfoView->SetIcon(fileFormat.mime_type) == B_OK; 480 } else 481 fContentsView->Insert("\n"); 482 BString label = B_TRANSLATE("Location"); 483 fLabelsView->Insert(label << "\n"); 484 if (fController->GetLocation(&s) < B_OK) 485 s = B_TRANSLATE("<unknown>"); 486 s << "\n"; 487 fContentsView->Insert(s.String()); 488 if (fController->GetName(&s) < B_OK) 489 s = B_TRANSLATE("<unnamed media>"); 490 fFilenameView->SetText(s.String()); 491 } else { 492 fFilenameView->SetText(B_TRANSLATE("<no media>")); 493 } 494 if (!iconSet) 495 fInfoView->SetGenericIcon(); 496 } 497 498 if ((which & INFO_COPYRIGHT) && fController->HasFile()) { 499 BString s; 500 if (fController->GetCopyright(&s) == B_OK && s.Length() > 0) { 501 BString label = B_TRANSLATE("Copyright"); 502 fLabelsView->Insert(label << "\n\n"); 503 s << "\n\n"; 504 fContentsView->Insert(s.String()); 505 } 506 } 507 508 fController->Unlock(); 509 510 ResizeToPreferred(); 511 } 512