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
17
18// -------------------------------------------------------- //
19// implemention of BBufferConsumer
20// -------------------------------------------------------- //
21
22// Check to make sure the format is okay, then remove
23// any wildcards corresponding to our requirements.
24status_t VideoMixerNode::AcceptFormat(
25				const media_destination &dest,
26				media_format *format)
27{
28	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::AcceptFormat\n");
29
30	if (fInitialInput.destination != dest) {
31		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION");
32		return B_MEDIA_BAD_DESTINATION; // none of our inputs matched the dest
33	}
34
35	media_format myFormat;
36
37	GetInputFormat(&myFormat);
38
39	AddRequirements(format);
40
41	return B_OK;
42}
43
44status_t VideoMixerNode::GetNextInput(
45				int32 *cookie,
46				media_input *out_input)
47{
48	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::GetNextInput (%ld)\n",*cookie);
49
50	// Cookie 0 is the connecting input, all others are connected inputs
51	if (uint32(*cookie) == fConnectedInputs.size()) {
52		*out_input = fInitialInput;
53	} else {
54		out_input = GetInput(*cookie);
55
56		if (out_input == NULL) {
57			fprintf(stderr,"<- B_ERROR (no more inputs)\n");
58			return B_ERROR;
59		}
60	}
61
62	// so next time they won't get the same input again
63	(*cookie)++;
64
65	return B_OK;
66}
67
68void VideoMixerNode::DisposeInputCookie(
69				int32 cookie)
70{
71	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::DisposeInputCookie\n");
72	// nothing to do since our cookies are just integers
73}
74
75void VideoMixerNode::BufferReceived(BBuffer *buffer)
76{
77	switch (buffer->Header()->type) {
78//		case B_MEDIA_PARAMETERS:
79//			{
80//			status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
81//			if (status != B_OK) {
82//				fprintf(stderr,"ApplyParameterData in MediaDemultiplexerNode::BufferReceived failed\n");
83//			}
84//			buffer->Recycle();
85//			}
86//			break;
87		case B_MEDIA_RAW_VIDEO:
88			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
89				fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in VideoMixerNode::BufferReceived\n");
90				// XXX: implement this part
91				buffer->Recycle();
92			} else {
93				media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
94										buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
95				status_t status = EventQueue()->AddEvent(event);
96				if (status != B_OK) {
97					fprintf(stderr,"EventQueue()->AddEvent(event) in VideoMixerNode::BufferReceived failed\n");
98					buffer->Recycle();
99				}
100			}
101			break;
102		default:
103			fprintf(stderr,"unexpected buffer type in VideoMixerNode::BufferReceived\n");
104			buffer->Recycle();
105			break;
106	}
107}
108
109void VideoMixerNode::ProducerDataStatus(
110				const media_destination &for_whom,
111				int32 status,
112				bigtime_t at_performance_time)
113{
114	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::ProducerDataStatus\n");
115	media_input *input = GetInput(for_whom);
116
117	if (input == NULL) {
118		fprintf(stderr,"invalid destination received in VideoMixerNode::ProducerDataStatus\n");
119		return;
120	}
121
122	media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
123			&input, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
124	EventQueue()->AddEvent(event);
125}
126
127status_t VideoMixerNode::GetLatencyFor(
128				const media_destination &for_whom,
129				bigtime_t *out_latency,
130				media_node_id *out_timesource)
131{
132	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::GetLatencyFor\n");
133	if ((out_latency == 0) || (out_timesource == 0)) {
134		fprintf(stderr,"<- B_BAD_VALUE\n");
135		return B_BAD_VALUE;
136	}
137
138	media_input *input = GetInput(for_whom);
139
140	if (input == NULL) {
141		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
142		return B_MEDIA_BAD_DESTINATION;
143	}
144
145	*out_latency = EventLatency();
146	*out_timesource = TimeSource()->ID();
147
148	return B_OK;
149}
150
151status_t VideoMixerNode::Connected(
152				const media_source &producer,	/* here's a good place to request buffer group usage */
153				const media_destination &where,
154				const media_format &with_format,
155				media_input *out_input)
156{
157	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::Connected\n");
158
159	if (fInitialInput.destination != where) {
160		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
161		return B_MEDIA_BAD_DESTINATION;
162	}
163
164	media_input *input = CreateInput(fConnectedInputs.size());
165	fConnectedInputs.push_back(input);
166
167	// Specialise the output?
168
169	// compute the latency or just guess
170	fInternalLatency = 500; // just a guess
171	fprintf(stderr,"  internal latency guessed = %lld\n", fInternalLatency);
172
173	SetEventLatency(fInternalLatency);
174
175	// record the agreed upon values
176	input->destination = where;
177	input->source = producer;
178	input->format = with_format;
179
180	*out_input = *input;
181
182	// Reset the Initial Input
183	ClearInput(&fInitialInput);
184	fInitialInput.destination.id = fConnectedInputs.size();
185	fInitialInput.destination.port = ControlPort();
186
187	return B_OK;
188}
189
190void VideoMixerNode::Disconnected(
191				const media_source &producer,
192				const media_destination &where)
193{
194	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::Disconnected\n");
195
196	media_input *input = GetInput(where);
197
198	if (input == NULL) {
199		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
200		return;
201	}
202
203	if (input->source != producer) {
204		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
205		return;
206	}
207
208	bufferMixer.RemoveBuffer(input->destination.id);
209
210	// disconnected but not deleted (important)
211	input->source = media_source::null;
212	GetInputFormat(&input->format);
213}
214
215/* The notification comes from the upstream producer, so he's already cool with */
216/* the format; you should not ask him about it in here. */
217status_t VideoMixerNode::FormatChanged(
218				const media_source & producer,
219				const media_destination & consumer,
220				int32 change_tag,
221				const media_format & format)
222{
223	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::FormatChanged\n");
224
225	media_input *input = GetInput(producer);
226
227	if (input == NULL) {
228		return B_MEDIA_BAD_SOURCE;
229	}
230
231	if (input->destination != consumer) {
232		return B_MEDIA_BAD_DESTINATION;
233	}
234
235	input->format = format;
236	return B_OK;
237}
238
239/* Given a performance time of some previous buffer, retrieve the remembered tag */
240/* of the closest (previous or exact) performance time. Set *out_flags to 0; the */
241/* idea being that flags can be added later, and the understood flags returned in */
242/* *out_flags. */
243status_t VideoMixerNode::SeekTagRequested(
244				const media_destination & destination,
245				bigtime_t in_target_time,
246				uint32 in_flags,
247				media_seek_tag * out_seek_tag,
248				bigtime_t * out_tagged_time,
249				uint32 * out_flags)
250{
251	fprintf(stderr,"VideoMixerNode(BBufferConsumer)::SeekTagRequested\n");
252	// XXX: implement this
253	return BBufferConsumer::SeekTagRequested(destination,in_target_time, in_flags,
254											out_seek_tag, out_tagged_time, out_flags);
255}
256