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:
@@ -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';
|
||||
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';
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user