Added documentation about the packed/assembled video frame stuff. Added a function for splitting and writing such frames.

This commit is contained in:
Moritz Bunkus 2004-03-24 15:29:44 +00:00
parent 93e60e04e7
commit f606c368f2
2 changed files with 247 additions and 0 deletions

View File

@ -151,6 +151,54 @@
free(buffer); free(buffer);
} }
\endcode \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:
<tt>AABBBBBB BCCCCCCC {DEFFFFFF FFFFFFFF (GGGGGGGG GGGGGGGG)
HIJJJJJJ JJJJJJJJ (KKKKKKKK KKKKKKKK) LLLLLLLL}</tt>
- \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 <tt>A, B</tt>
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 <tt>(F << 16) | G</tt>, 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 <tt>(J << 16) | K</tt>, 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 #ifndef __RMFF_H
@ -160,6 +208,8 @@
extern "C" { extern "C" {
#endif #endif
#include "os.h"
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
@ -723,6 +773,22 @@ int rmff_fix_headers(rmff_file_t *file);
*/ */
int rmff_write_frame(rmff_track_t *track, rmff_frame_t *frame); 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. /** \brief Creates an index at the end of the file.
The application can request that an index is created For each track 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); 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. /** \brief Write a 16bit uint at an address.
The value is converted from the machine's byte order to big endian byte 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); 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) #if defined(__cplusplus)
} }
#endif #endif

View File

@ -58,6 +58,7 @@ typedef struct rmff_track_internal_t {
uint32_t avg_packet_size; uint32_t avg_packet_size;
uint32_t highest_timecode; uint32_t highest_timecode;
uint32_t num_packets; uint32_t num_packets;
uint32_t num_packed_frames;
int index_this; int index_this;
uint32_t total_bytes; uint32_t total_bytes;
rmff_bitrate_t bitrate; rmff_bitrate_t bitrate;
@ -126,6 +127,21 @@ rmff_get_uint32_be(const void *buf) {
return ret; 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 void
rmff_put_uint16_be(void *buf, rmff_put_uint16_be(void *buf,
uint16_t value) { uint16_t value) {
@ -150,6 +166,19 @@ rmff_put_uint32_be(void *buf,
tmp[0] = (value >>= 8) & 0xff; 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_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)
@ -1379,3 +1408,124 @@ rmff_write_index(rmff_file_t *file) {
return clear_error(); 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);
}