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
This commit is contained in:
shatty
2004-01-20 06:10:42 +00:00
parent 51f421dfee
commit 830ac450b6
@@ -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<ogg_packet> * 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<ogg_packet> * 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<ogg_packet> * 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<ogg_packet> * packets,
@@ -125,7 +280,10 @@ GetTobiasCodecStreamInfo(std::vector<ogg_packet> * 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;
}