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 takes in multiple video streams and supplies
8* a single stream as the output.
9* each stream is converted to the same colourspace and should match
10* either the primary input OR the requested colourspace from the output
11* destination.
12*
13* The first input is considered the primary input
14* subsequent input framesize should match the primary input framesize
15* The output framerate will be the same as the primary input
16*
17*/
18
19#include <stdio.h>
20#include <string.h>
21
22#include "VideoMixerNode.h"
23
24using std::vector;
25
26VideoMixerNode::~VideoMixerNode(void)
27{
28	fprintf(stderr,"VideoMixerNode::~VideoMixerNode\n");
29	// Stop the BMediaEventLooper thread
30	Quit();
31}
32
33VideoMixerNode::VideoMixerNode(
34				const flavor_info *info,
35				BMessage *config,
36				BMediaAddOn *addOn)
37	: BMediaNode("VideoMixerNode"),
38  	  BBufferConsumer(B_MEDIA_RAW_VIDEO),	// Raw video buffers in
39  	  BBufferProducer(B_MEDIA_RAW_VIDEO),	// Raw video buffers out
40	  BMediaEventLooper()
41{
42	fprintf(stderr,"VideoMixerNode::VideoMixerNode\n");
43	// keep our creator around for AddOn calls later
44	fAddOn = addOn;
45	// NULL out our latency estimates
46	fDownstreamLatency = 0;
47	fInternalLatency = 0;
48
49	// Start with 1 input and 1 output
50	ClearInput(&fInitialInput);
51
52	strncpy(fOutput.name,"VideoMixer Output", B_MEDIA_NAME_LENGTH-1);
53	fOutput.name[B_MEDIA_NAME_LENGTH-1] = '\0';
54
55	// initialize the output
56	fOutput.node = media_node::null;               // until registration
57	fOutput.destination = media_destination::null;
58	fOutput.source.port = ControlPort();
59	fOutput.source.id = 0;
60
61	GetOutputFormat(&fOutput.format);
62
63	fInitCheckStatus = B_OK;
64}
65
66void VideoMixerNode::NodeRegistered(void)
67{
68	fprintf(stderr,"VideoMixerNode::NodeRegistered\n");
69
70	// for every node created so far set to this Node;
71	for (uint32 i=0;i<fConnectedInputs.size();i++) {
72		fConnectedInputs[i]->node = Node();
73		fConnectedInputs[i]->destination.id = i;
74		fConnectedInputs[i]->destination.port = ControlPort();
75	}
76
77	fInitialInput.node = Node();
78	fInitialInput.destination.id = fConnectedInputs.size();
79	fInitialInput.destination.port = ControlPort();
80
81	GetOutputFormat(&fOutput.format);
82	fOutput.node = Node();
83
84	// start the BMediaEventLooper thread
85	SetPriority(B_REAL_TIME_PRIORITY);
86	Run();
87}
88
89media_input *
90VideoMixerNode::CreateInput(uint32 inputID) {
91	media_input *input = new media_input();
92
93	ClearInput(input);
94
95	// don't overwrite available space, and be sure to terminate
96	sprintf(input->name, "VideoMixer Input %ld", inputID);
97
98	return input;
99}
100
101void
102VideoMixerNode::ClearInput(media_input *input) {
103
104	// initialize the input
105	sprintf(input->name, "VideoMixer Input");
106	input->node = Node();
107	input->source = media_source::null;
108	input->destination = media_destination::null;
109
110	GetInputFormat(&input->format);
111}
112
113media_input *
114VideoMixerNode::GetInput(const media_source &source) {
115
116	vector<media_input *>::iterator each;
117
118	for (each=fConnectedInputs.begin(); each<fConnectedInputs.end(); each++) {
119		if ((*each)->source == source) {
120			return *each;
121		}
122	}
123
124	return NULL;
125}
126
127media_input *
128VideoMixerNode::GetInput(const media_destination &destination) {
129
130	vector<media_input *>::iterator each;
131
132	for (each=fConnectedInputs.begin(); each<fConnectedInputs.end(); each++) {
133		if ((*each)->destination == destination) {
134			return *each;
135		}
136	}
137
138	return NULL;
139}
140
141media_input *
142VideoMixerNode::GetInput(const int32 id) {
143
144	vector<media_input *>::iterator each;
145
146	for (each=fConnectedInputs.begin(); each<fConnectedInputs.end(); each++) {
147		if ((*each)->destination.id == id) {
148			return *each;
149		}
150	}
151
152	return NULL;
153}
154
155status_t VideoMixerNode::InitCheck(void) const
156{
157	fprintf(stderr,"VideoMixerNode::InitCheck\n");
158	return fInitCheckStatus;
159}
160
161status_t VideoMixerNode::GetConfigurationFor(
162				BMessage *into_message)
163{
164	fprintf(stderr,"VideoMixerNode::GetConfigurationFor\n");
165	return B_OK;
166}
167
168// -------------------------------------------------------- //
169// implementation of BMediaNode
170// -------------------------------------------------------- //
171
172BMediaAddOn *VideoMixerNode::AddOn(
173				int32 *internal_id) const
174{
175	fprintf(stderr,"VideoMixerNode::AddOn\n");
176	// BeBook says this only gets called if we were in an add-on.
177	if (fAddOn != NULL) {
178		// If we get a null pointer then we just won't write.
179		if (internal_id != NULL) {
180			internal_id = 0;
181		}
182	}
183	return fAddOn;
184}
185
186void VideoMixerNode::Start(bigtime_t performance_time)
187{
188	fprintf(stderr,"VideoMixerNode::Start(pt=%lld)\n", performance_time);
189	BMediaEventLooper::Start(performance_time);
190}
191
192void VideoMixerNode::Stop(
193				bigtime_t performance_time,
194				bool immediate)
195{
196	if (immediate) {
197		fprintf(stderr,"VideoMixerNode::Stop(pt=%lld,<immediate>)\n", performance_time);
198	} else {
199		fprintf(stderr,"VideoMixerNode::Stop(pt=%lld,<scheduled>)\n", performance_time);
200	}
201	BMediaEventLooper::Stop(performance_time, immediate);
202}
203
204void VideoMixerNode::Seek(
205				bigtime_t media_time,
206				bigtime_t performance_time)
207{
208	fprintf(stderr,"VideoMixerNode::Seek(mt=%lld,pt=%lld)\n", media_time,performance_time);
209	BMediaEventLooper::Seek(media_time, performance_time);
210}
211
212void VideoMixerNode::SetRunMode(run_mode mode)
213{
214	fprintf(stderr,"VideoMixerNode::SetRunMode(%i)\n", mode);
215	BMediaEventLooper::SetRunMode(mode);
216}
217
218void VideoMixerNode::TimeWarp(
219				bigtime_t at_real_time,
220				bigtime_t to_performance_time)
221{
222	fprintf(stderr,"VideoMixerNode::TimeWarp(rt=%lld,pt=%lld)\n", at_real_time, to_performance_time);
223	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
224}
225
226void VideoMixerNode::Preroll(void)
227{
228	fprintf(stderr,"VideoMixerNode::Preroll\n");
229	// XXX:Performance opportunity
230	BMediaNode::Preroll();
231}
232
233void VideoMixerNode::SetTimeSource(BTimeSource *time_source)
234{
235	fprintf(stderr,"VideoMixerNode::SetTimeSource\n");
236	BMediaNode::SetTimeSource(time_source);
237}
238
239status_t VideoMixerNode::HandleMessage(
240				int32 message,
241				const void *data,
242				size_t size)
243{
244	fprintf(stderr,"VideoMixerNode::HandleMessage\n");
245	status_t status = B_OK;
246	switch (message) {
247		// no special messages for now
248		default:
249			status = BBufferConsumer::HandleMessage(message, data, size);
250			if (status == B_OK) {
251				break;
252			}
253			status = BBufferProducer::HandleMessage(message, data, size);
254			if (status == B_OK) {
255				break;
256			}
257			status = BMediaNode::HandleMessage(message, data, size);
258			if (status == B_OK) {
259				break;
260			}
261			BMediaNode::HandleBadMessage(message, data, size);
262			status = B_ERROR;
263			break;
264	}
265	return status;
266}
267
268status_t VideoMixerNode::RequestCompleted(const media_request_info &info)
269{
270	fprintf(stderr,"VideoMixerNode::RequestCompleted\n");
271	return BMediaNode::RequestCompleted(info);
272}
273
274status_t VideoMixerNode::DeleteHook(BMediaNode *node)
275{
276	fprintf(stderr,"VideoMixerNode::DeleteHook\n");
277	return BMediaEventLooper::DeleteHook(node);
278}
279
280status_t VideoMixerNode::GetNodeAttributes(
281				media_node_attribute *outAttributes,
282				size_t inMaxCount)
283{
284	fprintf(stderr,"VideoMixerNode::GetNodeAttributes\n");
285	return BMediaNode::GetNodeAttributes(outAttributes, inMaxCount);
286}
287
288status_t VideoMixerNode::AddTimer(
289					bigtime_t at_performance_time,
290					int32 cookie)
291{
292	fprintf(stderr,"VideoMixerNode::AddTimer\n");
293	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
294}
295
296// -------------------------------------------------------- //
297// VideoMixerNode specific functions
298// -------------------------------------------------------- //
299
300// public:
301
302void VideoMixerNode::GetFlavor(flavor_info *outInfo, int32 id)
303{
304	fprintf(stderr,"VideoMixerNode::GetFlavor\n");
305
306	if (outInfo != NULL) {
307		outInfo->internal_id = id;
308		strcpy(outInfo->name, "Haiku VideoMixer");
309		strcpy(outInfo->info, "A VideoMixerNode node mixes multiple video"
310			" streams into a single stream.");
311		outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER;
312		outInfo->flavor_flags = B_FLAVOR_IS_LOCAL;
313		outInfo->possible_count = INT_MAX;	// no limit
314		outInfo->in_format_count = 1;
315		media_format *inFormats = new media_format[outInfo->in_format_count];
316		GetInputFormat(&inFormats[0]);
317		outInfo->in_formats = inFormats;
318		outInfo->out_format_count = 1; // single output
319		media_format *outFormats = new media_format[outInfo->out_format_count];
320		GetOutputFormat(&outFormats[0]);
321		outInfo->out_formats = outFormats;
322	}
323}
324
325void VideoMixerNode::GetInputFormat(media_format *outFormat)
326{
327	fprintf(stderr,"VideoMixerNode::GetInputFormat\n");
328
329	if (outFormat != NULL) {
330		outFormat->type = B_MEDIA_RAW_VIDEO;
331		outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
332		outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
333		outFormat->u.raw_video = media_raw_video_format::wildcard;
334	}
335}
336
337void VideoMixerNode::GetOutputFormat(media_format *outFormat)
338{
339	fprintf(stderr,"VideoMixerNode::GetOutputFormat\n");
340	if (outFormat != NULL) {
341		outFormat->type = B_MEDIA_RAW_VIDEO;
342		outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
343		outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
344		outFormat->u.raw_video = media_raw_video_format::wildcard;
345	}
346}
347
348// protected:
349
350status_t VideoMixerNode::AddRequirements(media_format *format)
351{
352	fprintf(stderr,"VideoMixerNode::AddRequirements\n");
353	return B_OK;
354}
355