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