2 * Copyright 2004-2007, Marcus Overhagen. All rights reserved.
3 * Copyright 2008, Maurice Kalinowski. All rights reserved.
4 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
6 * Distributed under the terms of the MIT License.
10 #include "MediaExtractor.h"
18 #include "ChunkCache.h"
20 #include "PluginManager.h"
23 // should be 0, to disable the chunk cache set it to 1
24 #define DISABLE_CHUNK_CACHE 0
27 static const size_t kMaxCacheBytes
= 3 * 1024 * 1024;
30 class MediaExtractorChunkProvider
: public ChunkProvider
{
32 MediaExtractorChunkProvider(MediaExtractor
* extractor
, int32 stream
)
34 fExtractor(extractor
),
39 virtual status_t
GetNextChunk(const void** _chunkBuffer
, size_t* _chunkSize
,
40 media_header
*mediaHeader
)
42 return fExtractor
->GetNextChunk(fStream
, _chunkBuffer
, _chunkSize
,
47 MediaExtractor
* fExtractor
;
55 MediaExtractor::MediaExtractor(BDataIO
* source
, int32 flags
)
67 MediaExtractor::_Init(BDataIO
* source
, int32 flags
)
73 #if !DISABLE_CHUNK_CACHE
74 // start extractor thread
75 fExtractorWaitSem
= create_sem(1, "media extractor thread sem");
76 if (fExtractorWaitSem
< 0) {
77 fInitStatus
= fExtractorWaitSem
;
82 fInitStatus
= gPluginManager
.CreateReader(&fReader
, &fStreamCount
,
83 &fFileFormat
, source
);
84 if (fInitStatus
!= B_OK
)
87 fStreamInfo
= new stream_info
[fStreamCount
];
89 // initialize stream infos
90 for (int32 i
= 0; i
< fStreamCount
; i
++) {
91 fStreamInfo
[i
].status
= B_OK
;
92 fStreamInfo
[i
].cookie
= 0;
93 fStreamInfo
[i
].hasCookie
= false;
94 fStreamInfo
[i
].infoBuffer
= 0;
95 fStreamInfo
[i
].infoBufferSize
= 0;
96 fStreamInfo
[i
].chunkCache
97 = new ChunkCache(fExtractorWaitSem
, kMaxCacheBytes
);
98 fStreamInfo
[i
].lastChunk
= NULL
;
99 memset(&fStreamInfo
[i
].encodedFormat
, 0,
100 sizeof(fStreamInfo
[i
].encodedFormat
));
102 if (fStreamInfo
[i
].chunkCache
->InitCheck() != B_OK
) {
103 fInitStatus
= B_NO_MEMORY
;
108 // create all stream cookies
109 for (int32 i
= 0; i
< fStreamCount
; i
++) {
110 if (fReader
->AllocateCookie(i
, &fStreamInfo
[i
].cookie
) != B_OK
) {
111 fStreamInfo
[i
].cookie
= 0;
112 fStreamInfo
[i
].hasCookie
= false;
113 fStreamInfo
[i
].status
= B_ERROR
;
114 ERROR("MediaExtractor::MediaExtractor: AllocateCookie for stream %"
115 B_PRId32
" failed\n", i
);
117 fStreamInfo
[i
].hasCookie
= true;
120 // get info for all streams
121 for (int32 i
= 0; i
< fStreamCount
; i
++) {
122 if (fStreamInfo
[i
].status
!= B_OK
)
127 if (fReader
->GetStreamInfo(fStreamInfo
[i
].cookie
, &frameCount
,
128 &duration
, &fStreamInfo
[i
].encodedFormat
,
129 &fStreamInfo
[i
].infoBuffer
, &fStreamInfo
[i
].infoBufferSize
)
131 fStreamInfo
[i
].status
= B_ERROR
;
132 ERROR("MediaExtractor::MediaExtractor: GetStreamInfo for "
133 "stream %" B_PRId32
" failed\n", i
);
137 #if !DISABLE_CHUNK_CACHE
138 // start extractor thread
139 fExtractorThread
= spawn_thread(_ExtractorEntry
, "media extractor thread",
140 B_NORMAL_PRIORITY
+ 4, this);
141 resume_thread(fExtractorThread
);
146 MediaExtractor::~MediaExtractor()
150 // stop the extractor thread, if still running
153 // free all stream cookies
155 for (int32 i
= 0; i
< fStreamCount
; i
++) {
156 if (fStreamInfo
[i
].hasCookie
)
157 fReader
->FreeCookie(fStreamInfo
[i
].cookie
);
159 delete fStreamInfo
[i
].chunkCache
;
162 gPluginManager
.DestroyReader(fReader
);
164 delete[] fStreamInfo
;
165 // fSource is owned by the BMediaFile
170 MediaExtractor::InitCheck()
178 MediaExtractor::Source() const
185 MediaExtractor::GetFileFormatInfo(media_file_format
* fileFormat
) const
188 *fileFormat
= fFileFormat
;
193 MediaExtractor::GetMetaData(BMessage
* _data
) const
196 return fReader
->GetMetaData(_data
);
201 MediaExtractor::StreamCount()
209 MediaExtractor::Copyright()
211 return fReader
->Copyright();
216 MediaExtractor::EncodedFormat(int32 stream
)
218 return &fStreamInfo
[stream
].encodedFormat
;
223 MediaExtractor::CountFrames(int32 stream
) const
226 if (fStreamInfo
[stream
].status
!= B_OK
)
232 const void* infoBuffer
;
235 fReader
->GetStreamInfo(fStreamInfo
[stream
].cookie
, &frameCount
, &duration
,
236 &format
, &infoBuffer
, &infoSize
);
243 MediaExtractor::Duration(int32 stream
) const
247 if (fStreamInfo
[stream
].status
!= B_OK
)
253 const void* infoBuffer
;
256 fReader
->GetStreamInfo(fStreamInfo
[stream
].cookie
, &frameCount
, &duration
,
257 &format
, &infoBuffer
, &infoSize
);
264 MediaExtractor::Seek(int32 stream
, uint32 seekTo
, int64
* _frame
,
269 stream_info
& info
= fStreamInfo
[stream
];
270 if (info
.status
!= B_OK
)
273 BAutolock
_(info
.chunkCache
);
275 status_t status
= fReader
->Seek(info
.cookie
, seekTo
, _frame
, _time
);
279 // clear buffered chunks after seek
280 info
.chunkCache
->MakeEmpty();
287 MediaExtractor::FindKeyFrame(int32 stream
, uint32 seekTo
, int64
* _frame
,
288 bigtime_t
* _time
) const
292 stream_info
& info
= fStreamInfo
[stream
];
293 if (info
.status
!= B_OK
)
296 return fReader
->FindKeyFrame(info
.cookie
, seekTo
, _frame
, _time
);
301 MediaExtractor::GetNextChunk(int32 stream
, const void** _chunkBuffer
,
302 size_t* _chunkSize
, media_header
* mediaHeader
)
304 stream_info
& info
= fStreamInfo
[stream
];
306 if (info
.status
!= B_OK
)
309 #if DISABLE_CHUNK_CACHE
310 return fReader
->GetNextChunk(fStreamInfo
[stream
].cookie
, _chunkBuffer
,
311 _chunkSize
, mediaHeader
);
313 BAutolock
_(info
.chunkCache
);
315 _RecycleLastChunk(info
);
317 // Retrieve next chunk - read it directly, if the cache is drained
318 chunk_buffer
* chunk
= info
.chunkCache
->NextChunk(fReader
, info
.cookie
);
323 info
.lastChunk
= chunk
;
325 *_chunkBuffer
= chunk
->buffer
;
326 *_chunkSize
= chunk
->size
;
327 *mediaHeader
= chunk
->header
;
329 return chunk
->status
;
335 MediaExtractor::CreateDecoder(int32 stream
, Decoder
** _decoder
,
336 media_codec_info
* codecInfo
)
340 status_t status
= fStreamInfo
[stream
].status
;
341 if (status
!= B_OK
) {
342 ERROR("MediaExtractor::CreateDecoder can't create decoder for "
343 "stream %" B_PRId32
": %s\n", stream
, strerror(status
));
347 // TODO: Here we should work out a way so that if there is a setup
348 // failure we can try the next decoder
350 status
= gPluginManager
.CreateDecoder(&decoder
,
351 fStreamInfo
[stream
].encodedFormat
);
352 if (status
!= B_OK
) {
354 char formatString
[256];
355 string_for_format(fStreamInfo
[stream
].encodedFormat
, formatString
,
356 sizeof(formatString
));
358 ERROR("MediaExtractor::CreateDecoder gPluginManager.CreateDecoder "
359 "failed for stream %" B_PRId32
", format: %s: %s\n", stream
,
360 formatString
, strerror(status
));
365 ChunkProvider
* chunkProvider
366 = new(std::nothrow
) MediaExtractorChunkProvider(this, stream
);
367 if (chunkProvider
== NULL
) {
368 gPluginManager
.DestroyDecoder(decoder
);
369 ERROR("MediaExtractor::CreateDecoder can't create chunk provider "
370 "for stream %" B_PRId32
"\n", stream
);
374 decoder
->SetChunkProvider(chunkProvider
);
376 status
= decoder
->Setup(&fStreamInfo
[stream
].encodedFormat
,
377 fStreamInfo
[stream
].infoBuffer
, fStreamInfo
[stream
].infoBufferSize
);
378 if (status
!= B_OK
) {
379 gPluginManager
.DestroyDecoder(decoder
);
380 ERROR("MediaExtractor::CreateDecoder Setup failed for stream %" B_PRId32
381 ": %s\n", stream
, strerror(status
));
385 status
= gPluginManager
.GetDecoderInfo(decoder
, codecInfo
);
386 if (status
!= B_OK
) {
387 gPluginManager
.DestroyDecoder(decoder
);
388 ERROR("MediaExtractor::CreateDecoder GetCodecInfo failed for stream %"
389 B_PRId32
": %s\n", stream
, strerror(status
));
399 MediaExtractor::GetStreamMetaData(int32 stream
, BMessage
* _data
) const
401 const stream_info
& info
= fStreamInfo
[stream
];
403 if (info
.status
!= B_OK
)
406 return fReader
->GetStreamMetaData(fStreamInfo
[stream
].cookie
, _data
);
411 MediaExtractor::StopProcessing()
413 #if !DISABLE_CHUNK_CACHE
414 if (fExtractorWaitSem
> -1) {
415 // terminate extractor thread
416 delete_sem(fExtractorWaitSem
);
417 fExtractorWaitSem
= -1;
420 wait_for_thread(fExtractorThread
, &status
);
427 MediaExtractor::_RecycleLastChunk(stream_info
& info
)
429 if (info
.lastChunk
!= NULL
) {
430 info
.chunkCache
->RecycleChunk(info
.lastChunk
);
431 info
.lastChunk
= NULL
;
437 MediaExtractor::_ExtractorEntry(void* extractor
)
439 static_cast<MediaExtractor
*>(extractor
)->_ExtractorThread();
445 MediaExtractor::_ExtractorThread()
450 status
= acquire_sem(fExtractorWaitSem
);
451 } while (status
== B_INTERRUPTED
);
453 if (status
!= B_OK
) {
454 // we were asked to quit
458 // Iterate over all streams until they are all filled
464 for (int32 stream
= 0; stream
< fStreamCount
; stream
++) {
465 stream_info
& info
= fStreamInfo
[stream
];
466 if (info
.status
!= B_OK
) {
471 BAutolock
_(info
.chunkCache
);
473 if (!info
.chunkCache
->SpaceLeft()
474 || !info
.chunkCache
->ReadNextChunk(fReader
, info
.cookie
))
477 } while (streamsFilled
< fStreamCount
);