diff --git a/src/add-ons/media/plugins/ogg/Jamfile b/src/add-ons/media/plugins/ogg/Jamfile index e1cc3977b4..8ea6c2ab97 100644 --- a/src/add-ons/media/plugins/ogg/Jamfile +++ b/src/add-ons/media/plugins/ogg/Jamfile @@ -6,6 +6,7 @@ SubDirHdrs [ FDirName $(SUBDIR) libogg ] ; Addon ogg : media plugins : OggReaderPlugin.cpp + OggReaderPluginCodecs.cpp : false : libogg.a ; diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp index 3c28354b30..5bf2c0fa74 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp @@ -15,24 +15,6 @@ #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"); @@ -187,7 +169,7 @@ oggReader::Sniff(int32 *streamCount) memcpy(buffer, packet.packet, packet.bytes); packet.packet = buffer; fHeaderPackets[serialno].push_back(packet); - fHeaderPacketsLeft[serialno] = getPacketsLeft(&packet); + fHeaderPacketsLeft[serialno] = GetExtraHeaderPacketCount(&packet); if (GetPage(&page) != B_OK) { return B_ERROR; } @@ -272,22 +254,6 @@ oggReader::FreeCookie(void *cookie) return B_OK; } -// estimate: 1 frame = 256 bytes -int filePositionToFrame(int position) { - return position/256; -} -int frameToFilePosition(int frame) { - return frame*256; -} - -// estimate: 1 second = 8 bytes -int filePositionToTime(int position) { - return position*1000/8; -} -int timeToFilePosition(int duration) { - return duration*8/1000; -} - status_t oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, media_format *format, void **infoBuffer, int32 *infoSize) @@ -295,28 +261,9 @@ oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, TRACE("oggReader::GetStreamInfo\n"); ogg_stream_state * stream = static_cast(cookie); memset(format, 0, sizeof(*format)); - if (fSeekable) { - off_t input_length; - { - off_t current_position = fSeekable->Seek(0,SEEK_CUR); - input_length = fSeekable->Seek(0,SEEK_END)-fPostSniffPosition; - fSeekable->Seek(current_position,SEEK_SET); - } - *frameCount = filePositionToFrame(input_length); - *duration = filePositionToTime(input_length); - } else { - assert(sizeof(bigtime_t)==sizeof(uint64_t)); - // if the input is not seekable, we return really large sizes - *frameCount = INT64_MAX; - *duration = INT64_MAX; - } 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'; - } + GetCodecStreamInfo(stream,frameCount,duration,format); *infoBuffer = 0; *infoSize = 0; return B_OK; @@ -336,9 +283,9 @@ oggReader::Seek(void *cookie, ogg_stream_state * stream = static_cast(cookie); int position; if (seekTo & B_MEDIA_SEEK_TO_FRAME) { - position = fPostSniffPosition+frameToFilePosition(*frame); + position = fPostSniffPosition+frameToPosition(stream,*frame); } else if (seekTo & B_MEDIA_SEEK_TO_TIME) { - position = fPostSniffPosition+timeToFilePosition(*time); + position = fPostSniffPosition+timeToPosition(stream,*time); } else { return B_ERROR; } @@ -391,8 +338,8 @@ synced: return B_ERROR; } // output the values from where we regained sync - *frame = filePositionToFrame(position); - *time = filePositionToTime(position); + *frame = positionToFrame(stream,position); + *time = positionToTime(stream,position); return B_OK; } diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h index 17d73ef903..7f5129a1da 100644 --- a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h @@ -42,6 +42,15 @@ public: private: status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE, bool short_page = false); + int GetExtraHeaderPacketCount(ogg_packet * packet); + status_t GetCodecStreamInfo(ogg_stream_state * stream, + int64 *frameCount, bigtime_t *duration, + media_format *format); + + int64 positionToFrame(ogg_stream_state * stream, off_t position); + off_t frameToPosition(ogg_stream_state * stream, int64 frame); + bigtime_t positionToTime(ogg_stream_state * stream, off_t position); + off_t timeToPosition(ogg_stream_state * stream, bigtime_t time); BPositionIO * fSeekable; off_t fPostSniffPosition; diff --git a/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp b/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp new file mode 100644 index 0000000000..113b2c3729 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggReaderPluginCodecs.cpp @@ -0,0 +1,219 @@ +#include +#include "OggReaderPlugin.h" + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) ((void)0) +#endif + +// codec identification + +static bool +findIdentifier(ogg_packet * packet, const char * id, uint location) +{ + uint length = strlen(id); + if ((unsigned)packet->bytes < location+length) { + return false; + } + return !memcmp(&packet->packet[location], id, length); +} + +/*---- vorbis ----*/ + + +static bool +isVorbisPacket(ogg_packet * packet) +{ + return findIdentifier(packet,"vorbis",1); +} + +static const int vorbis_extra_packet_count = 2; + +static status_t +GetVorbisCodecStreamInfo(std::vector * packets, + int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + format->type = B_MEDIA_ENCODED_AUDIO; + format->u.encoded_audio.encoding + = (media_encoded_audio_format::audio_encoding)'vorb'; + return B_OK; +} + + +/*---- speex ----*/ + + +static bool +isSpeexPacket(ogg_packet * packet) +{ + return findIdentifier(packet,"Speex",0); +} + +static const int speex_extra_packet_count = 2; + +static status_t +GetSpeexCodecStreamInfo(std::vector * packets, + int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + format->type = B_MEDIA_ENCODED_AUDIO; + format->u.encoded_audio.encoding + = (media_encoded_audio_format::audio_encoding)'Spee'; + return B_OK; +} + + +/*---- tobias ----*/ + + +// 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 + 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 bool +isTobiasPacket(ogg_packet * packet) +{ + return findIdentifier(packet,"video",1); +} + +static const int tobias_extra_packet_count = 1; + +static status_t +GetTobiasCodecStreamInfo(std::vector * packets, + int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + ogg_packet * packet = &((*packets)[0]); + if (packet->bytes < (signed)sizeof(tobias_stream_header)) { + return B_ERROR; + } + void * data = &(packet->packet[1]); + tobias_stream_header * header = (tobias_stream_header *)data; + format->type = B_MEDIA_ENCODED_VIDEO; + int32 encoding = header->subtype[3] << 24 | header->subtype[2] << 16 + | header->subtype[1] << 8 | header->subtype[0]; + format->u.encoded_video.encoding + = (media_encoded_video_format::video_encoding)encoding; + format->u.encoded_video.frame_size + = header->video.width * header->video.height; + format->u.encoded_video.output.display.line_width = header->video.width; + format->u.encoded_video.output.display.line_count = header->video.height; + // TODO: wring more info out of the headers + return B_OK; +} + +/*---- generic routines ----*/ + + +int +oggReader::GetExtraHeaderPacketCount(ogg_packet * packet) +{ + if (isVorbisPacket(packet)) { + return vorbis_extra_packet_count; + } + if (isTobiasPacket(packet)) { + return tobias_extra_packet_count; + } + if (isSpeexPacket(packet)) { + return speex_extra_packet_count; + } + return 0; +} + +status_t +oggReader::GetCodecStreamInfo(ogg_stream_state * stream, + int64 *frameCount, bigtime_t *duration, + media_format *format) +{ + std::vector * packets = &fHeaderPackets[stream->serialno]; + if (fSeekable) { + off_t input_length; + { + off_t current_position = fSeekable->Seek(0,SEEK_CUR); + input_length = fSeekable->Seek(0,SEEK_END)-fPostSniffPosition; + fSeekable->Seek(current_position,SEEK_SET); + } + *frameCount = positionToFrame(stream,input_length); + *duration = positionToTime(stream,input_length); + } else { + assert(sizeof(bigtime_t)==sizeof(uint64_t)); + // if the input is not seekable, we return really large sizes + *frameCount = INT64_MAX; + *duration = INT64_MAX; + } + if (!packets || packets->size() < 1) { + return B_BAD_VALUE; + } + ogg_packet * packet = &((*packets)[0]); + if (isVorbisPacket(packet)) { + return GetVorbisCodecStreamInfo(packets,frameCount,duration,format); + } + if (isTobiasPacket(packet)) { + return GetTobiasCodecStreamInfo(packets,frameCount,duration,format); + } + if (isSpeexPacket(packet)) { + return GetSpeexCodecStreamInfo(packets,frameCount,duration,format); + } + return B_OK; +} + +// estimate: 1 frame = 256 bytes +int64 +oggReader::positionToFrame(ogg_stream_state * stream, off_t position) +{ + return position/256; +} + +off_t +oggReader::frameToPosition(ogg_stream_state * stream, int64 frame) +{ + return frame*256; +} + +bigtime_t +oggReader::positionToTime(ogg_stream_state * stream, off_t position) +{ + return position*1000/8; +} + +off_t +oggReader::timeToPosition(ogg_stream_state * stream, bigtime_t time) +{ + return time*8/1000; +}