From 7eef05968f3746984cc9e8f381a43e741621c9db Mon Sep 17 00:00:00 2001 From: shatty Date: Mon, 9 Feb 2004 09:17:08 +0000 Subject: [PATCH] seekable version 0 [not seekable lol] git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6544 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/ogg/Jamfile | 2 + .../media/plugins/ogg/OggReaderPlugin.cpp | 70 +---- .../media/plugins/ogg/OggReaderPlugin.h | 8 +- src/add-ons/media/plugins/ogg/OggSeekable.cpp | 117 ++++++-- src/add-ons/media/plugins/ogg/OggSeekable.h | 20 +- .../media/plugins/ogg/OggSpeexSeekable.cpp | 161 +++++++++++ .../media/plugins/ogg/OggSpeexSeekable.h | 26 ++ .../media/plugins/ogg/OggTobiasSeekable.cpp | 251 ++++++++++++++++++ .../media/plugins/ogg/OggTobiasSeekable.h | 29 ++ 9 files changed, 587 insertions(+), 97 deletions(-) create mode 100644 src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggSpeexSeekable.h create mode 100644 src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggTobiasSeekable.h diff --git a/src/add-ons/media/plugins/ogg/Jamfile b/src/add-ons/media/plugins/ogg/Jamfile index 2c6a4e1a8c..95a984b90d 100644 --- a/src/add-ons/media/plugins/ogg/Jamfile +++ b/src/add-ons/media/plugins/ogg/Jamfile @@ -10,8 +10,10 @@ Addon ogg : media plugins : OggStream.cpp OggSeekable.cpp OggSpeexStream.cpp + OggSpeexSeekable.cpp OggTheoraStream.cpp OggTobiasStream.cpp + OggTobiasSeekable.cpp OggVorbisStream.cpp OggVorbisSeekable.cpp : false : libogg.a diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp index 6f234fcb54..f22573693b 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp @@ -25,7 +25,7 @@ OggReader::OggReader() ogg_sync_init(&fSync); fSeekable = NULL; fFile = NULL; - fPosition = -1; + fPosition = 0; } @@ -79,7 +79,6 @@ retry: 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"); @@ -124,18 +123,7 @@ retry: #endif STRICT_OGG } 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); + fTracks[serialno] = OggSeekable::makeOggSeekable(fSeekable, &fSeekableLock, serialno, packet); } else { class Interface : public StreamInterface { private: @@ -152,63 +140,15 @@ retry: } fCookies.push_back(serialno); } - off_t pageStart = fPosition - page.header_len - page.body_len; - status_t status = fTracks[serialno]->AddPage(pageStart, page); + status_t status = fTracks[serialno]->AddPage(fPosition, page); if (status != B_OK) { return status; } + fPosition += 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 * get_seekable(BDataIO * data) { @@ -267,7 +207,7 @@ OggReader::Sniff(int32 *streamCount) #else bool first_page = false; #endif -// fSeekable = get_seekable(Source()); + fSeekable = get_seekable(Source()); fFile = get_file(Source()); ssize_t bytes = ReadPage(first_page); diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h index 36baf2ebc9..b9d095edc9 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h @@ -42,22 +42,18 @@ private: serialno_vector fCookies; BPositionIO * fSeekable; + BLocker fSeekableLock; BFile * fFile; off_t fPosition; - // interfaces for OggTracks + // interface for OggStreams ssize_t ReadPage(bool first_page = false); - ssize_t ReadPageAt(off_t position, int read_size = 4*B_PAGE_SIZE); class StreamInterface { public: 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 diff --git a/src/add-ons/media/plugins/ogg/OggSeekable.cpp b/src/add-ons/media/plugins/ogg/OggSeekable.cpp index 3252e889e9..2ecf4425a8 100644 --- a/src/add-ons/media/plugins/ogg/OggSeekable.cpp +++ b/src/add-ons/media/plugins/ogg/OggSeekable.cpp @@ -1,4 +1,6 @@ #include "OggSeekable.h" +#include "OggSpeexSeekable.h" +#include "OggTobiasSeekable.h" #include "OggVorbisSeekable.h" #include #include @@ -15,23 +17,24 @@ */ /* static */ OggSeekable * -OggSeekable::makeOggSeekable(OggReader::SeekableInterface * interface, - long serialno, const ogg_packet & packet) +OggSeekable::makeOggSeekable(BPositionIO * positionIO, BLocker * positionLock, + 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 (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; + stream->fPositionIO = positionIO; + stream->fPositionLock = positionLock; return stream; } @@ -57,11 +60,18 @@ OggSeekable::~OggSeekable() } +static void +insert_position(std::vector & positions, off_t position) +{ + positions.push_back(position); +} + + status_t OggSeekable::AddPage(off_t position, const ogg_page & page) { TRACE("OggSeekable::AddPage\n"); - BAutolock autolock(fSyncLock); + insert_position(fPagePositions, position); char * buffer; // copy the header to our local sync 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); memcpy(buffer, page.body, 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; } @@ -146,21 +219,23 @@ 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; + do { + int result = ogg_sync_pageout(&fSync, &page); + if (result == 0) { + status_t status = ReadPage(&page); + if (status != B_OK) { + TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(status)); + return status; + } } - } - if (result == -1) { - TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n"); - return B_ERROR; - } - if (ogg_stream_pagein(&fStreamState,&page) != 0) { + if (result == -1) { + TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n"); + return B_ERROR; + } + } while (ogg_page_serialno(&page) != GetSerial()); + if (ogg_stream_pagein(&fStreamState, &page) != 0) { + debugger("huh?"); TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n"); return B_ERROR; } diff --git a/src/add-ons/media/plugins/ogg/OggSeekable.h b/src/add-ons/media/plugins/ogg/OggSeekable.h index afdc905610..eb5bc0bd9a 100644 --- a/src/add-ons/media/plugins/ogg/OggSeekable.h +++ b/src/add-ons/media/plugins/ogg/OggSeekable.h @@ -3,13 +3,13 @@ #include "OggTrack.h" #include "OggReaderPlugin.h" -#include +#include namespace BPrivate { namespace media { class OggSeekable : public OggTrack { public: - static OggSeekable * makeOggSeekable(OggReader::SeekableInterface * interface, + static OggSeekable * makeOggSeekable(BPositionIO * positionIO, BLocker * positionLock, long serialno, const ogg_packet & packet); // interface for OggReader @@ -27,15 +27,25 @@ public: status_t AddPage(off_t position, const ogg_page & page); protected: + status_t ReadPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE); + // subclass pull input function status_t GetPacket(ogg_packet * packet); + ogg_packet fChunkPacket; + +protected: + int64 fCurrentFrame; + bigtime_t fCurrentTime; + +private: + off_t fPosition; + std::vector fPagePositions; private: ogg_sync_state fSync; - BLocker fSyncLock; ogg_stream_state fStreamState; - OggReader::SeekableInterface * fReaderInterface; - ogg_packet fChunkPacket; + BPositionIO * fPositionIO; + BLocker fPositionLock; }; } } // namespace BPrivate::media diff --git a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp new file mode 100644 index 0000000000..2c6d455807 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp @@ -0,0 +1,161 @@ +#include "OggSpeexFormats.h" +#include "OggSpeexSeekable.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); +} + +/* + * 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; +} diff --git a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h new file mode 100644 index 0000000000..08054a11be --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h @@ -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 diff --git a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp new file mode 100644 index 0000000000..9d356f5c30 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp @@ -0,0 +1,251 @@ +#include "OggTobiasFormats.h" +#include "OggTobiasSeekable.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); +} + +/* + * 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; +} diff --git a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h new file mode 100644 index 0000000000..a51e4c07fa --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h @@ -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