mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 04:11:44 +00:00
Added documentation about the packed/assembled video frame stuff. Added a function for splitting and writing such frames.
This commit is contained in:
parent
93e60e04e7
commit
f606c368f2
@ -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
|
||||||
|
150
librmff/rmff.c
150
librmff/rmff.c
@ -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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user