10fc56ed5SStephan Aßmus/*	Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
20fc56ed5SStephan Aßmus *	Distributed under the terms of the Be Sample Code license.
30fc56ed5SStephan Aßmus *
40fc56ed5SStephan Aßmus *	Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
50fc56ed5SStephan Aßmus *	Copyright (c) 2000-2008, Stephan A��mus <superstippi@gmx.de>,
60fc56ed5SStephan Aßmus *	All Rights Reserved. Distributed under the terms of the MIT license.
70fc56ed5SStephan Aßmus */
80fc56ed5SStephan Aßmus#include "VideoConsumer.h"
90fc56ed5SStephan Aßmus
100fc56ed5SStephan Aßmus#include <fcntl.h>
1112f1188bSStephan Aßmus#include <stdio.h>
120fc56ed5SStephan Aßmus#include <string.h>
1312f1188bSStephan Aßmus#include <unistd.h>
1412f1188bSStephan Aßmus
1512f1188bSStephan Aßmus#include <Buffer.h>
1612f1188bSStephan Aßmus#include <BufferGroup.h>
170fc56ed5SStephan Aßmus#include <NodeInfo.h>
180fc56ed5SStephan Aßmus#include <Bitmap.h>
190fc56ed5SStephan Aßmus#include <View.h>
200fc56ed5SStephan Aßmus#include <scheduler.h>
210fc56ed5SStephan Aßmus#include <TimeSource.h>
220fc56ed5SStephan Aßmus#include <MediaRoster.h>
230fc56ed5SStephan Aßmus
249639f1bfSStephan Aßmus#include "ColorSpaceToString.h"
250fc56ed5SStephan Aßmus#include "NodeManager.h"
260fc56ed5SStephan Aßmus#include "VideoTarget.h"
270fc56ed5SStephan Aßmus
280fc56ed5SStephan Aßmus
290fc56ed5SStephan Aßmus// debugging
300fc56ed5SStephan Aßmus//#define TRACE_VIDEO_CONSUMER
310fc56ed5SStephan Aßmus#ifdef TRACE_VIDEO_CONSUMER
320fc56ed5SStephan Aßmus# define TRACE(x...)		printf(x)
330fc56ed5SStephan Aßmus# define PROGRESS(x...)		printf(x)
340fc56ed5SStephan Aßmus# define FUNCTION(x...)		printf(x)
350fc56ed5SStephan Aßmus# define LOOP(x...)			printf(x)
360fc56ed5SStephan Aßmus# define ERROR(x...)		fprintf(stderr, x)
370fc56ed5SStephan Aßmus#else
380fc56ed5SStephan Aßmus# define TRACE(x...)
390fc56ed5SStephan Aßmus# define PROGRESS(x...)
400fc56ed5SStephan Aßmus# define FUNCTION(x...)
410fc56ed5SStephan Aßmus# define LOOP(x...)
420fc56ed5SStephan Aßmus# define ERROR(x...)		fprintf(stderr, x)
430fc56ed5SStephan Aßmus#endif
440fc56ed5SStephan Aßmus
450fc56ed5SStephan Aßmus#define M1 ((double)1000000.0)
466eac6bdeSStephan Aßmusstatic const bigtime_t kMaxBufferLateness = 20000LL;
470fc56ed5SStephan Aßmus
480fc56ed5SStephan Aßmus
490fc56ed5SStephan AßmusVideoConsumer::VideoConsumer(const char* name, BMediaAddOn* addon,
500fc56ed5SStephan Aßmus		const uint32 internal_id, NodeManager* manager,
510fc56ed5SStephan Aßmus		VideoTarget* target)
520fc56ed5SStephan Aßmus	: BMediaNode(name),
530fc56ed5SStephan Aßmus	  BMediaEventLooper(),
540fc56ed5SStephan Aßmus	  BBufferConsumer(B_MEDIA_RAW_VIDEO),
550fc56ed5SStephan Aßmus	  fInternalID(internal_id),
560fc56ed5SStephan Aßmus	  fAddOn(addon),
570fc56ed5SStephan Aßmus	  fConnectionActive(false),
586eac6bdeSStephan Aßmus	  fMyLatency(3000),
590fc56ed5SStephan Aßmus	  fOurBuffers(false),
600fc56ed5SStephan Aßmus	  fBuffers(NULL),
610fc56ed5SStephan Aßmus	  fManager(manager),
620fc56ed5SStephan Aßmus	  fTargetLock(),
630fc56ed5SStephan Aßmus	  fTarget(target),
646eac6bdeSStephan Aßmus	  fLastBufferIndex(-1),
6512f1188bSStephan Aßmus	  fTryOverlay(true)
660fc56ed5SStephan Aßmus{
670fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::VideoConsumer\n");
680fc56ed5SStephan Aßmus
690fc56ed5SStephan Aßmus	AddNodeKind(B_PHYSICAL_OUTPUT);
700fc56ed5SStephan Aßmus	SetEventLatency(0);
710fc56ed5SStephan Aßmus
720fc56ed5SStephan Aßmus	for (uint32 i = 0; i < kBufferCount; i++) {
730fc56ed5SStephan Aßmus		fBitmap[i] = NULL;
746eac6bdeSStephan Aßmus		fBufferMap[i] = NULL;
750fc56ed5SStephan Aßmus	}
760fc56ed5SStephan Aßmus
770fc56ed5SStephan Aßmus	SetPriority(B_DISPLAY_PRIORITY);
780fc56ed5SStephan Aßmus}
790fc56ed5SStephan Aßmus
800fc56ed5SStephan Aßmus
810fc56ed5SStephan AßmusVideoConsumer::~VideoConsumer()
820fc56ed5SStephan Aßmus{
830fc56ed5SStephan Aßmus	Quit();
840fc56ed5SStephan Aßmus	DeleteBuffers();
850fc56ed5SStephan Aßmus}
860fc56ed5SStephan Aßmus
870fc56ed5SStephan Aßmus
880fc56ed5SStephan AßmusBMediaAddOn*
89843a122fSJérôme DuvalVideoConsumer::AddOn(int32* cookie) const
900fc56ed5SStephan Aßmus{
910fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::AddOn\n");
920fc56ed5SStephan Aßmus	// do the right thing if we're ever used with an add-on
930fc56ed5SStephan Aßmus	*cookie = fInternalID;
940fc56ed5SStephan Aßmus	return fAddOn;
950fc56ed5SStephan Aßmus}
960fc56ed5SStephan Aßmus
970fc56ed5SStephan Aßmus
980fc56ed5SStephan Aßmusvoid
990fc56ed5SStephan AßmusVideoConsumer::NodeRegistered()
1000fc56ed5SStephan Aßmus{
1010fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::NodeRegistered\n");
1020fc56ed5SStephan Aßmus	fIn.destination.port = ControlPort();
1030fc56ed5SStephan Aßmus	fIn.destination.id = 0;
1040fc56ed5SStephan Aßmus	fIn.source = media_source::null;
1050fc56ed5SStephan Aßmus	fIn.format.type = B_MEDIA_RAW_VIDEO;
1060fc56ed5SStephan Aßmus	// wild cards yet
1070fc56ed5SStephan Aßmus	fIn.format.u.raw_video = media_raw_video_format::wildcard;
1080fc56ed5SStephan Aßmus	fIn.format.u.raw_video.interlace = 1;
1090fc56ed5SStephan Aßmus	fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
1100fc56ed5SStephan Aßmus	fIn.format.u.raw_video.display.bytes_per_row = 0;
1110fc56ed5SStephan Aßmus	fIn.format.u.raw_video.display.line_width = 0;
1120fc56ed5SStephan Aßmus	fIn.format.u.raw_video.display.line_count = 0;
1130fc56ed5SStephan Aßmus
1140fc56ed5SStephan Aßmus	Run();
1150fc56ed5SStephan Aßmus}
1160fc56ed5SStephan Aßmus
1170fc56ed5SStephan Aßmus
1180fc56ed5SStephan Aßmusstatus_t
1190fc56ed5SStephan AßmusVideoConsumer::RequestCompleted(const media_request_info& info)
1200fc56ed5SStephan Aßmus{
1210fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::RequestCompleted\n");
1220fc56ed5SStephan Aßmus	switch(info.what) {
1230fc56ed5SStephan Aßmus		case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
1240fc56ed5SStephan Aßmus			if (info.status != B_OK)
1250fc56ed5SStephan Aßmus				ERROR("VideoConsumer::RequestCompleted: Not using our "
1260fc56ed5SStephan Aßmus					"buffers!\n");
1270fc56ed5SStephan Aßmus			break;
1280fc56ed5SStephan Aßmus
1290fc56ed5SStephan Aßmus		default:
1300fc56ed5SStephan Aßmus			break;
1310fc56ed5SStephan Aßmus	}
1320fc56ed5SStephan Aßmus	return B_OK;
1330fc56ed5SStephan Aßmus}
1340fc56ed5SStephan Aßmus
1350fc56ed5SStephan Aßmus
1360fc56ed5SStephan Aßmusstatus_t
1370fc56ed5SStephan AßmusVideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
1380fc56ed5SStephan Aßmus{
1390fc56ed5SStephan Aßmus	return B_OK;
1400fc56ed5SStephan Aßmus}
1410fc56ed5SStephan Aßmus
1420fc56ed5SStephan Aßmus
1430fc56ed5SStephan Aßmusvoid
1440fc56ed5SStephan AßmusVideoConsumer::BufferReceived(BBuffer* buffer)
1450fc56ed5SStephan Aßmus{
1464b99d297SAlexander von Gluck IV	LOOP("VideoConsumer::Buffer #%" B_PRId32 " received\n", buffer->ID());
1470fc56ed5SStephan Aßmus
1480fc56ed5SStephan Aßmus	if (RunState() == B_STOPPED) {
1490fc56ed5SStephan Aßmus		buffer->Recycle();
1500fc56ed5SStephan Aßmus		return;
1510fc56ed5SStephan Aßmus	}
1520fc56ed5SStephan Aßmus
1530fc56ed5SStephan Aßmus	media_timed_event event(buffer->Header()->start_time,
1540fc56ed5SStephan Aßmus		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
1550fc56ed5SStephan Aßmus		BTimedEventQueue::B_RECYCLE_BUFFER);
1560fc56ed5SStephan Aßmus	EventQueue()->AddEvent(event);
1570fc56ed5SStephan Aßmus}
1580fc56ed5SStephan Aßmus
1590fc56ed5SStephan Aßmus
1600fc56ed5SStephan Aßmusvoid
1610fc56ed5SStephan AßmusVideoConsumer::ProducerDataStatus(const media_destination& forWhom,
1620fc56ed5SStephan Aßmus	int32 status, bigtime_t atMediaTime)
1630fc56ed5SStephan Aßmus{
1640fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::ProducerDataStatus\n");
1650fc56ed5SStephan Aßmus
1660fc56ed5SStephan Aßmus	if (forWhom != fIn.destination)
1670fc56ed5SStephan Aßmus		return;
1680fc56ed5SStephan Aßmus}
1690fc56ed5SStephan Aßmus
1700fc56ed5SStephan Aßmus
1710fc56ed5SStephan Aßmusstatus_t
1720fc56ed5SStephan AßmusVideoConsumer::CreateBuffers(const media_format& format)
1730fc56ed5SStephan Aßmus{
1740fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::CreateBuffers\n");
1750fc56ed5SStephan Aßmus
1760fc56ed5SStephan Aßmus	// delete any old buffers
1770fc56ed5SStephan Aßmus	DeleteBuffers();
1780fc56ed5SStephan Aßmus
1790fc56ed5SStephan Aßmus	status_t status = B_OK;
1800fc56ed5SStephan Aßmus
1810fc56ed5SStephan Aßmus	// create a buffer group
1820fc56ed5SStephan Aßmus	uint32 width = format.u.raw_video.display.line_width;
1830fc56ed5SStephan Aßmus	uint32 height = format.u.raw_video.display.line_count;
1840fc56ed5SStephan Aßmus	color_space colorSpace = format.u.raw_video.display.format;
1854b99d297SAlexander von Gluck IV	PROGRESS("VideoConsumer::CreateBuffers - Width = %" B_PRIu32 " - "
1864b99d297SAlexander von Gluck IV		"Height = %" B_PRIu32 " - Colorspace = %d\n",
1874b99d297SAlexander von Gluck IV		width, height, colorSpace);
1880fc56ed5SStephan Aßmus
1890fc56ed5SStephan Aßmus	fBuffers = new BBufferGroup();
1900fc56ed5SStephan Aßmus	status = fBuffers->InitCheck();
1910fc56ed5SStephan Aßmus	if (B_OK != status) {
1920fc56ed5SStephan Aßmus		ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
1930fc56ed5SStephan Aßmus		return status;
1940fc56ed5SStephan Aßmus	}
19512f1188bSStephan Aßmus
1960fc56ed5SStephan Aßmus	// and attach the bitmaps to the buffer group
1970fc56ed5SStephan Aßmus	BRect bounds(0, 0, width - 1, height - 1);
1980fc56ed5SStephan Aßmus	for (uint32 i = 0; i < kBufferCount; i++) {
1990fc56ed5SStephan Aßmus		// figure out the bitmap creation flags
2000fc56ed5SStephan Aßmus		uint32 bitmapFlags = 0;
20112f1188bSStephan Aßmus		if (fTryOverlay) {
2020fc56ed5SStephan Aßmus			// try to use hardware overlay
2030fc56ed5SStephan Aßmus			bitmapFlags |= B_BITMAP_WILL_OVERLAY;
2040fc56ed5SStephan Aßmus			if (i == 0)
2050fc56ed5SStephan Aßmus				bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL;
2060fc56ed5SStephan Aßmus		} else
2070fc56ed5SStephan Aßmus			bitmapFlags = B_BITMAP_IS_LOCKED;
2080fc56ed5SStephan Aßmus
2090fc56ed5SStephan Aßmus		fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace);
2100fc56ed5SStephan Aßmus		status = fBitmap[i]->InitCheck();
2110fc56ed5SStephan Aßmus		if (status >= B_OK) {
2120fc56ed5SStephan Aßmus			buffer_clone_info info;
2130fc56ed5SStephan Aßmus
2140fc56ed5SStephan Aßmus			uint8* bits = (uint8*)fBitmap[i]->Bits();
2150fc56ed5SStephan Aßmus			info.area = area_for(bits);
2160fc56ed5SStephan Aßmus			area_info bitmapAreaInfo;
2170fc56ed5SStephan Aßmus			status = get_area_info(info.area, &bitmapAreaInfo);
2180fc56ed5SStephan Aßmus			if (status != B_OK) {
2190fc56ed5SStephan Aßmus				fprintf(stderr, "VideoConsumer::CreateBuffers() - "
2200fc56ed5SStephan Aßmus					"get_area_info(): %s\n", strerror(status));
2210fc56ed5SStephan Aßmus				return status;
2220fc56ed5SStephan Aßmus			}
2230fc56ed5SStephan Aßmus
2240fc56ed5SStephan Aßmus//printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
2250fc56ed5SStephan Aßmus//printf("        area: %ld\n", bitmapAreaInfo.area);
2260fc56ed5SStephan Aßmus//printf("        size: %ld\n", bitmapAreaInfo.size);
2270fc56ed5SStephan Aßmus//printf("        lock: %ld\n", bitmapAreaInfo.lock);
2280fc56ed5SStephan Aßmus//printf("  protection: %ld\n", bitmapAreaInfo.protection);
2290fc56ed5SStephan Aßmus//printf("    ram size: %ld\n", bitmapAreaInfo.ram_size);
2300fc56ed5SStephan Aßmus//printf("  copy_count: %ld\n", bitmapAreaInfo.copy_count);
2310fc56ed5SStephan Aßmus//printf("   out_count: %ld\n", bitmapAreaInfo.out_count);
2320fc56ed5SStephan Aßmus//printf("     address: %p\n", bitmapAreaInfo.address);
2330fc56ed5SStephan Aßmus
2340fc56ed5SStephan Aßmus			info.offset = bits - (uint8*)bitmapAreaInfo.address;
2350fc56ed5SStephan Aßmus			info.size = (size_t)fBitmap[i]->BitsLength();
2360fc56ed5SStephan Aßmus			info.flags = 0;
2370fc56ed5SStephan Aßmus			info.buffer = 0;
238d7f2503aSStephan Aßmus				// the media buffer id
2390fc56ed5SStephan Aßmus
2406eac6bdeSStephan Aßmus			BBuffer* buffer = NULL;
2410fc56ed5SStephan Aßmus			if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) {
242591ce51aSStephan Aßmus				ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER "
243843a122fSJérôme Duval					"TO GROUP (%" B_PRId32 "): %s\n", i, strerror(status));
2440fc56ed5SStephan Aßmus				return status;
245591ce51aSStephan Aßmus			} else {
246591ce51aSStephan Aßmus				PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD "
247591ce51aSStephan Aßmus					"BUFFER TO GROUP\n");
248591ce51aSStephan Aßmus			}
2496eac6bdeSStephan Aßmus			fBufferMap[i] = buffer;
2500fc56ed5SStephan Aßmus		} else {
251591ce51aSStephan Aßmus			ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
252843a122fSJérôme Duval				"BUFFER (Index %" B_PRId32 " Width %" B_PRId32 " Height %"
253843a122fSJérôme Duval				B_PRId32 " Colorspace %d: %s\n", i, width, height, colorSpace,
254843a122fSJérôme Duval				strerror(status));
2550fc56ed5SStephan Aßmus			return status;
2560fc56ed5SStephan Aßmus		}
2570fc56ed5SStephan Aßmus	}
2580fc56ed5SStephan Aßmus
2590fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
2600fc56ed5SStephan Aßmus	return status;
2610fc56ed5SStephan Aßmus}
2620fc56ed5SStephan Aßmus
2630fc56ed5SStephan Aßmus
2640fc56ed5SStephan Aßmusvoid
2650fc56ed5SStephan AßmusVideoConsumer::DeleteBuffers()
2660fc56ed5SStephan Aßmus{
2670fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::DeleteBuffers\n");
2680fc56ed5SStephan Aßmus	if (fBuffers) {
2690fc56ed5SStephan Aßmus		fTargetLock.Lock();
2706eac6bdeSStephan Aßmus		if (fLastBufferIndex >= 0) {
2710fc56ed5SStephan Aßmus			if (fTarget)
2720fc56ed5SStephan Aßmus				fTarget->SetBitmap(NULL);
2736eac6bdeSStephan Aßmus			fLastBufferIndex = -1;
2740fc56ed5SStephan Aßmus		}
2750fc56ed5SStephan Aßmus		fTargetLock.Unlock();
2760fc56ed5SStephan Aßmus
2770fc56ed5SStephan Aßmus		delete fBuffers;
2780fc56ed5SStephan Aßmus		fBuffers = NULL;
2790fc56ed5SStephan Aßmus
2800fc56ed5SStephan Aßmus		for (uint32 i = 0; i < kBufferCount; i++) {
2810fc56ed5SStephan Aßmus			snooze(20000);
2820fc56ed5SStephan Aßmus			delete fBitmap[i];
2830fc56ed5SStephan Aßmus			fBitmap[i] = NULL;
2840fc56ed5SStephan Aßmus		}
2850fc56ed5SStephan Aßmus	}
2860fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
2870fc56ed5SStephan Aßmus}
2880fc56ed5SStephan Aßmus
2890fc56ed5SStephan Aßmus
2900fc56ed5SStephan Aßmusvoid
2910fc56ed5SStephan AßmusVideoConsumer::SetTarget(VideoTarget* target)
2920fc56ed5SStephan Aßmus{
2930fc56ed5SStephan Aßmus	fTargetLock.Lock();
2940fc56ed5SStephan Aßmus	if (fTarget)
2950fc56ed5SStephan Aßmus		fTarget->SetBitmap(NULL);
2960fc56ed5SStephan Aßmus	fTarget = target;
2976eac6bdeSStephan Aßmus	if (fTarget && fLastBufferIndex >= 0)
2986eac6bdeSStephan Aßmus		fTarget->SetBitmap(fBitmap[fLastBufferIndex]);
2990fc56ed5SStephan Aßmus	fTargetLock.Unlock();
3000fc56ed5SStephan Aßmus}
3010fc56ed5SStephan Aßmus
3020fc56ed5SStephan Aßmus
30312f1188bSStephan Aßmusvoid
30412f1188bSStephan AßmusVideoConsumer::SetTryOverlay(bool tryOverlay)
30512f1188bSStephan Aßmus{
30612f1188bSStephan Aßmus	fTryOverlay = tryOverlay;
30712f1188bSStephan Aßmus}
30812f1188bSStephan Aßmus
30912f1188bSStephan Aßmus
3100fc56ed5SStephan Aßmusstatus_t
3110fc56ed5SStephan AßmusVideoConsumer::Connected(const media_source& producer,
3120fc56ed5SStephan Aßmus	const media_destination& where, const media_format& format,
3130fc56ed5SStephan Aßmus	media_input* outInput)
3140fc56ed5SStephan Aßmus{
3150fc56ed5SStephan Aßmus	FUNCTION("VideoConsumer::Connected\n");
3160fc56ed5SStephan Aßmus
3170fc56ed5SStephan Aßmus	fIn.source = producer;
3180fc56ed5SStephan Aßmus	fIn.format = format;
3190fc56ed5SStephan Aßmus	fIn.node = Node();
3200fc56ed5SStephan Aßmus	sprintf(fIn.name, "Video Consumer");
3210fc56ed5SStephan Aßmus	*outInput = fIn;
3220fc56ed5SStephan Aßmus
3236eac6bdeSStephan Aßmus	uint32 userData = 0;
324749df748SStephan Aßmus	int32 changeTag = 1;
325749df748SStephan Aßmus	status_t ret = CreateBuffers(format);
326749df748SStephan Aßmus	if (ret == B_OK) {
327a574c5ceSStephan Aßmus		// TODO: With overlay bitmaps, there seems to be a problem with
328a574c5ceSStephan Aßmus		// mapping the BBitmap areas into the BBuffers. Until that is fixed,
329a574c5ceSStephan Aßmus		// don't enable a shared BBufferGroup.
330a574c5ceSStephan Aßmus		if (!fTryOverlay) {
331a574c5ceSStephan Aßmus			ret =