1/*
2* Copyright (C) 2009-2010 David McPaul
3*
4* All rights reserved. Distributed under the terms of the MIT License.
5* VideoMixerNode.cpp
6*
7* The VideoMixerNode class
8* takes in multiple video streams and supplies
9* a single stream as the output.
10* each stream is converted to the same colourspace
11*/
12
13#include "VideoMixerNode.h"
14
15#include <stdio.h>
16#include <string.h>
17
18
19// -------------------------------------------------------- //
20// implemention of BBufferProducer
21// -------------------------------------------------------- //
22
23// They are asking us to make the first offering.
24// So, we get a fresh format and then add requirements
25status_t VideoMixerNode::FormatSuggestionRequested(
26				media_type type,
27				int32 quality,
28				media_format * format)
29{
30	fprintf(stderr,"VideoMixerNode(BBufferProducer)::FormatSuggestionRequested\n");
31
32	if (format->type == B_MEDIA_NO_TYPE) {
33		format->type = B_MEDIA_RAW_VIDEO;
34	}
35
36	if (format->type != B_MEDIA_RAW_VIDEO) {
37		return B_MEDIA_BAD_FORMAT;
38	}
39
40	GetOutputFormat(format);
41
42	return B_OK;
43}
44
45// They made an offer to us.  We should make sure that the offer is
46// acceptable, and then we can add any requirements we have on top of
47// that.  We leave wildcards for anything that we don't care about.
48status_t VideoMixerNode::FormatProposal(
49				const media_source &output_source,
50				media_format *format)
51{
52	fprintf(stderr,"VideoMixerNode(BBufferProducer)::FormatProposal\n");
53
54	fOutput.source = output_source;
55
56	// If we have an input then set our output as the same except for color_space
57	if (fConnectedInputs.size() > 0) {
58		if (fOutput.format.u.raw_video == media_raw_video_format::wildcard) {
59			// First proposal
60			fOutput.format = fConnectedInputs[0]->format;
61			fOutput.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
62		} else {
63			// Second proposal
64			fOutput.format = fConnectedInputs[0]->format;
65			fOutput.format.u.raw_video.display.format = B_RGBA32;
66		}
67	}
68
69	*format = fOutput.format;
70
71	return B_OK;
72}
73
74// Presumably we have already agreed with them that this format is
75// okay.  But just in case, we check the offer. (and complain if it
76// is invalid)  Then as the last thing we do, we get rid of any
77// remaining wilcards.
78status_t VideoMixerNode::FormatChangeRequested(
79				const media_source &source,
80				const media_destination &destination,
81				media_format *io_format,
82				int32 * _deprecated_)
83{
84	fprintf(stderr,"VideoMixerNode(BBufferProducer)::FormatChangeRequested\n");
85
86	if (fOutput.source != source) {
87		// we don't have that output
88		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
89		return B_MEDIA_BAD_SOURCE;
90	}
91
92	fOutput.destination = destination;
93	fOutput.format = *io_format;
94
95	return B_OK;
96}
97
98status_t VideoMixerNode::GetNextOutput(	/* cookie starts as 0 */
99				int32 *cookie,
100				media_output *out_output)
101{
102	fprintf(stderr,"VideoMixerNode(BBufferProducer)::GetNextOutput (%ld)\n",*cookie);
103
104	// only 1 output
105	if (*cookie != 0) {
106		fprintf(stderr,"<- B_ERROR (no more outputs)\n");
107		return B_ERROR;
108	}
109
110	*out_output = fOutput;
111	*cookie = 1;
112
113	return B_OK;
114}
115
116status_t VideoMixerNode::DisposeOutputCookie(int32 cookie)
117{
118	fprintf(stderr,"VideoMixerNode(BBufferProducer)::DisposeOutputCookie\n");
119	// nothing to do since our cookies are part of the vector iterator
120	return B_OK;
121}
122
123status_t VideoMixerNode::SetBufferGroup(
124				const media_source & for_source,
125				BBufferGroup * group)
126{
127	fprintf(stderr,"VideoMixerNode(BBufferProducer)::SetBufferGroup\n");
128
129	if (fOutput.source != for_source) {
130		// we don't have that output
131		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
132		return B_MEDIA_BAD_SOURCE;
133	}
134
135	return B_OK;
136}
137
138	/* Format of clipping is (as int16-s): <from line> <npairs> <startclip> <endclip>. */
139	/* Repeat for each line where the clipping is different from the previous line. */
140	/* If <npairs> is negative, use the data from line -<npairs> (there are 0 pairs after */
141	/* a negative <npairs>. Yes, we only support 32k*32k frame buffers for clipping. */
142	/* Any non-0 field of 'display' means that that field changed, and if you don't support */
143	/* that change, you should return an error and ignore the request. Note that the buffer */
144	/* offset values do not have wildcards; 0 (or -1, or whatever) are real values and must */
145	/* be adhered to. */
146status_t VideoMixerNode::VideoClippingChanged(
147				const media_source & for_source,
148				int16 num_shorts,
149				int16 * clip_data,
150				const media_video_display_info & display,
151				int32 * _deprecated_)
152{
153	return BBufferProducer::VideoClippingChanged(for_source, num_shorts, clip_data, display, _deprecated_);
154}
155
156status_t VideoMixerNode::GetLatency(
157				bigtime_t *out_latency)
158{
159	fprintf(stderr,"VideoMixerNode(BBufferProducer)::GetLatency\n");
160	if (out_latency == NULL) {
161		fprintf(stderr,"<- B_BAD_VALUE\n");
162		return B_BAD_VALUE;
163	}
164
165	*out_latency = EventLatency() + SchedulingLatency();
166	return B_OK;
167}
168
169status_t VideoMixerNode::PrepareToConnect(
170				const media_source &what,
171				const media_destination &where,
172				media_format *format,
173				media_source *out_source,
174				char *out_name)
175{
176	fprintf(stderr,"VideoMixerNode(BBufferProducer)::PrepareToConnect\n");
177
178	if (fOutput.source != what) {
179		// we don't have that output
180		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
181		return B_MEDIA_BAD_SOURCE;
182	}
183
184	*out_source = fOutput.source;
185	strcpy(out_name, fOutput.name);
186
187	fOutput.destination = where;
188
189	return B_OK;
190}
191
192void VideoMixerNode::Connect(
193				status_t error,
194				const media_source &source,
195				const media_destination &destination,
196				const media_format &format,
197				char *io_name)
198{
199	fprintf(stderr,"VideoMixerNode(BBufferProducer)::Connect\n");
200
201	if (fOutput.source != source) {
202		// we don't have that output
203		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
204		return;
205	}
206
207	if (error != B_OK) {
208		fprintf(stderr,"<- error already\n");
209		fOutput.destination = media_destination::null;
210		fOutput.format.u.raw_video = media_raw_video_format::wildcard;
211		return;
212	}
213
214	// calculate the downstream latency
215	// must happen before itr->Connect
216	bigtime_t downstreamLatency;
217	media_node_id id;
218	FindLatencyFor(fOutput.destination, &downstreamLatency, &id);
219
220	// record the agreed upon values
221	fOutput.format = format;
222	fOutput.destination = destination;
223	strcpy(io_name, fOutput.name);
224
225	// compute the internal latency
226	// must happen after itr->Connect
227	if (fInternalLatency == 0) {
228		fInternalLatency = 100; // temporary until we finish computing it
229		ComputeInternalLatency();
230	}
231
232	// If the downstream latency for this output is larger
233	// than our current downstream latency, we have to increase
234	// our current downstream latency to be the larger value.
235	if (downstreamLatency > fDownstreamLatency) {
236		SetEventLatency(fDownstreamLatency + fInternalLatency);
237	}
238}
239
240void VideoMixerNode::ComputeInternalLatency() {
241	fprintf(stderr,"VideoMixerNode(BBufferProducer)::ComputeInternalLatency\n");
242	fInternalLatency = 100; // just guess
243	fprintf(stderr,"  internal latency guessed = %lld\n",fInternalLatency);
244}
245
246void VideoMixerNode::Disconnect(
247				const media_source & what,
248				const media_destination & where)
249{
250	fprintf(stderr,"VideoMixerNode(BBufferProducer)::Disconnect\n");
251
252	if (fOutput.source != what) {
253		// we don't have that output
254		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
255		return;
256	}
257
258	if (fOutput.destination != where) {
259		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
260		return;
261	}
262
263	fOutput.destination = media_destination::null;
264	GetOutputFormat(&fOutput.format);
265}
266
267void VideoMixerNode::LateNoticeReceived(
268				const media_source & what,
269				bigtime_t how_much,
270				bigtime_t performance_time)
271{
272	fprintf(stderr,"VideoMixerNode(BBufferProducer)::LateNoticeReceived\n");
273
274	if (fOutput.source != what) {
275		// we don't have that output
276		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
277		return;
278	}
279
280	switch (RunMode()) {
281		case B_OFFLINE:
282		    // nothing to do
283			break;
284		case B_RECORDING:
285		    // nothing to do
286			break;
287		case B_INCREASE_LATENCY:
288			fInternalLatency += how_much;
289			SetEventLatency(fDownstreamLatency + fInternalLatency);
290			break;
291		case B_DECREASE_PRECISION:
292			// XXX: try to catch up by producing buffers faster
293			break;
294		case B_DROP_DATA:
295			// XXX: should we really drop buffers?  just for that output?
296			break;
297		default:
298			fprintf(stderr,"VideoMixerNode::LateNoticeReceived with unexpected run mode.\n");
299			break;
300	}
301}
302
303void VideoMixerNode::EnableOutput(
304				const media_source &what,
305				bool enabled,
306				int32 *_deprecated_)
307{
308	fprintf(stderr,"VideoMixerNode(BBufferProducer)::EnableOutput\n");
309
310	if (fOutput.source != what) {
311		// we don't have that output
312		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
313		return;
314	}
315
316	status_t status = B_OK;
317	if (status != B_OK) {
318		fprintf(stderr,"  error in itr->EnableOutput\n");
319	}
320}
321
322status_t VideoMixerNode::SetPlayRate(
323				int32 numer,
324				int32 denom)
325{
326	BBufferProducer::SetPlayRate(numer, denom); // XXX: do something intelligent later
327	return B_OK;
328}
329
330void VideoMixerNode::AdditionalBufferRequested(			//	used to be Reserved 0
331				const media_source & source,
332				media_buffer_id prev_buffer,
333				bigtime_t prev_time,
334				const media_seek_tag * prev_tag)
335{
336	fprintf(stderr,"VideoMixerNode(BBufferProducer)::AdditionalBufferRequested\n");
337
338	if (fOutput.source != source) {
339		// we don't have that output
340		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
341		return;
342	}
343
344//	BBuffer * buffer;
345//	status_t status = itr->AdditionalBufferRequested(prev_buffer, prev_time, prev_tag);
346//	if (status != B_OK) {
347//		fprintf(stderr,"  itr->AdditionalBufferRequested returned an error.\n");
348//	}
349}
350
351void VideoMixerNode::LatencyChanged(
352				const media_source & source,
353				const media_destination & destination,
354				bigtime_t new_latency,
355				uint32 flags)
356{
357	fprintf(stderr,"VideoMixerNode(BBufferProducer)::LatencyChanged\n");
358
359	if (fOutput.source != source) {
360		// we don't have that output
361		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
362		return;
363	}
364
365	if (fOutput.destination != destination) {
366		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
367		return;
368	}
369
370	fDownstreamLatency = new_latency;
371	SetEventLatency(fDownstreamLatency + fInternalLatency);
372
373	// XXX: we may have to recompute the number of buffers that we are using
374	// see SetBufferGroup
375}
376