Moved some functionality from avi_reader into OpenDMLParser.
Added basic seeking support. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21401 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -51,21 +51,18 @@ struct avi_cookie
|
||||
char * buffer;
|
||||
unsigned buffer_size;
|
||||
|
||||
int64 frame_count;
|
||||
bigtime_t duration;
|
||||
bool is_audio;
|
||||
bool is_video;
|
||||
|
||||
media_format format;
|
||||
|
||||
bool audio;
|
||||
|
||||
// audio only:
|
||||
int64 byte_pos;
|
||||
uint32 bytes_per_sec_rate;
|
||||
uint32 bytes_per_sec_scale;
|
||||
|
||||
// video only:
|
||||
uint32 frame_pos;
|
||||
bigtime_t duration;
|
||||
int64 frame_count;
|
||||
int64 frame_pos;
|
||||
uint32 frames_per_sec_rate;
|
||||
uint32 frames_per_sec_scale;
|
||||
|
||||
// video only:
|
||||
uint32 line_count;
|
||||
};
|
||||
|
||||
@@ -111,7 +108,7 @@ aviReader::Sniff(int32 *streamCount)
|
||||
TRACE("aviReader::Sniff: this stream seems to be supported\n");
|
||||
|
||||
fFile = new OpenDMLFile(pos_io_source);
|
||||
if (B_OK != fFile->Init()) {
|
||||
if (fFile->Init() < B_OK) {
|
||||
ERROR("aviReader::Sniff: can't setup OpenDMLFile\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
@@ -144,6 +141,8 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
cookie->stream = streamNumber;
|
||||
cookie->buffer = 0;
|
||||
cookie->buffer_size = 0;
|
||||
cookie->is_audio = false;
|
||||
cookie->is_video = false;
|
||||
|
||||
BMediaFormats formats;
|
||||
media_format *format = &cookie->format;
|
||||
@@ -167,54 +166,21 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (audio_format->format_tag == 0x0001) {// PCM
|
||||
cookie->frame_count = stream_header->length / ((stream_header->sample_size + 7) / 8);
|
||||
TRACE("audio frame_count %Ld (is PCM)\n", cookie->frame_count);
|
||||
} else if (stream_header->rate) { // not PCM
|
||||
cookie->frame_count = (stream_header->length * (int64)audio_format->frames_per_sec) / stream_header->rate;
|
||||
TRACE("audio frame_count %Ld (using rate)\n", cookie->frame_count);
|
||||
} else { // not PCM
|
||||
cookie->frame_count = (stream_header->length * (int64)audio_format->frames_per_sec) / (stream_header->sample_size * audio_format->avg_bytes_per_sec);
|
||||
TRACE("audio frame_count %Ld (using fallback)\n", cookie->frame_count);
|
||||
}
|
||||
cookie->is_audio = true;
|
||||
cookie->duration = fFile->StreamInfo(streamNumber)->duration;
|
||||
cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count;
|
||||
cookie->frame_pos = 0;
|
||||
cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate;
|
||||
cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale;
|
||||
|
||||
if (stream_header->rate && stream_header->scale) {
|
||||
cookie->duration = (1000000LL * (int64)stream_header->length * (int64)stream_header->scale) / stream_header->rate;
|
||||
TRACE("audio duration %.6f (%Ld) (using scale & rate)\n", cookie->duration / 1E6, cookie->duration);
|
||||
} else if (stream_header->rate) {
|
||||
cookie->duration = (1000000LL * (int64)stream_header->length) / stream_header->rate;
|
||||
TRACE("audio duration %.6f (%Ld) (using rate)\n", cookie->duration / 1E6, cookie->duration);
|
||||
} else {
|
||||
cookie->duration = fFile->Duration();
|
||||
TRACE("audio duration %.6f (%Ld) (using fallback)\n", cookie->duration / 1E6, cookie->duration);
|
||||
}
|
||||
|
||||
cookie->audio = true;
|
||||
cookie->byte_pos = 0;
|
||||
|
||||
if (stream_header->scale && stream_header->rate && stream_header->sample_size) {
|
||||
cookie->bytes_per_sec_rate = stream_header->rate * stream_header->sample_size;
|
||||
cookie->bytes_per_sec_scale = stream_header->scale;
|
||||
TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
|
||||
} else if (audio_format->avg_bytes_per_sec) {
|
||||
cookie->bytes_per_sec_rate = audio_format->avg_bytes_per_sec;
|
||||
cookie->bytes_per_sec_scale = 1;
|
||||
TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using avg_bytes_per_sec)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
|
||||
} else if (stream_header->rate) {
|
||||
cookie->bytes_per_sec_rate = stream_header->rate;
|
||||
cookie->bytes_per_sec_scale = 1;
|
||||
TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
|
||||
} else {
|
||||
cookie->bytes_per_sec_rate = 128000;
|
||||
cookie->bytes_per_sec_scale = 8;
|
||||
TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using fallback)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
|
||||
}
|
||||
TRACE("audio frame_count %Ld\n", cookie->frame_count);
|
||||
TRACE("audio duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration);
|
||||
|
||||
if (audio_format->format_tag == 0x0001) {
|
||||
// a raw PCM format
|
||||
description.family = B_BEOS_FORMAT_FAMILY;
|
||||
description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
|
||||
if (B_OK != formats.GetFormatFor(description, format))
|
||||
if (formats.GetFormatFor(description, format) < B_OK)
|
||||
format->type = B_MEDIA_RAW_AUDIO;
|
||||
format->u.raw_audio.frame_rate = audio_format->frames_per_sec;
|
||||
format->u.raw_audio.channel_count = audio_format->channels;
|
||||
@@ -237,14 +203,14 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
// some encoded format
|
||||
description.family = B_WAV_FORMAT_FAMILY;
|
||||
description.u.wav.codec = audio_format->format_tag;
|
||||
if (B_OK != formats.GetFormatFor(description, format))
|
||||
if (formats.GetFormatFor(description, format) < B_OK)
|
||||
format->type = B_MEDIA_ENCODED_AUDIO;
|
||||
format->u.encoded_audio.bit_rate = 8 * audio_format->avg_bytes_per_sec;
|
||||
TRACE("bit_rate %.3f\n", format->u.encoded_audio.bit_rate);
|
||||
format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec;
|
||||
format->u.encoded_audio.output.channel_count = audio_format->channels;
|
||||
}
|
||||
// this doesn't seem to work (it's not even a fourcc)
|
||||
// TODO: this doesn't seem to work (it's not even a fourcc)
|
||||
format->user_data_type = B_CODEC_TYPE_INFO;
|
||||
*(uint32 *)format->user_data = audio_format->format_tag; format->user_data[4] = 0;
|
||||
|
||||
@@ -270,27 +236,14 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
cookie->audio = false;
|
||||
cookie->is_video = true;
|
||||
cookie->duration = fFile->StreamInfo(streamNumber)->duration;
|
||||
cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count;
|
||||
cookie->frame_pos = 0;
|
||||
cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate;
|
||||
cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale;
|
||||
cookie->line_count = fFile->AviMainHeader()->height;
|
||||
|
||||
if (stream_header->scale && stream_header->rate) {
|
||||
cookie->frames_per_sec_rate = stream_header->rate;
|
||||
cookie->frames_per_sec_scale = stream_header->scale;
|
||||
TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using both)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
|
||||
} else if (fFile->AviMainHeader()->micro_sec_per_frame) {
|
||||
cookie->frames_per_sec_rate = 1000000;
|
||||
cookie->frames_per_sec_scale = fFile->AviMainHeader()->micro_sec_per_frame;
|
||||
TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using micro_sec_per_frame)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
|
||||
} else {
|
||||
cookie->frames_per_sec_rate = 25;
|
||||
cookie->frames_per_sec_scale = 1;
|
||||
TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using fallback)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
|
||||
}
|
||||
|
||||
cookie->frame_count = stream_header->length;
|
||||
cookie->duration = (cookie->frame_count * (int64)cookie->frames_per_sec_scale * 1000000LL) / cookie->frames_per_sec_rate;
|
||||
|
||||
TRACE("video frame_count %Ld\n", cookie->frame_count);
|
||||
TRACE("video duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration);
|
||||
|
||||
@@ -299,13 +252,13 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
description.u.avi.codec = video_format->compression;
|
||||
else
|
||||
description.u.avi.codec = stream_header->fourcc_handler;
|
||||
if (B_OK != formats.GetFormatFor(description, format))
|
||||
if (formats.GetFormatFor(description, format) < B_OK)
|
||||
format->type = B_MEDIA_ENCODED_VIDEO;
|
||||
|
||||
format->user_data_type = B_CODEC_TYPE_INFO;
|
||||
*(uint32 *)format->user_data = description.u.avi.codec; format->user_data[4] = 0;
|
||||
format->u.encoded_video.max_bit_rate = 8 * fFile->AviMainHeader()->max_bytes_per_sec;
|
||||
format->u.encoded_video.avg_bit_rate = format->u.encoded_video.max_bit_rate / 2; // XXX fix this
|
||||
format->u.encoded_video.avg_bit_rate = (format->u.encoded_video.max_bit_rate * 3 / 4); // XXX fix this
|
||||
format->u.encoded_video.output.field_rate = cookie->frames_per_sec_rate / (float)cookie->frames_per_sec_scale;
|
||||
format->u.encoded_video.output.interlace = 1; // 1: progressive
|
||||
format->u.encoded_video.output.first_active = 0;
|
||||
@@ -360,18 +313,25 @@ aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
|
||||
|
||||
|
||||
status_t
|
||||
aviReader::Seek(void *cookie,
|
||||
uint32 seekTo,
|
||||
aviReader::Seek(void *_cookie, uint32 seekTo,
|
||||
int64 *frame, bigtime_t *time)
|
||||
{
|
||||
TRACE("aviReader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
|
||||
avi_cookie *cookie = (avi_cookie *)_cookie;
|
||||
|
||||
TRACE("aviReader::Seek: stream %d, seekTo%s%s%s%s, time %Ld, frame %Ld\n",
|
||||
cookie->stream,
|
||||
(seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
|
||||
(seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
|
||||
(seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
|
||||
(seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
|
||||
*time, *frame);
|
||||
|
||||
return B_ERROR;
|
||||
status_t rv = fFile->Seek(cookie->stream, seekTo, frame, time);
|
||||
if (rv == B_OK) {
|
||||
cookie->frame_pos = *frame;
|
||||
TRACE("aviReader::Seek: stream %d, success, setting frame_pos to %lld\n", cookie->stream, cookie->frame_pos);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -396,24 +356,26 @@ aviReader::GetNextChunk(void *_cookie,
|
||||
cookie->buffer_size = (size + 15) & ~15;
|
||||
cookie->buffer = new char [cookie->buffer_size];
|
||||
}
|
||||
|
||||
mediaHeader->start_time = (cookie->frame_pos * 1000000 * cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
|
||||
|
||||
if (cookie->audio) {
|
||||
mediaHeader->start_time = (cookie->byte_pos * 1000000LL * (int64)cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
|
||||
if (cookie->is_audio) {
|
||||
mediaHeader->type = B_MEDIA_ENCODED_AUDIO;
|
||||
mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
|
||||
|
||||
cookie->byte_pos += size;
|
||||
} else {
|
||||
mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
|
||||
cookie->frame_pos += size;
|
||||
} else if (cookie->is_video) {
|
||||
mediaHeader->type = B_MEDIA_ENCODED_VIDEO;
|
||||
mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
|
||||
mediaHeader->u.encoded_video.first_active_line = 0;
|
||||
mediaHeader->u.encoded_video.line_count = cookie->line_count;
|
||||
|
||||
mediaHeader->u.encoded_video.line_count = cookie->line_count;
|
||||
cookie->frame_pos += 1;
|
||||
} else {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
|
||||
TRACE("stream %d (%s): start_time %.6f, pos %.3f %%\n",
|
||||
cookie->stream, cookie->is_audio ? "A" : cookie->is_video ? "V" : "?",
|
||||
mediaHeader->start_time / 1000000.0, cookie->frame_pos * 100.0 / cookie->frame_count);
|
||||
|
||||
*chunkBuffer = cookie->buffer;
|
||||
*chunkSize = size;
|
||||
|
||||
@@ -96,18 +96,10 @@ OpenDMLFile::Init()
|
||||
{
|
||||
fParser = new OpenDMLParser(fSource);
|
||||
|
||||
if (fParser->Parse() < B_OK) {
|
||||
ERROR("OpenDMLFile::SetTo: warning, file parsing failed\n");
|
||||
}
|
||||
|
||||
if (!fParser->AviMainHeader()) {
|
||||
ERROR("OpenDMLFile::SetTo: avi main header not found\n");
|
||||
if (fParser->Init() < B_OK) {
|
||||
ERROR("OpenDMLFile::SetTo: parser init failed\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (fParser->StreamCount() == 0) {
|
||||
ERROR("OpenDMLFile::SetTo: no streams found\n");
|
||||
}
|
||||
|
||||
if (fParser->StreamInfo(0)->odml_index_size != 0) {
|
||||
fIndex = new OpenDMLIndex(fSource, fParser);
|
||||
@@ -391,18 +383,26 @@ OpenDMLFile::AviGetNextChunkInfo(int stream_index, int64 *start, uint32 *size, b
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
status_t
|
||||
OpenDMLFile::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe)
|
||||
{
|
||||
return fIndex->GetNextChunkInfo(stream_index, start, size, keyframe);
|
||||
}
|
||||
|
||||
status_t
|
||||
OpenDMLFile::Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time)
|
||||
{
|
||||
return fIndex->Seek(stream_index, seekTo, frame, time);
|
||||
}
|
||||
|
||||
int
|
||||
OpenDMLFile::StreamCount()
|
||||
{
|
||||
return fParser->StreamCount();
|
||||
}
|
||||
|
||||
/*
|
||||
bigtime_t
|
||||
OpenDMLFile::Duration()
|
||||
{
|
||||
@@ -422,7 +422,8 @@ OpenDMLFile::FrameCount()
|
||||
return fParser->AviMainHeader()->total_frames;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
bool
|
||||
OpenDMLFile::IsVideo(int stream_index)
|
||||
{
|
||||
@@ -466,3 +467,9 @@ OpenDMLFile::StreamFormat(int stream_index)
|
||||
return (fParser->StreamInfo(stream_index)->stream_header_valid) ?
|
||||
&fParser->StreamInfo(stream_index)->stream_header : 0;
|
||||
}
|
||||
|
||||
const stream_info *
|
||||
OpenDMLFile::StreamInfo(int index)
|
||||
{
|
||||
return fParser->StreamInfo(index);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,14 @@ public:
|
||||
status_t Init();
|
||||
|
||||
int StreamCount();
|
||||
|
||||
const stream_info * StreamInfo(int index);
|
||||
|
||||
/*
|
||||
bigtime_t Duration();
|
||||
uint32 FrameCount();
|
||||
|
||||
*/
|
||||
|
||||
bool IsVideo(int stream_index);
|
||||
bool IsAudio(int stream_index);
|
||||
|
||||
@@ -54,6 +58,7 @@ public:
|
||||
const avi_stream_header * StreamFormat(int stream_index);
|
||||
|
||||
status_t GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe);
|
||||
status_t Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time);
|
||||
|
||||
BPositionIO *Source() { return fSource; }
|
||||
|
||||
|
||||
@@ -41,12 +41,6 @@
|
||||
#define DO_SWAP_INT16(x) x = B_SWAP_INT16(x)
|
||||
#endif
|
||||
|
||||
struct movie_chunk
|
||||
{
|
||||
movie_chunk * next;
|
||||
int64 start;
|
||||
uint32 size;
|
||||
};
|
||||
|
||||
OpenDMLParser::OpenDMLParser(BPositionIO *source)
|
||||
: fSource(source),
|
||||
@@ -128,6 +122,10 @@ OpenDMLParser::CreateNewStreamInfo()
|
||||
info->video_format_valid = false;
|
||||
info->odml_index_start = 0;
|
||||
info->odml_index_size = 0;
|
||||
info->duration = 0;
|
||||
info->frame_count = 0;
|
||||
info->frames_per_sec_rate = 1;
|
||||
info->frames_per_sec_scale = 1;
|
||||
|
||||
// append the new stream_info to the fStreams list and point fCurrentStream to it
|
||||
if (fStreams) {
|
||||
@@ -142,18 +140,128 @@ OpenDMLParser::CreateNewStreamInfo()
|
||||
}
|
||||
|
||||
status_t
|
||||
OpenDMLParser::Parse()
|
||||
OpenDMLParser::Init()
|
||||
{
|
||||
TRACE("OpenDMLParser::Parse\n");
|
||||
TRACE("OpenDMLParser::Init\n");
|
||||
|
||||
if (!fSource) {
|
||||
ERROR("OpenDMLParser::Parse: no source\n");
|
||||
ERROR("OpenDMLParser::Init: no source\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
if (fSize < 32) {
|
||||
ERROR("OpenDMLParser::Parse: file too small\n");
|
||||
ERROR("OpenDMLParser::Init: file too small\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (Parse() < B_OK) {
|
||||
ERROR("OpenDMLParser::Init: warning: file parsing failed\n");
|
||||
}
|
||||
|
||||
if (AviMainHeader() == NULL) {
|
||||
ERROR("OpenDMLParser::Init: avi main header not found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (StreamCount() == 0) {
|
||||
ERROR("OpenDMLParser::Init: no streams found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
uint32 frame_count = OdmlExtendedHeader() ? OdmlExtendedHeader()->total_frames : AviMainHeader()->total_frames;
|
||||
bigtime_t duration = frame_count * AviMainHeader()->micro_sec_per_frame;
|
||||
printf("AVI Header frame count %lu, duration %.6f\n", frame_count, duration / 1E6);
|
||||
|
||||
for (int i = 0; i < fStreamCount; i++) {
|
||||
SetupStreamLength(const_cast<stream_info *>(StreamInfo(i)));
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::SetupStreamLength(stream_info *stream)
|
||||
{
|
||||
if (stream->is_audio)
|
||||
SetupAudioStreamLength(stream);
|
||||
if (stream->is_video)
|
||||
SetupVideoStreamLength(stream);
|
||||
}
|
||||
|
||||
// F:\avi-info\Information on AVI file.htm
|
||||
|
||||
void
|
||||
OpenDMLParser::SetupAudioStreamLength(stream_info *stream)
|
||||
{
|
||||
stream->frame_count = stream->stream_header.length;
|
||||
|
||||
if (stream->audio_format->format_tag == 0x0001
|
||||
&& stream->stream_header.sample_size != 0
|
||||
&& stream->stream_header.sample_size != 1) { // PCM
|
||||
stream->frame_count /= (stream->stream_header.sample_size + 7) / 8;
|
||||
printf("audio: messing up PCM frame_count?\n");
|
||||
}
|
||||
|
||||
if (stream->stream_header.rate && stream->stream_header.scale) {
|
||||
stream->frames_per_sec_rate = stream->stream_header.rate;
|
||||
stream->frames_per_sec_scale = stream->stream_header.scale;
|
||||
stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate;
|
||||
printf("audio: using rate+scale\n");
|
||||
} else if (stream->audio_format->avg_bytes_per_sec) {
|
||||
stream->frames_per_sec_rate = stream->audio_format->avg_bytes_per_sec;
|
||||
stream->frames_per_sec_scale = 1;
|
||||
stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate;
|
||||
printf("audio: using avg_bytes_per_sec\n");
|
||||
} else if (AviMainHeader()->micro_sec_per_frame) {
|
||||
uint32 video_frame_count = OdmlExtendedHeader() ? OdmlExtendedHeader()->total_frames : AviMainHeader()->total_frames;
|
||||
stream->duration = video_frame_count * AviMainHeader()->micro_sec_per_frame;
|
||||
stream->frames_per_sec_rate = (stream->frame_count * 1000 * 1000000) / stream->duration;
|
||||
stream->frames_per_sec_scale = 1000;
|
||||
printf("audio: using micro_sec_per_frame\n");
|
||||
} else {
|
||||
printf("audio: no idea what to do\n");
|
||||
}
|
||||
|
||||
if (stream->audio_format->avg_bytes_per_sec) {
|
||||
int64 expected_frame_count = (stream->duration * stream->audio_format->avg_bytes_per_sec) / 1000000;
|
||||
printf("audio: expected frame_count %lld, calculated stream frame_count %lld\n", expected_frame_count, stream->frame_count);
|
||||
if (expected_frame_count * 9 > stream->frame_count * 10) {
|
||||
printf("audio: something is wrong, ignoring calculated stream frame_count, rate and scale\n");
|
||||
stream->frame_count = expected_frame_count;
|
||||
stream->frames_per_sec_rate = stream->audio_format->avg_bytes_per_sec;
|
||||
stream->frames_per_sec_scale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("audio: frame_count %lld, duration %.6f, fps %.3f\n",
|
||||
stream->frame_count, stream->duration / 1E6, stream->frames_per_sec_rate / (double)stream->frames_per_sec_scale);
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::SetupVideoStreamLength(stream_info *stream)
|
||||
{
|
||||
stream->frame_count = stream->stream_header.length;
|
||||
if (stream->stream_header.rate && stream->stream_header.scale) {
|
||||
stream->frames_per_sec_rate = stream->stream_header.rate;
|
||||
stream->frames_per_sec_scale = stream->stream_header.scale;
|
||||
printf("video: using rate+scale\n");
|
||||
} else if (AviMainHeader()->micro_sec_per_frame) {
|
||||
stream->frames_per_sec_rate = 1000000;
|
||||
stream->frames_per_sec_scale = AviMainHeader()->micro_sec_per_frame;
|
||||
printf("video: using micro_sec_per_frame\n");
|
||||
} else {
|
||||
stream->frames_per_sec_rate = 25;
|
||||
stream->frames_per_sec_scale = 1;
|
||||
printf("video: using fallback\n");
|
||||
}
|
||||
stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate;
|
||||
|
||||
printf("video: frame_count %lld, duration %.6f, fps %.3f\n",
|
||||
stream->frame_count, stream->duration / 1E6, stream->frames_per_sec_rate / (double)stream->frames_per_sec_scale);
|
||||
}
|
||||
|
||||
status_t
|
||||
OpenDMLParser::Parse()
|
||||
{
|
||||
TRACE("OpenDMLParser::Parse\n");
|
||||
|
||||
uint64 pos = 0;
|
||||
int riff_chunk_number = 0;
|
||||
@@ -460,11 +568,6 @@ OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size)
|
||||
DO_SWAP_INT16(fCurrentStream->stream_header.rect_bottom);
|
||||
#endif
|
||||
|
||||
if (fCurrentStream->stream_header.scale == 0) {
|
||||
printf("OpenDMLParser::ParseChunk_strh: scale is 0\n");
|
||||
fCurrentStream->stream_header.scale = 1;
|
||||
}
|
||||
|
||||
fCurrentStream->stream_header_valid = true;
|
||||
fCurrentStream->is_audio = fCurrentStream->stream_header.fourcc_type == FOURCC('a','u','d','s');
|
||||
fCurrentStream->is_video = fCurrentStream->stream_header.fourcc_type == FOURCC('v','i','d','s');
|
||||
@@ -478,7 +581,6 @@ OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size)
|
||||
TRACE("initial_frames = %lu\n", fCurrentStream->stream_header.initial_frames);
|
||||
TRACE("scale = %lu\n", fCurrentStream->stream_header.scale);
|
||||
TRACE("rate = %lu\n", fCurrentStream->stream_header.rate);
|
||||
TRACE("frames/sec = %.3f\n", fCurrentStream->stream_header.rate / (float)fCurrentStream->stream_header.scale);
|
||||
TRACE("start = %lu\n", fCurrentStream->stream_header.start);
|
||||
TRACE("length = %lu\n", fCurrentStream->stream_header.length);
|
||||
TRACE("suggested_buffer_size = %lu\n", fCurrentStream->stream_header.suggested_buffer_size);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
struct stream_info
|
||||
{
|
||||
stream_info * next;
|
||||
stream_info * next; // TODO: replace with a vector<>
|
||||
bool is_audio;
|
||||
bool is_video;
|
||||
bool stream_header_valid;
|
||||
@@ -41,6 +41,11 @@ struct stream_info
|
||||
bitmap_info_header video_format;
|
||||
int64 odml_index_start;
|
||||
uint32 odml_index_size;
|
||||
|
||||
bigtime_t duration;
|
||||
int64 frame_count;
|
||||
uint32 frames_per_sec_rate;
|
||||
uint32 frames_per_sec_scale;
|
||||
};
|
||||
|
||||
class OpenDMLParser
|
||||
@@ -49,7 +54,7 @@ public:
|
||||
OpenDMLParser(BPositionIO *source);
|
||||
~OpenDMLParser();
|
||||
|
||||
status_t Parse();
|
||||
status_t Init();
|
||||
|
||||
int StreamCount();
|
||||
|
||||
@@ -64,6 +69,7 @@ public:
|
||||
const odml_extended_header * OdmlExtendedHeader();
|
||||
|
||||
private:
|
||||
status_t Parse();
|
||||
status_t ParseChunk_AVI(int number, uint64 start, uint32 size);
|
||||
status_t ParseChunk_LIST(uint64 start, uint32 size);
|
||||
status_t ParseChunk_idx1(uint64 start, uint32 size);
|
||||
@@ -78,8 +84,12 @@ private:
|
||||
status_t ParseList_INFO(uint64 start, uint32 size);
|
||||
status_t ParseList_strl(uint64 start, uint32 size);
|
||||
|
||||
private:
|
||||
void CreateNewStreamInfo();
|
||||
void SetupStreamLength(stream_info *stream);
|
||||
void SetupAudioStreamLength(stream_info *stream);
|
||||
void SetupVideoStreamLength(stream_info *stream);
|
||||
|
||||
private:
|
||||
|
||||
BPositionIO * fSource;
|
||||
int64 fSize;
|
||||
|
||||
@@ -33,12 +33,6 @@
|
||||
#include <StopWatch.h>
|
||||
|
||||
|
||||
struct StandardIndex::stream_data
|
||||
{
|
||||
uint32 chunk_id;
|
||||
uint32 pos;
|
||||
};
|
||||
|
||||
StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser)
|
||||
: Index(source, parser)
|
||||
, fIndex(NULL)
|
||||
@@ -53,6 +47,10 @@ StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser)
|
||||
StandardIndex::~StandardIndex()
|
||||
{
|
||||
delete [] fIndex;
|
||||
if (fStreamData) {
|
||||
for (int i = 0; i < fStreamCount; i++)
|
||||
delete [] fStreamData[i].seek_hints;
|
||||
}
|
||||
delete [] fStreamData;
|
||||
}
|
||||
|
||||
@@ -63,6 +61,11 @@ StandardIndex::Init()
|
||||
uint32 indexBytes = fParser->StandardIndexSize();
|
||||
fIndexSize = indexBytes / sizeof(avi_standard_index_entry);
|
||||
indexBytes = fIndexSize * sizeof(avi_standard_index_entry);
|
||||
uint32 seekHintsStride = 1800 * fStreamCount;
|
||||
uint32 seekHintsMax = fIndexSize / seekHintsStride;
|
||||
|
||||
printf("StandardIndex::Init: seekHintsStride %lu\n", seekHintsStride);
|
||||
printf("StandardIndex::Init: seekHintsMax %lu\n", seekHintsMax);
|
||||
|
||||
{ BStopWatch w("StandardIndex::Init: malloc");
|
||||
|
||||
@@ -92,13 +95,61 @@ StandardIndex::Init()
|
||||
}
|
||||
}
|
||||
|
||||
//DumpIndex();
|
||||
|
||||
fStreamData = new stream_data[fStreamCount];
|
||||
for (int i = 0; i < fStreamCount; i++) {
|
||||
fStreamData[i].chunk_id = i / 10 + '0' + (i % 10 + '0') * 256;
|
||||
fStreamData[i].pos = 0;
|
||||
fStreamData[i].chunk_count = 0;
|
||||
fStreamData[i].keyframe_count = 0;
|
||||
fStreamData[i].stream_pos = 0;
|
||||
fStreamData[i].stream_size = 0;
|
||||
fStreamData[i].seek_hints = new seek_hint[seekHintsMax];
|
||||
fStreamData[i].seek_hints_count = 0;
|
||||
fStreamData[i].seek_hints_next = seekHintsStride;
|
||||
}
|
||||
|
||||
{ BStopWatch w("StandardIndex::Init: scan index");
|
||||
|
||||
//DumpIndex();
|
||||
for (int stream = 0; stream < fStreamCount; stream++) {
|
||||
uint32 chunk_id = fStreamData[stream].chunk_id;
|
||||
uint64 stream_size = 0;
|
||||
uint32 chunk_count = 0;
|
||||
uint32 keyframe_count = 0;
|
||||
int seek_hints_next = seekHintsStride;
|
||||
int seek_hints_count = 0;
|
||||
for (int i = 0; i < fIndexSize; i++) {
|
||||
if ((fIndex[i].chunk_id & 0xffff) == chunk_id) {
|
||||
stream_size += fIndex[i].chunk_length;
|
||||
chunk_count++;
|
||||
keyframe_count += (fIndex[i].flags >> AVIIF_KEYFRAME_SHIFT) & 1;
|
||||
|
||||
if (i >= seek_hints_next) {
|
||||
seek_hints_next = i + seekHintsStride;
|
||||
seek_hint *hint = &fStreamData[stream].seek_hints[seek_hints_count++];
|
||||
hint->stream_pos = fIndex[i].chunk_offset;
|
||||
hint->index_pos = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
fStreamData[stream].stream_size = stream_size;
|
||||
fStreamData[stream].chunk_count = chunk_count;
|
||||
fStreamData[stream].keyframe_count = keyframe_count;
|
||||
fStreamData[stream].seek_hints_count = seek_hints_count;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fStreamCount; i++) {
|
||||
printf("stream %d, stream_size %llu\n", i, fStreamData[i].stream_size);
|
||||
printf("stream %d, chunk_count %lu\n", i, fStreamData[i].chunk_count);
|
||||
printf("stream %d, keyframe_count %lu\n", i, fStreamData[i].keyframe_count);
|
||||
printf("stream %d, seek_hints_count %lu\n", i, fStreamData[i].seek_hints_count);
|
||||
for (int j = 0; j < fStreamData[i].seek_hints_count; j++) {
|
||||
printf(" seek_hint %3d, index_pos %6lu, stream_pos %lld\n", j,
|
||||
fStreamData[i].seek_hints[j].index_pos,
|
||||
fStreamData[i].seek_hints[j].stream_pos);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@@ -132,15 +183,15 @@ status_t
|
||||
StandardIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe)
|
||||
{
|
||||
stream_data *data = &fStreamData[stream_index];
|
||||
while (data->pos < fIndexSize) {
|
||||
if ((fIndex[data->pos].chunk_id & 0xffff) == data->chunk_id) {
|
||||
*keyframe = fIndex[data->pos].flags & AVIIF_KEYFRAME;
|
||||
*start = fDataOffset + fIndex[data->pos].chunk_offset + 8; // skip 8 bytes (chunk id + chunk size)
|
||||
*size = fIndex[data->pos].chunk_length;
|
||||
data->pos++;
|
||||
while (data->stream_pos < fIndexSize) {
|
||||
if ((fIndex[data->stream_pos].chunk_id & 0xffff) == data->chunk_id) {
|
||||
*keyframe = fIndex[data->stream_pos].flags & AVIIF_KEYFRAME;
|
||||
*start = fDataOffset + fIndex[data->stream_pos].chunk_offset + 8; // skip 8 bytes (chunk id + chunk size)
|
||||
*size = fIndex[data->stream_pos].chunk_length;
|
||||
data->stream_pos++;
|
||||
return B_OK;
|
||||
}
|
||||
data->pos++;
|
||||
data->stream_pos++;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
@@ -150,13 +201,59 @@ StandardIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bo
|
||||
status_t
|
||||
StandardIndex::Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time)
|
||||
{
|
||||
/*
|
||||
printf("StandardIndex::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
|
||||
printf("StandardIndex::Seek: stream %d, seekTo%s%s%s%s, time %Ld, frame %Ld\n",
|
||||
stream_index,
|
||||
(seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
|
||||
(seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
|
||||
(seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
|
||||
(seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "");
|
||||
*/
|
||||
(seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
|
||||
*time, *frame);
|
||||
|
||||
const stream_info *stream = fParser->StreamInfo(stream_index);
|
||||
stream_data *data = &fStreamData[stream_index];
|
||||
|
||||
int64 frame_pos;
|
||||
if (seekTo & B_MEDIA_SEEK_TO_FRAME)
|
||||
frame_pos = *frame;
|
||||
else if (seekTo & B_MEDIA_SEEK_TO_TIME)
|
||||
frame_pos = (*time * stream->frames_per_sec_rate) / (1000000 * stream->frames_per_sec_scale);
|
||||
else
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (stream->is_audio) {
|
||||
int64 bytes = 0;
|
||||
for (int i = 0; i < fIndexSize; i++) {
|
||||
if ((fIndex[i].chunk_id & 0xffff) == data->chunk_id) {
|
||||
int64 bytesNext = bytes + fIndex[i].chunk_length;
|
||||
if (bytes <= frame_pos && bytesNext > frame_pos) {
|
||||
data->stream_pos = i;
|
||||
goto done;
|
||||
}
|
||||
bytes = bytesNext;
|
||||
}
|
||||
}
|
||||
} else if (stream->is_video) {
|
||||
int pos = 0;
|
||||
for (int i = 0; i < fIndexSize; i++) {
|
||||
if ((fIndex[i].chunk_id & 0xffff) == data->chunk_id) {
|
||||
if (pos == frame_pos) {
|
||||
data->stream_pos = i;
|
||||
goto done;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
printf("seek failed, position not found\n");
|
||||
return B_ERROR;
|
||||
|
||||
done:
|
||||
printf("seek done: index: pos %d, size %d\n", data->stream_pos, fIndexSize);
|
||||
*frame = frame_pos;
|
||||
*time = (frame_pos * 1000000 * stream->frames_per_sec_scale) / stream->frames_per_sec_rate;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,24 @@ private:
|
||||
void DumpIndex();
|
||||
|
||||
private:
|
||||
struct stream_data;
|
||||
struct seek_hint
|
||||
{
|
||||
uint64 stream_pos;
|
||||
uint32 index_pos;
|
||||
};
|
||||
|
||||
struct stream_data
|
||||
{
|
||||
uint32 chunk_id;
|
||||
uint32 chunk_count;
|
||||
uint32 keyframe_count;
|
||||
uint32 stream_pos;
|
||||
uint64 stream_size;
|
||||
seek_hint * seek_hints;
|
||||
int seek_hints_count;
|
||||
uint32 seek_hints_next;
|
||||
};
|
||||
|
||||
avi_standard_index_entry *fIndex;
|
||||
uint32 fIndexSize;
|
||||
stream_data * fStreamData;
|
||||
|
||||
@@ -57,6 +57,7 @@ struct avi_standard_index_entry
|
||||
} _PACKED;
|
||||
#define AVIIF_LIST 0x00000001
|
||||
#define AVIIF_KEYFRAME 0x00000010
|
||||
#define AVIIF_KEYFRAME_SHIFT 4
|
||||
#define AVIIF_FIRSTPART 0x00000020
|
||||
#define AVIIF_LASTPART 0x00000040
|
||||
#define AVIIF_MIDPART (AVIIF_LASTPART | AVIFF_FIRSTPART)
|
||||
|
||||
Reference in New Issue
Block a user