diff --git a/librmff/librmff.h b/librmff/librmff.h index 8df83cedf..710e3e6a4 100644 --- a/librmff/librmff.h +++ b/librmff/librmff.h @@ -24,6 +24,82 @@ * \author Moritz Bunkus */ +/** \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 . 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 #define __RMFF_H @@ -36,9 +112,14 @@ extern "C" { #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 - * 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 { uint32_t max_bit_rate; @@ -55,9 +136,10 @@ typedef struct { } rmff_prop_t; /** \brief Comments about the file in question. + * * This structure contains the parsed values of the CONT header. These * 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. */ typedef struct { @@ -67,18 +149,59 @@ typedef struct { char *comment; } 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 { + /** \brief The track number. It is unique regarding the file. */ 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; + /** \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; + /** \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; + /** \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 start_time; uint32_t preroll; 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; + /** \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; + /** \brief The size of the track specific data in bytes. */ 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; } rmff_mdpr_t; @@ -162,7 +285,7 @@ typedef struct rmff_frame_t { /** \brief The track contains video data. */ #define RMFF_TRACK_TYPE_VIDEO 2 -struct rmff_file_t ; +struct rmff_file_t; typedef struct rmff_track_t { uint32_t id; @@ -210,15 +333,18 @@ typedef struct rmff_file_t { /** \brief An error has occured for which \c errno should be consulted. */ #define RMFF_ERR_CHECK_ERRNO -6 -#define rmffFOURCC(a, b, c, d) (uint32_t)((((unsigned char)a) << 24) + \ - (((unsigned char)b) << 16) + \ - (((unsigned char)c) << 8) + \ - ((unsigned char)d)) +/** \brief Convert the four bytes into a 'FOURCC' uint32. */ +#define rmffFOURCC(a, b, c, d) \ + (uint32_t)((((unsigned char)a) << 24) + \ + (((unsigned char)b) << 16) + \ + (((unsigned char)c) << 8) + \ + ((unsigned char)d)) #define RMFF_OPEN_MODE_READING 0 #define RMFF_OPEN_MODE_WRITING 1 /** \brief Opens a RealMedia file for reading or writing. + * * 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. * 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 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. * \see rmff_open_file_with_io */ rmff_file_t *rmff_open_file(const char *path, int mode); /** \brief Opens a RealMedia file for reading or writing. + * * 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. - * 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 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); /** \brief Close the file and release all resources. + * * 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 * \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); /** \brief Reads the file and track headers. + * * 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 * 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); /** \brief Reads the next frame from the file. + * * The frame must be released by rmff_release_frame(rmff_frame_t*). * * \param file The file to read from. * \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. * 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 * and its metadata on success or \c NULL if the call failed. This frame * must be freed with rmff_release_frame(rmff_frame_t*). */ 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. +* * If the frame buffer was allocated by the library it will be freed as well. * * \param frame The frame to free. */ 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 * 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. * * \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 *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. * 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. */ extern int rmff_last_error; @@ -334,6 +509,42 @@ extern const char *rmff_last_error_msg; */ 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) } #endif diff --git a/librmff/rmff.c b/librmff/rmff.c index caf89cd46..011911599 100644 --- a/librmff/rmff.c +++ b/librmff/rmff.c @@ -60,8 +60,8 @@ set_error(int error_number, return return_value; } -static uint16_t -get_uint16_be(const void *buf) { +uint16_t +rmff_get_uint16_be(const void *buf) { uint16_t ret; unsigned char *tmp; @@ -73,8 +73,8 @@ get_uint16_be(const void *buf) { return ret; } -static uint32_t -get_uint32_be(const void *buf) { +uint32_t +rmff_get_uint32_be(const void *buf) { uint32_t ret; unsigned char *tmp; @@ -88,8 +88,8 @@ get_uint32_be(const void *buf) { return ret; } -static void -put_uint16_be(void *buf, +void +rmff_put_uin16_be(void *buf, uint16_t value) { unsigned char *tmp; @@ -99,8 +99,8 @@ put_uint16_be(void *buf, tmp[0] = (value >>= 8) & 0xff; } -static void -put_uint32_be(void *buf, +void +rmff_put_uin32_be(void *buf, uint32_t value) { unsigned char *tmp; @@ -115,9 +115,9 @@ put_uint32_be(void *buf, #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) +#define read_uint16_be_to(addr) rmff_put_uin16_be(addr, read_uint16_be()) +#define read_uint32_be_to(addr) rmff_put_uin32_be(addr, read_uint32_be()) +#define get_fourcc(b) rmff_get_uint32_be(b) static uint8_t 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]; io->read(fh, tmp, 2); - return get_uint16_be(tmp); + return rmff_get_uint16_be(tmp); } static uint32_t @@ -143,7 +143,7 @@ file_read_uint32_be(mb_file_io_t *io, unsigned char tmp[4]; io->read(fh, tmp, 4); - return get_uint32_be(tmp); + return rmff_get_uint32_be(tmp); } void @@ -183,24 +183,24 @@ _safestrdup(const char *s, return copy; } -/* static void * */ -/* _safememdup(const void *s, */ -/* size_t size, */ -/* const char *file, */ -/* int line) { */ -/* void *copy; */ +static void * +_safememdup(const void *s, + size_t size, + const char *file, + int line) { + void *copy; -/* if (s == NULL) */ -/* return NULL; */ + 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); */ + 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; */ -/* } */ + return copy; +} static void * _safemalloc(size_t size, @@ -440,7 +440,7 @@ rmff_read_headers(rmff_file_t *file) { track.file = (struct rmff_file_t *)file; mdpr = &track.mdpr_header; 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->avg_bit_rate); read_uint32_be_to(&mdpr->max_packet_size); @@ -474,7 +474,7 @@ rmff_read_headers(rmff_file_t *file) { (get_fourcc(&ra4p->fourcc1) == rmffFOURCC('.', 'r', 'a', 0xfd))) { 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))) return set_error(RMFF_ERR_DATA, "RealAudio v5 data indicated but " "data too small", RMFF_ERR_DATA); @@ -588,6 +588,24 @@ rmff_read_next_frame(rmff_file_t *file, 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 rmff_release_frame(rmff_frame_t *frame) { if (frame == NULL) @@ -615,3 +633,32 @@ rmff_set_cont_header(rmff_file_t *file, file->cont_header.copyright = safestrdup(copyright); 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); + } +}