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 40MediaWriter::~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 50MediaWriter::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 77void MediaWriter::Preroll(void) 78{ 79 fprintf(stderr,"MediaWriter::Preroll\n"); 80 // XXX:Performance opportunity 81 BMediaNode::Preroll(); 82} 83 84status_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 104void 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 121status_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. 204status_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 236status_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 260void 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 268void 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 304void 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 319status_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 338status_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 391void 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. */ 415status_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. */ 438status_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? 459status_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 479status_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 496void 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 514void 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 524void 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 537status_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 560status_t MediaWriter::_Reserved_MediaWriter_0(void *) { return B_ERROR; } 561status_t MediaWriter::_Reserved_MediaWriter_1(void *) { return B_ERROR; } 562status_t MediaWriter::_Reserved_MediaWriter_2(void *) { return B_ERROR; } 563status_t MediaWriter::_Reserved_MediaWriter_3(void *) { return B_ERROR; } 564status_t MediaWriter::_Reserved_MediaWriter_4(void *) { return B_ERROR; } 565status_t MediaWriter::_Reserved_MediaWriter_5(void *) { return B_ERROR; } 566status_t MediaWriter::_Reserved_MediaWriter_6(void *) { return B_ERROR; } 567status_t MediaWriter::_Reserved_MediaWriter_7(void *) { return B_ERROR; } 568status_t MediaWriter::_Reserved_MediaWriter_8(void *) { return B_ERROR; } 569status_t MediaWriter::_Reserved_MediaWriter_9(void *) { return B_ERROR; } 570status_t MediaWriter::_Reserved_MediaWriter_10(void *) { return B_ERROR; } 571status_t MediaWriter::_Reserved_MediaWriter_11(void *) { return B_ERROR; } 572status_t MediaWriter::_Reserved_MediaWriter_12(void *) { return B_ERROR; } 573status_t MediaWriter::_Reserved_MediaWriter_13(void *) { return B_ERROR; } 574status_t MediaWriter::_Reserved_MediaWriter_14(void *) { return B_ERROR; } 575status_t MediaWriter::_Reserved_MediaWriter_15(void *) { return B_ERROR; } 576