seekable version 0 [not seekable lol]
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6544 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -10,8 +10,10 @@ Addon ogg : media plugins :
|
|||||||
OggStream.cpp
|
OggStream.cpp
|
||||||
OggSeekable.cpp
|
OggSeekable.cpp
|
||||||
OggSpeexStream.cpp
|
OggSpeexStream.cpp
|
||||||
|
OggSpeexSeekable.cpp
|
||||||
OggTheoraStream.cpp
|
OggTheoraStream.cpp
|
||||||
OggTobiasStream.cpp
|
OggTobiasStream.cpp
|
||||||
|
OggTobiasSeekable.cpp
|
||||||
OggVorbisStream.cpp
|
OggVorbisStream.cpp
|
||||||
OggVorbisSeekable.cpp
|
OggVorbisSeekable.cpp
|
||||||
: false : libogg.a
|
: false : libogg.a
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ OggReader::OggReader()
|
|||||||
ogg_sync_init(&fSync);
|
ogg_sync_init(&fSync);
|
||||||
fSeekable = NULL;
|
fSeekable = NULL;
|
||||||
fFile = NULL;
|
fFile = NULL;
|
||||||
fPosition = -1;
|
fPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -79,7 +79,6 @@ retry:
|
|||||||
TRACE("OggReader::GetPage: short first 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");
|
||||||
@@ -124,18 +123,7 @@ retry:
|
|||||||
#endif STRICT_OGG
|
#endif STRICT_OGG
|
||||||
}
|
}
|
||||||
if (fSeekable) {
|
if (fSeekable) {
|
||||||
class Interface : public SeekableInterface {
|
fTracks[serialno] = OggSeekable::makeOggSeekable(fSeekable, &fSeekableLock, serialno, packet);
|
||||||
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 {
|
} else {
|
||||||
class Interface : public StreamInterface {
|
class Interface : public StreamInterface {
|
||||||
private:
|
private:
|
||||||
@@ -152,63 +140,15 @@ retry:
|
|||||||
}
|
}
|
||||||
fCookies.push_back(serialno);
|
fCookies.push_back(serialno);
|
||||||
}
|
}
|
||||||
off_t pageStart = fPosition - page.header_len - page.body_len;
|
status_t status = fTracks[serialno]->AddPage(fPosition, page);
|
||||||
status_t status = fTracks[serialno]->AddPage(pageStart, page);
|
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
fPosition += page.header_len + page.body_len;
|
||||||
return page.header_len + page.body_len;
|
return page.header_len + page.body_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (bytes == 0) {
|
|
||||||
TRACE("OggReader::GetPage: Read: no data\n");
|
|
||||||
return B_LAST_BUFFER_ERROR;
|
|
||||||
}
|
|
||||||
if (bytes < 0) {
|
|
||||||
TRACE("OggReader::GetPage: Read: error\n");
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
position += bytes;
|
|
||||||
if (ogg_sync_wrote(&sync, bytes) != 0) {
|
|
||||||
TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n");
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result == -1) {
|
|
||||||
TRACE("OggReader::GetPageAt: ogg_sync_pageout: not synced??\n");
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
#ifdef STRICT_OGG
|
|
||||||
if (ogg_page_version(page) != 0) {
|
|
||||||
TRACE("OggReader::GetPageAt: ogg_page_version: error in page encoding??\n");
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ogg_stream_pagein(stream, &page) != 0) {
|
|
||||||
TRACE("oggReader::GetPageAt: ogg_stream_pagein: failed??\n");
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
ogg_sync_clear(&sync);
|
|
||||||
*/
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BPositionIO *
|
static BPositionIO *
|
||||||
get_seekable(BDataIO * data)
|
get_seekable(BDataIO * data)
|
||||||
{
|
{
|
||||||
@@ -267,7 +207,7 @@ OggReader::Sniff(int32 *streamCount)
|
|||||||
#else
|
#else
|
||||||
bool first_page = false;
|
bool first_page = false;
|
||||||
#endif
|
#endif
|
||||||
// fSeekable = get_seekable(Source());
|
fSeekable = get_seekable(Source());
|
||||||
fFile = get_file(Source());
|
fFile = get_file(Source());
|
||||||
|
|
||||||
ssize_t bytes = ReadPage(first_page);
|
ssize_t bytes = ReadPage(first_page);
|
||||||
|
|||||||
@@ -42,22 +42,18 @@ private:
|
|||||||
serialno_vector fCookies;
|
serialno_vector fCookies;
|
||||||
|
|
||||||
BPositionIO * fSeekable;
|
BPositionIO * fSeekable;
|
||||||
|
BLocker fSeekableLock;
|
||||||
BFile * fFile;
|
BFile * fFile;
|
||||||
|
|
||||||
off_t fPosition;
|
off_t fPosition;
|
||||||
|
|
||||||
// interfaces for OggTracks
|
// interface for OggStreams
|
||||||
ssize_t ReadPage(bool first_page = false);
|
ssize_t ReadPage(bool first_page = false);
|
||||||
ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE);
|
|
||||||
|
|
||||||
class StreamInterface {
|
class StreamInterface {
|
||||||
public:
|
public:
|
||||||
virtual ssize_t ReadPage() = 0;
|
virtual ssize_t ReadPage() = 0;
|
||||||
};
|
};
|
||||||
class SeekableInterface {
|
|
||||||
public:
|
|
||||||
virtual ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE) = 0;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OggReaderPlugin : public ReaderPlugin
|
class OggReaderPlugin : public ReaderPlugin
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "OggSeekable.h"
|
#include "OggSeekable.h"
|
||||||
|
#include "OggSpeexSeekable.h"
|
||||||
|
#include "OggTobiasSeekable.h"
|
||||||
#include "OggVorbisSeekable.h"
|
#include "OggVorbisSeekable.h"
|
||||||
#include <Autolock.h>
|
#include <Autolock.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -15,23 +17,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* static */ OggSeekable *
|
/* static */ OggSeekable *
|
||||||
OggSeekable::makeOggSeekable(OggReader::SeekableInterface * interface,
|
OggSeekable::makeOggSeekable(BPositionIO * positionIO, BLocker * positionLock,
|
||||||
long serialno, const ogg_packet & packet)
|
long serialno, const ogg_packet & packet)
|
||||||
{
|
{
|
||||||
TRACE("OggSeekable::makeOggSeekable\n");
|
TRACE("OggSeekable::makeOggSeekable\n");
|
||||||
OggSeekable * stream;
|
OggSeekable * stream;
|
||||||
if (OggVorbisSeekable::IsValidHeader(packet)) {
|
if (OggVorbisSeekable::IsValidHeader(packet)) {
|
||||||
stream = new OggVorbisSeekable(serialno);
|
stream = new OggVorbisSeekable(serialno);
|
||||||
// } else if (OggTobiasSeekable::IsValidHeader(packet)) {
|
} else if (OggTobiasSeekable::IsValidHeader(packet)) {
|
||||||
// stream = new OggTobiasSeekable(serialno);
|
stream = new OggTobiasSeekable(serialno);
|
||||||
// } else if (OggSpeexSeekable::IsValidHeader(packet)) {
|
} else if (OggSpeexSeekable::IsValidHeader(packet)) {
|
||||||
// stream = new OggSpeexSeekable(serialno);
|
stream = new OggSpeexSeekable(serialno);
|
||||||
// } else if (OggTheoraSeekable::IsValidHeader(packet)) {
|
// } else if (OggTheoraSeekable::IsValidHeader(packet)) {
|
||||||
// stream = new OggTheoraSeekable(serialno);
|
// stream = new OggTheoraSeekable(serialno);
|
||||||
} else {
|
} else {
|
||||||
stream = new OggSeekable(serialno);
|
stream = new OggSeekable(serialno);
|
||||||
}
|
}
|
||||||
stream->fReaderInterface = interface;
|
stream->fPositionIO = positionIO;
|
||||||
|
stream->fPositionLock = positionLock;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,11 +60,18 @@ OggSeekable::~OggSeekable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
insert_position(std::vector<off_t> & positions, off_t position)
|
||||||
|
{
|
||||||
|
positions.push_back(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
OggSeekable::AddPage(off_t position, const ogg_page & page)
|
OggSeekable::AddPage(off_t position, const ogg_page & page)
|
||||||
{
|
{
|
||||||
TRACE("OggSeekable::AddPage\n");
|
TRACE("OggSeekable::AddPage\n");
|
||||||
BAutolock autolock(fSyncLock);
|
insert_position(fPagePositions, position);
|
||||||
char * buffer;
|
char * buffer;
|
||||||
// copy the header to our local sync
|
// copy the header to our local sync
|
||||||
buffer = ogg_sync_buffer(&fSync, page.header_len);
|
buffer = ogg_sync_buffer(&fSync, page.header_len);
|
||||||
@@ -71,6 +81,69 @@ OggSeekable::AddPage(off_t position, const ogg_page & page)
|
|||||||
buffer = ogg_sync_buffer(&fSync, page.body_len);
|
buffer = ogg_sync_buffer(&fSync, page.body_len);
|
||||||
memcpy(buffer, page.body, page.body_len);
|
memcpy(buffer, page.body, page.body_len);
|
||||||
ogg_sync_wrote(&fSync, page.body_len);
|
ogg_sync_wrote(&fSync, page.body_len);
|
||||||
|
fPosition = position + page.header_len + page.body_len;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggSeekable::ReadPage(ogg_page * page, int read_size)
|
||||||
|
{
|
||||||
|
TRACE("OggSeekable::ReadPage (%llu)\n", fPosition);
|
||||||
|
BAutolock autolock(fPositionLock);
|
||||||
|
// align to page boundary
|
||||||
|
int offset;
|
||||||
|
while ((offset = ogg_sync_pageseek(&fSync, page)) <= 0) {
|
||||||
|
if (offset == 0) {
|
||||||
|
char * buffer = ogg_sync_buffer(&fSync, read_size);
|
||||||
|
ssize_t bytes = fPositionIO->ReadAt(fPosition, buffer, read_size);
|
||||||
|
if (bytes == 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ReadAt: no data\n");
|
||||||
|
return B_LAST_BUFFER_ERROR;
|
||||||
|
}
|
||||||
|
if (bytes < 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ReadAt: error\n");
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
fPosition += bytes;
|
||||||
|
if (ogg_sync_wrote(&fSync, bytes) != 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ogg_sync_wrote failed?: error\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// repeat until this is one of our pages
|
||||||
|
while (ogg_page_serialno(page) != GetSerial()) {
|
||||||
|
int result;
|
||||||
|
// read the page
|
||||||
|
while ((result = ogg_sync_pageout(&fSync, page)) == 0) {
|
||||||
|
char * buffer = ogg_sync_buffer(&fSync, read_size);
|
||||||
|
ssize_t bytes = fPositionIO->ReadAt(fPosition, buffer, read_size);
|
||||||
|
if (bytes == 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ReadAt 2: no data\n");
|
||||||
|
return B_LAST_BUFFER_ERROR;
|
||||||
|
}
|
||||||
|
if (bytes < 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ReadAt 2: error\n");
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
fPosition += bytes;
|
||||||
|
if (ogg_sync_wrote(&fSync, bytes) != 0) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ogg_sync_wrote 2 failed?: error\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
|
TRACE("OggReader::ReadPageAt: ogg_sync_pageout: not synced??\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
if (ogg_page_version(page) != 0) {
|
||||||
|
TRACE("OggReader::GetPageAt: ogg_page_version: error in page encoding??\n");
|
||||||
|
#ifdef STRICT_OGG
|
||||||
|
return B_ERROR;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,21 +219,23 @@ OggSeekable::GetPacket(ogg_packet * packet)
|
|||||||
{
|
{
|
||||||
// at the end, pull the packet
|
// at the end, pull the packet
|
||||||
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
||||||
BAutolock autolock(fSyncLock);
|
|
||||||
int result;
|
|
||||||
ogg_page page;
|
ogg_page page;
|
||||||
while ((result = ogg_sync_pageout(&fSync,&page)) == 0) {
|
do {
|
||||||
status_t result = B_ERROR; // fReaderInterface->ReadPage();
|
int result = ogg_sync_pageout(&fSync, &page);
|
||||||
if (result != B_OK) {
|
if (result == 0) {
|
||||||
TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(result));
|
status_t status = ReadPage(&page);
|
||||||
return result;
|
if (status != B_OK) {
|
||||||
|
TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (result == -1) {
|
||||||
if (result == -1) {
|
TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n");
|
||||||
TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n");
|
return B_ERROR;
|
||||||
return B_ERROR;
|
}
|
||||||
}
|
} while (ogg_page_serialno(&page) != GetSerial());
|
||||||
if (ogg_stream_pagein(&fStreamState,&page) != 0) {
|
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
|
||||||
|
debugger("huh?");
|
||||||
TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n");
|
TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
#include "OggTrack.h"
|
#include "OggTrack.h"
|
||||||
#include "OggReaderPlugin.h"
|
#include "OggReaderPlugin.h"
|
||||||
#include <map>
|
#include <vector>
|
||||||
|
|
||||||
namespace BPrivate { namespace media {
|
namespace BPrivate { namespace media {
|
||||||
|
|
||||||
class OggSeekable : public OggTrack {
|
class OggSeekable : public OggTrack {
|
||||||
public:
|
public:
|
||||||
static OggSeekable * makeOggSeekable(OggReader::SeekableInterface * interface,
|
static OggSeekable * makeOggSeekable(BPositionIO * positionIO, BLocker * positionLock,
|
||||||
long serialno, const ogg_packet & packet);
|
long serialno, const ogg_packet & packet);
|
||||||
|
|
||||||
// interface for OggReader
|
// interface for OggReader
|
||||||
@@ -27,15 +27,25 @@ public:
|
|||||||
status_t AddPage(off_t position, const ogg_page & page);
|
status_t AddPage(off_t position, const ogg_page & page);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
status_t ReadPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE);
|
||||||
|
|
||||||
// subclass pull input function
|
// subclass pull input function
|
||||||
status_t GetPacket(ogg_packet * packet);
|
status_t GetPacket(ogg_packet * packet);
|
||||||
|
ogg_packet fChunkPacket;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int64 fCurrentFrame;
|
||||||
|
bigtime_t fCurrentTime;
|
||||||
|
|
||||||
|
private:
|
||||||
|
off_t fPosition;
|
||||||
|
std::vector<off_t> fPagePositions;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ogg_sync_state fSync;
|
ogg_sync_state fSync;
|
||||||
BLocker fSyncLock;
|
|
||||||
ogg_stream_state fStreamState;
|
ogg_stream_state fStreamState;
|
||||||
OggReader::SeekableInterface * fReaderInterface;
|
BPositionIO * fPositionIO;
|
||||||
ogg_packet fChunkPacket;
|
BLocker fPositionLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // namespace BPrivate::media
|
} } // namespace BPrivate::media
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
#include "OggSpeexFormats.h"
|
||||||
|
#include "OggSpeexSeekable.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* speex header from libspeex/speex_header.h
|
||||||
|
* also documented at http://www.speex.org/manual/node7.html#SECTION00073000000000000000
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct SpeexHeader {
|
||||||
|
char speex_string[8]; /**< Identifies a Speex bit-stream, always set to "Speex " */
|
||||||
|
char speex_version[20]; /**< Speex version */
|
||||||
|
int speex_version_id; /**< Version for Speex (for checking compatibility) */
|
||||||
|
int header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
|
||||||
|
int rate; /**< Sampling rate used */
|
||||||
|
int mode; /**< Mode used (0 for narrowband, 1 for wideband) */
|
||||||
|
int mode_bitstream_version; /**< Version ID of the bit-stream */
|
||||||
|
int nb_channels; /**< Number of channels encoded */
|
||||||
|
int bitrate; /**< Bit-rate used */
|
||||||
|
int frame_size; /**< Size of frames */
|
||||||
|
int vbr; /**< 1 for a VBR encoding, 0 otherwise */
|
||||||
|
int frames_per_packet; /**< Number of frames stored per Ogg packet */
|
||||||
|
int extra_headers; /**< Number of additional headers after the comments */
|
||||||
|
int reserved1; /**< Reserved for future use, must be zero */
|
||||||
|
int reserved2; /**< Reserved for future use, must be zero */
|
||||||
|
} SpeexHeader;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OggSpeexSeekable implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
OggSpeexSeekable::IsValidHeader(const ogg_packet & packet)
|
||||||
|
{
|
||||||
|
return findIdentifier(packet,"Speex ",0);
|
||||||
|
}
|
||||||
|
|
||||||
|
OggSpeexSeekable::OggSpeexSeekable(long serialno)
|
||||||
|
: OggSeekable(serialno)
|
||||||
|
{
|
||||||
|
TRACE("OggSpeexSeekable::OggSpeexSeekable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
OggSpeexSeekable::~OggSpeexSeekable()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggSpeexSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
||||||
|
media_format *format)
|
||||||
|
{
|
||||||
|
TRACE("OggSpeexSeekable::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, check size against struct minus optional fields
|
||||||
|
if (packet.bytes < 1 + (signed)sizeof(SpeexHeader) - 2*(signed)sizeof(int)) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
void * data = &(packet.packet[0]);
|
||||||
|
SpeexHeader * header = (SpeexHeader *)data;
|
||||||
|
|
||||||
|
// get the format for the description
|
||||||
|
media_format_description description = speex_description();
|
||||||
|
BMediaFormats formats;
|
||||||
|
result = formats.InitCheck();
|
||||||
|
if (result == B_OK) {
|
||||||
|
result = formats.GetFormatFor(description, format);
|
||||||
|
}
|
||||||
|
if (result != B_OK) {
|
||||||
|
*format = speex_encoded_media_format();
|
||||||
|
// ignore error, allow user to use ReadChunk interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out format from header packet
|
||||||
|
if (header->bitrate > 0) {
|
||||||
|
format->u.encoded_audio.bit_rate = header->bitrate;
|
||||||
|
} else {
|
||||||
|
// TODO: manually compute it where possible
|
||||||
|
}
|
||||||
|
if (header->nb_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 = header->rate;
|
||||||
|
format->u.encoded_audio.output.channel_count = header->nb_channels;
|
||||||
|
// allocate buffer, round up to nearest speex output_length size
|
||||||
|
int buffer_size = AudioBufferSize(&format->u.encoded_audio.output);
|
||||||
|
int output_length = header->frame_size * header->nb_channels *
|
||||||
|
(format->u.encoded_audio.output.format & 0xf);
|
||||||
|
buffer_size = ((buffer_size - 1) / output_length + 1) * output_length;
|
||||||
|
format->u.encoded_audio.output.buffer_size = buffer_size;
|
||||||
|
|
||||||
|
// get comment packet
|
||||||
|
if (GetHeaderPackets().size() < 2) {
|
||||||
|
result = GetPacket(&packet);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
SaveHeaderPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get extra headers
|
||||||
|
while ((signed)GetHeaderPackets().size() < header->extra_headers) {
|
||||||
|
result = GetPacket(&packet);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
SaveHeaderPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
|
||||||
|
*duration = 100000000;
|
||||||
|
*frameCount = 60000;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggSpeexSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
||||||
|
media_header *mediaHeader)
|
||||||
|
{
|
||||||
|
status_t result = GetPacket(&fChunkPacket);
|
||||||
|
if (result != B_OK) {
|
||||||
|
TRACE("OggSpeexSeekable::GetNextChunk failed: GetPacket = %s\n", strerror(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*chunkBuffer = fChunkPacket.packet;
|
||||||
|
*chunkSize = fChunkPacket.bytes;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef _OGG_SPEEX_SEEKABLE_H
|
||||||
|
#define _OGG_SPEEX_SEEKABLE_H
|
||||||
|
|
||||||
|
#include "OggSeekable.h"
|
||||||
|
|
||||||
|
namespace BPrivate { namespace media {
|
||||||
|
|
||||||
|
class OggSpeexSeekable : public OggSeekable {
|
||||||
|
public:
|
||||||
|
static bool IsValidHeader(const ogg_packet & packet);
|
||||||
|
public:
|
||||||
|
OggSpeexSeekable(long serialno);
|
||||||
|
virtual ~OggSpeexSeekable();
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
using namespace BPrivate::media;
|
||||||
|
|
||||||
|
#endif // _OGG_SPEEX_SEEKABLE_H
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
#include "OggTobiasFormats.h"
|
||||||
|
#include "OggTobiasSeekable.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tobias header structs from http://tobias.everwicked.com/packfmt.htm
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct tobias_stream_header_video
|
||||||
|
{
|
||||||
|
ogg_int32_t width;
|
||||||
|
ogg_int32_t height;
|
||||||
|
} tobias_stream_header_video;
|
||||||
|
|
||||||
|
typedef struct tobias_stream_header_audio
|
||||||
|
{
|
||||||
|
ogg_int16_t channels;
|
||||||
|
ogg_int16_t blockalign;
|
||||||
|
ogg_int32_t avgbytespersec;
|
||||||
|
} tobias_stream_header_audio;
|
||||||
|
|
||||||
|
typedef struct tobias_stream_header
|
||||||
|
{
|
||||||
|
char streamtype[8];
|
||||||
|
char subtype[4];
|
||||||
|
|
||||||
|
ogg_int32_t size; // size of the structure
|
||||||
|
|
||||||
|
ogg_int64_t time_unit; // in reference time (100 ns units)
|
||||||
|
ogg_int64_t samples_per_unit;
|
||||||
|
ogg_int32_t default_len; // in media time
|
||||||
|
|
||||||
|
ogg_int32_t buffersize;
|
||||||
|
ogg_int16_t bits_per_sample;
|
||||||
|
|
||||||
|
union {
|
||||||
|
// Video specific
|
||||||
|
tobias_stream_header_video video;
|
||||||
|
// Audio specific
|
||||||
|
tobias_stream_header_audio audio;
|
||||||
|
};
|
||||||
|
} tobias_stream_header;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OggTobiasSeekable implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
OggTobiasSeekable::IsValidHeader(const ogg_packet & packet)
|
||||||
|
{
|
||||||
|
return findIdentifier(packet,"video",1)
|
||||||
|
|| findIdentifier(packet,"audio",1)
|
||||||
|
|| findIdentifier(packet,"text",1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OggTobiasSeekable::OggTobiasSeekable(long serialno)
|
||||||
|
: OggSeekable(serialno)
|
||||||
|
{
|
||||||
|
TRACE("OggTobiasSeekable::OggTobiasSeekable\n");
|
||||||
|
fMicrosecPerFrame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OggTobiasSeekable::~OggTobiasSeekable()
|
||||||
|
{
|
||||||
|
TRACE("OggTobiasSeekable::~OggTobiasSeekable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_video_format(tobias_stream_header * header, media_format * format)
|
||||||
|
{
|
||||||
|
TRACE(" get_video_format\n");
|
||||||
|
// get the format for the description
|
||||||
|
media_format_description description = tobias_video_description();
|
||||||
|
description.u.avi.codec = header->subtype[3] << 24 | header->subtype[2] << 16
|
||||||
|
| header->subtype[1] << 8 | header->subtype[0];
|
||||||
|
BMediaFormats formats;
|
||||||
|
status_t result = formats.InitCheck();
|
||||||
|
if (result == B_OK) {
|
||||||
|
result = formats.GetFormatFor(description, format);
|
||||||
|
}
|
||||||
|
if (result != B_OK) {
|
||||||
|
*format = tobias_video_encoded_media_format();
|
||||||
|
// ignore error, allow user to use ReadChunk interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out format from header packet
|
||||||
|
format->user_data_type = B_CODEC_TYPE_INFO;
|
||||||
|
strncpy((char*)format->user_data, header->subtype, 4);
|
||||||
|
format->u.encoded_video.frame_size
|
||||||
|
= header->video.width * header->video.height;
|
||||||
|
format->u.encoded_video.output.field_rate = 10000000.0 / header->time_unit;
|
||||||
|
format->u.encoded_video.output.interlace = 1;
|
||||||
|
format->u.encoded_video.output.first_active = 0;
|
||||||
|
format->u.encoded_video.output.last_active = header->video.height - 1;
|
||||||
|
format->u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT;
|
||||||
|
format->u.encoded_video.output.pixel_width_aspect = 1;
|
||||||
|
format->u.encoded_video.output.pixel_height_aspect = 1;
|
||||||
|
format->u.encoded_video.output.display.line_width = header->video.width;
|
||||||
|
format->u.encoded_video.output.display.line_count = header->video.height;
|
||||||
|
format->u.encoded_video.output.display.bytes_per_row = 0;
|
||||||
|
format->u.encoded_video.output.display.pixel_offset = 0;
|
||||||
|
format->u.encoded_video.output.display.line_offset = 0;
|
||||||
|
format->u.encoded_video.output.display.flags = 0;
|
||||||
|
|
||||||
|
// TODO: wring more info out of the headers
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_audio_format(tobias_stream_header * header, media_format * format)
|
||||||
|
{
|
||||||
|
TRACE(" get_audio_format\n");
|
||||||
|
debugger("get_audio_format");
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_text_format(tobias_stream_header * header, media_format * format)
|
||||||
|
{
|
||||||
|
TRACE(" get_text_format\n");
|
||||||
|
// get the format for the description
|
||||||
|
media_format_description description = tobias_text_description();
|
||||||
|
BMediaFormats formats;
|
||||||
|
status_t result = formats.InitCheck();
|
||||||
|
if (result == B_OK) {
|
||||||
|
result = formats.GetFormatFor(description, format);
|
||||||
|
}
|
||||||
|
if (result != B_OK) {
|
||||||
|
*format = tobias_text_encoded_media_format();
|
||||||
|
// ignore error, allow user to use ReadChunk interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out format from header packet
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
||||||
|
media_format *format)
|
||||||
|
{
|
||||||
|
TRACE("OggTobiasSeekable::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
|
||||||
|
if (packet.bytes < 1+(signed)sizeof(tobias_stream_header)) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
void * data = &(packet.packet[1]);
|
||||||
|
tobias_stream_header * header = (tobias_stream_header *)data;
|
||||||
|
|
||||||
|
if (strcmp(header->streamtype, "video") == 0) {
|
||||||
|
result = get_video_format(header, format);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*frameCount = (bigtime_t)(3 * 3600 * format->u.encoded_video.output.field_rate);
|
||||||
|
} else if (strcmp(header->streamtype, "audio") == 0) {
|
||||||
|
result = get_audio_format(header, format);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*frameCount = 2000000;
|
||||||
|
} else if (strcmp(header->streamtype, "text") == 0) {
|
||||||
|
result = get_text_format(header, format);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*frameCount = 2000000;
|
||||||
|
} else {
|
||||||
|
*frameCount = 0;
|
||||||
|
// unknown streamtype
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get comment packet
|
||||||
|
if (GetHeaderPackets().size() < 2) {
|
||||||
|
result = GetPacket(&packet);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
SaveHeaderPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
|
||||||
|
fMediaFormat = *format;
|
||||||
|
fMicrosecPerFrame = header->time_unit / 10.0;
|
||||||
|
*duration = (bigtime_t)(*frameCount * fMicrosecPerFrame);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
||||||
|
media_header *mediaHeader)
|
||||||
|
{
|
||||||
|
status_t result = GetPacket(&fChunkPacket);
|
||||||
|
if (result != B_OK) {
|
||||||
|
TRACE("OggTobiasSeekable::GetNextChunk failed: GetPacket = %s\n", strerror(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*chunkBuffer = fChunkPacket.packet;
|
||||||
|
*chunkSize = fChunkPacket.bytes;
|
||||||
|
bool keyframe = fChunkPacket.packet[0] & (1 << 3); // ??
|
||||||
|
if (fMediaFormat.type == B_MEDIA_ENCODED_VIDEO) {
|
||||||
|
mediaHeader->type = fMediaFormat.type;
|
||||||
|
mediaHeader->start_time = fCurrentTime;
|
||||||
|
mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0);
|
||||||
|
mediaHeader->u.encoded_video.first_active_line
|
||||||
|
= fMediaFormat.u.encoded_video.output.first_active;
|
||||||
|
mediaHeader->u.encoded_video.line_count
|
||||||
|
= fMediaFormat.u.encoded_video.output.display.line_count;
|
||||||
|
}
|
||||||
|
fCurrentFrame++;
|
||||||
|
fCurrentTime = (bigtime_t)(fCurrentFrame * fMicrosecPerFrame);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef _OGG_TOBIAS_SEEKABLE_H
|
||||||
|
#define _OGG_TOBIAS_SEEKABLE_H
|
||||||
|
|
||||||
|
#include "OggSeekable.h"
|
||||||
|
|
||||||
|
namespace BPrivate { namespace media {
|
||||||
|
|
||||||
|
class OggTobiasSeekable : public OggSeekable {
|
||||||
|
public:
|
||||||
|
static bool IsValidHeader(const ogg_packet & packet);
|
||||||
|
public:
|
||||||
|
OggTobiasSeekable(long serialno);
|
||||||
|
virtual ~OggTobiasSeekable();
|
||||||
|
|
||||||
|
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
||||||
|
media_format *format);
|
||||||
|
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
||||||
|
media_header *mediaHeader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
media_format fMediaFormat;
|
||||||
|
double fMicrosecPerFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // namespace BPrivate::media
|
||||||
|
|
||||||
|
using namespace BPrivate::media;
|
||||||
|
|
||||||
|
#endif // _OGG_TOBIAS_SEEKABLE_H
|
||||||
Reference in New Issue
Block a user