1d6f3c208Sbeveloper/*
2d6f3c208Sbeveloper	Copyright 1999, Be Incorporated.   All Rights Reserved.
3d6f3c208Sbeveloper	This file may be used under the terms of the Be Sample Code License.
4d6f3c208Sbeveloper*/
5b289aaf6SAxel Dörfler
6b289aaf6SAxel Dörfler
7d6f3c208Sbeveloper#include <fcntl.h>
8d6f3c208Sbeveloper#include <malloc.h>
9d6f3c208Sbeveloper#include <math.h>
10d6f3c208Sbeveloper#include <stdio.h>
11d6f3c208Sbeveloper#include <string.h>
12d6f3c208Sbeveloper#include <sys/uio.h>
13d6f3c208Sbeveloper#include <unistd.h>
14d6f3c208Sbeveloper
15d6f3c208Sbeveloper#include <Buffer.h>
16d6f3c208Sbeveloper#include <BufferGroup.h>
17d6f3c208Sbeveloper#include <ParameterWeb.h>
18d6f3c208Sbeveloper#include <TimeSource.h>
19d6f3c208Sbeveloper
20d6f3c208Sbeveloper#include <Autolock.h>
21d6f3c208Sbeveloper#include <Debug.h>
22d6f3c208Sbeveloper
23d6f3c208Sbeveloper#define TOUCH(x) ((void)(x))
24d6f3c208Sbeveloper
25d6f3c208Sbeveloper#define PRINTF(a,b) \
26d6f3c208Sbeveloper		do { \
27d6f3c208Sbeveloper			if (a < 2) { \
28d6f3c208Sbeveloper				printf("VideoProducer::"); \
29d6f3c208Sbeveloper				printf b; \
30d6f3c208Sbeveloper			} \
31d6f3c208Sbeveloper		} while (0)
32d6f3c208Sbeveloper
33d6f3c208Sbeveloper#include "Producer.h"
34d6f3c208Sbeveloper
35d6f3c208Sbeveloper#define FIELD_RATE 30.f
36d6f3c208Sbeveloper
37d6f3c208SbeveloperVideoProducer::VideoProducer(
38d6f3c208Sbeveloper		BMediaAddOn *addon, const char *name, int32 internal_id)
39d6f3c208Sbeveloper  :	BMediaNode(name),
40d6f3c208Sbeveloper	BMediaEventLooper(),
41d6f3c208Sbeveloper	BBufferProducer(B_MEDIA_RAW_VIDEO),
42d6f3c208Sbeveloper	BControllable()
43d6f3c208Sbeveloper{
44d6f3c208Sbeveloper	fInitStatus = B_NO_INIT;
45d6f3c208Sbeveloper
46d6f3c208Sbeveloper	fInternalID = internal_id;
47d6f3c208Sbeveloper	fAddOn = addon;
48d6f3c208Sbeveloper
49d6f3c208Sbeveloper	fBufferGroup = NULL;
50d6f3c208Sbeveloper
51d6f3c208Sbeveloper	fThread = -1;
52d6f3c208Sbeveloper	fFrameSync = -1;
53d6f3c208Sbeveloper	fProcessingLatency = 0LL;
54d6f3c208Sbeveloper
55d6f3c208Sbeveloper	fRunning = false;
56d6f3c208Sbeveloper	fConnected = false;
57d6f3c208Sbeveloper	fEnabled = false;
58d6f3c208Sbeveloper
59d6f3c208Sbeveloper	fOutput.destination = media_destination::null;
60d6f3c208Sbeveloper
61d6f3c208Sbeveloper	fInitStatus = B_OK;
62d6f3c208Sbeveloper	return;
63d6f3c208Sbeveloper}
64d6f3c208Sbeveloper
65d6f3c208SbeveloperVideoProducer::~VideoProducer()
66d6f3c208Sbeveloper{
67d6f3c208Sbeveloper	if (fInitStatus == B_OK) {
68d6f3c208Sbeveloper		/* Clean up after ourselves, in case the application didn't make us
69d6f3c208Sbeveloper		 * do so. */
70d6f3c208Sbeveloper		if (fConnected)
71d6f3c208Sbeveloper			Disconnect(fOutput.source, fOutput.destination);
72d6f3c208Sbeveloper		if (fRunning)
73d6f3c208Sbeveloper			HandleStop();
74d6f3c208Sbeveloper	}
75d6f3c208Sbeveloper}
76d6f3c208Sbeveloper
77d6f3c208Sbeveloper/* BMediaNode */
78d6f3c208Sbeveloper
79d6f3c208Sbeveloperport_id
80d6f3c208SbeveloperVideoProducer::ControlPort() const
81d6f3c208Sbeveloper{
82d6f3c208Sbeveloper	return BMediaNode::ControlPort();
83d6f3c208Sbeveloper}
84d6f3c208Sbeveloper
85d6f3c208SbeveloperBMediaAddOn *
86d6f3c208SbeveloperVideoProducer::AddOn(int32 *internal_id) const
87d6f3c208Sbeveloper{
88d6f3c208Sbeveloper	if (internal_id)
89d6f3c208Sbeveloper		*internal_id = fInternalID;
90d6f3c208Sbeveloper	return fAddOn;
91d6f3c208Sbeveloper}
92d6f3c208Sbeveloper
93d6f3c208Sbeveloperstatus_t
94d6f3c208SbeveloperVideoProducer::HandleMessage(int32 message, const void *data, size_t size)
95d6f3c208Sbeveloper{
96d6f3c208Sbeveloper	return B_ERROR;
97d6f3c208Sbeveloper}
98d6f3c208Sbeveloper
99d6f3c208Sbevelopervoid
100d6f3c208SbeveloperVideoProducer::Preroll()
101d6f3c208Sbeveloper{
102d6f3c208Sbeveloper	/* This hook may be called before the node is started to give the hardware
103d6f3c208Sbeveloper	 * a chance to start. */
104d6f3c208Sbeveloper}
105d6f3c208Sbeveloper
106d6f3c208Sbevelopervoid
107d6f3c208SbeveloperVideoProducer::SetTimeSource(BTimeSource *time_source)
108d6f3c208Sbeveloper{
109d6f3c208Sbeveloper	/* Tell frame generation thread to recalculate delay value */
110d6f3c208Sbeveloper	release_sem(fFrameSync);
111d6f3c208Sbeveloper}
112d6f3c208Sbeveloper
113d6f3c208Sbeveloperstatus_t
114d6f3c208SbeveloperVideoProducer::RequestCompleted(const media_request_info &info)
115d6f3c208Sbeveloper{
116d6f3c208Sbeveloper	return BMediaNode::RequestCompleted(info);
117d6f3c208Sbeveloper}
118d6f3c208Sbeveloper
119d6f3c208Sbeveloper/* BMediaEventLooper */
120d6f3c208Sbeveloper
121d6f3c208Sbevelopervoid
122d6f3c208SbeveloperVideoProducer::NodeRegistered()
123d6f3c208Sbeveloper{
124d6f3c208Sbeveloper	if (fInitStatus != B_OK) {
125d6f3c208Sbeveloper		ReportError(B_NODE_IN_DISTRESS);
126d6f3c208Sbeveloper		return;
127d6f3c208Sbeveloper	}
128d6f3c208Sbeveloper
129d6f3c208Sbeveloper	/* Set up the parameter web */
130d6f3c208Sbeveloper	BParameterWeb *web = new BParameterWeb();
131d6f3c208Sbeveloper	BParameterGroup *main = web->MakeGroup(Name());
132d6f3c208Sbeveloper	BDiscreteParameter *state = main->MakeDiscreteParameter(
133d6f3c208Sbeveloper			P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color");
134dc706a1bSDavid McPaul	state->AddItem(B_HOST_TO_LENDIAN_INT32(0xff000000), "Block");
135d6f3c208Sbeveloper	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red");
136d6f3c208Sbeveloper	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green");
137d6f3c208Sbeveloper	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue");
138d6f3c208Sbeveloper
139d6f3c208Sbeveloper	fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000);
140d6f3c208Sbeveloper	fLastColorChange = system_time();
141d6f3c208Sbeveloper
142d6f3c208Sbeveloper	/* After this call, the BControllable owns the BParameterWeb object and
143d6f3c208Sbeveloper	 * will delete it for you */
144d6f3c208Sbeveloper	SetParameterWeb(web);
145d6f3c208Sbeveloper
146d6f3c208Sbeveloper	fOutput.node = Node();
147d6f3c208Sbeveloper	fOutput.source.port = ControlPort();
148d6f3c208Sbeveloper	fOutput.source.id = 0;
149d6f3c208Sbeveloper	fOutput.destination = media_destination::null;
150d6f3c208Sbeveloper	strcpy(fOutput.name, Name());
151d6f3c208Sbeveloper
152d6f3c208Sbeveloper	/* Tailor these for the output of your device */
153d6f3c208Sbeveloper	fOutput.format.type = B_MEDIA_RAW_VIDEO;
154d6f3c208Sbeveloper	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
155d6f3c208Sbeveloper	fOutput.format.u.raw_video.interlace = 1;
156d6f3c208Sbeveloper	fOutput.format.u.raw_video.display.format = B_RGB32;
157d6f3c208Sbeveloper
158d6f3c208Sbeveloper	/* Start the BMediaEventLooper control loop running */
159d6f3c208Sbeveloper	Run();
160d6f3c208Sbeveloper}
161d6f3c208Sbeveloper
162d6f3c208Sbevelopervoid
163d6f3c208SbeveloperVideoProducer::Start(bigtime_t performance_time)
164d6f3c208Sbeveloper{
165d6f3c208Sbeveloper	BMediaEventLooper::Start(performance_time);
166d6f3c208Sbeveloper}
167d6f3c208Sbeveloper
168d6f3c208Sbevelopervoid
169d6f3c208SbeveloperVideoProducer::Stop(bigtime_t performance_time, bool immediate)
170d6f3c208Sbeveloper{
171d6f3c208Sbeveloper	BMediaEventLooper::Stop(performance_time, immediate);
172d6f3c208Sbeveloper}
173d6f3c208Sbeveloper
174d6f3c208Sbevelopervoid
175d6f3c208SbeveloperVideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
176d6f3c208Sbeveloper{
177d6f3c208Sbeveloper	BMediaEventLooper::Seek(media_time, performance_time);
178d6f3c208Sbeveloper}
179d6f3c208Sbeveloper
180d6f3c208Sbevelopervoid
181d6f3c208SbeveloperVideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
182d6f3c208Sbeveloper{
183d6f3c208Sbeveloper	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
184d6f3c208Sbeveloper}
185d6f3c208Sbeveloper
186d6f3c208Sbeveloperstatus_t
187d6f3c208SbeveloperVideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
188d6f3c208Sbeveloper{
189d6f3c208Sbeveloper	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
190d6f3c208Sbeveloper}
191d6f3c208Sbeveloper
192d6f3c208Sbevelopervoid
193d6f3c208SbeveloperVideoProducer::SetRunMode(run_mode mode)
194d6f3c208Sbeveloper{
195d6f3c208Sbeveloper	BMediaEventLooper::SetRunMode(mode);
196d6f3c208Sbeveloper}
197d6f3c208Sbeveloper
198d6f3c208Sbevelopervoid
199d6f3c208SbeveloperVideoProducer::HandleEvent(const media_timed_event *event,
200d6f3c208Sbeveloper		bigtime_t lateness, bool realTimeEvent)
201d6f3c208Sbeveloper{
202d6f3c208Sbeveloper	TOUCH(lateness); TOUCH(realTimeEvent);
203d6f3c208Sbeveloper
204d6f3c208Sbeveloper	switch(event->type)
205d6f3c208Sbeveloper	{
206d6f3c208Sbeveloper		case BTimedEventQueue::B_START:
207d6f3c208Sbeveloper			HandleStart(event->event_time);
208d6f3c208Sbeveloper			break;
209d6f3c208Sbeveloper		case BTimedEventQueue::B_STOP:
210d6f3c208Sbeveloper			HandleStop();
211d6f3c208Sbeveloper			break;
212d6f3c208Sbeveloper		case BTimedEventQueue::B_WARP:
213d6f3c208Sbeveloper			HandleTimeWarp(event->bigdata);
214d6f3c208Sbeveloper			break;
215d6f3c208Sbeveloper		case BTimedEventQueue::B_SEEK:
216d6f3c208Sbeveloper			HandleSeek(event->bigdata);
217d6f3c208Sbeveloper			break;
218d6f3c208Sbeveloper		case BTimedEventQueue::B_HANDLE_BUFFER:
219d6f3c208Sbeveloper		case BTimedEventQueue::B_DATA_STATUS:
220d6f3c208Sbeveloper		case BTimedEventQueue::B_PARAMETER:
221d6f3c208Sbeveloper		default:
222d6f3c208Sbeveloper			PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type));
223d6f3c208Sbeveloper			break;
224d6f3c208Sbeveloper	}
225d6f3c208Sbeveloper}
226d6f3c208Sbeveloper
227d6f3c208Sbevelopervoid
228d6f3c208SbeveloperVideoProducer::CleanUpEvent(const media_timed_event *event)
229d6f3c208Sbeveloper{
230d6f3c208Sbeveloper	BMediaEventLooper::CleanUpEvent(event);
231d6f3c208Sbeveloper}
232d6f3c208Sbeveloper
233d6f3c208Sbeveloperbigtime_t
234d6f3c208SbeveloperVideoProducer::OfflineTime()
235d6f3c208Sbeveloper{
236d6f3c208Sbeveloper	return BMediaEventLooper::OfflineTime();
237d6f3c208Sbeveloper}
238d6f3c208Sbeveloper
239d6f3c208Sbevelopervoid
240d6f3c208SbeveloperVideoProducer::ControlLoop()
241d6f3c208Sbeveloper{
242d6f3c208Sbeveloper	BMediaEventLooper::ControlLoop();
243d6f3c208Sbeveloper}
244d6f3c208Sbeveloper
245d6f3c208Sbeveloperstatus_t
246d6f3c208SbeveloperVideoProducer::DeleteHook(BMediaNode * node)
247d6f3c208Sbeveloper{
248d6f3c208Sbeveloper	return BMediaEventLooper::DeleteHook(node);
249d6f3c208Sbeveloper}
250d6f3c208Sbeveloper
251d6f3c208Sbeveloper/* BBufferProducer */
252d6f3c208Sbeveloper
253d6f3c208Sbeveloperstatus_t
254d6f3c208SbeveloperVideoProducer::FormatSuggestionRequested(
255d6f3c208Sbeveloper		media_type type, int32 quality, media_format *format)
256d6f3c208Sbeveloper{
257d6f3c208Sbeveloper	if (type != B_MEDIA_ENCODED_VIDEO)
258d6f3c208Sbeveloper		return B_MEDIA_BAD_FORMAT;
259d6f3c208Sbeveloper
260d6f3c208Sbeveloper	TOUCH(quality);
261d6f3c208Sbeveloper
262dc706a1bSDavid McPaul	if (fOutput.format.u.raw_video.display.line_width == 0)
263dc706a1bSDavid McPaul		fOutput.format.u.raw_video.display.line_width = 320;
264dc706a1bSDavid McPaul	if (fOutput.format.u.raw_video.display.line_count == 0)
265dc706a1bSDavid McPaul		fOutput.format.u.raw_video.display.line_count = 240;
266dc706a1bSDavid McPaul	if (fOutput.format.u.raw_video.field_rate == 0)
267dc706a1bSDavid McPaul		fOutput.format.u.raw_video.field_rate = 29.97f;
268dc706a1bSDavid McPaul
269d6f3c208Sbeveloper	*format = fOutput.format;
270d6f3c208Sbeveloper	return B_OK;
271d6f3c208Sbeveloper}
272d6f3c208Sbeveloper
273d6f3c208Sbeveloperstatus_t
274d6f3c208SbeveloperVideoProducer::FormatProposal(const media_source &output, media_format *format)
275d6f3c208Sbeveloper{
276d6f3c208Sbeveloper	status_t err;
277d6f3c208Sbeveloper
278d6f3c208Sbeveloper	if (!format)
279d6f3c208Sbeveloper		return B_BAD_VALUE;
280d6f3c208Sbeveloper
281d6f3c208Sbeveloper	if (output != fOutput.source)
282d6f3c208Sbeveloper		return B_MEDIA_BAD_SOURCE;
283d6f3c208Sbeveloper
284d6f3c208Sbeveloper	err = format_is_compatible(*format, fOutput.format) ?
285d6f3c208Sbeveloper			B_OK : B_MEDIA_BAD_FORMAT;
286d6f3c208Sbeveloper	*format = fOutput.format;
287dc706a1bSDavid McPaul
288dc706a1bSDavid McPaul	return err;
289d6f3c208Sbeveloper}
290d6f3c208Sbeveloper
291d6f3c208Sbeveloperstatus_t
292d6f3c208SbeveloperVideoProducer::FormatChangeRequested(const media_source &source,
293d6f3c208Sbeveloper		const media_destination &destination, media_format *io_format,
294d6f3c208Sbeveloper		int32 *_deprecated_)
295d6f3c208Sbeveloper{
296d6f3c208Sbeveloper	TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
297d6f3c208Sbeveloper	if (source != fOutput.source)
298d6f3c208Sbeveloper		return B_MEDIA_BAD_SOURCE;
299d6f3c208Sbeveloper
300d6f3c208Sbeveloper	return B_ERROR;
301d6f3c208Sbeveloper}
302d6f3c208Sbeveloper
303d6f3c208Sbeveloperstatus_t
304d6f3c208SbeveloperVideoProducer::GetNextOutput(int32 *cookie, media_output *out_output)
305d6f3c208Sbeveloper{
306d6f3c208Sbeveloper	if (!out_output)
307d6f3c208Sbeveloper		return B_BAD_VALUE;
308d6f3c208Sbeveloper
309d6f3c208Sbeveloper	if ((*cookie) != 0)
310d6f3c208Sbeveloper		return B_BAD_INDEX;
311d6f3c208Sbeveloper
312d6f3c208Sbeveloper	*out_output = fOutput;
313d6f3c208Sbeveloper	(*cookie)++;
314d6f3c208Sbeveloper	return B_OK;
315d6f3c208Sbeveloper}
316d6f3c208Sbeveloper
317d6f3c208Sbeveloperstatus_t
318d6f3c208SbeveloperVideoProducer::DisposeOutputCookie(int32 cookie)
319d6f3c208Sbeveloper{
320d6f3c208Sbeveloper	TOUCH(cookie);
321d6f3c208Sbeveloper
322d6f3c208Sbeveloper	return B_OK;
323d6f3c208Sbeveloper}
324d6f3c208Sbeveloper
325d6f3c208Sbeveloperstatus_t
326d6f3c208SbeveloperVideoProducer::SetBufferGroup(const media_source &for_source,
327d6f3c208Sbeveloper		BBufferGroup *group)
328d6f3c208Sbeveloper{
329d6f3c208Sbeveloper	TOUCH(for_source); TOUCH(group);
330d6f3c208Sbeveloper
331d6f3c208Sbeveloper	return B_ERROR;
332d6f3c208Sbeveloper}
333d6f3c208Sbeveloper
334d6f3c208Sbeveloperstatus_t
335d6f3c208SbeveloperVideoProducer::VideoClippingChanged(const media_source &for_source,
336d6f3c208Sbeveloper		int16 num_shorts, int16 *clip_data,
337d6f3c208Sbeveloper		const media_video_display_info &display, int32 *_deprecated_)
338d6f3c208Sbeveloper{
339d6f3c208Sbeveloper	TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
340d6f3c208Sbeveloper	TOUCH(display); TOUCH(_deprecated_);
341d6f3c208Sbeveloper
342d6f3c208Sbeveloper	return B_ERROR;
343d6f3c208Sbeveloper}
344d6f3c208Sbeveloper
345d6f3c208Sbeveloperstatus_t
346d6f3c208SbeveloperVideoProducer::GetLatency(bigtime_t *out_latency)
347d6f3c208Sbeveloper{
348d6f3c208Sbeveloper	*out_latency = EventLatency() + SchedulingLatency();
349d6f3c208Sbeveloper	return B_OK;
350d6f3c208Sbeveloper}
351d6f3c208Sbeveloper
352d6f3c208Sbeveloperstatus_t
353d6f3c208SbeveloperVideoProducer::PrepareToConnect(const media_source &source,
354d6f3c208Sbeveloper		const media_destination &destination, media_format *format,
355d6f3c208Sbeveloper		media_source *out_source, char *out_name)
356d6f3c208Sbeveloper{
357d6f3c208Sbeveloper	PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \
358d6f3c208Sbeveloper			format->u.raw_video.display.line_width, \
359d6f3c208Sbeveloper			format->u.raw_video.display.line_count));
360d6f3c208Sbeveloper
361d6f3c208Sbeveloper	if (fConnected) {
362d6f3c208Sbeveloper		PRINTF(0, ("PrepareToConnect: Already connected\n"));
363d6f3c208Sbeveloper		return EALREADY;
364d6f3c208Sbeveloper	}
365d6f3c208Sbeveloper
366d6f3c208Sbeveloper	if (source != fOutput.source)
367d6f3c208Sbeveloper		return B_MEDIA_BAD_SOURCE;
368d6f3c208Sbeveloper
369d6f3c208Sbeveloper	if (fOutput.destination != media_destination::null)
370d6f3c208Sbeveloper		return B_MEDIA_ALREADY_CONNECTED;
371d6f3c208Sbeveloper
372d6f3c208Sbeveloper	/* The format parameter comes in with the suggested format, and may be
373d6f3c208Sbeveloper	 * specialized as desired by the node */
374d6f3c208Sbeveloper	if (!format_is_compatible(*format, fOutput.format)) {
375d6f3c208Sbeveloper		*format = fOutput.format;
376d6f3c208Sbeveloper		return B_MEDIA_BAD_FORMAT;
377d6f3c208Sbeveloper	}
378d6f3c208Sbeveloper
379d6f3c208Sbeveloper	if (format->u.raw_video.display.line_width == 0)
380d6f3c208Sbeveloper		format->u.raw_video.display.line_width = 320;
381d6f3c208Sbeveloper	if (format->u.raw_video.display.line_count == 0)
382d6f3c208Sbeveloper		format->u.raw_video.display.line_count = 240;
383d6f3c208Sbeveloper	if (format->u.raw_video.field_rate == 0)
384d6f3c208Sbeveloper		format->u.raw_video.field_rate = 29.97f;
385d6f3c208Sbeveloper
386d6f3c208Sbeveloper	*out_source = fOutput.source;
387d6f3c208Sbeveloper	strcpy(out_name, fOutput.name);
388d6f3c208Sbeveloper
389d6f3c208Sbeveloper	fOutput.destination = destination;
390d6f3c208Sbeveloper
391d6f3c208Sbeveloper	return B_OK;
392d6f3c208Sbeveloper}
393d6f3c208Sbeveloper
394d6f3c208Sbevelopervoid
395d6f3c208SbeveloperVideoProducer::Connect(status_t error, const media_source &source,
396d6f3c208Sbeveloper		const media_destination &destination, const media_format &format,
397d6f3c208Sbeveloper		char *io_name)
398d6f3c208Sbeveloper{
399d6f3c208Sbeveloper	PRINTF(1, ("Connect() %ldx%ld\n", \
400d6f3c208Sbeveloper			format.u.raw_video.display.line_width, \
401d6f3c208Sbeveloper			format.u.raw_video.display.line_count));
402d6f3c208Sbeveloper
403d6f3c208Sbeveloper	if (fConnected) {
404d6f3c208Sbeveloper		PRINTF(0, ("Connect: Already connected\n"));
405d6f3c208Sbeveloper		return;
406d6f3c208Sbeveloper	}
407d6f3c208Sbeveloper
408d6f3c208Sbeveloper	if (	(source != fOutput.source) || (error < B_OK) ||
409d6f3c208Sbeveloper			!const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
410d6f3c208Sbeveloper		PRINTF(1, ("Connect: Connect error\n"));
411d6f3c208Sbeveloper		return;
412d6f3c208Sbeveloper	}
413d6f3c208Sbeveloper
414d6f3c208Sbeveloper	fOutput.destination = destination;
415d6f3c208Sbeveloper	strcpy(io_name, fOutput.name);
416d6f3c208Sbeveloper
417d6f3c208Sbeveloper	if (fOutput.format.u.raw_video.field_rate != 0.0f) {
418d6f3c208Sbeveloper		fPerformanceTimeBase = fPerformanceTimeBase +
419d6f3c208Sbeveloper				(bigtime_t)
420d6f3c208Sbeveloper					((fFrame - fFrameBase) *
421d6f3c208Sbeveloper					(1000000 / fOutput.format.u.raw_video.field_rate));
422d6f3c208Sbeveloper		fFrameBase = fFrame;
423d6f3c208Sbeveloper	}
424d6f3c208Sbeveloper
425d6f3c208Sbeveloper	fConnectedFormat = format.u.raw_video;
426d6f3c208Sbeveloper
427d6f3c208Sbeveloper	/* get the latency */
428d6f3c208Sbeveloper	bigtime_t latency = 0;
429d6f3c208Sbeveloper	media_node_id tsID = 0;
430d6f3c208Sbeveloper	FindLatencyFor(fOutput.destination, &latency, &tsID);
431d6f3c208Sbeveloper	#define NODE_LATENCY 1000
432d6f3c208Sbeveloper	SetEventLatency(latency + NODE_LATENCY);
433d6f3c208Sbeveloper
434d6f3c208Sbeveloper	uint32 *buffer, *p, f = 3;
435d6f3c208Sbeveloper	p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count *
436d6f3c208Sbeveloper			fConnectedFormat.display.line_width);
437d6f3c208Sbeveloper	if (!buffer) {
438d6f3c208Sbeveloper		PRINTF(0, ("Connect: Out of memory\n"));
439d6f3c208Sbeveloper		return;
440d6f3c208Sbeveloper	}
441d6f3c208Sbeveloper	bigtime_t now = system_time();
44229f200aaSbeveloper	for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++)
44329f200aaSbeveloper		for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++)
444d6f3c208Sbeveloper			*(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor);
445d6f3c208Sbeveloper	fProcessingLatency = system_time() - now;
446d6f3c208Sbeveloper	free(buffer);
447d6f3c208Sbeveloper
448d6f3c208Sbeveloper	/* Create the buffer group */
449d6f3c208Sbeveloper	fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
450d6f3c208Sbeveloper			fConnectedFormat.display.line_count, 8);
451d6f3c208Sbeveloper	if (fBufferGroup->InitCheck() < B_OK) {
452d6f3c208Sbeveloper		delete fBufferGroup;
453d6f3c208Sbeveloper		fBufferGroup = NULL;
454d6f3c208Sbeveloper		return;
455d6f3c208Sbeveloper	}
456d6f3c208Sbeveloper
457d6f3c208Sbeveloper	fConnected = true;
458d6f3c208Sbeveloper	fEnabled = true;
459d6f3c208Sbeveloper
460d6f3c208Sbeveloper	/* Tell frame generation thread to recalculate delay value */
461d6f3c208Sbeveloper	release_sem(fFrameSync);
462d6f3c208Sbeveloper}
463d6f3c208Sbeveloper
464d6f3c208Sbevelopervoid
465d6f3c208SbeveloperVideoProducer::Disconnect(const media_source &source,
466d6f3c208Sbeveloper		const media_destination &destination)
467d6f3c208Sbeveloper{
468d6f3c208Sbeveloper	PRINTF(1, ("Disconnect()\n"));
469d6f3c208Sbeveloper
470d6f3c208Sbeveloper	if (!fConnected) {
471d6f3c208Sbeveloper		PRINTF(0, ("Disconnect: Not connected\n"));
472d6f3c208Sbeveloper		return;
473d6f3c208Sbeveloper	}
474d6f3c208Sbeveloper
475d6f3c208Sbeveloper	if ((source != fOutput.source) || (destination != fOutput.destination)) {
476d6f3c208Sbeveloper		PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
477d6f3c208Sbeveloper		return;
478d6f3c208Sbeveloper	}
479d6f3c208Sbeveloper
480d6f3c208Sbeveloper	fEnabled = false;
481d6f3c208Sbeveloper	fOutput.destination = media_destination::null;
482d6f3c208Sbeveloper
483d6f3c208Sbeveloper	fLock.Lock();
484d6f3c208Sbeveloper		delete fBufferGroup;
485d6f3c208Sbeveloper		fBufferGroup = NULL;
486d6f3c208Sbeveloper	fLock.Unlock();
487d6f3c208Sbeveloper
488d6f3c208Sbeveloper	fConnected = false;
489d6f3c208Sbeveloper}
490d6f3c208Sbeveloper
491d6f3c208Sbevelopervoid
492d6f3c208SbeveloperVideoProducer::LateNoticeReceived(const media_source &source,
493d6f3c208Sbeveloper		bigtime_t how_much, bigtime_t performance_time)
494d6f3c208Sbeveloper{
495d6f3c208Sbeveloper	TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
496d6f3c208Sbeveloper}
497d6f3c208Sbeveloper
498d6f3c208Sbevelopervoid
499d6f3c208SbeveloperVideoProducer::EnableOutput(const media_source &source, bool enabled,
500d6f3c208Sbeveloper		int32 *_deprecated_)
501d6f3c208Sbeveloper{
502d6f3c208Sbeveloper	TOUCH(_deprecated_);
503d6f3c208Sbeveloper
504d6f3c208Sbeveloper	if (source != fOutput.source)
505d6f3c208Sbeveloper		return;
506d6f3c208Sbeveloper
507d6f3c208Sbeveloper	fEnabled = enabled;
508d6f3c208Sbeveloper}
509d6f3c208Sbeveloper
510d6f3c208Sbeveloperstatus_t
511d6f3c208SbeveloperVideoProducer::SetPlayRate(int32 numer, int32 denom)
512d6f3c208Sbeveloper{
513d6f3c208Sbeveloper	TOUCH(numer); TOUCH(denom);
514d6f3c208Sbeveloper
515d6f3c208Sbeveloper	return B_ERROR;
516d6f3c208Sbeveloper}
517d6f3c208Sbeveloper
518d6f3c208Sbevelopervoid
519d6f3c208SbeveloperVideoProducer::AdditionalBufferRequested(const media_source &source,
520d6f3c208Sbeveloper		media_buffer_id prev_buffer, bigtime_t prev_time,
521d6f3c208Sbeveloper		const media_seek_tag *prev_tag)
522d6f3c208Sbeveloper{
523d6f3c208Sbeveloper	TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
524d6f3c208Sbeveloper}
525d6f3c208Sbeveloper
526d6f3c208Sbevelopervoid
527d6f3c208SbeveloperVideoProducer::LatencyChanged(const media_source &source,
528d6f3c208Sbeveloper		const media_destination &destination, bigtime_t new_latency,
529d6f3c208Sbeveloper		uint32 flags)
530d6f3c208Sbeveloper{
531d6f3c208Sbeveloper	TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
532d6f3c208Sbeveloper}
533d6f3c208Sbeveloper
534d6f3c208Sbeveloper/* BControllable */
535d6f3c208Sbeveloper
536d6f3c208Sbeveloperstatus_t
537d6f3c208SbeveloperVideoProducer::GetParameterValue(
538d6f3c208Sbeveloper	int32 id, bigtime_t *last_change, void *value, size_t *size)
539d6f3c208Sbeveloper{
540d6f3c208Sbeveloper	if (id != P_COLOR)
541d6f3c208Sbeveloper		return B_BAD_VALUE;
542d6f3c208Sbeveloper
543d6f3c208Sbeveloper	*last_change = fLastColorChange;
544d6f3c208Sbeveloper	*size = sizeof(uint32);
545d6f3c208Sbeveloper	*((uint32 *)value) = fColor;
546d6f3c208Sbeveloper
547d6f3c208Sbeveloper	return B_OK;
548d6f3c208Sbeveloper}
549d6f3c208Sbeveloper
550d6f3c208Sbevelopervoid
551d6f3c208SbeveloperVideoProducer::SetParameterValue(
552d6f3c208Sbeveloper	int32 id, bigtime_t when, const void *value, size_t size)
553d6f3c208Sbeveloper{
554d6f3c208Sbeveloper	if ((id != P_COLOR) || !value || (size != sizeof(uint32)))
555d6f3c208Sbeveloper		return;
556d6f3c208Sbeveloper
55729f200aaSbeveloper	if (*(uint32 *)value == fColor)
558d6f3c208Sbeveloper		return;
559d6f3c208Sbeveloper
560d6f3c208Sbeveloper	fColor = *(uint32 *)value;
561d6f3c208Sbeveloper	fLastColorChange = when;
562d6f3c208Sbeveloper
563d6f3c208Sbeveloper	BroadcastNewParameterValue(
564d6f3c208Sbeveloper			fLastColorChange, P_COLOR, &fColor, sizeof(fColor));
565d6f3c208Sbeveloper}
566d6f3c208Sbeveloper
567d6f3c208Sbeveloperstatus_t
568d6f3c208SbeveloperVideoProducer::StartControlPanel(BMessenger *out_messenger)
569d6f3c208Sbeveloper{
570d6f3c208Sbeveloper	return BControllable::StartControlPanel(out_messenger);
571d6f3c208Sbeveloper}
572d6f3c208Sbeveloper
573d6f3c208Sbeveloper/* VideoProducer */
574d6f3c208Sbeveloper
575d6f3c208Sbevelopervoid
576d6f3c208SbeveloperVideoProducer::HandleStart(bigtime_t performance_time)
577d6f3c208Sbeveloper{
578d6f3c208Sbeveloper	/* Start producing frames, even if the output hasn't been connected yet. */
579d6f3c208Sbeveloper
580d6f3c208Sbeveloper	PRINTF(1, ("HandleStart(%Ld)\n", performance_time));
581d6f3c208Sbeveloper
582d6f3c208Sbeveloper	if (fRunning) {
583d6f3c208Sbeveloper		PRINTF(-1, ("HandleStart: Node already started\n"));
584d6f3c208Sbeveloper		return;
585d6f3c208Sbeveloper	}
586d6f3c208Sbeveloper
587d6f3c208Sbeveloper	fFrame = 0;
588d6f3c208Sbeveloper	fFrameBase = 0;
589d6f3c208Sbeveloper	fPerformanceTimeBase = performance_time;
590d6f3c208Sbeveloper
591d6f3c208Sbeveloper	fFrameSync = create_sem(0, "frame synchronization");
592d6f3c208Sbeveloper	if (fFrameSync < B_OK)
593d6f3c208Sbeveloper		goto err1;
594d6f3c208Sbeveloper
595d6f3c208Sbeveloper	fThread = spawn_thread(_frame_generator_, "frame generator",
596d6f3c208Sbeveloper			B_NORMAL_PRIORITY, this);
597d6f3c208Sbeveloper	if (fThread < B_OK)
598d6f3c208Sbeveloper		goto err2;
599d6f3c208Sbeveloper
600d6f3c208Sbeveloper	resume_thread(fThread);
601d6f3c208Sbeveloper
602d6f3c208Sbeveloper	fRunning = true;
603d6f3c208Sbeveloper	return;
604d6f3c208Sbeveloper
605d6f3c208Sbevelopererr2:
606d6f3c208Sbeveloper	delete_sem(fFrameSync);
607d6f3c208Sbevelopererr1:
608d6f3c208Sbeveloper	return;
609d6f3c208Sbeveloper}
610d6f3c208Sbeveloper
611d6f3c208Sbevelopervoid
612d6f3c208SbeveloperVideoProducer::HandleStop(void)
613d6f3c208Sbeveloper{
614d6f3c208Sbeveloper	PRINTF(1, ("HandleStop()\n"));
615d6f3c208Sbeveloper
616d6f3c208Sbeveloper	if (!fRunning) {
617d6f3c208Sbeveloper		PRINTF(-1, ("HandleStop: Node isn't running\n"));
618d6f3c208Sbeveloper		return;
619d6f3c208Sbeveloper	}
620d6f3c208Sbeveloper
621d6f3c208Sbeveloper	delete_sem(fFrameSync);
622d6f3c208Sbeveloper	wait_for_thread(fThread, &fThread);
623d6f3c208Sbeveloper
624d6f3c208Sbeveloper	fRunning = false;
625d6f3c208Sbeveloper}
626d6f3c208Sbeveloper
627d6f3c208Sbevelopervoid
628d6f3c208SbeveloperVideoProducer::HandleTimeWarp(bigtime_t performance_time)
629d6f3c208Sbeveloper{
630d6f3c208Sbeveloper	fPerformanceTimeBase = performance_time;
631d6f3c208Sbeveloper	fFrameBase = fFrame;
632d6f3c208Sbeveloper
633d6f3c208Sbeveloper	/* Tell frame generation thread to recalculate delay value */
634d6f3c208Sbeveloper	release_sem(fFrameSync);
635d6f3c208Sbeveloper}
636d6f3c208Sbeveloper
637d6f3c208Sbevelopervoid
638d6f3c208SbeveloperVideoProducer::HandleSeek(bigtime_t performance_time)
639d6f3c208Sbeveloper{
640d6f3c208Sbeveloper	fPerformanceTimeBase = performance_time;
641d6f3c208Sbeveloper	fFrameBase = fFrame;
642d6f3c208Sbeveloper
643d6f3c208Sbeveloper	/* Tell frame generation thread to recalculate delay value */
644d6f3c208Sbeveloper	release_sem(fFrameSync);
645d6f3c208Sbeveloper}
646d6f3c208Sbeveloper
647d6f3c208Sbeveloper/* The following functions form the thread that generates frames. You should
648d6f3c208Sbeveloper * replace this with the code that interfaces to your hardware. */
649d6f3c208Sbeveloperint32
650d6f3c208SbeveloperVideoProducer::FrameGenerator()
651d6f3c208Sbeveloper{
652d6f3c208Sbeveloper	bigtime_t wait_until = system_time();
653d6f3c208Sbeveloper
654d6f3c208Sbeveloper	while (1) {
655d6f3c208Sbeveloper		status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
656d6f3c208Sbeveloper				wait_until);
657d6f3c208Sbeveloper
658d6f3c208Sbeveloper		/* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
659d6f3c208Sbeveloper		 * else means the thread should quit. Deleting the semaphore, as in
660d6f3c208Sbeveloper		 * VideoProducer::HandleStop(), will trigger this behavior. */
661d6f3c208Sbeveloper		if ((err != B_OK) && (err != B_TIMED_OUT))
662d6f3c208Sbeveloper			break;
663d6f3c208Sbeveloper
664d6f3c208Sbeveloper		fFrame++;
665d6f3c208Sbeveloper
666d6f3c208Sbeveloper		/* Recalculate the time until the thread should wake up to begin
667d6f3c208Sbeveloper		 * processing the next frame. Subtract fProcessingLatency so that
668d6f3c208Sbeveloper		 * the frame is sent in time. */
669b3d94504SStephan Aßmus		wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase +
670d6f3c208Sbeveloper				(bigtime_t)
671d6f3c208Sbeveloper						((fFrame - fFrameBase) *
672b3d94504SStephan Aßmus						(1000000 / fConnectedFormat.field_rate)), 0) -
673d6f3c208Sbeveloper				fProcessingLatency;
674d6f3c208Sbeveloper
675d6f3c208Sbeveloper		/* Drop frame if it's at least a frame late */
676d6f3c208Sbeveloper		if (wait_until < system_time())
677d6f3c208Sbeveloper			continue;
678d6f3c208Sbeveloper
679d6f3c208Sbeveloper		/* If the semaphore was acquired successfully, it means something
680d6f3c208Sbeveloper		 * changed the timing information (see VideoProducer::Connect()) and
681d6f3c208Sbeveloper		 * so the thread should go back to sleep until the newly-calculated
682d6f3c208Sbeveloper		 * wait_until time. */
683d6f3c208Sbeveloper		if (err == B_OK)
684d6f3c208Sbeveloper			continue;
685d6f3c208Sbeveloper
686d6f3c208Sbeveloper		/* Send buffers only if the node is running and the output has been
687d6f3c208Sbeveloper		 * enabled */
688d6f3c208Sbeveloper		if (!fRunning || !fEnabled)
689d6f3c208Sbeveloper			continue;
690d6f3c208Sbeveloper
691d6f3c208Sbeveloper		BAutolock _(fLock);
692d6f3c208Sbeveloper
693d6f3c208Sbeveloper		/* Fetch a buffer from the buffer group */
694d6f3c208Sbeveloper		BBuffer *buffer = fBufferGroup->RequestBuffer(
695d6f3c208Sbeveloper						4 * fConnectedFormat.display.line_width *
696d6f3c208Sbeveloper						fConnectedFormat.display.line_count, 0LL);
697d6f3c208Sbeveloper		if (!buffer)
698d6f3c208Sbeveloper			continue;
699d6f3c208Sbeveloper
700d6f3c208Sbeveloper		/* Fill out the details about this buffer. */
701d6f3c208Sbeveloper		media_header *h = buffer->Header();
702d6f3c208Sbeveloper		h->type = B_MEDIA_RAW_VIDEO;
703d6f3c208Sbeveloper		h->time_source = TimeSource()->ID();
704d6f3c208Sbeveloper		h->size_used = 4 * fConnectedFormat.display.line_width *
705d6f3c208Sbeveloper						fConnectedFormat.display.line_count;
706d6f3c208Sbeveloper		/* For a buffer originating from a device, you might want to calculate
707d6f3c208Sbeveloper		 * this based on the PerformanceTimeFor the time your buffer arrived at
708d6f3c208Sbeveloper		 * the hardware (plus any applicable adjustments). */
709d6f3c208Sbeveloper		h->start_time = fPerformanceTimeBase +
710d6f3c208Sbeveloper						(bigtime_t)
711d6f3c208Sbeveloper							((fFrame - fFrameBase) *
712d6f3c208Sbeveloper							(1000000 / fConnectedFormat.field_rate));
713d6f3c208Sbeveloper		h->file_pos = 0;
714d6f3c208Sbeveloper		h->orig_size = 0;
715d6f3c208Sbeveloper		h->data_offset = 0;
716d6f3c208Sbeveloper		h->u.raw_video.field_gamma = 1.0;
717d6f3c208Sbeveloper		h->u.raw_video.field_sequence = fFrame;
718d6f3c208Sbeveloper		h->u.raw_video.field_number = 0;
719d6f3c208Sbeveloper		h->u.raw_video.pulldown_number = 0;
720d6f3c208Sbeveloper		h->u.raw_video.first_active_line = 1;
721d6f3c208Sbeveloper		h->u.raw_video.line_count = fConnectedFormat.display.line_count;
722d6f3c208Sbeveloper
723dc706a1bSDavid McPaul		if (fColor == 0xff000000) {
7248391490aSDavid McPaul			// display a gray block that moves
725dc706a1bSDavid McPaul			uint32 *p = (uint32 *)buffer->Data();
726dc706a1bSDavid McPaul			for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++)
727dc706a1bSDavid McPaul				for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++) {
7288391490aSDavid McPaul					if (x > (fFrame & 0xff) && x < (fFrame & 0xff) + 60 && y > 90 && y < 150) {
7298391490aSDavid McPaul						*(p++) = 0xff777777;
730dc706a1bSDavid McPaul					} else {
7318391490aSDavid McPaul						*(p++) = 0x00000000;
732dc706a1bSDavid McPaul					}
733dc706a1bSDavid McPaul				}
734dc706a1bSDavid McPaul		} else {
735dc706a1bSDavid McPaul
736dc706a1bSDavid McPaul			/* Fill in a pattern */
737dc706a1bSDavid McPaul			uint32 *p = (uint32 *)buffer->Data();
738dc706a1bSDavid McPaul			for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++)
739dc706a1bSDavid McPaul				for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++)
740dc706a1bSDavid McPaul					*(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor);
741dc706a1bSDavid McPaul		}
742dc706a1bSDavid McPaul
743d6f3c208Sbeveloper		/* Send the buffer on down to the consumer */
744b289aaf6SAxel Dörfler		if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) {
745d6f3c208Sbeveloper			PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
746d6f3c208Sbeveloper			/* If there is a problem sending the buffer, return it to its
747d6f3c208Sbeveloper			 * buffer group. */
748d6f3c208Sbeveloper			buffer->Recycle();
749d6f3c208Sbeveloper		}
750d6f3c208Sbeveloper	}
751d6f3c208Sbeveloper
752d6f3c208Sbeveloper	return B_OK;
753d6f3c208Sbeveloper}
754d6f3c208Sbeveloper
755d6f3c208Sbeveloperint32
756d6f3c208SbeveloperVideoProducer::_frame_generator_(void *data)
757d6f3c208Sbeveloper{
758d6f3c208Sbeveloper	return ((VideoProducer *)data)->FrameGenerator();
759d6f3c208Sbeveloper}
760