1/*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <Bitmap.h>
26#include <MediaRoster.h>
27#include <MessageRunner.h>
28#include "VideoView.h"
29#include "VideoNode.h"
30#include "ConvertBitmap.h"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#define CHECK_ACTIVITY 'ChkA'
37
38VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask,
39	uint32 flags)
40 :	BView(frame, name, resizeMask, flags)
41 ,	fVideoNode(0)
42 ,	fVideoActive(false)
43 ,	fOverlayActive(false)
44 ,	fActivityCheckMsgRunner(0)
45 ,	fLastFrame(0)
46{
47	SetViewColor(B_TRANSPARENT_COLOR);
48
49	status_t err = B_OK;
50	BMediaRoster *mroster = BMediaRoster::Roster(&err);
51	if (!mroster || err) {
52		printf("VideoView::VideoView: media_server is dead\n");
53		exit(1);
54	} else {
55		fVideoNode = new VideoNode("video in", this);
56		err = mroster->RegisterNode(fVideoNode);
57	}
58}
59
60
61VideoView::~VideoView()
62{
63	delete fActivityCheckMsgRunner;
64
65	if (fVideoNode) {
66		BMediaRoster::Roster()->UnregisterNode(fVideoNode);
67		delete fVideoNode;
68	}
69}
70
71
72void
73VideoView::AttachedToWindow()
74{
75	BMessage msg(CHECK_ACTIVITY);
76	fActivityCheckMsgRunner = new BMessageRunner(BMessenger(this), &msg,
77		200000);
78}
79
80
81VideoNode *
82VideoView::Node()
83{
84	return fVideoNode;
85}
86
87
88void
89VideoView::OverlayLockAcquire()
90{
91   printf("VideoView::OverlayLockAcquire\n");
92}
93
94
95void
96VideoView::OverlayLockRelease()
97{ /* [19:54] <Francois> Rudolf forwarded me a mail once about it
98   * [19:55] <Francois> when you get relmease msg you are supposed to
99   				UnlockBits() on the overlay bitmaps you use
100   * [19:55] <Francois> it's used when switching workspaces
101   * [19:55] <Francois> as the bits might get relocated
102   */
103   printf("VideoView::OverlayLockRelease\n");
104
105}
106
107
108void
109VideoView::OverlayScreenshotPrepare()
110{
111	printf("OverlayScreenshotPrepare enter\n");
112/*
113	fVideoNode->LockBitmap();
114	if (fOverlayActive) {
115		BBitmap *bmp = fVideoNode->Bitmap();
116		if (bmp) {
117//			Window()->UpdateIfNeeded();
118//			Sync();
119			BBitmap *tmp = new BBitmap(bmp->Bounds(), 0, B_RGB32);
120//			ConvertBitmap(tmp, bmp);
121			ClearViewOverlay();
122			DrawBitmap(tmp, Bounds());
123			delete tmp;
124//			Sync();
125		}
126	}
127	fVideoNode->UnlockBitmap();
128*/
129	printf("OverlayScreenshotPrepare leave\n");
130}
131
132
133void
134VideoView::OverlayScreenshotCleanup()
135{
136	printf("OverlayScreenshotCleanup enter\n");
137/*
138	snooze(50000); // give app server some time to take the screenshot
139	fVideoNode->LockBitmap();
140	if (fOverlayActive) {
141		BBitmap *bmp = fVideoNode->Bitmap();
142		if (bmp) {
143			DrawBitmap(bmp, Bounds());
144			SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
145				B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL
146				| B_OVERLAY_FILTER_VERTICAL);
147			Invalidate();
148		}
149	}
150	fVideoNode->UnlockBitmap();
151*/
152	printf("OverlayScreenshotCleanup leave\n");
153}
154
155
156void
157VideoView::RemoveVideoDisplay()
158{
159	printf("VideoView::RemoveVideoDisplay\n");
160
161	if (fOverlayActive) {
162		ClearViewOverlay();
163		fOverlayActive = false;
164	}
165	fVideoActive = false;
166	Invalidate();
167}
168
169
170void
171VideoView::RemoveOverlay()
172{
173	printf("VideoView::RemoveOverlay\n");
174	if (LockLooperWithTimeout(50000) == B_OK) {
175		ClearViewOverlay();
176		fOverlayActive = false;
177		UnlockLooper();
178	}
179}
180
181
182void
183VideoView::CheckActivity()
184{
185	if (!fVideoActive)
186		return;
187	if (system_time() - fLastFrame < 700000)
188		return;
189
190	printf("VideoView::CheckActivity: lag detected\n");
191
192	fVideoActive = false;
193	Invalidate();
194}
195
196
197void
198VideoView::Draw(BRect updateRect)
199{
200	if (!fVideoActive) {
201		DrawTestImage();
202		return;
203	}
204	if (fOverlayActive) {
205		SetHighColor(fOverlayKeyColor);
206		FillRect(updateRect);
207	} else {
208		fVideoNode->LockBitmap();
209		BBitmap *bmp = fVideoNode->Bitmap();
210		if (bmp)
211			DrawBitmap(bmp, bmp->Bounds(), Bounds(), B_FILTER_BITMAP_BILINEAR);
212		fVideoNode->UnlockBitmap();
213	}
214}
215
216
217void
218VideoView::DrawFrame()
219{
220//	printf("VideoView::DrawFrame\n");
221	if (!fVideoActive) {
222		fVideoActive = true;
223		if (LockLooperWithTimeout(50000) != B_OK)
224			return;
225		Invalidate();
226		UnlockLooper();
227	}
228	fLastFrame = system_time();
229
230	bool want_overlay = fVideoNode->IsOverlayActive();
231
232	if (!want_overlay && fOverlayActive) {
233		if (LockLooperWithTimeout(50000) == B_OK) {
234			ClearViewOverlay();
235			UnlockLooper();
236			fOverlayActive = false;
237		} else {
238			printf("can't ClearViewOverlay, as LockLooperWithTimeout "
239				"failed\n");
240		}
241	}
242	if (want_overlay && !fOverlayActive) {
243		fVideoNode->LockBitmap();
244		BBitmap *bmp = fVideoNode->Bitmap();
245		if (bmp && LockLooperWithTimeout(50000) == B_OK) {
246			SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
247				B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL
248				| B_OVERLAY_FILTER_VERTICAL);
249			fOverlayActive = true;
250
251			Invalidate();
252			UnlockLooper();
253		}
254		fVideoNode->UnlockBitmap();
255	}
256	if (!fOverlayActive) {
257		if (LockLooperWithTimeout(50000) != B_OK)
258			return;
259		Invalidate();
260		UnlockLooper();
261	}
262}
263
264
265void
266VideoView::DrawTestImage()
267{
268	static const rgb_color cols[8] = {
269		{255,255,255}, {255,255,0}, {0,255,255}, {0,255,0},
270		{255,0,255}, {255,0,0}, {0,0,255}, {0,0,0}
271//		{255,255,255}, {255,255,0}, {0,255,255},
272//		{255,0,255}, {255,0,0}, {0,255,0}, {0,0,255}, {0,0,0}
273	};
274	float bar_width;
275	float left;
276	float right;
277
278	BRect bnd = Bounds();
279	int seperator_y1 = int(0.60 * (bnd.Height() + 1));
280	int seperator_y2 = int(0.80 * (bnd.Height() + 1));
281	int steps;
282
283	bar_width = bnd.Width() / 8;
284	if (bar_width < 1)
285		bar_width = 1;
286
287	left = 0;
288	for (int i = 0; i < 8; i++) {
289		SetHighColor(cols[i]);
290		right = (i != 7) ? left + bar_width - 1 : bnd.right;
291		FillRect(BRect(left, 0, right, seperator_y1));
292		left = right + 1;
293	}
294
295	steps = 32;
296
297	bar_width = bnd.Width() / steps;
298//	if (bar_width < 1)
299//		bar_width = 1;
300
301	left = 0;
302	for (int i = 0; i < steps; i++) {
303		uint8 c = i * 255 / (steps - 1);
304		SetHighColor(c, c, c);
305		right = (i != steps - 1) ? left + bar_width - 1 : bnd.right;
306		FillRect(BRect(left, seperator_y1 + 1, right, seperator_y2));
307		left = right + 1;
308	}
309
310	steps = 256;
311
312	bar_width = bnd.Width() / steps;
313	if (bar_width < 1)
314		bar_width = 1;
315
316	left = 0;
317	for (int i = 0; i < steps; i++) {
318		uint8 c = 255 - (i * 255 / (steps - 1));
319		SetHighColor(c, c, c);
320		right = (i != steps - 1) ? left + bar_width - 1 : bnd.right;
321		FillRect(BRect(left, seperator_y2 + 1, right, bnd.bottom));
322		left = right + 1;
323	}
324}
325
326
327void
328VideoView::MessageReceived(BMessage *msg)
329{
330	switch (msg->what) {
331		case CHECK_ACTIVITY:
332			CheckActivity();
333			break;
334		default:
335			BView::MessageReceived(msg);
336	}
337}
338
339
340bool
341VideoView::IsOverlaySupported()
342{
343	struct colorcombo {
344		color_space colspace;
345		const char *name;
346	} colspace[] = {
347		{ B_RGB32,		"B_RGB32"},
348		{ B_RGBA32,		"B_RGBA32"},
349		{ B_RGB24,		"B_RGB24"},
350		{ B_RGB16,		"B_RGB16"},
351		{ B_RGB15,		"B_RGB15"},
352		{ B_RGBA15,		"B_RGBA15"},
353		{ B_RGB32_BIG,	"B_RGB32_BIG"},
354		{ B_RGBA32_BIG,	"B_RGBA32_BIG "},
355		{ B_RGB24_BIG,	"B_RGB24_BIG "},
356		{ B_RGB16_BIG,	"B_RGB16_BIG "},
357		{ B_RGB15_BIG,	"B_RGB15_BIG "},
358		{ B_RGBA15_BIG, "B_RGBA15_BIG "},
359		{ B_YCbCr422,	"B_YCbCr422"},
360		{ B_YCbCr411,	"B_YCbCr411"},
361		{ B_YCbCr444,	"B_YCbCr444"},
362		{ B_YCbCr420,	"B_YCbCr420"},
363		{ B_YUV422,		"B_YUV422"},
364		{ B_YUV411,		"B_YUV411"},
365		{ B_YUV444,		"B_YUV444"},
366		{ B_YUV420,		"B_YUV420"},
367		{ B_NO_COLOR_SPACE, NULL}
368	};
369
370	bool supported = false;
371	for (int i = 0; colspace[i].name; i++) {
372		BBitmap *test = new BBitmap(BRect(0,0,320,240),	B_BITMAP_WILL_OVERLAY
373			| B_BITMAP_RESERVE_OVERLAY_CHANNEL, colspace[i].colspace);
374		if (test->InitCheck() == B_OK) {
375			printf("Display supports %s (0x%08x) overlay\n", colspace[i].name,
376				colspace[i].colspace);
377			overlay_restrictions restrict;
378			if (B_OK == test->GetOverlayRestrictions(&restrict)) {
379				printf(
380					"Overlay restrictions: source horizontal_alignment %d\n",
381					restrict.source.horizontal_alignment);
382				printf("Overlay restrictions: source vertical_alignment %d\n",
383					restrict.source.vertical_alignment);
384				printf("Overlay restrictions: source width_alignment %d\n",
385					restrict.source.width_alignment);
386				printf("Overlay restrictions: source height_alignment %d\n",
387					restrict.source.height_alignment);
388				printf("Overlay restrictions: source min_width %d\n",
389					restrict.source.min_width);
390				printf("Overlay restrictions: source max_width %d\n",
391					restrict.source.max_width);
392				printf("Overlay restrictions: source min_height %d\n",
393					restrict.source.min_height);
394				printf("Overlay restrictions: source max_height %d\n",
395					restrict.source.max_height);
396				printf(
397					"Overlay restrictions: destination horizontal_alignment "
398					"%d\n", restrict.destination.horizontal_alignment);
399				printf("Overlay restrictions: destination vertical_alignment "
400					"%d\n", restrict.destination.vertical_alignment);
401				printf("Overlay restrictions: destination width_alignment "
402					"%d\n", restrict.destination.width_alignment);
403				printf("Overlay restrictions: destination height_alignment "
404					"%d\n", restrict.destination.height_alignment);
405				printf("Overlay restrictions: destination min_width %d\n",
406					restrict.destination.min_width);
407				printf("Overlay restrictions: destination max_width %d\n",
408					restrict.destination.max_width);
409				printf("Overlay restrictions: destination min_height %d\n",
410					restrict.destination.min_height);
411				printf("Overlay restrictions: destination max_height %d\n",
412					restrict.destination.max_height);
413				printf("Overlay restrictions: min_width_scale %.3f\n",
414					restrict.min_width_scale);
415				printf("Overlay restrictions: max_width_scale %.3f\n",
416					restrict.max_width_scale);
417				printf("Overlay restrictions: min_height_scale %.3f\n",
418					restrict.min_height_scale);
419				printf("Overlay restrictions: max_height_scale %.3f\n",
420					restrict.max_height_scale);
421			}
422			supported = true;
423		}
424		delete test;
425//		if (supported)
426//			break;
427	}
428	return supported;
429}
430
431