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
24 using std::vector;
25
~VideoMixerNode(void)26 VideoMixerNode::~VideoMixerNode(void)
27 {
28 fprintf(stderr,"VideoMixerNode::~VideoMixerNode\n");
29 // Stop the BMediaEventLooper thread
30 Quit();
31 }
32
VideoMixerNode(const flavor_info * info,BMessage * config,BMediaAddOn * addOn)33 VideoMixerNode::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
NodeRegistered(void)66 void 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
89 media_input *
CreateInput(uint32 inputID)90 VideoMixerNode::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
101 void
ClearInput(media_input * input)102 VideoMixerNode::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
113 media_input *
GetInput(const media_source & source)114 VideoMixerNode::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
127 media_input *
GetInput(const media_destination & destination)128 VideoMixerNode::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
141 media_input *
GetInput(const int32 id)142 VideoMixerNode::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
InitCheck(void) const155 status_t VideoMixerNode::InitCheck(void) const
156 {
157 fprintf(stderr,"VideoMixerNode::InitCheck\n");
158 return fInitCheckStatus;
159 }
160
GetConfigurationFor(BMessage * into_message)161 status_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
AddOn(int32 * internal_id) const172 BMediaAddOn *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
Start(bigtime_t performance_time)186 void VideoMixerNode::Start(bigtime_t performance_time)
187 {
188 fprintf(stderr,"VideoMixerNode::Start(pt=%lld)\n", performance_time);
189 BMediaEventLooper::Start(performance_time);
190 }
191
Stop(bigtime_t performance_time,bool immediate)192 void 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
Seek(bigtime_t media_time,bigtime_t performance_time)204 void 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
SetRunMode(run_mode mode)212 void VideoMixerNode::SetRunMode(run_mode mode)
213 {
214 fprintf(stderr,"VideoMixerNode::SetRunMode(%i)\n", mode);
215 BMediaEventLooper::SetRunMode(mode);
216 }
217
TimeWarp(bigtime_t at_real_time,bigtime_t to_performance_time)218 void 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
Preroll(void)226 void VideoMixerNode::Preroll(void)
227 {
228 fprintf(stderr,"VideoMixerNode::Preroll\n");
229 // XXX:Performance opportunity
230 BMediaNode::Preroll();
231 }
232
SetTimeSource(BTimeSource * time_source)233 void VideoMixerNode::SetTimeSource(BTimeSource *time_source)
234 {
235 fprintf(stderr,"VideoMixerNode::SetTimeSource\n");
236 BMediaNode::SetTimeSource(time_source);
237 }
238
HandleMessage(int32 message,const void * data,size_t size)239 status_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
RequestCompleted(const media_request_info & info)268 status_t VideoMixerNode::RequestCompleted(const media_request_info &info)
269 {
270 fprintf(stderr,"VideoMixerNode::RequestCompleted\n");
271 return BMediaNode::RequestCompleted(info);
272 }
273
DeleteHook(BMediaNode * node)274 status_t VideoMixerNode::DeleteHook(BMediaNode *node)
275 {
276 fprintf(stderr,"VideoMixerNode::DeleteHook\n");
277 return BMediaEventLooper::DeleteHook(node);
278 }
279
GetNodeAttributes(media_node_attribute * outAttributes,size_t inMaxCount)280 status_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
AddTimer(bigtime_t at_performance_time,int32 cookie)288 status_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
GetFlavor(flavor_info * outInfo,int32 id)302 void 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
GetInputFormat(media_format * outFormat)325 void 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
GetOutputFormat(media_format * outFormat)337 void 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
AddRequirements(media_format * format)350 status_t VideoMixerNode::AddRequirements(media_format *format)
351 {
352 fprintf(stderr,"VideoMixerNode::AddRequirements\n");
353 return B_OK;
354 }
355