diff --git a/librmff/librmff.h b/librmff/librmff.h
index ce7c40d66..dc9d8a7ce 100644
--- a/librmff/librmff.h
+++ b/librmff/librmff.h
@@ -151,6 +151,54 @@
free(buffer);
}
\endcode
+
+
+ \section packed_video_frames Packed video frames
+
+ The RealVideo frames are not stored one-complete-frame in one packet
+ (packet meaning one 'frame' used by ::rmff_write_frame and returned by
+ ::rmff_read_next_frame). They are split up into several sub packets.
+ However, the Real decoder libraries usually need a completely assembled
+ frame that contains all sub packets along with the offsets to each sub
+ packet in the complete frame.
+
+ \a librmff can make the developper's life easier because it provides
+ a function ::rmff_write_packed_video_frame that will split up such
+ an assembled packet into all the sub packets and write those
+ automatically.
+
+ \subsection packed_format Bitstream format
+
+ The bitstream for such a sub packet looks like this:
+
+ AABBBBBB BCCCCCCC {DEFFFFFF FFFFFFFF (GGGGGGGG GGGGGGGG)
+ HIJJJJJJ JJJJJJJJ (KKKKKKKK KKKKKKKK) LLLLLLLL}
+
+ - \c A, two bits: Sub packet type indicator
+ - \c 00 partial frame
+ - \c 01 whole frame. If this is the case then only the bits A, B
+ and \c C are present. In all the other cases the other bits are present
+ as well.
+ - \c 10 last partial frame
+ - \c 11 multiple frames
+ - \c B, seven bits: number of sub packets in the complete frame
+ - \c C, seven bits: current sub packet's sequence number starting at 1
+ - \c D, one bit: unknown
+ - \c E, one bit: If set the \c G bits/bytes are not present.
+ - \c F, 14 bits: The complete frame's length in bytes. If \c E is NOT set
+ then the total length is (F << 16) | G, otherwise \c G is not present
+ and the total length is just \c F.
+ - \c H, one bit, unknown
+ - \c I, one bit: If set the \c L bits/bytes are not present.
+ - \c J, 14 bits: The current packet's offset in bytes. If \c J is NOT set
+ then the offset is (J << 16) | K, otherwise \c K is not present
+ and the offset is just \c J. Note that if \c AA is 10 then the offset is
+ to be interpreted from the end of the of the complete frame. In this case
+ it is equal to the current sub packet's length.
+ - \c L, 8 bits: The current frame's sequence number / frame number % 255.
+ Can be used for detecting frame boundaries if the sub packaging has failed
+ or the stream is broken.
+ - The rest is the video data.
*/
#ifndef __RMFF_H
@@ -160,6 +208,8 @@
extern "C" {
#endif
+#include "os.h"
+
#include
#include
@@ -723,6 +773,22 @@ int rmff_fix_headers(rmff_file_t *file);
*/
int rmff_write_frame(rmff_track_t *track, rmff_frame_t *frame);
+/** \brief Unpacks a packed video frame into sub packets and writes each.
+
+ Please see the section about packed video \ref packed_video_frames frames
+ for a description of this function.
+
+ This function takes such a complete frame, splits it up into their sub
+ packets and writes each sub packet out into the file this track belongs to.
+
+ \param track The track this frame belongs to.
+ \param frame The assembled/packed video frame that is to be split up.
+
+ \returns \c RMFF_ERR_OK on success or one of the other \c RMFF_ERR_*
+ constants on error.
+*/
+int rmff_write_packed_video_frame(rmff_track_t *track, rmff_frame_t *frame);
+
/** \brief Creates an index at the end of the file.
The application can request that an index is created For each track
@@ -808,6 +874,17 @@ uint16_t rmff_get_uint16_be(const void *buf);
*/
uint32_t rmff_get_uint32_be(const void *buf);
+/** \brief Reads a 32bit uint from an address.
+
+ The uint is converted from little 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_le(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
@@ -828,6 +905,26 @@ void rmff_put_uint16_be(void *buf, uint16_t value);
*/
void rmff_put_uint32_be(void *buf, uint32_t value);
+/** \brief Write a 32bit uint at an address.
+
+ The value is converted from the machine's byte order to little endian byte
+ order.
+
+ \param buf The address to write to.
+ \param value The value to write.
+*/
+void rmff_put_uint32_le(void *buf, uint32_t value);
+
+#if defined(ARCH_LITTLEENDIAN)
+# define rmff_get_uint32_me(b) rmff_get_uint32_le(b)
+# define rmff_put_uint32_me(b) rmff_put_uint32_le(b)
+#elif defined(ARCH_BIGENDIAN)
+# define rmff_get_uint32_me(b) rmff_get_uint32_be(b)
+# define rmff_put_uint32_me(b) rmff_put_uint32_be(b)
+#else
+# error Neither ARCH_LITTLEENDIAN nor ARCH_BIGENDIAN has been defined.
+#endif
+
#if defined(__cplusplus)
}
#endif
diff --git a/librmff/rmff.c b/librmff/rmff.c
index 6473144d5..53292891e 100644
--- a/librmff/rmff.c
+++ b/librmff/rmff.c
@@ -58,6 +58,7 @@ typedef struct rmff_track_internal_t {
uint32_t avg_packet_size;
uint32_t highest_timecode;
uint32_t num_packets;
+ uint32_t num_packed_frames;
int index_this;
uint32_t total_bytes;
rmff_bitrate_t bitrate;
@@ -126,6 +127,21 @@ rmff_get_uint32_be(const void *buf) {
return ret;
}
+uint32_t
+rmff_get_uint32_le(const void *buf) {
+ uint32_t ret;
+ unsigned char *tmp;
+
+ tmp = (unsigned char *) buf;
+
+ ret = tmp[3] & 0xff;
+ ret = (ret << 8) + (tmp[2] & 0xff);
+ ret = (ret << 8) + (tmp[1] & 0xff);
+ ret = (ret << 8) + (tmp[0] & 0xff);
+
+ return ret;
+}
+
void
rmff_put_uint16_be(void *buf,
uint16_t value) {
@@ -150,6 +166,19 @@ rmff_put_uint32_be(void *buf,
tmp[0] = (value >>= 8) & 0xff;
}
+void
+rmff_put_uint32_le(void *buf,
+ uint32_t value) {
+ unsigned char *tmp;
+
+ tmp = (unsigned char *) buf;
+
+ tmp[0] = value & 0xff;
+ tmp[1] = (value >>= 8) & 0xff;
+ tmp[2] = (value >>= 8) & 0xff;
+ tmp[3] = (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)
@@ -1379,3 +1408,124 @@ rmff_write_index(rmff_file_t *file) {
return clear_error();
}
+
+int
+rmff_write_packed_video_frame(rmff_track_t *track,
+ rmff_frame_t *frame) {
+ unsigned char *src_ptr, *ptr, *dst;
+ int num_subpackets, i, offset, total_len;
+ uint32_t *offsets, *lengths;
+ rmff_frame_t *spframe;
+ rmff_track_internal_t *tint;
+
+ if ((track == NULL) || (frame == NULL) ||
+ (track->file == NULL) ||
+ (track->file->open_mode != RMFF_OPEN_MODE_WRITING))
+ return set_error(RMFF_ERR_PARAMETERS, NULL, RMFF_ERR_PARAMETERS);
+
+ tint = (rmff_track_internal_t *)track->internal;
+ src_ptr = frame->data;
+ num_subpackets = *src_ptr + 1;
+ src_ptr++;
+ if (frame->size < (num_subpackets * 8 + 1))
+ return set_error(RMFF_ERR_DATA, "RealVideo unpacking failed: frame size "
+ "too small. Could not extract sub packet offsets.",
+ RMFF_ERR_DATA);
+
+ offsets = (uint32_t *)safemalloc(num_subpackets * sizeof(uint32_t));
+ for (i = 0; i < num_subpackets; i++) {
+ src_ptr += 4;
+ offsets[i] = rmff_get_uint32_me(src_ptr);
+ src_ptr += 4;
+ }
+ if ((offsets[num_subpackets - 1] + (src_ptr - frame->data)) >= frame->size) {
+ safefree(offsets);
+ return set_error(RMFF_ERR_DATA, "RealVideo unpacking failed: frame size "
+ "too small. The sub packet offsets indicate a size "
+ "larger than the actual size.", RMFF_ERR_DATA);
+ }
+ total_len = frame->size - (src_ptr - frame->data);
+ lengths = (uint32_t *)safemalloc(num_subpackets * sizeof(uint32_t));
+ for (i = 0; i < (num_subpackets - 1); i++)
+ lengths[i] = offsets[i + 1] - offsets[i];
+ lengths[num_subpackets - 1] = total_len - offsets[num_subpackets - 1];
+
+ dst = (unsigned char *)safemalloc(frame->size * 2);
+ for (i = 0; i < num_subpackets; i++) {
+ ptr = dst;
+ if (num_subpackets == 1) {
+ /* BROKEN! */
+ *ptr = 0xc0; /* complete frame */
+ ptr++;
+
+ } else {
+ *ptr = (num_subpackets >> 1) & 0x7f; /* number of subpackets */
+ if (i == (num_subpackets - 1)) /* last fragment? */
+ *ptr |= 0x80;
+ ptr++;
+
+ *ptr = i + 1; /* fragment number */
+ *ptr |= ((num_subpackets & 0x01) << 7); /* number of subpackets */
+ ptr++;
+ }
+
+ /* total packet length: */
+ if (total_len > 0x3fff) {
+ rmff_put_uint16_be(ptr, ((total_len & 0x3fff0000) >> 16));
+ ptr += 2;
+ rmff_put_uint16_be(ptr, total_len & 0x0000ffff);
+ } else
+ rmff_put_uint16_be(ptr, 0x4000 | total_len);
+ ptr += 2;
+
+ /* fragment offset from beginning/end: */
+ if (num_subpackets == 1)
+ offset = frame->timecode;
+ else if (i < (num_subpackets - 1))
+ offset = offsets[i];
+ else
+ /* If it's the last packet then the 'offset' is the fragment's length. */
+ offset = lengths[i];
+
+ if (offset > 0x3fff) {
+ rmff_put_uint16_be(ptr, ((offset & 0x3fff0000) >> 16));
+ ptr += 2;
+ rmff_put_uint16_be(ptr, offset & 0x0000ffff);
+ } else
+ rmff_put_uint16_be(ptr, 0x4000 | offset);
+ ptr += 2;
+
+ /* sequence number = frame number & 0xff */
+ *ptr = tint->num_packed_frames & 0xff;
+ ptr++;
+
+ memcpy(ptr, src_ptr, lengths[i]);
+ src_ptr += lengths[i];
+ ptr += lengths[i];
+
+ spframe = rmff_allocate_frame(ptr - dst, dst);
+ if (spframe == NULL) {
+ safefree(offsets);
+ safefree(lengths);
+ safefree(dst);
+ return set_error(RMFF_ERR_IO, "Memory allocation error: Could not get a "
+ "rmff_frame_t", RMFF_ERR_IO);
+ }
+ spframe->timecode = frame->timecode;
+ spframe->flags = frame->flags;
+ if (rmff_write_frame(track, spframe) != RMFF_ERR_OK) {
+ safefree(offsets);
+ safefree(lengths);
+ safefree(dst);
+ return rmff_last_error;
+ }
+ rmff_release_frame(spframe);
+ }
+ safefree(offsets);
+ safefree(lengths);
+ safefree(dst);
+
+ tint->num_packed_frames++;
+
+ return set_error(RMFF_ERR_OK, NULL, RMFF_ERR_OK);
+}