1/*
2 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>. All rights reserved.
3 * Copyright (C) 2008 Maurice Kalinowski <haiku@kaldience.com>. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7#include "VideoNode.h"
8#include "VideoView.h"
9#include "VideoWindow.h"
10
11
12#include <Bitmap.h>
13#include <Buffer.h>
14#include <BufferGroup.h>
15#include <Debug.h>
16#include <MediaRoster.h>
17#include <Locker.h>
18#include <TimeSource.h>
19#include <Window.h>
20#include <stdio.h>
21#include <string.h>
22
23
24VideoNode::VideoNode(const char *name)
25 :	BMediaNode(name)
26 ,	BMediaEventLooper()
27 ,	BBufferConsumer(B_MEDIA_RAW_VIDEO)
28 ,	fWindow(0)
29 ,	fVideoView(0)
30 ,	fInput()
31 ,	fOverlayEnabled(true)
32 ,	fOverlayActive(false)
33 ,	fDirectOverlayBuffer(false)
34 ,	fBitmap(0)
35 ,	fBitmapLocker(new BLocker("Video Node Locker"))
36 ,	fAddOn(0)
37 ,	fInternalFlavorId(0)
38{
39	_InitDisplay();
40}
41
42
43VideoNode::VideoNode(const char *name, BMediaAddOn* addon, int32 id)
44 :	BMediaNode(name)
45 ,	BMediaEventLooper()
46 ,	BBufferConsumer(B_MEDIA_RAW_VIDEO)
47 ,	fWindow(0)
48 ,	fVideoView(0)
49 ,	fInput()
50 ,	fOverlayEnabled(true)
51 ,	fOverlayActive(false)
52 ,	fDirectOverlayBuffer(false)
53 ,	fBitmap(0)
54 ,	fBitmapLocker(new BLocker("Video Node Locker"))
55 ,	fAddOn(addon)
56 ,	fInternalFlavorId(id)
57{
58	_InitDisplay();
59}
60
61
62VideoNode::~VideoNode()
63{
64	Quit();
65	DeleteBuffers();
66	delete fBitmapLocker;
67	if (fWindow && fWindow->Lock())
68		fWindow->Quit();
69}
70
71
72BMediaAddOn	*
73VideoNode::AddOn(int32 *internal_id) const
74{
75	*internal_id = fInternalFlavorId;
76	return fAddOn;
77}
78
79
80void
81VideoNode::NodeRegistered()
82{
83	fInput.node = Node();
84	fInput.source = media_source::null;
85	fInput.destination.port = ControlPort();
86	fInput.destination.id = 0;
87	fInput.format.type = B_MEDIA_RAW_VIDEO;
88	fInput.format.u.raw_video = media_raw_video_format::wildcard;
89	strcpy(fInput.name, "video in");
90
91	SetPriority(B_DISPLAY_PRIORITY);
92	Run();
93}
94
95
96void
97VideoNode::BufferReceived(BBuffer * buffer)
98{
99	if (RunState() != B_STARTED) {
100		buffer->Recycle();
101		return;
102	}
103	if (fOverlayActive && fDirectOverlayBuffer) {
104		HandleBuffer(buffer);
105	} else {
106		media_timed_event event(buffer->Header()->start_time,
107			BTimedEventQueue::B_HANDLE_BUFFER, buffer,
108			BTimedEventQueue::B_RECYCLE_BUFFER);
109		EventQueue()->AddEvent(event);
110	}
111}
112
113
114status_t
115VideoNode::GetNextInput(int32 *cookie,	media_input *out_input)
116{
117	if (*cookie < 1) {
118		*out_input = fInput;
119		*cookie += 1;
120		return B_OK;
121	}
122	return B_ERROR;
123}
124
125
126void
127VideoNode::DisposeInputCookie(int32 cookie)
128{
129	// nothing to do
130}
131
132
133status_t
134VideoNode::	HandleMessage(int32 message, const void *data, size_t size)
135{
136	return B_ERROR;
137}
138
139
140void
141VideoNode::HandleEvent(const media_timed_event *event, bigtime_t lateness,
142	bool realTimeEvent)
143{
144	switch (event->type) {
145		case BTimedEventQueue::B_START:
146			break;
147		case BTimedEventQueue::B_STOP:
148			EventQueue()->FlushEvents(event->event_time,
149				BTimedEventQueue::B_ALWAYS, true,
150				BTimedEventQueue::B_HANDLE_BUFFER);
151			break;
152		case BTimedEventQueue::B_HANDLE_BUFFER:
153			HandleBuffer((BBuffer *)event->pointer);
154			break;
155		case BTimedEventQueue::B_SEEK:
156			fprintf(stderr, "VideoNode::HandleEvent Seek event not handled\n");
157			break;
158		default:
159			fprintf(stderr, "VideoNode::HandleEvent unknown event\n");
160			break;
161	}
162}
163
164
165void
166VideoNode::ProducerDataStatus(const media_destination &dst, int32 status,
167	bigtime_t at_media_time)
168{
169	// do nothing
170}
171
172
173status_t
174VideoNode::GetLatencyFor(const media_destination &dst,  bigtime_t *out_latency,
175	media_node_id *out_id)
176{
177	if (dst != fInput.destination)
178		return B_MEDIA_BAD_DESTINATION;
179
180	*out_latency = 10000;
181	*out_id = TimeSource()->ID();
182	return B_OK;
183}
184
185
186status_t
187VideoNode::AcceptFormat(const media_destination &dst, media_format *format)
188{
189	/* The connection process:
190	 *                BBufferProducer::FormatProposal
191	 * we are here => BBufferConsumer::AcceptFormat
192	 *                BBufferProducer::PrepareToConnect
193	 *                BBufferConsumer::Connected
194	 *                BBufferProducer::Connect
195	 */
196	if (dst != fInput.destination)
197		return B_MEDIA_BAD_DESTINATION;
198
199	if (format->type == B_MEDIA_NO_TYPE)
200		format->type = B_MEDIA_RAW_VIDEO;
201
202	if (format->type != B_MEDIA_RAW_VIDEO)
203		return B_MEDIA_BAD_FORMAT;
204
205	return B_OK;
206}
207
208
209status_t
210VideoNode::Connected(const media_source &src, const media_destination &dst,
211	const media_format &format, media_input *out_input)
212{
213	/* The connection process:
214	 *                BBufferProducer::FormatProposal
215	 *                BBufferConsumer::AcceptFormat
216	 *                BBufferProducer::PrepareToConnect
217	 * we are here => BBufferConsumer::Connected
218	 *                BBufferProducer::Connect
219	 */
220
221	if (dst != fInput.destination)
222		return B_MEDIA_BAD_DESTINATION;
223
224	fInput.source = src;
225	fInput.format = format;
226
227	if (fInput.format.u.raw_video.field_rate < 1.0)
228		fInput.format.u.raw_video.field_rate = 25.0;
229
230	// In order to display video we need to create a buffer that is either
231	// in the overlay colorspace B_YCbCr422
232	// or the requested colorspace if not B_YCbCr422
233	// and we need to tell the node upstream of our choice
234
235	BRect frame(0, 0, format.u.raw_video.display.line_width - 1,
236		format.u.raw_video.display.line_count - 1);
237
238	DeleteBuffers();
239	status_t err;
240
241	if (format.u.raw_video.display.format == B_NO_COLOR_SPACE) {
242		// upstream node is leaving it up to us so we try overlay then
243		// B_RGBA32 (We probably should try what format the screen is)
244		err = CreateBuffers(frame, B_YCbCr422, true);
245		SetOverlayEnabled(err == B_OK);
246		if (!fOverlayEnabled) {
247			// no overlay available so fall back to RGBA32
248			err = CreateBuffers(frame, B_RGBA32, false);
249		}
250	} else if (format.u.raw_video.display.format == B_YCbCr422) {
251		// upstream node is likely requesting overlay
252		err = CreateBuffers(frame, B_YCbCr422, true);
253		SetOverlayEnabled(err == B_OK);
254		// if we cannot give them what they want then return error
255	} else {
256		// upstream node is requesting some other format
257		SetOverlayEnabled(false);
258		err = CreateBuffers(frame, format.u.raw_video.display.format,
259			fOverlayEnabled);
260	}
261
262	if (err) {
263		fprintf(stderr, "VideoNode::Connected failed, fOverlayEnabled = %d\n",
264			fOverlayEnabled);
265		return err;
266	} else {
267		fInput.format.u.raw_video.display.format = fBitmap->ColorSpace();
268	}
269
270	*out_input = fInput;
271
272	return B_OK;
273}
274
275
276void
277VideoNode::Disconnected(const media_source &src, const media_destination &dst)
278{
279	if (src != fInput.source)
280		return;
281	if (dst != fInput.destination)
282		return;
283
284	DeleteBuffers();
285
286	// disconnect the connection
287	fInput.source = media_source::null;
288}
289
290
291status_t
292VideoNode::FormatChanged(const media_source &src, const media_destination &dst,
293	int32 from_change_count, const media_format &format)
294{
295	if (src != fInput.source)
296		return B_MEDIA_BAD_SOURCE;
297	if (dst != fInput.destination)
298		return B_MEDIA_BAD_DESTINATION;
299
300	color_space colorspace = format.u.raw_video.display.format;
301	BRect frame(0, 0, format.u.raw_video.display.line_width - 1,
302		format.u.raw_video.display.line_count - 1);
303
304	status_t err;
305
306	DeleteBuffers();
307	if (fOverlayEnabled) {
308		fVideoView->RemoveOverlay();
309		err = CreateBuffers(frame, colorspace, true); // try overlay
310		if (err) {
311			fprintf(stderr, "VideoNode::FormatChanged creating overlay buffer failed\n");
312			err = CreateBuffers(frame, colorspace, false); // no overlay
313		}
314	} else {
315		err = CreateBuffers(frame, colorspace, false); // no overlay
316	}
317
318	if (err) {
319		fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n");
320		return B_MEDIA_BAD_FORMAT;
321	}
322
323	fInput.format = format;
324
325	return B_OK;
326}
327
328
329void
330VideoNode::HandleBuffer(BBuffer *buffer)
331{
332	LockBitmap();
333	if (fBitmap && fWindow && fVideoView) {
334//		bigtime_t start = system_time();
335		if (fOverlayActive) {
336			if (B_OK == fBitmap->LockBits()) {
337				memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
338				fBitmap->UnlockBits();
339			}
340		} else {
341			memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
342		}
343//		printf("overlay copy: %lld usec\n", system_time() - start);
344	}
345	UnlockBitmap();
346
347	buffer->Recycle();
348
349	fVideoView->DrawFrame();
350}
351
352
353void
354VideoNode::SetOverlayEnabled(bool yesno)
355{
356	fOverlayEnabled = yesno;
357}
358
359
360void
361VideoNode::LockBitmap()
362{
363	fBitmapLocker->Lock();
364}
365
366
367BBitmap *
368VideoNode::Bitmap()
369{
370	return fBitmap;
371}
372
373
374void
375VideoNode::UnlockBitmap()
376{
377	fBitmapLocker->Unlock();
378}
379
380
381bool
382VideoNode::IsOverlayActive()
383{
384	return fOverlayActive;
385}
386
387
388status_t
389VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay)
390{
391	printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, "
392		"overlay %d\n", int(frame.left), int(frame.top), int(frame.right),
393		int(frame.bottom), int(cspace), overlay);
394
395	LockBitmap();
396	ASSERT(fBitmap == 0);
397
398	uint32 flags = 0;
399	if (overlay)
400		flags = B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL;
401
402	fBitmap = new BBitmap(frame, flags, cspace);
403	if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) {
404		delete fBitmap;
405		fBitmap = NULL;
406		fOverlayActive = false;
407		UnlockBitmap();
408		fprintf(stderr, "VideoNode::CreateBuffers failed\n");
409		return B_MEDIA_BAD_FORMAT;
410	}
411	fOverlayActive = overlay;
412	UnlockBitmap();
413
414	return B_OK;
415}
416
417
418void
419VideoNode::DeleteBuffers()
420{
421	LockBitmap();
422	delete fBitmap;
423	fBitmap = NULL;
424	UnlockBitmap();
425}
426
427
428void
429VideoNode::_InitDisplay()
430{
431	// TODO: Get rid of hardcoded values
432	BRect size(0,0,320,240);
433	fVideoView = new VideoView(size, "Video View", B_FOLLOW_ALL_SIDES,
434		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, this);
435
436	size.OffsetBy(40.f, 40.f);
437	fWindow = new VideoWindow(size, fVideoView);
438	fWindow->Show();
439}
440