From 830ac450b6382ed0d49d21e68e3315be1587ebd5 Mon Sep 17 00:00:00 2001 From: shatty Date: Tue, 20 Jan 2004 06:10:42 +0000 Subject: [PATCH] ogg complete parsing of speex, tobias video, vorbis headers, including "friendly" strings for MediaPlayer missing codec errors git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6184 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../plugins/ogg/OggReaderPluginCodecs.cpp | 176 +++++++++++++++++- 1 file changed, 167 insertions(+), 9 deletions(-) diff --git a/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp b/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp index 113b2c3729..7208947677 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp +++ b/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp @@ -20,8 +20,67 @@ findIdentifier(ogg_packet * packet, const char * id, uint location) return !memcmp(&packet->packet[location], id, length); } +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 ----*/ +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); + oggpack_read(opb, 4); // discard blocksizes 0 + oggpack_read(opb, 4); // discard blocksizes 1 + if (vi->rate < 1) { + return -1; + } + if (vi->channels < 1) { + return -1; + } + if (oggpack_read(opb, 1) != 1) { + return -1; + } /* EOP check */ + return 0; +} static bool isVorbisPacket(ogg_packet * packet) @@ -29,16 +88,57 @@ isVorbisPacket(ogg_packet * packet) return findIdentifier(packet,"vorbis",1); } -static const int vorbis_extra_packet_count = 2; +static int +GetVorbisExtraHeaderPacketCount(ogg_packet * packet) +{ + return 2; +} static status_t GetVorbisCodecStreamInfo(std::vector * packets, int64 *frameCount, bigtime_t *duration, media_format *format) { + ogg_packet * packet = &((*packets)[0]); + + // based on libvorbis/info.c vorbis_synthesis_headerin(...) + oggpack_buffer opb; + oggpack_readinit(&opb, packet->packet, packet->bytes); + int packtype = oggpack_read(&opb, 8); + // discard vorbis string + for (uint i = 0 ; i < sizeof("vorbis") - 1 ; i++) { + oggpack_read(&opb, 8); + } + if (packtype != 0x01) { + return B_ERROR; // first packet was not an info packet + } + if (!packet->b_o_s) { + return B_ERROR; // first packet was not beginning of stream + } + vorbis_info info; + if (_vorbis_unpack_info(&info, &opb) != 0) { + return B_ERROR; // couldn't unpack info + } + format->type = B_MEDIA_ENCODED_AUDIO; + format->user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)format->user_data, "vorb", 4); format->u.encoded_audio.encoding - = (media_encoded_audio_format::audio_encoding)'vorb'; + = (media_encoded_audio_format::audio_encoding)'vorb'; + 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; + } + format->u.encoded_audio.frame_size = sizeof(ogg_packet); + format->u.encoded_audio.output.frame_rate = (float)info.rate; + format->u.encoded_audio.output.channel_count = info.channels; + format->u.encoded_audio.output.format = media_raw_audio_format::B_AUDIO_FLOAT; + format->u.encoded_audio.output.byte_order = B_MEDIA_HOST_ENDIAN; + format->u.encoded_audio.output.buffer_size + = AudioBufferSize(&format->u.encoded_audio.output); return B_OK; } @@ -46,22 +146,73 @@ GetVorbisCodecStreamInfo(std::vector * packets, /*---- speex ----*/ +// http://www.speex.org/manual/node7.html#SECTION00073000000000000000 +typedef struct speex_header +{ + char speex_string[8]; + char speex_version[20]; + int32 speex_version_id; + int32 header_size; + int32 rate; + int32 mode; + int32 mode_bitstream_version; + int32 nb_channels; + int32 bitrate; + int32 frame_size; + int32 vbr; + int32 frames_per_packet; + int32 extra_headers; + int32 reserved1; + int32 reserved2; +} speex_header; + static bool isSpeexPacket(ogg_packet * packet) { - return findIdentifier(packet,"Speex",0); + return findIdentifier(packet,"Speex ",0); } -static const int speex_extra_packet_count = 2; +static int +GetSpeexExtraHeaderPacketCount(ogg_packet * packet) +{ + return 1; +} static status_t GetSpeexCodecStreamInfo(std::vector * packets, int64 *frameCount, bigtime_t *duration, media_format *format) { + ogg_packet * packet = &((*packets)[0]); + if (packet->bytes < (signed)sizeof(speex_header)) { + return B_ERROR; + } + void * data = &(packet->packet[0]); + speex_header * header = (speex_header *)data; + format->type = B_MEDIA_ENCODED_AUDIO; + format->user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)format->user_data,"Spee",4); format->u.encoded_audio.encoding - = (media_encoded_audio_format::audio_encoding)'Spee'; + = (media_encoded_audio_format::audio_encoding)'Spee'; + if (header->bitrate > 0) { + format->u.encoded_audio.bit_rate = header->bitrate; + } else { + // TODO: manually compute it where possible + } + format->u.encoded_audio.frame_size = header->frame_size; + 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.frame_size = sizeof(ogg_packet); + format->u.encoded_audio.output.frame_rate = header->rate; + format->u.encoded_audio.output.channel_count = header->nb_channels; + format->u.encoded_audio.output.format = media_raw_audio_format::B_AUDIO_FLOAT; + format->u.encoded_audio.output.byte_order = B_MEDIA_HOST_ENDIAN; + format->u.encoded_audio.output.buffer_size + = AudioBufferSize(&format->u.encoded_audio.output); return B_OK; } @@ -112,7 +263,11 @@ isTobiasPacket(ogg_packet * packet) return findIdentifier(packet,"video",1); } -static const int tobias_extra_packet_count = 1; +static int +GetTobiasExtraHeaderPacketCount(ogg_packet * packet) +{ + return 1; +} static status_t GetTobiasCodecStreamInfo(std::vector * packets, @@ -125,7 +280,10 @@ GetTobiasCodecStreamInfo(std::vector * packets, } void * data = &(packet->packet[1]); tobias_stream_header * header = (tobias_stream_header *)data; + format->type = B_MEDIA_ENCODED_VIDEO; + format->user_data_type = B_CODEC_TYPE_INFO; + strncpy((char*)format->user_data,header->subtype,4); int32 encoding = header->subtype[3] << 24 | header->subtype[2] << 16 | header->subtype[1] << 8 | header->subtype[0]; format->u.encoded_video.encoding @@ -145,13 +303,13 @@ int oggReader::GetExtraHeaderPacketCount(ogg_packet * packet) { if (isVorbisPacket(packet)) { - return vorbis_extra_packet_count; + return GetVorbisExtraHeaderPacketCount(packet); } if (isTobiasPacket(packet)) { - return tobias_extra_packet_count; + return GetTobiasExtraHeaderPacketCount(packet); } if (isSpeexPacket(packet)) { - return speex_extra_packet_count; + return GetSpeexExtraHeaderPacketCount(packet); } return 0; }