[haiku-commits] r34243 - in haiku/trunk: headers/private/media src/kits/media

  • From: axeld@xxxxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 25 Nov 2009 15:24:52 +0100 (CET)

Author: axeld
Date: 2009-11-25 15:24:52 +0100 (Wed, 25 Nov 2009)
New Revision: 34243
Changeset: http://dev.haiku-os.org/changeset/34243/haiku

Modified:
   haiku/trunk/headers/private/media/MediaExtractor.h
   haiku/trunk/src/kits/media/ChunkCache.cpp
   haiku/trunk/src/kits/media/ChunkCache.h
   haiku/trunk/src/kits/media/MediaExtractor.cpp
Log:
* 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.


Modified: haiku/trunk/headers/private/media/MediaExtractor.h
===================================================================
--- haiku/trunk/headers/private/media/MediaExtractor.h  2009-11-25 12:40:27 UTC 
(rev 34242)
+++ haiku/trunk/headers/private/media/MediaExtractor.h  2009-11-25 14:24:52 UTC 
(rev 34243)
@@ -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@xxxxxxxxxxxxxxxxx
+ *
  * Distributed under the terms of the MIT License.
  */
 #ifndef _MEDIA_EXTRACTOR_H
@@ -14,8 +16,11 @@
 namespace BPrivate {
 namespace media {
 
+
 class ChunkCache;
+struct chunk_buffer;
 
+
 struct stream_info {
        status_t                status;
        void*                   cookie;
@@ -23,9 +28,11 @@
        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 @@
                                                                        
media_codec_info* codecInfo);
 
 private:
+                       void                            
_RecycleLastChunk(stream_info& info);
        static  int32                           _ExtractorEntry(void* arg);
                        void                            _ExtractorThread();
 
@@ -66,7 +74,6 @@
 
                        sem_id                          fExtractorWaitSem;
                        thread_id                       fExtractorThread;
-                       volatile bool           fTerminateExtractor;
 
                        BDataIO*                        fSource;
                        Reader*                         fReader;

Modified: haiku/trunk/src/kits/media/ChunkCache.cpp
===================================================================
--- haiku/trunk/src/kits/media/ChunkCache.cpp   2009-11-25 12:40:27 UTC (rev 
34242)
+++ haiku/trunk/src/kits/media/ChunkCache.cpp   2009-11-25 14:24:52 UTC (rev 
34243)
@@ -1,143 +1,144 @@
 /*
- * Copyright 2004, Marcus Overhagen. All rights reserved.
+ * Copyright 2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx
  * 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);
+       return chunk;
+}
 
-       locker.Unlock();
 
-       *_chunkBuffer = fNextGet->buffer;
-       *_chunkSize = fNextGet->sizeUsed;
-       *mediaHeader = fNextGet->mediaHeader;
-       status_t status = fNextGet->status;
-       fNextGet = fNextGet->next;
+/*!    Moves the specified chunk from the in-flight list to the unused list.
+       This means the chunk data can be overwritten again.
+*/
+void
+ChunkCache::RecycleChunk(chunk_buffer* chunk)
+{
+       ASSERT(IsLocked());
 
-       return status;
+       fInFlightChunks.Remove(chunk);
+       fUnusedChunks.Add(chunk);
 }
 
 
-void
-ChunkCache::PutNextChunk(const void* chunkBuffer, size_t chunkSize,
-       const media_header& mediaHeader, status_t status)
+bool
+ChunkCache::ReadNextChunk(Reader* reader, void* cookie)
 {
-//     printf("ChunkCache::PutNextChunk: %p fEmptyChunkCount %ld, 
fReadyChunkCount %ld\n", fNextPut, fEmptyChunkCount, fReadyChunkCount);
+       ASSERT(IsLocked());
 
-       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;
+       // 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;
+
        }
 
-       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;
 }

Modified: haiku/trunk/src/kits/media/ChunkCache.h
===================================================================
--- haiku/trunk/src/kits/media/ChunkCache.h     2009-11-25 12:40:27 UTC (rev 
34242)
+++ haiku/trunk/src/kits/media/ChunkCache.h     2009-11-25 14:24:52 UTC (rev 
34243)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004, Marcus Overhagen. All rights reserved.
+ * Copyright 2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx
  * 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;
 

Modified: haiku/trunk/src/kits/media/MediaExtractor.cpp
===================================================================
--- haiku/trunk/src/kits/media/MediaExtractor.cpp       2009-11-25 12:40:27 UTC 
(rev 34242)
+++ haiku/trunk/src/kits/media/MediaExtractor.cpp       2009-11-25 14:24:52 UTC 
(rev 34243)
@@ -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@xxxxxxxxxxxxxxxxx
+ *
  * Distributed under the terms of the MIT License.
  */
 
@@ -13,8 +15,8 @@
 
 #include <Autolock.h>
 
+#include "ChunkCache.h"
 #include "debug.h"
-#include "ChunkCache.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 @@
 
 
 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 @@
                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 @@
                }
        }
 
-#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 @@
 {
        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 @@
        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,21 +236,24 @@
 
 
 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 @@
        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 @@
 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);
+#else
+       BAutolock _(info.chunkCache);
+
+       _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
-
-       status_t status = 
fStreamInfo[stream].chunkCache->GetNextChunk(_chunkBuffer,
-               _chunkSize, mediaHeader);
-       release_sem(fExtractorWaitSem);
-       return status;
 }
 
 
@@ -339,6 +387,16 @@
 }
 
 
+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 @@
 MediaExtractor::_ExtractorThread()
 {
        while (true) {
-               acquire_sem(fExtractorWaitSem);
-               if (fTerminateExtractor)
+               status_t status;
+               do {
+                       status = acquire_sem(fExtractorWaitSem);
+               } while (status == B_INTERRUPTED);
+
+               if (status != B_OK) {
+                       // we were asked to quit
                        return;
+               }
 
-               bool refillDone;
+               // Iterate over all streams until they are all filled
+
+               int32 streamsFilled;
                do {
-                       refillDone = false;
+                       streamsFilled = 0;
+
                        for (int32 stream = 0; stream < fStreamCount; stream++) 
{
-                               if (fStreamInfo[stream].status != B_OK)
+                               stream_info& info = fStreamInfo[stream];
+                               if (info.status != B_OK) {
+                                       streamsFilled++;
                                        continue;
+                               }
 
-                               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;
-                               }
+                               BAutolock _(info.chunkCache);
+
+                               if (!info.chunkCache->SpaceLeft()
+                                       || 
!info.chunkCache->ReadNextChunk(fReader, info.cookie))
+                                       streamsFilled++;
                        }
-                       if (fTerminateExtractor)
-                               return;
-               } while (refillDone);
+               } while (streamsFilled < fStreamCount);
        }
 }


Other related posts:

  • » [haiku-commits] r34243 - in haiku/trunk: headers/private/media src/kits/media - axeld