initial support for reading OpenDML AVI files
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6256 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -4,6 +4,12 @@ UsePrivateHeaders media ;
|
||||
|
||||
Addon avi_reader : media plugins :
|
||||
avi_reader.cpp
|
||||
:
|
||||
false
|
||||
:
|
||||
libopendml.a
|
||||
;
|
||||
|
||||
LinkSharedOSLibs avi_reader : be libmedia.so ;
|
||||
|
||||
SubInclude OBOS_TOP src add-ons media plugins avi_reader libOpenDML ;
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <DataIO.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <MediaFormats.h>
|
||||
#include "RawFormats.h"
|
||||
#include "avi_reader.h"
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...)
|
||||
#endif
|
||||
|
||||
// http://www.microsoft.com/Developer/PRODINFO/directx/dxm/help/ds/FiltDev/DV_Data_AVI_File_Format.htm
|
||||
|
||||
struct avi_cookie
|
||||
{
|
||||
int stream;
|
||||
char * buffer;
|
||||
int buffer_size;
|
||||
|
||||
bool audio;
|
||||
|
||||
int64 byte_pos;
|
||||
uint32 bytes_per_sec_rate;
|
||||
uint32 bytes_per_sec_scale;
|
||||
|
||||
uint32 frame_pos;
|
||||
};
|
||||
|
||||
|
||||
aviReader::aviReader()
|
||||
: fFile(0)
|
||||
{
|
||||
TRACE("aviReader::aviReader\n");
|
||||
}
|
||||
|
||||
|
||||
aviReader::~aviReader()
|
||||
{
|
||||
delete fFile;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
aviReader::Copyright()
|
||||
{
|
||||
return "AVI & OpenDML reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
aviReader::Sniff(int32 *streamCount)
|
||||
{
|
||||
TRACE("aviReader::Sniff\n");
|
||||
|
||||
BPositionIO *pos_io_source;
|
||||
|
||||
pos_io_source = dynamic_cast<BPositionIO *>(Reader::Source());
|
||||
if (!pos_io_source) {
|
||||
TRACE("aviReader::Sniff: not a BPositionIO\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (!OpenDMLFile::IsSupported(pos_io_source)) {
|
||||
TRACE("aviReader::Sniff: unsupported file type\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
TRACE("aviReader::Sniff: this stream seems to be supported\n");
|
||||
|
||||
fFile = new OpenDMLFile();
|
||||
if (B_OK != fFile->SetTo(pos_io_source)) {
|
||||
TRACE("aviReader::Sniff: can't setup OpenDMLFile\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
*streamCount = fFile->StreamCount();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void
|
||||
aviReader::GetFileFormatInfo(media_file_format *mff)
|
||||
{
|
||||
mff->capabilities = media_file_format::B_READABLE
|
||||
| media_file_format::B_KNOWS_ENCODED_VIDEO
|
||||
| media_file_format::B_KNOWS_ENCODED_AUDIO
|
||||
| media_file_format::B_IMPERFECTLY_SEEKABLE;
|
||||
mff->family = B_MISC_FORMAT_FAMILY;
|
||||
mff->version = 100;
|
||||
strcpy(mff->mime_type, "audio/x-avi");
|
||||
strcpy(mff->file_extension, "avi");
|
||||
strcpy(mff->short_name, "AVI");
|
||||
strcpy(mff->pretty_name, "Audio/Video Interleaved (AVI) file format");
|
||||
}
|
||||
|
||||
status_t
|
||||
aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
|
||||
{
|
||||
avi_cookie *cookie = new avi_cookie;
|
||||
*_cookie = cookie;
|
||||
|
||||
cookie->stream = streamNumber;
|
||||
cookie->buffer = 0;
|
||||
cookie->buffer_size = 0;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
aviReader::FreeCookie(void *_cookie)
|
||||
{
|
||||
avi_cookie *cookie = (avi_cookie *)_cookie;
|
||||
|
||||
delete [] cookie->buffer;
|
||||
|
||||
delete cookie;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
|
||||
media_format *format, void **infoBuffer, int32 *infoSize)
|
||||
{
|
||||
avi_cookie *cookie = (avi_cookie *)_cookie;
|
||||
|
||||
*duration = fFile->Duration();
|
||||
*infoBuffer = 0;
|
||||
*infoSize = 0;
|
||||
|
||||
BMediaFormats formats;
|
||||
media_format_description description;
|
||||
|
||||
const avi_stream_header *stream_header;
|
||||
stream_header = fFile->StreamFormat(cookie->stream);
|
||||
if (!stream_header) {
|
||||
TRACE("aviReader::GetStreamInfo: stream %d has no header\n", cookie->stream);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (fFile->IsAudio(cookie->stream)) {
|
||||
const wave_format_ex *audio_format = fFile->AudioFormat(cookie->stream);
|
||||
if (!audio_format) {
|
||||
TRACE("aviReader::GetStreamInfo: audio stream %d has no format\n", cookie->stream);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (audio_format->format_tag == 0x0001) // PCM
|
||||
*frameCount = stream_header->length / ((stream_header->sample_size + 7) / 8);
|
||||
else // not PCM
|
||||
*frameCount = (stream_header->length * audio_format->frames_per_sec) / (stream_header->sample_size * audio_format->avg_bytes_per_sec);
|
||||
|
||||
cookie->audio = true;
|
||||
cookie->byte_pos = 0;
|
||||
|
||||
cookie->bytes_per_sec_rate = audio_format->avg_bytes_per_sec;
|
||||
cookie->bytes_per_sec_scale = 1;
|
||||
|
||||
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;
|
||||
formats.GetFormatFor(description, format);
|
||||
format->u.raw_audio.frame_rate = audio_format->frames_per_sec;
|
||||
format->u.raw_audio.channel_count = audio_format->channels;
|
||||
if (audio_format->bits_per_sample <= 8)
|
||||
format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8;
|
||||
else if (audio_format->bits_per_sample <= 16)
|
||||
format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
|
||||
else if (audio_format->bits_per_sample <= 24)
|
||||
format->u.raw_audio.format = B_AUDIO_FORMAT_INT24;
|
||||
else if (audio_format->bits_per_sample <= 32)
|
||||
format->u.raw_audio.format = B_AUDIO_FORMAT_INT32;
|
||||
else {
|
||||
TRACE("WavReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->bits_per_sample);
|
||||
return B_ERROR;
|
||||
}
|
||||
format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE;
|
||||
format->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
|
||||
format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size;
|
||||
} else {
|
||||
// some encoded format
|
||||
description.family = B_WAV_FORMAT_FAMILY;
|
||||
description.u.wav.codec = audio_format->format_tag;
|
||||
formats.GetFormatFor(description, format);
|
||||
format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec;
|
||||
format->u.encoded_audio.output.channel_count = audio_format->channels;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (fFile->IsVideo(cookie->stream)) {
|
||||
const bitmap_info_header *video_format = fFile->VideoFormat(cookie->stream);
|
||||
if (!video_format) {
|
||||
TRACE("aviReader::GetStreamInfo: video stream %d has no format\n", cookie->stream);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
*frameCount = fFile->FrameCount();
|
||||
|
||||
cookie->audio = false;
|
||||
cookie->frame_pos = 0;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
aviReader::Seek(void *cookie,
|
||||
uint32 seekTo,
|
||||
int64 *frame, bigtime_t *time)
|
||||
{
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
aviReader::GetNextChunk(void *_cookie,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
avi_cookie *cookie = (avi_cookie *)_cookie;
|
||||
|
||||
int64 start; uint32 size; bool keyframe;
|
||||
if (!fFile->GetNextChunkInfo(cookie->stream, &start, &size, &keyframe))
|
||||
return B_LAST_BUFFER_ERROR;
|
||||
|
||||
if (cookie->buffer_size < size) {
|
||||
delete [] cookie->buffer;
|
||||
cookie->buffer_size = (size + 15) & ~15;
|
||||
cookie->buffer = new char [cookie->buffer_size];
|
||||
}
|
||||
|
||||
if (cookie->audio) {
|
||||
mediaHeader->start_time = (cookie->byte_pos * 1000000ULL * cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
|
||||
|
||||
cookie->byte_pos += size;
|
||||
} else {
|
||||
cookie->frame_pos += 1;
|
||||
}
|
||||
|
||||
printf("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
|
||||
|
||||
*chunkBuffer = cookie->buffer;
|
||||
*chunkSize = size;
|
||||
return size == fFile->Source()->ReadAt(start, cookie->buffer, size) ? B_OK : B_LAST_BUFFER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Reader *
|
||||
aviReaderPlugin::NewReader()
|
||||
{
|
||||
return new aviReader;
|
||||
}
|
||||
|
||||
|
||||
MediaPlugin *instantiate_plugin()
|
||||
{
|
||||
return new aviReaderPlugin;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "ReaderPlugin.h"
|
||||
#include "libOpenDML/OpenDMLFile.h"
|
||||
|
||||
class aviReader : public Reader
|
||||
{
|
||||
public:
|
||||
aviReader();
|
||||
~aviReader();
|
||||
|
||||
const char *Copyright();
|
||||
|
||||
status_t Sniff(int32 *streamCount);
|
||||
|
||||
void GetFileFormatInfo(media_file_format *mff);
|
||||
|
||||
status_t AllocateCookie(int32 streamNumber, void **cookie);
|
||||
status_t FreeCookie(void *cookie);
|
||||
|
||||
status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
|
||||
media_format *format, void **infoBuffer, int32 *infoSize);
|
||||
|
||||
status_t Seek(void *cookie,
|
||||
uint32 seekTo,
|
||||
int64 *frame, bigtime_t *time);
|
||||
|
||||
status_t GetNextChunk(void *cookie,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
private:
|
||||
OpenDMLFile *fFile;
|
||||
};
|
||||
|
||||
|
||||
class aviReaderPlugin : public ReaderPlugin
|
||||
{
|
||||
public:
|
||||
Reader *NewReader();
|
||||
};
|
||||
|
||||
MediaPlugin *instantiate_plugin();
|
||||
@@ -0,0 +1,6 @@
|
||||
SubDir OBOS_TOP src add-ons media plugins avi_reader libOpenDML ;
|
||||
|
||||
StaticLibrary opendml :
|
||||
OpenDMLFile.cpp
|
||||
OpenDMLParser.cpp
|
||||
;
|
||||
@@ -0,0 +1,313 @@
|
||||
#include <stdio.h>
|
||||
#include "OpenDMLFile.h"
|
||||
|
||||
#define TRACE printf
|
||||
|
||||
#define INDEX_CHUNK_SIZE 32768
|
||||
|
||||
struct OpenDMLFile::stream_data
|
||||
{
|
||||
const stream_info *info;
|
||||
|
||||
uint32 chunk_id;
|
||||
|
||||
char * superindex;
|
||||
int superindex_entry_size;
|
||||
int superindex_entry_count;
|
||||
int superindex_entry_pos;
|
||||
|
||||
// index info (superindex entry)
|
||||
int64 index_entry_start;
|
||||
int64 index_base_offset;
|
||||
int index_entry_size;
|
||||
int index_entry_count;
|
||||
int index_entry_pos;
|
||||
|
||||
// index chunk
|
||||
char * index_chunk;
|
||||
int index_chunk_entry_count;
|
||||
int index_chunk_entry_pos;
|
||||
};
|
||||
|
||||
OpenDMLFile::OpenDMLFile()
|
||||
: fSource(0),
|
||||
fParser(0),
|
||||
fStreamCount(0),
|
||||
fStreamData(0)
|
||||
{
|
||||
}
|
||||
|
||||
OpenDMLFile::~OpenDMLFile()
|
||||
{
|
||||
delete fParser;
|
||||
delete [] fStreamData;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
OpenDMLFile::IsSupported(BPositionIO *source)
|
||||
{
|
||||
uint8 h[12];
|
||||
if (12 != source->ReadAt(0, h, 12))
|
||||
return false;
|
||||
return h[0] == 'R' && h[1] == 'I' && h[2] == 'F' && h[3] == 'F' &&
|
||||
h[8] == 'A' && h[9] == 'V' && h[10] == 'I' && h[11] == ' ';
|
||||
}
|
||||
|
||||
status_t
|
||||
OpenDMLFile::SetTo(BPositionIO *source)
|
||||
{
|
||||
delete fParser;
|
||||
fSource = source;
|
||||
fParser = new OpenDMLParser;
|
||||
fParser->Parse(source);
|
||||
|
||||
if (!fParser->AviMainHeader()) {
|
||||
TRACE("OpenDMLFile::SetTo: avi main header not found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (fParser->StreamCount() != 0 && fParser->StandardIndexSize() == 0) {
|
||||
TRACE("OpenDMLFile::SetTo file has no standard avi index\n");
|
||||
bool found_odml_index = false;
|
||||
for (int i = 0; i < fParser->StreamCount(); i++) {
|
||||
if (fParser->StreamInfo(i)->odml_index_size != 0) {
|
||||
found_odml_index = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_odml_index) {
|
||||
TRACE("OpenDMLFile::SetTo file has no standard avi index, and no OpenDML track index found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("OpenDMLFile::SetTo: this is a %s AVI file with %d streams\n", fParser->OdmlExtendedHeader() ? "OpenDML" : "standard", fParser->StreamCount());
|
||||
|
||||
InitData();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLFile::InitData()
|
||||
{
|
||||
delete [] fStreamData;
|
||||
|
||||
fStreamCount = fParser->StreamCount();
|
||||
fStreamData = new stream_data[fStreamCount];
|
||||
|
||||
for (int stream = 0; stream < fStreamCount; stream++) {
|
||||
TRACE("OpenDMLFile::InitData: stream %d\n", stream);
|
||||
|
||||
fStreamData[stream].info = fParser->StreamInfo(stream);
|
||||
if (fStreamData[stream].info->odml_index_size) {
|
||||
TRACE("OpenDMLFile::InitData: index header, start %Ld, size %lu\n", fStreamData[stream].info->odml_index_start, fStreamData[stream].info->odml_index_size);
|
||||
|
||||
odml_index_header h;
|
||||
|
||||
// XXX error checking + endian conv.
|
||||
|
||||
fSource->ReadAt(fStreamData[stream].info->odml_index_start, &h, sizeof(h));
|
||||
|
||||
TRACE("longs_per_entry %u\n", h.longs_per_entry);
|
||||
TRACE("index_sub_type %u\n", h.index_sub_type);
|
||||
TRACE("index_type %u\n", h.index_type);
|
||||
TRACE("entries_used %lu\n", h.entries_used);
|
||||
TRACE("chunk_id "FOURCC_FORMAT"\n", FOURCC_PARAM(h.chunk_id));
|
||||
|
||||
if (h.index_type == AVI_INDEX_OF_INDEXES) {
|
||||
int size = h.entries_used * h.longs_per_entry * 4;
|
||||
TRACE("OpenDMLFile::InitData: reading superindex of %d bytes\n", size);
|
||||
fStreamData[stream].superindex_entry_size = h.longs_per_entry * 4;
|
||||
fStreamData[stream].superindex_entry_count = h.entries_used;
|
||||
fStreamData[stream].superindex_entry_pos = 0;
|
||||
fStreamData[stream].superindex = new char [size];
|
||||
fSource->ReadAt(fStreamData[stream].info->odml_index_start + sizeof(h), fStreamData[stream].superindex, size);
|
||||
} else if (h.index_type == AVI_INDEX_OF_CHUNKS){
|
||||
TRACE("OpenDMLFile::InitData: creating fake superindex\n");
|
||||
fStreamData[stream].superindex_entry_size = 16;
|
||||
fStreamData[stream].superindex_entry_count = 1;
|
||||
fStreamData[stream].superindex_entry_pos = 0;
|
||||
fStreamData[stream].superindex = new char [16];
|
||||
((odml_superindex_entry *)fStreamData[stream].superindex)->start = fStreamData[stream].info->odml_index_start;
|
||||
((odml_superindex_entry *)fStreamData[stream].superindex)->size = fStreamData[stream].info->odml_index_size;
|
||||
((odml_superindex_entry *)fStreamData[stream].superindex)->duration = 0;
|
||||
} else if (h.index_type == AVI_INDEX_IS_DATA){
|
||||
TRACE("OpenDMLFile::InitData: AVI_INDEX_IS_DATA not supported\n");
|
||||
fStreamData[stream].superindex = 0;
|
||||
fStreamData[stream].superindex_entry_count = 0;
|
||||
fStreamData[stream].superindex_entry_pos = 0;
|
||||
} else {
|
||||
TRACE("OpenDMLFile::InitData: index type not recongnized\n");
|
||||
fStreamData[stream].superindex = 0;
|
||||
fStreamData[stream].superindex_entry_count = 0;
|
||||
fStreamData[stream].superindex_entry_pos = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fStreamData[stream].superindex_entry_count; i++) {
|
||||
odml_superindex_entry *entry = (odml_superindex_entry *) (fStreamData[stream].superindex + i * fStreamData[stream].superindex_entry_size);
|
||||
TRACE("superindex entry %d: start %10Ld, size %8ld, duration %lu\n", i, entry->start, entry->size, entry->duration);
|
||||
}
|
||||
|
||||
fStreamData[stream].index_entry_start = 0;
|
||||
fStreamData[stream].index_base_offset = 0;
|
||||
fStreamData[stream].index_entry_size = 0;
|
||||
fStreamData[stream].index_entry_count = 0;
|
||||
fStreamData[stream].index_entry_pos = 0;
|
||||
fStreamData[stream].index_chunk = new char [INDEX_CHUNK_SIZE];
|
||||
fStreamData[stream].index_chunk_entry_count = 0;
|
||||
fStreamData[stream].index_chunk_entry_pos = 0;
|
||||
} else {
|
||||
fStreamData[stream].superindex = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
OpenDMLFile::ReadIndexInfo(int stream_index)
|
||||
{
|
||||
stream_data *data = &fStreamData[stream_index];
|
||||
|
||||
if (data->superindex_entry_pos >= data->superindex_entry_count) {
|
||||
TRACE("reached end of superindex\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
odml_superindex_entry *entry = (odml_superindex_entry *) (data->superindex + data->superindex_entry_pos * data->superindex_entry_size);
|
||||
|
||||
TRACE("OpenDMLFile::ReadIndexInfo: stream %d, pos %d, start %Ld, size %lu, duration %u\n",
|
||||
stream_index, data->superindex_entry_pos, entry->start, entry->size, entry->duration);
|
||||
|
||||
odml_chunk_index_header chunk_index_header;
|
||||
|
||||
if (sizeof(chunk_index_header) != fSource->ReadAt(entry->start + 8, &chunk_index_header, sizeof(chunk_index_header))) {
|
||||
TRACE("read error\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
TRACE("longs_per_entry %u\n", chunk_index_header.longs_per_entry);
|
||||
TRACE("index_sub_type %u\n", chunk_index_header.index_sub_type);
|
||||
TRACE("index_type %u\n", chunk_index_header.index_type);
|
||||
TRACE("entries_used %lu\n", chunk_index_header.entries_used);
|
||||
TRACE("chunk_id "FOURCC_FORMAT"\n", FOURCC_PARAM(chunk_index_header.chunk_id));
|
||||
TRACE("base_offset %Ld\n", chunk_index_header.base_offset);
|
||||
|
||||
data->index_base_offset = chunk_index_header.base_offset;
|
||||
data->index_entry_start = entry->start + sizeof(chunk_index_header) + 8;
|
||||
data->index_entry_size = chunk_index_header.longs_per_entry * 4;
|
||||
data->index_entry_count = chunk_index_header.entries_used;
|
||||
data->index_entry_pos = 0;
|
||||
|
||||
data->superindex_entry_pos++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OpenDMLFile::ReadIndexChunk(int stream_index)
|
||||
{
|
||||
stream_data *data = &fStreamData[stream_index];
|
||||
|
||||
while (data->index_entry_pos >= data->index_entry_count) {
|
||||
if (!ReadIndexInfo(stream_index))
|
||||
return false;
|
||||
}
|
||||
|
||||
data->index_chunk_entry_count = min_c(data->index_entry_count - data->index_entry_pos, INDEX_CHUNK_SIZE / data->index_entry_size);
|
||||
data->index_chunk_entry_pos = 0;
|
||||
int size = data->index_chunk_entry_count * data->index_entry_size;
|
||||
int64 start = data->index_entry_start + data->index_entry_pos * data->index_entry_size;
|
||||
|
||||
TRACE("OpenDMLFile::ReadIndexChunk: stream %d, index_chunk_entry_count %d, size %d, start %Ld\n",
|
||||
stream_index, data->index_chunk_entry_count, size, start);
|
||||
|
||||
if (size != fSource->ReadAt(start, data->index_chunk, size)) {
|
||||
TRACE("read error\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
data->index_entry_pos += data->index_chunk_entry_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OpenDMLFile::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe)
|
||||
{
|
||||
stream_data *data = &fStreamData[stream_index];
|
||||
|
||||
while (data->index_chunk_entry_pos >= data->index_chunk_entry_count) {
|
||||
if (!ReadIndexChunk(stream_index))
|
||||
return false;
|
||||
}
|
||||
|
||||
odml_index_entry *entry = (odml_index_entry *)(data->index_chunk + data->index_chunk_entry_pos * data->index_entry_size);
|
||||
|
||||
*start = data->index_base_offset + entry->start;
|
||||
*size = entry->size & 0x7fffffff;
|
||||
*keyframe = (entry->size & 0x80000000) ? false : true;
|
||||
|
||||
data->index_chunk_entry_pos++;
|
||||
|
||||
printf("OpenDMLFile::GetNextChunkInfo: stream %d: start %15Ld, size %6d%s\n",
|
||||
stream_index, *start, *size, *keyframe ? ", keyframe" : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
OpenDMLFile::StreamCount()
|
||||
{
|
||||
return fStreamCount;
|
||||
}
|
||||
|
||||
bigtime_t
|
||||
OpenDMLFile::Duration()
|
||||
{
|
||||
if (!fParser->AviMainHeader())
|
||||
return 0;
|
||||
if (fParser->OdmlExtendedHeader())
|
||||
return fParser->OdmlExtendedHeader()->total_frames * (bigtime_t)fParser->AviMainHeader()->micro_sec_per_frame;
|
||||
return fParser->AviMainHeader()->total_frames * (bigtime_t)fParser->AviMainHeader()->micro_sec_per_frame;
|
||||
}
|
||||
|
||||
uint32
|
||||
OpenDMLFile::FrameCount()
|
||||
{
|
||||
if (fParser->OdmlExtendedHeader())
|
||||
return fParser->OdmlExtendedHeader()->total_frames;
|
||||
if (fParser->AviMainHeader())
|
||||
return fParser->AviMainHeader()->total_frames;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
OpenDMLFile::IsVideo(int stream_index)
|
||||
{
|
||||
return fStreamData[stream_index].info->is_video;
|
||||
}
|
||||
|
||||
bool
|
||||
OpenDMLFile::IsAudio(int stream_index)
|
||||
{
|
||||
return fStreamData[stream_index].info->is_audio;
|
||||
}
|
||||
|
||||
const wave_format_ex *
|
||||
OpenDMLFile::AudioFormat(int stream_index)
|
||||
{
|
||||
return (fStreamData[stream_index].info->is_audio && fStreamData[stream_index].info->audio_format_valid) ?
|
||||
&fStreamData[stream_index].info->audio_format : 0;
|
||||
}
|
||||
|
||||
const bitmap_info_header *
|
||||
OpenDMLFile::VideoFormat(int stream_index)
|
||||
{
|
||||
return (fStreamData[stream_index].info->is_video && fStreamData[stream_index].info->video_format_valid) ?
|
||||
&fStreamData[stream_index].info->video_format : 0;
|
||||
}
|
||||
|
||||
const avi_stream_header *
|
||||
OpenDMLFile::StreamFormat(int stream_index)
|
||||
{
|
||||
return (fStreamData[stream_index].info->stream_header_valid) ?
|
||||
&fStreamData[stream_index].info->stream_header : 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <DataIO.h>
|
||||
#include "OpenDMLParser.h"
|
||||
|
||||
class OpenDMLFile
|
||||
{
|
||||
public:
|
||||
OpenDMLFile();
|
||||
~OpenDMLFile();
|
||||
|
||||
static bool IsSupported(BPositionIO *source);
|
||||
|
||||
status_t SetTo(BPositionIO *source);
|
||||
|
||||
int StreamCount();
|
||||
|
||||
bigtime_t Duration();
|
||||
uint32 FrameCount();
|
||||
|
||||
bool IsVideo(int stream_index);
|
||||
bool IsAudio(int stream_index);
|
||||
|
||||
const wave_format_ex * AudioFormat(int stream_index);
|
||||
const bitmap_info_header * VideoFormat(int stream_index);
|
||||
const avi_stream_header * StreamFormat(int stream_index);
|
||||
|
||||
bool GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe);
|
||||
|
||||
BPositionIO *Source() { return fSource; }
|
||||
|
||||
private:
|
||||
void InitData();
|
||||
bool ReadIndexChunk(int stream_index);
|
||||
bool ReadIndexInfo(int stream_index);
|
||||
|
||||
private:
|
||||
BPositionIO * fSource;
|
||||
OpenDMLParser * fParser;
|
||||
|
||||
struct stream_data;
|
||||
|
||||
int fStreamCount;
|
||||
stream_data * fStreamData;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,609 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "OpenDMLParser.h"
|
||||
#include "avi.h"
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...)
|
||||
#endif
|
||||
|
||||
struct movie_chunk
|
||||
{
|
||||
movie_chunk * next;
|
||||
int64 start;
|
||||
uint32 size;
|
||||
};
|
||||
|
||||
OpenDMLParser::OpenDMLParser()
|
||||
: fSource(0),
|
||||
fSize(0),
|
||||
fStandardIndexStart(0),
|
||||
fStandardIndexSize(0),
|
||||
fStreamCount(0),
|
||||
fMovieChunkCount(0),
|
||||
fAviMainHeaderValid(false),
|
||||
fOdmlExtendedHeaderValid(false),
|
||||
fStreams(0),
|
||||
fCurrentStream(0)
|
||||
{
|
||||
}
|
||||
|
||||
OpenDMLParser::~OpenDMLParser()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
OpenDMLParser::StreamCount()
|
||||
{
|
||||
return fStreamCount;
|
||||
}
|
||||
|
||||
const stream_info *
|
||||
OpenDMLParser::StreamInfo(int index)
|
||||
{
|
||||
if (index < 0 || index >= fStreamCount)
|
||||
return 0;
|
||||
|
||||
stream_info *info = fStreams;
|
||||
while (index--)
|
||||
info = info->next;
|
||||
return info;
|
||||
}
|
||||
|
||||
int64
|
||||
OpenDMLParser::StandardIndexStart()
|
||||
{
|
||||
return fStandardIndexStart;
|
||||
}
|
||||
|
||||
uint32
|
||||
OpenDMLParser::StandardIndexSize()
|
||||
{
|
||||
return fStandardIndexSize;
|
||||
}
|
||||
|
||||
const avi_main_header *
|
||||
OpenDMLParser::AviMainHeader()
|
||||
{
|
||||
return fAviMainHeaderValid ? &fAviMainHeader : 0;
|
||||
}
|
||||
|
||||
const odml_extended_header *
|
||||
OpenDMLParser::OdmlExtendedHeader()
|
||||
{
|
||||
return fOdmlExtendedHeaderValid ? &fOdmlExtendedHeader : 0;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::CreateNewStreamInfo()
|
||||
{
|
||||
stream_info *info = new stream_info;
|
||||
info->next = 0;
|
||||
info->is_audio = false;
|
||||
info->is_video = false;
|
||||
info->stream_header_valid = false;
|
||||
info->audio_format_valid = false;
|
||||
info->video_format_valid = false;
|
||||
info->odml_index_start = 0;
|
||||
info->odml_index_size = 0;
|
||||
|
||||
// append the new stream_info to the fStreams list and point fCurrentStream to it
|
||||
if (fStreams) {
|
||||
stream_info *cur = fStreams;
|
||||
while (cur->next)
|
||||
cur = cur->next;
|
||||
cur->next = info;
|
||||
} else {
|
||||
fStreams = info;
|
||||
}
|
||||
fCurrentStream = info;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::Parse(BPositionIO *source)
|
||||
{
|
||||
TRACE("OpenDMLParser::Parse\n");
|
||||
|
||||
fSource = source;
|
||||
fSize = source->Seek(0, SEEK_END);
|
||||
|
||||
if (fSize < 32) {
|
||||
TRACE("OpenDMLParser::Parse: file to small\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 pos = 0;
|
||||
int riff_chunk_number = 0;
|
||||
while (pos < (uint64)fSize) {
|
||||
uint32 temp;
|
||||
uint32 fourcc;
|
||||
uint32 size;
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos);
|
||||
goto err;
|
||||
}
|
||||
pos += 4;
|
||||
fourcc = AVI_UINT32(temp);
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos);
|
||||
goto err;
|
||||
}
|
||||
pos += 4;
|
||||
size = AVI_UINT32(temp);
|
||||
|
||||
if (fourcc == FOURCC('J','U','N','K')) {
|
||||
TRACE("OpenDMLParser::Parse: JUNK chunk ignored, size: %lu bytes\n", size);
|
||||
goto cont;
|
||||
}
|
||||
|
||||
if (fourcc != FOURCC('R','I','F','F')) {
|
||||
if (riff_chunk_number == 0) {
|
||||
TRACE("OpenDMLParser::Parse: not a RIFF file\n");
|
||||
} else {
|
||||
TRACE("OpenDMLParser::Parse: unknown chunk '"FOURCC_FORMAT"' (expected 'RIFF'), size = %lu ignored\n", FOURCC_PARAM(fourcc), size);
|
||||
goto cont;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("OpenDMLParser::Parse: RIFF chunk %d size: %lu bytes\n", riff_chunk_number, size);
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos);
|
||||
goto err;
|
||||
}
|
||||
fourcc = AVI_UINT32(temp);
|
||||
|
||||
if (riff_chunk_number == 0 && fourcc != FOURCC('A','V','I',' ')) {
|
||||
TRACE("OpenDMLParser::Parse: not a AVI file\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fourcc != FOURCC('A','V','I',' ') && fourcc != FOURCC('A','V','I','X')) {
|
||||
TRACE("OpenDMLParser::Parse: unknown chunk '"FOURCC_FORMAT"' , size = %lu ignored\n", FOURCC_PARAM(fourcc), size);
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ParseChunk_AVI(riff_chunk_number, pos + 4, size - 4);
|
||||
|
||||
cont:
|
||||
pos += (size) + (size & 1);
|
||||
riff_chunk_number++;
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
fStreamCount = 0;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_AVI(int number, uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_AVI\n");
|
||||
uint64 pos = start;
|
||||
uint64 end = start + size;
|
||||
|
||||
while (pos < end) {
|
||||
uint32 temp;
|
||||
uint32 Chunkfcc;
|
||||
uint32 Chunksize;
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::ParseChunk_AVI: read error at pos %llu\n",pos);
|
||||
return;
|
||||
}
|
||||
pos += 4;
|
||||
Chunkfcc = AVI_UINT32(temp);
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::ParseChunk_AVI: read error at pos %llu\n",pos);
|
||||
return;
|
||||
}
|
||||
pos += 4;
|
||||
Chunksize = AVI_UINT32(temp);
|
||||
|
||||
TRACE("OpenDMLParser::ParseChunk_AVI: chunk '"FOURCC_FORMAT"', size = %lu\n", FOURCC_PARAM(Chunkfcc), Chunksize);
|
||||
|
||||
if (Chunkfcc == FOURCC('J','U','N','K'))
|
||||
goto cont;
|
||||
else if (Chunkfcc == FOURCC('L','I','S','T'))
|
||||
ParseChunk_LIST(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('i','d','x','1')) {
|
||||
ParseChunk_idx1(pos, Chunksize);
|
||||
} else {
|
||||
TRACE("OpenDMLParser::ParseChunk_AVI: unknown chunk ignored\n");
|
||||
}
|
||||
|
||||
cont:
|
||||
pos += (Chunksize) + (Chunksize & 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_LIST(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_LIST\n");
|
||||
uint32 temp;
|
||||
uint32 fourcc;
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(start, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::ParseChunk_LIST: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
fourcc = AVI_UINT32(temp);
|
||||
|
||||
TRACE("OpenDMLParser::ParseChunk_LIST: type '"FOURCC_FORMAT"'\n", FOURCC_PARAM(fourcc));
|
||||
|
||||
if (fourcc == FOURCC('m','o','v','i'))
|
||||
ParseList_movi(start + 4, size - 4);
|
||||
else if (fourcc == FOURCC('r','e','c',' '))
|
||||
ParseList_movi(start + 4, size - 4); //XXX parse rec simliar to movi???
|
||||
else if (fourcc == FOURCC('h','d','r','l'))
|
||||
ParseList_generic(start + 4, size - 4);
|
||||
else if (fourcc == FOURCC('s','t','r','l'))
|
||||
ParseList_strl(start + 4, size - 4);
|
||||
else if (fourcc == FOURCC('o','d','m','l'))
|
||||
ParseList_generic(start + 4, size - 4);
|
||||
else
|
||||
TRACE("OpenDMLParser::ParseChunk_LIST: unknown list type ignored\n");
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_idx1(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_idx1\n");
|
||||
|
||||
if (fStandardIndexSize != 0) {
|
||||
TRACE("OpenDMLParser::ParseChunk_idx1: found a second chunk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fStandardIndexStart = start;
|
||||
fStandardIndexSize = size;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_avih(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_avih\n");
|
||||
|
||||
if (fAviMainHeaderValid) {
|
||||
TRACE("OpenDMLParser::ParseChunk_avih: found a second chunk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < sizeof(fAviMainHeader)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_avih: warning, avi header chunk too small\n");
|
||||
}
|
||||
|
||||
memset(&fAviMainHeader, 0, sizeof(fAviMainHeader));
|
||||
size = min_c(size, sizeof(fAviMainHeader));
|
||||
if ((ssize_t)size != fSource->ReadAt(start, &fAviMainHeader, size)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_avih: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_BENDIAN
|
||||
B_SWAP_INT32(&fAviMainHeader.micro_sec_per_frame);
|
||||
B_SWAP_INT32(&fAviMainHeader.max_bytes_per_sec);
|
||||
B_SWAP_INT32(&fAviMainHeader.padding_granularity);
|
||||
B_SWAP_INT32(&fAviMainHeader.flags);
|
||||
B_SWAP_INT32(&fAviMainHeader.total_frames);
|
||||
B_SWAP_INT32(&fAviMainHeader.initial_frames);
|
||||
B_SWAP_INT32(&fAviMainHeader.streams);
|
||||
B_SWAP_INT32(&fAviMainHeader.suggested_buffer_size);
|
||||
B_SWAP_INT32(&fAviMainHeader.width);
|
||||
B_SWAP_INT32(&fAviMainHeader.height);
|
||||
#endif
|
||||
|
||||
fAviMainHeaderValid = true;
|
||||
|
||||
TRACE("fAviMainHeader:\n");
|
||||
TRACE("micro_sec_per_frame = %lu\n", fAviMainHeader.micro_sec_per_frame);
|
||||
TRACE("max_bytes_per_sec = %lu\n", fAviMainHeader.max_bytes_per_sec);
|
||||
TRACE("padding_granularity = %lu\n", fAviMainHeader.padding_granularity);
|
||||
TRACE("flags = 0x%lx\n", fAviMainHeader.flags);
|
||||
TRACE("total_frames = %lu\n", fAviMainHeader.total_frames);
|
||||
TRACE("initial_frames = %lu\n", fAviMainHeader.initial_frames);
|
||||
TRACE("streams = %lu\n", fAviMainHeader.streams);
|
||||
TRACE("suggested_buffer_size = %lu\n", fAviMainHeader.suggested_buffer_size);
|
||||
TRACE("width = %lu\n", fAviMainHeader.width);
|
||||
TRACE("height = %lu\n", fAviMainHeader.height);
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_strh\n");
|
||||
|
||||
if (fCurrentStream == 0) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strh: error, no Stream info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fCurrentStream->stream_header_valid) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strh: error, already have stream header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < sizeof(fCurrentStream->stream_header)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strh: warning, avi stream header chunk too small\n");
|
||||
}
|
||||
|
||||
memset(&fCurrentStream->stream_header, 0, sizeof(fCurrentStream->stream_header));
|
||||
|
||||
size = min_c(size, sizeof(fCurrentStream->stream_header));
|
||||
if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->stream_header, size)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strh: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_BENDIAN
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.fourcc_type);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.fourcc_handler);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.flags);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.priority);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.language);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.initial_frames);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.scale);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.rate);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.start);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.length);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.suggested_buffer_size);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.quality);
|
||||
B_SWAP_INT32(&fCurrentStream->stream_header.sample_size);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.rect_left);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.rect_top);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.rect_right);
|
||||
B_SWAP_INT16(&fCurrentStream->stream_header.rect_bottom);
|
||||
#endif
|
||||
|
||||
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');
|
||||
|
||||
TRACE("stream_header, Stream %d, is_audio %d, is_video %d:\n", fStreamCount - 1, fCurrentStream->is_audio, fCurrentStream->is_video);
|
||||
TRACE("fourcc_type = '"FOURCC_FORMAT"'\n",FOURCC_PARAM(fCurrentStream->stream_header.fourcc_type));
|
||||
TRACE("fourcc_handler = '"FOURCC_FORMAT"'\n",FOURCC_PARAM(fCurrentStream->stream_header.fourcc_handler));
|
||||
TRACE("flags = 0x%lx\n", fCurrentStream->stream_header.flags);
|
||||
TRACE("priority = %u\n", fCurrentStream->stream_header.priority);
|
||||
TRACE("language = %u\n", fCurrentStream->stream_header.language);
|
||||
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);
|
||||
TRACE("quality = %lu\n", fCurrentStream->stream_header.quality);
|
||||
TRACE("sample_size = %lu\n", fCurrentStream->stream_header.sample_size);
|
||||
TRACE("rect_left = %d\n", fCurrentStream->stream_header.rect_left);
|
||||
TRACE("rect_top = %d\n", fCurrentStream->stream_header.rect_top);
|
||||
TRACE("rect_right = %d\n", fCurrentStream->stream_header.rect_right );
|
||||
TRACE("rect_bottom = %d\n", fCurrentStream->stream_header.rect_bottom);
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_strf(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_strf\n");
|
||||
|
||||
if (fCurrentStream == 0) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: error, no Stream info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fCurrentStream->is_audio) {
|
||||
|
||||
if (fCurrentStream->audio_format_valid) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: error, already have audio format header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// if (size < sizeof(fCurrentStream->audio_format)) {
|
||||
// TRACE("OpenDMLParser::ParseChunk_strf: warning, avi audio header chunk too small\n");
|
||||
// }
|
||||
|
||||
memset(&fCurrentStream->audio_format, 0, sizeof(fCurrentStream->audio_format));
|
||||
|
||||
size = min_c(size, sizeof(fCurrentStream->audio_format));
|
||||
if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->audio_format, size)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_BENDIAN
|
||||
B_SWAP_INT16(&fCurrentStream->audio_format.format_tag);
|
||||
B_SWAP_INT16(&fCurrentStream->audio_format.channels);
|
||||
B_SWAP_INT32(&fCurrentStream->audio_format.frames_per_sec);
|
||||
B_SWAP_INT32(&fCurrentStream->audio_format.avg_bytes_per_sec);
|
||||
B_SWAP_INT32(&fCurrentStream->audio_format.block_align);
|
||||
B_SWAP_INT32(&fCurrentStream->audio_format.bits_per_sample);
|
||||
B_SWAP_INT32(&fCurrentStream->audio_format.extra_size);
|
||||
#endif
|
||||
|
||||
fCurrentStream->audio_format_valid = true;
|
||||
|
||||
TRACE("audio_format:\n");
|
||||
TRACE("format_tag = 0x%x\n", fCurrentStream->audio_format.format_tag);
|
||||
TRACE("channels = %u\n", fCurrentStream->audio_format.channels);
|
||||
TRACE("frames_per_sec = %lu\n", fCurrentStream->audio_format.frames_per_sec);
|
||||
TRACE("avg_bytes_per_sec = %lu\n", fCurrentStream->audio_format.avg_bytes_per_sec);
|
||||
TRACE("block_align = %u\n", fCurrentStream->audio_format.block_align);
|
||||
TRACE("bits_per_sample = %u\n", fCurrentStream->audio_format.bits_per_sample);
|
||||
TRACE("extra_size = %u\n", fCurrentStream->audio_format.extra_size);
|
||||
|
||||
// XXX read extra data
|
||||
|
||||
} else if (fCurrentStream->is_video) {
|
||||
|
||||
if (fCurrentStream->video_format_valid) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: error, already have video format header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// if (size < sizeof(fCurrentStream->video_format)) {
|
||||
// TRACE("OpenDMLParser::ParseChunk_strf: warning, avi video header chunk too small\n");
|
||||
// }
|
||||
|
||||
memset(&fCurrentStream->video_format, 0, sizeof(fCurrentStream->video_format));
|
||||
|
||||
size = min_c(size, sizeof(fCurrentStream->video_format));
|
||||
if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->video_format, size)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_BENDIAN
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.size);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.width);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.height);
|
||||
B_SWAP_INT16(&fCurrentStream->video_format.planes);
|
||||
B_SWAP_INT16(&fCurrentStream->video_format.bit_count);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.compression);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.image_size);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.x_pels_per_meter);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.y_pels_per_meter);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.clr_used);
|
||||
B_SWAP_INT32(&fCurrentStream->video_format.clr_important);
|
||||
#endif
|
||||
|
||||
fCurrentStream->video_format_valid = true;
|
||||
|
||||
TRACE("audio_format:\n");
|
||||
TRACE("size = %lu\n", fCurrentStream->video_format.size);
|
||||
TRACE("width = %lu\n", fCurrentStream->video_format.width);
|
||||
TRACE("height = %lu\n", fCurrentStream->video_format.height);
|
||||
TRACE("planes = %u\n", fCurrentStream->video_format.planes);
|
||||
TRACE("bit_count = %u\n", fCurrentStream->video_format.bit_count);
|
||||
TRACE("compression = 0x%08lx\n", fCurrentStream->video_format.compression);
|
||||
TRACE("image_size = %lu\n", fCurrentStream->video_format.image_size);
|
||||
TRACE("x_pels_per_meter = %lu\n", fCurrentStream->video_format.x_pels_per_meter);
|
||||
TRACE("y_pels_per_meter = %lu\n", fCurrentStream->video_format.y_pels_per_meter);
|
||||
TRACE("clr_used = %lu\n", fCurrentStream->video_format.clr_used);
|
||||
TRACE("clr_important = %lu\n", fCurrentStream->video_format.clr_important);
|
||||
|
||||
} else {
|
||||
TRACE("OpenDMLParser::ParseChunk_strf: error, unknown Stream type\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_indx(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_indx\n");
|
||||
|
||||
if (fCurrentStream == 0) {
|
||||
TRACE("OpenDMLParser::ParseChunk_indx: error, no stream info\n");
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
fCurrentStream->odml_index_start = start;
|
||||
fCurrentStream->odml_index_size = size;
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseChunk_dmlh(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseChunk_dmlh\n");
|
||||
|
||||
if (fOdmlExtendedHeaderValid) {
|
||||
TRACE("OpenDMLParser::ParseChunk_dmlh: found a second chunk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < sizeof(fOdmlExtendedHeader)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_dmlh: warning, avi header chunk too small\n");
|
||||
}
|
||||
|
||||
memset(&fOdmlExtendedHeader, 0, sizeof(fOdmlExtendedHeader));
|
||||
size = min_c(size, sizeof(fOdmlExtendedHeader));
|
||||
if ((ssize_t)size != fSource->ReadAt(start, &fOdmlExtendedHeader, size)) {
|
||||
TRACE("OpenDMLParser::ParseChunk_dmlh: read error at pos %llu\n", start);
|
||||
return;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_BENDIAN
|
||||
B_SWAP_INT32(&fOdmlExtendedHeader.total_frames);
|
||||
#endif
|
||||
|
||||
fOdmlExtendedHeaderValid = true;
|
||||
|
||||
TRACE("fOdmlExtendedHeader:\n");
|
||||
TRACE("total_frames = %ld\n", fOdmlExtendedHeader.total_frames);
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseList_strl(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseList_strl\n");
|
||||
|
||||
CreateNewStreamInfo();
|
||||
fStreamCount++;
|
||||
|
||||
ParseList_generic(start, size);
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseList_generic(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseList_generic\n");
|
||||
uint64 pos = start;
|
||||
uint64 end = start + size;
|
||||
|
||||
while (pos < end) {
|
||||
uint32 temp;
|
||||
uint32 Chunkfcc;
|
||||
uint32 Chunksize;
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::ParseList_generic: read error at pos %llu\n",pos);
|
||||
return;
|
||||
}
|
||||
pos += 4;
|
||||
Chunkfcc = AVI_UINT32(temp);
|
||||
|
||||
if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) {
|
||||
TRACE("OpenDMLParser::ParseList_generic: read error at pos %llu\n",pos);
|
||||
return;
|
||||
}
|
||||
pos += 4;
|
||||
Chunksize = AVI_UINT32(temp);
|
||||
|
||||
TRACE("OpenDMLParser::ParseList_generic: chunk '"FOURCC_FORMAT"', size = %ld\n", FOURCC_PARAM(Chunkfcc), Chunksize);
|
||||
|
||||
if (Chunkfcc == FOURCC('J','U','N','K'))
|
||||
goto cont;
|
||||
else if (Chunkfcc == FOURCC('a','v','i','h'))
|
||||
ParseChunk_avih(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('L','I','S','T'))
|
||||
ParseChunk_LIST(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('s','t','r','h'))
|
||||
ParseChunk_strh(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('s','t','r','f'))
|
||||
ParseChunk_strf(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('i','n','d','x'))
|
||||
ParseChunk_indx(pos, Chunksize);
|
||||
else if (Chunkfcc == FOURCC('d','m','l','h'))
|
||||
ParseChunk_dmlh(pos, Chunksize);
|
||||
else
|
||||
TRACE("OpenDMLParser::ParseList_generic: unknown chunk ignored\n");
|
||||
|
||||
cont:
|
||||
pos += (Chunksize) + (Chunksize & 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpenDMLParser::ParseList_movi(uint64 start, uint32 size)
|
||||
{
|
||||
TRACE("OpenDMLParser::ParseList_movi\n");
|
||||
|
||||
fMovieChunkCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef _OPENDML_PARSER_H
|
||||
#define _OPENDML_PARSER_H
|
||||
|
||||
#include <DataIO.h>
|
||||
#include "avi.h"
|
||||
|
||||
struct stream_info
|
||||
{
|
||||
stream_info * next;
|
||||
bool is_audio;
|
||||
bool is_video;
|
||||
bool stream_header_valid;
|
||||
avi_stream_header stream_header;
|
||||
bool audio_format_valid;
|
||||
wave_format_ex audio_format;
|
||||
bool video_format_valid;
|
||||
bitmap_info_header video_format;
|
||||
int64 odml_index_start;
|
||||
uint32 odml_index_size;
|
||||
};
|
||||
|
||||
class OpenDMLParser
|
||||
{
|
||||
public:
|
||||
OpenDMLParser();
|
||||
~OpenDMLParser();
|
||||
void Parse(BPositionIO *source);
|
||||
|
||||
int StreamCount();
|
||||
|
||||
const stream_info * StreamInfo(int index);
|
||||
|
||||
int64 StandardIndexStart();
|
||||
uint32 StandardIndexSize();
|
||||
|
||||
const avi_main_header * AviMainHeader();
|
||||
const odml_extended_header * OdmlExtendedHeader();
|
||||
|
||||
private:
|
||||
void ParseChunk_AVI(int number, uint64 start, uint32 size);
|
||||
void ParseChunk_LIST(uint64 start, uint32 size);
|
||||
void ParseChunk_idx1(uint64 start, uint32 size);
|
||||
void ParseChunk_indx(uint64 start, uint32 size);
|
||||
void ParseChunk_avih(uint64 start, uint32 size);
|
||||
void ParseChunk_strh(uint64 start, uint32 size);
|
||||
void ParseChunk_strf(uint64 start, uint32 size);
|
||||
void ParseChunk_dmlh(uint64 start, uint32 size);
|
||||
void ParseList_movi(uint64 start, uint32 size);
|
||||
void ParseList_generic(uint64 start, uint32 size);
|
||||
void ParseList_strl(uint64 start, uint32 size);
|
||||
|
||||
private:
|
||||
void CreateNewStreamInfo();
|
||||
|
||||
BPositionIO * fSource;
|
||||
int64 fSize;
|
||||
|
||||
int64 fStandardIndexStart;
|
||||
uint32 fStandardIndexSize;
|
||||
|
||||
int fStreamCount;
|
||||
int fMovieChunkCount;
|
||||
|
||||
avi_main_header fAviMainHeader;
|
||||
bool fAviMainHeaderValid;
|
||||
|
||||
odml_extended_header fOdmlExtendedHeader;
|
||||
bool fOdmlExtendedHeaderValid;
|
||||
|
||||
stream_info * fStreams;
|
||||
stream_info * fCurrentStream;
|
||||
};
|
||||
|
||||
#endif // _OPENDML_PARSER_H
|
||||
@@ -0,0 +1,144 @@
|
||||
#ifndef _AVI_H
|
||||
#define _AVI_H
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
#define AVI_UINT32(a) ((uint32)B_LENDIAN_TO_HOST_INT32(a))
|
||||
|
||||
#define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) | (((uint32)(b)) << 8) | ((uint32)(a)))
|
||||
#define FOURCC_FORMAT "%c%c%c%c"
|
||||
#define FOURCC_PARAM(f) (int)(f & 0xff),(int)((f >> 8) & 0xff),(int)((f >> 16) & 0xff),(int)((f >> 24) & 0xff)
|
||||
|
||||
struct avi_main_header
|
||||
{
|
||||
uint32 micro_sec_per_frame;
|
||||
uint32 max_bytes_per_sec;
|
||||
uint32 padding_granularity;
|
||||
uint32 flags;
|
||||
uint32 total_frames;
|
||||
uint32 initial_frames;
|
||||
uint32 streams;
|
||||
uint32 suggested_buffer_size;
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
uint32 reserved[4];
|
||||
} _PACKED;
|
||||
|
||||
struct avi_standard_index_entry
|
||||
{
|
||||
uint32 chunk_id;
|
||||
uint32 flags;
|
||||
uint32 chunk_offset;
|
||||
uint32 chunk_length;
|
||||
} _PACKED;
|
||||
|
||||
struct odml_extended_header
|
||||
{
|
||||
uint32 total_frames;
|
||||
} _PACKED;
|
||||
|
||||
struct avi_stream_header
|
||||
{
|
||||
uint32 fourcc_type;
|
||||
uint32 fourcc_handler;
|
||||
uint32 flags;
|
||||
uint16 priority;
|
||||
uint16 language;
|
||||
uint32 initial_frames;
|
||||
uint32 scale;
|
||||
uint32 rate;
|
||||
uint32 start;
|
||||
uint32 length;
|
||||
uint32 suggested_buffer_size;
|
||||
uint32 quality;
|
||||
uint32 sample_size;
|
||||
int16 rect_left;
|
||||
int16 rect_top;
|
||||
int16 rect_right;
|
||||
int16 rect_bottom;
|
||||
} _PACKED;
|
||||
|
||||
struct bitmap_info_header
|
||||
{
|
||||
uint32 size;
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
uint16 planes;
|
||||
uint16 bit_count;
|
||||
uint32 compression;
|
||||
uint32 image_size;
|
||||
uint32 x_pels_per_meter;
|
||||
uint32 y_pels_per_meter;
|
||||
uint32 clr_used;
|
||||
uint32 clr_important;
|
||||
} _PACKED;
|
||||
|
||||
struct wave_format_ex
|
||||
{
|
||||
uint16 format_tag;
|
||||
uint16 channels;
|
||||
uint32 frames_per_sec;
|
||||
uint32 avg_bytes_per_sec;
|
||||
uint16 block_align;
|
||||
uint16 bits_per_sample;
|
||||
uint16 extra_size;
|
||||
// char extra_data[extra_size]
|
||||
} _PACKED;
|
||||
|
||||
struct odml_index_header
|
||||
{
|
||||
uint16 longs_per_entry;
|
||||
uint8 index_sub_type;
|
||||
uint8 index_type;
|
||||
uint32 entries_used;
|
||||
uint32 chunk_id;
|
||||
uint32 reserved[3];
|
||||
// index entries go here
|
||||
} _PACKED;
|
||||
|
||||
struct odml_chunk_index_header // also field index
|
||||
{
|
||||
uint16 longs_per_entry;
|
||||
uint8 index_sub_type;
|
||||
uint8 index_type;
|
||||
uint32 entries_used;
|
||||
uint32 chunk_id;
|
||||
uint64 base_offset;
|
||||
uint32 reserved;
|
||||
// index entries go here
|
||||
} _PACKED;
|
||||
typedef odml_chunk_index_header odml_field_index_header;
|
||||
|
||||
|
||||
|
||||
// index_type codes
|
||||
#define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex
|
||||
// array points to an index chunk
|
||||
#define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex array
|
||||
// points to a chunk in the file
|
||||
#define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is really the data
|
||||
|
||||
// index_sub_type codes for INDEX_OF_CHUNKS
|
||||
#define AVI_INDEX_2FIELD 0x01 // when fields within frames are also indexed
|
||||
|
||||
struct odml_superindex_entry
|
||||
{
|
||||
uint64 start;
|
||||
uint32 size;
|
||||
uint32 duration;
|
||||
};
|
||||
|
||||
struct odml_index_entry
|
||||
{
|
||||
uint32 start;
|
||||
uint32 size; // bit 31 set if not keyframe
|
||||
};
|
||||
|
||||
struct odml_field_index_entry
|
||||
{
|
||||
uint32 start;
|
||||
uint32 size; // bit 31 set if not keyframe
|
||||
uint32 start_field2;
|
||||
};
|
||||
|
||||
#endif // _AVI_H
|
||||
Reference in New Issue
Block a user