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