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