3 // Andrew Bachmann, 2002
5 // A MediaReader is a node that
6 // implements FileInterface and BBufferProducer.
7 // It reads any file and produces one output,
8 // which is a multistream.
10 // see also MediaReaderAddOn.cpp
11 #include "../AbstractFileInterfaceNode.h"
12 #include "MediaReader.h"
17 #include <BufferGroup.h>
18 #include <BufferProducer.h>
19 #include <Controllable.h>
23 #include <FileInterface.h>
24 #include <MediaAddOn.h>
25 #include <MediaDefs.h>
26 #include <MediaEventLooper.h>
27 #include <MediaNode.h>
28 #include <MediaRoster.h>
29 #include <ParameterWeb.h>
30 #include <TimeSource.h>
38 MediaReader::~MediaReader(void)
40 fprintf(stderr
,"MediaReader::~MediaReader\n");
41 if (fBufferGroup
!= 0) {
42 BBufferGroup
* group
= fBufferGroup
;
49 MediaReader::MediaReader(
50 size_t defaultChunkSize
,
52 const flavor_info
* info
,
55 : BMediaNode("MediaReader"),
56 BBufferProducer(B_MEDIA_MULTISTREAM
),
57 AbstractFileInterfaceNode(defaultChunkSize
, defaultBitRate
, info
, config
, addOn
)
64 fOutputEnabled
= true;
65 // don't overwrite available space, and be sure to terminate
66 strncpy(output
.name
,"MediaReader Output",B_MEDIA_NAME_LENGTH
-1);
67 output
.name
[B_MEDIA_NAME_LENGTH
-1] = '\0';
68 // initialize the output
69 output
.node
= media_node::null
; // until registration
70 output
.source
= media_source::null
; // until registration
71 output
.destination
= media_destination::null
;
72 GetFormat(&output
.format
);
76 // -------------------------------------------------------- //
77 // implementation of BMediaNode
78 // -------------------------------------------------------- //
79 void MediaReader::Preroll(void)
82 // XXX:Performance opportunity
83 BMediaNode::Preroll();
87 status_t
MediaReader::HandleMessage(
94 status_t status
= B_OK
;
97 // no special messages for now
99 status
= BBufferProducer::HandleMessage(message
,data
,size
);
100 if (status
== B_OK
) {
103 status
= AbstractFileInterfaceNode::HandleMessage(message
,data
,size
);
111 void MediaReader::NodeRegistered(void)
115 // now we can do this
116 output
.node
= Node();
117 output
.source
.id
= 0;
118 output
.source
.port
= output
.node
.port
; // same as ControlPort();
120 // creates the parameter web and starts the looper thread
121 AbstractFileInterfaceNode::NodeRegistered();
125 // -------------------------------------------------------- //
126 // implementation of BFileInterface
127 // -------------------------------------------------------- //
128 status_t
MediaReader::SetRef(
129 const entry_ref
& file
,
131 bigtime_t
* out_time
)
135 status_t status
= AbstractFileInterfaceNode::SetRef(file
,B_READ_ONLY
,create
,out_time
);
136 if (status
!= B_OK
) {
137 PRINT("AbstractFileInterfaceNode::SetRef returned an error\n");
141 if (output
.destination
== media_destination::null
) {
142 // reset the format, and set the requirements imposed by this file
143 GetFormat(&output
.format
);
144 AddRequirements(&output
.format
);
148 // if we are connected we may have to re-negotiate the connection
151 AddRequirements(&format
);
152 if (format_is_acceptible(format
,output
.format
)) {
153 fprintf(stderr
," compatible format = no re-negotiation necessary\n");
156 // first try the easy way : SORRY DEPRECATED into private :-(
157 // this code from MediaWriter would be different for MediaReader even if it worked...
158 // int32 change_tag = NewChangeTag();
159 // status = this->BBufferConsumer::RequestFormatChange(output.source,output.destination,&format,&change_tag);
160 // if (status == B_OK) {
161 // fprintf(stderr," format change successful\n");
165 // okay, the hard way requires we get the MediaRoster
166 BMediaRoster
* roster
= BMediaRoster::Roster(&status
);
168 return B_MEDIA_SYSTEM_FAILURE
;
173 // before disconnect one should always stop the nodes (bebook says)
174 // requires run_state cast since the return type on RunState() is
176 run_state destinationRunState
= run_state(RunState());
177 if (destinationRunState
== BMediaEventLooper::B_STARTED
)
178 Stop(0,true); // stop us right now
180 // should also stop the destination if it is running, but how?
181 /* BMediaNode destinationNode = ??
182 run_state destinationRunState = destinationNode->??;
183 status = destinationNode->StopNode(??,0,true);
184 if (status != B_OK) {
187 // we should disconnect right now
188 media_destination outputDestination
= output
.destination
;
189 status
= roster
->Disconnect(output
.source
.id
,output
.source
,
190 output
.destination
.id
,output
.destination
);
194 // if that went okay, we'll try reconnecting
195 media_output connectOutput
;
196 media_input connectInput
;
197 status
= roster
->Connect(output
.source
,outputDestination
,
198 &format
,&connectOutput
,&connectInput
);
202 // now restart if necessary
203 if (destinationRunState
== BMediaEventLooper::B_STARTED
) {
210 // -------------------------------------------------------- //
211 // implemention of BBufferProducer
212 // -------------------------------------------------------- //
214 // They are asking us to make the first offering.
215 // So, we get a fresh format and then add requirements based
216 // on the current file. (if any)
217 status_t
MediaReader::FormatSuggestionRequested(
220 media_format
* format
)
224 if ((type
!= B_MEDIA_MULTISTREAM
) && (type
!= B_MEDIA_UNKNOWN_TYPE
)) {
225 PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
226 return B_MEDIA_BAD_FORMAT
;
230 AddRequirements(format
);
235 // They made an offer to us. We should make sure that the offer is
236 // acceptable, and then we can add any requirements we have on top of
237 // that. We leave wildcards for anything that we don't care about.
238 status_t
MediaReader::FormatProposal(
239 const media_source
& output_source
,
240 media_format
* format
)
244 if (output
.source
!= output_source
) {
245 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
246 return B_MEDIA_BAD_SOURCE
; // we only have one output so that better be it
248 /* media_format * myFormat = GetFormat();
249 fprintf(stderr,"proposed format: ");
250 print_media_format(format);
251 fprintf(stderr,"\n");
252 fprintf(stderr,"my format: ");
253 print_media_format(myFormat);
254 fprintf(stderr,"\n"); */
255 // Be's format_is_compatible doesn't work.
256 // if (!format_is_compatible(*format,*myFormat)) {
257 media_format myFormat
;
258 GetFormat(&myFormat
);
259 if (!format_is_acceptible(*format
,myFormat
)) {
260 PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
261 return B_MEDIA_BAD_FORMAT
;
263 AddRequirements(format
);
268 // Presumably we have already agreed with them that this format is
269 // okay. But just in case, we check the offer. (and complain if it
270 // is invalid) Then as the last thing we do, we get rid of any
271 // remaining wilcards.
272 status_t
MediaReader::FormatChangeRequested(
273 const media_source
& source
,
274 const media_destination
& destination
,
275 media_format
* io_format
,
276 int32
* _deprecated_
)
280 if (output
.source
!= source
) {
281 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
282 return B_MEDIA_BAD_SOURCE
;
284 status_t status
= FormatProposal(source
,io_format
);
285 if (status
!= B_OK
) {
286 PRINT("\terror returned by FormatProposal\n");
287 GetFormat(io_format
);
291 return ResolveWildcards(io_format
);
295 status_t
MediaReader::GetNextOutput( /* cookie starts as 0 */
297 media_output
* out_output
)
302 PRINT("\t<- B_ERROR (no more outputs)\n");
306 // so next time they won't get the same output again
308 *out_output
= output
;
313 status_t
MediaReader::DisposeOutputCookie(
317 // nothing to do since our cookies are just integers
322 status_t
MediaReader::SetBufferGroup(
323 const media_source
& for_source
,
324 BBufferGroup
* group
)
328 if (output
.source
!= for_source
) {
329 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
330 return B_MEDIA_BAD_SOURCE
; // we only have one output so that better be it
332 if (fBufferGroup
!= 0) {
333 if (fBufferGroup
== group
)
334 return B_OK
; // time saver
338 fBufferGroup
= group
;
340 // let's take advantage of this opportunity to recalculate
341 // our downstream latency and ensure that it is up to date
343 FindLatencyFor(output
.destination
, &fDownstreamLatency
, &id
);
344 // buffer period gets initialized in Connect() because
345 // that is the first time we get the real values for
346 // chunk size and bit rate, which are used to compute buffer period
347 // note: you can still make a buffer group before connecting (why?)
348 // but we don't make it, you make it yourself and pass it here.
349 // not sure why anybody would want to do that since they need
350 // a connection anyway...
351 if (fBufferPeriod
<= 0) {
352 fprintf(stderr
,"<- B_NO_INIT");
355 int32 count
= int32(fDownstreamLatency
/fBufferPeriod
)+2;
356 PRINT("\tdownstream latency = %lld, buffer period = %lld, buffer count = %ld\n",
357 fDownstreamLatency
, fBufferPeriod
, count
);
359 // allocate the buffers
360 fBufferGroup
= new BBufferGroup(output
.format
.u
.multistream
.max_chunk_size
,count
);
361 if (fBufferGroup
== 0) {
362 PRINT("\t<- B_NO_MEMORY\n");
365 status_t status
= fBufferGroup
->InitCheck();
366 if (status
!= B_OK
) {
367 PRINT("\t<- fBufferGroup initialization failed\n");
375 /* Format of clipping is (as int16-s): <from line> <npairs> <startclip> <endclip>. */
376 /* Repeat for each line where the clipping is different from the previous line. */
377 /* If <npairs> is negative, use the data from line -<npairs> (there are 0 pairs after */
378 /* a negative <npairs>. Yes, we only support 32k*32k frame buffers for clipping. */
379 /* Any non-0 field of 'display' means that that field changed, and if you don't support */
380 /* that change, you should return an error and ignore the request. Note that the buffer */
381 /* offset values do not have wildcards; 0 (or -1, or whatever) are real values and must */
383 status_t
MediaReader::VideoClippingChanged(
384 const media_source
& for_source
,
387 const media_video_display_info
& display
,
388 int32
* _deprecated_
)
390 return BBufferProducer::VideoClippingChanged(for_source
,num_shorts
,clip_data
,display
,_deprecated_
);
394 status_t
MediaReader::GetLatency(
395 bigtime_t
* out_latency
)
399 *out_latency
= EventLatency() + SchedulingLatency();
404 status_t
MediaReader::PrepareToConnect(
405 const media_source
& what
,
406 const media_destination
& where
,
407 media_format
* format
,
408 media_source
* out_source
,
413 if (output
.source
!= what
) {
414 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
415 return B_MEDIA_BAD_SOURCE
;
417 if (output
.destination
!= media_destination::null
) {
418 PRINT("\t<- B_MEDIA_ALREADY_CONNECTED\n");
419 return B_MEDIA_ALREADY_CONNECTED
;
422 status_t status
= FormatChangeRequested(output
.source
,where
,format
,0);
423 if (status
!= B_OK
) {
424 PRINT("\t<- MediaReader::FormatChangeRequested failed\n");
428 // last check for wildcards and general validity
429 if (format
->type
!= B_MEDIA_MULTISTREAM
) {
430 PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
431 return B_MEDIA_BAD_FORMAT
;
434 *out_source
= output
.source
;
435 output
.destination
= where
;
436 strncpy(out_name
,output
.name
,B_MEDIA_NAME_LENGTH
-1);
437 out_name
[B_MEDIA_NAME_LENGTH
] = '\0';
438 return ResolveWildcards(format
);
442 void MediaReader::Connect(
444 const media_source
& source
,
445 const media_destination
& destination
,
446 const media_format
& format
,
452 PRINT("\t<- error already\n");
453 output
.destination
= media_destination::null
;
454 GetFormat(&output
.format
);
457 if (output
.source
!= source
) {
458 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
459 output
.destination
= media_destination::null
;
460 GetFormat(&output
.format
);
464 // record the agreed upon values
465 output
.destination
= destination
;
466 output
.format
= format
;
467 strncpy(io_name
,output
.name
,B_MEDIA_NAME_LENGTH
-1);
468 io_name
[B_MEDIA_NAME_LENGTH
-1] = '\0';
470 // determine our downstream latency
472 FindLatencyFor(output
.destination
, &fDownstreamLatency
, &id
);
474 // compute the buffer period (must be done before setbuffergroup)
475 fBufferPeriod
= bigtime_t(1000u * 8000000u / 1024u
476 * output
.format
.u
.multistream
.max_chunk_size
477 / output
.format
.u
.multistream
.max_bit_rate
);
479 PRINT("\tmax chunk size = %ld, max bit rate = %f, buffer period = %lld\n",
480 output
.format
.u
.multistream
.max_chunk_size
,
481 output
.format
.u
.multistream
.max_bit_rate
,fBufferPeriod
);
483 // setup the buffers if they aren't setup yet
484 if (fBufferGroup
== 0) {
485 status_t status
= SetBufferGroup(output
.source
,0);
486 if (status
!= B_OK
) {
487 PRINT("\t<- SetBufferGroup failed\n");
488 output
.destination
= media_destination::null
;
489 GetFormat(&output
.format
);
494 SetBufferDuration(fBufferPeriod
);
496 if (GetCurrentFile() != 0) {
497 bigtime_t start
, end
;
498 // buffer group buffer size
499 uint8
* data
= new uint8
[output
.format
.u
.multistream
.max_chunk_size
];
500 BBuffer
* buffer
= 0;
501 ssize_t bytesRead
= 0;
503 start
= TimeSource()->RealTime();
504 // first we try to use a real BBuffer
505 buffer
= fBufferGroup
->RequestBuffer(
506 output
.format
.u
.multistream
.max_chunk_size
,fBufferPeriod
);
508 FillFileBuffer(buffer
);
510 // didn't get a real BBuffer, try simulation by just a read from the disk
511 bytesRead
= GetCurrentFile()->Read(
512 data
, output
.format
.u
.multistream
.max_chunk_size
);
514 end
= TimeSource()->RealTime();
516 bytesRead
= buffer
->SizeUsed();
521 GetCurrentFile()->Seek(-bytesRead
,SEEK_CUR
); // put it back where we found it
523 fInternalLatency
= end
- start
;
525 PRINT("\tinternal latency from disk read = %lld\n", fInternalLatency
);
527 fInternalLatency
= 100; // just guess
528 PRINT("\tinternal latency guessed = %lld\n", fInternalLatency
);
531 SetEventLatency(fDownstreamLatency
+ fInternalLatency
);
533 // XXX: do anything else?
537 void MediaReader::Disconnect(
538 const media_source
& what
,
539 const media_destination
& where
)
543 if (output
.destination
!= where
) {
544 PRINT("\t<- B_MEDIA_BAD_DESTINATION\n");
547 if (output
.source
!= what
) {
548 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
552 output
.destination
= media_destination::null
;
553 GetFormat(&output
.format
);
554 if (fBufferGroup
!= 0) {
555 BBufferGroup
* group
= fBufferGroup
;
562 void MediaReader::LateNoticeReceived(
563 const media_source
& what
,
565 bigtime_t performance_time
)
569 if (what
== output
.source
) {
577 case B_INCREASE_LATENCY
:
578 fInternalLatency
+= how_much
;
579 SetEventLatency(fDownstreamLatency
+ fInternalLatency
);
581 case B_DECREASE_PRECISION
:
582 // XXX : shorten our buffer period
583 // We could opt to just not wait but we should
584 // probably gradually shorten the period so we
585 // don't starve others. Also, we need to make
586 // sure we are catching up! We may have some sort
587 // of time goal for how long it takes us to
588 // catch up, as well.
591 // Okay you asked for it, we'll skip ahead in the file!
592 // We'll drop 1 buffer's worth
593 if (GetCurrentFile() == 0) {
594 PRINT("MediaReader::LateNoticeReceived called without"
595 "an GetCurrentFile() (!)\n");
597 GetCurrentFile()->Seek(output
.format
.u
.multistream
.max_chunk_size
,SEEK_CUR
);
601 // huh?? there aren't any more run modes.
602 PRINT("MediaReader::LateNoticeReceived with unexpected run mode.\n");
609 void MediaReader::EnableOutput(
610 const media_source
& what
,
612 int32
* _deprecated_
)
616 if (output
.source
!= what
) {
617 PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
621 fOutputEnabled
= enabled
;
625 status_t
MediaReader::SetPlayRate(
629 return BBufferProducer::SetPlayRate(numer
,denom
); // XXX: do something intelligent later
633 void MediaReader::AdditionalBufferRequested( // used to be Reserved 0
634 const media_source
& source
,
635 media_buffer_id prev_buffer
,
637 const media_seek_tag
* prev_tag
)
641 if (output
.source
== source
) {
643 status_t status
= GetFilledBuffer(&buffer
);
644 if (status
!= B_OK
) {
645 PRINT("MediaReader::AdditionalBufferRequested got an error from GetFilledBuffer.\n");
646 return; // don't send the buffer
648 SendBuffer(buffer
, output
.source
, output
.destination
);
653 void MediaReader::LatencyChanged(
654 const media_source
& source
,
655 const media_destination
& destination
,
656 bigtime_t new_latency
,
660 if ((output
.source
== source
) && (output
.destination
== destination
)) {
661 fDownstreamLatency
= new_latency
;
662 SetEventLatency(fDownstreamLatency
+ fInternalLatency
);
664 // we may have to recompute the number of buffers that we are using
665 // see SetBufferGroup
669 // -------------------------------------------------------- //
670 // implementation for BMediaEventLooper
671 // -------------------------------------------------------- //
673 status_t
MediaReader::HandleBuffer(
674 const media_timed_event
*event
,
680 if (output
.destination
== media_destination::null
)
681 return B_MEDIA_NOT_CONNECTED
;
683 status_t status
= B_OK
;
684 BBuffer
* buffer
= fBufferGroup
->RequestBuffer(output
.format
.u
.multistream
.max_chunk_size
,fBufferPeriod
);
686 status
= FillFileBuffer(buffer
);
687 if (status
!= B_OK
) {
688 PRINT("MediaReader::HandleEvent got an error from FillFileBuffer.\n");
691 if (fOutputEnabled
) {
692 status
= SendBuffer(buffer
, output
.source
, output
.destination
);
693 if (status
!= B_OK
) {
694 PRINT("MediaReader::HandleEvent got an error from SendBuffer.\n");
702 bigtime_t nextEventTime
= event
->event_time
+fBufferPeriod
;
703 media_timed_event
nextBufferEvent(nextEventTime
, BTimedEventQueue::B_HANDLE_BUFFER
);
704 EventQueue()->AddEvent(nextBufferEvent
);
709 status_t
MediaReader::HandleDataStatus(
710 const media_timed_event
*event
,
715 return SendDataStatus(event
->data
,output
.destination
,event
->event_time
);
719 // -------------------------------------------------------- //
720 // MediaReader specific functions
721 // -------------------------------------------------------- //
723 void MediaReader::GetFlavor(flavor_info
* outInfo
, int32 id
)
730 AbstractFileInterfaceNode::GetFlavor(outInfo
,id
);
731 outInfo
->name
= strdup("Media Reader");
732 outInfo
->info
= strdup(
733 "The Haiku Media Reader reads a file and produces a multistream.");
734 outInfo
->kinds
|= B_BUFFER_PRODUCER
;
735 outInfo
->out_format_count
= 1; // 1 output
736 media_format
* formats
= new media_format
[outInfo
->out_format_count
];
737 GetFormat(&formats
[0]);
738 outInfo
->out_formats
= formats
;
743 void MediaReader::GetFormat(media_format
* outFormat
)
747 AbstractFileInterfaceNode::GetFormat(outFormat
);
752 void MediaReader::GetFileFormat(media_file_format
* outFileFormat
)
756 AbstractFileInterfaceNode::GetFileFormat(outFileFormat
);
757 outFileFormat
->capabilities
|= media_file_format::B_READABLE
;
763 status_t
MediaReader::GetFilledBuffer(
764 BBuffer
** outBuffer
)
768 BBuffer
* buffer
= fBufferGroup
->RequestBuffer(output
.format
.u
.multistream
.max_chunk_size
,-1);
770 // XXX: add a new buffer and get it
771 PRINT("MediaReader::GetFilledBuffer needs a new buffer.\n");
772 return B_ERROR
; // don't send the buffer
775 status_t status
= FillFileBuffer(buffer
);
781 status_t
MediaReader::FillFileBuffer(
786 if (GetCurrentFile() == 0) {
787 PRINT("\t<- B_NO_INIT\n");
790 PRINT("\t%ld buffer bytes used, %ld buffer bytes available\n",
791 buffer
->SizeUsed(), buffer
->SizeAvailable());
792 off_t position
= GetCurrentFile()->Position();
793 ssize_t bytesRead
= GetCurrentFile()->Read(buffer
->Data(),buffer
->SizeAvailable());
795 PRINT("\t<- B_FILE_ERROR\n");
796 return B_FILE_ERROR
; // some sort of file related error
798 PRINT("\t%ld file bytes read at position %ld.\n",
799 bytesRead
, position
);
801 buffer
->SetSizeUsed(bytesRead
);
802 media_header
* header
= buffer
->Header();
803 header
->type
= B_MEDIA_MULTISTREAM
;
804 header
->size_used
= bytesRead
;
805 header
->file_pos
= position
;
806 header
->orig_size
= bytesRead
;
807 header
->time_source
= TimeSource()->ID();
808 header
->start_time
= TimeSource()->Now();
809 // nothing more to say?
814 // -------------------------------------------------------- //
816 // -------------------------------------------------------- //
817 status_t
MediaReader::_Reserved_MediaReader_0(void *) { return B_ERROR
; }
818 status_t
MediaReader::_Reserved_MediaReader_1(void *) { return B_ERROR
; }
819 status_t
MediaReader::_Reserved_MediaReader_2(void *) { return B_ERROR
; }
820 status_t
MediaReader::_Reserved_MediaReader_3(void *) { return B_ERROR
; }
821 status_t
MediaReader::_Reserved_MediaReader_4(void *) { return B_ERROR
; }
822 status_t
MediaReader::_Reserved_MediaReader_5(void *) { return B_ERROR
; }
823 status_t
MediaReader::_Reserved_MediaReader_6(void *) { return B_ERROR
; }
824 status_t
MediaReader::_Reserved_MediaReader_7(void *) { return B_ERROR
; }
825 status_t
MediaReader::_Reserved_MediaReader_8(void *) { return B_ERROR
; }
826 status_t
MediaReader::_Reserved_MediaReader_9(void *) { return B_ERROR
; }
827 status_t
MediaReader::_Reserved_MediaReader_10(void *) { return B_ERROR
; }
828 status_t
MediaReader::_Reserved_MediaReader_11(void *) { return B_ERROR
; }
829 status_t
MediaReader::_Reserved_MediaReader_12(void *) { return B_ERROR
; }
830 status_t
MediaReader::_Reserved_MediaReader_13(void *) { return B_ERROR
; }
831 status_t
MediaReader::_Reserved_MediaReader_14(void *) { return B_ERROR
; }
832 status_t
MediaReader::_Reserved_MediaReader_15(void *) { return B_ERROR
; }