separate out codec id and properties into a different file, add identification and format setup for tobias video (typically mpeg4) and speex codec (by observation, not spec)
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6175 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,6 +6,7 @@ SubDirHdrs [ FDirName $(SUBDIR) libogg ] ;
|
|||||||
|
|
||||||
Addon ogg : media plugins :
|
Addon ogg : media plugins :
|
||||||
OggReaderPlugin.cpp
|
OggReaderPlugin.cpp
|
||||||
|
OggReaderPluginCodecs.cpp
|
||||||
: false : libogg.a
|
: false : libogg.a
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -15,24 +15,6 @@
|
|||||||
#define TRACE(a...) ((void)0)
|
#define TRACE(a...) ((void)0)
|
||||||
#endif
|
#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()
|
oggReader::oggReader()
|
||||||
{
|
{
|
||||||
TRACE("oggReader::oggReader\n");
|
TRACE("oggReader::oggReader\n");
|
||||||
@@ -187,7 +169,7 @@ oggReader::Sniff(int32 *streamCount)
|
|||||||
memcpy(buffer, packet.packet, packet.bytes);
|
memcpy(buffer, packet.packet, packet.bytes);
|
||||||
packet.packet = buffer;
|
packet.packet = buffer;
|
||||||
fHeaderPackets[serialno].push_back(packet);
|
fHeaderPackets[serialno].push_back(packet);
|
||||||
fHeaderPacketsLeft[serialno] = getPacketsLeft(&packet);
|
fHeaderPacketsLeft[serialno] = GetExtraHeaderPacketCount(&packet);
|
||||||
if (GetPage(&page) != B_OK) {
|
if (GetPage(&page) != B_OK) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
@@ -272,22 +254,6 @@ oggReader::FreeCookie(void *cookie)
|
|||||||
return B_OK;
|
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
|
status_t
|
||||||
oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
|
oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
|
||||||
media_format *format, void **infoBuffer, int32 *infoSize)
|
media_format *format, void **infoBuffer, int32 *infoSize)
|
||||||
@@ -295,28 +261,9 @@ oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
|
|||||||
TRACE("oggReader::GetStreamInfo\n");
|
TRACE("oggReader::GetStreamInfo\n");
|
||||||
ogg_stream_state * stream = static_cast<ogg_stream_state *>(cookie);
|
ogg_stream_state * stream = static_cast<ogg_stream_state *>(cookie);
|
||||||
memset(format, 0, sizeof(*format));
|
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_packet> * ogg_packets = &fHeaderPackets[stream->serialno];
|
std::vector<ogg_packet> * ogg_packets = &fHeaderPackets[stream->serialno];
|
||||||
format->SetMetaData((void*)ogg_packets,sizeof(ogg_packets));
|
format->SetMetaData((void*)ogg_packets,sizeof(ogg_packets));
|
||||||
if (isVorbisPacket(&(*ogg_packets)[0])) {
|
GetCodecStreamInfo(stream,frameCount,duration,format);
|
||||||
format->type = B_MEDIA_ENCODED_AUDIO;
|
|
||||||
format->u.encoded_audio.encoding
|
|
||||||
= (media_encoded_audio_format::audio_encoding)'vorb';
|
|
||||||
}
|
|
||||||
*infoBuffer = 0;
|
*infoBuffer = 0;
|
||||||
*infoSize = 0;
|
*infoSize = 0;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -336,9 +283,9 @@ oggReader::Seek(void *cookie,
|
|||||||
ogg_stream_state * stream = static_cast<ogg_stream_state *>(cookie);
|
ogg_stream_state * stream = static_cast<ogg_stream_state *>(cookie);
|
||||||
int position;
|
int position;
|
||||||
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
|
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
|
||||||
position = fPostSniffPosition+frameToFilePosition(*frame);
|
position = fPostSniffPosition+frameToPosition(stream,*frame);
|
||||||
} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
|
} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
|
||||||
position = fPostSniffPosition+timeToFilePosition(*time);
|
position = fPostSniffPosition+timeToPosition(stream,*time);
|
||||||
} else {
|
} else {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
@@ -391,8 +338,8 @@ synced:
|
|||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
// output the values from where we regained sync
|
// output the values from where we regained sync
|
||||||
*frame = filePositionToFrame(position);
|
*frame = positionToFrame(stream,position);
|
||||||
*time = filePositionToTime(position);
|
*time = positionToTime(stream,position);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ public:
|
|||||||
private:
|
private:
|
||||||
status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE,
|
status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE,
|
||||||
bool short_page = false);
|
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;
|
BPositionIO * fSeekable;
|
||||||
off_t fPostSniffPosition;
|
off_t fPostSniffPosition;
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#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<ogg_packet> * 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<ogg_packet> * 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<ogg_packet> * 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<ogg_packet> * 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user