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