Merged 1572:1575.

This commit is contained in:
Moritz Bunkus 2004-03-18 20:47:24 +00:00
parent 484b0c2fd9
commit 6d7ae72724
2 changed files with 303 additions and 45 deletions

View File

@ -24,6 +24,82 @@
* \author Moritz Bunkus <moritz@bunkus.org> * \author Moritz Bunkus <moritz@bunkus.org>
*/ */
/** \mainpage
*
* \section Introduction
*
* \a librmff is short for 'RealMedia file format access library'. It aims
* at providing the programmer an easy way to read and write RealMedia
* files. It does not contain any codecs for audio/video handling.
*
* \section License
*
* The library was written by Moritz Bunkus <moritz@bunkus.org>. It is
* licensed under the terms of the GNU Lesser General Public License (GNU
* LGPL) which can be found in the file COPYING.
*
* \section Usage
*
* Here are very short samples of how to use the library.
*
* \subsection reading_existing Reading an existing file
*
* Reading an existing file requires four steps: Opening the file, reading
* the headers, reading all the frames and closing the file.
*
* \code
rmff_file_t *file;
rmff_frame_t *frame;
file = rmff_open_file("sample_file.rm", RMFF_OPEN_MODE_READING);
if (file == NULL) {
// Output some general information about the tracks in the file.
// Now read all the frames.
while ((frame = rmff_read_next_frame(file, NULL)) != NULL) {
// Do something with the frame and release it afterwards.
rmff_release_frame(frame);
}
rmff_close_file(file);
}
\endcode
*
* \section memory_handling Memory handling
*
* Generally \a librmff allocates and frees memory itself. You should
* \b never mess with pointers inside the structures directly but use
* the provided functions for manipulating it. There's one exception to
* this rule: the frame handling.
*
* The functions rmff_read_next_frame(rmff_file_t*,void*),
* rmff_release_frame(rmff_frame_t*) and
* rmff_allocate_frame(uint32_t,void*) allow the application to provide its
* own buffers for storing the frame contents. \a librmff will not copy
* this memory further. It will read directly into the buffer or write directly
* from the buffer into the file.
*
* Example: \code
rmff_frame_t *frame;
unsigned char *buffer,
int size;
while (1) {
// Get the next frame's size.
size = rmff_get_next_frame_size(file);
// Have we reached the end of the file?
if (size == -1)
break;
// Allocate enough space for this frame and let librmff read directly
// into it.
buffer = (unsigned char *)malloc(size);
frame = rmff_read_next_frame(file, buffer);
// Now do something with the buffer. Afterwards release the frame.
rmff_release_frame(frame);
// The buffer is still allocated. So free it now.
free(buffer);
}
\endcode
*/
#ifndef __RMFF_H #ifndef __RMFF_H
#define __RMFF_H #define __RMFF_H
@ -36,9 +112,14 @@ extern "C" {
#include "mb_file_io.h" #include "mb_file_io.h"
/** \brief Contains the PROP file header. /** \brief The global PROP file header.
*
* This header is mandatory for a RealMedia file. It contains statistical * This header is mandatory for a RealMedia file. It contains statistical
* and global data. Each member is in big endian. * and global data. The values are stored in big endian byte order.
* The application should use the functions
* rmff_get_uint16_be(const void*), rmff_get_uint32_be(const void*),
* rmff_put_uint16_be(void*,uint16_t) and
* rmff_put_uint32_be(void*,uint32_t) for accessing the members.
*/ */
typedef struct { typedef struct {
uint32_t max_bit_rate; uint32_t max_bit_rate;
@ -55,9 +136,10 @@ typedef struct {
} rmff_prop_t; } rmff_prop_t;
/** \brief Comments about the file in question. /** \brief Comments about the file in question.
*
* This structure contains the parsed values of the CONT header. These * This structure contains the parsed values of the CONT header. These
* strings must not be modified by the application. The function * strings must not be modified by the application. The function
* rmff_set_cont_header(const_char*,const_char*,const_char*,const_char*) * rmff_set_cont_header(rmff_file_t*,const char*,const char*,const char*,const char*)
* must be used instead. * must be used instead.
*/ */
typedef struct { typedef struct {
@ -67,18 +149,59 @@ typedef struct {
char *comment; char *comment;
} rmff_cont_t; } rmff_cont_t;
/** \brief The MDPR track headers.
*
* Each track in a RealMedia file contains the MDPR header. The values
* are stored in big endian byte order.
* The application should use the functions
* rmff_get_uint16_be(const void*), rmff_get_uint32_be(const void*),
* rmff_put_uint16_be(void*,uint16_t) and
* rmff_put_uint32_be(void*,uint32_t) for accessing the members.
*/
typedef struct { typedef struct {
/** \brief The track number. It is unique regarding the file. */
uint16_t id; uint16_t id;
/** \brief The maximum bitrate in bit/second.
*
* When creating a file this value will
* be updated automatically by the library. */
uint32_t max_bit_rate; uint32_t max_bit_rate;
/** \brief The average bitrate in bit/second.
*
* When creating a file this value will
* be updated automatically by the library. */
uint32_t avg_bit_rate; uint32_t avg_bit_rate;
/** \brief The maximum packet size in bytes.
*
* When creating a file this value will
* be updated automatically by the library. */
uint32_t max_packet_size; uint32_t max_packet_size;
/** \brief The average packet size in bytes.
*
* When creating a file this value will
* be updated automatically by the library. */
uint32_t avg_packet_size; uint32_t avg_packet_size;
uint32_t start_time; uint32_t start_time;
uint32_t preroll; uint32_t preroll;
uint32_t duration; uint32_t duration;
/** \brief The track's name.
*
* Use the rmff_set_track_data(rmff_track_t*,const char*,const char*)
* function for setting it. */
char *name; char *name;
/** \brief The track's MIME type.
*
* Use the rmff_set_track_data(rmff_track_t*,const char*,const char*)
* function for setting it. */
char *mime_type; char *mime_type;
/** \brief The size of the track specific data in bytes. */
uint32_t type_specific_size; uint32_t type_specific_size;
/** \brief Track type specific data.
*
* Use the
* rmff_set_track_specific_data(rmff_track_t*,const unsigned char*,uint32_t)
* function for setting it. It usually contains a ::real_video_props_t or
* ::real_audio_v4_props_t structure. */
unsigned char *type_specific_data; unsigned char *type_specific_data;
} rmff_mdpr_t; } rmff_mdpr_t;
@ -162,7 +285,7 @@ typedef struct rmff_frame_t {
/** \brief The track contains video data. */ /** \brief The track contains video data. */
#define RMFF_TRACK_TYPE_VIDEO 2 #define RMFF_TRACK_TYPE_VIDEO 2
struct rmff_file_t ; struct rmff_file_t;
typedef struct rmff_track_t { typedef struct rmff_track_t {
uint32_t id; uint32_t id;
@ -210,15 +333,18 @@ typedef struct rmff_file_t {
/** \brief An error has occured for which \c errno should be consulted. */ /** \brief An error has occured for which \c errno should be consulted. */
#define RMFF_ERR_CHECK_ERRNO -6 #define RMFF_ERR_CHECK_ERRNO -6
#define rmffFOURCC(a, b, c, d) (uint32_t)((((unsigned char)a) << 24) + \ /** \brief Convert the four bytes into a 'FOURCC' uint32. */
(((unsigned char)b) << 16) + \ #define rmffFOURCC(a, b, c, d) \
(((unsigned char)c) << 8) + \ (uint32_t)((((unsigned char)a) << 24) + \
((unsigned char)d)) (((unsigned char)b) << 16) + \
(((unsigned char)c) << 8) + \
((unsigned char)d))
#define RMFF_OPEN_MODE_READING 0 #define RMFF_OPEN_MODE_READING 0
#define RMFF_OPEN_MODE_WRITING 1 #define RMFF_OPEN_MODE_WRITING 1
/** \brief Opens a RealMedia file for reading or writing. /** \brief Opens a RealMedia file for reading or writing.
*
* Can be used to open an existing file for reading or for creating a new * Can be used to open an existing file for reading or for creating a new
* file. The file headers will neither be read nor written automatically. * file. The file headers will neither be read nor written automatically.
* This function uses the standard file I/O functions provided by the * This function uses the standard file I/O functions provided by the
@ -226,16 +352,17 @@ typedef struct rmff_file_t {
* *
* \param path the name of the file that should be opened * \param path the name of the file that should be opened
* \param mode either ::RMFF_OPEN_MODE_READING or ::RMFF_OPEN_MODE_WRITING * \param mode either ::RMFF_OPEN_MODE_READING or ::RMFF_OPEN_MODE_WRITING
* \returns a pointer to \c rmff_file_t structure or \c NULL if an error * \returns a pointer to ::rmff_file_t structure or \c NULL if an error
* occured. In the latter case ::rmff_last_error will be set. * occured. In the latter case ::rmff_last_error will be set.
* \see rmff_open_file_with_io * \see rmff_open_file_with_io
*/ */
rmff_file_t *rmff_open_file(const char *path, int mode); rmff_file_t *rmff_open_file(const char *path, int mode);
/** \brief Opens a RealMedia file for reading or writing. /** \brief Opens a RealMedia file for reading or writing.
*
* Can be used to open an existing file for reading or for creating a new * Can be used to open an existing file for reading or for creating a new
* file. The file headers will neither be read nor written automatically. * file. The file headers will neither be read nor written automatically.
* This function uses I/O functions provided by the \c io parameter. * This function uses I/O functions provided by the \a io parameter.
* *
* \param path the name of the file that should be opened * \param path the name of the file that should be opened
* \param mode either ::RMFF_OPEN_MODE_READING or ::RMFF_OPEN_MODE_WRITING * \param mode either ::RMFF_OPEN_MODE_READING or ::RMFF_OPEN_MODE_WRITING
@ -248,8 +375,9 @@ rmff_file_t *rmff_open_file_with_io(const char *path, int mode,
mb_file_io_t *io); mb_file_io_t *io);
/** \brief Close the file and release all resources. /** \brief Close the file and release all resources.
*
* Closes the file and releases all resources associated with it, including * Closes the file and releases all resources associated with it, including
* the \c file structure. If the file was open for writing then * the ::rmff_file_t structure. If the file was open for writing then
* rmff_write_headers() should be called prior to closing the file as * rmff_write_headers() should be called prior to closing the file as
* \c rmff_close_file does not fix the headers itself. * \c rmff_close_file does not fix the headers itself.
* *
@ -258,6 +386,7 @@ rmff_file_t *rmff_open_file_with_io(const char *path, int mode,
void rmff_close_file(rmff_file_t *file); void rmff_close_file(rmff_file_t *file);
/** \brief Reads the file and track headers. /** \brief Reads the file and track headers.
*
* This function should be called after directly after opening it for reading. * This function should be called after directly after opening it for reading.
* It will try to read the file and track headers and position the file pointer * It will try to read the file and track headers and position the file pointer
* right before the first data packet. * right before the first data packet.
@ -277,30 +406,47 @@ int rmff_read_headers(rmff_file_t *file);
int rmff_get_next_frame_size(rmff_file_t *file); int rmff_get_next_frame_size(rmff_file_t *file);
/** \brief Reads the next frame from the file. /** \brief Reads the next frame from the file.
*
* The frame must be released by rmff_release_frame(rmff_frame_t*). * The frame must be released by rmff_release_frame(rmff_frame_t*).
* *
* \param file The file to read from. * \param file The file to read from.
* \param buffer A buffer to read the frame into. This parameter may be * \param buffer A buffer to read the frame into. This parameter may be
* \c NULL in which case the buffer will be allocated by the library. * \c NULL in which case the buffer will be allocated by the library.
* If the application provides the buffer it must be large enough. * If the application provides the buffer it must be large enough.
* The function rmff_get_next_frame_size() can be used in this case. * The function rmff_get_next_frame_size(rmff_file_t*) can be used in this
* case.
* \returns a pointer to a ::rmff_frame_t structure containing the frame * \returns a pointer to a ::rmff_frame_t structure containing the frame
* and its metadata on success or \c NULL if the call failed. This frame * and its metadata on success or \c NULL if the call failed. This frame
* must be freed with rmff_release_frame(rmff_frame_t*). * must be freed with rmff_release_frame(rmff_frame_t*).
*/ */
rmff_frame_t *rmff_read_next_frame(rmff_file_t *file, void *buffer); rmff_frame_t *rmff_read_next_frame(rmff_file_t *file, void *buffer);
/** \brief Allocates a frame and possibly a buffer for its contents.
*
* \param size The size of this frame.
* \param buffer A buffer that holds the frame. This parameter may be
* \c NULL in which case the buffer will be allocated by the library.
* If the application provides the buffer it must be large enough.
* \returns a pointer to an empty ::rmff_frame_t structure which can be filled
* with the frame contents and its metadata. This frame
* must be freed with rmff_release_frame(rmff_frame_t*).
*/
rmff_frame_t *rmff_allocate_frame(uint32_t size, void *buffer);
/** \brief Frees all resources associated with a frame. /** \brief Frees all resources associated with a frame.
*
* If the frame buffer was allocated by the library it will be freed as well. * If the frame buffer was allocated by the library it will be freed as well.
* *
* \param frame The frame to free. * \param frame The frame to free.
*/ */
void rmff_release_frame(rmff_frame_t *frame); void rmff_release_frame(rmff_frame_t *frame);
/** \brief Sets the contents of the CONT file header. /** \brief Sets the contents of the \link ::rmff_cont_t CONT file header
* \endlink.
*
* Frees the old contents if any and allocates copies of the given * Frees the old contents if any and allocates copies of the given
* strings. If the CONT header should be written to the file * strings. If the CONT header should be written to the file
* in rmff_write_headers(rmff_file_t*) then the \c cont_header_found * in rmff_write_headers(rmff_file_t*) then the \a cont_header_present
* member must be set to 1. * member must be set to 1.
* *
* \param file The file whose CONT header should be set. * \param file The file whose CONT header should be set.
@ -313,9 +459,38 @@ void rmff_set_cont_header(rmff_file_t *file, const char *title,
const char *author, const char *copyright, const char *author, const char *copyright,
const char *comment); const char *comment);
/** \brief Sets the strings in the \link ::rmff_mdpr_t MDPR track header
* \endlink structure.
*
* Frees the old contents and allocates copies of the given strings.
*
* \param track The track whose MDPR header should be set.
* \param name The track's name.
* \param mime_type The MIME type. A video track should have the MIME type
* \c video/x-pn-realvideo, and an audio track should have the MIMT type
* \c audio/x-pn-realaudio.
*/
void rmff_set_track_data(rmff_track_t *track, const char *name,
const char *mime_type);
/** \brief Sets the \a track_specific_data member of the \link ::rmff_mdpr_t
* MDPR header\endlink structure.
*
* The \a track_specific_data usually contains a
* ::real_video_props_t structure or a ::real_audio_props_t structure.
* The existing data, if any, will be freed, and a copy of the memory
* \a data points to will be made.
*
* \param track The track whose MDPR header should be set.
* \param data A pointer to the track specific data.
* \param size The track specific data's size in bytes.
*/
void rmff_set_track_specific_data(rmff_track_t *track,
const unsigned char *data, uint32_t size);
/** \brief The error code of the last function call. /** \brief The error code of the last function call.
* Contains the last error code for a function that failed. If a function * Contains the last error code for a function that failed. If a function
* succeeded it usually does not reset \c rmff_last_error to \c RMFF_ERR_OK. * succeeded it usually does not reset \a rmff_last_error to \c RMFF_ERR_OK.
* This variable can contain one of the \c RMFF_ERR_* constants. * This variable can contain one of the \c RMFF_ERR_* constants.
*/ */
extern int rmff_last_error; extern int rmff_last_error;
@ -334,6 +509,42 @@ extern const char *rmff_last_error_msg;
*/ */
const char *rmff_get_error_str(int code); const char *rmff_get_error_str(int code);
/** \brief Reads a 16bit uint from an address.
* The uint is converted from big endian byte order to the machine's byte
* order.
*
* \param buf The address to read from.
* \returns The 16bit uint converted to the machine's byte order.
*/
uint16_t rmff_get_uint16_be(const void *buf);
/** \brief Reads a 32bit uint from an address.
* The uint is converted from big endian byte order to the machine's byte
* order.
*
* \param buf The address to read from.
* \returns The 32bit uint converted to the machine's byte order.
*/
uint32_t rmff_get_uint32_be(const void *buf);
/** \brief Write a 16bit uint at an address.
* The value is converted from the machine's byte order to big endian byte
* order.
*
* \param buf The address to write to.
* \param value The value to write.
*/
void rmff_put_uint16_be(void *buf, uint16_t value);
/** \brief Write a 32bit uint at an address.
* The value is converted from the machine's byte order to big endian byte
* order.
*
* \param buf The address to write to.
* \param value The value to write.
*/
void rmff_put_uint32_be(void *buf, uint32_t value);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
#endif #endif

View File

@ -60,8 +60,8 @@ set_error(int error_number,
return return_value; return return_value;
} }
static uint16_t uint16_t
get_uint16_be(const void *buf) { rmff_get_uint16_be(const void *buf) {
uint16_t ret; uint16_t ret;
unsigned char *tmp; unsigned char *tmp;
@ -73,8 +73,8 @@ get_uint16_be(const void *buf) {
return ret; return ret;
} }
static uint32_t uint32_t
get_uint32_be(const void *buf) { rmff_get_uint32_be(const void *buf) {
uint32_t ret; uint32_t ret;
unsigned char *tmp; unsigned char *tmp;
@ -88,8 +88,8 @@ get_uint32_be(const void *buf) {
return ret; return ret;
} }
static void void
put_uint16_be(void *buf, rmff_put_uin16_be(void *buf,
uint16_t value) { uint16_t value) {
unsigned char *tmp; unsigned char *tmp;
@ -99,8 +99,8 @@ put_uint16_be(void *buf,
tmp[0] = (value >>= 8) & 0xff; tmp[0] = (value >>= 8) & 0xff;
} }
static void void
put_uint32_be(void *buf, rmff_put_uin32_be(void *buf,
uint32_t value) { uint32_t value) {
unsigned char *tmp; unsigned char *tmp;
@ -115,9 +115,9 @@ put_uint32_be(void *buf,
#define read_uint8() file_read_uint8(io, fh) #define read_uint8() file_read_uint8(io, fh)
#define read_uint16_be() file_read_uint16_be(io, fh) #define read_uint16_be() file_read_uint16_be(io, fh)
#define read_uint32_be() file_read_uint32_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_uint16_be_to(addr) rmff_put_uin16_be(addr, read_uint16_be())
#define read_uint32_be_to(addr) put_uint32_be(addr, read_uint32_be()) #define read_uint32_be_to(addr) rmff_put_uin32_be(addr, read_uint32_be())
#define get_fourcc(b) get_uint32_be(b) #define get_fourcc(b) rmff_get_uint32_be(b)
static uint8_t static uint8_t
file_read_uint8(mb_file_io_t *io, file_read_uint8(mb_file_io_t *io,
@ -134,7 +134,7 @@ file_read_uint16_be(mb_file_io_t *io,
unsigned char tmp[2]; unsigned char tmp[2];
io->read(fh, tmp, 2); io->read(fh, tmp, 2);
return get_uint16_be(tmp); return rmff_get_uint16_be(tmp);
} }
static uint32_t static uint32_t
@ -143,7 +143,7 @@ file_read_uint32_be(mb_file_io_t *io,
unsigned char tmp[4]; unsigned char tmp[4];
io->read(fh, tmp, 4); io->read(fh, tmp, 4);
return get_uint32_be(tmp); return rmff_get_uint32_be(tmp);
} }
void void
@ -183,24 +183,24 @@ _safestrdup(const char *s,
return copy; return copy;
} }
/* static void * */ static void *
/* _safememdup(const void *s, */ _safememdup(const void *s,
/* size_t size, */ size_t size,
/* const char *file, */ const char *file,
/* int line) { */ int line) {
/* void *copy; */ void *copy;
/* if (s == NULL) */ if (s == NULL)
/* return NULL; */ return NULL;
/* copy = malloc(size); */ copy = malloc(size);
/* if (copy == NULL) */ if (copy == NULL)
/* die("safememdup() called from file %s, line %d: malloc() " */ die("safememdup() called from file %s, line %d: malloc() "
/* "returned NULL for a size of %d bytes.", file, line, size); */ "returned NULL for a size of %d bytes.", file, line, size);
/* memcpy(copy, s, size); */ memcpy(copy, s, size);
/* return copy; */ return copy;
/* } */ }
static void * static void *
_safemalloc(size_t size, _safemalloc(size_t size,
@ -440,7 +440,7 @@ rmff_read_headers(rmff_file_t *file) {
track.file = (struct rmff_file_t *)file; track.file = (struct rmff_file_t *)file;
mdpr = &track.mdpr_header; mdpr = &track.mdpr_header;
read_uint16_be_to(&mdpr->id); read_uint16_be_to(&mdpr->id);
track.id = get_uint16_be(&mdpr->id); track.id = rmff_get_uint16_be(&mdpr->id);
read_uint32_be_to(&mdpr->max_bit_rate); read_uint32_be_to(&mdpr->max_bit_rate);
read_uint32_be_to(&mdpr->avg_bit_rate); read_uint32_be_to(&mdpr->avg_bit_rate);
read_uint32_be_to(&mdpr->max_packet_size); read_uint32_be_to(&mdpr->max_packet_size);
@ -474,7 +474,7 @@ rmff_read_headers(rmff_file_t *file) {
(get_fourcc(&ra4p->fourcc1) == (get_fourcc(&ra4p->fourcc1) ==
rmffFOURCC('.', 'r', 'a', 0xfd))) { rmffFOURCC('.', 'r', 'a', 0xfd))) {
track.type = RMFF_TRACK_TYPE_AUDIO; track.type = RMFF_TRACK_TYPE_AUDIO;
if ((get_uint16_be(&ra4p->version1) == 5) && if ((rmff_get_uint16_be(&ra4p->version1) == 5) &&
(size < sizeof(real_audio_v5_props_t))) (size < sizeof(real_audio_v5_props_t)))
return set_error(RMFF_ERR_DATA, "RealAudio v5 data indicated but " return set_error(RMFF_ERR_DATA, "RealAudio v5 data indicated but "
"data too small", RMFF_ERR_DATA); "data too small", RMFF_ERR_DATA);
@ -588,6 +588,24 @@ rmff_read_next_frame(rmff_file_t *file,
return frame; return frame;
} }
rmff_frame_t *
rmff_allocate_frame(uint32_t size,
void *buffer) {
rmff_frame_t *frame;
if (size == 0)
return (rmff_frame_t *)set_error(RMFF_ERR_PARAMETERS, NULL, 0);
frame = (rmff_frame_t *)safecalloc(sizeof(rmff_frame_t));
if (buffer == NULL) {
buffer = safemalloc(size);
frame->allocated_by_rmff = 1;
}
frame->size = size;
frame->data = (unsigned char *)buffer;
return frame;
}
void void
rmff_release_frame(rmff_frame_t *frame) { rmff_release_frame(rmff_frame_t *frame) {
if (frame == NULL) if (frame == NULL)
@ -615,3 +633,32 @@ rmff_set_cont_header(rmff_file_t *file,
file->cont_header.copyright = safestrdup(copyright); file->cont_header.copyright = safestrdup(copyright);
file->cont_header.comment = safestrdup(comment); file->cont_header.comment = safestrdup(comment);
} }
void
rmff_set_track_data(rmff_track_t *track,
const char *name,
const char *mime_type) {
if (track == NULL)
return;
if (name != track->mdpr_header.name) {
safefree(track->mdpr_header.name);
track->mdpr_header.name = safestrdup(name);
}
if (mime_type != track->mdpr_header.mime_type) {
safefree(track->mdpr_header.mime_type);
track->mdpr_header.mime_type = safestrdup(mime_type);
}
}
void
rmff_set_track_specific_data(rmff_track_t *track,
const unsigned char *data,
uint32_t size) {
if (track == NULL)
return;
if (data != track->mdpr_header.type_specific_data) {
safefree(track->mdpr_header.type_specific_data);
track->mdpr_header.type_specific_data =
(unsigned char *)safememdup(data, size);
}
}