diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp index e5d05dd539..3c28354b30 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp @@ -15,6 +15,24 @@ #define TRACE(a...) ((void)0) #endif +// codec identification +static bool isVorbisPacket(ogg_packet * packet) { + static const char * vorbis = "vorbis"; + if ((unsigned)packet->bytes < (sizeof(vorbis)+1)) { + return false; + } + return !memcmp(&packet->packet[1], vorbis, sizeof(vorbis)); +} + +// codec-dependent packet gobbling for extra header packets +static int getPacketsLeft(ogg_packet * packet) { + if (isVorbisPacket(packet)) { + return 2; + } + return 0; +} + + oggReader::oggReader() { TRACE("oggReader::oggReader\n"); @@ -164,14 +182,38 @@ oggReader::Sniff(int32 *streamCount) return B_ERROR; #endif STRICT_OGG } + // save a copy of the packet information unsigned char * buffer = new unsigned char[packet.bytes]; - fInitialHeaderPackets[serialno] = packet; - memcpy(buffer,packet.packet,packet.bytes); - fInitialHeaderPackets[serialno].packet = buffer; - if (GetPage(&page,4096,short_page) != B_OK) { + memcpy(buffer, packet.packet, packet.bytes); + packet.packet = buffer; + fHeaderPackets[serialno].push_back(packet); + fHeaderPacketsLeft[serialno] = getPacketsLeft(&packet); + if (GetPage(&page) != B_OK) { return B_ERROR; } } + packet_count_map::iterator iter = fHeaderPacketsLeft.begin(); + while (iter != fHeaderPacketsLeft.end()) { + int current_serial = iter->first; + while (fHeaderPacketsLeft[current_serial] > 0) { + int serialno = ogg_page_serialno(&page); + if (fHeaderPacketsLeft[serialno] > 0) { + ogg_stream_state * stream = fStreams[serialno]; + ogg_packet packet; + ogg_stream_packetout(stream,&packet); + // save a copy of the packet information + unsigned char * buffer = new unsigned char[packet.bytes]; + memcpy(buffer, packet.packet, packet.bytes); + packet.packet = buffer; + fHeaderPackets[serialno].push_back(packet); + fHeaderPacketsLeft[serialno]--; + } + if (GetPage(&page) != B_OK) { + return B_ERROR; + } + } + iter++; + } *streamCount = fStreams.size(); if (fSeekable) { fPostSniffPosition = fSeekable->Seek(0,SEEK_CUR); @@ -257,7 +299,7 @@ oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, off_t input_length; { off_t current_position = fSeekable->Seek(0,SEEK_CUR); - input_length = fSeekable->Seek(0,SEEK_END); + input_length = fSeekable->Seek(0,SEEK_END)-fPostSniffPosition; fSeekable->Seek(current_position,SEEK_SET); } *frameCount = filePositionToFrame(input_length); @@ -268,8 +310,13 @@ oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, *frameCount = INT64_MAX; *duration = INT64_MAX; } - ogg_packet * packet = &fInitialHeaderPackets[stream->serialno]; - format->SetMetaData((void*)packet,sizeof(ogg_packet)); + std::vector * ogg_packets = &fHeaderPackets[stream->serialno]; + format->SetMetaData((void*)ogg_packets,sizeof(ogg_packets)); + if (isVorbisPacket(&(*ogg_packets)[0])) { + format->type = B_MEDIA_ENCODED_AUDIO; + format->u.encoded_audio.encoding + = (media_encoded_audio_format::audio_encoding)'vorb'; + } *infoBuffer = 0; *infoSize = 0; return B_OK; @@ -289,9 +336,9 @@ oggReader::Seek(void *cookie, ogg_stream_state * stream = static_cast(cookie); int position; if (seekTo & B_MEDIA_SEEK_TO_FRAME) { - position = frameToFilePosition(*frame); + position = fPostSniffPosition+frameToFilePosition(*frame); } else if (seekTo & B_MEDIA_SEEK_TO_TIME) { - position = timeToFilePosition(*time); + position = fPostSniffPosition+timeToFilePosition(*time); } else { return B_ERROR; } diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h index a6597e53ab..17d73ef903 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h @@ -4,11 +4,14 @@ #include "ReaderPlugin.h" #include "ogg/ogg.h" #include +#include namespace BPrivate { namespace media { typedef std::map ogg_stream_map; typedef std::map ogg_packet_map; +typedef std::map > ogg_packets_map; +typedef std::map packet_count_map; class oggReader : public Reader { @@ -44,7 +47,8 @@ private: off_t fPostSniffPosition; ogg_sync_state fSync; - ogg_packet_map fInitialHeaderPackets; + ogg_packets_map fHeaderPackets; + packet_count_map fHeaderPacketsLeft; ogg_stream_map fStreams; ogg_packet_map fPackets; }; diff --git a/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.cpp b/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.cpp index 168681bb04..0f6ef18360 100644 --- a/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.cpp +++ b/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "vorbisCodecPlugin.h" #define TRACE_THIS 1 @@ -27,10 +28,6 @@ vorbisDecoder::vorbisDecoder() vorbis_info_init(&fInfo); vorbis_comment_init(&fComment); - fHeaderPacketParsed = false; - fCommentPacketParsed = false; - fCodebookPacketParsed = false; - fStartTime = 0; fFrameSize = 0; fOutputBufferSize = 0; @@ -47,79 +44,43 @@ status_t vorbisDecoder::Setup(media_format *inputFormat, const void *infoBuffer, int32 infoSize) { - if ((inputFormat->type != B_MEDIA_UNKNOWN_TYPE) - && (inputFormat->type != B_MEDIA_ENCODED_AUDIO)) { - TRACE("vorbisDecoder::Setup not called with audio/unknown stream: not vorbis\n"); + if (inputFormat->type != B_MEDIA_ENCODED_AUDIO) { + TRACE("vorbisDecoder::Setup not called with audio stream: not vorbis\n"); return B_ERROR; } - if (inputFormat->MetaDataSize() == sizeof(ogg_packet)) { - if (!fHeaderPacketParsed) { - // header packet passed in meta data - ogg_packet * packet = (ogg_packet*)inputFormat->MetaData(); - // parse header packet - if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) { - TRACE("vorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header\n"); - return B_ERROR; - } - fHeaderPacketParsed = true; - } - } else if (inputFormat->MetaDataSize() != 0) { - // unexpected meta data + if (inputFormat->u.encoded_audio.encoding != 'vorb') { + TRACE("vorbisDecoder::Setup not called with 'vorb' stream: not vorbis\n"); return B_ERROR; - } - return InitializeInput(inputFormat); -} - -status_t -vorbisDecoder::InitializeInput(media_format *ioEncodedFormat) -{ - ogg_packet * packet; - int32 size; - media_header mh; - if (!fHeaderPacketParsed) { - // get header packet - if (GetNextChunk((void**)&packet, &size, &mh) != B_OK) { - TRACE("vorbisDecoder::Setup: GetNextChunk failed to get comment\n"); - return B_ERROR; - } - // parse header packet - if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) { - TRACE("vorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header\n"); - return B_ERROR; - } - fHeaderPacketParsed = true; } - if (!fCommentPacketParsed) { - // get comment packet - if (GetNextChunk((void**)&packet, &size, &mh) != B_OK) { - TRACE("vorbisDecoder::Setup: GetNextChunk failed to get comment\n"); - return B_ERROR; - } - // parse comment packet - if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) { - TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis comment\n"); - return B_ERROR; - } - fCommentPacketParsed = true; + if (inputFormat->MetaDataSize() != sizeof(std::vector *)) { + TRACE("vorbisDecoder::Setup not called with ogg_packet meta data: not vorbis\n"); + return B_ERROR; } - if (!fCodebookPacketParsed) { - // get codebook packet - if (GetNextChunk((void**)&packet, &size, &mh) != B_OK) { - TRACE("vorbisDecoder::Setup: GetNextChunk failed to get codebook\n"); - return B_ERROR; - } - // parse codebook packet - if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) { - TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis codebook\n"); - return B_ERROR; - } - // initialize decoder - vorbis_synthesis_init(&fDspState,&fInfo); - vorbis_block_init(&fDspState,&fBlock); + std::vector * packets = (std::vector *)inputFormat->MetaData(); + if (packets->size() != 3) { + TRACE("vorbisDecoder::Setup not called with three ogg_packets: not vorbis\n"); + return B_ERROR; } + // parse header packet + if (vorbis_synthesis_headerin(&fInfo,&fComment,&(*packets)[0]) != 0) { + TRACE("vorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header\n"); + return B_ERROR; + } + // parse comment packet + if (vorbis_synthesis_headerin(&fInfo,&fComment,&(*packets)[1]) != 0) { + TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis comment\n"); + return B_ERROR; + } + // parse codebook packet + if (vorbis_synthesis_headerin(&fInfo,&fComment,&(*packets)[2]) != 0) { + TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis codebook\n"); + return B_ERROR; + } + // initialize decoder + vorbis_synthesis_init(&fDspState,&fInfo); + vorbis_block_init(&fDspState,&fBlock); // fill out the encoding format - CopyInfoToEncodedFormat(ioEncodedFormat); - + CopyInfoToEncodedFormat(inputFormat); return B_OK; } @@ -263,7 +224,6 @@ vorbisDecoderPlugin::NewDecoder() status_t vorbisDecoderPlugin::RegisterPlugin() { - PublishDecoder("unknown", "vorbis", "vorbis decoder, based on libvorbis"); PublishDecoder("audiocodec/vorbis", "vorbis", "vorbis decoder, based on libvorbis"); return B_OK; } diff --git a/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.h b/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.h index b35fbeaa01..6f920fc957 100644 --- a/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.h +++ b/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.h @@ -11,8 +11,6 @@ public: status_t Setup(media_format *inputFormat, const void *infoBuffer, int32 infoSize); - status_t InitializeInput(media_format * ioEncodedFormat); - status_t NegotiateOutputFormat(media_format *ioDecodedFormat); status_t Seek(uint32 seekTo, @@ -27,9 +25,6 @@ private: void CopyInfoToEncodedFormat(media_format * format); void CopyInfoToDecodedFormat(media_raw_audio_format * raf); - bool fHeaderPacketParsed; - bool fCommentPacketParsed; - bool fCodebookPacketParsed; vorbis_info fInfo; vorbis_comment fComment; vorbis_dsp_state fDspState;