2004-03-17 19:51:30 +00:00
|
|
|
/*
|
|
|
|
* rmff.c
|
|
|
|
*
|
|
|
|
* Copyright (C) Moritz Bunkus - March 2004
|
|
|
|
*
|
|
|
|
* librmff is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2.1, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* librmff is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this library; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "librmff.h"
|
|
|
|
|
|
|
|
int rmff_last_error = RMFF_ERR_OK;
|
|
|
|
const char *rmff_last_error_msg = NULL;
|
|
|
|
static const char *rmff_std_error_messages[] = {
|
|
|
|
"No error",
|
|
|
|
"File is not a RealMedia file",
|
|
|
|
"Inconsistent data found in file",
|
|
|
|
"End of file reached",
|
|
|
|
"Input/output error",
|
|
|
|
"Invalid parameters"
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *
|
|
|
|
rmff_get_error_str(int code) {
|
|
|
|
code *= -1;
|
|
|
|
if ((code >= 0) && (code <= 5))
|
|
|
|
return rmff_std_error_messages[code];
|
|
|
|
else
|
|
|
|
return "Unknown error";
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_error(int error_number,
|
|
|
|
const char *error_msg,
|
|
|
|
int return_value) {
|
|
|
|
rmff_last_error = error_number;
|
|
|
|
if (error_msg == NULL)
|
|
|
|
rmff_last_error_msg = rmff_get_error_str(error_number);
|
|
|
|
else
|
|
|
|
rmff_last_error_msg = error_msg;
|
|
|
|
|
|
|
|
return return_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
get_uint16_be(const void *buf) {
|
|
|
|
uint16_t ret;
|
|
|
|
unsigned char *tmp;
|
|
|
|
|
|
|
|
tmp = (unsigned char *) buf;
|
|
|
|
|
|
|
|
ret = tmp[0] & 0xff;
|
|
|
|
ret = (ret << 8) + (tmp[1] & 0xff);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
get_uint32_be(const void *buf) {
|
|
|
|
uint32_t ret;
|
|
|
|
unsigned char *tmp;
|
|
|
|
|
|
|
|
tmp = (unsigned char *) buf;
|
|
|
|
|
|
|
|
ret = tmp[0] & 0xff;
|
|
|
|
ret = (ret << 8) + (tmp[1] & 0xff);
|
|
|
|
ret = (ret << 8) + (tmp[2] & 0xff);
|
|
|
|
ret = (ret << 8) + (tmp[3] & 0xff);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
put_uint16_be(void *buf,
|
|
|
|
uint16_t value) {
|
|
|
|
unsigned char *tmp;
|
|
|
|
|
|
|
|
tmp = (unsigned char *) buf;
|
|
|
|
|
|
|
|
tmp[1] = value & 0xff;
|
|
|
|
tmp[0] = (value >>= 8) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
put_uint32_be(void *buf,
|
|
|
|
uint32_t value) {
|
|
|
|
unsigned char *tmp;
|
|
|
|
|
|
|
|
tmp = (unsigned char *) buf;
|
|
|
|
|
|
|
|
tmp[3] = value & 0xff;
|
|
|
|
tmp[2] = (value >>= 8) & 0xff;
|
|
|
|
tmp[1] = (value >>= 8) & 0xff;
|
|
|
|
tmp[0] = (value >>= 8) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define read_uint8() file_read_uint8(io, fh)
|
|
|
|
#define read_uint16_be() file_read_uint16_be(io, fh)
|
|
|
|
#define read_uint32_be() file_read_uint32_be(io, fh)
|
|
|
|
#define read_uint16_be_to(addr) put_uint16_be(addr, read_uint16_be())
|
|
|
|
#define read_uint32_be_to(addr) put_uint32_be(addr, read_uint32_be())
|
|
|
|
#define get_fourcc(b) get_uint32_be(b)
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
file_read_uint8(mb_file_io_t *io,
|
|
|
|
void *fh) {
|
|
|
|
unsigned char tmp;
|
|
|
|
|
|
|
|
io->read(fh, &tmp, 1);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
file_read_uint16_be(mb_file_io_t *io,
|
|
|
|
void *fh) {
|
|
|
|
unsigned char tmp[2];
|
|
|
|
|
|
|
|
io->read(fh, tmp, 2);
|
|
|
|
return get_uint16_be(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
file_read_uint32_be(mb_file_io_t *io,
|
|
|
|
void *fh) {
|
|
|
|
unsigned char tmp[4];
|
|
|
|
|
|
|
|
io->read(fh, tmp, 4);
|
|
|
|
return get_uint32_be(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
die(const char *fmt,
|
|
|
|
...) {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
fprintf(stderr, "'die' called: ");
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define safestrdup(s) _safestrdup(s, __FILE__, __LINE__)
|
|
|
|
#define safememdup(s, b) _safememdup(s, b, __FILE__, __LINE__)
|
|
|
|
#define safemalloc(s) _safemalloc(s, __FILE__, __LINE__)
|
|
|
|
#define safecalloc(s) _safecalloc(s, __FILE__, __LINE__)
|
|
|
|
#define saferealloc(s, b) _saferealloc(s, b, __FILE__, __LINE__)
|
|
|
|
#define safefree(s) { if ((s) != NULL) free(s); }
|
|
|
|
|
|
|
|
static char *
|
|
|
|
_safestrdup(const char *s,
|
|
|
|
const char *file,
|
|
|
|
int line) {
|
|
|
|
char *copy;
|
|
|
|
|
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
copy = strdup(s);
|
|
|
|
if (copy == NULL)
|
|
|
|
die("safestrdup() called from file %s, line %d: strdup() "
|
|
|
|
"returned NULL for '%s'.", file, line, s);
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static void * */
|
|
|
|
/* _safememdup(const void *s, */
|
|
|
|
/* size_t size, */
|
|
|
|
/* const char *file, */
|
|
|
|
/* int line) { */
|
|
|
|
/* void *copy; */
|
|
|
|
|
|
|
|
/* if (s == NULL) */
|
|
|
|
/* return NULL; */
|
|
|
|
|
|
|
|
/* copy = malloc(size); */
|
|
|
|
/* if (copy == NULL) */
|
|
|
|
/* die("safememdup() called from file %s, line %d: malloc() " */
|
|
|
|
/* "returned NULL for a size of %d bytes.", file, line, size); */
|
|
|
|
/* memcpy(copy, s, size); */
|
|
|
|
|
|
|
|
/* return copy; */
|
|
|
|
/* } */
|
|
|
|
|
|
|
|
static void *
|
|
|
|
_safemalloc(size_t size,
|
|
|
|
const char *file,
|
|
|
|
int line) {
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
mem = malloc(size);
|
|
|
|
if (mem == NULL)
|
|
|
|
die("safemalloc() called from file %s, line %d: malloc() "
|
|
|
|
"returned NULL for a size of %d bytes.", file, line, size);
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *_safecalloc(size_t size,
|
|
|
|
const char *file,
|
|
|
|
int line) {
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
mem = calloc(size, 1);
|
|
|
|
if (mem == NULL)
|
|
|
|
die("safemalloc() called from file %s, line %d: malloc() "
|
|
|
|
"returned NULL for a size of %d bytes.", file, line, size);
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
_saferealloc(void *mem,
|
|
|
|
size_t size,
|
|
|
|
const char *file,
|
|
|
|
int line) {
|
|
|
|
mem = realloc(mem, size);
|
|
|
|
if (mem == NULL)
|
|
|
|
die("saferealloc() called from file %s, line %d: realloc() "
|
|
|
|
"returned NULL for a size of %d bytes.", file, line, size);
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
static rmff_file_t *
|
|
|
|
open_file_for_reading(const char *path,
|
|
|
|
mb_file_io_t *io) {
|
2004-03-17 19:51:30 +00:00
|
|
|
rmff_file_t *file;
|
|
|
|
void *file_h;
|
2004-03-18 19:01:35 +00:00
|
|
|
char signature[5];
|
2004-03-17 19:51:30 +00:00
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
file_h = io->open(path, MB_OPEN_MODE_READING);
|
2004-03-17 19:51:30 +00:00
|
|
|
if (file_h == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
signature[4] = 0;
|
|
|
|
if ((io->read(file_h, signature, 4) != 4) ||
|
|
|
|
strcmp(signature, ".RMF")) {
|
2004-03-17 19:51:30 +00:00
|
|
|
io->close(file_h);
|
|
|
|
return (rmff_file_t *)set_error(RMFF_ERR_NOT_RMFF, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
file = (rmff_file_t *)safecalloc(sizeof(rmff_file_t));
|
|
|
|
file->handle = file_h;
|
|
|
|
file->name = safestrdup(path);
|
|
|
|
file->io = io;
|
|
|
|
io->seek(file_h, 0, SEEK_END);
|
|
|
|
file->size = io->tell(file_h);
|
|
|
|
io->seek(file_h, 4, SEEK_SET);
|
2004-03-18 19:01:35 +00:00
|
|
|
file->open_mode = RMFF_OPEN_MODE_READING;
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
static rmff_file_t *
|
|
|
|
open_file_for_writing(const char *path,
|
|
|
|
mb_file_io_t *io) {
|
|
|
|
rmff_file_t *file;
|
|
|
|
void *file_h;
|
|
|
|
const char *signature = ".RMF";
|
|
|
|
|
|
|
|
file_h = io->open(path, MB_OPEN_MODE_WRITING);
|
|
|
|
if (file_h == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (io->write(file_h, signature, 4) != 4) {
|
|
|
|
io->close(file_h);
|
|
|
|
return (rmff_file_t *)set_error(RMFF_ERR_IO, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
file = (rmff_file_t *)safecalloc(sizeof(rmff_file_t));
|
|
|
|
file->handle = file_h;
|
|
|
|
file->name = safestrdup(path);
|
|
|
|
file->io = io;
|
|
|
|
file->size = -1;
|
|
|
|
file->open_mode = RMFF_OPEN_MODE_WRITING;
|
2004-03-17 19:51:30 +00:00
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
rmff_file_t *
|
|
|
|
rmff_open_file(const char *path,
|
|
|
|
int mode) {
|
|
|
|
return rmff_open_file_with_io(path, mode, &std_mb_file_io);
|
|
|
|
}
|
|
|
|
|
|
|
|
rmff_file_t *
|
|
|
|
rmff_open_file_with_io(const char *path,
|
|
|
|
int mode,
|
|
|
|
mb_file_io_t *io) {
|
|
|
|
if ((path == NULL) || (io == NULL) ||
|
|
|
|
((mode != RMFF_OPEN_MODE_READING) && (mode != RMFF_OPEN_MODE_WRITING)))
|
|
|
|
return (rmff_file_t *)set_error(RMFF_ERR_PARAMETERS, NULL, 0);
|
|
|
|
|
|
|
|
if (mode == RMFF_OPEN_MODE_READING)
|
|
|
|
return open_file_for_reading(path, io);
|
|
|
|
else
|
|
|
|
return open_file_for_writing(path, io);
|
|
|
|
}
|
|
|
|
|
2004-03-17 19:51:30 +00:00
|
|
|
void
|
|
|
|
rmff_free_track_data(rmff_track_t *track) {
|
|
|
|
if (track == NULL)
|
|
|
|
return;
|
|
|
|
safefree(track->mdpr_header.name);
|
|
|
|
safefree(track->mdpr_header.mime_type);
|
|
|
|
safefree(track->mdpr_header.type_specific_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rmff_close_file(rmff_file_t *file) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (file == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
safefree(file->name);
|
|
|
|
for (i = 0; i < file->num_tracks; i++)
|
|
|
|
rmff_free_track_data(&file->tracks[i]);
|
|
|
|
safefree(file->tracks);
|
|
|
|
safefree(file->cont_header.title);
|
|
|
|
safefree(file->cont_header.author);
|
|
|
|
safefree(file->cont_header.copyright);
|
|
|
|
safefree(file->cont_header.comment);
|
|
|
|
file->io->close(file->handle);
|
|
|
|
safefree(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define skip(num) { if (io->seek(fh, num, SEEK_CUR) != 0) \
|
|
|
|
return set_error(RMFF_ERR_IO, NULL, -1); }
|
|
|
|
|
|
|
|
int
|
|
|
|
rmff_read_headers(rmff_file_t *file) {
|
|
|
|
mb_file_io_t *io;
|
|
|
|
void *fh;
|
|
|
|
uint32_t object_id, object_size, size;
|
|
|
|
uint16_t object_version;
|
|
|
|
rmff_prop_t *prop;
|
|
|
|
rmff_cont_t *cont;
|
|
|
|
rmff_mdpr_t *mdpr;
|
|
|
|
rmff_track_t track;
|
|
|
|
real_video_props_t *rvp;
|
|
|
|
real_audio_v4_props_t *ra4p;
|
2004-03-18 19:01:35 +00:00
|
|
|
int prop_header_found;
|
2004-03-17 19:51:30 +00:00
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
if ((file == NULL) || (file->open_mode != RMFF_OPEN_MODE_READING))
|
2004-03-17 19:51:30 +00:00
|
|
|
return set_error(RMFF_ERR_PARAMETERS, NULL, RMFF_ERR_PARAMETERS);
|
|
|
|
if (file->headers_read)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
io = file->io;
|
|
|
|
fh = file->handle;
|
|
|
|
if (io->seek(fh, 4, SEEK_SET))
|
|
|
|
return set_error(RMFF_ERR_IO, NULL, -1);
|
|
|
|
|
|
|
|
skip(4); /* header_size */
|
|
|
|
skip(2); /* object_version */
|
|
|
|
skip(4); /* file_version */
|
|
|
|
skip(4); /* num_headers */
|
|
|
|
|
|
|
|
prop = &file->prop_header;
|
2004-03-18 19:01:35 +00:00
|
|
|
prop_header_found = 0;
|
2004-03-17 19:51:30 +00:00
|
|
|
cont = &file->cont_header;
|
|
|
|
while (1) {
|
|
|
|
rmff_last_error = RMFF_ERR_OK;
|
|
|
|
object_id = read_uint32_be();
|
|
|
|
object_size = read_uint32_be();
|
|
|
|
object_version = read_uint16_be();
|
|
|
|
if (rmff_last_error != RMFF_ERR_OK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (object_id == rmffFOURCC('P', 'R', 'O', 'P')) {
|
|
|
|
read_uint32_be_to(&prop->max_bit_rate);
|
|
|
|
read_uint32_be_to(&prop->avg_bit_rate);
|
|
|
|
read_uint32_be_to(&prop->max_packet_size);
|
|
|
|
read_uint32_be_to(&prop->avg_packet_size);
|
|
|
|
read_uint32_be_to(&prop->num_packets);
|
|
|
|
read_uint32_be_to(&prop->duration);
|
|
|
|
read_uint32_be_to(&prop->preroll);
|
|
|
|
read_uint32_be_to(&prop->index_offset);
|
|
|
|
read_uint32_be_to(&prop->data_offset);
|
|
|
|
read_uint16_be_to(&prop->num_streams);
|
|
|
|
read_uint16_be_to(&prop->flags);
|
2004-03-18 19:01:35 +00:00
|
|
|
prop_header_found = 1;
|
2004-03-17 19:51:30 +00:00
|
|
|
|
|
|
|
} else if (object_id == rmffFOURCC('C', 'O', 'N', 'T')) {
|
2004-03-18 19:01:35 +00:00
|
|
|
if (file->cont_header_present) {
|
2004-03-17 19:51:30 +00:00
|
|
|
safefree(cont->title);
|
|
|
|
safefree(cont->author);
|
|
|
|
safefree(cont->copyright);
|
|
|
|
safefree(cont->comment);
|
|
|
|
}
|
|
|
|
memset(cont, 0, sizeof(rmff_cont_t));
|
|
|
|
|
|
|
|
size = read_uint16_be(); /* title_len */
|
|
|
|
if (size > 0) {
|
|
|
|
cont->title = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, cont->title, size);
|
|
|
|
}
|
|
|
|
size = read_uint16_be(); /* author_len */
|
|
|
|
if (size > 0) {
|
|
|
|
cont->author = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, cont->author, size);
|
|
|
|
}
|
|
|
|
size = read_uint16_be(); /* copyright_len */
|
|
|
|
if (size > 0) {
|
|
|
|
cont->copyright = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, cont->copyright, size);
|
|
|
|
}
|
|
|
|
size = read_uint16_be(); /* comment_len */
|
|
|
|
if (size > 0) {
|
|
|
|
cont->comment = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, cont->comment, size);
|
|
|
|
}
|
2004-03-18 19:01:35 +00:00
|
|
|
file->cont_header_present = 1;
|
2004-03-17 19:51:30 +00:00
|
|
|
|
|
|
|
} else if (object_id == rmffFOURCC('M', 'D', 'P', 'R')) {
|
|
|
|
memset(&track, 0, sizeof(rmff_track_t));
|
|
|
|
track.file = (struct rmff_file_t *)file;
|
|
|
|
mdpr = &track.mdpr_header;
|
|
|
|
read_uint16_be_to(&mdpr->id);
|
2004-03-17 21:47:28 +00:00
|
|
|
track.id = get_uint16_be(&mdpr->id);
|
2004-03-17 19:51:30 +00:00
|
|
|
read_uint32_be_to(&mdpr->max_bit_rate);
|
|
|
|
read_uint32_be_to(&mdpr->avg_bit_rate);
|
|
|
|
read_uint32_be_to(&mdpr->max_packet_size);
|
|
|
|
read_uint32_be_to(&mdpr->avg_packet_size);
|
|
|
|
read_uint32_be_to(&mdpr->start_time);
|
|
|
|
read_uint32_be_to(&mdpr->preroll);
|
|
|
|
read_uint32_be_to(&mdpr->duration);
|
|
|
|
size = read_uint8(); /* stream_name_len */
|
|
|
|
if (size > 0) {
|
|
|
|
mdpr->name = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, mdpr->name, size);
|
|
|
|
}
|
|
|
|
size = read_uint8(); /* mime_type_len */
|
|
|
|
if (size > 0) {
|
|
|
|
mdpr->mime_type = (char *)safemalloc(size + 1);
|
|
|
|
io->read(fh, mdpr->mime_type, size);
|
|
|
|
}
|
|
|
|
size = read_uint32_be(); /* type_specific_size */
|
|
|
|
mdpr->type_specific_size = size;
|
|
|
|
if (size > 0) {
|
|
|
|
mdpr->type_specific_data = (unsigned char *)safemalloc(size);
|
|
|
|
io->read(fh, mdpr->type_specific_data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
rvp = (real_video_props_t *)mdpr->type_specific_data;
|
|
|
|
ra4p = (real_audio_v4_props_t *)mdpr->type_specific_data;
|
|
|
|
if ((size >= sizeof(real_video_props_t)) &&
|
|
|
|
(get_fourcc(&rvp->fourcc1) == rmffFOURCC('V', 'I', 'D', 'O')))
|
|
|
|
track.type = RMFF_TRACK_TYPE_VIDEO;
|
|
|
|
else if ((size >= sizeof(real_audio_v4_props_t)) &&
|
|
|
|
(get_fourcc(&ra4p->fourcc1) ==
|
|
|
|
rmffFOURCC('.', 'r', 'a', 0xfd))) {
|
|
|
|
track.type = RMFF_TRACK_TYPE_AUDIO;
|
|
|
|
if ((get_uint16_be(&ra4p->version1) == 5) &&
|
|
|
|
(size < sizeof(real_audio_v5_props_t)))
|
|
|
|
return set_error(RMFF_ERR_DATA, "RealAudio v5 data indicated but "
|
|
|
|
"data too small", RMFF_ERR_DATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
file->tracks =
|
|
|
|
(rmff_track_t *)saferealloc(file->tracks, (file->num_tracks + 1) *
|
|
|
|
sizeof(rmff_track_t));
|
|
|
|
file->tracks[file->num_tracks] = track;
|
|
|
|
file->num_tracks++;
|
|
|
|
|
|
|
|
} else if (object_id == rmffFOURCC('D', 'A', 'T', 'A')) {
|
|
|
|
file->num_packets_in_chunk = read_uint32_be();
|
|
|
|
file->next_data_header_offset = read_uint32_be();
|
|
|
|
file->first_data_header_offset = io->tell(fh) - (4 + 4 + 2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Unknown header type */
|
|
|
|
return set_error(RMFF_ERR_DATA, NULL, RMFF_ERR_DATA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-18 19:01:35 +00:00
|
|
|
if (prop_header_found && (file->first_data_header_offset > 0)) {
|
2004-03-17 19:51:30 +00:00
|
|
|
file->headers_read = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return set_error(RMFF_ERR_DATA, NULL, RMFF_ERR_DATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rmff_get_next_frame_size(rmff_file_t *file) {
|
|
|
|
uint16_t object_version, length;
|
|
|
|
uint32_t object_id;
|
|
|
|
mb_file_io_t *io;
|
|
|
|
void *fh;
|
|
|
|
int result;
|
|
|
|
int64_t old_pos;
|
|
|
|
|
|
|
|
if ((file == NULL) || (!file->headers_read) || (file->io == NULL) ||
|
2004-03-18 19:01:35 +00:00
|
|
|
(file->handle == NULL) || (file->open_mode != RMFF_OPEN_MODE_READING))
|
2004-03-17 19:51:30 +00:00
|
|
|
return set_error(RMFF_ERR_PARAMETERS, NULL, RMFF_ERR_PARAMETERS);
|
|
|
|
io = file->io;
|
|
|
|
fh = file->handle;
|
|
|
|
old_pos = io->tell(fh);
|
|
|
|
if ((file->size - old_pos) < 12)
|
|
|
|
return set_error(RMFF_ERR_EOF, NULL, RMFF_ERR_EOF);
|
|
|
|
|
|
|
|
object_version = read_uint16_be();
|
|
|
|
length = read_uint16_be();
|
|
|
|
object_id = ((uint32_t)object_version) << 16 | length;
|
|
|
|
if (object_id == rmffFOURCC('D', 'A', 'T', 'A')) {
|
|
|
|
skip(4 + 4); /* packets_in_chunk, next_data_header */
|
|
|
|
result = rmff_get_next_frame_size(file);
|
|
|
|
io->seek(fh, old_pos, SEEK_SET);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
io->seek(fh, old_pos, SEEK_SET);
|
|
|
|
if (object_id == rmffFOURCC('I', 'N', 'D', 'X'))
|
|
|
|
return set_error(RMFF_ERR_EOF, NULL, RMFF_ERR_EOF);
|
|
|
|
return length - 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
rmff_frame_t *
|
|
|
|
rmff_read_next_frame(rmff_file_t *file,
|
|
|
|
void *buffer) {
|
|
|
|
rmff_frame_t *frame;
|
|
|
|
uint16_t object_version, length;
|
|
|
|
uint32_t object_id;
|
|
|
|
mb_file_io_t *io;
|
|
|
|
void *fh;
|
|
|
|
|
|
|
|
if ((file == NULL) || (!file->headers_read) || (file->io == NULL) ||
|
2004-03-18 19:01:35 +00:00
|
|
|
(file->handle == NULL) || (file->open_mode != RMFF_OPEN_MODE_READING))
|
2004-03-17 19:51:30 +00:00
|
|
|
return (rmff_frame_t *)set_error(RMFF_ERR_PARAMETERS, NULL, 0);
|
|
|
|
io = file->io;
|
|
|
|
fh = file->handle;
|
|
|
|
if ((file->size - io->tell(fh)) < 12)
|
|
|
|
return (rmff_frame_t *)set_error(RMFF_ERR_EOF, NULL, 0);
|
|
|
|
|
|
|
|
object_version = read_uint16_be();
|
|
|
|
length = read_uint16_be();
|
|
|
|
object_id = ((uint32_t)object_version) << 16 | length;
|
|
|
|
if (object_id == rmffFOURCC('D', 'A', 'T', 'A')) {
|
|
|
|
file->num_packets_in_chunk = read_uint32_be();
|
|
|
|
file->next_data_header_offset = read_uint32_be();
|
|
|
|
file->num_packets_read = 0;
|
|
|
|
return rmff_read_next_frame(file, buffer);
|
|
|
|
}
|
|
|
|
if ((file->num_packets_read >= file->num_packets_in_chunk) ||
|
|
|
|
(object_id == rmffFOURCC('I', 'N', 'D', 'X')))
|
|
|
|
return (rmff_frame_t *)set_error(RMFF_ERR_EOF, NULL, 0);
|
|
|
|
|
|
|
|
frame = (rmff_frame_t *)safecalloc(sizeof(rmff_frame_t));
|
|
|
|
if (buffer == NULL) {
|
|
|
|
buffer = safemalloc(length);
|
|
|
|
frame->allocated_by_rmff = 1;
|
|
|
|
}
|
|
|
|
frame->data = buffer;
|
|
|
|
frame->size = length - 12;
|
|
|
|
frame->id = read_uint16_be();
|
|
|
|
frame->timecode = read_uint32_be();
|
|
|
|
frame->reserved = read_uint8();
|
|
|
|
frame->flags = read_uint8();
|
|
|
|
if (io->read(fh, frame->data, frame->size) != frame->size) {
|
|
|
|
rmff_release_frame(frame);
|
|
|
|
return (rmff_frame_t *)set_error(RMFF_ERR_EOF, NULL, 0);
|
|
|
|
}
|
|
|
|
file->num_packets_read++;
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rmff_release_frame(rmff_frame_t *frame) {
|
|
|
|
if (frame == NULL)
|
|
|
|
return;
|
|
|
|
if (frame->allocated_by_rmff)
|
|
|
|
safefree(frame->data);
|
|
|
|
safefree(frame);
|
|
|
|
}
|
2004-03-18 19:01:35 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
rmff_set_cont_header(rmff_file_t *file,
|
|
|
|
const char *title,
|
|
|
|
const char *author,
|
|
|
|
const char *copyright,
|
|
|
|
const char *comment) {
|
|
|
|
if (file == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
safefree(file->cont_header.title);
|
|
|
|
safefree(file->cont_header.author);
|
|
|
|
safefree(file->cont_header.copyright);
|
|
|
|
safefree(file->cont_header.comment);
|
|
|
|
file->cont_header.title = safestrdup(title);
|
|
|
|
file->cont_header.author = safestrdup(author);
|
|
|
|
file->cont_header.copyright = safestrdup(copyright);
|
|
|
|
file->cont_header.comment = safestrdup(comment);
|
|
|
|
}
|