tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / media / MediaExtractor.cpp
blobfdf7aebf5c4d2823cb745e345e1aafa30b81add4
1 /*
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.
7 */
10 #include "MediaExtractor.h"
12 #include <new>
13 #include <stdio.h>
14 #include <string.h>
16 #include <Autolock.h>
18 #include "ChunkCache.h"
19 #include "debug.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 {
31 public:
32 MediaExtractorChunkProvider(MediaExtractor* extractor, int32 stream)
34 fExtractor(extractor),
35 fStream(stream)
39 virtual status_t GetNextChunk(const void** _chunkBuffer, size_t* _chunkSize,
40 media_header *mediaHeader)
42 return fExtractor->GetNextChunk(fStream, _chunkBuffer, _chunkSize,
43 mediaHeader);
46 private:
47 MediaExtractor* fExtractor;
48 int32 fStream;
52 // #pragma mark -
55 MediaExtractor::MediaExtractor(BDataIO* source, int32 flags)
57 fExtractorThread(-1),
58 fSource(source),
59 fReader(NULL),
60 fStreamInfo(NULL),
61 fStreamCount(0)
63 CALLED();
65 #if !DISABLE_CHUNK_CACHE
66 // start extractor thread
67 fExtractorWaitSem = create_sem(1, "media extractor thread sem");
68 if (fExtractorWaitSem < 0) {
69 fInitStatus = fExtractorWaitSem;
70 return;
72 #endif
74 fInitStatus = gPluginManager.CreateReader(&fReader, &fStreamCount,
75 &fFileFormat, source);
76 if (fInitStatus != B_OK)
77 return;
79 fStreamInfo = new stream_info[fStreamCount];
81 // initialize stream infos
82 for (int32 i = 0; i < fStreamCount; i++) {
83 fStreamInfo[i].status = B_OK;
84 fStreamInfo[i].cookie = 0;
85 fStreamInfo[i].hasCookie = false;
86 fStreamInfo[i].infoBuffer = 0;
87 fStreamInfo[i].infoBufferSize = 0;
88 fStreamInfo[i].chunkCache
89 = new ChunkCache(fExtractorWaitSem, kMaxCacheBytes);
90 fStreamInfo[i].lastChunk = NULL;
91 memset(&fStreamInfo[i].encodedFormat, 0,
92 sizeof(fStreamInfo[i].encodedFormat));
94 if (fStreamInfo[i].chunkCache->InitCheck() != B_OK) {
95 fInitStatus = B_NO_MEMORY;
96 return;
100 // create all stream cookies
101 for (int32 i = 0; i < fStreamCount; i++) {
102 if (fReader->AllocateCookie(i, &fStreamInfo[i].cookie) != B_OK) {
103 fStreamInfo[i].cookie = 0;
104 fStreamInfo[i].hasCookie = false;
105 fStreamInfo[i].status = B_ERROR;
106 ERROR("MediaExtractor::MediaExtractor: AllocateCookie for stream %"
107 B_PRId32 " failed\n", i);
108 } else
109 fStreamInfo[i].hasCookie = true;
112 // get info for all streams
113 for (int32 i = 0; i < fStreamCount; i++) {
114 if (fStreamInfo[i].status != B_OK)
115 continue;
117 int64 frameCount;
118 bigtime_t duration;
119 if (fReader->GetStreamInfo(fStreamInfo[i].cookie, &frameCount,
120 &duration, &fStreamInfo[i].encodedFormat,
121 &fStreamInfo[i].infoBuffer, &fStreamInfo[i].infoBufferSize)
122 != B_OK) {
123 fStreamInfo[i].status = B_ERROR;
124 ERROR("MediaExtractor::MediaExtractor: GetStreamInfo for "
125 "stream %" B_PRId32 " failed\n", i);
129 #if !DISABLE_CHUNK_CACHE
130 // start extractor thread
131 fExtractorThread = spawn_thread(_ExtractorEntry, "media extractor thread",
132 B_NORMAL_PRIORITY + 4, this);
133 resume_thread(fExtractorThread);
134 #endif
138 MediaExtractor::~MediaExtractor()
140 CALLED();
142 #if !DISABLE_CHUNK_CACHE
143 // terminate extractor thread
144 delete_sem(fExtractorWaitSem);
146 status_t status;
147 wait_for_thread(fExtractorThread, &status);
148 #endif
150 // free all stream cookies
151 // and chunk caches
152 for (int32 i = 0; i < fStreamCount; i++) {
153 if (fStreamInfo[i].hasCookie)
154 fReader->FreeCookie(fStreamInfo[i].cookie);
156 delete fStreamInfo[i].chunkCache;
159 gPluginManager.DestroyReader(fReader);
161 delete[] fStreamInfo;
162 // fSource is owned by the BMediaFile
166 status_t
167 MediaExtractor::InitCheck()
169 CALLED();
170 return fInitStatus;
174 void
175 MediaExtractor::GetFileFormatInfo(media_file_format* fileFormat) const
177 CALLED();
178 *fileFormat = fFileFormat;
182 status_t
183 MediaExtractor::GetMetaData(BMessage* _data) const
185 CALLED();
186 return fReader->GetMetaData(_data);
190 int32
191 MediaExtractor::StreamCount()
193 CALLED();
194 return fStreamCount;
198 const char*
199 MediaExtractor::Copyright()
201 return fReader->Copyright();
205 const media_format*
206 MediaExtractor::EncodedFormat(int32 stream)
208 return &fStreamInfo[stream].encodedFormat;
212 int64
213 MediaExtractor::CountFrames(int32 stream) const
215 CALLED();
216 if (fStreamInfo[stream].status != B_OK)
217 return 0LL;
219 int64 frameCount;
220 bigtime_t duration;
221 media_format format;
222 const void* infoBuffer;
223 size_t infoSize;
225 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration,
226 &format, &infoBuffer, &infoSize);
228 return frameCount;
232 bigtime_t
233 MediaExtractor::Duration(int32 stream) const
235 CALLED();
237 if (fStreamInfo[stream].status != B_OK)
238 return 0LL;
240 int64 frameCount;
241 bigtime_t duration;
242 media_format format;
243 const void* infoBuffer;
244 size_t infoSize;
246 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration,
247 &format, &infoBuffer, &infoSize);
249 return duration;
253 status_t
254 MediaExtractor::Seek(int32 stream, uint32 seekTo, int64* _frame,
255 bigtime_t* _time)
257 CALLED();
259 stream_info& info = fStreamInfo[stream];
260 if (info.status != B_OK)
261 return info.status;
263 BAutolock _(info.chunkCache);
265 status_t status = fReader->Seek(info.cookie, seekTo, _frame, _time);
266 if (status != B_OK)
267 return status;
269 // clear buffered chunks after seek
270 info.chunkCache->MakeEmpty();
272 return B_OK;
276 status_t
277 MediaExtractor::FindKeyFrame(int32 stream, uint32 seekTo, int64* _frame,
278 bigtime_t* _time) const
280 CALLED();
282 stream_info& info = fStreamInfo[stream];
283 if (info.status != B_OK)
284 return info.status;
286 return fReader->FindKeyFrame(info.cookie, seekTo, _frame, _time);
290 status_t
291 MediaExtractor::GetNextChunk(int32 stream, const void** _chunkBuffer,
292 size_t* _chunkSize, media_header* mediaHeader)
294 stream_info& info = fStreamInfo[stream];
296 if (info.status != B_OK)
297 return info.status;
299 #if DISABLE_CHUNK_CACHE
300 return fReader->GetNextChunk(fStreamInfo[stream].cookie, _chunkBuffer,
301 _chunkSize, mediaHeader);
302 #else
303 BAutolock _(info.chunkCache);
305 _RecycleLastChunk(info);
307 // Retrieve next chunk - read it directly, if the cache is drained
308 chunk_buffer* chunk = info.chunkCache->NextChunk(fReader, info.cookie);
310 if (chunk == NULL)
311 return B_NO_MEMORY;
313 info.lastChunk = chunk;
315 *_chunkBuffer = chunk->buffer;
316 *_chunkSize = chunk->size;
317 *mediaHeader = chunk->header;
319 return chunk->status;
320 #endif
324 status_t
325 MediaExtractor::CreateDecoder(int32 stream, Decoder** _decoder,
326 media_codec_info* codecInfo)
328 CALLED();
330 status_t status = fStreamInfo[stream].status;
331 if (status != B_OK) {
332 ERROR("MediaExtractor::CreateDecoder can't create decoder for "
333 "stream %" B_PRId32 ": %s\n", stream, strerror(status));
334 return status;
337 // TODO: Here we should work out a way so that if there is a setup
338 // failure we can try the next decoder
339 Decoder* decoder;
340 status = gPluginManager.CreateDecoder(&decoder,
341 fStreamInfo[stream].encodedFormat);
342 if (status != B_OK) {
343 #if DEBUG
344 char formatString[256];
345 string_for_format(fStreamInfo[stream].encodedFormat, formatString,
346 sizeof(formatString));
348 ERROR("MediaExtractor::CreateDecoder gPluginManager.CreateDecoder "
349 "failed for stream %" B_PRId32 ", format: %s: %s\n", stream,
350 formatString, strerror(status));
351 #endif
352 return status;
355 ChunkProvider* chunkProvider
356 = new(std::nothrow) MediaExtractorChunkProvider(this, stream);
357 if (chunkProvider == NULL) {
358 gPluginManager.DestroyDecoder(decoder);
359 ERROR("MediaExtractor::CreateDecoder can't create chunk provider "
360 "for stream %" B_PRId32 "\n", stream);
361 return B_NO_MEMORY;
364 decoder->SetChunkProvider(chunkProvider);
366 status = decoder->Setup(&fStreamInfo[stream].encodedFormat,
367 fStreamInfo[stream].infoBuffer, fStreamInfo[stream].infoBufferSize);
368 if (status != B_OK) {
369 gPluginManager.DestroyDecoder(decoder);
370 ERROR("MediaExtractor::CreateDecoder Setup failed for stream %" B_PRId32
371 ": %s\n", stream, strerror(status));
372 return status;
375 status = gPluginManager.GetDecoderInfo(decoder, codecInfo);
376 if (status != B_OK) {
377 gPluginManager.DestroyDecoder(decoder);
378 ERROR("MediaExtractor::CreateDecoder GetCodecInfo failed for stream %"
379 B_PRId32 ": %s\n", stream, strerror(status));
380 return status;
383 *_decoder = decoder;
384 return B_OK;
388 status_t
389 MediaExtractor::GetStreamMetaData(int32 stream, BMessage* _data) const
391 const stream_info& info = fStreamInfo[stream];
393 if (info.status != B_OK)
394 return info.status;
396 return fReader->GetStreamMetaData(fStreamInfo[stream].cookie, _data);
400 void
401 MediaExtractor::_RecycleLastChunk(stream_info& info)
403 if (info.lastChunk != NULL) {
404 info.chunkCache->RecycleChunk(info.lastChunk);
405 info.lastChunk = NULL;
410 status_t
411 MediaExtractor::_ExtractorEntry(void* extractor)
413 static_cast<MediaExtractor*>(extractor)->_ExtractorThread();
414 return B_OK;
418 void
419 MediaExtractor::_ExtractorThread()
421 while (true) {
422 status_t status;
423 do {
424 status = acquire_sem(fExtractorWaitSem);
425 } while (status == B_INTERRUPTED);
427 if (status != B_OK) {
428 // we were asked to quit
429 return;
432 // Iterate over all streams until they are all filled
434 int32 streamsFilled;
435 do {
436 streamsFilled = 0;
438 for (int32 stream = 0; stream < fStreamCount; stream++) {
439 stream_info& info = fStreamInfo[stream];
440 if (info.status != B_OK) {
441 streamsFilled++;
442 continue;
445 BAutolock _(info.chunkCache);
447 if (!info.chunkCache->SpaceLeft()
448 || !info.chunkCache->ReadNextChunk(fReader, info.cookie))
449 streamsFilled++;
451 } while (streamsFilled < fStreamCount);