1 // MediaWriter.cpp 2 // 3 // Andrew Bachmann, 2002 4 // 5 // A MediaWriter is a node that 6 // implements FileInterface and BBufferConsumer. 7 // It consumes on input, which is a multistream, 8 // and writes the stream to a file. 9 // 10 // see also MediaWriterAddOn.cpp 11 12 #include <MediaDefs.h> 13 #include <MediaNode.h> 14 #include <MediaAddOn.h> 15 #include <BufferConsumer.h> 16 #include <FileInterface.h> 17 #include <Controllable.h> 18 #include <MediaEventLooper.h> 19 #include <File.h> 20 #include <Errors.h> 21 #include <Entry.h> 22 #include <BufferGroup.h> 23 #include <TimeSource.h> 24 #include <Buffer.h> 25 #include <ParameterWeb.h> 26 #include <MediaRoster.h> 27 #include <limits.h> 28 29 #include "../AbstractFileInterfaceNode.h" 30 #include "MediaWriter.h" 31 #include "../misc.h" 32 33 #include <stdio.h> 34 #include <string.h> 35 36 // -------------------------------------------------------- // 37 // ctor/dtor 38 // -------------------------------------------------------- // 39 40 MediaWriter::~MediaWriter(void) 41 { 42 fprintf(stderr,"MediaWriter::~MediaWriter\n"); 43 if (fBufferGroup != 0) { 44 BBufferGroup * group = fBufferGroup; 45 fBufferGroup = 0; 46 delete group; 47 } 48 } 49 50 MediaWriter::MediaWriter( 51 size_t defaultChunkSize, 52 float defaultBitRate, 53 const flavor_info * info, 54 BMessage * config, 55 BMediaAddOn * addOn) 56 : BMediaNode("MediaWriter"), 57 BBufferConsumer(B_MEDIA_MULTISTREAM), 58 AbstractFileInterfaceNode(defaultChunkSize,defaultBitRate,info,config,addOn) 59 { 60 fprintf(stderr,"MediaWriter::MediaWriter\n"); 61 // null some fields 62 fBufferGroup = 0; 63 // don't overwrite available space, and be sure to terminate 64 strncpy(input.name,"MediaWriter Input",B_MEDIA_NAME_LENGTH-1); 65 input.name[B_MEDIA_NAME_LENGTH-1] = '\0'; 66 // initialize the input 67 input.node = media_node::null; // until registration 68 input.source = media_source::null; 69 input.destination = media_destination::null; // until registration 70 GetFormat(&input.format); 71 } 72 73 // -------------------------------------------------------- // 74 // implementation of BMediaNode 75 // -------------------------------------------------------- // 76 77 void MediaWriter::Preroll(void) 78 { 79 fprintf(stderr,"MediaWriter::Preroll\n"); 80 // XXX:Performance opportunity 81 BMediaNode::Preroll(); 82 } 83 84 status_t MediaWriter::HandleMessage( 85 int32 message, 86 const void * data, 87 size_t size) 88 { 89 fprintf(stderr,"MediaWriter::HandleMessage\n"); 90 status_t status = B_OK; 91 switch (message) { 92 // no special messages for now 93 default: 94 status = BBufferConsumer::HandleMessage(message,data,size); 95 if (status == B_OK) { 96 break; 97 } 98 status = AbstractFileInterfaceNode::HandleMessage(message,data,size); 99 break; 100 } 101 return status; 102 } 103 104 void MediaWriter::NodeRegistered(void) 105 { 106 fprintf(stderr,"MediaWriter::NodeRegistered\n"); 107 108 // now we can do this 109 input.node = Node(); 110 input.destination.id = 0; 111 input.destination.port = input.node.port; // same as ControlPort() 112 113 // creates the parameter web and starts the looper thread 114 AbstractFileInterfaceNode::NodeRegistered(); 115 } 116 117 // -------------------------------------------------------- // 118 // implementation of BFileInterface 119 // -------------------------------------------------------- // 120 121 status_t MediaWriter::SetRef( 122 const entry_ref & file, 123 bool create, 124 bigtime_t * out_time) 125 { 126 fprintf(stderr,"MediaWriter::SetRef\n"); 127 status_t status; 128 status = AbstractFileInterfaceNode::SetRef(file,B_WRITE_ONLY,create,out_time); 129 if (status != B_OK) { 130 fprintf(stderr,"AbstractFileInterfaceNode::SetRef returned an error\n"); 131 return status; 132 } 133 if (input.source == media_source::null) { 134 // reset the format, and set the requirements imposed by this file 135 GetFormat(&input.format); 136 AddRequirements(&input.format); 137 return B_OK; 138 } 139 // if we are connected we may have to re-negotiate the connection 140 media_format format; 141 GetFormat(&format); 142 AddRequirements(&format); 143 if (format_is_acceptible(input.format,format)) { 144 fprintf(stderr," compatible format = no re-negotiation necessary\n"); 145 return B_OK; 146 } 147 // first try the easy way : SORRY DEPRECATED into private :-( 148 // int32 change_tag = NewChangeTag(); 149 // status = this->BBufferConsumer::RequestFormatChange(input.source,input.destination,&format,&change_tag); 150 // if (status == B_OK) { 151 // fprintf(stderr," format change successful\n"); 152 // return B_OK; 153 // } 154 // okay, the hard way requires we get the MediaRoster 155 BMediaRoster * roster = BMediaRoster::Roster(&status); 156 if (roster == 0) { 157 return B_MEDIA_SYSTEM_FAILURE; 158 } 159 if (status != B_OK) { 160 return status; 161 } 162 // before disconnect one should always stop the nodes (bebook says) 163 // requires run_state cast since the return type on RunState() is 164 // wrong [int32] 165 run_state destinationRunState = run_state(RunState()); 166 if (destinationRunState == BMediaEventLooper::B_STARTED) { 167 Stop(0,true); // stop us right now 168 } 169 // should also stop the source if it is running, but how? 170 /* BMediaNode sourceNode = ?? 171 run_state sourceRunState = sourceNode->??; 172 status = sourceNode->StopNode(??,0,true); 173 if (status != B_OK) { 174 return status; 175 } */ 176 // we should disconnect right now 177 media_source inputSource = input.source; 178 status = roster->Disconnect(input.source.id,input.source, 179 input.destination.id,input.destination); 180 if (status != B_OK) { 181 return status; 182 } 183 // if that went okay, we'll try reconnecting 184 media_output connectOutput; 185 media_input connectInput; 186 status = roster->Connect(inputSource,input.destination, 187 &format,&connectOutput,&connectInput); 188 if (status != B_OK) { 189 return status; 190 } 191 // now restart if necessary 192 if (destinationRunState == BMediaEventLooper::B_STARTED) { 193 Start(0); 194 } 195 return status; 196 } 197 198 // -------------------------------------------------------- // 199 // implemention of BBufferConsumer 200 // -------------------------------------------------------- // 201 202 // Check to make sure the format is okay, then remove 203 // any wildcards corresponding to our requirements. 204 status_t MediaWriter::AcceptFormat( 205 const media_destination & dest, 206 media_format * format) 207 { 208 fprintf(stderr,"MediaWriter::AcceptFormat\n"); 209 if (format == 0) { 210 fprintf(stderr,"<- B_BAD_VALUE\n"); 211 return B_BAD_VALUE; // no crashing 212 } 213 if (input.destination != dest) { 214 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION"); 215 return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it 216 } 217 /* media_format * myFormat = GetFormat(); 218 fprintf(stderr,"proposed format: "); 219 print_media_format(format); 220 fprintf(stderr,"\n"); 221 fprintf(stderr,"my format: "); 222 print_media_format(myFormat); 223 fprintf(stderr,"\n");*/ 224 // Be's format_is_compatible doesn't work. 225 // if (!format_is_compatible(*format,*myFormat)) { 226 media_format myFormat; 227 GetFormat(&myFormat); 228 if (!format_is_acceptible(*format,myFormat)) { 229 fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); 230 return B_MEDIA_BAD_FORMAT; 231 } 232 AddRequirements(format); 233 return B_OK; 234 } 235 236 status_t MediaWriter::GetNextInput( 237 int32 * cookie, 238 media_input * out_input) 239 { 240 fprintf(stderr,"MediaWriter::GetNextInput\n"); 241 // let's not crash even if they are stupid 242 if (out_input == 0) { 243 // no place to write! 244 fprintf(stderr,"<- B_BAD_VALUE\n"); 245 return B_BAD_VALUE; 246 } 247 if (cookie != 0) { 248 // it's valid but they already got our 1 input 249 if (*cookie != 0) { 250 fprintf(stderr,"<- B_ERROR (no more inputs)\n"); 251 return B_ERROR; 252 } 253 // so next time they won't get the same input again 254 *cookie = 1; 255 } 256 *out_input = input; 257 return B_OK; 258 } 259 260 void MediaWriter::DisposeInputCookie( 261 int32 cookie) 262 { 263 fprintf(stderr,"MediaWriter::DisposeInputCookie\n"); 264 // nothing to do since our cookies are just integers 265 return; // B_OK; 266 } 267 268 void MediaWriter::BufferReceived( 269 BBuffer * buffer) 270 { 271 fprintf(stderr,"MediaWriter::BufferReceived\n"); 272 switch (buffer->Header()->type) { 273 case B_MEDIA_PARAMETERS: 274 { 275 status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed()); 276 if (status != B_OK) { 277 fprintf(stderr,"ApplyParameterData in MediaWriter::BufferReceived failed\n"); 278 } 279 buffer->Recycle(); 280 } 281 break; 282 case B_MEDIA_MULTISTREAM: 283 if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) { 284 fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in MediaWriter::BufferReceived\n"); 285 // XXX: implement this part 286 buffer->Recycle(); 287 } else { 288 media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, 289 buffer, BTimedEventQueue::B_RECYCLE_BUFFER); 290 status_t status = EventQueue()->AddEvent(event); 291 if (status != B_OK) { 292 fprintf(stderr,"EventQueue()->AddEvent(event) in MediaWriter::BufferReceived failed\n"); 293 buffer->Recycle(); 294 } 295 } 296 break; 297 default: 298 fprintf(stderr,"unexpected buffer type in MediaWriter::BufferReceived\n"); 299 buffer->Recycle(); 300 break; 301 } 302 } 303 304 void MediaWriter::ProducerDataStatus( 305 const media_destination & for_whom, 306 int32 status, 307 bigtime_t at_performance_time) 308 { 309 fprintf(stderr,"MediaWriter::ProducerDataStatus\n"); 310 if (input.destination != for_whom) { 311 fprintf(stderr,"invalid destination received in MediaWriter::ProducerDataStatus\n"); 312 return; 313 } 314 media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS, 315 &input, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); 316 EventQueue()->AddEvent(event); 317 } 318 319 status_t MediaWriter::GetLatencyFor( 320 const media_destination & for_whom, 321 bigtime_t * out_latency, 322 media_node_id * out_timesource) 323 { 324 fprintf(stderr,"MediaWriter::GetLatencyFor\n"); 325 if ((out_latency == 0) || (out_timesource == 0)) { 326 fprintf(stderr,"<- B_BAD_VALUE\n"); 327 return B_BAD_VALUE; 328 } 329 if (input.destination != for_whom) { 330 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); 331 return B_MEDIA_BAD_DESTINATION; 332 } 333 *out_latency = EventLatency(); 334 *out_timesource = TimeSource()->ID(); 335 return B_OK; 336 } 337 338 status_t MediaWriter::Connected( 339 const media_source & producer, /* here's a good place to request buffer group usage */ 340 const media_destination & where, 341 const media_format & with_format, 342 media_input * out_input) 343 { 344 fprintf(stderr,"MediaWriter::Connected\n"); 345 if (out_input == 0) { 346 fprintf(stderr,"<- B_BAD_VALUE\n"); 347 return B_BAD_VALUE; // no crashing 348 } 349 if (input.destination != where) { 350 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); 351 return B_MEDIA_BAD_DESTINATION; 352 } 353 354 // clear any stale buffer groups 355 if (fBufferGroup != 0) { 356 BBufferGroup * group = fBufferGroup; 357 fBufferGroup = 0; 358 delete group; 359 } 360 361 // compute the latency or just guess 362 if (GetCurrentFile() != 0) { 363 bigtime_t start, end; 364 uint8 * data = new uint8[input.format.u.multistream.max_chunk_size]; // <- buffer group buffer size 365 ssize_t bytesWritten = 0; 366 { // timed section 367 start = TimeSource()->RealTime(); 368 bytesWritten = GetCurrentFile()->Write(data,input.format.u.multistream.max_chunk_size); 369 end = TimeSource()->RealTime(); 370 } 371 delete[] data; 372 GetCurrentFile()->Seek(-bytesWritten,SEEK_CUR); // put it back where we found it 373 374 fInternalLatency = end - start; 375 376 fprintf(stderr," internal latency from disk write = %lld\n",fInternalLatency); 377 } else { 378 fInternalLatency = 500; // just guess 379 fprintf(stderr," internal latency guessed = %lld\n",fInternalLatency); 380 } 381 382 SetEventLatency(fInternalLatency); 383 384 // record the agreed upon values 385 input.source = producer; 386 input.format = with_format; 387 *out_input = input; 388 return B_OK; 389 } 390 391 void MediaWriter::Disconnected( 392 const media_source & producer, 393 const media_destination & where) 394 { 395 fprintf(stderr,"MediaWriter::Disconnected\n"); 396 if (input.destination != where) { 397 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); 398 return; 399 } 400 if (input.source != producer) { 401 fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n"); 402 return; 403 } 404 input.source = media_source::null; 405 GetFormat(&input.format); 406 if (fBufferGroup != 0) { 407 BBufferGroup * group = fBufferGroup; 408 fBufferGroup = 0; 409 delete group; 410 } 411 } 412 413 /* The notification comes from the upstream producer, so he's already cool with */ 414 /* the format; you should not ask him about it in here. */ 415 status_t MediaWriter::FormatChanged( 416 const media_source & producer, 417 const media_destination & consumer, 418 int32 change_tag, 419 const media_format & format) 420 { 421 fprintf(stderr,"MediaWriter::FormatChanged\n"); 422 if (input.source != producer) { 423 return B_MEDIA_BAD_SOURCE; 424 } 425 if (input.destination != consumer) { 426 return B_MEDIA_BAD_DESTINATION; 427 } 428 // Since we don't really care about the format of the data 429 // we can just continue to treat things the same way. 430 input.format = format; 431 return B_OK; 432 } 433 434 /* Given a performance time of some previous buffer, retrieve the remembered tag */ 435 /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */ 436 /* idea being that flags can be added later, and the understood flags returned in */ 437 /* *out_flags. */ 438 status_t MediaWriter::SeekTagRequested( 439 const media_destination & destination, 440 bigtime_t in_target_time, 441 uint32 in_flags, 442 media_seek_tag * out_seek_tag, 443 bigtime_t * out_tagged_time, 444 uint32 * out_flags) 445 { 446 fprintf(stderr,"MediaWriter::SeekTagRequested\n"); 447 return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags, 448 out_seek_tag,out_tagged_time,out_flags); 449 } 450 451 // -------------------------------------------------------- // 452 // implementation for BMediaEventLooper 453 // -------------------------------------------------------- // 454 455 // protected: 456 457 // how should we handle late buffers? drop them? 458 // notify the producer? 459 status_t MediaWriter::HandleBuffer( 460 const media_timed_event *event, 461 bigtime_t lateness, 462 bool realTimeEvent) 463 { 464 fprintf(stderr,"MediaWriter::HandleBuffer\n"); 465 BBuffer * buffer = const_cast<BBuffer*>((BBuffer*)event->pointer); 466 if (buffer == 0) { 467 fprintf(stderr,"<- B_BAD_VALUE\n"); 468 return B_BAD_VALUE; 469 } 470 if (buffer->Header()->destination != input.destination.id) { 471 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); 472 return B_MEDIA_BAD_DESTINATION; 473 } 474 WriteFileBuffer(buffer); 475 buffer->Recycle(); 476 return B_OK; 477 } 478 479 status_t MediaWriter::HandleDataStatus( 480 const media_timed_event *event, 481 bigtime_t lateness, 482 bool realTimeEvents) 483 { 484 fprintf(stderr,"MediaWriter::HandleDataStatus"); 485 // we have no where to send a data status to. 486 return B_OK; 487 } 488 489 490 // -------------------------------------------------------- // 491 // MediaWriter specific functions 492 // -------------------------------------------------------- // 493 494 // static: 495 496 void MediaWriter::GetFlavor(flavor_info * outInfo, int32 id) 497 { 498 fprintf(stderr,"MediaWriter::GetFlavor\n"); 499 if (outInfo == 0) { 500 return; 501 } 502 AbstractFileInterfaceNode::GetFlavor(outInfo,id); 503 strcpy(outInfo->name, "Media Writer"); 504 strcpy(outInfo->info, 505 "The Haiku Media Writer consumes a multistream and writes a file."); 506 outInfo->kinds |= B_BUFFER_CONSUMER; 507 outInfo->in_format_count = 1; // 1 input 508 media_format * formats = new media_format[outInfo->in_format_count]; 509 GetFormat(&formats[0]); 510 outInfo->in_formats = formats; 511 return; 512 } 513 514 void MediaWriter::GetFormat(media_format * outFormat) 515 { 516 fprintf(stderr,"MediaWriter::GetFormat\n"); 517 if (outFormat == 0) { 518 return; 519 } 520 AbstractFileInterfaceNode::GetFormat(outFormat); 521 return; 522 } 523 524 void MediaWriter::GetFileFormat(media_file_format * outFileFormat) 525 { 526 fprintf(stderr,"MediaWriter::GetFileFormat\n"); 527 if (outFileFormat == 0) { 528 return; 529 } 530 AbstractFileInterfaceNode::GetFileFormat(outFileFormat); 531 outFileFormat->capabilities |= media_file_format::B_WRITABLE; 532 return; 533 } 534 535 // protected: 536 537 status_t MediaWriter::WriteFileBuffer( 538 BBuffer * buffer) 539 { 540 fprintf(stderr,"MediaWriter::WriteFileBuffer\n"); 541 if (GetCurrentFile() == 0) { 542 fprintf(stderr,"<- B_NO_INIT\n"); 543 return B_NO_INIT; 544 } 545 fprintf(stderr," writing %" B_PRId32 " bytes at %lld\n", 546 buffer->SizeUsed(),GetCurrentFile()->Position()); 547 ssize_t bytesWriten = GetCurrentFile()->Write(buffer->Data(),buffer->SizeUsed()); 548 if (bytesWriten < 0) { 549 fprintf(stderr,"<- B_FILE_ERROR\n"); 550 return B_FILE_ERROR; // some sort of file related error 551 } 552 // nothing more to say? 553 return B_OK; 554 } 555 556 // -------------------------------------------------------- // 557 // stuffing 558 // -------------------------------------------------------- // 559 560 status_t MediaWriter::_Reserved_MediaWriter_0(void *) { return B_ERROR; } 561 status_t MediaWriter::_Reserved_MediaWriter_1(void *) { return B_ERROR; } 562 status_t MediaWriter::_Reserved_MediaWriter_2(void *) { return B_ERROR; } 563 status_t MediaWriter::_Reserved_MediaWriter_3(void *) { return B_ERROR; } 564 status_t MediaWriter::_Reserved_MediaWriter_4(void *) { return B_ERROR; } 565 status_t MediaWriter::_Reserved_MediaWriter_5(void *) { return B_ERROR; } 566 status_t MediaWriter::_Reserved_MediaWriter_6(void *) { return B_ERROR; } 567 status_t MediaWriter::_Reserved_MediaWriter_7(void *) { return B_ERROR; } 568 status_t MediaWriter::_Reserved_MediaWriter_8(void *) { return B_ERROR; } 569 status_t MediaWriter::_Reserved_MediaWriter_9(void *) { return B_ERROR; } 570 status_t MediaWriter::_Reserved_MediaWriter_10(void *) { return B_ERROR; } 571 status_t MediaWriter::_Reserved_MediaWriter_11(void *) { return B_ERROR; } 572 status_t MediaWriter::_Reserved_MediaWriter_12(void *) { return B_ERROR; } 573 status_t MediaWriter::_Reserved_MediaWriter_13(void *) { return B_ERROR; } 574 status_t MediaWriter::_Reserved_MediaWriter_14(void *) { return B_ERROR; } 575 status_t MediaWriter::_Reserved_MediaWriter_15(void *) { return B_ERROR; } 576