xref: /haiku/src/add-ons/media/media-add-ons/videowindow/VideoNode.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 	return B_OK;
206 }
207 
208 
209 status_t
210 VideoNode::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 
276 void
277 VideoNode::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 
291 status_t
292 VideoNode::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 
329 void
330 VideoNode::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: %Ld usec\n", system_time() - start);
344 	}
345 	UnlockBitmap();
346 
347 	buffer->Recycle();
348 
349 	fVideoView->DrawFrame();
350 }
351 
352 
353 void
354 VideoNode::SetOverlayEnabled(bool yesno)
355 {
356 	fOverlayEnabled = yesno;
357 }
358 
359 
360 void
361 VideoNode::LockBitmap()
362 {
363 	fBitmapLocker->Lock();
364 }
365 
366 
367 BBitmap *
368 VideoNode::Bitmap()
369 {
370 	return fBitmap;
371 }
372 
373 
374 void
375 VideoNode::UnlockBitmap()
376 {
377 	fBitmapLocker->Unlock();
378 }
379 
380 
381 bool
382 VideoNode::IsOverlayActive()
383 {
384 	return fOverlayActive;
385 }
386 
387 
388 status_t
389 VideoNode::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 
418 void
419 VideoNode::DeleteBuffers()
420 {
421 	LockBitmap();
422 	delete fBitmap;
423 	fBitmap = NULL;
424 	UnlockBitmap();
425 }
426 
427 
428 void
429 VideoNode::_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