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
This commit is contained in:
shatty
2004-02-08 09:53:22 +00:00
parent 8c30698233
commit b8827c3055
20 changed files with 821 additions and 487 deletions
+3 -1
View File
@@ -5,13 +5,15 @@ UsePrivateHeaders media ;
SubDirHdrs [ FDirName $(SUBDIR) libogg ] ; SubDirHdrs [ FDirName $(SUBDIR) libogg ] ;
Addon ogg : media plugins : Addon ogg : media plugins :
OggFrameInfo.cpp
OggReaderPlugin.cpp OggReaderPlugin.cpp
OggTrack.cpp
OggStream.cpp OggStream.cpp
OggSeekable.cpp
OggSpeexStream.cpp OggSpeexStream.cpp
OggTheoraStream.cpp OggTheoraStream.cpp
OggTobiasStream.cpp OggTobiasStream.cpp
OggVorbisStream.cpp OggVorbisStream.cpp
OggVorbisSeekable.cpp
: false : libogg.a : false : libogg.a
; ;
@@ -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;
}
@@ -1,26 +0,0 @@
#ifndef _OGG_FRAME_INFO_H
#define _OGG_FRAME_INFO_H
#include <SupportDefs.h>
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
+118 -98
View File
@@ -2,12 +2,15 @@
#include <string.h> #include <string.h>
#include <malloc.h> #include <malloc.h>
#include <stdint.h> #include <stdint.h>
#include <DataIO.h> #include <Autolock.h>
#include <ByteOrder.h> #include <ByteOrder.h>
#include <DataIO.h>
#include <File.h>
#include <InterfaceDefs.h> #include <InterfaceDefs.h>
#include <MediaFormats.h> #include <MediaFormats.h>
#include "OggReaderPlugin.h" #include "OggReaderPlugin.h"
#include "OggStream.h" #include "OggStream.h"
#include "OggSeekable.h"
#define TRACE_THIS 1 #define TRACE_THIS 1
#if TRACE_THIS #if TRACE_THIS
@@ -20,20 +23,22 @@ OggReader::OggReader()
{ {
TRACE("OggReader::OggReader\n"); TRACE("OggReader::OggReader\n");
ogg_sync_init(&fSync); ogg_sync_init(&fSync);
fSeekable = NULL;
fFile = NULL;
fPosition = -1;
} }
OggReader::~OggReader() OggReader::~OggReader()
{ {
TRACE("OggReader::~OggReader\n"); TRACE("OggReader::~OggReader\n");
serialno_OggStream_map::iterator i = fStreams.begin(); serialno_OggTrack_map::iterator i = fTracks.begin();
while (i != fStreams.end()) { while (i != fTracks.end()) {
serialno_OggStream_map::iterator j = i; serialno_OggTrack_map::iterator j = i;
i++; i++;
delete j->second; delete j->second;
} }
ogg_sync_clear(&fSync); ogg_sync_clear(&fSync);
fNextPosition = -1;
} }
@@ -44,16 +49,19 @@ OggReader::Copyright()
} }
status_t // the main page reading hook (stream friendly)
OggReader::GetPage(ogg_page * page, int read_size, bool short_page) 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: 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) { while (result == 0) {
char * buffer = ogg_sync_buffer(&fSync,read_size); char * buffer = ogg_sync_buffer(&fSync, read_size);
ssize_t bytes = Source()->Read(buffer,read_size); ssize_t bytes = Source()->Read(buffer, read_size);
if (bytes == 0) { if (bytes == 0) {
TRACE("OggReader::GetPage: Read: no data\n"); TRACE("OggReader::GetPage: Read: no data\n");
return B_LAST_BUFFER_ERROR; return B_LAST_BUFFER_ERROR;
@@ -62,46 +70,50 @@ retry:
TRACE("OggReader::GetPage: Read: error\n"); TRACE("OggReader::GetPage: Read: error\n");
return bytes; 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"); TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n");
return B_ERROR; return B_ERROR;
} }
result = ogg_sync_pageout(&fSync,page); result = ogg_sync_pageout(&fSync, &page);
if (short_page && (result != 1)) { if (first_page && (result != 1)) {
TRACE("OggReader::GetPage: short page not found: error\n"); TRACE("OggReader::GetPage: short first page not found: error\n");
return B_ERROR; return B_ERROR;
} }
fPosition += bytes;
} }
if (result == -1) { if (result == -1) {
TRACE("OggReader::GetPage: ogg_sync_pageout: not synced: error\n"); TRACE("OggReader::GetPage: ogg_sync_pageout: not synced: error\n");
return B_ERROR; 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"); TRACE("OggReader::GetPage: ogg_page_version: error in page encoding: error\n");
return B_ERROR; return B_ERROR;
} }
#endif long serialno = ogg_page_serialno(&page);
fNextPosition += (fSeekable ? page->header_len + page->body_len : 0); if (fTracks.find(serialno) == fTracks.end()) {
long serialno = ogg_page_serialno(page);
if (fStreams.find(serialno) == fStreams.end()) {
// this is an unknown serialno // 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"); TRACE("OggReader::GetPage: non-bos page with unknown serialno\n");
#ifdef STRICT_OGG #ifdef STRICT_OGG
return B_ERROR; return B_ERROR;
#else #else
// silently discard packets with unknown serialno // silently discard non-bos packets with unknown serialno
goto retry; goto retry;
#endif #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 // this is a beginning of stream page
ogg_stream_state stream; ogg_stream_state stream;
if (ogg_stream_init(&stream, serialno) != 0) { if (ogg_stream_init(&stream, serialno) != 0) {
TRACE("oggReader::GetPage: ogg_stream_init failed?: error\n"); TRACE("oggReader::GetPage: ogg_stream_init failed?: error\n");
return B_ERROR; 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"); TRACE("oggReader::GetPage: ogg_stream_pagein: failed: error\n");
return B_ERROR; return B_ERROR;
} }
@@ -111,51 +123,59 @@ retry:
return B_ERROR; return B_ERROR;
#endif STRICT_OGG #endif STRICT_OGG
} }
class Interface : public GetPageInterface { if (fSeekable) {
private: class Interface : public SeekableInterface {
OggReader * reader; private:
long serialno; OggReader * reader;
public: public:
Interface(OggReader * reader, long serialno) { Interface(OggReader * reader) {
this->reader = reader; this->reader = reader;
this->serialno = serialno; }
} virtual ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE) {
virtual status_t GetNextPage() { return reader->ReadPageAt(position, read_size);
ogg_page next_page; }
do { };
status_t result = reader->GetPage(&next_page); fTracks[serialno] = OggSeekable::makeOggSeekable(new Interface(this), serialno, packet);
if (result != B_OK) { } else {
return result; class Interface : public StreamInterface {
} private:
} while (ogg_page_serialno(&next_page) != serialno); OggReader * reader;
return B_OK; public:
} Interface(OggReader * reader) {
virtual status_t GetPageAt(off_t position, ogg_stream_state * stream, this->reader = reader;
int read_size = 4*B_PAGE_SIZE) { }
return reader->GetPageAt(position,stream,read_size); virtual ssize_t ReadPage() {
} return reader->ReadPage();
}; }
fStreams[serialno] = OggStream::makeOggStream(new Interface(this, serialno), serialno, packet); };
fTracks[serialno] = OggStream::makeOggStream(new Interface(this), serialno, packet);
}
fCookies.push_back(serialno); 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 ssize_t
OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size) OggReader::ReadPageAt(off_t position, int read_size)
{ {
TRACE("OggReader::GetPageAt %llu\n", position); TRACE("OggReader::GetPageAt %llu\n", position);
if (!fSeekable) { if (!fSeekable) {
return B_ERROR; return B_ERROR;
} }
/*
ogg_sync_state sync; ogg_sync_state sync;
ogg_sync_init(&sync); ogg_sync_init(&sync);
ogg_page page; ogg_page page;
int result; int result;
while ((result = ogg_sync_pageout(&sync,&page)) == 0) { while ((result = ogg_sync_pageout(&sync, &page)) == 0) {
char * buffer = ogg_sync_buffer(&sync,read_size); char * buffer = ogg_sync_buffer(&sync, read_size);
ssize_t bytes = fSeekable->ReadAt(position,buffer,read_size); ssize_t bytes = fSeekable->ReadAt(position, buffer, read_size);
if (bytes == 0) { if (bytes == 0) {
TRACE("OggReader::GetPage: Read: no data\n"); TRACE("OggReader::GetPage: Read: no data\n");
return B_LAST_BUFFER_ERROR; return B_LAST_BUFFER_ERROR;
@@ -165,7 +185,7 @@ OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size)
return bytes; return bytes;
} }
position += 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"); TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n");
return B_ERROR; return B_ERROR;
} }
@@ -185,6 +205,7 @@ OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size)
return B_ERROR; return B_ERROR;
} }
ogg_sync_clear(&sync); ogg_sync_clear(&sync);
*/
return B_OK; return B_OK;
} }
@@ -198,7 +219,7 @@ get_seekable(BDataIO * data)
return 0; return 0;
} }
// first try to get our current location // 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) { if (current < 0) {
return 0; return 0;
} }
@@ -207,58 +228,57 @@ get_seekable(BDataIO * data)
return 0; return 0;
} }
// next try to seek to our current location (absolutely) // 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; return 0;
} }
// next try to seek to the start of the stream (absolutely) // 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; return 0;
} }
// then seek back to where we started // then seek back to where we started
if (seekable->Seek(current,SEEK_SET) < 0) { if (seekable->Seek(current, SEEK_SET) < 0) {
// really screwed // really screwed
return 0; return 0;
} }
return seekable; 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<BFile *>(data);
if (file == 0) {
return 0;
}
if (file->InitCheck() != B_OK) {
return 0;
}
return file;
}
status_t status_t
OggReader::Sniff(int32 *streamCount) OggReader::Sniff(int32 *streamCount)
{ {
TRACE("OggReader::Sniff\n"); TRACE("OggReader::Sniff\n");
#ifdef STRICT_OGG #ifdef STRICT_OGG
bool short_page = true; bool first_page = true;
#else #else
bool short_page = false; bool first_page = false;
#endif #endif
fSeekable = get_seekable(Source()); // fSeekable = get_seekable(Source());
fFile = get_file(Source());
fNextPosition = (fSeekable ? fSeekable->Position() : -1); ssize_t bytes = ReadPage(first_page);
if (bytes < 0) {
ogg_page page; return bytes;
if (GetPage(&page,4096,short_page) != B_OK) {
return B_ERROR;
} }
uint count = 1;
// page sanity checks while (count++ == fCookies.size()) {
if (ogg_page_version(&page) != 0) { ssize_t bytes = ReadPage();
TRACE("OggReader::Sniff: ogg_page_version: error in page encoding: not ogg\n"); if (bytes < 0) {
return B_ERROR; return bytes;
}
#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;
} }
} }
*streamCount = fCookies.size(); *streamCount = fCookies.size();
@@ -292,9 +312,9 @@ OggReader::AllocateCookie(int32 streamNumber, void **cookie)
TRACE("OggReader::AllocateCookie: invalid streamNumber: bail\n"); TRACE("OggReader::AllocateCookie: invalid streamNumber: bail\n");
return B_ERROR; return B_ERROR;
} }
OggStream * stream = fStreams[fCookies[streamNumber]]; OggTrack * track = fTracks[fCookies[streamNumber]];
// store the cookie // store the cookie
*cookie = stream; *cookie = track;
return B_OK; return B_OK;
} }
@@ -314,8 +334,8 @@ OggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
TRACE("OggReader::GetStreamInfo\n"); TRACE("OggReader::GetStreamInfo\n");
*infoBuffer = 0; *infoBuffer = 0;
*infoSize = 0; *infoSize = 0;
OggStream * stream = static_cast<OggStream*>(cookie); OggTrack * track = static_cast<OggTrack*>(cookie);
return stream->GetStreamInfo(frameCount,duration,format); return track->GetStreamInfo(frameCount, duration, format);
} }
@@ -324,9 +344,9 @@ OggReader::Seek(void *cookie,
uint32 seekTo, uint32 seekTo,
int64 *frame, bigtime_t *time) int64 *frame, bigtime_t *time)
{ {
TRACE("OggReader::Seek to %lld : %lld\n",*frame,*time); TRACE("OggReader::Seek to %lld : %lld\n", *frame, *time);
OggStream * stream = static_cast<OggStream*>(cookie); OggTrack * track = static_cast<OggTrack*>(cookie);
return stream->Seek(seekTo,frame,time); return track->Seek(seekTo, frame, time);
} }
@@ -336,8 +356,8 @@ OggReader::GetNextChunk(void *cookie,
media_header *mediaHeader) media_header *mediaHeader)
{ {
// TRACE("OggReader::GetNextChunk\n"); // TRACE("OggReader::GetNextChunk\n");
OggStream * stream = static_cast<OggStream*>(cookie); OggTrack * track = static_cast<OggTrack*>(cookie);
return stream->GetNextChunk(chunkBuffer,chunkSize,mediaHeader); return track->GetNextChunk(chunkBuffer, chunkSize, mediaHeader);
} }
+24 -25
View File
@@ -8,9 +8,9 @@
namespace BPrivate { namespace media { namespace BPrivate { namespace media {
class OggStream; class OggTrack;
typedef std::map<long,OggStream*> serialno_OggStream_map; typedef std::map<long,OggTrack*> serialno_OggTrack_map;
typedef std::vector<long> serialno_vector; typedef std::vector<long> serialno_vector;
class OggReader : public Reader class OggReader : public Reader
@@ -28,36 +28,35 @@ public:
status_t AllocateCookie(int32 streamNumber, void **cookie); status_t AllocateCookie(int32 streamNumber, void **cookie);
status_t FreeCookie(void *cookie); status_t FreeCookie(void *cookie);
// delegated to OggStream // delegated to OggTrack
status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
media_format *format, void **infoBuffer, int32 *infoSize); media_format *format, void **infoBuffer, int32 *infoSize);
status_t Seek(void *cookie, status_t Seek(void *cookie, uint32 seekTo, int64 *frame, bigtime_t *time);
uint32 seekTo, status_t GetNextChunk(void *cookie, void **chunkBuffer, int32 *chunkSize,
int64 *frame, bigtime_t *time);
status_t GetNextChunk(void *cookie,
void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader); 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: 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: public:
virtual status_t GetNextPage() = 0; virtual ssize_t ReadPage() = 0;
virtual status_t GetPageAt(off_t position, ogg_stream_state * stream, };
int read_size = 4*B_PAGE_SIZE) = 0; class SeekableInterface {
public:
virtual ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE) = 0;
}; };
}; };
@@ -0,0 +1,174 @@
#include "OggSeekable.h"
#include "OggVorbisSeekable.h"
#include <Autolock.h>
#include <stdio.h>
#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;
}
@@ -0,0 +1,45 @@
#ifndef _OGG_SEEKABLE_H
#define _OGG_SEEKABLE_H
#include "OggTrack.h"
#include "OggReaderPlugin.h"
#include <map>
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
@@ -70,14 +70,14 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
ogg_packet packet; ogg_packet packet;
// get header packet // get header packet
if (fHeaderPackets.size() < 1) { if (GetHeaderPackets().size() < 1) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
} }
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
packet = fHeaderPackets[0]; packet = GetHeaderPackets()[0];
if (!packet.b_o_s) { if (!packet.b_o_s) {
return B_ERROR; // first packet was not beginning of stream 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; format->u.encoded_audio.output.buffer_size = buffer_size;
// get comment packet // get comment packet
if (fHeaderPackets.size() < 2) { if (GetHeaderPackets().size() < 2) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
@@ -131,7 +131,7 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
} }
// get extra headers // get extra headers
while ((signed)fHeaderPackets.size() < header->extra_headers) { while ((signed)GetHeaderPackets().size() < header->extra_headers) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
@@ -139,8 +139,23 @@ OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
*duration = 100000000; *duration = 100000000;
*frameCount = 60000; *frameCount = 60000;
return B_OK; 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;
}
@@ -14,6 +14,9 @@ public:
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
media_format *format); media_format *format);
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader);
}; };
} } // namespace BPrivate::media } } // namespace BPrivate::media
+37 -175
View File
@@ -18,7 +18,7 @@
*/ */
/* static */ OggStream * /* static */ OggStream *
OggStream::makeOggStream(OggReader::GetPageInterface * interface, OggStream::makeOggStream(OggReader::StreamInterface * interface,
long serialno, const ogg_packet & packet) long serialno, const ogg_packet & packet)
{ {
TRACE("OggStream::makeOggStream\n"); 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 generic functions
*/ */
OggStream::OggStream(long serialno) OggStream::OggStream(long serialno)
: OggTrack(serialno)
{ {
TRACE("OggStream::OggStream\n"); TRACE("OggStream::OggStream\n");
fCurrentFrame = 0; fCurrentFrame = 0;
fCurrentTime = 0; fCurrentTime = 0;
this->fSerialno = serialno;
ogg_sync_init(&fSync); ogg_sync_init(&fSync);
ogg_stream_init(&fSeekStreamState,serialno); ogg_stream_init(&fStreamState,serialno);
fCurrentPage = 0;
fPacketOnCurrentPage = 0;
fCurrentPacket = 0;
ogg_stream_init(&fEndStreamState,serialno);
fEndPage = 0;
fPacketOnEndPage = 0;
fEndPacket = 0;
} }
OggStream::~OggStream() OggStream::~OggStream()
{ {
// free internal header packet storage
std::vector<ogg_packet>::iterator iter = fHeaderPackets.begin();
while (iter != fHeaderPackets.end()) {
delete iter->packet;
iter++;
}
// free internal stream state storage // free internal stream state storage
ogg_sync_clear(&fSync); ogg_sync_clear(&fSync);
ogg_stream_clear(&fSeekStreamState); ogg_stream_clear(&fStreamState);
ogg_stream_clear(&fEndStreamState);
}
long
OggStream::GetSerial() const
{
return fSerialno;
} }
status_t status_t
OggStream::AddPage(off_t position, ogg_page * page) OggStream::AddPage(off_t position, const ogg_page & page)
{ {
TRACE("OggStream::AddPage"); TRACE("OggStream::AddPage\n");
if (position >= 0) {
TRACE(" %lld", position);
fPagePositions.push_back(position);
}
TRACE("\n");
BAutolock autolock(fSyncLock); BAutolock autolock(fSyncLock);
char * buffer; char * buffer;
buffer = ogg_sync_buffer(&fSync, page->header_len); // copy the header to our local sync
memcpy(buffer,page->header, page->header_len); buffer = ogg_sync_buffer(&fSync, page.header_len);
ogg_sync_wrote(&fSync, page->header_len); memcpy(buffer, page.header, page.header_len);
buffer = ogg_sync_buffer(&fSync, page->body_len); ogg_sync_wrote(&fSync, page.header_len);
memcpy(buffer,page->body, page->body_len); // copy the body to our local sync
ogg_sync_wrote(&fSync, page->body_len); 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; return B_OK;
} }
status_t status_t
OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
media_format *format) media_format *format)
{ {
TRACE("OggStream::GetStreamInfo\n"); TRACE("OggStream::GetStreamInfo\n");
status_t result = B_OK; status_t result = B_OK;
ogg_packet packet; ogg_packet packet;
if (fHeaderPackets.size() < 1) { if (GetHeaderPackets().size() < 1) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
TRACE("OggStream::GetStreamInfo failed to get header packet\n"); TRACE("OggStream::GetStreamInfo failed to get header packet\n");
@@ -130,7 +95,7 @@ OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
} }
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
packet = fHeaderPackets[0]; packet = GetHeaderPackets()[0];
if (!packet.b_o_s) { if (!packet.b_o_s) {
TRACE("OggStream::GetStreamInfo failed : not beginning of stream\n"); TRACE("OggStream::GetStreamInfo failed : not beginning of stream\n");
return B_ERROR; // first packet was not beginning of stream 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; format->user_data_type = B_CODEC_TYPE_INFO;
strncpy((char*)format->user_data, (char*)(&packet), 4); strncpy((char*)format->user_data, (char*)(&packet), 4);
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
*duration = 140000000; *duration = 140000000;
*frameCount = 60000; *frameCount = 60000;
return B_OK; return B_OK;
} }
status_t // the default chunk is an ogg packet
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;
}
status_t status_t
OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader) media_header *mediaHeader)
{ {
if (fCurrentPacket - fHeaderPackets.size() == fOggFrameInfos.size()) {
OggFrameInfo info(fEndPage,fPacketOnEndPage,fEndPacket);
fOggFrameInfos.push_back(info);
}
status_t result = GetPacket(&fChunkPacket); status_t result = GetPacket(&fChunkPacket);
if (result != B_OK) { if (result != B_OK) {
TRACE("OggStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); TRACE("OggStream::GetNextChunk failed: GetPacket = %s\n", strerror(result));
@@ -236,82 +149,31 @@ OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
status_t status_t
OggStream::GetPacket(ogg_packet * packet) OggStream::GetPacket(ogg_packet * packet)
{ {
if (fCurrentPacket >= fEndPacket) { // at the end, pull the packet
// at the end, pull the packet while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
uint8 pageno = fEndPage; BAutolock autolock(fSyncLock);
while (ogg_stream_packetpeek(&fEndStreamState, NULL) != 1) { int result;
BAutolock autolock(fSyncLock); ogg_page page;
int result; while ((result = ogg_sync_pageout(&fSync,&page)) == 0) {
ogg_page page; result = fReaderInterface->ReadPage();
while ((result = ogg_sync_pageout(&fSync,&page)) == 0) { if (result < 0) {
status_t result = fReaderInterface->GetNextPage(); TRACE("OggStream::GetPacket: GetNextPage = %s\n", strerror(result));
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));
return result; return result;
} }
} }
if (ogg_stream_packetout(&fSeekStreamState, packet) != 1) { if (result == -1) {
TRACE("OggStream::GetPacket: ogg_stream_packetout failed in the middle\n"); TRACE("OggStream::GetPacket: ogg_sync_pageout: not synced??\n");
return B_ERROR; return B_ERROR;
} }
fCurrentPacket++; if (ogg_stream_pagein(&fStreamState,&page) != 0) {
if (page != fCurrentPage) { TRACE("OggStream::GetPacket: ogg_stream_pagein: failed??\n");
fPacketOnCurrentPage = 0; return B_ERROR;
} else {
fPacketOnCurrentPage++;
} }
} }
if (ogg_stream_packetout(&fStreamState, packet) != 1) {
TRACE("OggStream::GetPacket: ogg_stream_packetout failed\n");
return B_ERROR;
}
return B_OK; 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);
}
+13 -35
View File
@@ -1,67 +1,45 @@
#ifndef _OGG_STREAM_H #ifndef _OGG_STREAM_H
#define _OGG_STREAM_H #define _OGG_STREAM_H
#include <SupportDefs.h> #include "OggTrack.h"
#include <MediaDefs.h>
#include "ogg/ogg.h"
#include "OggReaderPlugin.h" #include "OggReaderPlugin.h"
#include "OggFrameInfo.h"
#include <map> #include <map>
#include <vector>
namespace BPrivate { namespace media { namespace BPrivate { namespace media {
class OggStream { class OggStream : public OggTrack {
public: public:
static OggStream * makeOggStream(OggReader::GetPageInterface * interface, static OggStream * makeOggStream(OggReader::StreamInterface * interface,
long serialno, const ogg_packet & packet); 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: protected:
static bool findIdentifier(const ogg_packet & packet,
const char * id, uint pos);
OggStream(long serialno); OggStream(long serialno);
public: public:
virtual ~OggStream(); virtual ~OggStream();
long GetSerial() const;
// reader push input function // reader push input function
status_t AddPage(off_t position, ogg_page * page); status_t AddPage(off_t position, const 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);
protected: protected:
// subclass pull input function // subclass pull input function
status_t GetPacket(ogg_packet * packet); status_t GetPacket(ogg_packet * packet);
ogg_packet fChunkPacket;
// GetStreamInfo helpers
void SaveHeaderPacket(ogg_packet packet);
std::vector<ogg_packet> fHeaderPackets;
protected: protected:
int64 fCurrentFrame; int64 fCurrentFrame;
bigtime_t fCurrentTime; bigtime_t fCurrentTime;
private: private:
long fSerialno;
std::vector<off_t> fPagePositions;
std::vector<OggFrameInfo> fOggFrameInfos;
ogg_sync_state fSync; ogg_sync_state fSync;
BLocker fSyncLock; BLocker fSyncLock;
ogg_stream_state fSeekStreamState; ogg_stream_state fStreamState;
uint fCurrentPage; OggReader::StreamInterface * fReaderInterface;
uint fPacketOnCurrentPage;
uint fCurrentPacket;
ogg_stream_state fEndStreamState;
uint fEndPage;
uint fPacketOnEndPage;
uint fEndPacket;
OggReader::GetPageInterface * fReaderInterface;
ogg_packet fChunkPacket;
}; };
} } // namespace BPrivate::media } } // namespace BPrivate::media
@@ -152,14 +152,14 @@ OggTheoraStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
ogg_packet packet; ogg_packet packet;
// get header packet // get header packet
if (fHeaderPackets.size() < 1) { if (GetHeaderPackets().size() < 1) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
} }
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
packet = fHeaderPackets[0]; packet = GetHeaderPackets()[0];
if (!packet.b_o_s) { if (!packet.b_o_s) {
return B_ERROR; // first packet was not beginning of stream 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; format->u.encoded_video.output.display.line_count = info.frame_height;
// TODO: wring more info out of the headers // TODO: wring more info out of the headers
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
*duration = 80000000; *duration = 80000000;
*frameCount = 60000; *frameCount = 60000;
return B_OK; return B_OK;
@@ -73,7 +73,7 @@ OggTobiasStream::OggTobiasStream(long serialno)
OggTobiasStream::~OggTobiasStream() OggTobiasStream::~OggTobiasStream()
{ {
TRACE("OggTobiasStream::~OggTobiasStream\n");
} }
status_t status_t
@@ -85,14 +85,14 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
ogg_packet packet; ogg_packet packet;
// get header packet // get header packet
if (fHeaderPackets.size() < 1) { if (GetHeaderPackets().size() < 1) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
} }
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
packet = fHeaderPackets[0]; packet = GetHeaderPackets()[0];
if (!packet.b_o_s) { if (!packet.b_o_s) {
return B_ERROR; // first packet was not beginning of stream 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 // TODO: wring more info out of the headers
// get comment packet // get comment packet
if (fHeaderPackets.size() < 2) { if (GetHeaderPackets().size() < 2) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
@@ -136,7 +136,7 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
*duration = 80000000; *duration = 80000000;
*frameCount = 60000; *frameCount = 60000;
return B_OK; return B_OK;
@@ -144,10 +144,14 @@ OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
status_t status_t
OggTobiasStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, OggTobiasStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader) media_header *mediaHeader)
{ {
OggStream::GetNextChunk(chunkBuffer, chunkSize, mediaHeader); status_t result = GetPacket(&fChunkPacket);
*chunkSize = ((ogg_packet*)chunkBuffer)->bytes; if (result != B_OK) {
*chunkBuffer = ((ogg_packet*)chunkBuffer)->packet; TRACE("OggTobiasStream::GetNextChunk failed: GetPacket = %s\n", strerror(result));
return result;
}
*chunkBuffer = fChunkPacket.packet;
*chunkSize = fChunkPacket.bytes;
return B_OK; return B_OK;
} }
@@ -16,8 +16,6 @@ public:
media_format *format); media_format *format);
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader); media_header *mediaHeader);
private:
ogg_packet fChunkPacket;
}; };
@@ -0,0 +1,95 @@
#include "OggTrack.h"
#include <stdio.h>
#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<ogg_packet>::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<ogg_packet> &
OggTrack::GetHeaderPackets()
{
return fHeaderPackets;
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef _OGG_TRACK_H
#define _OGG_TRACK_H
#include <SupportDefs.h>
#include <MediaDefs.h>
#include "ogg/ogg.h"
#include <vector>
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<ogg_packet> & GetHeaderPackets();
private:
std::vector<ogg_packet> fHeaderPackets;
long fSerialno;
};
} } // namespace BPrivate::media
using namespace BPrivate::media;
#endif // _OGG_TRACK_H
@@ -0,0 +1,197 @@
#include "OggVorbisFormats.h"
#include "OggVorbisSeekable.h"
#include <stdio.h>
#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;
}
@@ -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
@@ -93,13 +93,11 @@ OggVorbisStream::OggVorbisStream(long serialno)
: OggStream(serialno) : OggStream(serialno)
{ {
TRACE("OggVorbisStream::OggVorbisStream\n"); TRACE("OggVorbisStream::OggVorbisStream\n");
fFrameCount = -1;
fDuration = -1;
} }
OggVorbisStream::~OggVorbisStream() OggVorbisStream::~OggVorbisStream()
{ {
TRACE("OggVorbisStream::~OggVorbisStream\n");
} }
status_t status_t
@@ -111,14 +109,14 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
ogg_packet packet; ogg_packet packet;
// get header packet // get header packet
if (fHeaderPackets.size() < 1) { if (GetHeaderPackets().size() < 1) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
} }
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
packet = fHeaderPackets[0]; packet = GetHeaderPackets()[0];
if (!packet.b_o_s) { if (!packet.b_o_s) {
return B_ERROR; // first packet was not beginning of stream 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); = AudioBufferSize(&format->u.encoded_audio.output);
// get comment packet // get comment packet
if (fHeaderPackets.size() < 2) { if (GetHeaderPackets().size() < 2) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
@@ -180,7 +178,7 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
} }
// get codebook packet // get codebook packet
if (fHeaderPackets.size() < 3) { if (GetHeaderPackets().size() < 3) {
result = GetPacket(&packet); result = GetPacket(&packet);
if (result != B_OK) { if (result != B_OK) {
return result; return result;
@@ -188,49 +186,12 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
SaveHeaderPacket(packet); SaveHeaderPacket(packet);
} }
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets)); format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
// 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<ogg_packet *>(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<ogg_packet *>(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;
// compute frame count and duration from sample count // compute frame count and duration from sample count
*frameCount = fFrameCount = samples; int64 samples = 1000000;
*duration = fDuration = (1000000LL * samples) / (long long)format->u.encoded_audio.output.frame_rate; *frameCount = samples;
*duration = (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);
return B_OK; return B_OK;
} }
@@ -14,9 +14,6 @@ public:
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
media_format *format); media_format *format);
private:
int64 fFrameCount;
bigtime_t fDuration;
}; };
} } // namespace BPrivate::media } } // namespace BPrivate::media