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 26 VideoMixerNode::~VideoMixerNode(void) 27 { 28 fprintf(stderr,"VideoMixerNode::~VideoMixerNode\n"); 29 // Stop the BMediaEventLooper thread 30 Quit(); 31 } 32 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 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 * 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 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 * 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 * 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 * 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 155 status_t VideoMixerNode::InitCheck(void) const 156 { 157 fprintf(stderr,"VideoMixerNode::InitCheck\n"); 158 return fInitCheckStatus; 159 } 160 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 172 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 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 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 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 212 void VideoMixerNode::SetRunMode(run_mode mode) 213 { 214 fprintf(stderr,"VideoMixerNode::SetRunMode(%i)\n", mode); 215 BMediaEventLooper::SetRunMode(mode); 216 } 217 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 226 void VideoMixerNode::Preroll(void) 227 { 228 fprintf(stderr,"VideoMixerNode::Preroll\n"); 229 // XXX:Performance opportunity 230 BMediaNode::Preroll(); 231 } 232 233 void VideoMixerNode::SetTimeSource(BTimeSource *time_source) 234 { 235 fprintf(stderr,"VideoMixerNode::SetTimeSource\n"); 236 BMediaNode::SetTimeSource(time_source); 237 } 238 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 268 status_t VideoMixerNode::RequestCompleted(const media_request_info &info) 269 { 270 fprintf(stderr,"VideoMixerNode::RequestCompleted\n"); 271 return BMediaNode::RequestCompleted(info); 272 } 273 274 status_t VideoMixerNode::DeleteHook(BMediaNode *node) 275 { 276 fprintf(stderr,"VideoMixerNode::DeleteHook\n"); 277 return BMediaEventLooper::DeleteHook(node); 278 } 279 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 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 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 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 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 350 status_t VideoMixerNode::AddRequirements(media_format *format) 351 { 352 fprintf(stderr,"VideoMixerNode::AddRequirements\n"); 353 return B_OK; 354 } 355