xref: /haiku/src/apps/mediaplayer/media_node_framework/video/VideoConsumer.cpp (revision 2222d0559df303a9846a2fad53741f8b20b14d7c)
1 /*	Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2  *	Distributed under the terms of the Be Sample Code license.
3  *
4  *	Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
5  *	Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
6  *	All Rights Reserved. Distributed under the terms of the MIT license.
7  */
8 #include "VideoConsumer.h"
9 
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <Buffer.h>
16 #include <BufferGroup.h>
17 #include <NodeInfo.h>
18 #include <Bitmap.h>
19 #include <View.h>
20 #include <scheduler.h>
21 #include <TimeSource.h>
22 #include <MediaRoster.h>
23 
24 #include "ColorSpaceToString.h"
25 #include "NodeManager.h"
26 #include "VideoTarget.h"
27 
28 
29 // debugging
30 //#define TRACE_VIDEO_CONSUMER
31 #ifdef TRACE_VIDEO_CONSUMER
32 # define TRACE(x...)		printf(x)
33 # define PROGRESS(x...)		printf(x)
34 # define FUNCTION(x...)		printf(x)
35 # define LOOP(x...)			printf(x)
36 # define ERROR(x...)		fprintf(stderr, x)
37 #else
38 # define TRACE(x...)
39 # define PROGRESS(x...)
40 # define FUNCTION(x...)
41 # define LOOP(x...)
42 # define ERROR(x...)		fprintf(stderr, x)
43 #endif
44 
45 #define M1 ((double)1000000.0)
46 #define JITTER		20000
47 
48 
49 VideoConsumer::VideoConsumer(const char* name, BMediaAddOn* addon,
50 		const uint32 internal_id, NodeManager* manager,
51 		VideoTarget* target)
52 	: BMediaNode(name),
53 	  BMediaEventLooper(),
54 	  BBufferConsumer(B_MEDIA_RAW_VIDEO),
55 	  fInternalID(internal_id),
56 	  fAddOn(addon),
57 	  fConnectionActive(false),
58 	  fMyLatency(20000),
59 	  fOurBuffers(false),
60 	  fBuffers(NULL),
61 	  fManager(manager),
62 	  fTargetLock(),
63 	  fTarget(target),
64 	  fTargetBufferIndex(-1),
65 	  fTryOverlay(true)
66 {
67 	FUNCTION("VideoConsumer::VideoConsumer\n");
68 
69 	AddNodeKind(B_PHYSICAL_OUTPUT);
70 	SetEventLatency(0);
71 
72 	for (uint32 i = 0; i < kBufferCount; i++) {
73 		fBitmap[i] = NULL;
74 		fBufferMap[i] = 0;
75 	}
76 
77 	SetPriority(B_DISPLAY_PRIORITY);
78 }
79 
80 
81 VideoConsumer::~VideoConsumer()
82 {
83 	Quit();
84 	DeleteBuffers();
85 }
86 
87 
88 BMediaAddOn*
89 VideoConsumer::AddOn(long* cookie) const
90 {
91 	FUNCTION("VideoConsumer::AddOn\n");
92 	// do the right thing if we're ever used with an add-on
93 	*cookie = fInternalID;
94 	return fAddOn;
95 }
96 
97 
98 // This implementation is required to get around a bug in
99 // the ppc compiler.
100 void
101 VideoConsumer::Start(bigtime_t performance_time)
102 {
103 	BMediaEventLooper::Start(performance_time);
104 }
105 
106 
107 void
108 VideoConsumer::Stop(bigtime_t performance_time, bool immediate)
109 {
110 	BMediaEventLooper::Stop(performance_time, immediate);
111 }
112 
113 
114 void
115 VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
116 {
117 	BMediaEventLooper::Seek(media_time, performance_time);
118 }
119 
120 
121 void
122 VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
123 {
124 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
125 }
126 
127 
128 void
129 VideoConsumer::NodeRegistered()
130 {
131 	FUNCTION("VideoConsumer::NodeRegistered\n");
132 	fIn.destination.port = ControlPort();
133 	fIn.destination.id = 0;
134 	fIn.source = media_source::null;
135 	fIn.format.type = B_MEDIA_RAW_VIDEO;
136 	// wild cards yet
137 	fIn.format.u.raw_video = media_raw_video_format::wildcard;
138 	fIn.format.u.raw_video.interlace = 1;
139 	fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
140 	fIn.format.u.raw_video.display.bytes_per_row = 0;
141 	fIn.format.u.raw_video.display.line_width = 0;
142 	fIn.format.u.raw_video.display.line_count = 0;
143 
144 	Run();
145 }
146 
147 
148 status_t
149 VideoConsumer::RequestCompleted(const media_request_info& info)
150 {
151 	FUNCTION("VideoConsumer::RequestCompleted\n");
152 	switch(info.what) {
153 		case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
154 			if (info.status != B_OK)
155 				ERROR("VideoConsumer::RequestCompleted: Not using our "
156 					"buffers!\n");
157 			break;
158 
159 		default:
160 			break;
161 	}
162 	return B_OK;
163 }
164 
165 
166 status_t
167 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
168 {
169 	return B_OK;
170 }
171 
172 
173 void
174 VideoConsumer::BufferReceived(BBuffer* buffer)
175 {
176 	LOOP("VideoConsumer::Buffer #%ld received\n", buffer->ID());
177 
178 	if (RunState() == B_STOPPED) {
179 		buffer->Recycle();
180 		return;
181 	}
182 
183 	media_timed_event event(buffer->Header()->start_time,
184 		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
185 		BTimedEventQueue::B_RECYCLE_BUFFER);
186 	EventQueue()->AddEvent(event);
187 }
188 
189 
190 void
191 VideoConsumer::ProducerDataStatus(const media_destination& forWhom,
192 	int32 status, bigtime_t atMediaTime)
193 {
194 	FUNCTION("VideoConsumer::ProducerDataStatus\n");
195 
196 	if (forWhom != fIn.destination)
197 		return;
198 }
199 
200 
201 status_t
202 VideoConsumer::CreateBuffers(const media_format& format)
203 {
204 	FUNCTION("VideoConsumer::CreateBuffers\n");
205 
206 	// delete any old buffers
207 	DeleteBuffers();
208 
209 	status_t status = B_OK;
210 
211 	// create a buffer group
212 	uint32 width = format.u.raw_video.display.line_width;
213 	uint32 height = format.u.raw_video.display.line_count;
214 	color_space colorSpace = format.u.raw_video.display.format;
215 	PROGRESS("VideoConsumer::CreateBuffers - Width = %ld - Height = %ld - "
216 		"Colorspace = %d\n", width, height, colorSpace);
217 
218 	fBuffers = new BBufferGroup();
219 	status = fBuffers->InitCheck();
220 	if (B_OK != status) {
221 		ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
222 		return status;
223 	}
224 
225 	// and attach the bitmaps to the buffer group
226 	BRect bounds(0, 0, width - 1, height - 1);
227 	for (uint32 i = 0; i < kBufferCount; i++) {
228 		// figure out the bitmap creation flags
229 		uint32 bitmapFlags = 0;
230 		if (fTryOverlay) {
231 			// try to use hardware overlay
232 			bitmapFlags |= B_BITMAP_WILL_OVERLAY;
233 			if (i == 0)
234 				bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL;
235 		} else
236 			bitmapFlags = B_BITMAP_IS_LOCKED;
237 
238 		fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace);
239 		status = fBitmap[i]->InitCheck();
240 		if (status >= B_OK) {
241 			buffer_clone_info info;
242 
243 			uint8* bits = (uint8*)fBitmap[i]->Bits();
244 			info.area = area_for(bits);
245 			area_info bitmapAreaInfo;
246 			status = get_area_info(info.area, &bitmapAreaInfo);
247 			if (status != B_OK) {
248 				fprintf(stderr, "VideoConsumer::CreateBuffers() - "
249 					"get_area_info(): %s\n", strerror(status));
250 				return status;
251 			}
252 
253 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
254 //printf("        area: %ld\n", bitmapAreaInfo.area);
255 //printf("        size: %ld\n", bitmapAreaInfo.size);
256 //printf("        lock: %ld\n", bitmapAreaInfo.lock);
257 //printf("  protection: %ld\n", bitmapAreaInfo.protection);
258 //printf("    ram size: %ld\n", bitmapAreaInfo.ram_size);
259 //printf("  copy_count: %ld\n", bitmapAreaInfo.copy_count);
260 //printf("   out_count: %ld\n", bitmapAreaInfo.out_count);
261 //printf("     address: %p\n", bitmapAreaInfo.address);
262 
263 			info.offset = bits - (uint8*)bitmapAreaInfo.address;
264 			info.size = (size_t)fBitmap[i]->BitsLength();
265 			info.flags = 0;
266 			info.buffer = 0;
267 				// the media buffer id
268 
269 			BBuffer *buffer = NULL;
270 			if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) {
271 				ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER "
272 					"TO GROUP (%ld): %s\n", i, strerror(status));
273 				return status;
274 			} else {
275 				PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD "
276 					"BUFFER TO GROUP\n");
277 			}
278 		} else {
279 			ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
280 				"BUFFER (%ld): %s\n", i, strerror(status));
281 			return status;
282 		}
283 	}
284 
285 	BBuffer** buffList = new BBuffer*[kBufferCount];
286 	for (uint32 i = 0; i < kBufferCount; i++)
287 		buffList[i] = 0;
288 
289 	if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) {
290 		for (uint32 i = 0; i < kBufferCount; i++) {
291 			if (buffList[i] != NULL) {
292 				fBufferMap[i] = (uint32)buffList[i];
293 				PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]);
294 			} else {
295 				ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
296 				return B_ERROR;
297 			}
298 		}
299 	} else
300 		ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
301 
302 	delete[] buffList;
303 
304 	FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
305 	return status;
306 }
307 
308 
309 void
310 VideoConsumer::DeleteBuffers()
311 {
312 	FUNCTION("VideoConsumer::DeleteBuffers\n");
313 	if (fBuffers) {
314 		fTargetLock.Lock();
315 		if (fTargetBufferIndex >= 0) {
316 			if (fTarget)
317 				fTarget->SetBitmap(NULL);
318 			fTargetBufferIndex = -1;
319 		}
320 		fTargetLock.Unlock();
321 
322 		delete fBuffers;
323 		fBuffers = NULL;
324 
325 		for (uint32 i = 0; i < kBufferCount; i++) {
326 			snooze(20000);
327 			delete fBitmap[i];
328 			fBitmap[i] = NULL;
329 		}
330 	}
331 	FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
332 }
333 
334 
335 void
336 VideoConsumer::SetTarget(VideoTarget* target)
337 {
338 	fTargetLock.Lock();
339 	if (fTarget)
340 		fTarget->SetBitmap(NULL);
341 	fTarget = target;
342 	if (fTarget && fTargetBufferIndex >= 0)
343 		fTarget->SetBitmap(fBitmap[fTargetBufferIndex]);
344 	fTargetLock.Unlock();
345 }
346 
347 
348 void
349 VideoConsumer::SetTryOverlay(bool tryOverlay)
350 {
351 	fTryOverlay = tryOverlay;
352 }
353 
354 
355 status_t
356 VideoConsumer::Connected(const media_source& producer,
357 	const media_destination& where, const media_format& format,
358 	media_input* outInput)
359 {
360 	FUNCTION("VideoConsumer::Connected\n");
361 
362 	fIn.source = producer;
363 	fIn.format = format;
364 	fIn.node = Node();
365 	sprintf(fIn.name, "Video Consumer");
366 	*outInput = fIn;
367 
368 	uint32 user_data = 0;
369 	int32 change_tag = 1;
370 	if (CreateBuffers(format) == B_OK) {
371 		BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
372 											 fBuffers, (void *)&user_data,
373 											 &change_tag, true);
374 		fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow();
375 	} else {
376 		ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
377 		return B_ERROR;
378 	}
379 
380 	*outInput = fIn;
381 		// bytes per row might have changed
382 	fConnectionActive = true;
383 
384 	FUNCTION("VideoConsumer::Connected - EXIT\n");
385 	return B_OK;
386 }
387 
388 
389 void
390 VideoConsumer::Disconnected(const media_source& producer,
391 	const media_destination& where)
392 {
393 	FUNCTION("VideoConsumer::Disconnected\n");
394 
395 	if (where != fIn.destination || producer != fIn.source)
396 		return;
397 
398 	// reclaim our buffers
399 	int32 changeTag = 0;
400 	BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL,
401 		NULL, &changeTag, false);
402 	if (fOurBuffers) {
403 		status_t reclaimError = fBuffers->ReclaimAllBuffers();
404 		if (reclaimError != B_OK) {
405 			fprintf(stderr, "VideoConsumer::Disconnected() - Failed to "
406 				"reclaim our buffers: %s\n", strerror(reclaimError));
407 		}
408 	}
409 	// disconnect the connection
410 	fIn.source = media_source::null;
411 	fConnectionActive = false;
412 
413 	// Unset the target's bitmap. Just to be safe -- since it is usually
414 	// done when the stop event arrives, but someone may disonnect
415 	// without stopping us before.
416 	fTargetLock.Lock();
417 	if (fTargetBufferIndex >= 0) {
418 		if (fTarget)
419 			fTarget->SetBitmap(NULL);
420 		if (fOurBuffers)
421 			((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
422 		fTargetBufferIndex = -1;
423 	}
424 	fTargetLock.Unlock();
425 }
426 
427 
428 status_t
429 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
430 {
431 	FUNCTION("VideoConsumer::AcceptFormat\n");
432 
433 	if (dest != fIn.destination) {
434 		ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
435 		return B_MEDIA_BAD_DESTINATION;
436 	}
437 
438 	if (format->type == B_MEDIA_NO_TYPE)
439 		format->type = B_MEDIA_RAW_VIDEO;
440 
441 	if (format->type != B_MEDIA_RAW_VIDEO) {
442 		ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
443 		return B_MEDIA_BAD_FORMAT;
444 	}
445 
446 	if (format->u.raw_video.display.format
447 			!= media_raw_video_format::wildcard.display.format) {
448 		uint32 flags = 0;
449 		bool supported = bitmaps_support_space(
450 			format->u.raw_video.display.format, &flags);
451 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
452 		// GRRR! BeOS implementation claims not
453 		// to support these formats, while they work just fine.
454 		switch (format->u.raw_video.display.format) {
455 			case B_YCbCr422:
456 			case B_YCbCr411:
457 			case B_YCbCr444:
458 			case B_YCbCr420:
459 				supported = true;
460 				break;
461 			default:
462 				break;
463 		}
464 #endif
465 		if (!supported) {
466 			// cannot create bitmaps with such a color space
467 			ERROR("AcceptFormat - unsupported color space for BBitmaps "
468 				"(%s)!\n",
469 				color_space_to_string(format->u.raw_video.display.format));
470 			return B_MEDIA_BAD_FORMAT;
471 		}
472 		if (!fTryOverlay && (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) {
473 			// BViews do not support drawing such a bitmap
474 			ERROR("AcceptFormat - BViews cannot draw bitmaps in given "
475 				"colorspace (%s)!\n",
476 				color_space_to_string(format->u.raw_video.display.format));
477 			return B_MEDIA_BAD_FORMAT;
478 		}
479 	}
480 
481 	#ifdef TRACE_VIDEO_CONSUMER
482 		char string[256];
483 		string[0] = 0;
484 		string_for_format(*format, string, 256);
485 		FUNCTION("VideoConsumer::AcceptFormat: %s\n", string);
486 	#endif
487 
488 	return B_OK;
489 }
490 
491 
492 status_t
493 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
494 {
495 	FUNCTION("VideoConsumer::GetNextInput\n");
496 
497 	// custom build a destination for this connection
498 	// put connection number in id
499 
500 	if (*cookie < 1) {
501 		fIn.node = Node();
502 		fIn.destination.id = *cookie;
503 		sprintf(fIn.name, "Video Consumer");
504 		*outInput = fIn;
505 		(*cookie)++;
506 		return B_OK;
507 	} else {
508 		return B_MEDIA_BAD_DESTINATION;
509 	}
510 }
511 
512 
513 void
514 VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
515 {
516 }
517 
518 
519 status_t
520 VideoConsumer::GetLatencyFor(
521 	const media_destination &for_whom,
522 	bigtime_t * out_latency,
523 	media_node_id * out_timesource)
524 {
525 	FUNCTION("VideoConsumer::GetLatencyFor\n");
526 
527 	if (for_whom != fIn.destination)
528 		return B_MEDIA_BAD_DESTINATION;
529 
530 	*out_latency = fMyLatency;
531 	*out_timesource = TimeSource()->ID();
532 	return B_OK;
533 }
534 
535 
536 status_t
537 VideoConsumer::FormatChanged(const media_source& producer,
538 	const media_destination& consumer, int32 fromChangeCount,
539 	const media_format& format)
540 {
541 	FUNCTION("VideoConsumer::FormatChanged\n");
542 
543 	if (consumer != fIn.destination)
544 		return B_MEDIA_BAD_DESTINATION;
545 
546 	if (producer != fIn.source)
547 		return B_MEDIA_BAD_SOURCE;
548 
549 	fIn.format = format;
550 
551 	return CreateBuffers(format);
552 }
553 
554 
555 void
556 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
557 	bool realTimeEvent)
558 {
559 	LOOP("VideoConsumer::HandleEvent\n");
560 
561 	BBuffer* buffer;
562 
563 	switch (event->type) {
564 		case BTimedEventQueue::B_START:
565 			PROGRESS("VideoConsumer::HandleEvent - START\n");
566 			break;
567 
568 		case BTimedEventQueue::B_STOP:
569 			PROGRESS("VideoConsumer::HandleEvent - STOP\n");
570 			EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
571 			// unset the target's bitmap
572 			fTargetLock.Lock();
573 			if (fTargetBufferIndex >= 0) {
574 				if (fTarget)
575 					fTarget->SetBitmap(NULL);
576 				if (fOurBuffers)
577 					((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
578 				fTargetBufferIndex = -1;
579 			}
580 			fTargetLock.Unlock();
581 			break;
582 
583 		case BTimedEventQueue::B_HANDLE_BUFFER:
584 			LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
585 			buffer = (BBuffer *) event->pointer;
586 
587 			if (RunState() == B_STARTED && fConnectionActive) {
588 				// see if this is one of our buffers
589 				uint32 index = 0;
590 				fOurBuffers = true;
591 				while (index < kBufferCount) {
592 					if ((uint32)buffer == fBufferMap[index])
593 						break;
594 					else
595 						index++;
596 				}
597 				if (index == kBufferCount) {
598 					// no, buffers belong to consumer
599 					fOurBuffers = false;
600 					index = (fTargetBufferIndex + 1) % kBufferCount;
601 				}
602 
603 				bool dropped = false;
604 				bool recycle = true;
605 				if ((RunMode() == B_OFFLINE)
606 // TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
607 // not do the trick. I made the VideoConsumer check the performance
608 // time of the buffer and if it is 0, it plays it regardless.
609 || (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency())
610 					|| (/*(TimeSource()->Now()
611 						> (buffer->Header()->start_time - JITTER)) &&*/
612 						(TimeSource()->Now() < (buffer->Header()->start_time
613 							+ JITTER)))) {
614 					if (!fOurBuffers) {
615 						memcpy(fBitmap[index]->Bits(), buffer->Data(),
616 							fBitmap[index]->BitsLength());
617 					}
618 
619 					fTargetLock.Lock();
620 					if (fTarget) {
621 						fTarget->SetBitmap(fBitmap[index]);
622 						if (fOurBuffers) {
623 							// recycle the previous but not the current buffer
624 							if (fTargetBufferIndex >= 0) {
625 								((BBuffer*)fBufferMap[fTargetBufferIndex])
626 									->Recycle();
627 							}
628 							recycle = false;
629 						}
630 						fTargetBufferIndex = index;
631 					}
632 					fTargetLock.Unlock();
633 				} else {
634 					dropped = true;
635 					PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n"
636 						"   start_time: %lld, current: %lld, latency: %lld\n",
637 						buffer->Header()->start_time, TimeSource()->Now(),
638 						SchedulingLatency());
639 				}
640 				if (dropped) {
641 					if (fManager->LockWithTimeout(10000) == B_OK) {
642 						fManager->FrameDropped();
643 						fManager->Unlock();
644 					}
645 				}
646 				if (recycle)
647 					buffer->Recycle();
648 			} else {
649 				TRACE("RunState() != B_STARTED\n");
650 				buffer->Recycle();
651 			}
652 			break;
653 		default:
654 			ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
655 			break;
656 	}
657 }
658