diff --git a/src/add-ons/media/plugins/ogg/OggCodecs.cpp b/src/add-ons/media/plugins/ogg/OggCodecs.cpp new file mode 100644 index 0000000000..0a13ded35e --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggCodecs.cpp @@ -0,0 +1,238 @@ +#include "OggCodecs.h" +#include +#include + +#define TRACE_THIS 0 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + +/** + * Useful ogg_packet functions + */ + +bool +BPrivate::media::findIdentifier(const ogg_packet & packet, const char * id, uint pos) +{ + TRACE("findIdentifier\n"); + uint length = strlen(id); + if ((unsigned)packet.bytes < pos+length) { + return false; + } + return !memcmp(&packet.packet[pos], id, length); +} + + +/** + * OggCodec implementations + */ + + +OggCodec::OggCodec() + : fCurrentFrame(-1), + fCurrentTime(0), + fOldFrame(-1), + fOldGranulePos(-1), + fInitCheck(B_NO_INIT), + fPacketData(NULL) +{ +} + + +/* virtual */ +OggCodec::~OggCodec() +{ + delete fPacketData; +} + + +/* virtual */ bool +OggCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const +{ + return (packetno == 0); +} + + +/* virtual */ status_t +OggCodec::HandlePacket(const ogg_packet & packet) +{ + TRACE("OggCodec::HandlePacket\n"); + if (!IsHeaderPacket(packet, (fInitCheck == B_OK ? 1 : 0))) { + return B_BAD_VALUE; + } + if (!packet.b_o_s) { + TRACE("OggCodec::HandlePacket 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; + if (formats.InitCheck() == B_OK) { + formats.GetFormatFor(description, &fMediaFormat); + } + + // fill out format from header packet + fMediaFormat.user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)fMediaFormat.user_data, (char*)(&packet), 4); + + fPacketData = new unsigned char[packet.bytes]; + if (fPacketData == NULL) { + return B_NO_MEMORY; + } + memcpy(fPacketData, packet.packet, packet.bytes); + ogg_packet copy = packet; + copy.packet = fPacketData; + + fMediaFormat.SetMetaData(©,sizeof(copy)); + fInitCheck = B_OK; + return B_OK; +} + + +/* virtual */ status_t +OggCodec::GetFormat(media_format * format) const +{ + if (format == 0) { + return B_BAD_VALUE; + } + if (fInitCheck != B_OK) { + return fInitCheck; + } + *format = fMediaFormat; + return B_OK; +} + + +/* virtual */ int64 +OggCodec::GranulesToFrames(int64 granules) const +{ + return granules; +} + + +/* virtual */ int64 +OggCodec::FramesToGranules(int64 frames) const +{ + return frames; +} + + +/* virtual */ status_t +OggCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + int result; + while ((result = ogg_stream_packetout(stream, &fChunkPacket)) < 0) { + // ignore previous packet fragments + } + if (result == 0) { + return B_BUFFER_NOT_AVAILABLE; + } + *chunkBuffer = &fChunkPacket; + *chunkSize = sizeof(fChunkPacket); + mediaHeader->start_time = fCurrentTime; + ogg_int64_t granulepos = fChunkPacket.granulepos; + if (streaming) { + if (granulepos < 0) { + fCurrentFrame = -1; + } else { + fCurrentFrame = fOldFrame + GranulesToFrames(granulepos) + - GranulesToFrames(fOldGranulePos); + fOldFrame = fCurrentFrame; + fOldGranulePos = granulepos; + } + } else { + debugger("explode"); + } + // default (silly) 1 frame/sec + fCurrentTime = 1000000LL * fCurrentFrame; + return B_OK; +} + + +/** + * OggCodecTest implementations + */ + + +OggCodecTest::OggCodecTest() +{ + TRACE("OggCodecTest::OggCodecTest\n"); + OggCodecRoster * roster = OggCodecRoster::Roster(); + if (roster != NULL) { + roster->AddCodecTest(this); + } +} + + +/* virtual */ +OggCodecTest::~OggCodecTest() +{ + TRACE("OggCodecTest::~OggCodecTest\n"); +} + + +/** + * OggCodecRoster implementations + */ + + +/* static */ OggCodecRoster * +OggCodecRoster::Roster(status_t * outError) +{ + TRACE("OggCodecRoster::Roster\n"); + static OggCodecRoster * ogg_codec_roster = NULL; + if (ogg_codec_roster == NULL) { + ogg_codec_roster = new OggCodecRoster(); + if ((ogg_codec_roster == NULL) && (outError != NULL)) { + *outError = B_NO_MEMORY; + } + } + return ogg_codec_roster; +} + + +status_t +OggCodecRoster::AddCodecTest(const OggCodecTest * test) +{ + TRACE("OggCodecRoster::AddCodecTest\n"); + fCodecs.push_back(test); + return B_OK; +} + + +OggCodec * +OggCodecRoster::CodecFor(const ogg_packet & packet) const +{ + TRACE("OggCodecRoster::CodecFor\n"); + std::vector::const_iterator iter; + for(iter = fCodecs.begin() ; iter != fCodecs.end() ; iter++) { + const OggCodecTest * test = *iter; + if (test->RecognizesInitialPacket(packet)) { + return test->InstantiateCodec(); + } + } + return NULL; +} + + +OggCodecRoster::OggCodecRoster() +{ + TRACE("OggCodecRoster::OggCodecRoster\n"); +} + + +OggCodecRoster::~OggCodecRoster() +{ + TRACE("OggCodecRoster::~OggCodecRoster\n"); +} diff --git a/src/add-ons/media/plugins/ogg/OggCodecs.h b/src/add-ons/media/plugins/ogg/OggCodecs.h new file mode 100644 index 0000000000..2d29bea6e6 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggCodecs.h @@ -0,0 +1,93 @@ +#ifndef _OGG_CODECS_H +#define _OGG_CODECS_H + +#include +#include +#include "ogg/ogg.h" +#include + +namespace BPrivate { namespace media { + + +/** + * Useful ogg_packet functions + */ + +bool findIdentifier(const ogg_packet & packet, const char * id, uint pos); + +/** + * OggCodec + * + * An OggCodec is instantiated to assist an OggTrack with interacting with the codecs. + * It helps with seeking, timing, and transforming the ogg_packets into chunks. + */ +class OggCodec { +public: + OggCodec(); + virtual ~OggCodec(); + + virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const; + virtual status_t HandlePacket(const ogg_packet & packet); + virtual status_t GetFormat(media_format * format) const; + virtual int64 GranulesToFrames(int64 granules) const; + virtual int64 FramesToGranules(int64 frames) const; + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + ogg_packet fChunkPacket; + int64 fCurrentFrame; + bigtime_t fCurrentTime; + int64 fOldFrame; + ogg_int64_t fOldGranulePos; + media_format fMediaFormat; + status_t fInitCheck; + +private: + unsigned char * fPacketData; +}; + + +/** + * OggCodecTest + * + * An OggCodecTest is capable of determining if an initial packet belongs to a + * particular codec. It can instantiate that particular codec. + */ +class OggCodecTest { +protected: + OggCodecTest(); +public: + virtual ~OggCodecTest(); + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const = 0; + virtual OggCodec * InstantiateCodec() const = 0; +}; + + +/** + * OggCodecRoster + * + * An OggCodecRoster provides a single location to register OggCodecTests + * and retrieve an appropriate OggCodec for an ogg_packet. + */ +class OggCodecRoster { +public: +static OggCodecRoster * Roster(status_t * outError = NULL); + + status_t AddCodecTest(const OggCodecTest * wrapper); + OggCodec * CodecFor(const ogg_packet & packet) const; + +private: + OggCodecRoster(); + ~OggCodecRoster(); + + std::vector fCodecs; +}; + + +} } // BPrivate::media + +using namespace BPrivate::media; + +#endif // _OGG_CODECS_H diff --git a/src/add-ons/media/plugins/ogg/OggSpeexCodec.cpp b/src/add-ons/media/plugins/ogg/OggSpeexCodec.cpp new file mode 100644 index 0000000000..58f9984187 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggSpeexCodec.cpp @@ -0,0 +1,228 @@ +#include "OggSpeexFormats.h" +#include "OggCodecs.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; + + +/* + * OggSpeexCodec implementations + */ + +class OggSpeexCodec : public OggCodec { +public: + OggSpeexCodec(); + virtual ~OggSpeexCodec(); + + virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const; + virtual status_t HandlePacket(const ogg_packet & packet); + + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); +private: + std::vector fMetaDataPackets; + unsigned char * fHeaderPacketData; + unsigned char * fCommentPacketData; + std::vector fExtraPacketData; + unsigned int fPacketCount; + unsigned int fExtraHeadersField; +}; + + +OggSpeexCodec::OggSpeexCodec() +{ + TRACE("OggSpeexCodec::OggSpeexCodec\n"); + fHeaderPacketData = NULL; + fCommentPacketData = NULL; + fPacketCount = 0; +} + + +OggSpeexCodec::~OggSpeexCodec() +{ + TRACE("OggSpeexCodec::~OggSpeexCodec\n"); + delete fHeaderPacketData; + delete fCommentPacketData; + std::vector::iterator iter; + for (iter = fExtraPacketData.begin() ; iter != fExtraPacketData.end() ; iter++) { + uchar * data = *iter; + delete data; + } +} + + +/* virtual */ bool +OggSpeexCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const +{ + if (packetno < 2) { + return true; + } + if (fPacketCount == 0) { + debugger("IsHeaderPacket called for packetno >= 2, before receiving the header packet"); + return false; + } + return (packetno < 2 + fExtraHeadersField); +} + + +/* virtual */ status_t +OggSpeexCodec::HandlePacket(const ogg_packet & packet) +{ + TRACE("OggSpeexCodec::HandlePacket\n"); + if (!IsHeaderPacket(packet, fPacketCount)) { + return B_BAD_VALUE; + } + switch (fPacketCount) { + case 0: { + // header packet + 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; + + // store how many extra header packets + fExtraHeadersField = header->extra_headers; + + // get the format for the description + media_format_description description = speex_description(); + BMediaFormats formats; + if ((formats.InitCheck() != B_OK) || + (formats.GetFormatFor(description, &fMediaFormat) != B_OK)) { + fMediaFormat = speex_encoded_media_format(); + } + + // fill out format from header packet + if (header->bitrate > 0) { + fMediaFormat.u.encoded_audio.bit_rate = header->bitrate; + } else { + // TODO: manually compute it where possible + } + if (header->nb_channels == 1) { + fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT; + } else { + fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + } + fMediaFormat.u.encoded_audio.output.frame_rate = header->rate; + fMediaFormat.u.encoded_audio.output.channel_count = header->nb_channels; + // allocate buffer, round up to nearest speex output_length size + int buffer_size = AudioBufferSize(&fMediaFormat.u.encoded_audio.output); + int output_length = header->frame_size * header->nb_channels * + (fMediaFormat.u.encoded_audio.output.format & 0xf); + buffer_size = ((buffer_size - 1) / output_length + 1) * output_length; + fMediaFormat.u.encoded_audio.output.buffer_size = buffer_size; + + fHeaderPacketData = new unsigned char[packet.bytes]; + memcpy(fHeaderPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fHeaderPacketData; + break; + } + case 1: { + // comment packet + fCommentPacketData = new unsigned char[packet.bytes]; + memcpy(fCommentPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCommentPacketData; + break; + } + default: + // extra headers + uchar * extraHeaderData = new unsigned char[packet.bytes]; + memcpy(extraHeaderData, packet.packet, packet.bytes); + fExtraPacketData.push_back(extraHeaderData); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = extraHeaderData; + break; + } + if (fPacketCount == 1 + fExtraHeadersField) { + fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets)); + fInitCheck = B_OK; + } + fPacketCount++; + return B_OK; +} + + +/* virtual */ status_t +OggSpeexCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + if (fCurrentFrame == -1) { + fCurrentTime = -1; + } else { + fCurrentTime = (1000000LL * fCurrentFrame) + / (long long)fMediaFormat.u.encoded_audio.output.frame_rate; + } + status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); + if (result != B_OK) { + return result; + } + *chunkBuffer = fChunkPacket.packet; + *chunkSize = fChunkPacket.bytes; + return B_OK; +} + + +/* + * OggSpeexCodecTest + */ + +static class OggSpeexCodecTest : public OggCodecTest { +public: + OggSpeexCodecTest() {} + virtual ~OggSpeexCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet, "Speex ", 0); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggSpeexCodec(); + } +} ogg_speex_codec_test; diff --git a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp deleted file mode 100644 index c17c61bb1a..0000000000 --- a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#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; - } - fFrameRate = 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())); - - // TODO: count the frames in the first page.. somehow.. :-/ - int64 frames = 0; - - ogg_page page; - // read the first page - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 fFirstGranulepos = ogg_page_granulepos(&page); - TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos); - // read our last page - off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET); - if (last < 0) { - return last; - } - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 last_granulepos = ogg_page_granulepos(&page); - - // seek back to the start - int64 frame = 0; - bigtime_t time = 0; - result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time); - if (result != B_OK) { - return result; - } - - // compute frame count and duration from sample count - frames = last_granulepos - fFirstGranulepos; - - *frameCount = frames; - *duration = (1000000LL * frames) / (long long)fFrameRate; - - return B_OK; -} - - -status_t -OggSpeexSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader) -{ - status_t result = inherited::GetNextChunk(chunkBuffer, chunkSize, mediaHeader); - if (result != B_OK) { - TRACE("OggSpeexSeekable::GetNextChunk failed: GetNextChunk = %s\n", strerror(result)); - return result; - } - *chunkSize = ((ogg_packet*)*chunkBuffer)->bytes; - *chunkBuffer = ((ogg_packet*)*chunkBuffer)->packet; - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h b/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h deleted file mode 100644 index a1a6065a1c..0000000000 --- a/src/add-ons/media/plugins/ogg/OggSpeexSeekable.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _OGG_SPEEX_SEEKABLE_H -#define _OGG_SPEEX_SEEKABLE_H - -#include "OggSeekable.h" - -namespace BPrivate { namespace media { - -class OggSpeexSeekable : public OggSeekable { -private: - typedef OggSeekable inherited; -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/OggSpeexStream.cpp b/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp deleted file mode 100644 index 1d7d97aa86..0000000000 --- a/src/add-ons/media/plugins/ogg/OggSpeexStream.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "OggSpeexFormats.h" -#include "OggSpeexStream.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; - - -/* - * OggSpeexStream implementations - */ - -/* static */ bool -OggSpeexStream::IsValidHeader(const ogg_packet & packet) -{ - return findIdentifier(packet,"Speex ",0); -} - -OggSpeexStream::OggSpeexStream(long serialno) - : OggStream(serialno) -{ - TRACE("OggSpeexStream::OggSpeexStream\n"); -} - -OggSpeexStream::~OggSpeexStream() -{ - -} - -status_t -OggSpeexStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format) -{ - TRACE("OggSpeexStream::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 -OggSpeexStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader) -{ - status_t result = GetPacket(&fChunkPacket); - if (result != B_OK) { - TRACE("OggSpeexStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); - return result; - } - *chunkBuffer = fChunkPacket.packet; - *chunkSize = fChunkPacket.bytes; - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggSpeexStream.h b/src/add-ons/media/plugins/ogg/OggSpeexStream.h deleted file mode 100644 index e50ccf399d..0000000000 --- a/src/add-ons/media/plugins/ogg/OggSpeexStream.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _OGG_SPEEX_STREAM_H -#define _OGG_SPEEX_STREAM_H - -#include "OggStream.h" - -namespace BPrivate { namespace media { - -class OggSpeexStream : public OggStream { -public: - static bool IsValidHeader(const ogg_packet & packet); -public: - OggSpeexStream(long serialno); - virtual ~OggSpeexStream(); - - 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_STREAM_H diff --git a/src/add-ons/media/plugins/ogg/OggTheoraCodec.cpp b/src/add-ons/media/plugins/ogg/OggTheoraCodec.cpp new file mode 100644 index 0000000000..72e9c9f43b --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTheoraCodec.cpp @@ -0,0 +1,327 @@ +#include "OggTheoraFormats.h" +#include "OggCodecs.h" +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + + +/* + * theora header parsing code from theora/theara.h + */ + +typedef enum { + OC_CS_UNSPECIFIED, + OC_CS_ITU_REC_470M, + OC_CS_ITU_REC_470BG, +} theora_colorspace; + +typedef struct { + ogg_uint32_t width; + ogg_uint32_t height; + ogg_uint32_t frame_width; + ogg_uint32_t frame_height; + ogg_uint32_t offset_x; + ogg_uint32_t offset_y; + ogg_uint32_t fps_numerator; + ogg_uint32_t fps_denominator; + ogg_uint32_t aspect_numerator; + ogg_uint32_t aspect_denominator; + theora_colorspace colorspace; + int target_bitrate; + int quality; + int quick_p; /* quick encode/decode */ + + /* decode only */ + unsigned char version_major; + unsigned char version_minor; + unsigned char version_subminor; + + void *codec_setup; + + /* encode only */ + int dropframes_p; + int keyframe_auto_p; + ogg_uint32_t keyframe_frequency; + ogg_uint32_t keyframe_frequency_force; /* also used for decode init to + get granpos shift correct */ + ogg_uint32_t keyframe_data_target_bitrate; + ogg_int32_t keyframe_auto_threshold; + ogg_uint32_t keyframe_mindistance; + ogg_int32_t noise_sensitivity; + ogg_int32_t sharpness; + +} theora_info; + +typedef struct theora_comment{ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} theora_comment; + +// based on theora/lib/toplevel.c _theora_unpack_info + +#define theora_read(x,y,z) ( *z = oggpackB_read(x,y) ) + +#define OC_BADHEADER -1 + +static int _theora_unpack_info(theora_info *ci, oggpack_buffer *opb){ + long ret; + + theora_read(opb,8,&ret); + ci->version_major=(unsigned char)ret; + theora_read(opb,8,&ret); + ci->version_minor=(unsigned char)ret; + theora_read(opb,8,&ret); + ci->version_subminor=(unsigned char)ret; + +// if(ci->version_major!=VERSION_MAJOR)return(OC_VERSION); +// if(ci->version_minor>VERSION_MINOR)return(OC_VERSION); + + theora_read(opb,16,&ret); + ci->width=ret<<4; + theora_read(opb,16,&ret); + ci->height=ret<<4; + theora_read(opb,24,&ret); + ci->frame_width=ret; + theora_read(opb,24,&ret); + ci->frame_height=ret; + theora_read(opb,8,&ret); + ci->offset_x=ret; + theora_read(opb,8,&ret); + ci->offset_y=ret; + + theora_read(opb,32,&ret); + ci->fps_numerator=ret; + theora_read(opb,32,&ret); + ci->fps_denominator=ret; + theora_read(opb,24,&ret); + ci->aspect_numerator=ret; + theora_read(opb,24,&ret); + ci->aspect_denominator=ret; + + theora_read(opb,8,&ret); + ci->colorspace=(theora_colorspace)ret; + theora_read(opb,24,&ret); + ci->target_bitrate=ret; + theora_read(opb,6,&ret); + ci->quality=ret=ret; + + theora_read(opb,5,&ret); + ci->keyframe_frequency_force=1< fMetaDataPackets; + unsigned char * fHeaderPacketData; + unsigned char * fCommentPacketData; + unsigned char * fCodecPacketData; + unsigned int fPacketCount; +}; + + +OggTheoraCodec::OggTheoraCodec() +{ + TRACE("OggTheoraCodec::OggTheoraCodec\n"); + fHeaderPacketData = NULL; + fCommentPacketData = NULL; + fCodecPacketData = NULL; + fPacketCount = 0; +} + + +OggTheoraCodec::~OggTheoraCodec() +{ + delete fHeaderPacketData; + delete fCommentPacketData; + delete fCodecPacketData; +} + + +/* virtual */ bool +OggTheoraCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const +{ + oggpack_buffer opb; + oggpackB_readinit(&opb, packet.packet, packet.bytes); + uint packtype = oggpackB_read(&opb, 8); + return (packetno == 0 && packtype == 0x80) + || (packetno == 1 && packtype == 0x81) + || (packetno == 2 && packtype == 0x82); +} + + +/* virtual */ status_t +OggTheoraCodec::HandlePacket(const ogg_packet & packet) +{ + TRACE("OggTheoraCodec::HandlePacket\n"); + if (!IsHeaderPacket(packet, fPacketCount)) { + return B_BAD_VALUE; + } + switch (fPacketCount) { + case 0: { + // header packet + 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; + oggpackB_readinit(&opb, packet.packet, packet.bytes); + // discard packet type (already validated in IsHeaderPacket) + oggpackB_read(&opb, 8); + // discard theora string + for (uint i = 0 ; i < sizeof("theora") - 1 ; i++) { + oggpackB_read(&opb, 8); + } + theora_info info; + if (_theora_unpack_info(&info, &opb) != 0) { + return B_ERROR; // couldn't unpack info + } + + // get the format for the description + media_format_description description = theora_description(); + BMediaFormats formats; + if ((formats.InitCheck() != B_OK) || + (formats.GetFormatFor(description, &fMediaFormat) != B_OK)) { + fMediaFormat = theora_encoded_media_format(); + } + + // fill out format from header packet + // sanity check + if (info.fps_denominator != 0) { + fMediaFormat.u.encoded_video.output.field_rate = + (double)info.fps_numerator / (double)info.fps_denominator; + } + fMediaFormat.u.encoded_video.output.first_active = info.offset_y; + fMediaFormat.u.encoded_video.output.last_active = info.offset_y + info.height; + fMediaFormat.u.encoded_video.output.pixel_width_aspect = info.aspect_numerator; + fMediaFormat.u.encoded_video.output.pixel_height_aspect = info.aspect_denominator; + fMediaFormat.u.encoded_video.output.display.line_width = info.frame_width; + fMediaFormat.u.encoded_video.output.display.line_count = info.frame_height; + fMediaFormat.u.encoded_video.avg_bit_rate = info.target_bitrate; // advisory only + fMediaFormat.u.encoded_video.frame_size = info.width * info.height; + // TODO: wring more info out of the headers + + fHeaderPacketData = new unsigned char[packet.bytes]; + memcpy(fHeaderPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fHeaderPacketData; + break; + } + case 1: { + // comment packet + fCommentPacketData = new unsigned char[packet.bytes]; + memcpy(fCommentPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCommentPacketData; + break; + } + case 2: { + // codec setup packet + fCodecPacketData = new unsigned char[packet.bytes]; + memcpy(fCodecPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCodecPacketData; + + fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets)); + fInitCheck = B_OK; + break; + } + default: + // huh? + break; + } + fPacketCount++; + return B_OK; +} + + +/* virtual */ status_t +OggTheoraCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + if (streaming) { + if (fChunkPacket.granulepos == -1) { + fCurrentTime = -1; + } else { + fCurrentTime = (bigtime_t) ((1000000LL * fCurrentFrame) + / (fMediaFormat.u.encoded_video.output.field_rate * + fMediaFormat.u.encoded_video.output.interlace)); + } + } else { + debugger("kaboom"); + } + status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); + if (result != B_OK) { + return result; + } + oggpack_buffer opb; + oggpackB_readinit(&opb, fChunkPacket.packet, fChunkPacket.bytes); + // Read 1 bit. This must be 0 for all data packets. + uint packtype = oggpackB_read(&opb, 1); + if (packtype != 0) { + // packet was not a data packet + return B_ERROR; + } + // read 1 bit (frametype) this is 0 for keyframes, 1 for P frames. + uint keyframe = oggpackB_read(&opb, 1); + // read 6 bits (quality mask) this is the quality index for this frame. +// uint quality = oggpackB_read(&opb, 6); +/* +if the frametype is 0 (keyframe): +read 1 bit (keyframetype) this indicates the coding variety of the keyframe. +read 2 bits (spare) these bits are unused. +*/ + mediaHeader->u.encoded_video.field_number = fCurrentFrame; + mediaHeader->u.encoded_video.field_flags = (keyframe == 1 ? B_MEDIA_KEY_FRAME : 0); + return result; +} + + +/* + * OggTheoraCodecWrapper + */ + +static class OggTheoraCodecTest : public OggCodecTest { +public: + OggTheoraCodecTest() {} + virtual ~OggTheoraCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet, "theora", 1); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggTheoraCodec(); + } +} ogg_theora_codec_test; diff --git a/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp b/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp deleted file mode 100644 index ebc3bc3649..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTheoraStream.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "OggTheoraFormats.h" -#include "OggTheoraStream.h" -#include -#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); -} - -/* - * theora header parsing code from theora/theara.h - */ - -typedef enum { - OC_CS_UNSPECIFIED, - OC_CS_ITU_REC_470M, - OC_CS_ITU_REC_470BG, -} theora_colorspace; - -typedef struct { - ogg_uint32_t width; - ogg_uint32_t height; - ogg_uint32_t frame_width; - ogg_uint32_t frame_height; - ogg_uint32_t offset_x; - ogg_uint32_t offset_y; - ogg_uint32_t fps_numerator; - ogg_uint32_t fps_denominator; - ogg_uint32_t aspect_numerator; - ogg_uint32_t aspect_denominator; - theora_colorspace colorspace; - int target_bitrate; - int quality; - int quick_p; /* quick encode/decode */ - - /* decode only */ - unsigned char version_major; - unsigned char version_minor; - unsigned char version_subminor; - - void *codec_setup; - - /* encode only */ - int dropframes_p; - int keyframe_auto_p; - ogg_uint32_t keyframe_frequency; - ogg_uint32_t keyframe_frequency_force; /* also used for decode init to - get granpos shift correct */ - ogg_uint32_t keyframe_data_target_bitrate; - ogg_int32_t keyframe_auto_threshold; - ogg_uint32_t keyframe_mindistance; - ogg_int32_t noise_sensitivity; - ogg_int32_t sharpness; - -} theora_info; - -// based on theora/lib/toplevel.c _theora_unpack_info - -#define theora_read(x,y,z) ( *z = oggpack_read(x,y) ) - -#define OC_BADHEADER -1 - -static int _theora_unpack_info(theora_info *ci, oggpack_buffer *opb){ - long ret; - - theora_read(opb,8,&ret); - ci->version_major=(unsigned char)ret; - theora_read(opb,8,&ret); - ci->version_minor=(unsigned char)ret; - theora_read(opb,8,&ret); - ci->version_subminor=(unsigned char)ret; - -// if(ci->version_major!=VERSION_MAJOR)return(OC_VERSION); -// if(ci->version_minor>VERSION_MINOR)return(OC_VERSION); - - theora_read(opb,16,&ret); - ci->width=ret<<4; - theora_read(opb,16,&ret); - ci->height=ret<<4; - theora_read(opb,24,&ret); - ci->frame_width=ret; - theora_read(opb,24,&ret); - ci->frame_height=ret; - theora_read(opb,8,&ret); - ci->offset_x=ret; - theora_read(opb,8,&ret); - ci->offset_y=ret; - - theora_read(opb,32,&ret); - ci->fps_numerator=ret; - theora_read(opb,32,&ret); - ci->fps_denominator=ret; - theora_read(opb,24,&ret); - ci->aspect_numerator=ret; - theora_read(opb,24,&ret); - ci->aspect_denominator=ret; - - theora_read(opb,8,&ret); - ci->colorspace=(theora_colorspace)ret; - theora_read(opb,24,&ret); - ci->target_bitrate=ret; - theora_read(opb,6,&ret); - ci->quality=ret=ret; - - theora_read(opb,5,&ret); - ci->keyframe_frequency_force=1<u.encoded_video.frame_size = info.frame_width * info.frame_height ; - format->u.encoded_video.output.display.line_width = info.frame_width; - format->u.encoded_video.output.display.line_count = info.frame_height; - // TODO: wring more info out of the headers - - format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); - *duration = 80000000; - *frameCount = 60000; - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggTheoraStream.h b/src/add-ons/media/plugins/ogg/OggTheoraStream.h deleted file mode 100644 index 338fec39ef..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTheoraStream.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _OGG_THEORA_STREAM_H -#define _OGG_THEORA_STREAM_H - -#include "OggStream.h" - -namespace BPrivate { namespace media { - -class OggTheoraStream : public OggStream { -public: - static bool IsValidHeader(const ogg_packet & packet); -public: - OggTheoraStream(long serialno); - virtual ~OggTheoraStream(); - - virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format); -}; - -} } // namespace BPrivate::media - -using namespace BPrivate::media; - -#endif // _OGG_THEORA_STREAM_H diff --git a/src/add-ons/media/plugins/ogg/OggTobiasCodecs.cpp b/src/add-ons/media/plugins/ogg/OggTobiasCodecs.cpp new file mode 100644 index 0000000000..ccde74963c --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggTobiasCodecs.cpp @@ -0,0 +1,465 @@ +#include "OggTobiasFormats.h" +#include "OggCodecs.h" +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + + +/* + * 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; + + +/* +static int64 +count_granules(const ogg_packet & packet) +{ + // thanks to Marcus: + int lenbytes = ((packet.packet[0] & 0xc0) >> 6) | ((packet.packet[0] & 0x02) << 1); + if (lenbytes == 0) { + return 1; + } + int64 granules = 0; + int count = 0; +// fprintf(stderr, "lenbytes = %d, ", lenbytes); + while (lenbytes-- > 0) { + granules += ((uint8*)packet.packet)[count+1] * (1LL << 8 * count); + count++; + } +// fprintf(stderr, "granules = %lld\n", granules); + return granules; +} +*/ + + +/* + * OggTobiasCodec + */ + + +class OggTobiasCodec : public OggCodec { +public: + OggTobiasCodec() {} + virtual ~OggTobiasCodec() {} + + virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const; + virtual status_t HandlePacket(const ogg_packet & packet); + +protected: + virtual status_t HeaderToFormat(const tobias_stream_header & header) = 0; + + std::vector fMetaDataPackets; + unsigned char * fHeaderPacketData; + unsigned char * fCommentPacketData; + unsigned int fPacketCount; +}; + + +/* virtual */ bool +OggTobiasCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const +{ + oggpack_buffer opb; + oggpack_readinit(&opb, packet.packet, packet.bytes); + uint packtype = oggpack_read(&opb, 8); + return (packetno == 0 && packtype == 0x01) + || (packetno == 1 && packtype == 0x03); +} + + +/* virtual */ status_t +OggTobiasCodec::HandlePacket(const ogg_packet & packet) +{ + TRACE("OggTobiasCodec::HandlePacket\n"); + if (!IsHeaderPacket(packet, fPacketCount)) { + return B_ERROR; + } + switch (fPacketCount) { + case 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; + + status_t result = HeaderToFormat(*header); + if (result != B_OK) { + return result; + } + + // initialize codec info + fMediaFormat.user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)fMediaFormat.user_data, header->subtype, 4); + + // save the header packet for use in meta data + fHeaderPacketData = new unsigned char[packet.bytes]; + memcpy(fHeaderPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fHeaderPacketData; + break; + } + case 1: { + // save the comment packet for use in meta data + fCommentPacketData = new unsigned char[packet.bytes]; + memcpy(fCommentPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCommentPacketData; + + // ready for showtime + fInitCheck = B_OK; + fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets)); + break; + } + default: + // huh? + break; + } + fPacketCount++; + return B_OK; +} + + +/* + * OggTobiasVideoCodec + */ + + +class OggTobiasVideoCodec : public OggTobiasCodec { +public: + OggTobiasVideoCodec(); + virtual ~OggTobiasVideoCodec(); + + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + virtual status_t HeaderToFormat(const tobias_stream_header & header); +}; + + +OggTobiasVideoCodec::OggTobiasVideoCodec() +{ + TRACE("OggTobiasVideoCodec::OggTobiasVideoCodec\n"); +} + + +OggTobiasVideoCodec::~OggTobiasVideoCodec() +{ + TRACE("OggTobiasVideoCodec::~OggTobiasVideoCodec\n"); +} + + +/* virtual */ status_t +OggTobiasVideoCodec::HeaderToFormat(const tobias_stream_header & header) +{ + TRACE("OggTobiasVideoCodec::HeaderToFormat\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; + if ((formats.InitCheck() != B_OK) || + (formats.GetFormatFor(description, &fMediaFormat) != B_OK)) { + fMediaFormat = tobias_video_encoded_media_format(); + } + + // fill out format from header packet + fMediaFormat.u.encoded_video.frame_size + = header.video.width * header.video.height; + fMediaFormat.u.encoded_video.output.field_rate = 10000000.0 / header.time_unit; + fMediaFormat.u.encoded_video.output.interlace = 1; + fMediaFormat.u.encoded_video.output.first_active = 0; + fMediaFormat.u.encoded_video.output.last_active = header.video.height - 1; + fMediaFormat.u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT; + fMediaFormat.u.encoded_video.output.pixel_width_aspect = 1; + fMediaFormat.u.encoded_video.output.pixel_height_aspect = 1; + fMediaFormat.u.encoded_video.output.display.line_width = header.video.width; + fMediaFormat.u.encoded_video.output.display.line_count = header.video.height; + fMediaFormat.u.encoded_video.output.display.bytes_per_row = 0; + fMediaFormat.u.encoded_video.output.display.pixel_offset = 0; + fMediaFormat.u.encoded_video.output.display.line_offset = 0; + fMediaFormat.u.encoded_video.output.display.flags = 0; + + // TODO: wring more info out of the headers? + return B_OK; +} + + +/* virtual */ status_t +OggTobiasVideoCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); + if (result != B_OK) { + return result; + } + if (streaming) { + if (fChunkPacket.granulepos == -1) { + int new_granulepos = fOldGranulePos + 1; + fCurrentFrame = fOldFrame + GranulesToFrames(new_granulepos) + - GranulesToFrames(fOldGranulePos); + fOldFrame = fCurrentFrame; + fOldGranulePos = new_granulepos; + } else { +// fprintf(stderr, "granulepos: %lld ", fChunkPacket.granulepos); +// fprintf(stderr, "out: frame = %lld\n", fCurrentFrame); + } + } else { + debugger("kaboom"); + } + fCurrentTime = (bigtime_t) ((1000000LL * fCurrentFrame) + / (fMediaFormat.u.encoded_video.output.field_rate * + fMediaFormat.u.encoded_video.output.interlace)); +// fprintf(stderr, "video in: frame = %lld time = %lld\n", fCurrentFrame, fCurrentTime); + int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1); + *chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]); + *chunkSize = fChunkPacket.bytes-(lenbytes+1); + bool keyframe = (fChunkPacket.packet[0] & (1 << 3)); + mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0); + return result; +} + + +// OggTobiasVideoCodecTest + +static class OggTobiasVideoCodecTest : public OggCodecTest { +public: + OggTobiasVideoCodecTest() {} + virtual ~OggTobiasVideoCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet, "video", 1); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggTobiasVideoCodec(); + } +} ogg_tobias_video_test; + + +/* + * OggTobiasAudioCodec + */ + + +class OggTobiasAudioCodec : public OggTobiasCodec { +public: + OggTobiasAudioCodec(); + virtual ~OggTobiasAudioCodec(); + + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + virtual status_t HeaderToFormat(const tobias_stream_header & header); +}; + + +OggTobiasAudioCodec::OggTobiasAudioCodec() +{ + TRACE("OggTobiasAudioCodec::OggTobiasAudioCodec\n"); +} + + +OggTobiasAudioCodec::~OggTobiasAudioCodec() +{ + TRACE("OggTobiasAudioCodec::~OggTobiasAudioCodec\n"); +} + + +/* virtual */ status_t +OggTobiasAudioCodec::HeaderToFormat(const tobias_stream_header & header) +{ + TRACE("OggTobiasAudioCodec::HeaderToFormat\n"); + + debugger("get_audio_format"); + + return B_OK; +} + + +/* virtual */ status_t +OggTobiasAudioCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + if (fCurrentFrame == -1) { + fCurrentFrame = fOldFrame + 1; + fOldFrame = fCurrentFrame; + fOldGranulePos++; + } + fCurrentTime = (bigtime_t) (1000000LL * fCurrentFrame) + / (long long)fMediaFormat.u.encoded_audio.output.frame_rate; + status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); + if (result != B_OK) { + return result; + } + int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1); + *chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]); + *chunkSize = fChunkPacket.bytes-(lenbytes+1); + return result; +} + + +// OggTobiasAudioCodecTest + +static class OggTobiasAudioCodecTest : public OggCodecTest { +public: + OggTobiasAudioCodecTest() {} + virtual ~OggTobiasAudioCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet, "audio", 1); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggTobiasAudioCodec(); + } +} ogg_tobias_audio_test; + + +/* + * OggTobiasTextCodec + */ + +class OggTobiasTextCodec : public OggTobiasCodec { +public: + OggTobiasTextCodec(); + virtual ~OggTobiasTextCodec(); + + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + +protected: + virtual status_t HeaderToFormat(const tobias_stream_header & header); +}; + + +OggTobiasTextCodec::OggTobiasTextCodec() +{ + TRACE("OggTobiasTextCodec::OggTobiasTextCodec\n"); +} + + +OggTobiasTextCodec::~OggTobiasTextCodec() +{ + TRACE("OggTobiasTextCodec::~OggTobiasTextCodec\n"); +} + + + +/* virtual */ status_t +OggTobiasTextCodec::HeaderToFormat(const tobias_stream_header & header) +{ + TRACE("OggTobiasTextCodec::HeaderToFormat\n"); + + // get the format for the description + media_format_description description = tobias_text_description(); + BMediaFormats formats; + if ((formats.InitCheck() != B_OK) || + (formats.GetFormatFor(description, &fMediaFormat) != B_OK)) { + fMediaFormat = tobias_text_encoded_media_format(); + } + + // fill out format from header packet + (void)header; + + return B_OK; +} + + +/* virtual */ status_t +OggTobiasTextCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + if (fCurrentFrame == -1) { + fCurrentFrame = fOldFrame + 1; + fOldFrame = fCurrentFrame; + fOldGranulePos++; + } + fCurrentTime = (bigtime_t) (1000000LL * fCurrentFrame) + / (long long)fMediaFormat.u.encoded_audio.output.frame_rate; + status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); + if (result != B_OK) { + return result; + } + // thanks to Marcus: + int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1); + *chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]); + *chunkSize = fChunkPacket.bytes-(lenbytes+1); + bool keyframe = (fChunkPacket.packet[0] & (1 << 3)); + mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0); + return result; +} + + +// OggTobiasTextCodecTest + +static class OggTobiasTextCodecTest : public OggCodecTest { +public: + OggTobiasTextCodecTest() {} + virtual ~OggTobiasTextCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet,"text",1); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggTobiasTextCodec(); + } +} ogg_tobias_text_test; + + diff --git a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp deleted file mode 100644 index cf40cec64b..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#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; - } - } else if (strcmp(header->streamtype, "audio") == 0) { - result = get_audio_format(header, format); - if (result != B_OK) { - return result; - } - } else if (strcmp(header->streamtype, "text") == 0) { - result = get_text_format(header, format); - if (result != B_OK) { - return result; - } - } 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; - fFrameRate = 1000000.0 / fMicrosecPerFrame; - - // TODO: count the frames in the first page.. somehow.. :-/ - int64 frames = 0; - - ogg_page page; - // read the first page - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 fFirstGranulepos = ogg_page_granulepos(&page); - TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos); - // read our last page - off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET); - if (last < 0) { - return last; - } - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 last_granulepos = ogg_page_granulepos(&page); - - // seek back to the start - int64 frame = 0; - bigtime_t time = 0; - result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time); - if (result != B_OK) { - return result; - } - - // compute frame count and duration from sample count - frames = last_granulepos - fFirstGranulepos; - - *frameCount = frames; - *duration = (1000000LL * frames) / (long long)fFrameRate; - return B_OK; -} - - -status_t -OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader) -{ - status_t result = inherited::GetNextChunk(chunkBuffer, chunkSize, mediaHeader); - if (result != B_OK) { - TRACE("OggTobiasSeekable::GetNextChunk failed: GetNextChunk = %s\n", strerror(result)); - return result; - } - *chunkSize = ((ogg_packet*)*chunkBuffer)->bytes; - *chunkBuffer = ((ogg_packet*)*chunkBuffer)->packet; - bool keyframe = ((uint*)chunkBuffer)[0] & (1 << 3); // ?? - if (fMediaFormat.type == B_MEDIA_ENCODED_VIDEO) { - mediaHeader->type = fMediaFormat.type; - 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; - } - if (mediaHeader->start_time < 0) { - fCurrentFrame++; - fCurrentTime = (bigtime_t)((fCurrentFrame * 1000000LL) / fFrameRate); - mediaHeader->start_time = fCurrentTime; - } -// fprintf(stderr, "current frame = %lld, time = %lld\n", fCurrentFrame, fCurrentTime); - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h b/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h deleted file mode 100644 index e7d2a83e7a..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTobiasSeekable.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _OGG_TOBIAS_SEEKABLE_H -#define _OGG_TOBIAS_SEEKABLE_H - -#include "OggSeekable.h" - -namespace BPrivate { namespace media { - -class OggTobiasSeekable : public OggSeekable { -private: - typedef OggSeekable inherited; -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 diff --git a/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp b/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp deleted file mode 100644 index 9175c91f0d..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTobiasStream.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "OggTobiasFormats.h" -#include "OggTobiasStream.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; - -/* - * OggTobiasStream implementations - */ - -/* static */ bool -OggTobiasStream::IsValidHeader(const ogg_packet & packet) -{ - return findIdentifier(packet,"video",1) - || findIdentifier(packet,"audio",1) - || findIdentifier(packet,"text",1); -} - - -OggTobiasStream::OggTobiasStream(long serialno) - : OggStream(serialno) -{ - TRACE("OggTobiasStream::OggTobiasStream\n"); - fMicrosecPerFrame = 0; -} - - -OggTobiasStream::~OggTobiasStream() -{ - TRACE("OggTobiasStream::~OggTobiasStream\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 -OggTobiasStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format) -{ - TRACE("OggTobiasStream::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 -OggTobiasStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader) -{ - status_t result = GetPacket(&fChunkPacket); - if (result != B_OK) { - TRACE("OggTobiasStream::GetNextChunk failed: GetPacket = %s\n", strerror(result)); - return result; - } - *chunkBuffer = fChunkPacket.packet; - *chunkSize = fChunkPacket.bytes; - 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; -} - - -status_t -OggTobiasStream::AddPage(off_t position, const ogg_page & page) -{ - status_t status = OggStream::AddPage(position, page); - if (fMediaFormat.type == B_MEDIA_HTML) { - ogg_packet packet; - GetPacket(&packet); - if (packet.bytes > 4) { - fprintf(stderr, "%s\n", &(packet.packet[3])); - } - } - return status; -} diff --git a/src/add-ons/media/plugins/ogg/OggTobiasStream.h b/src/add-ons/media/plugins/ogg/OggTobiasStream.h deleted file mode 100644 index c9e27d531e..0000000000 --- a/src/add-ons/media/plugins/ogg/OggTobiasStream.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _OGG_TOBIAS_STREAM_H -#define _OGG_TOBIAS_STREAM_H - -#include "OggStream.h" - -namespace BPrivate { namespace media { - -class OggTobiasStream : public OggStream { -public: - static bool IsValidHeader(const ogg_packet & packet); -public: - OggTobiasStream(long serialno); - virtual ~OggTobiasStream(); - - virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format); - virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize, - media_header *mediaHeader); - - // reader push input function - virtual status_t AddPage(off_t position, const ogg_page & page); - -private: - media_format fMediaFormat; - double fMicrosecPerFrame; -}; - -} } // namespace BPrivate::media - -using namespace BPrivate::media; - -#endif // _OGG_TOBIAS_STREAM_H diff --git a/src/add-ons/media/plugins/ogg/OggVorbisCodec.cpp b/src/add-ons/media/plugins/ogg/OggVorbisCodec.cpp new file mode 100644 index 0000000000..c4af9f4541 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggVorbisCodec.cpp @@ -0,0 +1,262 @@ +#include "OggVorbisFormats.h" +#include "OggCodecs.h" +#include + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + + +inline size_t +AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */) +{ + return (raf->format & 0xf) * (raf->channel_count) + * (size_t)((raf->frame_rate * buffer_duration) / 1000000.0); +} + + +/* + * vorbis header parsing code from libvorbis/info.c + */ + + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + + +// based on libvorbis/info.c _vorbis_unpack_info +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + vi->version = oggpack_read(opb, 32); + if (vi->version != 0) { + return -1; + } + vi->channels = oggpack_read(opb, 8); + vi->rate = oggpack_read(opb, 32); + vi->bitrate_upper = oggpack_read(opb, 32); + vi->bitrate_nominal = oggpack_read(opb, 32); + vi->bitrate_lower = oggpack_read(opb, 32); + long blocksizes0 = oggpack_read(opb, 4); + long blocksizes1 = oggpack_read(opb, 4); + if (vi->rate < 1) { + return -1; + } + if (vi->channels < 1) { + return -1; + } + if (blocksizes0 < 8) { + return -1; + } + if (blocksizes1 < blocksizes0) { + return -1; + } + if (oggpack_read(opb, 1) != 1) { + return -1; + } /* EOP check */ + return 0; +} + + +/* + * OggVorbisCodec + */ + + +class OggVorbisCodec : public OggCodec { +public: + OggVorbisCodec(); + virtual ~OggVorbisCodec(); + + virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const; + virtual status_t HandlePacket(const ogg_packet & packet); + + virtual status_t GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); +private: + std::vector fMetaDataPackets; + unsigned char * fHeaderPacketData; + unsigned char * fCommentPacketData; + unsigned char * fCodebookPacketData; + unsigned int fPacketCount; +}; + + +OggVorbisCodec::OggVorbisCodec() +{ + TRACE("OggVorbisCodec::OggVorbisCodec\n"); + fHeaderPacketData = NULL; + fCommentPacketData = NULL; + fCodebookPacketData = NULL; + fPacketCount = 0; +} + + +OggVorbisCodec::~OggVorbisCodec() +{ + delete fHeaderPacketData; + delete fCommentPacketData; + delete fCodebookPacketData; +} + + +/* virtual */ bool +OggVorbisCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const +{ + oggpack_buffer opb; + oggpack_readinit(&opb, packet.packet, packet.bytes); + uint packtype = oggpack_read(&opb, 8); + return (packetno == 0 && packtype == 0x01) + || (packetno == 1 && packtype == 0x03) + || (packetno == 2 && packtype == 0x05); +} + + +/* virtual */ status_t +OggVorbisCodec::HandlePacket(const ogg_packet & packet) +{ + TRACE("OggVorbisCodec::HandlePacket\n"); + if (!IsHeaderPacket(packet, fPacketCount)) { + return B_BAD_VALUE; + } + switch (fPacketCount) { + case 0: { + // header packet + 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); + // discard packet type (already validated in IsHeaderPacket) + oggpack_read(&opb, 8); + // 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; + if ((formats.InitCheck() != B_OK) || + (formats.GetFormatFor(description, &fMediaFormat) != B_OK)) { + fMediaFormat = vorbis_encoded_media_format(); + } + + // fill out format from header packet + if (info.bitrate_nominal > 0) { + fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_nominal; + } else if (info.bitrate_upper > 0) { + fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_upper; + } else if (info.bitrate_lower > 0) { + fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_lower; + } + if (info.channels == 1) { + fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT; + } else { + fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + } + fMediaFormat.u.encoded_audio.output.frame_rate = (float)info.rate; + fMediaFormat.u.encoded_audio.output.channel_count = info.channels; + fMediaFormat.u.encoded_audio.output.buffer_size + = AudioBufferSize(&fMediaFormat.u.encoded_audio.output); + + fHeaderPacketData = new unsigned char[packet.bytes]; + memcpy(fHeaderPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fHeaderPacketData; + break; + } + case 1: { + // comment packet + fCommentPacketData = new unsigned char[packet.bytes]; + memcpy(fCommentPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCommentPacketData; + break; + } + case 2: { + // codebook packet + fCodebookPacketData = new unsigned char[packet.bytes]; + memcpy(fCodebookPacketData, packet.packet, packet.bytes); + fMetaDataPackets.push_back(packet); + fMetaDataPackets[fPacketCount].packet = fCodebookPacketData; + + fInitCheck = B_OK; + fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets)); + break; + } + default: + // huh? + break; + } + fPacketCount++; + return B_OK; +} + + +/* virtual */ status_t +OggVorbisCodec::GetChunk(ogg_stream_state * stream, bool streaming, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + if (fCurrentFrame == -1) { + fCurrentTime = -1; + } else { + fCurrentTime = (1000000LL * fCurrentFrame) + / (long long)fMediaFormat.u.encoded_audio.output.frame_rate; + } + return OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader); +} + + +/* + * OggVorbisCodecTest + */ + +static class OggVorbisCodecTest : public OggCodecTest { +public: + OggVorbisCodecTest() {} + virtual ~OggVorbisCodecTest() {} + virtual bool RecognizesInitialPacket(const ogg_packet & packet) const + { + return findIdentifier(packet, "vorbis", 1); + } + virtual OggCodec * InstantiateCodec() const + { + return new OggVorbisCodec(); + } +} ogg_vorbis_codec_test; diff --git a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp deleted file mode 100644 index d3928675cb..0000000000 --- a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "OggVorbisFormats.h" -#include "OggVorbisSeekable.h" -#include - -#define TRACE_THIS 1 -#if TRACE_THIS - #define TRACE printf -#else - #define TRACE(a...) ((void)0) -#endif - -inline size_t -AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */) -{ - return (raf->format & 0xf) * (raf->channel_count) - * (size_t)((raf->frame_rate * buffer_duration) / 1000000.0); -} - -/* - * vorbis header parsing code from libvorbis/info.c - */ - -typedef struct vorbis_info{ - int version; - int channels; - long rate; - - /* The below bitrate declarations are *hints*. - Combinations of the three values carry the following implications: - - all three set to the same value: - implies a fixed rate bitstream - only nominal set: - implies a VBR stream that averages the nominal bitrate. No hard - upper/lower limit - upper and or lower set: - implies a VBR bitstream that obeys the bitrate limits. nominal - may also be set to give a nominal rate. - none set: - the coder does not care to speculate. - */ - - long bitrate_upper; - long bitrate_nominal; - long bitrate_lower; - long bitrate_window; - - void *codec_setup; -} vorbis_info; - - -// based on libvorbis/info.c _vorbis_unpack_info -static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ - vi->version = oggpack_read(opb, 32); - if (vi->version != 0) { - return -1; - } - vi->channels = oggpack_read(opb, 8); - vi->rate = oggpack_read(opb, 32); - vi->bitrate_upper = oggpack_read(opb, 32); - vi->bitrate_nominal = oggpack_read(opb, 32); - vi->bitrate_lower = oggpack_read(opb, 32); - long blocksizes0 = oggpack_read(opb, 4); - long blocksizes1 = oggpack_read(opb, 4); - if (vi->rate < 1) { - return -1; - } - if (vi->channels < 1) { - return -1; - } - if (blocksizes0 < 8) { - return -1; - } - if (blocksizes1 < blocksizes0) { - return -1; - } - if (oggpack_read(opb, 1) != 1) { - return -1; - } /* EOP check */ - return 0; -} - - -/* - * OggVorbisSeekable implementations - */ - -/* static */ bool -OggVorbisSeekable::IsValidHeader(const ogg_packet & packet) -{ - return findIdentifier(packet,"vorbis",1); -} - -OggVorbisSeekable::OggVorbisSeekable(long serialno) - : OggSeekable(serialno) -{ - TRACE("OggVorbisSeekable::OggVorbisSeekable\n"); -} - -OggVorbisSeekable::~OggVorbisSeekable() -{ - TRACE("OggVorbisSeekable::~OggVorbisSeekable\n"); -} - -status_t -OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format) -{ - TRACE("OggVorbisSeekable::GetStreamInfo\n"); - status_t result = B_OK; - ogg_packet packet; - - // get header packet - if (GetHeaderPackets().size() < 1) { - result = GetPacket(&packet); - if (result != B_OK) { - return result; - } - SaveHeaderPacket(packet); - } - packet = GetHeaderPackets()[0]; - if (!packet.b_o_s) { - return B_ERROR; // first packet was not beginning of stream - } - - // parse header packet - // based on libvorbis/info.c vorbis_synthesis_headerin(...) - oggpack_buffer opb; - oggpack_readinit(&opb, packet.packet, packet.bytes); - int packtype = oggpack_read(&opb, 8); - if (packtype != 0x01) { - return B_ERROR; // first packet was not an info packet - } - // discard vorbis string - for (uint i = 0 ; i < sizeof("vorbis") - 1 ; i++) { - oggpack_read(&opb, 8); - } - vorbis_info info; - if (_vorbis_unpack_info(&info, &opb) != 0) { - return B_ERROR; // couldn't unpack info - } - - // get the format for the description - media_format_description description = vorbis_description(); - BMediaFormats formats; - result = formats.InitCheck(); - if (result == B_OK) { - result = formats.GetFormatFor(description, format); - } - if (result != B_OK) { - *format = vorbis_encoded_media_format(); - // ignore error, allow user to use ReadChunk interface - } - - // fill out format from header packet - if (info.bitrate_nominal > 0) { - format->u.encoded_audio.bit_rate = info.bitrate_nominal; - } else if (info.bitrate_upper > 0) { - format->u.encoded_audio.bit_rate = info.bitrate_upper; - } else if (info.bitrate_lower > 0) { - format->u.encoded_audio.bit_rate = info.bitrate_lower; - } - if (info.channels == 1) { - format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT; - } else { - format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT; - } - fFrameRate = 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())); - - // TODO: count the frames in the first page.. somehow.. :-/ - int64 frames = 0; - - ogg_page page; - // read the first page - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 fFirstGranulepos = ogg_page_granulepos(&page); - TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos); - // read our last page - off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET); - if (last < 0) { - return last; - } - result = ReadPage(&page); - if (result != B_OK) { - return result; - } - int64 last_granulepos = ogg_page_granulepos(&page); - - // seek back to the start - int64 frame = 0; - bigtime_t time = 0; - result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time); - if (result != B_OK) { - return result; - } - - // compute frame count and duration from sample count - frames = last_granulepos - fFirstGranulepos; - - *frameCount = frames; - *duration = (1000000LL * frames) / (long long)fFrameRate; - - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h b/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h deleted file mode 100644 index 76081c71ef..0000000000 --- a/src/add-ons/media/plugins/ogg/OggVorbisSeekable.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _OGG_VORBIS_SEEKABLE_H -#define _OGG_VORBIS_SEEKABLE_H - -#include "OggSeekable.h" - -namespace BPrivate { namespace media { - -class OggVorbisSeekable : public OggSeekable { -private: - typedef OggSeekable inherited; -public: - static bool IsValidHeader(const ogg_packet & packet); -public: - OggVorbisSeekable(long serialno); - virtual ~OggVorbisSeekable(); - - virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format); -}; - -} } // namespace BPrivate::media - -using namespace BPrivate::media; - -#endif // _OGG_VORBIS_SEEKABLE_H diff --git a/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp b/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp deleted file mode 100644 index 7b190b16ff..0000000000 --- a/src/add-ons/media/plugins/ogg/OggVorbisStream.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "OggVorbisFormats.h" -#include "OggVorbisStream.h" -#include - -#define TRACE_THIS 1 -#if TRACE_THIS - #define TRACE printf -#else - #define TRACE(a...) ((void)0) -#endif - -inline size_t -AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */) -{ - return (raf->format & 0xf) * (raf->channel_count) - * (size_t)((raf->frame_rate * buffer_duration) / 1000000.0); -} - -/* - * vorbis header parsing code from libvorbis/info.c - */ - -typedef struct vorbis_info{ - int version; - int channels; - long rate; - - /* The below bitrate declarations are *hints*. - Combinations of the three values carry the following implications: - - all three set to the same value: - implies a fixed rate bitstream - only nominal set: - implies a VBR stream that averages the nominal bitrate. No hard - upper/lower limit - upper and or lower set: - implies a VBR bitstream that obeys the bitrate limits. nominal - may also be set to give a nominal rate. - none set: - the coder does not care to speculate. - */ - - long bitrate_upper; - long bitrate_nominal; - long bitrate_lower; - long bitrate_window; - - void *codec_setup; -} vorbis_info; - -// based on libvorbis/info.c _vorbis_unpack_info -static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ - vi->version = oggpack_read(opb, 32); - if (vi->version != 0) { - return -1; - } - vi->channels = oggpack_read(opb, 8); - vi->rate = oggpack_read(opb, 32); - vi->bitrate_upper = oggpack_read(opb, 32); - vi->bitrate_nominal = oggpack_read(opb, 32); - vi->bitrate_lower = oggpack_read(opb, 32); - long blocksizes0 = oggpack_read(opb, 4); - long blocksizes1 = oggpack_read(opb, 4); - if (vi->rate < 1) { - return -1; - } - if (vi->channels < 1) { - return -1; - } - if (blocksizes0 < 8) { - return -1; - } - if (blocksizes1 < blocksizes0) { - return -1; - } - if (oggpack_read(opb, 1) != 1) { - return -1; - } /* EOP check */ - return 0; -} - -/* - * OggVorbisStream implementations - */ - -/* static */ bool -OggVorbisStream::IsValidHeader(const ogg_packet & packet) -{ - return findIdentifier(packet,"vorbis",1); -} - -OggVorbisStream::OggVorbisStream(long serialno) - : OggStream(serialno) -{ - TRACE("OggVorbisStream::OggVorbisStream\n"); -} - -OggVorbisStream::~OggVorbisStream() -{ - TRACE("OggVorbisStream::~OggVorbisStream\n"); -} - -status_t -OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format) -{ - TRACE("OggVorbisStream::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 - *duration = 5 * 60 * 1000000; - *frameCount = *duration * (long long)format->u.encoded_audio.output.frame_rate; - - return B_OK; -} diff --git a/src/add-ons/media/plugins/ogg/OggVorbisStream.h b/src/add-ons/media/plugins/ogg/OggVorbisStream.h deleted file mode 100644 index 7d13c7712a..0000000000 --- a/src/add-ons/media/plugins/ogg/OggVorbisStream.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _OGG_VORBIS_STREAM_H -#define _OGG_VORBIS_STREAM_H - -#include "OggStream.h" - -namespace BPrivate { namespace media { - -class OggVorbisStream : public OggStream { -public: - static bool IsValidHeader(const ogg_packet & packet); -public: - OggVorbisStream(long serialno); - virtual ~OggVorbisStream(); - - virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration, - media_format *format); -}; - -} } // namespace BPrivate::media - -using namespace BPrivate::media; - -#endif // _OGG_VORBIS_STREAM_H