xref: /haiku/src/apps/mediaplayer/media_node_framework/video/VideoConsumer.cpp (revision 959ff00ddee8411dabb09211f3bfbd52d87229da)
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 - Colorspace = %d\n", colorSpace);
216 
217 	fBuffers = new BBufferGroup();
218 	status = fBuffers->InitCheck();
219 	if (B_OK != status) {
220 		ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
221 		return status;
222 	}
223 
224 	// and attach the bitmaps to the buffer group
225 	BRect bounds(0, 0, width - 1, height - 1);
226 	for (uint32 i = 0; i < kBufferCount; i++) {
227 		// figure out the bitmap creation flags
228 		uint32 bitmapFlags = 0;
229 		if (fTryOverlay) {
230 			// try to use hardware overlay
231 			bitmapFlags |= B_BITMAP_WILL_OVERLAY;
232 			if (i == 0)
233 				bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL;
234 		} else
235 			bitmapFlags = B_BITMAP_IS_LOCKED;
236 
237 		fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace);
238 		status = fBitmap[i]->InitCheck();
239 		if (status >= B_OK) {
240 			buffer_clone_info info;
241 
242 			uint8* bits = (uint8*)fBitmap[i]->Bits();
243 			info.area = area_for(bits);
244 			area_info bitmapAreaInfo;
245 			status = get_area_info(info.area, &bitmapAreaInfo);
246 			if (status != B_OK) {
247 				fprintf(stderr, "VideoConsumer::CreateBuffers() - "
248 					"get_area_info(): %s\n", strerror(status));
249 				return status;
250 			}
251 
252 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
253 //printf("        area: %ld\n", bitmapAreaInfo.area);
254 //printf("        size: %ld\n", bitmapAreaInfo.size);
255 //printf("        lock: %ld\n", bitmapAreaInfo.lock);
256 //printf("  protection: %ld\n", bitmapAreaInfo.protection);
257 //printf("    ram size: %ld\n", bitmapAreaInfo.ram_size);
258 //printf("  copy_count: %ld\n", bitmapAreaInfo.copy_count);
259 //printf("   out_count: %ld\n", bitmapAreaInfo.out_count);
260 //printf("     address: %p\n", bitmapAreaInfo.address);
261 
262 			info.offset = bits - (uint8*)bitmapAreaInfo.address;
263 			info.size = (size_t)fBitmap[i]->BitsLength();
264 			info.flags = 0;
265 			info.buffer = 0;
266 				// the media buffer id
267 
268 			BBuffer *buffer = NULL;
269 			if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) {
270 				ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
271 				return status;
272 			} else
273 				PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
274 		} else {
275 			ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %s\n", strerror(status));
276 			return status;
277 		}
278 	}
279 
280 	BBuffer** buffList = new BBuffer*[kBufferCount];
281 	for (uint32 i = 0; i < kBufferCount; i++)
282 		buffList[i] = 0;
283 
284 	if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) {
285 		for (uint32 i = 0; i < kBufferCount; i++) {
286 			if (buffList[i] != NULL) {
287 				fBufferMap[i] = (uint32)buffList[i];
288 				PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]);
289 			} else {
290 				ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
291 				return B_ERROR;
292 			}
293 		}
294 	} else
295 		ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
296 
297 	delete[] buffList;
298 
299 	FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
300 	return status;
301 }
302 
303 
304 void
305 VideoConsumer::DeleteBuffers()
306 {
307 	FUNCTION("VideoConsumer::DeleteBuffers\n");
308 	if (fBuffers) {
309 		fTargetLock.Lock();
310 		if (fTargetBufferIndex >= 0) {
311 			if (fTarget)
312 				fTarget->SetBitmap(NULL);
313 			fTargetBufferIndex = -1;
314 		}
315 		fTargetLock.Unlock();
316 
317 		delete fBuffers;
318 		fBuffers = NULL;
319 
320 		for (uint32 i = 0; i < kBufferCount; i++) {
321 			snooze(20000);
322 			delete fBitmap[i];
323 			fBitmap[i] = NULL;
324 		}
325 	}
326 	FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
327 }
328 
329 
330 void
331 VideoConsumer::SetTarget(VideoTarget* target)
332 {
333 	fTargetLock.Lock();
334 	if (fTarget)
335 		fTarget->SetBitmap(NULL);
336 	fTarget = target;
337 	if (fTarget && fTargetBufferIndex >= 0)
338 		fTarget->SetBitmap(fBitmap[fTargetBufferIndex]);
339 	fTargetLock.Unlock();
340 }
341 
342 
343 void
344 VideoConsumer::SetTryOverlay(bool tryOverlay)
345 {
346 	fTryOverlay = tryOverlay;
347 }
348 
349 
350 status_t
351 VideoConsumer::Connected(const media_source& producer,
352 	const media_destination& where, const media_format& format,
353 	media_input* outInput)
354 {
355 	FUNCTION("VideoConsumer::Connected\n");
356 
357 	fIn.source = producer;
358 	fIn.format = format;
359 	fIn.node = Node();
360 	sprintf(fIn.name, "Video Consumer");
361 	*outInput = fIn;
362 
363 	uint32 user_data = 0;
364 	int32 change_tag = 1;
365 	if (CreateBuffers(format) == B_OK) {
366 		BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
367 											 fBuffers, (void *)&user_data,
368 											 &change_tag, true);
369 		fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow();
370 	} else {
371 		ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
372 		return B_ERROR;
373 	}
374 
375 	*outInput = fIn;
376 		// bytes per row might have changed
377 	fConnectionActive = true;
378 
379 	FUNCTION("VideoConsumer::Connected - EXIT\n");
380 	return B_OK;
381 }
382 
383 
384 void
385 VideoConsumer::Disconnected(const media_source& producer,
386 	const media_destination& where)
387 {
388 	FUNCTION("VideoConsumer::Disconnected\n");
389 
390 	if (where != fIn.destination || producer != fIn.source)
391 		return;
392 
393 	// reclaim our buffers
394 	int32 changeTag = 0;
395 	BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL,
396 		NULL, &changeTag, false);
397 	if (fOurBuffers) {
398 		status_t reclaimError = fBuffers->ReclaimAllBuffers();
399 		if (reclaimError != B_OK) {
400 			fprintf(stderr, "VideoConsumer::Disconnected() - Failed to "
401 				"reclaim our buffers: %s\n", strerror(reclaimError));
402 		}
403 	}
404 	// disconnect the connection
405 	fIn.source = media_source::null;
406 	fConnectionActive = false;
407 
408 	// Unset the target's bitmap. Just to be safe -- since it is usually
409 	// done when the stop event arrives, but someone may disonnect
410 	// without stopping us before.
411 	fTargetLock.Lock();
412 	if (fTargetBufferIndex >= 0) {
413 		if (fTarget)
414 			fTarget->SetBitmap(NULL);
415 		if (fOurBuffers)
416 			((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
417 		fTargetBufferIndex = -1;
418 	}
419 	fTargetLock.Unlock();
420 }
421 
422 
423 status_t
424 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
425 {
426 	FUNCTION("VideoConsumer::AcceptFormat\n");
427 
428 	if (dest != fIn.destination) {
429 		ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
430 		return B_MEDIA_BAD_DESTINATION;
431 	}
432 
433 	if (format->type == B_MEDIA_NO_TYPE)
434 		format->type = B_MEDIA_RAW_VIDEO;
435 
436 	if (format->type != B_MEDIA_RAW_VIDEO) {
437 		ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
438 		return B_MEDIA_BAD_FORMAT;
439 	}
440 
441 	if (format->u.raw_video.display.format
442 			!= media_raw_video_format::wildcard.display.format) {
443 		uint32 flags = 0;
444 		bool supported = bitmaps_support_space(
445 			format->u.raw_video.display.format, &flags);
446 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
447 		// GRRR! BeOS implementation claims not
448 		// to support these formats, while they work just fine.
449 		switch (format->u.raw_video.display.format) {
450 			case B_YCbCr422:
451 			case B_YCbCr411:
452 			case B_YCbCr444:
453 			case B_YCbCr420:
454 				supported = true;
455 				break;
456 			default:
457 				break;
458 		}
459 #endif
460 		if (!supported) {
461 			// cannot create bitmaps with such a color space
462 			ERROR("AcceptFormat - unsupported color space for BBitmaps "
463 				"(%s)!\n",
464 				color_space_to_string(format->u.raw_video.display.format));
465 			return B_MEDIA_BAD_FORMAT;
466 		}
467 		if (!fTryOverlay && (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) {
468 			// BViews do not support drawing such a bitmap
469 			ERROR("AcceptFormat - BViews cannot draw bitmaps in given "
470 				"colorspace (%s)!\n",
471 				color_space_to_string(format->u.raw_video.display.format));
472 			return B_MEDIA_BAD_FORMAT;
473 		}
474 	}
475 
476 	#ifdef TRACE_VIDEO_CONSUMER
477 		char string[256];
478 		string[0] = 0;
479 		string_for_format(*format, string, 256);
480 		FUNCTION("VideoConsumer::AcceptFormat: %s\n", string);
481 	#endif
482 
483 	return B_OK;
484 }
485 
486 
487 status_t
488 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
489 {
490 	FUNCTION("VideoConsumer::GetNextInput\n");
491 
492 	// custom build a destination for this connection
493 	// put connection number in id
494 
495 	if (*cookie < 1) {
496 		fIn.node = Node();
497 		fIn.destination.id = *cookie;
498 		sprintf(fIn.name, "Video Consumer");
499 		*outInput = fIn;
500 		(*cookie)++;
501 		return B_OK;
502 	} else {
503 		return B_MEDIA_BAD_DESTINATION;
504 	}
505 }
506 
507 
508 void
509 VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
510 {
511 }
512 
513 
514 status_t
515 VideoConsumer::GetLatencyFor(
516 	const media_destination &for_whom,
517 	bigtime_t * out_latency,
518 	media_node_id * out_timesource)
519 {
520 	FUNCTION("VideoConsumer::GetLatencyFor\n");
521 
522 	if (for_whom != fIn.destination)
523 		return B_MEDIA_BAD_DESTINATION;
524 
525 	*out_latency = fMyLatency;
526 	*out_timesource = TimeSource()->ID();
527 	return B_OK;
528 }
529 
530 
531 status_t
532 VideoConsumer::FormatChanged(const media_source& producer,
533 	const media_destination& consumer, int32 fromChangeCount,
534 	const media_format& format)
535 {
536 	FUNCTION("VideoConsumer::FormatChanged\n");
537 
538 	if (consumer != fIn.destination)
539 		return B_MEDIA_BAD_DESTINATION;
540 
541 	if (producer != fIn.source)
542 		return B_MEDIA_BAD_SOURCE;
543 
544 	fIn.format = format;
545 
546 	return CreateBuffers(format);
547 }
548 
549 
550 void
551 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
552 	bool realTimeEvent)
553 {
554 	LOOP("VideoConsumer::HandleEvent\n");
555 
556 	BBuffer* buffer;
557 
558 	switch (event->type) {
559 		case BTimedEventQueue::B_START:
560 			PROGRESS("VideoConsumer::HandleEvent - START\n");
561 			break;
562 
563 		case BTimedEventQueue::B_STOP:
564 			PROGRESS("VideoConsumer::HandleEvent - STOP\n");
565 			EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
566 			// unset the target's bitmap
567 			fTargetLock.Lock();
568 			if (fTargetBufferIndex >= 0) {
569 				if (fTarget)
570 					fTarget->SetBitmap(NULL);
571 				if (fOurBuffers)
572 					((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
573 				fTargetBufferIndex = -1;
574 			}
575 			fTargetLock.Unlock();
576 			break;
577 
578 		case BTimedEventQueue::B_HANDLE_BUFFER:
579 			LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
580 			buffer = (BBuffer *) event->pointer;
581 
582 			if (RunState() == B_STARTED && fConnectionActive) {
583 				// see if this is one of our buffers
584 				uint32 index = 0;
585 				fOurBuffers = true;
586 				while (index < kBufferCount) {
587 					if ((uint32)buffer == fBufferMap[index])
588 						break;
589 					else
590 						index++;
591 				}
592 				if (index == kBufferCount) {
593 					// no, buffers belong to consumer
594 					fOurBuffers = false;
595 					index = (fTargetBufferIndex + 1) % kBufferCount;
596 				}
597 
598 				bool dropped = false;
599 				bool recycle = true;
600 				if ((RunMode() == B_OFFLINE)
601 // TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
602 // not do the trick. I made the VideoConsumer check the performance
603 // time of the buffer and if it is 0, it plays it regardless.
604 || (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency())
605 					|| (/*(TimeSource()->Now()
606 						> (buffer->Header()->start_time - JITTER)) &&*/
607 						(TimeSource()->Now() < (buffer->Header()->start_time
608 							+ JITTER)))) {
609 					if (!fOurBuffers) {
610 						memcpy(fBitmap[index]->Bits(), buffer->Data(),
611 							fBitmap[index]->BitsLength());
612 					}
613 
614 					fTargetLock.Lock();
615 					if (fTarget) {
616 						fTarget->SetBitmap(fBitmap[index]);
617 						if (fOurBuffers) {
618 							// recycle the previous but not the current buffer
619 							if (fTargetBufferIndex >= 0) {
620 								((BBuffer*)fBufferMap[fTargetBufferIndex])
621 									->Recycle();
622 							}
623 							recycle = false;
624 						}
625 						fTargetBufferIndex = index;
626 					}
627 					fTargetLock.Unlock();
628 				} else {
629 					dropped = true;
630 					PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n"
631 						"   start_time: %lld, current: %lld, latency: %lld\n",
632 						buffer->Header()->start_time, TimeSource()->Now(),
633 						SchedulingLatency());
634 				}
635 				if (dropped) {
636 					if (fManager->LockWithTimeout(10000) == B_OK) {
637 						fManager->FrameDropped();
638 						fManager->Unlock();
639 					}
640 				}
641 				if (recycle)
642 					buffer->Recycle();
643 			} else {
644 				TRACE("RunState() != B_STARTED\n");
645 				buffer->Recycle();
646 			}
647 			break;
648 		default:
649 			ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
650 			break;
651 	}
652 }
653