1/******************************************************************************
2/
3/	File:			RadeonProducer.cpp
4/
5/	Description:	ATI Radeon Video Producer media node.
6/
7/	Copyright 2001, Carlos Hasan
8/
9*******************************************************************************/
10
11#include <fcntl.h>
12#include <malloc.h>
13#include <math.h>
14#include <stdio.h>
15#include <string.h>
16#include <sys/uio.h>
17#include <unistd.h>
18#include <scheduler.h>
19
20#include <media/Buffer.h>
21#include <media/BufferGroup.h>
22#include <media/ParameterWeb.h>
23#include <media/TimeSource.h>
24
25#include <support/Autolock.h>
26#include <support/Debug.h>
27
28#include <app/Message.h>
29
30#include "RadeonAddOn.h"
31#include "VideoIn.h"
32
33#define DPRINT(args)	{ PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); }
34
35#define TOUCH(x) ((void)(x))
36
37#define PRINTF(a,b) \
38		do { \
39			if (a < 2) { \
40				printf("CRadeonProducer::"); \
41				printf b; \
42			} \
43		} while (0)
44
45#include "RadeonProducer.h"
46
47// convert Be video standard to video-in standard;
48// Be forgot some standards and define code 7 and 8 to be MPEG1/2, i.e.
49// didn't leave any space for enhancements, so I chose to use 101 and up;
50// this way, we get a scattered list of video standards, needing special
51// functions to convert to scattered Be-code to the compact video-in-code
52video_in_standard BeToVideoInStandard( int32 be_standard )
53{
54
55	DPRINT(("BeToVideoInStandard %d \n", be_standard));
56	switch( be_standard ) {
57	case 1:	return C_VIDEO_IN_NTSC;
58	case 2:	return C_VIDEO_IN_NTSC_JAPAN;
59	case 3:	return C_VIDEO_IN_PAL_BDGHI;
60	case 4:	return C_VIDEO_IN_PAL_M;
61	case 5:	return C_VIDEO_IN_PAL_N;
62	case 6:	return C_VIDEO_IN_SECAM;
63	case 101:	return C_VIDEO_IN_NTSC_443;
64	case 102:	return C_VIDEO_IN_PAL_60;
65	case 103:	return C_VIDEO_IN_PAL_NC;
66	default:	return C_VIDEO_IN_NTSC;
67	}
68}
69
70int32 VideoInStandardToBe( video_in_standard standard )
71{
72	DPRINT(("VideoInStandardToBe %d \n", standard));
73	switch( standard ) {
74		case C_VIDEO_IN_NTSC:		return 1;
75		case C_VIDEO_IN_NTSC_JAPAN:	return 2;
76		case C_VIDEO_IN_PAL_BDGHI:	return 3;
77		case C_VIDEO_IN_PAL_M:		return 4;
78		case C_VIDEO_IN_PAL_N:		return 5;
79		case C_VIDEO_IN_SECAM:		return 6;
80		case C_VIDEO_IN_NTSC_443:	return 101;
81		case C_VIDEO_IN_PAL_60:		return 102;
82		case C_VIDEO_IN_PAL_NC:		return 103;
83		default: return 1;
84	}
85}
86
87status_t CRadeonProducer::FindInt32(
88	BMessage *config, EOptions option, int32 min_value, int32 max_value,
89	int32 default_value, int32 *value )
90{
91	char name[5];
92	status_t res;
93
94	*value = default_value;
95
96	*(int32 *)name = option;
97	name[4] = 0;
98
99	res = config->FindInt32( name, value );
100	if( res == B_NAME_NOT_FOUND )
101		return B_OK;
102
103	if( res != B_OK )
104		return res;
105
106	*value = MAX( *value, min_value );
107	*value = MIN( *value, max_value );
108	return B_OK;
109}
110
111CRadeonProducer::CRadeonProducer(
112		CRadeonAddOn *addon, const char *name, const char *device_name, int32 internal_id,
113		BMessage *config )
114  :	BMediaNode(name),
115	BMediaEventLooper(),
116	BBufferProducer(B_MEDIA_RAW_VIDEO),
117	BControllable(),
118	fVideoIn( device_name )
119{
120	DPRINT(("CRadeonProducer::CRadeonProducer()\n"));
121
122	fInitStatus = B_NO_INIT;
123
124	fInternalID = internal_id;
125	fAddOn = addon;
126
127	fBufferGroup = NULL;
128	//fUsedBufferGroup = NULL;
129
130	fProcessingLatency = 0LL;
131
132	//fConnected = false;
133	fEnabled = true;
134
135	AddNodeKind(B_PHYSICAL_INPUT);
136
137	if( fVideoIn.InitCheck() == B_OK )
138		fInitStatus = B_OK;
139
140	fSource = ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE);
141	fStandard = C_VIDEO_IN_NTSC;
142	fMode = C_VIDEO_IN_WEAVE;
143	fFormat = B_RGB32;
144	fResolution = 4;
145	fTuner = 25;
146	fBrightness = 0;
147	fContrast = 0;
148	fSaturation = 0;
149	fHue = 0;
150	fSharpness = 0;
151
152	if( config != NULL ) {
153		status_t res;
154		int32 standard;
155
156		if( (res = FindInt32( config, P_SOURCE, 0, C_VIDEO_IN_SOURCE_MAX,
157			 (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE,
158			 &fSource )) != B_OK ||
159			(res = FindInt32( config, P_STANDARD, 0, C_VIDEO_IN_STANDARD_MAX,
160				C_VIDEO_IN_NTSC, &standard )) != B_OK ||
161			(res = FindInt32( config, P_MODE, 0, C_VIDEO_IN_CAPTURE_MODE_MAX,
162				C_VIDEO_IN_FIELD, &fMode )) != B_OK ||
163			(res = FindInt32( config, P_FORMAT, -2147483647L-1, 2147483647L,
164				B_RGB16, &fFormat )) != B_OK ||
165			(res = FindInt32( config, P_RESOLUTION, 0, C_RESOLUTION_MAX,
166				4, &fResolution )) != B_OK ||
167			(res = FindInt32( config, P_TUNER, 0, C_CHANNEL_MAX,
168				25, &fTuner )) != B_OK ||
169			(res = FindInt32( config, P_BRIGHTNESS, -100, +100,
170				0, &fBrightness )) != B_OK ||
171			(res = FindInt32( config, P_CONTRAST, 0, 100,
172				0, &fContrast )) != B_OK ||
173			(res = FindInt32( config, P_SATURATION, -100, +100,
174				0, &fSaturation )) != B_OK ||
175			(res = FindInt32( config, P_HUE, -90, +90,
176				0, &fHue )) != B_OK ||
177			(res = FindInt32( config, P_SHARPNESS, 0, 15,
178				0, &fSharpness )) != B_OK )
179		{
180			DPRINT(("Corrupted settings (%s)\n", strerror( res )));
181		}
182
183		// standard is stored as internal code (which has no "holes" in its numbering);
184		// time to convert it
185		// if this value comes from our setup web is it not already linear?
186		fStandard = VideoInStandardToBe( (video_in_standard)standard );
187
188		// if there is no tuner, force composite input
189		if( (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) == 0 )
190			fSource = C_VIDEO_IN_COMPOSITE;
191
192		// format ids are scattered, so we must verify them manually
193		switch( fFormat ) {
194		case B_YCbCr422:
195		case B_GRAY8:
196		case B_RGB15:
197		case B_RGB16:
198		case B_RGB32:
199			break;
200		default:
201			fFormat = B_RGB16;
202		}
203	}
204
205	fSourceLastChange =
206	fStandardLastChange =
207	fModeLastChange =
208	fFormatLastChange =
209	fResolutionLastChange =
210	fTunerLastChange =
211	fBrightnessLastChange =
212	fContrastLastChange =
213	fSaturationLastChange =
214	fHueLastChange =
215	fSharpnessLastChange = system_time();
216
217	fOutput.destination = media_destination::null;
218	strcpy(fOutput.name, Name());
219
220	// we provide interlaced raw video in any format
221	fOutput.format.type = B_MEDIA_RAW_VIDEO;
222	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
223}
224
225
226void CRadeonProducer::setupWeb()
227{
228	///////////
229	/* Set up the parameter web */
230
231	// in "kind" value of parameters is "stampTV-compatible", i.e.
232	// if not defined, we use the name used in stampTV
233	BParameterWeb *web = new BParameterWeb();
234	BParameterGroup *controls = web->MakeGroup("Controls");
235	BParameterGroup *options = web->MakeGroup("Video");
236	/*BParameterGroup *audio = web->MakeGroup("Audio");*/
237
238	BParameterGroup *controls1 = controls->MakeGroup("Controls1");
239	BParameterGroup *controls2 = controls->MakeGroup("Controls2");
240	BParameterGroup *controls3 = controls->MakeGroup("Controls3");
241
242	BParameterGroup *options1 = options->MakeGroup("Options1");
243	BParameterGroup *options2 = options->MakeGroup("Options2");
244
245	/*BParameterGroup *audio1 = audio->MakeGroup("Audio1");
246	BParameterGroup *audio2 = audio->MakeGroup("Audio2");*/
247
248
249	// Controls
250	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) {
251		// Controls.Channel
252		BDiscreteParameter *tuner = controls1->MakeDiscreteParameter(
253			P_TUNER, B_MEDIA_NO_TYPE, "Channel:", B_TUNER_CHANNEL);
254
255		for (int channel = 0; channel <= 125; channel++) {
256			char buffer[32];
257			sprintf(buffer, "%d", channel);
258			tuner->AddItem(channel, buffer);
259		}
260	}
261
262	// Controls.Source
263	BDiscreteParameter *source = controls1->MakeDiscreteParameter(
264		P_SOURCE, B_MEDIA_RAW_VIDEO, "Video Input:", "Video Input:");
265
266	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0)
267		source->AddItem(C_VIDEO_IN_TUNER, "Tuner");
268	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0)
269		source->AddItem(C_VIDEO_IN_COMPOSITE, "Composite");
270	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0)
271		source->AddItem(C_VIDEO_IN_SVIDEO, "SVideo");
272
273	// TODO:
274	BDiscreteParameter *source2 = controls1->MakeDiscreteParameter(
275		P_AUDIO_SOURCE, B_MEDIA_RAW_VIDEO, "Audio Input:", "Audio Input:");
276	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0)
277		source2->AddItem(C_VIDEO_IN_TUNER, "Tuner");
278/*	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0)
279		source2->AddItem(C_VIDEO_IN_COMPOSITE, "Composite");
280	if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0)
281		source2->AddItem(C_VIDEO_IN_SVIDEO, "SVideo");
282*/
283
284	// Controls.Brightness/Contrast/Saturation/Hue
285	controls2->MakeContinuousParameter(P_BRIGHTNESS, B_MEDIA_RAW_VIDEO,"Brightness", "BRIGHTNESS", "", -100, 100, 1);
286	controls2->MakeContinuousParameter(P_CONTRAST, B_MEDIA_RAW_VIDEO, "Contrast", "CONTRAST", "", 0, 100, 1);
287	controls2->MakeContinuousParameter(P_SHARPNESS, B_MEDIA_RAW_VIDEO, "Sharpness", B_LEVEL, "dB", 0, 15, 1);
288
289	controls3->MakeContinuousParameter(P_SATURATION, B_MEDIA_RAW_VIDEO, "Saturation", "SATURATION", "", -100, 100, 1);
290	controls3->MakeContinuousParameter(P_HUE, B_MEDIA_RAW_VIDEO, "Hue", B_LEVEL, "��", -90, 90, 1);
291
292
293	// Options.Resolution
294	BDiscreteParameter *resolution = options1->MakeDiscreteParameter(
295		P_RESOLUTION, B_MEDIA_RAW_VIDEO, "Default Image Size:", B_RESOLUTION);
296
297	resolution->AddItem(6, "768x576");
298	resolution->AddItem(5, "720x576");
299	resolution->AddItem(4, "720x480");
300	resolution->AddItem(0, "640x480");
301	resolution->AddItem(3, "480x360");
302	resolution->AddItem(1, "320x240");
303	resolution->AddItem(2, "160x120");
304
305	// Options.Format
306	BDiscreteParameter *format = options1->MakeDiscreteParameter(
307		P_FORMAT, B_MEDIA_RAW_VIDEO, "Default Colors:", B_COLOR_SPACE);
308
309	format->AddItem(B_YCbCr422, "YCbCr422 (fastest)");
310	format->AddItem(B_GRAY8, "8 Bits/Pixel (gray)");
311	format->AddItem(B_RGB15, "15 Bits/Pixel");
312	format->AddItem(B_RGB16, "16 Bits/Pixel");
313	format->AddItem(B_RGB32, "32 Bits/Pixel");
314
315	// Options.Standard
316	BDiscreteParameter *standard = options2->MakeDiscreteParameter(
317		P_STANDARD, B_MEDIA_RAW_VIDEO, "Video Format:", B_VIDEO_FORMAT);
318
319	standard->AddItem(1, "NTSC");
320	standard->AddItem(2, "NTSC Japan");
321	standard->AddItem(101, "NTSC 443");
322	standard->AddItem(4, "PAL M");
323	standard->AddItem(3, "PAL BDGHI");
324	standard->AddItem(5, "PAL N");
325	standard->AddItem(102, "PAL 60");
326	standard->AddItem(103, "PAL NC");
327	standard->AddItem(6, "SECAM");
328
329	// Options.Mode
330	BDiscreteParameter *mode = options2->MakeDiscreteParameter(
331		P_MODE, B_MEDIA_RAW_VIDEO, "Video Interlace:", B_GENERIC);
332
333	mode->AddItem(C_VIDEO_IN_FIELD, "Field");
334	mode->AddItem(C_VIDEO_IN_BOB, "Bob");
335	mode->AddItem(C_VIDEO_IN_WEAVE, "Weave");
336
337
338	// TODO:
339	/*
340	BDiscreteParameter *standard2 = audio1->MakeDiscreteParameter(
341		P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Format:", B_VIDEO_FORMAT);
342	standard2->AddItem(0, "Stereo");
343	standard2->AddItem(1, "Mono");
344	standard2->AddItem(2, "NICAM");
345
346	BDiscreteParameter *audioSource = audio1->MakeDiscreteParameter(
347		P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Source:", B_VIDEO_FORMAT);
348	audioSource->AddItem(0, "FM");
349	audioSource->AddItem(1, "Stereo");
350	audioSource->AddItem(2, "SCART");
351	audioSource->AddItem(3, "Language A");
352	audioSource->AddItem(4, "Language B");
353
354	BDiscreteParameter *matrix= audio2->MakeDiscreteParameter(
355		P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Matrix:", B_VIDEO_FORMAT);
356	matrix->AddItem(0, "Sound A");
357	matrix->AddItem(1, "Sound B");
358	matrix->AddItem(2, "Stereo");
359	matrix->AddItem(3, "Mono");*/
360
361	/* After this call, the BControllable owns the BParameterWeb object and
362	 * will delete it for you */
363	SetParameterWeb(web);
364	/////////
365}
366
367CRadeonProducer::~CRadeonProducer()
368{
369	DPRINT(("CRadeonProducer::~CRadeonProducer()\n"));
370
371	if (fInitStatus == B_OK) {
372		/* Clean up after ourselves, in case the application didn't make us
373		 * do so. */
374		/*if (fConnected)
375			Disconnect(fOutput.source, fOutput.destination);*/
376
377		HandleStop();
378	}
379
380	delete fBufferGroup;
381	fBufferGroup = NULL;
382
383	BMessage settings;
384
385	GetConfiguration( &settings );
386
387	fAddOn->UnregisterNode( this, &settings );
388
389	Quit();
390}
391
392/* BMediaNode */
393
394port_id
395CRadeonProducer::ControlPort() const
396{
397	return BMediaNode::ControlPort();
398}
399
400BMediaAddOn *
401CRadeonProducer::AddOn(int32 *internal_id) const
402{
403	if (internal_id)
404		*internal_id = fInternalID;
405	return fAddOn;
406}
407
408status_t
409CRadeonProducer::HandleMessage(int32 message, const void *data, size_t size)
410{
411	//DPRINT(("CRadeonProducer::HandleMessage()\n"));
412
413	switch( message ) {
414		case C_GET_CONFIGURATION: {
415			const configuration_msg *request = (const configuration_msg *)data;
416			BMessage msg;
417			configuration_msg_reply *reply;
418			size_t reply_size, config_size;
419			status_t res;
420
421			if( size < sizeof( configuration_msg ))
422				return B_ERROR;
423
424			res = GetConfiguration( &msg );
425
426			config_size = msg.FlattenedSize();
427			reply_size = sizeof( *reply ) + config_size;
428			reply = (configuration_msg_reply *)malloc( reply_size );
429			if( reply == NULL )
430				return B_NO_MEMORY;
431
432			reply->res = res;
433			reply->config_size = config_size;
434			msg.Flatten( &reply->config, config_size );
435
436			write_port_etc( request->reply_port, C_GET_CONFIGURATION_REPLY,
437				reply, reply_size, B_TIMEOUT, 0 );
438
439			free( reply );
440			return B_OK;
441		}
442		default:
443			return B_ERROR;
444	//	return BControllable::HandleMessage(message, data, size);
445	}
446}
447
448void
449CRadeonProducer::Preroll()
450{
451	/* This hook may be called before the node is started to give the hardware
452	 * a chance to start. */
453	DPRINT(("CRadeonProducer::Preroll()\n"));
454}
455
456void
457CRadeonProducer::SetTimeSource(BTimeSource *time_source)
458{
459	DPRINT(("CRadeonProducer::SetTimeSource()\n"));
460
461	/* Tell frame generation thread to recalculate delay value */
462	//release_sem(fFrameSync);
463}
464
465status_t
466CRadeonProducer::RequestCompleted(const media_request_info &info)
467{
468	DPRINT(("CRadeonProducer::RequestCompleted()\n"));
469
470	return BMediaNode::RequestCompleted(info);
471}
472
473/* BMediaEventLooper */
474
475void
476CRadeonProducer::NodeRegistered()
477{
478	DPRINT(("CRadeonProducer::NodeRegistered()\n"));
479
480	if (fInitStatus != B_OK) {
481		ReportError(B_NODE_IN_DISTRESS);
482		return;
483	}
484
485	setupWeb();
486//!!//
487
488	fOutput.node = Node();
489	fOutput.source.port = ControlPort();
490	fOutput.source.id = 0;
491
492	/* Tailor these for the output of your device */
493	/********
494	fOutput.format.type = B_MEDIA_RAW_VIDEO;
495	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
496//	fOutput.format.u.raw_video.interlace = 1;
497	fOutput.format.u.raw_video.display.format = B_RGB32;
498	********/
499
500	// up to 60 frames (NTSC); jitter less then half a frame; processing time
501	// depends on whether colour space conversion is required, let's say half
502	// a frame in worst case
503	SetPriority( suggest_thread_priority( B_VIDEO_RECORDING, 60, 8000, 8000 ));
504
505	/* Start the BMediaEventLooper control loop running */
506	Run();
507}
508
509void
510CRadeonProducer::Start(bigtime_t performance_time)
511{
512	DPRINT(("CRadeonProducer::Start()\n"));
513
514	BMediaEventLooper::Start(performance_time);
515}
516
517void
518CRadeonProducer::Stop(bigtime_t performance_time, bool immediate)
519{
520	DPRINT(("CRadeonProducer::Stop()\n"));
521
522	BMediaEventLooper::Stop(performance_time, immediate);
523}
524
525void
526CRadeonProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
527{
528	DPRINT(("CRadeonProducer::Seek()\n"));
529
530	BMediaEventLooper::Seek(media_time, performance_time);
531}
532
533void
534CRadeonProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
535{
536	DPRINT(("CRadeonProducer::TimeWarp()\n"));
537
538	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
539}
540
541status_t
542CRadeonProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
543{
544	DPRINT(("CRadeonProducer::AddTimer()\n"));
545
546	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
547}
548
549void
550CRadeonProducer::SetRunMode(run_mode mode)
551{
552	DPRINT(("CRadeonProducer::SetRunMode()\n"));
553
554	BMediaEventLooper::SetRunMode(mode);
555}
556
557void
558CRadeonProducer::HandleEvent(const media_timed_event *event,
559		bigtime_t lateness, bool realTimeEvent)
560{
561	//DPRINT(("CRadeonProducer::HandleEvent()\n"));
562
563	TOUCH(lateness); TOUCH(realTimeEvent);
564
565	switch(event->type)
566	{
567		case BTimedEventQueue::B_START:
568			HandleStart(event->event_time);
569			break;
570		case BTimedEventQueue::B_STOP:
571			HandleStop();
572			break;
573		case BTimedEventQueue::B_WARP:
574			HandleTimeWarp(event->bigdata);
575			break;
576		case BTimedEventQueue::B_SEEK:
577			HandleSeek(event->bigdata);
578			break;
579		case BTimedEventQueue::B_HANDLE_BUFFER:
580		case BTimedEventQueue::B_DATA_STATUS:
581		case BTimedEventQueue::B_PARAMETER:
582		default:
583			PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type));
584			break;
585		case BTimedEventQueue::B_HARDWARE:
586			HandleHardware();
587			break;
588	}
589
590	//DPRINT(("CRadeonProducer::HandleEvent() done\n"));
591}
592
593void
594CRadeonProducer::CleanUpEvent(const media_timed_event *event)
595{
596	DPRINT(("CRadeonProducer::CleanUpEvent()\n"));
597
598	BMediaEventLooper::CleanUpEvent(event);
599}
600
601bigtime_t
602CRadeonProducer::OfflineTime()
603{
604	return BMediaEventLooper::OfflineTime();
605}
606
607void
608CRadeonProducer::ControlLoop()
609{
610	BMediaEventLooper::ControlLoop();
611}
612
613status_t
614CRadeonProducer::DeleteHook(BMediaNode * node)
615{
616	DPRINT(("CRadeonProducer::DeleteHook()\n"));
617
618	return BMediaEventLooper::DeleteHook(node);
619}
620
621
622
623/* BBufferProducer */
624
625// choose capture mode according to format and update format according to that
626status_t
627CRadeonProducer::verifySetMode( media_format *format )
628{
629	float frame_rate = fVideoIn.getFrameRate(
630		BeToVideoInStandard( fStandard )) / 1000.0f;
631
632	if( format->u.raw_video.interlace == media_raw_video_format::wildcard.interlace ) {
633		if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) {
634			format->u.raw_video.interlace = fMode == C_VIDEO_IN_BOB ? 2 : 1;
635			format->u.raw_video.field_rate = frame_rate * format->u.raw_video.interlace;
636		} else {
637			if( format->u.raw_video.field_rate == frame_rate )
638				format->u.raw_video.interlace = 1;
639			else if( format->u.raw_video.field_rate == frame_rate * 2 )
640				format->u.raw_video.interlace = 2;
641			else {
642				DPRINT(( "Unsupported field rate for active TV standard (%f)\n",
643					format->u.raw_video.field_rate ));
644				return B_MEDIA_BAD_FORMAT;
645			}
646		}
647
648	} else if( format->u.raw_video.interlace == 1 ) {
649		if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate )
650			format->u.raw_video.field_rate = frame_rate;
651		else {
652			// don't compare directly - there are rounding errors
653			if( fabs(format->u.raw_video.field_rate - frame_rate) > 0.001 ) {
654				DPRINT(( "Wrong field rate for active TV standard (%f) in progressive mode (expected %f)\n",
655					format->u.raw_video.field_rate - 29.976,
656					frame_rate - 29.976 ));
657				return B_MEDIA_BAD_FORMAT;
658			}
659		}
660
661	} else if( format->u.raw_video.interlace == 2 ) {
662		if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate )
663			format->u.raw_video.field_rate = frame_rate * 2;
664		else {
665			if( fabs(format->u.raw_video.field_rate - frame_rate * 2) > 0.001 ) {
666				DPRINT(( "Wrong field rate for active TV standard (%f) in interlace mode\n",
667					format->u.raw_video.field_rate ));
668				return B_MEDIA_BAD_FORMAT;
669			}
670		}
671
672	} else {
673		DPRINT(( "Invalid interlace mode (%d)\n", format->u.raw_video.interlace ));
674		return B_MEDIA_BAD_FORMAT;
675	}
676
677	return B_OK;
678}
679
680/*
681	Map BeOS capture mode to internal capture mode.
682*/
683int32
684CRadeonProducer::extractCaptureMode( const media_format *format )
685{
686	// if application requests interlace, it always gets BOB;
687	// if is requests non-interlace, it may get WEAVE or FIELD -
688	// if the user selected one of them, we are fine; else,
689	// we always choose WEAVE (could choose FIELD as well, make
690	// it dependant on resolution, but the more magic the more problems)
691	if( format->u.raw_video.interlace == 2 )
692		return C_VIDEO_IN_BOB;
693	else if( fMode == C_VIDEO_IN_BOB )
694		return C_VIDEO_IN_WEAVE;
695	else
696		return fMode;
697}
698
699// check pixel aspect of format and set it if it's wildcarded
700status_t
701CRadeonProducer::verifySetPixelAspect( media_format *format )
702{
703	// for simplicity, we always assume 1:1 aspect
704	if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect ||
705		format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect )
706	{
707		if( format->u.raw_video.pixel_width_aspect !=
708			format->u.raw_video.pixel_height_aspect )
709		{
710			DPRINT(( "Unsupported pixel aspect (%d:%d)\n",
711				format->u.raw_video.pixel_width_aspect,
712				format->u.raw_video.pixel_height_aspect ));
713			return B_MEDIA_BAD_FORMAT;
714		}
715	} else {
716		format->u.raw_video.pixel_width_aspect = 1;
717		format->u.raw_video.pixel_height_aspect = 1;
718	}
719
720	return B_OK;
721
722#if 0
723	// we assume 1:2 for interlaced and 1:1 for deinterlaced video
724	// (this is not really true as it depends on TV standard and
725	// resolution, but it should be enough for start)
726	if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect ||
727		format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect )
728	{
729		double ratio = mode == C_VIDEO_IN_WEAVE ? 1 : 0.5;
730
731		if( (float)format->u.raw_video.pixel_width_aspect /
732			format->u.raw_video.pixel_height_aspect != ratio )
733		{
734			DPRINT(( "Unsupported pixel aspect (%d:%d)\n",
735				format->u.raw_video.pixel_width_aspect,
736				format->u.raw_video.pixel_height_aspect ));
737			return B_MEDIA_BAD_FORMAT;
738		}
739	} else {
740		format->u.raw_video.pixel_width_aspect = 1;
741		format->u.raw_video.pixel_height_aspect =
742			mode == C_VIDEO_IN_WEAVE ? 1 : 2;
743	}
744
745	return B_OK;
746#endif
747}
748
749// verify active range defined as format
750status_t
751CRadeonProducer::verifyActiveRange( media_format *format )
752{
753	CRadeonRect active_rect;
754
755	fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect );
756
757	if( format->u.raw_video.first_active != media_raw_video_format::wildcard.first_active ) {
758		if( (int32)format->u.raw_video.first_active < 0 ) {
759			DPRINT(( "Unsupported first_active (%d)\n", format->u.raw_video.first_active ));
760			return B_MEDIA_BAD_FORMAT;
761		}
762	}
763
764	// don't care about last_active much - some programs set it to number of
765	// captured lines, which is really something different
766	// (I have the feeling, noone really knows how to use this value properly)
767	if( format->u.raw_video.last_active != media_raw_video_format::wildcard.last_active ) {
768		if( format->u.raw_video.last_active >= (uint32)active_rect.Height() ) {
769			DPRINT(( "Unsupported last_active (%d)\n", format->u.raw_video.last_active ));
770			return B_MEDIA_BAD_FORMAT;
771		}
772	}
773
774	return B_OK;
775}
776
777
778// set active range in format if yet undefined
779void
780CRadeonProducer::setActiveRange( media_format *format )
781{
782	CRadeonRect active_rect;
783
784	fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect );
785
786	if( format->u.raw_video.first_active == media_raw_video_format::wildcard.first_active )
787		format->u.raw_video.first_active = 0;
788
789	if( format->u.raw_video.last_active == media_raw_video_format::wildcard.last_active )
790		format->u.raw_video.last_active = (uint32)active_rect.Height() - 1;
791}
792
793
794// verify requested orientation
795status_t
796CRadeonProducer::verifyOrientation( media_format *format )
797{
798	if( format->u.raw_video.orientation != media_raw_video_format::wildcard.orientation ) {
799		if( format->u.raw_video.orientation != B_VIDEO_TOP_LEFT_RIGHT ) {
800			DPRINT(( "Unsupported orientation (%d)\n", format->u.raw_video.orientation ));
801			return B_MEDIA_BAD_FORMAT;
802		}
803	}
804
805	return B_OK;
806}
807
808
809// set image orientation if yet undefined
810void
811CRadeonProducer::setOrientation( media_format *format )
812{
813	if( format->u.raw_video.orientation == media_raw_video_format::wildcard.orientation )
814		format->u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
815}
816
817
818// verify requested pixel format
819status_t
820CRadeonProducer::verifyPixelFormat( media_format *format )
821{
822	if(	format->u.raw_video.display.format !=
823		media_raw_video_format::wildcard.display.format )
824	{
825		switch( format->u.raw_video.display.format ) {
826		case B_RGB32:
827		case B_RGB16:
828		case B_RGB15:
829		case B_YCbCr422:
830		case B_GRAY8:
831			break;
832
833		default:
834			DPRINT(("Unsupported colour space (%x)\n",
835				format->u.raw_video.display.format ));
836			return B_MEDIA_BAD_FORMAT;
837		}
838	}
839
840	return B_OK;
841}
842
843
844// set pixel format to user-defined default if not set yet
845void
846CRadeonProducer::setPixelFormat( media_format *format )
847{
848	if(	format->u.raw_video.display.format ==
849		media_raw_video_format::wildcard.display.format )
850		format->u.raw_video.display.format = (color_space)fFormat;
851}
852
853/*
854	Verify video size and set it if undefined.
855*/
856status_t
857CRadeonProducer::verifySetSize(
858	media_format *format, int32 mode, bool set_bytes_per_row )
859{
860	CRadeonRect active_rect;
861
862	fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect );
863
864	// width and height must both be defined, else we define it ourself,
865	// i.e. if the application leaves one of them wildcarded, we
866	// set both
867	if( format->u.raw_video.display.line_width !=
868		media_raw_video_format::wildcard.display.line_width &&
869		format->u.raw_video.display.line_count !=
870		media_raw_video_format::wildcard.display.line_count )
871	{
872		uint32 max_height = active_rect.Height();
873
874		if( mode != C_VIDEO_IN_WEAVE )
875			max_height /= 2;
876
877		if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() ||
878			format->u.raw_video.display.line_count > max_height )
879		{
880			DPRINT(("Requested image size is too high (%dx%d)\n",
881				format->u.raw_video.display.line_width,
882				format->u.raw_video.display.line_count));
883			return B_MEDIA_BAD_FORMAT;
884		}
885
886		// our format converters do up to 8 pixels at a time (grey8);
887		// to be absolutely sure we don't get trouble there, refuse
888		// any width that is not a multiple of 8
889
890		if( (format->u.raw_video.display.line_width & 7) != 0 ) {
891			DPRINT(( "Request image width is not multiple of 8 (%d)\n",
892				format->u.raw_video.display.line_width ));
893			return B_MEDIA_BAD_FORMAT;
894		}
895
896	} else {
897		switch (fResolution) {
898		case 0:
899			format->u.raw_video.display.line_width = 640;
900			format->u.raw_video.display.line_count = 480;
901			break;
902		case 3:
903			format->u.raw_video.display.line_width = 480;
904			format->u.raw_video.display.line_count = 360;
905			break;
906		case 4:
907			format->u.raw_video.display.line_width = 720;
908			format->u.raw_video.display.line_count = 480;
909			break;
910		case 5:
911			format->u.raw_video.display.line_width = 720;
912			format->u.raw_video.display.line_count = 576;
913			break;
914		case 6:
915			format->u.raw_video.display.line_width = 768;
916			format->u.raw_video.display.line_count = 576;
917			break;
918		case 1:
919			format->u.raw_video.display.line_width = 320;
920			format->u.raw_video.display.line_count = 240;
921			break;
922		case 2:
923			format->u.raw_video.display.line_width = 160;
924			format->u.raw_video.display.line_count = 120;
925			break;
926		}
927
928		if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() )
929			format->u.raw_video.display.line_width = (uint32)active_rect.Width() & ~7;
930
931		if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() )
932			format->u.raw_video.display.line_count = (uint32)active_rect.Height();
933
934		// BOB and FIELD modes provide only field, which has half height
935		if( mode != C_VIDEO_IN_WEAVE ) {
936			if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() / 2 )
937				format->u.raw_video.display.line_count = (uint32)active_rect.Height() / 2;
938		}
939	}
940
941	if( format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format ) {
942		uint32 bytes_per_row;
943
944		switch( format->u.raw_video.display.format ) {
945		case B_RGB32:
946			bytes_per_row =
947				format->u.raw_video.display.line_width * 4;
948			break;
949
950		case B_RGB15:
951		case B_RGB16:
952		case B_YCbCr422:
953			bytes_per_row =
954				format->u.raw_video.display.line_width * 2;
955			break;
956
957		default:
958			bytes_per_row =
959				format->u.raw_video.display.line_width;
960		}
961
962		if( format->u.raw_video.display.bytes_per_row !=
963			media_raw_video_format::wildcard.display.bytes_per_row )
964		{
965			if( format->u.raw_video.display.bytes_per_row < bytes_per_row ) {
966				DPRINT(( "Requested bytes per row are too small",
967					format->u.raw_video.display.bytes_per_row ));
968				return B_MEDIA_BAD_FORMAT;
969			}
970		} else if( set_bytes_per_row )
971			format->u.raw_video.display.bytes_per_row = bytes_per_row;
972	}
973
974
975	return B_OK;
976}
977
978
979// verify "offset" parameters of format
980status_t
981CRadeonProducer::verifyFormatOffsets( media_format *format )
982{
983	if( format->u.raw_video.display.pixel_offset !=
984		media_raw_video_format::wildcard.display.pixel_offset &&
985		format->u.raw_video.display.pixel_offset != 0 )
986	{
987		DPRINT(( "Requested pixel offset is not zero" ));
988		return B_MEDIA_BAD_FORMAT;
989	}
990
991	if( format->u.raw_video.display.line_offset !=
992		media_raw_video_format::wildcard.display.line_offset &&
993		format->u.raw_video.display.line_offset != 0 )
994	{
995		DPRINT(( "Requested line offset is not zero" ));
996		return B_MEDIA_BAD_FORMAT;
997	}
998
999	return B_OK;
1000}
1001
1002
1003// set "offset" parameters of format if not set yet
1004void
1005CRadeonProducer::setFormatOffsets( media_format *format )
1006{
1007	if( format->u.raw_video.display.pixel_offset ==
1008		media_raw_video_format::wildcard.display.pixel_offset )
1009		format->u.raw_video.display.pixel_offset = 0;
1010
1011	if( format->u.raw_video.display.line_offset ==
1012		media_raw_video_format::wildcard.display.line_offset )
1013		format->u.raw_video.display.line_offset = 0;
1014}
1015
1016
1017// verify "flags" parameter of format
1018status_t
1019CRadeonProducer::verifyFormatFlags( media_format *format )
1020{
1021	if( format->u.raw_video.display.flags !=
1022		media_raw_video_format::wildcard.display.flags &&
1023		format->u.raw_video.display.flags != 0 )
1024	{
1025		DPRINT(( "Requested display flags are not zero" ));
1026		return B_MEDIA_BAD_FORMAT;
1027	}
1028
1029	return B_OK;
1030}
1031
1032
1033// set "flags" parameter of format if not set yet
1034void
1035CRadeonProducer::setFormatFlags( media_format *format )
1036{
1037	if( format->u.raw_video.display.flags ==
1038		media_raw_video_format::wildcard.display.flags )
1039		format->u.raw_video.display.flags = 0;
1040}
1041
1042
1043/*
1044 *	Fill out all wildcards in a format descriptor.
1045 */
1046status_t
1047CRadeonProducer::finalizeFormat( media_format *format )
1048{
1049	if (format->type != B_MEDIA_RAW_VIDEO)
1050		return B_MEDIA_BAD_FORMAT;
1051
1052	status_t res;
1053
1054	res = verifySetMode( format );
1055	if( res != B_OK )
1056		return res;
1057
1058	int32 mode = extractCaptureMode( format );
1059
1060	res = verifyActiveRange( format );
1061	if( res != B_OK )
1062		return res;
1063
1064	setActiveRange( format );
1065
1066	res = verifyOrientation( format );
1067	if( res != B_OK )
1068		return res;
1069
1070	res = verifySetPixelAspect( format );
1071	if( res != B_OK )
1072		return res;
1073
1074	res = verifyPixelFormat( format );
1075	if( res != B_OK )
1076		return res;
1077
1078	setPixelFormat( format);
1079
1080	res = verifySetSize( format, mode, true );
1081	if( res != B_OK )
1082		return res;
1083
1084	res = verifyFormatOffsets( format );
1085	if( res != B_OK )
1086		return res;
1087
1088	setFormatOffsets( format );
1089
1090	res = verifyFormatFlags( format );
1091	if( res != B_OK )
1092		return res;
1093
1094	setFormatFlags( format );
1095	return res;
1096}
1097
1098
1099/*
1100 *	Someone has no idea what format we usually provide and asks us.
1101 *
1102 *	It's not really clear whether we are allowed to return wildcards.
1103 */
1104status_t
1105CRadeonProducer::FormatSuggestionRequested(
1106	media_type type, int32 quality, media_format *format)
1107{
1108	DPRINT(("CRadeonProducer::FormatSuggestionRequested()\n"));
1109
1110	if (type != B_MEDIA_RAW_VIDEO)
1111		return B_MEDIA_BAD_FORMAT;
1112
1113	TOUCH(quality);
1114
1115	format->type = B_MEDIA_RAW_VIDEO;
1116
1117	format->u.raw_video = media_raw_video_format::wildcard;
1118
1119	finalizeFormat( format );
1120
1121	return B_OK;
1122}
1123
1124
1125/*
1126	Initial format proposal as part of a connection establishment.
1127
1128	First, the application defines a format with many wildcards in it;
1129	this format is passed to us, so we can restrict it if necessary;
1130	we should leave as many wildcards as possible, because in the next
1131	step the consumer is asked, and he will not be happy if he has no choice left .
1132*/
1133status_t
1134CRadeonProducer::FormatProposal(const media_source &output, media_format *format)
1135{
1136	char buffer[256];
1137
1138	DPRINT(("CRadeonProducer::FormatProposal()\n"));
1139
1140	if( format == NULL )
1141		return B_BAD_VALUE;
1142
1143	if( output != fOutput.source )
1144		return B_MEDIA_BAD_SOURCE;
1145
1146	string_for_format(*format, buffer, sizeof(buffer));
1147
1148	DPRINT(("CRadeonProducer::FormatProposal() - in=%s\n", buffer));
1149
1150	if( format->type == B_MEDIA_NO_TYPE ) {
1151		// if there is not even a type, set raw video
1152		format->type = B_MEDIA_RAW_VIDEO;
1153		format->u.raw_video = media_raw_video_format::wildcard;
1154	}
1155
1156	if (format->type != B_MEDIA_RAW_VIDEO)
1157		return B_MEDIA_BAD_FORMAT;
1158
1159	status_t res;
1160
1161	// first, choose capture mode, so we know the maximum video size
1162	res = verifySetMode( format );
1163	if( res != B_OK )
1164		return res;
1165
1166	int32 mode = extractCaptureMode( format );
1167
1168	res = verifyActiveRange( format );
1169	if( res != B_OK )
1170		return res;
1171
1172	res = verifyOrientation( format );
1173	if( res != B_OK )
1174		return res;
1175
1176	setOrientation( format );
1177
1178	// simple aspect calculation: we always use 1:1, so setting this is easy
1179	res = verifySetPixelAspect( format );
1180	if( res != B_OK )
1181		return res;
1182
1183	res = verifyPixelFormat( format );
1184	if( res != B_OK )
1185		return res;
1186
1187	// if we don't set it, the consumer usually chooses a stupid format
1188	setPixelFormat( format );
1189
1190	// verify size and set if if undefined;
1191	// do that now, else the consumer will set it (making the defaults
1192	// set via preferences useless)
1193	// leave bytes_per_lines untouched, though
1194	// (we don't really care but the consumer may have some alignment restrictions)
1195	res = verifySetSize( format, mode, false );
1196	if( res != B_OK )
1197		return res;
1198
1199	res = verifyFormatOffsets( format );
1200	if( res != B_OK )
1201		return res;
1202
1203	res = verifyFormatFlags( format );
1204	if( res != B_OK )
1205		return res;
1206
1207	string_for_format(*format, buffer, sizeof(buffer));
1208
1209	DPRINT(("CRadeonProducer::FormatProposal() - out=%s\n", buffer));
1210
1211	return B_OK;
1212}
1213
1214status_t
1215CRadeonProducer::FormatChangeRequested(const media_source &source,
1216		const media_destination &destination, media_format *io_format,
1217		int32 *_deprecated_)
1218{
1219	DPRINT(("CRadeonProducer::FormatChangeRequested()\n"));
1220
1221	TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
1222	if (source != fOutput.source)
1223		return B_MEDIA_BAD_SOURCE;
1224
1225	return B_ERROR;
1226}
1227
1228status_t
1229CRadeonProducer::GetNextOutput(int32 *cookie, media_output *out_output)
1230{
1231	DPRINT(("CRadeonProducer::GetNextOutput()\n"));
1232
1233	if (!out_output)
1234		return B_BAD_VALUE;
1235
1236	if ((*cookie) != 0)
1237		return B_BAD_INDEX;
1238
1239	*out_output = fOutput;
1240	(*cookie)++;
1241	return B_OK;
1242}
1243
1244status_t
1245CRadeonProducer::DisposeOutputCookie(int32 cookie)
1246{
1247	DPRINT(("CRadeonProducer::DisposeOutputCookie()\n"));
1248
1249	TOUCH(cookie);
1250
1251	return B_OK;
1252}
1253
1254status_t
1255CRadeonProducer::SetBufferGroup(const media_source &for_source,
1256		BBufferGroup *group)
1257{
1258	DPRINT(("CRadeonProducer::SetBufferGroup()\n"));
1259
1260	if( for_source != fOutput.source )
1261		return B_MEDIA_BAD_SOURCE;
1262
1263	if( group != NULL ) {
1264		delete fBufferGroup;
1265		fBufferGroup = group;
1266	}
1267
1268	return B_OK;
1269}
1270
1271status_t
1272CRadeonProducer::VideoClippingChanged(const media_source &for_source,
1273		int16 num_shorts, int16 *clip_data,
1274		const media_video_display_info &display, int32 *_deprecated_)
1275{
1276	DPRINT(("CRadeonProducer::VideoClippingChanged()\n"));
1277
1278	TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
1279	TOUCH(display); TOUCH(_deprecated_);
1280
1281	return B_ERROR;
1282}
1283
1284status_t
1285CRadeonProducer::GetLatency(bigtime_t *out_latency)
1286{
1287	DPRINT(("CRadeonProducer::GetLatency()\n"));
1288
1289	// delay is one frame for capturing, a scheduling latency, the
1290	// DMA copying, the format conversion and the output nodes latency;
1291	// scheduling, DMA copying and format conversion is summed up in
1292	// fProcessingLatency
1293	bigtime_t capture_latency = (bigtime_t)(1000000.0 / fOutput.format.u.raw_video.field_rate);
1294
1295	// HACK: (see HandleHardware())
1296	// to be compatible to existing software, we write the ending time of
1297	// capture instead of the beginning time into buffers, thus we
1298	// have no capture delay
1299	capture_latency = 0;
1300
1301	bigtime_t buffer_latency = fProcessingLatency;
1302	BBufferProducer::GetLatency( &buffer_latency );
1303
1304	*out_latency = SchedulingLatency() + capture_latency + fProcessingLatency +
1305		buffer_latency;
1306
1307	DPRINT(("latency=%lld\n", *out_latency));
1308
1309	return B_OK;
1310}
1311
1312
1313status_t
1314CRadeonProducer::PrepareToConnect(const media_source &source,
1315		const media_destination &destination, media_format *format,
1316		media_source *out_source, char *out_name)
1317{
1318	DPRINT(("CRadeonProducer::PrepareToConnect()\n"));
1319
1320	PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \
1321			format->u.raw_video.display.line_width, \
1322			format->u.raw_video.display.line_count));
1323
1324	if (source != fOutput.source) {
1325		DPRINT(("bad source\n"));
1326		return B_MEDIA_BAD_SOURCE;
1327	}
1328
1329	if (fOutput.destination != media_destination::null) {
1330		DPRINT(("already connected\n"));
1331		return B_MEDIA_ALREADY_CONNECTED;
1332	}
1333
1334	char buffer[256];
1335	string_for_format(*format, buffer, sizeof(buffer));
1336	DPRINT(("CRadeonProducer::PrepareToConnect() - in=%s\n", buffer));
1337
1338	status_t res = finalizeFormat( format );
1339	if( res != B_OK )
1340		return res;
1341
1342	*out_source = fOutput.source;
1343	strcpy(out_name, fOutput.name);
1344
1345	string_for_format(*format, buffer, sizeof(buffer));
1346	DPRINT(("CRadeonProducer::PrepareToConnect() - out=%s\n", buffer));
1347
1348	// reserve connection
1349	fOutput.destination = destination;
1350
1351	DPRINT(("finished\n"));
1352
1353	return B_OK;
1354}
1355
1356void
1357CRadeonProducer::setDefaultBufferGroup()
1358{
1359	DPRINT(("CRadeonProducer::setDefaultBufferGroup()\n"));
1360	/*delete fBufferGroup;
1361	fBufferGroup = NULL;*/
1362	if( fBufferGroup != NULL ) {
1363		DPRINT(("Buffer already set\n"));
1364		return;
1365	}
1366
1367	fBufferGroup = new BBufferGroup(
1368		fOutput.format.u.raw_video.display.bytes_per_row *
1369		fOutput.format.u.raw_video.display.line_count,
1370		3, B_ANY_ADDRESS, B_FULL_LOCK );
1371
1372	if (fBufferGroup->InitCheck() < B_OK) {
1373		delete fBufferGroup;
1374		fBufferGroup = NULL;
1375		return;
1376	}
1377}
1378
1379void
1380CRadeonProducer::startCapturing()
1381{
1382	if( RunState() != BMediaEventLooper::B_STARTED ||
1383		fOutput.destination == media_destination::null )
1384		return;
1385
1386	fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); // was hardcoded to NTSC
1387	fVideoIn.SetBrightness(fBrightness);
1388	fVideoIn.SetContrast(fContrast);
1389	fVideoIn.SetSaturation(fSaturation);
1390	fVideoIn.SetHue(fHue);
1391	fVideoIn.SetSharpness(fSharpness);
1392
1393	fVideoIn.Start(video_in_source(fSource), BeToVideoInStandard( fStandard ),
1394		video_in_capture_mode(fCurMode),
1395		fOutput.format.u.raw_video.display.line_width,
1396		fOutput.format.u.raw_video.display.line_count);
1397
1398	char *tmp_buffer;
1399	tmp_buffer = (char *)malloc(
1400		fOutput.format.u.raw_video.display.bytes_per_row *
1401		fOutput.format.u.raw_video.display.line_count );
1402
1403	int field_sequence;
1404	short field_number;
1405	bigtime_t capture_time;
1406
1407	// do a real capturing to prime everything
1408	fVideoIn.Capture(
1409		tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE,
1410		tmp_buffer,
1411		fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count,
1412		fOutput.format.u.raw_video.display.bytes_per_row,
1413		&field_sequence, &field_number, &capture_time );
1414
1415	// capture some frames to be sure there are no pending buffers;
1416	// discard captured data to safe time (we want to catch up and not fall behind)
1417	for( int i = 0; i < 3; ++i ) {
1418		fVideoIn.Capture(
1419			B_NO_COLOR_SPACE,
1420			NULL,
1421			fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count,
1422			fOutput.format.u.raw_video.display.bytes_per_row,
1423			&field_sequence, &field_number, &capture_time );
1424
1425		DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() ));
1426	}
1427
1428	// do a real capturing to see how long it takes until the
1429	// buffer is ready, i.e. including DMA and colour conversion
1430	fVideoIn.Capture(
1431		tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE,
1432		tmp_buffer,
1433		fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count,
1434		fOutput.format.u.raw_video.display.bytes_per_row,
1435		&field_sequence, &field_number, &capture_time );
1436
1437	DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() ));
1438
1439	// now we know our internal latency
1440	fProcessingLatency = system_time() - capture_time;
1441
1442	DPRINT(("Processing latency: %d��s\n", fProcessingLatency ));
1443
1444	// store field sequence to start with zero
1445	// (capture-internal field sequence always counts up)
1446	fFieldSequenceBase = field_sequence;
1447
1448	if( tmp_buffer != NULL )
1449		free(tmp_buffer);
1450
1451	// tell latence MediaEventLooper so it can schedule events properly ahead
1452	bigtime_t total_latency;
1453
1454	GetLatency( &total_latency );
1455	SetEventLatency( total_latency );
1456
1457	// Create the buffer group
1458	setDefaultBufferGroup();
1459
1460	//fUsedBufferGroup = fBufferGroup;
1461
1462	// schedule a capture event after one field's time
1463	RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE );
1464
1465	media_timed_event event(
1466		capture_time + 1000000 / fOutput.format.u.raw_video.field_rate,
1467		BTimedEventQueue::B_HARDWARE);
1468
1469	RealTimeQueue()->AddEvent(event);
1470}
1471
1472void
1473CRadeonProducer::Connect(status_t error, const media_source &source,
1474		const media_destination &destination, const media_format &format,
1475		char *io_name)
1476{
1477	// we even get called if consumer reported error in AcceptFormat;
1478	// in this case, we must release the source already reserved by
1479	// PrepareToConnect
1480	if( error != B_OK ) {
1481		DPRINT(( "Connect: Consumer returned error (%s) - releasing source",
1482			strerror( error )));
1483		fOutput.destination = media_destination::null;
1484		return;
1485	}
1486
1487	if(	source != fOutput.source ) {
1488		DPRINT(( "Connect: Wrong source specified\n"));
1489		return;
1490	}
1491
1492	fOutput.destination = destination;
1493	fOutput.format = format;
1494	fCurMode = extractCaptureMode( &format );
1495
1496	char buffer[256];
1497	string_for_format(format, buffer, sizeof(buffer));
1498
1499	DPRINT(("CRadeonProducer::Connect() - %s\n", buffer));
1500
1501	strcpy(io_name, fOutput.name);
1502
1503	startCapturing();
1504
1505	DPRINT(("CRadeonProducer::Connect() done\n"));
1506}
1507
1508void
1509CRadeonProducer::Disconnect(const media_source &source,
1510		const media_destination &destination)
1511{
1512	DPRINT(("Disconnect()\n"));
1513
1514	if( source != fOutput.source || destination != fOutput.destination ) {
1515		DPRINT(("Disconnect: Bad source and/or destination\n"));
1516		return;
1517	}
1518
1519	fOutput.destination = media_destination::null;
1520
1521	delete fBufferGroup;
1522	fBufferGroup = NULL;
1523
1524	fVideoIn.Stop();
1525
1526	RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE );
1527
1528	// reset format to get rid of any connection left-overs
1529	fOutput.format.type = B_MEDIA_RAW_VIDEO;
1530	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
1531
1532	DPRINT(("Disconnect() done\n"));
1533}
1534
1535void
1536CRadeonProducer::LateNoticeReceived(const media_source &source,
1537		bigtime_t how_much, bigtime_t performance_time)
1538{
1539	DPRINT(("CRadeonProducer::LateNoticeReceived()\n"));
1540
1541	TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
1542}
1543
1544void
1545CRadeonProducer::EnableOutput(const media_source &source, bool enabled,
1546		int32 *_deprecated_)
1547{
1548	DPRINT(("CRadeonProducer::EnableOutput()\n"));
1549
1550	TOUCH(_deprecated_);
1551
1552	if (source != fOutput.source)
1553		return;
1554
1555	fEnabled = enabled;
1556}
1557
1558status_t
1559CRadeonProducer::SetPlayRate(int32 numer, int32 denom)
1560{
1561	DPRINT(("CRadeonProducer::SetPlayRate()\n"));
1562
1563	TOUCH(numer); TOUCH(denom);
1564
1565	return B_ERROR;
1566}
1567
1568void
1569CRadeonProducer::AdditionalBufferRequested(const media_source &source,
1570		media_buffer_id prev_buffer, bigtime_t prev_time,
1571		const media_seek_tag *prev_tag)
1572{
1573	DPRINT(("CRadeonProducer::AdditionalBufferRequested()\n"));
1574
1575	TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
1576}
1577
1578void
1579CRadeonProducer::LatencyChanged(const media_source &source,
1580		const media_destination &destination, bigtime_t new_latency,
1581		uint32 flags)
1582{
1583	DPRINT(("CRadeonProducer::LatencyChanged()\n"));
1584	BBufferProducer::LatencyChanged( source, destination, new_latency, flags );
1585}
1586
1587
1588
1589/* BControllable */
1590
1591status_t
1592CRadeonProducer::GetParameterValue(
1593	int32 id, bigtime_t *last_change, void *value, size_t *size)
1594{
1595	DPRINT(("CRadeonProducer::GetParameterValue(%d)\n", id));
1596
1597	switch (id) {
1598	case P_SOURCE:
1599		*last_change = fSourceLastChange;
1600		*size = sizeof(fSource);
1601		*((uint32 *) value) = fSource;
1602		break;
1603	case P_STANDARD:
1604		*last_change = fStandardLastChange;
1605		*size = sizeof(fStandard);
1606		*((uint32 *) value) = fStandard;
1607		break;
1608	case P_MODE:
1609		*last_change = fModeLastChange;
1610		*size = sizeof(fMode);
1611		*((uint32 *) value) = fMode;
1612		break;
1613	case P_FORMAT:
1614		*last_change = fFormatLastChange;
1615		*size = sizeof(fFormat);
1616		*((uint32 *) value) = fFormat;
1617		break;
1618	case P_RESOLUTION:
1619		*last_change = fResolutionLastChange;
1620		*size = sizeof(fResolution);
1621		*((uint32 *) value) = fResolution;
1622		break;
1623	case P_TUNER:
1624		*last_change = fTunerLastChange;
1625		*size = sizeof(fTuner);
1626		*((uint32 *) value) = fTuner;
1627		break;
1628	case P_BRIGHTNESS:
1629		*last_change = fBrightnessLastChange;
1630		*size = sizeof(fBrightness);
1631		*((float *) value) = fBrightness;
1632		break;
1633	case P_CONTRAST:
1634		*last_change = fContrastLastChange;
1635		*size = sizeof(fContrast);
1636		*((float *) value) = fContrast;
1637		break;
1638	case P_SATURATION:
1639		*last_change = fSaturationLastChange;
1640		*size = sizeof(fSaturation);
1641		*((float *) value) = fSaturation;
1642		break;
1643	case P_HUE:
1644		*last_change = fHueLastChange;
1645		*size = sizeof(fHue);
1646		*((float *) value) = fHue;
1647		break;
1648	case P_SHARPNESS:
1649		*last_change = fSharpnessLastChange;
1650		*size = sizeof(fSharpness);
1651		*((float *) value) = fSharpness;
1652		break;
1653	default:
1654		DPRINT(("Unknown parameter\n"));
1655		return B_BAD_VALUE;
1656	}
1657
1658	return B_OK;
1659}
1660
1661
1662/*
1663 *	Change video format instantly.
1664 *
1665 *	Used when user changes a settings that affect the video format.
1666 *	The new format must be the current format with some values
1667 *	replaced with wildcards. Don't put too many wildcards:
1668 *	some settings (like video size) are normally chosen by the
1669 *	application, so don't use this function as a secret override.
1670 */
1671void
1672CRadeonProducer::instantFormatChange( media_format *new_format )
1673{
1674	if( fOutput.destination == media_destination::null )
1675		return;
1676
1677	if( finalizeFormat( new_format ) != B_OK ) {
1678		DPRINT(("Current format does not allow to change interlace mode on-the-fly\n"));
1679		return;
1680	}
1681
1682	if( ChangeFormat( fOutput.source, fOutput.destination, new_format ) != B_OK ) {
1683		DPRINT(("Consumer does not allow to change interlace mode instantly\n"));
1684		return;
1685	}
1686
1687	fOutput.format = *new_format;
1688	fCurMode = extractCaptureMode( new_format );
1689
1690	fVideoIn.Stop();
1691	startCapturing();
1692}
1693
1694void
1695CRadeonProducer::SetParameterValue(
1696	int32 id, bigtime_t when, const void *value, size_t size)
1697{
1698	DPRINT(("CRadeonProducer::SetParameterValue()\n"));
1699
1700	if (!value || size != sizeof(uint32))
1701		return;
1702
1703	switch (id) {
1704	case P_SOURCE:
1705		if (*((const int32 *) value) == fSource)
1706			return;
1707		fSource = *((const uint32 *) value);
1708		fSourceLastChange = when;
1709
1710		// if there is no tuner, force composite input
1711		// (eXposer sets source manually to tuner, even if there is none)
1712		// if there is no tuner, it isn't in the list and can't be picked!
1713		//if( (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) == 0 )
1714		//	fSource = C_VIDEO_IN_COMPOSITE;
1715
1716		break;
1717	case P_STANDARD: {
1718		if (*((const int32 *) value) == fStandard)
1719			return;
1720
1721		fStandard = BeToVideoInStandard( *((const int32 *) value) );
1722
1723		fStandardLastChange = when;
1724
1725		media_format new_format = fOutput.format;
1726
1727		new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate;
1728		new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace;
1729		new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect;
1730		new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect;
1731		new_format.u.raw_video.first_active = media_raw_video_format::wildcard.first_active;
1732		new_format.u.raw_video.last_active = media_raw_video_format::wildcard.last_active;
1733
1734		instantFormatChange( &new_format );
1735		break; }
1736	case P_MODE: {
1737		if (*((const int32 *) value) == fMode)
1738			return;
1739
1740		fMode = *((const uint32 *) value);
1741		fModeLastChange = when;
1742
1743		media_format new_format = fOutput.format;
1744
1745		new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate;
1746		new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace;
1747		new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect;
1748		new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect;
1749
1750		instantFormatChange( &new_format );
1751
1752		break; }
1753	case P_FORMAT: {
1754		if (*((const int32 *) value) == fFormat)
1755			return;
1756		fFormat = *((const uint32 *) value);
1757		fFormatLastChange = when;
1758
1759		media_format new_format = fOutput.format;
1760
1761		new_format.u.raw_video.display.format = media_raw_video_format::wildcard.display.format;
1762		new_format.u.raw_video.display.bytes_per_row = media_raw_video_format::wildcard.display.bytes_per_row;
1763
1764		instantFormatChange( &new_format );
1765		break; }
1766	case P_RESOLUTION:
1767		if (*((const int32 *) value) == fResolution)
1768			return;
1769		fResolution = *((const uint32 *) value);
1770		fResolutionLastChange = when;
1771		// no live update - see instantFormatChange()
1772		break;
1773	case P_TUNER:
1774		if (*((const int32 *) value) == fTuner)
1775			return;
1776		fTuner = *((const uint32 *) value);
1777		fTunerLastChange = when;
1778		fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); // was hardcoded to NTSC
1779		break;
1780	case P_BRIGHTNESS:
1781		if (*((const float *) value) == fBrightness)
1782			return;
1783		fBrightness = (int32)*((const float *) value);
1784		fBrightnessLastChange = when;
1785		fVideoIn.SetBrightness(fBrightness);
1786		break;
1787	case P_CONTRAST:
1788		if (*((const float *) value) == fContrast)
1789			return;
1790		fContrast = (int32)*((const float *) value);
1791		fContrastLastChange = when;
1792		fVideoIn.SetContrast(fContrast);
1793		break;
1794	case P_SATURATION:
1795		if (*((const float *) value) == fSaturation)
1796			return;
1797		fSaturation = (int32)*((const float *) value);
1798		fSaturationLastChange = when;
1799		fVideoIn.SetSaturation(fSaturation);
1800		break;
1801	case P_HUE:
1802		if (*((const float *) value) == fHue)
1803			return;
1804		fHue = (int32)*((const float *) value);
1805		fHueLastChange = when;
1806		fVideoIn.SetHue(fHue);
1807		break;
1808	case P_SHARPNESS:
1809		if (*((const float *) value) == fSharpness)
1810			return;
1811		fSharpness = (int32)*((const float *) value);
1812		fSharpnessLastChange = when;
1813		fVideoIn.SetSharpness(fSharpness);
1814		break;
1815	default:
1816		return;
1817	}
1818
1819	BroadcastNewParameterValue(when, id, const_cast<void *>(value), sizeof(uint32));
1820}
1821
1822status_t
1823CRadeonProducer::StartControlPanel(BMessenger *out_messenger)
1824{
1825	return BControllable::StartControlPanel(out_messenger);
1826}
1827
1828status_t CRadeonProducer::AddInt32(
1829	BMessage *msg, EOptions option, int32 value )
1830{
1831	char name[5];
1832
1833	*(int32 *)name = option;
1834	name[4] = 0;
1835
1836	return msg->AddInt32( name, value );
1837}
1838
1839status_t
1840CRadeonProducer::GetConfiguration( BMessage *out )
1841{
1842	status_t res;
1843
1844	if( (res = AddInt32( out, P_SOURCE, fSource )) != B_OK ||
1845		(res = AddInt32( out, P_STANDARD, BeToVideoInStandard( fStandard ))) != B_OK ||
1846		(res = AddInt32( out, P_MODE, fMode )) != B_OK ||
1847		(res = AddInt32( out, P_FORMAT, fFormat )) != B_OK ||
1848		(res = AddInt32( out, P_RESOLUTION, fResolution )) != B_OK ||
1849		(res = AddInt32( out, P_TUNER, fTuner )) != B_OK ||
1850		(res = AddInt32( out, P_BRIGHTNESS, fBrightness )) != B_OK ||
1851		(res = AddInt32( out, P_CONTRAST, fContrast )) != B_OK ||
1852		(res = AddInt32( out, P_SATURATION, fSaturation )) != B_OK ||
1853		(res = AddInt32( out, P_HUE, fHue )) != B_OK ||
1854		(res = AddInt32( out, P_SHARPNESS, fSharpness )) != B_OK )
1855		return res;
1856
1857	return B_OK;
1858}
1859
1860
1861/* VideoProducer */
1862
1863void
1864CRadeonProducer::HandleStart(bigtime_t performance_time)
1865{
1866	/* Start producing frames, even if the output hasn't been connected yet. */
1867	DPRINT(("CRadeonProducer::HandleStart()\n"));
1868
1869	if( RunState() != BMediaEventLooper::B_STOPPED ) {
1870		DPRINT(("already running\n"));
1871		return;
1872	}
1873
1874	SetRunState( BMediaEventLooper::B_STARTED );
1875
1876	startCapturing();
1877}
1878
1879void
1880CRadeonProducer::HandleStop(void)
1881{
1882	DPRINT(("CRadeonProducer::HandleStop()\n"));
1883
1884	fVideoIn.Stop();
1885
1886	// discard pending capture event
1887	RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE );
1888}
1889
1890void
1891CRadeonProducer::HandleTimeWarp(bigtime_t performance_time)
1892{
1893	DPRINT(("CRadeonProducer::HandleTimeWarp()\n"));
1894}
1895
1896void
1897CRadeonProducer::HandleSeek(bigtime_t performance_time)
1898{
1899	DPRINT(("CRadeonProducer::HandleSeek()\n"));
1900}
1901
1902void
1903CRadeonProducer::captureField( bigtime_t *capture_time )
1904{
1905	*capture_time = system_time();
1906
1907	// don't capture if output is disabled
1908	if (!fEnabled)
1909		return;
1910
1911	BBuffer *buffer = fBufferGroup->RequestBuffer(
1912		fOutput.format.u.raw_video.display.bytes_per_row *
1913		fOutput.format.u.raw_video.display.line_count, 0LL);
1914
1915	if (!buffer) {
1916		DPRINT(( "No buffer\n" ));
1917		return;
1918	}
1919
1920	media_header *h = buffer->Header();
1921	h->type = B_MEDIA_RAW_VIDEO;
1922	h->time_source = TimeSource()->ID();
1923	h->size_used = fOutput.format.u.raw_video.display.bytes_per_row *
1924					fOutput.format.u.raw_video.display.line_count;
1925	h->file_pos = 0;
1926	h->orig_size = 0;
1927	h->data_offset = 0;
1928	h->u.raw_video.field_gamma = 1.0;
1929	h->u.raw_video.pulldown_number = 0;
1930	h->u.raw_video.first_active_line = fOutput.format.u.raw_video.first_active;
1931	h->u.raw_video.line_count = fOutput.format.u.raw_video.display.line_count;
1932
1933	int field_sequence;
1934	short field_number;
1935
1936	int dropped=fVideoIn.Capture(
1937		fOutput.format.u.raw_video.display.format,
1938		buffer->Data(),
1939		fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count,
1940		fOutput.format.u.raw_video.display.bytes_per_row,
1941		&field_sequence,
1942		&field_number,
1943		capture_time);
1944
1945	// HACK: store end instead of start time
1946	// obviously, the _start_ time of a frame is always in the past by one
1947	// frame; unfortunately, programs like stamptv expect the start time to
1948	// be in the present, else they drop the frame; therefore, we write the
1949	// _end_ time into the start time field, and everyone is happy (though
1950	// the time is wrong)
1951	// this leads to many tweaks in the code!
1952	h->start_time = TimeSource()->PerformanceTimeFor( *capture_time );
1953
1954	h->u.raw_video.field_sequence = field_sequence - fFieldSequenceBase;
1955	h->u.raw_video.field_number = field_number;
1956
1957	if (dropped > 1) {
1958		PRINT(("%d frames dropped\n", dropped-1));
1959	}
1960
1961	if (SendBuffer(buffer, fOutput.destination) < B_OK) {
1962		PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
1963		buffer->Recycle();
1964	}
1965}
1966
1967void
1968CRadeonProducer::HandleHardware()
1969{
1970	bigtime_t capture_time;
1971
1972	//DPRINT(("Hi\n"));
1973
1974	if( RunState() != BMediaEventLooper::B_STARTED )
1975		return;
1976
1977	captureField( &capture_time );
1978
1979	// generate next event after next field
1980	media_timed_event event(
1981		capture_time + 1000000 / fOutput.format.u.raw_video.field_rate,
1982		BTimedEventQueue::B_HARDWARE);
1983
1984	RealTimeQueue()->AddEvent(event);
1985}
1986