From b8827c305576fa99288e50eff5ab705b7188e2b1 Mon Sep 17 00:00:00 2001 From: shatty Date: Sun, 8 Feb 2004 09:53:22 +0000 Subject: [PATCH] new separation of streams from seekables - first view of ogm video! git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6523 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/ogg/Jamfile | 4 +- .../media/plugins/ogg/OggFrameInfo.cpp | 57 ----- src/add-ons/media/plugins/ogg/OggFrameInfo.h | 26 --- .../media/plugins/ogg/OggReaderPlugin.cpp | 216 ++++++++++-------- .../media/plugins/ogg/OggReaderPlugin.h | 49 ++-- src/add-ons/media/plugins/ogg/OggSeekable.cpp | 174 ++++++++++++++ src/add-ons/media/plugins/ogg/OggSeekable.h | 45 ++++ .../media/plugins/ogg/OggSpeexStream.cpp | 25 +- .../media/plugins/ogg/OggSpeexStream.h | 3 + src/add-ons/media/plugins/ogg/OggStream.cpp | 212 +++-------------- src/add-ons/media/plugins/ogg/OggStream.h | 48 ++-- .../media/plugins/ogg/OggTheoraStream.cpp | 6 +- .../media/plugins/ogg/OggTobiasStream.cpp | 22 +- .../media/plugins/ogg/OggTobiasStream.h | 2 - src/add-ons/media/plugins/ogg/OggTrack.cpp | 95 ++++++++ src/add-ons/media/plugins/ogg/OggTrack.h | 44 ++++ .../media/plugins/ogg/OggVorbisSeekable.cpp | 197 ++++++++++++++++ .../media/plugins/ogg/OggVorbisSeekable.h | 23 ++ .../media/plugins/ogg/OggVorbisStream.cpp | 57 +---- .../media/plugins/ogg/OggVorbisStream.h | 3 - 20 files changed, 821 insertions(+), 487 deletions(-) delete mode 100644 src/add-ons/media/plugins/ogg/OggFrameInfo.cpp delete mode 100644 src/add-ons/media/plugins/ogg/OggFrameInfo.h create mode 100644 src/add-ons/media/plugins/ogg/OggSeekable.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggSeekable.h create mode 100644 src/add-ons/media/plugins/ogg/OggTrack.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggTrack.h create mode 100644 src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggVorbisSeekable.h diff --git a/src/add-ons/media/plugins/ogg/Jamfile b/src/add-ons/media/plugins/ogg/Jamfile index e2a5260c9c..2c6a4e1a8c 100644 --- a/src/add-ons/media/plugins/ogg/Jamfile +++ b/src/add-ons/media/plugins/ogg/Jamfile @@ -5,13 +5,15 @@ UsePrivateHeaders media ; SubDirHdrs [ FDirName $(SUBDIR) libogg ] ; Addon ogg : media plugins : - OggFrameInfo.cpp OggReaderPlugin.cpp + OggTrack.cpp OggStream.cpp + OggSeekable.cpp OggSpeexStream.cpp OggTheoraStream.cpp OggTobiasStream.cpp OggVorbisStream.cpp + OggVorbisSeekable.cpp : false : libogg.a ; diff --git a/src/add-ons/media/plugins/ogg/OggFrameInfo.cpp b/src/add-ons/media/plugins/ogg/OggFrameInfo.cpp deleted file mode 100644 index bc448a28da..0000000000 --- a/src/add-ons/media/plugins/ogg/OggFrameInfo.cpp +++ /dev/null @@ -1,57 +0,0 @@ - -#include "OggFrameInfo.h" - -OggFrameInfo::OggFrameInfo(uint page, uint packetonpage, uint packet) -{ - this->page = page; - this->packetonpage = packetonpage; - this->packet = packet; -} - -void -OggFrameInfo::SetNextIsNewPage(bool newpage) -{ - this->newpage = newpage; -} - -/* virtual */ -OggFrameInfo::~OggFrameInfo() -{ -} - -uint -OggFrameInfo::GetPage() const -{ - return page; -} - -uint -OggFrameInfo::GetPacketOnPage() const -{ - return packetonpage; -} - -uint -OggFrameInfo::GetPacket() const -{ - return packet; -} - -uint -OggFrameInfo::GetNextPage() const -{ - return (newpage ? page + 1 : page); -} - -uint -OggFrameInfo::GetNextPacketOnPage() const -{ - return (newpage ? 0 : packetonpage + 1); -} - -uint -OggFrameInfo::GetNextPacket() const -{ - return packet + 1; -} - diff --git a/src/add-ons/media/plugins/ogg/OggFrameInfo.h b/src/add-ons/media/plugins/ogg/OggFrameInfo.h deleted file mode 100644 index 3a7d8eb9af..0000000000 --- a/src/add-ons/media/plugins/ogg/OggFrameInfo.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _OGG_FRAME_INFO_H -#define _OGG_FRAME_INFO_H - -#include - -class OggFrameInfo { -public: - OggFrameInfo(uint page, uint packetonpage, uint packet); - virtual ~OggFrameInfo(); - void SetNextIsNewPage(bool newpage = true); - - uint GetPage() const; - uint GetPacketOnPage() const; - uint GetPacket() const; - - uint GetNextPage() const; - uint GetNextPacketOnPage() const; - uint GetNextPacket() const; -private: - uint page; // the page the frame started on - uint16 packetonpage; // of all the packets on that page which packet is this - uint packet; // the packet the frame started on - bool newpage; -}; - -#endif _OGG_FRAME_INFO_H diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp index 4a7dfdc4d9..6f234fcb54 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp @@ -2,12 +2,15 @@ #include #include #include -#include +#include #include +#include +#include #include #include #include "OggReaderPlugin.h" #include "OggStream.h" +#include "OggSeekable.h" #define TRACE_THIS 1 #if TRACE_THIS @@ -20,20 +23,22 @@ OggReader::OggReader() { TRACE("OggReader::OggReader\n"); ogg_sync_init(&fSync); + fSeekable = NULL; + fFile = NULL; + fPosition = -1; } OggReader::~OggReader() { TRACE("OggReader::~OggReader\n"); - serialno_OggStream_map::iterator i = fStreams.begin(); - while (i != fStreams.end()) { - serialno_OggStream_map::iterator j = i; + serialno_OggTrack_map::iterator i = fTracks.begin(); + while (i != fTracks.end()) { + serialno_OggTrack_map::iterator j = i; i++; delete j->second; } ogg_sync_clear(&fSync); - fNextPosition = -1; } @@ -44,16 +49,19 @@ OggReader::Copyright() } -status_t -OggReader::GetPage(ogg_page * page, int read_size, bool short_page) +// the main page reading hook (stream friendly) +ssize_t +OggReader::ReadPage(bool first_page) { -// TRACE("OggReader::GetPage\n"); +// TRACE("OggReader::ReadPage\n"); + int read_size = (first_page ? 4096 : 4*B_PAGE_SIZE); + BAutolock autolock(fSyncLock); + ogg_page page; retry: - off_t position = fNextPosition; - int result = ogg_sync_pageout(&fSync,page); // first read leftovers + int result = ogg_sync_pageout(&fSync, &page); // first read leftovers while (result == 0) { - char * buffer = ogg_sync_buffer(&fSync,read_size); - ssize_t bytes = Source()->Read(buffer,read_size); + char * buffer = ogg_sync_buffer(&fSync, read_size); + ssize_t bytes = Source()->Read(buffer, read_size); if (bytes == 0) { TRACE("OggReader::GetPage: Read: no data\n"); return B_LAST_BUFFER_ERROR; @@ -62,46 +70,50 @@ retry: TRACE("OggReader::GetPage: Read: error\n"); return bytes; } - if (ogg_sync_wrote(&fSync,bytes) != 0) { + if (ogg_sync_wrote(&fSync, bytes) != 0) { TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n"); return B_ERROR; } - result = ogg_sync_pageout(&fSync,page); - if (short_page && (result != 1)) { - TRACE("OggReader::GetPage: short page not found: error\n"); + result = ogg_sync_pageout(&fSync, &page); + if (first_page && (result != 1)) { + TRACE("OggReader::GetPage: short first page not found: error\n"); return B_ERROR; } + fPosition += bytes; } if (result == -1) { TRACE("OggReader::GetPage: ogg_sync_pageout: not synced: error\n"); return B_ERROR; } -#ifdef STRICT_OGG - if (ogg_page_version(page) != 0) { + if (ogg_page_version(&page) != 0) { TRACE("OggReader::GetPage: ogg_page_version: error in page encoding: error\n"); return B_ERROR; } -#endif - fNextPosition += (fSeekable ? page->header_len + page->body_len : 0); - long serialno = ogg_page_serialno(page); - if (fStreams.find(serialno) == fStreams.end()) { + long serialno = ogg_page_serialno(&page); + if (fTracks.find(serialno) == fTracks.end()) { // this is an unknown serialno - if (ogg_page_bos(page) == 0) { + if (ogg_page_bos(&page) == 0) { TRACE("OggReader::GetPage: non-bos page with unknown serialno\n"); #ifdef STRICT_OGG return B_ERROR; #else - // silently discard packets with unknown serialno + // silently discard non-bos packets with unknown serialno goto retry; #endif } +#ifdef STRICT_OGG + if (ogg_page_continued(&page) != 0) { + TRACE("oggReader::GetPage: ogg_page_continued: continued page: not ogg\n"); + return B_ERROR; + } +#endif STRICT_OGG // this is a beginning of stream page ogg_stream_state stream; if (ogg_stream_init(&stream, serialno) != 0) { TRACE("oggReader::GetPage: ogg_stream_init failed?: error\n"); return B_ERROR; } - if (ogg_stream_pagein(&stream, page) != 0) { + if (ogg_stream_pagein(&stream, &page) != 0) { TRACE("oggReader::GetPage: ogg_stream_pagein: failed: error\n"); return B_ERROR; } @@ -111,51 +123,59 @@ retry: return B_ERROR; #endif STRICT_OGG } - class Interface : public GetPageInterface { - private: - OggReader * reader; - long serialno; - public: - Interface(OggReader * reader, long serialno) { - this->reader = reader; - this->serialno = serialno; - } - virtual status_t GetNextPage() { - ogg_page next_page; - do { - status_t result = reader->GetPage(&next_page); - if (result != B_OK) { - return result; - } - } while (ogg_page_serialno(&next_page) != serialno); - return B_OK; - } - virtual status_t GetPageAt(off_t position, ogg_stream_state * stream, - int read_size = 4*B_PAGE_SIZE) { - return reader->GetPageAt(position,stream,read_size); - } - }; - fStreams[serialno] = OggStream::makeOggStream(new Interface(this, serialno), serialno, packet); + if (fSeekable) { + class Interface : public SeekableInterface { + private: + OggReader * reader; + public: + Interface(OggReader * reader) { + this->reader = reader; + } + virtual ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE) { + return reader->ReadPageAt(position, read_size); + } + }; + fTracks[serialno] = OggSeekable::makeOggSeekable(new Interface(this), serialno, packet); + } else { + class Interface : public StreamInterface { + private: + OggReader * reader; + public: + Interface(OggReader * reader) { + this->reader = reader; + } + virtual ssize_t ReadPage() { + return reader->ReadPage(); + } + }; + fTracks[serialno] = OggStream::makeOggStream(new Interface(this), serialno, packet); + } fCookies.push_back(serialno); } - return fStreams[serialno]->AddPage(position,page); + off_t pageStart = fPosition - page.header_len - page.body_len; + status_t status = fTracks[serialno]->AddPage(pageStart, page); + if (status != B_OK) { + return status; + } + return page.header_len + page.body_len; } -status_t -OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size) +ssize_t +OggReader::ReadPageAt(off_t position, int read_size) { TRACE("OggReader::GetPageAt %llu\n", position); if (!fSeekable) { return B_ERROR; } +/* ogg_sync_state sync; ogg_sync_init(&sync); ogg_page page; int result; - while ((result = ogg_sync_pageout(&sync,&page)) == 0) { - char * buffer = ogg_sync_buffer(&sync,read_size); - ssize_t bytes = fSeekable->ReadAt(position,buffer,read_size); + while ((result = ogg_sync_pageout(&sync, &page)) == 0) { + char * buffer = ogg_sync_buffer(&sync, read_size); + ssize_t bytes = fSeekable->ReadAt(position, buffer, read_size); if (bytes == 0) { TRACE("OggReader::GetPage: Read: no data\n"); return B_LAST_BUFFER_ERROR; @@ -165,7 +185,7 @@ OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size) return bytes; } position += bytes; - if (ogg_sync_wrote(&sync,bytes) != 0) { + if (ogg_sync_wrote(&sync, bytes) != 0) { TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n"); return B_ERROR; } @@ -185,6 +205,7 @@ OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size) return B_ERROR; } ogg_sync_clear(&sync); +*/ return B_OK; } @@ -198,7 +219,7 @@ get_seekable(BDataIO * data) return 0; } // first try to get our current location - off_t current = seekable->Seek(0,SEEK_CUR); + off_t current = seekable->Seek(0, SEEK_CUR); if (current < 0) { return 0; } @@ -207,58 +228,57 @@ get_seekable(BDataIO * data) return 0; } // next try to seek to our current location (absolutely) - if (seekable->Seek(current,SEEK_SET) < 0) { + if (seekable->Seek(current, SEEK_SET) < 0) { return 0; } // next try to seek to the start of the stream (absolutely) - if (seekable->Seek(0,SEEK_SET) < 0) { + if (seekable->Seek(0, SEEK_SET) < 0) { return 0; } // then seek back to where we started - if (seekable->Seek(current,SEEK_SET) < 0) { + if (seekable->Seek(current, SEEK_SET) < 0) { // really screwed return 0; } return seekable; } +static BFile * +get_file(BDataIO * data) +{ + // try to cast to BFile then perform a series of + // sanity checks to ensure that the file is okay + BFile * file = dynamic_cast(data); + if (file == 0) { + return 0; + } + if (file->InitCheck() != B_OK) { + return 0; + } + return file; +} + status_t OggReader::Sniff(int32 *streamCount) { TRACE("OggReader::Sniff\n"); #ifdef STRICT_OGG - bool short_page = true; + bool first_page = true; #else - bool short_page = false; + bool first_page = false; #endif - fSeekable = get_seekable(Source()); +// fSeekable = get_seekable(Source()); + fFile = get_file(Source()); - fNextPosition = (fSeekable ? fSeekable->Position() : -1); - - ogg_page page; - if (GetPage(&page,4096,short_page) != B_OK) { - return B_ERROR; + ssize_t bytes = ReadPage(first_page); + if (bytes < 0) { + return bytes; } - - // page sanity checks - if (ogg_page_version(&page) != 0) { - TRACE("OggReader::Sniff: ogg_page_version: error in page encoding: not ogg\n"); - return B_ERROR; - } -#ifdef STRICT_OGG - if (ogg_page_bos(&page) == 0) { - TRACE("OggReader::Sniff: ogg_page_bos: not beginning of a bitstream: not ogg\n"); - return B_ERROR; - } - if (ogg_page_continued(&page) != 0) { - TRACE("OggReader::Sniff: ogg_page_continued: continued page: not ogg\n"); - return B_ERROR; - } -#endif STRICT_OGG - - while (ogg_page_bos(&page) > 0) { - if (GetPage(&page) != B_OK) { - return B_ERROR; + uint count = 1; + while (count++ == fCookies.size()) { + ssize_t bytes = ReadPage(); + if (bytes < 0) { + return bytes; } } *streamCount = fCookies.size(); @@ -292,9 +312,9 @@ OggReader::AllocateCookie(int32 streamNumber, void **cookie) TRACE("OggReader::AllocateCookie: invalid streamNumber: bail\n"); return B_ERROR; } - OggStream * stream = fStreams[fCookies[streamNumber]]; + OggTrack * track = fTracks[fCookies[streamNumber]]; // store the cookie - *cookie = stream; + *cookie = track; return B_OK; } @@ -314,8 +334,8 @@ OggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, TRACE("OggReader::GetStreamInfo\n"); *infoBuffer = 0; *infoSize = 0; - OggStream * stream = static_cast(cookie); - return stream->GetStreamInfo(frameCount,duration,format); + OggTrack * track = static_cast(cookie); + return track->GetStreamInfo(frameCount, duration, format); } @@ -324,9 +344,9 @@ OggReader::Seek(void *cookie, uint32 seekTo, int64 *frame, bigtime_t *time) { - TRACE("OggReader::Seek to %lld : %lld\n",*frame,*time); - OggStream * stream = static_cast(cookie); - return stream->Seek(seekTo,frame,time); + TRACE("OggReader::Seek to %lld : %lld\n", *frame, *time); + OggTrack * track = static_cast(cookie); + return track->Seek(seekTo, frame, time); } @@ -336,8 +356,8 @@ OggReader::GetNextChunk(void *cookie, media_header *mediaHeader) { // TRACE("OggReader::GetNextChunk\n"); - OggStream * stream = static_cast(cookie); - return stream->GetNextChunk(chunkBuffer,chunkSize,mediaHeader); + OggTrack * track = static_cast(cookie); + return track->GetNextChunk(chunkBuffer, chunkSize, mediaHeader); } diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h index 204ea67d4a..36baf2ebc9 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h @@ -8,9 +8,9 @@ namespace BPrivate { namespace media { -class OggStream; +class OggTrack; -typedef std::map serialno_OggStream_map; +typedef std::map serialno_OggTrack_map; typedef std::vector serialno_vector; class OggReader : public Reader @@ -28,36 +28,35 @@ public: status_t AllocateCookie(int32 streamNumber, void **cookie); status_t FreeCookie(void *cookie); - // delegated to OggStream + // delegated to OggTrack status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, media_format *format, void **infoBuffer, int32 *infoSize); - status_t Seek(void *cookie, - uint32 seekTo, - int64 *frame, bigtime_t *time); - status_t GetNextChunk(void *cookie, - void **chunkBuffer, int32 *chunkSize, + status_t Seek(void *cookie, uint32 seekTo, int64 *frame, bigtime_t *time); + status_t GetNextChunk(void *cookie, void **chunkBuffer, int32 *chunkSize, media_header *mediaHeader); -protected: - // called by OggStream through GetPageInterface - status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE, - bool short_page = false); - status_t GetPageAt(off_t position, ogg_stream_state * stream, - int read_size = 4*B_PAGE_SIZE); - - ogg_sync_state fSync; - serialno_OggStream_map fStreams; - serialno_vector fCookies; - BPositionIO * fSeekable; - private: - off_t fNextPosition; + ogg_sync_state fSync; + BLocker fSyncLock; + serialno_OggTrack_map fTracks; + serialno_vector fCookies; - class GetPageInterface { + BPositionIO * fSeekable; + BFile * fFile; + + off_t fPosition; + + // interfaces for OggTracks + ssize_t ReadPage(bool first_page = false); + ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE); + + class StreamInterface { public: - virtual status_t GetNextPage() = 0; - virtual status_t GetPageAt(off_t position, ogg_stream_state * stream, - int read_size = 4*B_PAGE_SIZE) = 0; + virtual ssize_t ReadPage() = 0; + }; + class SeekableInterface { + public: + virtual ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE) = 0; }; }; diff --git a/src/add-ons/media/plugins/ogg/OggSeekable.cpp b/src/add-ons/media/plugins/ogg/OggSeekable.cpp new file mode 100644 index 0000000000..3252e889e9 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggSeekable.cpp @@ -0,0 +1,174 @@ +#include "OggSeekable.h" +#include "OggVorbisSeekable.h" +#include +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + +/* + * OggSeekable codec identification and instantiation + */ + +/* static */ OggSeekable * +OggSeekable::makeOggSeekable(OggReader::SeekableInterface * interface, + long serialno, const ogg_packet & packet) +{ + TRACE("OggSeekable::makeOggSeekable\n"); + OggSeekable * stream; + if (OggVorbisSeekable::IsValidHeader(packet)) { + stream = new OggVorbisSeekable(serialno); +// } else if (OggTobiasSeekable::IsValidHeader(packet)) { +// stream = new OggTobiasSeekable(serialno); +// } else if (OggSpeexSeekable::IsValidHeader(packet)) { +// stream = new OggSpeexSeekable(serialno); +// } else if (OggTheoraSeekable::IsValidHeader(packet)) { +// stream = new OggTheoraSeekable(serialno); + } else { + stream = new OggSeekable(serialno); + } + stream->fReaderInterface = interface; + return stream; +} + + +/* + * OggSeekable generic functions + */ + +OggSeekable::OggSeekable(long serialno) + : OggTrack(serialno) +{ + TRACE("OggSeekable::OggSeekable\n"); + ogg_sync_init(&fSync); + ogg_stream_init(&fStreamState,serialno); +} + + +OggSeekable::~OggSeekable() +{ + // free internal stream state storage + ogg_sync_clear(&fSync); + ogg_stream_clear(&fStreamState); +} + + +status_t +OggSeekable::AddPage(off_t position, const ogg_page & page) +{ + TRACE("OggSeekable::AddPage\n"); + BAutolock autolock(fSyncLock); + char * buffer; + // copy the header to our local sync + buffer = ogg_sync_buffer(&fSync, page.header_len); + memcpy(buffer, page.header, page.header_len); + ogg_sync_wrote(&fSync, page.header_len); + // copy the body to our local sync + buffer = ogg_sync_buffer(&fSync, page.body_len); + memcpy(buffer, page.body, page.body_len); + ogg_sync_wrote(&fSync, page.body_len); + return B_OK; +} + + +status_t +OggSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + TRACE("OggSeekable::GetStreamInfo\n"); + status_t result = B_OK; + ogg_packet packet; + if (GetHeaderPackets().size() < 1) { + result = GetPacket(&packet); + if (result != B_OK) { + TRACE("OggSeekable::GetStreamInfo failed to get header packet\n"); + return result; + } + SaveHeaderPacket(packet); + } + packet = GetHeaderPackets()[0]; + if (!packet.b_o_s) { + TRACE("OggSeekable::GetStreamInfo failed : not beginning of stream\n"); + return B_ERROR; // first packet was not beginning of stream + } + + // parse header packet + uint32 four_bytes = *(uint32*)(&packet); + + // get the format for the description + media_format_description description; + description.family = B_MISC_FORMAT_FAMILY; + description.u.misc.file_format = 'OggS'; + description.u.misc.codec = four_bytes; + BMediaFormats formats; + result = formats.InitCheck(); + if (result == B_OK) { + result = formats.GetFormatFor(description, format); + } + if (result != B_OK) { + // ignore the error, allow the user to use ReadChunk interface + } + + // fill out format from header packet + format->user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)format->user_data, (char*)(&packet), 4); + + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); + *duration = 140000000; + *frameCount = 60000; + return B_OK; +} + + +// the default chunk is an ogg packet +status_t +OggSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + status_t result = GetPacket(&fChunkPacket); + if (result != B_OK) { + TRACE("OggSeekable::GetNextChunk failed: GetPacket = %s\n", strerror(result)); + return result; + } + *chunkBuffer = &fChunkPacket; + *chunkSize = sizeof(fChunkPacket); + return B_OK; +} + + +// subclass pull input function +status_t +OggSeekable::GetPacket(ogg_packet * packet) +{ + // at the end, pull the packet + while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) { + BAutolock autolock(fSyncLock); + int result; + ogg_page page; + while ((result = ogg_sync_pageout(&fSync,&page)) == 0) { + status_t result = B_ERROR; // fReaderInterface->ReadPage(); + if (result != B_OK) { + TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(result)); + return result; + } + } + if (result == -1) { + TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n"); + return B_ERROR; + } + if (ogg_stream_pagein(&fStreamState,&page) != 0) { + TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n"); + return B_ERROR; + } + } + if (ogg_stream_packetout(&fStreamState, packet) != 1) { + TRACE("OggSeekable::GetPacket: ogg_stream_packetout failed\n"); + return B_ERROR; + } + return B_OK; +} + diff --git a/src/add-ons/media/plugins/ogg/OggSeekable.h b/src/add-ons/media/plugins/ogg/OggSeekable.h new file mode 100644 index 0000000000..afdc905610 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggSeekable.h @@ -0,0 +1,45 @@ +#ifndef _OGG_SEEKABLE_H +#define _OGG_SEEKABLE_H + +#include "OggTrack.h" +#include "OggReaderPlugin.h" +#include + +namespace BPrivate { namespace media { + +class OggSeekable : public OggTrack { +public: + static OggSeekable * makeOggSeekable(OggReader::SeekableInterface * interface, + long serialno, const ogg_packet & packet); + + // interface for OggReader + virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format); + virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + OggSeekable(long serialno); +public: + virtual ~OggSeekable(); + + // reader push input function + status_t AddPage(off_t position, const ogg_page & page); + +protected: + // subclass pull input function + status_t GetPacket(ogg_packet * packet); + +private: + ogg_sync_state fSync; + BLocker fSyncLock; + ogg_stream_state fStreamState; + OggReader::SeekableInterface * fReaderInterface; + ogg_packet fChunkPacket; +}; + +} } // namespace BPrivate::media + +using namespace BPrivate::media; + +#endif // _OGG_SEEKABLE_H diff --git a/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp b/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp index 6fa90c5a97..1d7d97aa86 100644 --- a/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp +++ b/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp @@ -70,14 +70,14 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, ogg_packet packet; // get header packet - if (fHeaderPackets.size() < 1) { + if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } - packet = fHeaderPackets[0]; + packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { return B_ERROR; // first packet was not beginning of stream } @@ -122,7 +122,7 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, format->u.encoded_audio.output.buffer_size = buffer_size; // get comment packet - if (fHeaderPackets.size() < 2) { + if (GetHeaderPackets().size() < 2) { result = GetPacket(&packet); if (result != B_OK) { return result; @@ -131,7 +131,7 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, } // get extra headers - while ((signed)fHeaderPackets.size() < header->extra_headers) { + while ((signed)GetHeaderPackets().size() < header->extra_headers) { result = GetPacket(&packet); if (result != B_OK) { return result; @@ -139,8 +139,23 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, SaveHeaderPacket(packet); } - format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); *duration = 100000000; *frameCount = 60000; return B_OK; } + + +status_t +OggSpeexStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + status_t result = GetPacket(&fChunkPacket); + if (result != B_OK) { + TRACE("OggSpeexStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); + return result; + } + *chunkBuffer = fChunkPacket.packet; + *chunkSize = fChunkPacket.bytes; + return B_OK; +} diff --git a/src/add-ons/media/plugins/ogg/OggSpeexStream.h b/src/add-ons/media/plugins/ogg/OggSpeexStream.h index b63f91a5ec..e50ccf399d 100644 --- a/src/add-ons/media/plugins/ogg/OggSpeexStream.h +++ b/src/add-ons/media/plugins/ogg/OggSpeexStream.h @@ -14,6 +14,9 @@ public: virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, media_format *format); + virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + }; } } // namespace BPrivate::media diff --git a/src/add-ons/media/plugins/ogg/OggStream.cpp b/src/add-ons/media/plugins/ogg/OggStream.cpp index fb9c1ed822..5a6eb0aa3d 100644 --- a/src/add-ons/media/plugins/ogg/OggStream.cpp +++ b/src/add-ons/media/plugins/ogg/OggStream.cpp @@ -18,7 +18,7 @@ */ /* static */ OggStream * -OggStream::makeOggStream(OggReader::GetPageInterface * interface, +OggStream::makeOggStream(OggReader::StreamInterface * interface, long serialno, const ogg_packet & packet) { TRACE("OggStream::makeOggStream\n"); @@ -39,90 +39,55 @@ OggStream::makeOggStream(OggReader::GetPageInterface * interface, } -/* static */ bool -OggStream::findIdentifier(const ogg_packet & packet, const char * id, uint pos) -{ - uint length = strlen(id); - if ((unsigned)packet.bytes < pos+length) { - return false; - } - return !memcmp(&packet.packet[pos], id, length); -} - - /* * OggStream generic functions */ OggStream::OggStream(long serialno) + : OggTrack(serialno) { TRACE("OggStream::OggStream\n"); fCurrentFrame = 0; fCurrentTime = 0; - this->fSerialno = serialno; ogg_sync_init(&fSync); - ogg_stream_init(&fSeekStreamState,serialno); - fCurrentPage = 0; - fPacketOnCurrentPage = 0; - fCurrentPacket = 0; - ogg_stream_init(&fEndStreamState,serialno); - fEndPage = 0; - fPacketOnEndPage = 0; - fEndPacket = 0; + ogg_stream_init(&fStreamState,serialno); } OggStream::~OggStream() { - // free internal header packet storage - std::vector::iterator iter = fHeaderPackets.begin(); - while (iter != fHeaderPackets.end()) { - delete iter->packet; - iter++; - } // free internal stream state storage ogg_sync_clear(&fSync); - ogg_stream_clear(&fSeekStreamState); - ogg_stream_clear(&fEndStreamState); -} - - -long -OggStream::GetSerial() const -{ - return fSerialno; + ogg_stream_clear(&fStreamState); } status_t -OggStream::AddPage(off_t position, ogg_page * page) +OggStream::AddPage(off_t position, const ogg_page & page) { - TRACE("OggStream::AddPage"); - if (position >= 0) { - TRACE(" %lld", position); - fPagePositions.push_back(position); - } - TRACE("\n"); + TRACE("OggStream::AddPage\n"); BAutolock autolock(fSyncLock); char * buffer; - buffer = ogg_sync_buffer(&fSync, page->header_len); - memcpy(buffer,page->header, page->header_len); - ogg_sync_wrote(&fSync, page->header_len); - buffer = ogg_sync_buffer(&fSync, page->body_len); - memcpy(buffer,page->body, page->body_len); - ogg_sync_wrote(&fSync, page->body_len); + // copy the header to our local sync + buffer = ogg_sync_buffer(&fSync, page.header_len); + memcpy(buffer, page.header, page.header_len); + ogg_sync_wrote(&fSync, page.header_len); + // copy the body to our local sync + buffer = ogg_sync_buffer(&fSync, page.body_len); + memcpy(buffer, page.body, page.body_len); + ogg_sync_wrote(&fSync, page.body_len); return B_OK; } status_t OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format) + media_format *format) { TRACE("OggStream::GetStreamInfo\n"); status_t result = B_OK; ogg_packet packet; - if (fHeaderPackets.size() < 1) { + if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { TRACE("OggStream::GetStreamInfo failed to get header packet\n"); @@ -130,7 +95,7 @@ OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, } SaveHeaderPacket(packet); } - packet = fHeaderPackets[0]; + packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { TRACE("OggStream::GetStreamInfo failed : not beginning of stream\n"); return B_ERROR; // first packet was not beginning of stream @@ -157,70 +122,18 @@ OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, format->user_data_type = B_CODEC_TYPE_INFO; strncpy((char*)format->user_data, (char*)(&packet), 4); - format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); *duration = 140000000; *frameCount = 60000; return B_OK; } -status_t -OggStream::Seek(uint32 seekTo, int64 *frame, bigtime_t *time) -{ - TRACE("OggStream::Seek to %lld : %lld\n", *frame, *time); - if (seekTo & B_MEDIA_SEEK_TO_FRAME) { - if (*frame > fOggFrameInfos.size() - 1) { - // seek to end - fCurrentPage = fEndPage; - fPacketOnCurrentPage = fPacketOnEndPage; - fCurrentPacket = fEndPacket; - return B_OK; - } - *frame = max_c(0, *frame); // clip to zero - // input the page into a temporary seek stream - ogg_stream_state seekStreamState; - uint pageno = fOggFrameInfos[*frame].GetPage(); - off_t position = fPagePositions[pageno]; - ogg_stream_init(&seekStreamState, fSerialno); - status_t result = fReaderInterface->GetPageAt(position, &seekStreamState); - if (result != B_OK) { - TRACE("OggStream::GetPageAt %lld failed\n", position); - return result; // pageno/fPagePosition corrupted? - } - // discard earlier packets from this page - uint packetno = fOggFrameInfos[*frame].GetPacketOnPage(); - while (packetno-- > 0) { - ogg_packet packet; - // don't check for errors, we may have a packet ending on page - ogg_stream_packetout(&seekStreamState, &packet); - } - // clear out the former seek stream state - // this will delete its internal storage - ogg_stream_clear(&fSeekStreamState); - // initialize it with the temporary seek stream - // this will transfer the internal storage to it - fSeekStreamState = seekStreamState; - // we notably do not clear our temporary stream - // instead we just let it go out of scope - fCurrentPage = fOggFrameInfos[*frame].GetNextPage(); - fPacketOnCurrentPage = fOggFrameInfos[*frame].GetNextPacketOnPage(); - fCurrentPacket = fOggFrameInfos[*frame].GetPacket()+1; - } else if (seekTo & B_MEDIA_SEEK_TO_TIME) { - *frame = *time/50000; - return Seek(B_MEDIA_SEEK_TO_FRAME,frame,time); - } - return B_OK; -} - - +// the default chunk is an ogg packet status_t OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, media_header *mediaHeader) { - if (fCurrentPacket - fHeaderPackets.size() == fOggFrameInfos.size()) { - OggFrameInfo info(fEndPage,fPacketOnEndPage,fEndPacket); - fOggFrameInfos.push_back(info); - } status_t result = GetPacket(&fChunkPacket); if (result != B_OK) { TRACE("OggStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); @@ -236,82 +149,31 @@ OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, status_t OggStream::GetPacket(ogg_packet * packet) { - if (fCurrentPacket >= fEndPacket) { - // at the end, pull the packet - uint8 pageno = fEndPage; - while (ogg_stream_packetpeek(&fEndStreamState, NULL) != 1) { - BAutolock autolock(fSyncLock); - int result; - ogg_page page; - while ((result = ogg_sync_pageout(&fSync,&page)) == 0) { - status_t result = fReaderInterface->GetNextPage(); - if (result != B_OK) { - TRACE("OggStream::GetPacket: GetNextPage = %s\n", strerror(result)); - return result; - } - } - if (result == -1) { - TRACE("OggStream::GetPacket: ogg_sync_pageout: not synced??\n"); - return B_ERROR; - } - if (ogg_stream_pagein(&fEndStreamState,&page) != 0) { - TRACE("OggStream::GetPacket: ogg_stream_pagein: failed??\n"); - return B_ERROR; - } - fEndPage++; - } - if (ogg_stream_packetout(&fEndStreamState, packet) != 1) { - TRACE("OggStream::GetPacket: ogg_stream_packetout failed at the end\n"); - return B_ERROR; - } - fEndPacket++; - if (pageno != fEndPage) { - size_t last_info = fOggFrameInfos.size(); - if (last_info > 0) { - if (fOggFrameInfos[last_info-1].GetNextPacket() == fEndPacket) { - fOggFrameInfos[last_info-1].SetNextIsNewPage(); - } - } - fPacketOnEndPage = 0; - } else { - fPacketOnEndPage++; - } - fCurrentPacket = fEndPacket; - fPacketOnCurrentPage = fPacketOnEndPage; - fCurrentPage = fEndPage; - } else { - // in the middle, get packet at position - uint8 page = fCurrentPage; - while (ogg_stream_packetpeek(&fSeekStreamState, NULL) != 1) { - off_t position = fPagePositions[fCurrentPage++]; - status_t result = fReaderInterface->GetPageAt(position, &fSeekStreamState); - if (result != B_OK) { - TRACE("OggStream::GetPacket: GetPageAt = %s\n", strerror(result)); + // at the end, pull the packet + while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) { + BAutolock autolock(fSyncLock); + int result; + ogg_page page; + while ((result = ogg_sync_pageout(&fSync,&page)) == 0) { + result = fReaderInterface->ReadPage(); + if (result < 0) { + TRACE("OggStream::GetPacket: GetNextPage = %s\n", strerror(result)); return result; } } - if (ogg_stream_packetout(&fSeekStreamState, packet) != 1) { - TRACE("OggStream::GetPacket: ogg_stream_packetout failed in the middle\n"); + if (result == -1) { + TRACE("OggStream::GetPacket: ogg_sync_pageout: not synced??\n"); return B_ERROR; } - fCurrentPacket++; - if (page != fCurrentPage) { - fPacketOnCurrentPage = 0; - } else { - fPacketOnCurrentPage++; + if (ogg_stream_pagein(&fStreamState,&page) != 0) { + TRACE("OggStream::GetPacket: ogg_stream_pagein: failed??\n"); + return B_ERROR; } } + if (ogg_stream_packetout(&fStreamState, packet) != 1) { + TRACE("OggStream::GetPacket: ogg_stream_packetout failed\n"); + return B_ERROR; + } return B_OK; } - -// GetStreamInfo helpers -void -OggStream::SaveHeaderPacket(ogg_packet packet) -{ - uint8 * buffer = new uint8[packet.bytes]; - memcpy(buffer, packet.packet, packet.bytes); - packet.packet = buffer; - fHeaderPackets.push_back(packet); -} - diff --git a/src/add-ons/media/plugins/ogg/OggStream.h b/src/add-ons/media/plugins/ogg/OggStream.h index 6a9cec1bcf..751b372dc5 100644 --- a/src/add-ons/media/plugins/ogg/OggStream.h +++ b/src/add-ons/media/plugins/ogg/OggStream.h @@ -1,67 +1,45 @@ #ifndef _OGG_STREAM_H #define _OGG_STREAM_H -#include -#include -#include "ogg/ogg.h" +#include "OggTrack.h" #include "OggReaderPlugin.h" -#include "OggFrameInfo.h" #include -#include namespace BPrivate { namespace media { -class OggStream { +class OggStream : public OggTrack { public: - static OggStream * makeOggStream(OggReader::GetPageInterface * interface, + static OggStream * makeOggStream(OggReader::StreamInterface * interface, long serialno, const ogg_packet & packet); + // interface for OggReader + virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format); + virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + protected: - static bool findIdentifier(const ogg_packet & packet, - const char * id, uint pos); OggStream(long serialno); public: virtual ~OggStream(); - long GetSerial() const; // reader push input function - status_t AddPage(off_t position, ogg_page * page); - - // reader pull output functions - virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format); - virtual status_t Seek(uint32 seekTo, int64 *frame, bigtime_t *time); - virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader); + status_t AddPage(off_t position, const ogg_page & page); protected: // subclass pull input function status_t GetPacket(ogg_packet * packet); - - // GetStreamInfo helpers - void SaveHeaderPacket(ogg_packet packet); - std::vector fHeaderPackets; + ogg_packet fChunkPacket; protected: int64 fCurrentFrame; bigtime_t fCurrentTime; private: - long fSerialno; - std::vector fPagePositions; - std::vector fOggFrameInfos; ogg_sync_state fSync; BLocker fSyncLock; - ogg_stream_state fSeekStreamState; - uint fCurrentPage; - uint fPacketOnCurrentPage; - uint fCurrentPacket; - ogg_stream_state fEndStreamState; - uint fEndPage; - uint fPacketOnEndPage; - uint fEndPacket; - OggReader::GetPageInterface * fReaderInterface; - ogg_packet fChunkPacket; + ogg_stream_state fStreamState; + OggReader::StreamInterface * fReaderInterface; }; } } // namespace BPrivate::media diff --git a/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp b/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp index 7c17415f1f..ebc3bc3649 100644 --- a/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp +++ b/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp @@ -152,14 +152,14 @@ OggTheoraStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, ogg_packet packet; // get header packet - if (fHeaderPackets.size() < 1) { + if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } - packet = fHeaderPackets[0]; + packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { return B_ERROR; // first packet was not beginning of stream } @@ -199,7 +199,7 @@ OggTheoraStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, format->u.encoded_video.output.display.line_count = info.frame_height; // TODO: wring more info out of the headers - format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); *duration = 80000000; *frameCount = 60000; return B_OK; diff --git a/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp b/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp index 7843c29b6b..d9f2e39daa 100644 --- a/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp +++ b/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp @@ -73,7 +73,7 @@ OggTobiasStream::OggTobiasStream(long serialno) OggTobiasStream::~OggTobiasStream() { - + TRACE("OggTobiasStream::~OggTobiasStream\n"); } status_t @@ -85,14 +85,14 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, ogg_packet packet; // get header packet - if (fHeaderPackets.size() < 1) { + if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } - packet = fHeaderPackets[0]; + packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { return B_ERROR; // first packet was not beginning of stream } @@ -128,7 +128,7 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, // TODO: wring more info out of the headers // get comment packet - if (fHeaderPackets.size() < 2) { + if (GetHeaderPackets().size() < 2) { result = GetPacket(&packet); if (result != B_OK) { return result; @@ -136,7 +136,7 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, SaveHeaderPacket(packet); } - format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); *duration = 80000000; *frameCount = 60000; return B_OK; @@ -144,10 +144,14 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, status_t OggTobiasStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader) + media_header *mediaHeader) { - OggStream::GetNextChunk(chunkBuffer, chunkSize, mediaHeader); - *chunkSize = ((ogg_packet*)chunkBuffer)->bytes; - *chunkBuffer = ((ogg_packet*)chunkBuffer)->packet; + status_t result = GetPacket(&fChunkPacket); + if (result != B_OK) { + TRACE("OggTobiasStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); + return result; + } + *chunkBuffer = fChunkPacket.packet; + *chunkSize = fChunkPacket.bytes; return B_OK; } diff --git a/src/add-ons/media/plugins/ogg/OggTobiasStream.h b/src/add-ons/media/plugins/ogg/OggTobiasStream.h index c05edec1f9..c76e97a989 100644 --- a/src/add-ons/media/plugins/ogg/OggTobiasStream.h +++ b/src/add-ons/media/plugins/ogg/OggTobiasStream.h @@ -16,8 +16,6 @@ public: media_format *format); virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, media_header *mediaHeader); -private: - ogg_packet fChunkPacket; }; diff --git a/src/add-ons/media/plugins/ogg/OggTrack.cpp b/src/add-ons/media/plugins/ogg/OggTrack.cpp new file mode 100644 index 0000000000..a2bb6be012 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTrack.cpp @@ -0,0 +1,95 @@ +#include "OggTrack.h" +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + +/* static */ bool +OggTrack::findIdentifier(const ogg_packet & packet, const char * id, uint pos) +{ + uint length = strlen(id); + if ((unsigned)packet.bytes < pos+length) { + return false; + } + return !memcmp(&packet.packet[pos], id, length); +} + + +OggTrack::OggTrack(long serialno) +{ + TRACE("OggTrack::OggTrack\n"); + fSerialno = serialno; +} + + +OggTrack::~OggTrack() +{ + // free internal header packet storage + std::vector::iterator iter = fHeaderPackets.begin(); + while (iter != fHeaderPackets.end()) { + delete iter->packet; + iter++; + } +} + + +long +OggTrack::GetSerial() const +{ + return fSerialno; +} + + +status_t +OggTrack::GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + *frameCount = 0; + *duration = 0; + media_format f; + *format = f; + return B_UNSUPPORTED; +} + + +status_t +OggTrack::Seek(uint32 seekTo, int64 *frame, bigtime_t *time) +{ + *frame = 0; + *time = 0; + return B_UNSUPPORTED; +} + + +status_t +OggTrack::GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + *chunkBuffer = 0; + *chunkSize = 0; + media_header mh; + *mediaHeader = mh; + return B_UNSUPPORTED; +} + + +// GetStreamInfo helpers +void +OggTrack::SaveHeaderPacket(ogg_packet packet) +{ + uint8 * buffer = new uint8[packet.bytes]; + memcpy(buffer, packet.packet, packet.bytes); + packet.packet = buffer; + fHeaderPackets.push_back(packet); +} + + +const std::vector & +OggTrack::GetHeaderPackets() +{ + return fHeaderPackets; +} diff --git a/src/add-ons/media/plugins/ogg/OggTrack.h b/src/add-ons/media/plugins/ogg/OggTrack.h new file mode 100644 index 0000000000..81c7de53b3 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTrack.h @@ -0,0 +1,44 @@ +#ifndef _OGG_TRACK_H +#define _OGG_TRACK_H + +#include +#include +#include "ogg/ogg.h" +#include + +namespace BPrivate { namespace media { + +class OggTrack { +protected: + static bool findIdentifier(const ogg_packet & packet, + const char * id, uint pos); + OggTrack(long serialno); +public: + virtual ~OggTrack(); + long GetSerial() const; + + // push interface + virtual status_t AddPage(off_t position, const ogg_page & page) = 0; + + // interface for OggReader + virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format); + virtual status_t Seek(uint32 seekTo, int64 *frame, bigtime_t *time); + virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + // GetStreamInfo helpers + void SaveHeaderPacket(ogg_packet packet); + const std::vector & GetHeaderPackets(); + +private: + std::vector fHeaderPackets; + long fSerialno; +}; + +} } // namespace BPrivate::media + +using namespace BPrivate::media; + +#endif // _OGG_TRACK_H diff --git a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp new file mode 100644 index 0000000000..2323e8738f --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp @@ -0,0 +1,197 @@ +#include "OggVorbisFormats.h" +#include "OggVorbisSeekable.h" +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + +inline size_t +AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */) +{ + return (raf->format & 0xf) * (raf->channel_count) + * (size_t)((raf->frame_rate * buffer_duration) / 1000000.0); +} + +/* + * vorbis header parsing code from libvorbis/info.c + */ + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +// based on libvorbis/info.c _vorbis_unpack_info +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + vi->version = oggpack_read(opb, 32); + if (vi->version != 0) { + return -1; + } + vi->channels = oggpack_read(opb, 8); + vi->rate = oggpack_read(opb, 32); + vi->bitrate_upper = oggpack_read(opb, 32); + vi->bitrate_nominal = oggpack_read(opb, 32); + vi->bitrate_lower = oggpack_read(opb, 32); + long blocksizes0 = oggpack_read(opb, 4); + long blocksizes1 = oggpack_read(opb, 4); + if (vi->rate < 1) { + return -1; + } + if (vi->channels < 1) { + return -1; + } + if (blocksizes0 < 8) { + return -1; + } + if (blocksizes1 < blocksizes0) { + return -1; + } + if (oggpack_read(opb, 1) != 1) { + return -1; + } /* EOP check */ + return 0; +} + +/* + * OggVorbisSeekable implementations + */ + +/* static */ bool +OggVorbisSeekable::IsValidHeader(const ogg_packet & packet) +{ + return findIdentifier(packet,"vorbis",1); +} + +OggVorbisSeekable::OggVorbisSeekable(long serialno) + : OggSeekable(serialno) +{ + TRACE("OggVorbisSeekable::OggVorbisSeekable\n"); +} + +OggVorbisSeekable::~OggVorbisSeekable() +{ + TRACE("OggVorbisSeekable::~OggVorbisSeekable\n"); +} + +status_t +OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + TRACE("OggVorbisSeekable::GetStreamInfo\n"); + status_t result = B_OK; + ogg_packet packet; + + // get header packet + if (GetHeaderPackets().size() < 1) { + result = GetPacket(&packet); + if (result != B_OK) { + return result; + } + SaveHeaderPacket(packet); + } + packet = GetHeaderPackets()[0]; + if (!packet.b_o_s) { + return B_ERROR; // first packet was not beginning of stream + } + + // parse header packet + // based on libvorbis/info.c vorbis_synthesis_headerin(...) + oggpack_buffer opb; + oggpack_readinit(&opb, packet.packet, packet.bytes); + int packtype = oggpack_read(&opb, 8); + if (packtype != 0x01) { + return B_ERROR; // first packet was not an info packet + } + // discard vorbis string + for (uint i = 0 ; i < sizeof("vorbis") - 1 ; i++) { + oggpack_read(&opb, 8); + } + vorbis_info info; + if (_vorbis_unpack_info(&info, &opb) != 0) { + return B_ERROR; // couldn't unpack info + } + + // get the format for the description + media_format_description description = vorbis_description(); + BMediaFormats formats; + result = formats.InitCheck(); + if (result == B_OK) { + result = formats.GetFormatFor(description, format); + } + if (result != B_OK) { + *format = vorbis_encoded_media_format(); + // ignore error, allow user to use ReadChunk interface + } + + // fill out format from header packet + if (info.bitrate_nominal > 0) { + format->u.encoded_audio.bit_rate = info.bitrate_nominal; + } else if (info.bitrate_upper > 0) { + format->u.encoded_audio.bit_rate = info.bitrate_upper; + } else if (info.bitrate_lower > 0) { + format->u.encoded_audio.bit_rate = info.bitrate_lower; + } + if (info.channels == 1) { + format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT; + } else { + format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + } + format->u.encoded_audio.output.frame_rate = (float)info.rate; + format->u.encoded_audio.output.channel_count = info.channels; + format->u.encoded_audio.output.buffer_size + = AudioBufferSize(&format->u.encoded_audio.output); + + // get comment packet + if (GetHeaderPackets().size() < 2) { + result = GetPacket(&packet); + if (result != B_OK) { + return result; + } + SaveHeaderPacket(packet); + } + + // get codebook packet + if (GetHeaderPackets().size() < 3) { + result = GetPacket(&packet); + if (result != B_OK) { + return result; + } + SaveHeaderPacket(packet); + } + + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); + + // compute frame count and duration from sample count + int64 samples = 1000000; + *frameCount = samples; + *duration = (1000000LL * samples) / (long long)format->u.encoded_audio.output.frame_rate; + + return B_OK; +} diff --git a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h new file mode 100644 index 0000000000..7b7d108de2 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h @@ -0,0 +1,23 @@ +#ifndef _OGG_VORBIS_SEEKABLE_H +#define _OGG_VORBIS_SEEKABLE_H + +#include "OggSeekable.h" + +namespace BPrivate { namespace media { + +class OggVorbisSeekable : public OggSeekable { +public: + static bool IsValidHeader(const ogg_packet & packet); +public: + OggVorbisSeekable(long serialno); + virtual ~OggVorbisSeekable(); + + virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, + media_format *format); +}; + +} } // namespace BPrivate::media + +using namespace BPrivate::media; + +#endif // _OGG_VORBIS_SEEKABLE_H diff --git a/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp b/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp index 39b4bb2f1b..903c10eddb 100644 --- a/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp +++ b/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp @@ -93,13 +93,11 @@ OggVorbisStream::OggVorbisStream(long serialno) : OggStream(serialno) { TRACE("OggVorbisStream::OggVorbisStream\n"); - fFrameCount = -1; - fDuration = -1; } OggVorbisStream::~OggVorbisStream() { - + TRACE("OggVorbisStream::~OggVorbisStream\n"); } status_t @@ -111,14 +109,14 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, ogg_packet packet; // get header packet - if (fHeaderPackets.size() < 1) { + if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } - packet = fHeaderPackets[0]; + packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { return B_ERROR; // first packet was not beginning of stream } @@ -171,7 +169,7 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, = AudioBufferSize(&format->u.encoded_audio.output); // get comment packet - if (fHeaderPackets.size() < 2) { + if (GetHeaderPackets().size() < 2) { result = GetPacket(&packet); if (result != B_OK) { return result; @@ -180,7 +178,7 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, } // get codebook packet - if (fHeaderPackets.size() < 3) { + if (GetHeaderPackets().size() < 3) { result = GetPacket(&packet); if (result != B_OK) { return result; @@ -188,49 +186,12 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, SaveHeaderPacket(packet); } - format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); - - // use cached frame count and duration for now - if (fDuration >= 0) { - *frameCount = fFrameCount; - *duration = fDuration; - return B_OK; - } - - void *chunkBuffer = 0; - int32 chunkSize = 0; - media_header mediaHeader; - if (GetNextChunk(&chunkBuffer, &chunkSize, &mediaHeader) != B_OK) { - *frameCount = 0; - *duration = 0; - return B_OK; - } - ogg_packet * chunk_packet = static_cast(chunkBuffer); - ogg_int64_t first_packet_granulepos = chunk_packet->granulepos; - ogg_int64_t last_packet_granulepos = chunk_packet->granulepos; - ogg_int64_t next_packet = chunk_packet->packetno + 1; - int64 samples = 0; - while (GetNextChunk(&chunkBuffer, &chunkSize, &mediaHeader) == B_OK) { - chunk_packet = static_cast(chunkBuffer); - if (chunk_packet->packetno != next_packet) { - // there's a hole in the data, add samples so far, and start counting again - TRACE("OggVorbisStream::GetStreamInfo: substream end\n"); - samples += last_packet_granulepos - first_packet_granulepos; - first_packet_granulepos = chunk_packet->granulepos; - } - last_packet_granulepos = chunk_packet->granulepos; - next_packet = chunk_packet->packetno + 1; - } - samples += last_packet_granulepos - first_packet_granulepos; + format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); // compute frame count and duration from sample count - *frameCount = fFrameCount = samples; - *duration = fDuration = (1000000LL * samples) / (long long)format->u.encoded_audio.output.frame_rate; - - // restore our position to the start - int64 start_frame = 0; - bigtime_t start_time = 0; - Seek(B_MEDIA_SEEK_TO_FRAME, &start_frame, &start_time); + int64 samples = 1000000; + *frameCount = samples; + *duration = (1000000LL * samples) / (long long)format->u.encoded_audio.output.frame_rate; return B_OK; } diff --git a/src/add-ons/media/plugins/ogg/OggVorbisStream.h b/src/add-ons/media/plugins/ogg/OggVorbisStream.h index 647f2cce8b..7d13c7712a 100644 --- a/src/add-ons/media/plugins/ogg/OggVorbisStream.h +++ b/src/add-ons/media/plugins/ogg/OggVorbisStream.h @@ -14,9 +14,6 @@ public: virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, media_format *format); -private: - int64 fFrameCount; - bigtime_t fDuration; }; } } // namespace BPrivate::media