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