xref: /haiku/src/add-ons/media/media-add-ons/videowindow/VideoNode.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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 
24 VideoNode::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 
43 VideoNode::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 
62 VideoNode::~VideoNode()
63 {
64 	Quit();
65 	DeleteBuffers();
66 	delete fBitmapLocker;
67 	if (fWindow && fWindow->Lock())
68 		fWindow->Quit();
69 }
70 
71 
72 BMediaAddOn	*
73 VideoNode::AddOn(int32 *internal_id) const
74 {
75 	*internal_id = fInternalFlavorId;
76 	return fAddOn;
77 }
78 
79 
80 void
81 VideoNode::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 
96 void
97 VideoNode::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 
114 status_t
115 VideoNode::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 
126 void
127 VideoNode::DisposeInputCookie(int32 cookie)
128 {
129 	// nothing to do
130 }
131 
132 
133 status_t
134 VideoNode::	HandleMessage(int32 message, const void *data, size_t size)
135 {
136 	return B_ERROR;
137 }
138 
139 
140 void
141 VideoNode::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 
165 void
166 VideoNode::ProducerDataStatus(const media_destination &dst, int32 status,
167 	bigtime_t at_media_time)
168 {
169 	// do nothing
170 }
171 
172 
173 status_t
174 VideoNode::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 
186 status_t
187 VideoNode::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 	// In order to display video we need to create a buffer that is either
206 	// in the overlay colorspace B_YCbCr422
207 	// or the requested colorspace if not B_YCbCr422
208 	// and we need to tell the node upstream of our choice
209 
210 	BRect frame(0, 0, format->u.raw_video.display.line_width - 1,
211 		format->u.raw_video.display.line_count - 1);
212 
213 	DeleteBuffers();
214 	status_t err;
215 
216 	if (format->u.raw_video.display.format == B_NO_COLOR_SPACE) {
217 		// upstream node is leaving it up to us so we try overlay then B_RGBA32 (We probably should try what format the screen is)
218 		err = CreateBuffers(frame, B_YCbCr422, true);
219 		SetOverlayEnabled(err == B_OK);
220 		if (!fOverlayEnabled) {
221 			// no overlay available so fall back to RGBA32
222 			err = CreateBuffers(frame, B_RGBA32, false);
223 		}
224 	} else if (format->u.raw_video.display.format == B_YCbCr422) {
225 		// upstream node is likely requesting overlay
226 		err = CreateBuffers(frame, B_YCbCr422, true);
227 		SetOverlayEnabled(err == B_OK);
228 		// if we cannot give them what they want then return error
229 	} else {
230 		// upstream node is requesting some other format
231 		SetOverlayEnabled(false);
232 		err = CreateBuffers(frame, format->u.raw_video.display.format, fOverlayEnabled);
233 	}
234 
235 	if (err) {
236 		fprintf(stderr, "VideoNode::Connected failed, fOverlayEnabled = %d\n",
237 			fOverlayEnabled);
238 		return err;
239 	} else {
240 		format->u.raw_video.display.format = fBitmap->ColorSpace();
241 	}
242 
243 	return B_OK;
244 }
245 
246 
247 status_t
248 VideoNode::Connected(const media_source &src, const media_destination &dst,
249 	const media_format &format, media_input *out_input)
250 {
251 	/* The connection process:
252 	 *                BBufferProducer::FormatProposal
253 	 *                BBufferConsumer::AcceptFormat
254 	 *                BBufferProducer::PrepareToConnect
255 	 * we are here => BBufferConsumer::Connected
256 	 *                BBufferProducer::Connect
257 	 */
258 
259 	if (dst != fInput.destination)
260 		return B_MEDIA_BAD_DESTINATION;
261 
262 	fInput.source = src;
263 	fInput.format = format;
264 
265 	if (fInput.format.u.raw_video.field_rate < 1.0)
266 		fInput.format.u.raw_video.field_rate = 25.0;
267 
268 	*out_input = fInput;
269 
270 	return B_OK;
271 }
272 
273 
274 void
275 VideoNode::Disconnected(const media_source &src, const media_destination &dst)
276 {
277 	if (src != fInput.source)
278 		return;
279 	if (dst != fInput.destination)
280 		return;
281 
282 	DeleteBuffers();
283 
284 	// disconnect the connection
285 	fInput.source = media_source::null;
286 }
287 
288 
289 status_t
290 VideoNode::FormatChanged(const media_source &src, const media_destination &dst,
291 	int32 from_change_count, const media_format &format)
292 {
293 	if (src != fInput.source)
294 		return B_MEDIA_BAD_SOURCE;
295 	if (dst != fInput.destination)
296 		return B_MEDIA_BAD_DESTINATION;
297 
298 	color_space colorspace = format.u.raw_video.display.format;
299 	BRect frame(0, 0, format.u.raw_video.display.line_width - 1,
300 		format.u.raw_video.display.line_count - 1);
301 
302 	status_t err;
303 
304 	DeleteBuffers();
305 	if (fOverlayEnabled) {
306 		fVideoView->RemoveOverlay();
307 		err = CreateBuffers(frame, colorspace, true); // try overlay
308 		if (err) {
309 			fprintf(stderr, "VideoNode::FormatChanged creating overlay buffer failed\n");
310 			err = CreateBuffers(frame, colorspace, false); // no overlay
311 		}
312 	} else {
313 		err = CreateBuffers(frame, colorspace, false); // no overlay
314 	}
315 
316 	if (err) {
317 		fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n");
318 		return B_MEDIA_BAD_FORMAT;
319 	}
320 
321 	fInput.format = format;
322 
323 	return B_OK;
324 }
325 
326 
327 void
328 VideoNode::HandleBuffer(BBuffer *buffer)
329 {
330 	LockBitmap();
331 	if (fBitmap && fWindow && fVideoView) {
332 //		bigtime_t start = system_time();
333 		if (fOverlayActive) {
334 			if (B_OK == fBitmap->LockBits()) {
335 				memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
336 				fBitmap->UnlockBits();
337 			}
338 		} else {
339 			memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
340 		}
341 //		printf("overlay copy: %Ld usec\n", system_time() - start);
342 	}
343 	UnlockBitmap();
344 
345 	buffer->Recycle();
346 
347 	fVideoView->DrawFrame();
348 }
349 
350 
351 void
352 VideoNode::SetOverlayEnabled(bool yesno)
353 {
354 	fOverlayEnabled = yesno;
355 }
356 
357 
358 void
359 VideoNode::LockBitmap()
360 {
361 	fBitmapLocker->Lock();
362 }
363 
364 
365 BBitmap *
366 VideoNode::Bitmap()
367 {
368 	return fBitmap;
369 }
370 
371 
372 void
373 VideoNode::UnlockBitmap()
374 {
375 	fBitmapLocker->Unlock();
376 }
377 
378 
379 bool
380 VideoNode::IsOverlayActive()
381 {
382 	return fOverlayActive;
383 }
384 
385 
386 status_t
387 VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay)
388 {
389 	printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, "
390 		"overlay %d\n", int(frame.left), int(frame.top), int(frame.right),
391 		int(frame.bottom), int(cspace), overlay);
392 
393 	LockBitmap();
394 	ASSERT(fBitmap == 0);
395 
396 	uint32 flags = 0;
397 	if (overlay)
398 		flags = B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL;
399 
400 	fBitmap = new BBitmap(frame, flags, cspace);
401 	if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) {
402 		delete fBitmap;
403 		fBitmap = NULL;
404 		fOverlayActive = false;
405 		UnlockBitmap();
406 		fprintf(stderr, "VideoNode::CreateBuffers failed\n");
407 		return B_MEDIA_BAD_FORMAT;
408 	}
409 	fOverlayActive = overlay;
410 	UnlockBitmap();
411 
412 	return B_OK;
413 }
414 
415 
416 void
417 VideoNode::DeleteBuffers()
418 {
419 	LockBitmap();
420 	delete fBitmap;
421 	fBitmap = NULL;
422 	UnlockBitmap();
423 }
424 
425 
426 void
427 VideoNode::_InitDisplay()
428 {
429 	// TODO: Get rid of hardcoded values
430 	BRect size(0,0,320,240);
431 	fVideoView = new VideoView(size, "Video View", B_FOLLOW_ALL_SIDES,
432 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, this);
433 
434 	size.OffsetBy(40.f, 40.f);
435 	fWindow = new VideoWindow(size, fVideoView);
436 	fWindow->Show();
437 }
438