xref: /haiku/src/apps/mediaplayer/InfoWin.cpp (revision bf57c148f7787f0df15980976997c6dfb70ee067)
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