3 // Andrew Bachmann, 2002
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.
10 // see also MediaWriterAddOn.cpp
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>
22 #include <BufferGroup.h>
23 #include <TimeSource.h>
25 #include <ParameterWeb.h>
26 #include <MediaRoster.h>
29 #include "../AbstractFileInterfaceNode.h"
30 #include "MediaWriter.h"
36 // -------------------------------------------------------- //
38 // -------------------------------------------------------- //
40 MediaWriter::~MediaWriter(void)
42 fprintf(stderr
,"MediaWriter::~MediaWriter\n");
43 if (fBufferGroup
!= 0) {
44 BBufferGroup
* group
= fBufferGroup
;
50 MediaWriter::MediaWriter(
51 size_t defaultChunkSize
,
53 const flavor_info
* info
,
56 : BMediaNode("MediaWriter"),
57 BBufferConsumer(B_MEDIA_MULTISTREAM
),
58 AbstractFileInterfaceNode(defaultChunkSize
,defaultBitRate
,info
,config
,addOn
)
60 fprintf(stderr
,"MediaWriter::MediaWriter\n");
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
);
73 // -------------------------------------------------------- //
74 // implementation of BMediaNode
75 // -------------------------------------------------------- //
77 void MediaWriter::Preroll(void)
79 fprintf(stderr
,"MediaWriter::Preroll\n");
80 // XXX:Performance opportunity
81 BMediaNode::Preroll();
84 status_t
MediaWriter::HandleMessage(
89 fprintf(stderr
,"MediaWriter::HandleMessage\n");
90 status_t status
= B_OK
;
92 // no special messages for now
94 status
= BBufferConsumer::HandleMessage(message
,data
,size
);
98 status
= AbstractFileInterfaceNode::HandleMessage(message
,data
,size
);
104 void MediaWriter::NodeRegistered(void)
106 fprintf(stderr
,"MediaWriter::NodeRegistered\n");
108 // now we can do this
110 input
.destination
.id
= 0;
111 input
.destination
.port
= input
.node
.port
; // same as ControlPort()
113 // creates the parameter web and starts the looper thread
114 AbstractFileInterfaceNode::NodeRegistered();
117 // -------------------------------------------------------- //
118 // implementation of BFileInterface
119 // -------------------------------------------------------- //
121 status_t
MediaWriter::SetRef(
122 const entry_ref
& file
,
124 bigtime_t
* out_time
)
126 fprintf(stderr
,"MediaWriter::SetRef\n");
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");
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
);
139 // if we are connected we may have to re-negotiate the connection
142 AddRequirements(&format
);
143 if (format_is_acceptible(input
.format
,format
)) {
144 fprintf(stderr
," compatible format = no re-negotiation necessary\n");
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");
154 // okay, the hard way requires we get the MediaRoster
155 BMediaRoster
* roster
= BMediaRoster::Roster(&status
);
157 return B_MEDIA_SYSTEM_FAILURE
;
159 if (status
!= B_OK
) {
162 // before disconnect one should always stop the nodes (bebook says)
163 // requires run_state cast since the return type on RunState() is
165 run_state destinationRunState
= run_state(RunState());
166 if (destinationRunState
== BMediaEventLooper::B_STARTED
) {
167 Stop(0,true); // stop us right now
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) {
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
) {
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
) {
191 // now restart if necessary
192 if (destinationRunState
== BMediaEventLooper::B_STARTED
) {
198 // -------------------------------------------------------- //
199 // implemention of BBufferConsumer
200 // -------------------------------------------------------- //
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
)
208 fprintf(stderr
,"MediaWriter::AcceptFormat\n");
210 fprintf(stderr
,"<- B_BAD_VALUE\n");
211 return B_BAD_VALUE
; // no crashing
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
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
;
232 AddRequirements(format
);
236 status_t
MediaWriter::GetNextInput(
238 media_input
* out_input
)
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");
248 // it's valid but they already got our 1 input
250 fprintf(stderr
,"<- B_ERROR (no more inputs)\n");
253 // so next time they won't get the same input again
260 void MediaWriter::DisposeInputCookie(
263 fprintf(stderr
,"MediaWriter::DisposeInputCookie\n");
264 // nothing to do since our cookies are just integers
268 void MediaWriter::BufferReceived(
271 fprintf(stderr
,"MediaWriter::BufferReceived\n");
272 switch (buffer
->Header()->type
) {
273 case B_MEDIA_PARAMETERS
:
275 status_t status
= ApplyParameterData(buffer
->Data(),buffer
->SizeUsed());
276 if (status
!= B_OK
) {
277 fprintf(stderr
,"ApplyParameterData in MediaWriter::BufferReceived failed\n");
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
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");
298 fprintf(stderr
,"unexpected buffer type in MediaWriter::BufferReceived\n");
304 void MediaWriter::ProducerDataStatus(
305 const media_destination
& for_whom
,
307 bigtime_t at_performance_time
)
309 fprintf(stderr
,"MediaWriter::ProducerDataStatus\n");
310 if (input
.destination
!= for_whom
) {
311 fprintf(stderr
,"invalid destination received in MediaWriter::ProducerDataStatus\n");
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
);
319 status_t
MediaWriter::GetLatencyFor(
320 const media_destination
& for_whom
,
321 bigtime_t
* out_latency
,
322 media_node_id
* out_timesource
)
324 fprintf(stderr
,"MediaWriter::GetLatencyFor\n");
325 if ((out_latency
== 0) || (out_timesource
== 0)) {
326 fprintf(stderr
,"<- B_BAD_VALUE\n");
329 if (input
.destination
!= for_whom
) {
330 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
331 return B_MEDIA_BAD_DESTINATION
;
333 *out_latency
= EventLatency();
334 *out_timesource
= TimeSource()->ID();
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
)
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
349 if (input
.destination
!= where
) {
350 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
351 return B_MEDIA_BAD_DESTINATION
;
354 // clear any stale buffer groups
355 if (fBufferGroup
!= 0) {
356 BBufferGroup
* group
= fBufferGroup
;
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;
367 start
= TimeSource()->RealTime();
368 bytesWritten
= GetCurrentFile()->Write(data
,input
.format
.u
.multistream
.max_chunk_size
);
369 end
= TimeSource()->RealTime();
372 GetCurrentFile()->Seek(-bytesWritten
,SEEK_CUR
); // put it back where we found it
374 fInternalLatency
= end
- start
;
376 fprintf(stderr
," internal latency from disk write = %lld\n",fInternalLatency
);
378 fInternalLatency
= 500; // just guess
379 fprintf(stderr
," internal latency guessed = %lld\n",fInternalLatency
);
382 SetEventLatency(fInternalLatency
);
384 // record the agreed upon values
385 input
.source
= producer
;
386 input
.format
= with_format
;
391 void MediaWriter::Disconnected(
392 const media_source
& producer
,
393 const media_destination
& where
)
395 fprintf(stderr
,"MediaWriter::Disconnected\n");
396 if (input
.destination
!= where
) {
397 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
400 if (input
.source
!= producer
) {
401 fprintf(stderr
,"<- B_MEDIA_BAD_SOURCE\n");
404 input
.source
= media_source::null
;
405 GetFormat(&input
.format
);
406 if (fBufferGroup
!= 0) {
407 BBufferGroup
* group
= fBufferGroup
;
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
,
419 const media_format
& format
)
421 fprintf(stderr
,"MediaWriter::FormatChanged\n");
422 if (input
.source
!= producer
) {
423 return B_MEDIA_BAD_SOURCE
;
425 if (input
.destination
!= consumer
) {
426 return B_MEDIA_BAD_DESTINATION
;
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
;
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 */
438 status_t
MediaWriter::SeekTagRequested(
439 const media_destination
& destination
,
440 bigtime_t in_target_time
,
442 media_seek_tag
* out_seek_tag
,
443 bigtime_t
* out_tagged_time
,
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
);
451 // -------------------------------------------------------- //
452 // implementation for BMediaEventLooper
453 // -------------------------------------------------------- //
457 // how should we handle late buffers? drop them?
458 // notify the producer?
459 status_t
MediaWriter::HandleBuffer(
460 const media_timed_event
*event
,
464 fprintf(stderr
,"MediaWriter::HandleBuffer\n");
465 BBuffer
* buffer
= const_cast<BBuffer
*>((BBuffer
*)event
->pointer
);
467 fprintf(stderr
,"<- B_BAD_VALUE\n");
470 if (buffer
->Header()->destination
!= input
.destination
.id
) {
471 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
472 return B_MEDIA_BAD_DESTINATION
;
474 WriteFileBuffer(buffer
);
479 status_t
MediaWriter::HandleDataStatus(
480 const media_timed_event
*event
,
484 fprintf(stderr
,"MediaWriter::HandleDataStatus");
485 // we have no where to send a data status to.
490 // -------------------------------------------------------- //
491 // MediaWriter specific functions
492 // -------------------------------------------------------- //
496 void MediaWriter::GetFlavor(flavor_info
* outInfo
, int32 id
)
498 fprintf(stderr
,"MediaWriter::GetFlavor\n");
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
;
514 void MediaWriter::GetFormat(media_format
* outFormat
)
516 fprintf(stderr
,"MediaWriter::GetFormat\n");
517 if (outFormat
== 0) {
520 AbstractFileInterfaceNode::GetFormat(outFormat
);
524 void MediaWriter::GetFileFormat(media_file_format
* outFileFormat
)
526 fprintf(stderr
,"MediaWriter::GetFileFormat\n");
527 if (outFileFormat
== 0) {
530 AbstractFileInterfaceNode::GetFileFormat(outFileFormat
);
531 outFileFormat
->capabilities
|= media_file_format::B_WRITABLE
;
537 status_t
MediaWriter::WriteFileBuffer(
540 fprintf(stderr
,"MediaWriter::WriteFileBuffer\n");
541 if (GetCurrentFile() == 0) {
542 fprintf(stderr
,"<- B_NO_INIT\n");
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
552 // nothing more to say?
556 // -------------------------------------------------------- //
558 // -------------------------------------------------------- //
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
; }