* Completely rewrote the ChunkCache - the previous version had some issues with
regards to locking and seeking. * Furthermore, we now not only cache 4 chunks, but chunk up to a certain memory size (MediaExtractor uses 1 MB for now). * Since I still have occasional hickups, it looks like this wasn't the main cause for our audio problems. Still, this will reduce drive access considerably during play. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34243 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f03034d7c9
commit
4d89dfc712
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Marcus Overhagen. All rights reserved.
|
||||
* Copyright 2008, Maurice Kalinowski. All rights reserved.
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _MEDIA_EXTRACTOR_H
|
||||
@ -14,7 +16,10 @@
|
||||
namespace BPrivate {
|
||||
namespace media {
|
||||
|
||||
|
||||
class ChunkCache;
|
||||
struct chunk_buffer;
|
||||
|
||||
|
||||
struct stream_info {
|
||||
status_t status;
|
||||
@ -23,9 +28,11 @@ struct stream_info {
|
||||
const void* infoBuffer;
|
||||
size_t infoBufferSize;
|
||||
ChunkCache* chunkCache;
|
||||
chunk_buffer* lastChunk;
|
||||
media_format encodedFormat;
|
||||
};
|
||||
|
||||
|
||||
class MediaExtractor {
|
||||
public:
|
||||
MediaExtractor(BDataIO* source, int32 flags);
|
||||
@ -58,6 +65,7 @@ public:
|
||||
media_codec_info* codecInfo);
|
||||
|
||||
private:
|
||||
void _RecycleLastChunk(stream_info& info);
|
||||
static int32 _ExtractorEntry(void* arg);
|
||||
void _ExtractorThread();
|
||||
|
||||
@ -66,7 +74,6 @@ private:
|
||||
|
||||
sem_id fExtractorWaitSem;
|
||||
thread_id fExtractorThread;
|
||||
volatile bool fTerminateExtractor;
|
||||
|
||||
BDataIO* fSource;
|
||||
Reader* fReader;
|
||||
|
@ -1,143 +1,144 @@
|
||||
/*
|
||||
* Copyright 2004, Marcus Overhagen. All rights reserved.
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "ChunkCache.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include <Debug.h>
|
||||
|
||||
|
||||
ChunkCache::ChunkCache()
|
||||
chunk_buffer::chunk_buffer()
|
||||
:
|
||||
fLocker("media chunk cache locker")
|
||||
buffer(NULL),
|
||||
size(0),
|
||||
capacity(0)
|
||||
{
|
||||
// fEmptyChunkCount must be one less than the real chunk count,
|
||||
// because the buffer returned by GetNextChunk must be preserved
|
||||
// until the next call of that function, and must not be overwritten.
|
||||
}
|
||||
|
||||
fEmptyChunkCount = CHUNK_COUNT - 1;
|
||||
fReadyChunkCount = 0;
|
||||
fNeedsRefill = 1;
|
||||
|
||||
fGetWaitSem = create_sem(0, "media chunk cache sem");
|
||||
chunk_buffer::~chunk_buffer()
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
fNextPut = &fChunkInfos[0];
|
||||
fNextGet = &fChunkInfos[0];
|
||||
|
||||
for (int i = 0; i < CHUNK_COUNT; i++) {
|
||||
fChunkInfos[i].next = i == CHUNK_COUNT - 1
|
||||
? &fChunkInfos[0] : &fChunkInfos[i + 1];
|
||||
fChunkInfos[i].buffer = NULL;
|
||||
fChunkInfos[i].sizeUsed = 0;
|
||||
fChunkInfos[i].sizeMax = 0;
|
||||
fChunkInfos[i].status = B_ERROR;
|
||||
}
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
ChunkCache::ChunkCache(sem_id waitSem, size_t maxBytes)
|
||||
:
|
||||
BLocker("media chunk cache"),
|
||||
fWaitSem(waitSem),
|
||||
fMaxBytes(maxBytes),
|
||||
fBytes(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ChunkCache::~ChunkCache()
|
||||
{
|
||||
delete_sem(fGetWaitSem);
|
||||
while (chunk_buffer* chunk = fChunks.RemoveHead())
|
||||
delete chunk;
|
||||
|
||||
for (int i = 0; i < CHUNK_COUNT; i++) {
|
||||
free(fChunkInfos[i].buffer);
|
||||
}
|
||||
while (chunk_buffer* chunk = fUnusedChunks.RemoveHead())
|
||||
delete chunk;
|
||||
|
||||
while (chunk_buffer* chunk = fInFlightChunks.RemoveHead())
|
||||
delete chunk;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ChunkCache::MakeEmpty()
|
||||
{
|
||||
BAutolock _(fLocker);
|
||||
ASSERT(IsLocked());
|
||||
|
||||
fEmptyChunkCount = CHUNK_COUNT - 1;
|
||||
fReadyChunkCount = 0;
|
||||
fNextPut = &fChunkInfos[0];
|
||||
fNextGet = &fChunkInfos[0];
|
||||
atomic_or(&fNeedsRefill, 1);
|
||||
fUnusedChunks.MoveFrom(&fChunks);
|
||||
fBytes = 0;
|
||||
|
||||
release_sem(fWaitSem);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ChunkCache::NeedsRefill()
|
||||
ChunkCache::SpaceLeft() const
|
||||
{
|
||||
return atomic_or(&fNeedsRefill, 0);
|
||||
ASSERT(IsLocked());
|
||||
|
||||
return fBytes < fMaxBytes;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ChunkCache::GetNextChunk(const void** _chunkBuffer, size_t* _chunkSize,
|
||||
media_header* mediaHeader)
|
||||
chunk_buffer*
|
||||
ChunkCache::NextChunk()
|
||||
{
|
||||
uint8 retryCount = 0;
|
||||
ASSERT(IsLocked());
|
||||
|
||||
// printf("ChunkCache::GetNextChunk: %p fEmptyChunkCount %ld, fReadyChunkCount %ld\n", fNextGet, fEmptyChunkCount, fReadyChunkCount);
|
||||
retry:
|
||||
acquire_sem(fGetWaitSem);
|
||||
|
||||
BAutolock locker(fLocker);
|
||||
if (fReadyChunkCount == 0) {
|
||||
locker.Unlock();
|
||||
|
||||
printf("ChunkCache::GetNextChunk: %p retrying\n", fNextGet);
|
||||
// Limit to 5 retries
|
||||
retryCount++;
|
||||
if (retryCount > 4)
|
||||
return B_ERROR;
|
||||
|
||||
goto retry;
|
||||
chunk_buffer* chunk = fChunks.RemoveHead();
|
||||
if (chunk != NULL) {
|
||||
fBytes -= chunk->capacity;
|
||||
fInFlightChunks.Add(chunk);
|
||||
release_sem(fWaitSem);
|
||||
}
|
||||
|
||||
fEmptyChunkCount++;
|
||||
fReadyChunkCount--;
|
||||
atomic_or(&fNeedsRefill, 1);
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
*_chunkBuffer = fNextGet->buffer;
|
||||
*_chunkSize = fNextGet->sizeUsed;
|
||||
*mediaHeader = fNextGet->mediaHeader;
|
||||
status_t status = fNextGet->status;
|
||||
fNextGet = fNextGet->next;
|
||||
|
||||
return status;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
||||
/*! Moves the specified chunk from the in-flight list to the unused list.
|
||||
This means the chunk data can be overwritten again.
|
||||
*/
|
||||
void
|
||||
ChunkCache::PutNextChunk(const void* chunkBuffer, size_t chunkSize,
|
||||
const media_header& mediaHeader, status_t status)
|
||||
ChunkCache::RecycleChunk(chunk_buffer* chunk)
|
||||
{
|
||||
// printf("ChunkCache::PutNextChunk: %p fEmptyChunkCount %ld, fReadyChunkCount %ld\n", fNextPut, fEmptyChunkCount, fReadyChunkCount);
|
||||
ASSERT(IsLocked());
|
||||
|
||||
fInFlightChunks.Remove(chunk);
|
||||
fUnusedChunks.Add(chunk);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ChunkCache::ReadNextChunk(Reader* reader, void* cookie)
|
||||
{
|
||||
ASSERT(IsLocked());
|
||||
|
||||
// retrieve chunk buffer
|
||||
chunk_buffer* chunk = fUnusedChunks.RemoveHead();
|
||||
if (chunk == NULL) {
|
||||
// allocate a new one
|
||||
chunk = new(std::nothrow) chunk_buffer;
|
||||
if (chunk == NULL)
|
||||
return false;
|
||||
|
||||
if (status == B_OK) {
|
||||
if (fNextPut->sizeMax < chunkSize) {
|
||||
// printf("ChunkCache::PutNextChunk: %p resizing from %ld to %ld\n", fNextPut, fNextPut->sizeMax, chunkSize);
|
||||
free(fNextPut->buffer);
|
||||
fNextPut->buffer = malloc((chunkSize + 1024) & ~1023);
|
||||
fNextPut->sizeMax = chunkSize;
|
||||
}
|
||||
memcpy(fNextPut->buffer, chunkBuffer, chunkSize);
|
||||
fNextPut->sizeUsed = chunkSize;
|
||||
}
|
||||
|
||||
fNextPut->mediaHeader = mediaHeader;
|
||||
fNextPut->status = status;
|
||||
const void* buffer;
|
||||
size_t bufferSize;
|
||||
chunk->status = reader->GetNextChunk(cookie, &buffer, &bufferSize,
|
||||
&chunk->header);
|
||||
if (chunk->status == B_OK) {
|
||||
if (chunk->capacity < bufferSize) {
|
||||
// adapt buffer size
|
||||
free(chunk->buffer);
|
||||
chunk->capacity = (bufferSize + 2047) & ~2047;
|
||||
chunk->buffer = malloc(chunk->capacity);
|
||||
if (chunk->buffer == NULL) {
|
||||
delete chunk;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fNextPut = fNextPut->next;
|
||||
memcpy(chunk->buffer, buffer, bufferSize);
|
||||
chunk->size = bufferSize;
|
||||
fBytes += chunk->capacity;
|
||||
}
|
||||
|
||||
fLocker.Lock();
|
||||
fEmptyChunkCount--;
|
||||
fReadyChunkCount++;
|
||||
if (fEmptyChunkCount == 0)
|
||||
atomic_and(&fNeedsRefill, 0);
|
||||
fLocker.Unlock();
|
||||
|
||||
release_sem(fGetWaitSem);
|
||||
fChunks.Add(chunk);
|
||||
return chunk->status == B_OK;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004, Marcus Overhagen. All rights reserved.
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _CHUNK_CACHE_H
|
||||
@ -8,56 +8,54 @@
|
||||
|
||||
#include <Locker.h>
|
||||
#include <MediaDefs.h>
|
||||
#include "ReaderPlugin.h"
|
||||
|
||||
#include <kernel/util/DoublyLinkedList.h>
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
namespace media {
|
||||
|
||||
|
||||
struct chunk_info {
|
||||
chunk_info* next;
|
||||
struct chunk_buffer;
|
||||
typedef DoublyLinkedList<chunk_buffer> ChunkList;
|
||||
|
||||
struct chunk_buffer : public DoublyLinkedListLinkImpl<chunk_buffer> {
|
||||
chunk_buffer();
|
||||
~chunk_buffer();
|
||||
|
||||
void* buffer;
|
||||
size_t sizeUsed;
|
||||
size_t sizeMax;
|
||||
media_header mediaHeader;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
media_header header;
|
||||
status_t status;
|
||||
};
|
||||
|
||||
|
||||
class ChunkCache {
|
||||
class ChunkCache : public BLocker {
|
||||
public:
|
||||
ChunkCache();
|
||||
ChunkCache(sem_id waitSem, size_t maxBytes);
|
||||
~ChunkCache();
|
||||
|
||||
void MakeEmpty();
|
||||
bool NeedsRefill();
|
||||
bool SpaceLeft() const;
|
||||
|
||||
status_t GetNextChunk(const void** _chunkBuffer,
|
||||
size_t* _chunkSize,
|
||||
media_header* mediaHeader);
|
||||
void PutNextChunk(const void* chunkBuffer,
|
||||
size_t chunkSize,
|
||||
const media_header& mediaHeader,
|
||||
status_t status);
|
||||
chunk_buffer* NextChunk();
|
||||
void RecycleChunk(chunk_buffer* chunk);
|
||||
bool ReadNextChunk(Reader* reader, void* cookie);
|
||||
|
||||
private:
|
||||
enum { CHUNK_COUNT = 5 };
|
||||
|
||||
chunk_info* fNextPut;
|
||||
chunk_info* fNextGet;
|
||||
chunk_info fChunkInfos[CHUNK_COUNT];
|
||||
|
||||
sem_id fGetWaitSem;
|
||||
int32 fEmptyChunkCount;
|
||||
int32 fReadyChunkCount;
|
||||
int32 fNeedsRefill;
|
||||
|
||||
BLocker fLocker;
|
||||
sem_id fWaitSem;
|
||||
size_t fMaxBytes;
|
||||
size_t fBytes;
|
||||
ChunkList fChunks;
|
||||
ChunkList fUnusedChunks;
|
||||
ChunkList fInFlightChunks;
|
||||
};
|
||||
|
||||
|
||||
} // namespace media
|
||||
} // namespace BPrivate
|
||||
} // namespace media
|
||||
} // namespace BPrivate
|
||||
|
||||
using namespace BPrivate::media;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Marcus Overhagen. All rights reserved.
|
||||
* Copyright 2008, Maurice Kalinowski. All rights reserved.
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -13,8 +15,8 @@
|
||||
|
||||
#include <Autolock.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "ChunkCache.h"
|
||||
#include "debug.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
|
||||
@ -22,6 +24,9 @@
|
||||
#define DISABLE_CHUNK_CACHE 0
|
||||
|
||||
|
||||
static const size_t kMaxCacheBytes = 1024 * 1024;
|
||||
|
||||
|
||||
class MediaExtractorChunkProvider : public ChunkProvider {
|
||||
public:
|
||||
MediaExtractorChunkProvider(MediaExtractor* extractor, int32 stream)
|
||||
@ -48,21 +53,28 @@ private:
|
||||
|
||||
|
||||
MediaExtractor::MediaExtractor(BDataIO* source, int32 flags)
|
||||
:
|
||||
fExtractorThread(-1),
|
||||
fSource(source),
|
||||
fReader(NULL),
|
||||
fStreamInfo(NULL),
|
||||
fStreamCount(0)
|
||||
{
|
||||
CALLED();
|
||||
fSource = source;
|
||||
fStreamInfo = NULL;
|
||||
fExtractorThread = -1;
|
||||
fExtractorWaitSem = -1;
|
||||
fTerminateExtractor = false;
|
||||
|
||||
#if !DISABLE_CHUNK_CACHE
|
||||
// start extractor thread
|
||||
fExtractorWaitSem = create_sem(1, "media extractor thread sem");
|
||||
if (fExtractorWaitSem < 0) {
|
||||
fInitStatus = fExtractorWaitSem;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
fInitStatus = _plugin_manager.CreateReader(&fReader, &fStreamCount,
|
||||
&fFileFormat, source);
|
||||
if (fInitStatus != B_OK) {
|
||||
fStreamCount = 0;
|
||||
fReader = NULL;
|
||||
if (fInitStatus != B_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
fStreamInfo = new stream_info[fStreamCount];
|
||||
|
||||
@ -73,7 +85,8 @@ MediaExtractor::MediaExtractor(BDataIO* source, int32 flags)
|
||||
fStreamInfo[i].hasCookie = true;
|
||||
fStreamInfo[i].infoBuffer = 0;
|
||||
fStreamInfo[i].infoBufferSize = 0;
|
||||
fStreamInfo[i].chunkCache = new ChunkCache;
|
||||
fStreamInfo[i].chunkCache
|
||||
= new ChunkCache(fExtractorWaitSem, kMaxCacheBytes);
|
||||
memset(&fStreamInfo[i].encodedFormat, 0,
|
||||
sizeof(fStreamInfo[i].encodedFormat));
|
||||
}
|
||||
@ -106,11 +119,10 @@ MediaExtractor::MediaExtractor(BDataIO* source, int32 flags)
|
||||
}
|
||||
}
|
||||
|
||||
#if DISABLE_CHUNK_CACHE == 0
|
||||
#if !DISABLE_CHUNK_CACHE
|
||||
// start extractor thread
|
||||
fExtractorWaitSem = create_sem(1, "media extractor thread sem");
|
||||
fExtractorThread = spawn_thread(_ExtractorEntry, "media extractor thread",
|
||||
40, this);
|
||||
B_NORMAL_PRIORITY + 4, this);
|
||||
resume_thread(fExtractorThread);
|
||||
#endif
|
||||
}
|
||||
@ -120,24 +132,26 @@ MediaExtractor::~MediaExtractor()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
#if !DISABLE_CHUNK_CACHE
|
||||
// terminate extractor thread
|
||||
fTerminateExtractor = true;
|
||||
release_sem(fExtractorWaitSem);
|
||||
status_t err;
|
||||
wait_for_thread(fExtractorThread, &err);
|
||||
delete_sem(fExtractorWaitSem);
|
||||
|
||||
status_t status;
|
||||
wait_for_thread(fExtractorThread, &status);
|
||||
#endif
|
||||
|
||||
// free all stream cookies
|
||||
// and chunk caches
|
||||
for (int32 i = 0; i < fStreamCount; i++) {
|
||||
if (fStreamInfo[i].hasCookie)
|
||||
fReader->FreeCookie(fStreamInfo[i].cookie);
|
||||
|
||||
delete fStreamInfo[i].chunkCache;
|
||||
}
|
||||
|
||||
_plugin_manager.DestroyReader(fReader);
|
||||
|
||||
delete [] fStreamInfo;
|
||||
delete[] fStreamInfo;
|
||||
// fSource is owned by the BMediaFile
|
||||
}
|
||||
|
||||
@ -211,7 +225,7 @@ MediaExtractor::Duration(int32 stream) const
|
||||
int64 frameCount;
|
||||
bigtime_t duration;
|
||||
media_format format;
|
||||
const void *infoBuffer;
|
||||
const void* infoBuffer;
|
||||
size_t infoSize;
|
||||
|
||||
fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration,
|
||||
@ -222,20 +236,23 @@ MediaExtractor::Duration(int32 stream) const
|
||||
|
||||
|
||||
status_t
|
||||
MediaExtractor::Seek(int32 stream, uint32 seekTo, int64* frame, bigtime_t* time)
|
||||
MediaExtractor::Seek(int32 stream, uint32 seekTo, int64* _frame,
|
||||
bigtime_t* _time)
|
||||
{
|
||||
CALLED();
|
||||
if (fStreamInfo[stream].status != B_OK)
|
||||
return fStreamInfo[stream].status;
|
||||
|
||||
status_t result;
|
||||
result = fReader->Seek(fStreamInfo[stream].cookie, seekTo, frame, time);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
stream_info& info = fStreamInfo[stream];
|
||||
if (info.status != B_OK)
|
||||
return info.status;
|
||||
|
||||
// clear buffered chunks
|
||||
fStreamInfo[stream].chunkCache->MakeEmpty();
|
||||
release_sem(fExtractorWaitSem);
|
||||
BAutolock _(info.chunkCache);
|
||||
|
||||
status_t status = fReader->Seek(info.cookie, seekTo, _frame, _time);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// clear buffered chunks after seek
|
||||
info.chunkCache->MakeEmpty();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -246,11 +263,21 @@ MediaExtractor::FindKeyFrame(int32 stream, uint32 seekTo, int64* _frame,
|
||||
bigtime_t* _time) const
|
||||
{
|
||||
CALLED();
|
||||
if (fStreamInfo[stream].status != B_OK)
|
||||
return fStreamInfo[stream].status;
|
||||
|
||||
return fReader->FindKeyFrame(fStreamInfo[stream].cookie,
|
||||
seekTo, _frame, _time);
|
||||
stream_info& info = fStreamInfo[stream];
|
||||
if (info.status != B_OK)
|
||||
return info.status;
|
||||
|
||||
BAutolock _(info.chunkCache);
|
||||
|
||||
status_t status = fReader->FindKeyFrame(info.cookie, seekTo, _frame, _time);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// clear buffered chunks after seek
|
||||
info.chunkCache->MakeEmpty();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -258,20 +285,41 @@ status_t
|
||||
MediaExtractor::GetNextChunk(int32 stream, const void** _chunkBuffer,
|
||||
size_t* _chunkSize, media_header* mediaHeader)
|
||||
{
|
||||
if (fStreamInfo[stream].status != B_OK)
|
||||
return fStreamInfo[stream].status;
|
||||
stream_info& info = fStreamInfo[stream];
|
||||
|
||||
#if DISABLE_CHUNK_CACHE > 0
|
||||
if (info.status != B_OK)
|
||||
return info.status;
|
||||
|
||||
#if DISABLE_CHUNK_CACHE
|
||||
static BLocker locker("media extractor next chunk");
|
||||
BAutolock lock(locker);
|
||||
return fReader->GetNextChunk(fStreamInfo[stream].cookie, _chunkBuffer,
|
||||
_chunkSize, mediaHeader);
|
||||
#endif
|
||||
#else
|
||||
BAutolock _(info.chunkCache);
|
||||
|
||||
status_t status = fStreamInfo[stream].chunkCache->GetNextChunk(_chunkBuffer,
|
||||
_chunkSize, mediaHeader);
|
||||
release_sem(fExtractorWaitSem);
|
||||
return status;
|
||||
_RecycleLastChunk(info);
|
||||
|
||||
// Retrieve next chunk - read it directly, if the cache is drained
|
||||
chunk_buffer* chunk;
|
||||
do {
|
||||
chunk = info.chunkCache->NextChunk();
|
||||
if (chunk == NULL
|
||||
&& !info.chunkCache->ReadNextChunk(fReader, info.cookie))
|
||||
break;
|
||||
} while (chunk == NULL);
|
||||
|
||||
if (chunk == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
info.lastChunk = chunk;
|
||||
|
||||
*_chunkBuffer = chunk->buffer;
|
||||
*_chunkSize = chunk->size;
|
||||
*mediaHeader = chunk->header;
|
||||
|
||||
return chunk->status;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -339,6 +387,16 @@ MediaExtractor::CreateDecoder(int32 stream, Decoder** _decoder,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MediaExtractor::_RecycleLastChunk(stream_info& info)
|
||||
{
|
||||
if (info.lastChunk != NULL) {
|
||||
info.chunkCache->RecycleChunk(info.lastChunk);
|
||||
info.lastChunk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MediaExtractor::_ExtractorEntry(void* extractor)
|
||||
{
|
||||
@ -351,31 +409,35 @@ void
|
||||
MediaExtractor::_ExtractorThread()
|
||||
{
|
||||
while (true) {
|
||||
acquire_sem(fExtractorWaitSem);
|
||||
if (fTerminateExtractor)
|
||||
return;
|
||||
|
||||
bool refillDone;
|
||||
status_t status;
|
||||
do {
|
||||
refillDone = false;
|
||||
for (int32 stream = 0; stream < fStreamCount; stream++) {
|
||||
if (fStreamInfo[stream].status != B_OK)
|
||||
continue;
|
||||
status = acquire_sem(fExtractorWaitSem);
|
||||
} while (status == B_INTERRUPTED);
|
||||
|
||||
if (fStreamInfo[stream].chunkCache->NeedsRefill()) {
|
||||
media_header mediaHeader;
|
||||
const void* chunkBuffer;
|
||||
size_t chunkSize;
|
||||
status_t status = fReader->GetNextChunk(
|
||||
fStreamInfo[stream].cookie, &chunkBuffer, &chunkSize,
|
||||
&mediaHeader);
|
||||
fStreamInfo[stream].chunkCache->PutNextChunk(chunkBuffer,
|
||||
chunkSize, mediaHeader, status);
|
||||
refillDone = true;
|
||||
if (status != B_OK) {
|
||||
// we were asked to quit
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all streams until they are all filled
|
||||
|
||||
int32 streamsFilled;
|
||||
do {
|
||||
streamsFilled = 0;
|
||||
|
||||
for (int32 stream = 0; stream < fStreamCount; stream++) {
|
||||
stream_info& info = fStreamInfo[stream];
|
||||
if (info.status != B_OK) {
|
||||
streamsFilled++;
|
||||
continue;
|
||||
}
|
||||
|
||||
BAutolock _(info.chunkCache);
|
||||
|
||||
if (!info.chunkCache->SpaceLeft()
|
||||
|| !info.chunkCache->ReadNextChunk(fReader, info.cookie))
|
||||
streamsFilled++;
|
||||
}
|
||||
if (fTerminateExtractor)
|
||||
return;
|
||||
} while (refillDone);
|
||||
} while (streamsFilled < fStreamCount);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user