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