Commit a3e43e0c authored by Andreas Rheinhardt's avatar Andreas Rheinhardt Committed by James Almer

avformat/av1: Avoid allocation + copying when filtering OBUs

Certain types of OBUs are stripped away before muxing into Matroska and
ISOBMFF; there are two functions to do this: One that outputs by
directly writing in an AVIOContext and one that returns a freshly
allocated buffer with the units not stripped away copied into it.

The latter option is bad for performance, especially when the input
does already not contain any of the units intended to be stripped away
(this covers typical remuxing scenarios). Therefore this commit changes
this by avoiding allocating and copying when possible; it is possible if
the OBUs to be retained are consecutively in the input buffer (without
an OBU to be discarded between them). In this case, the caller receives
the offset as well as the length of the part of the buffer that contains
the units to be kept. This also avoids copying when e.g. the only unit
to be discarded is a temporal delimiter at the front.

For a 22.7mb/s file with average framesize 113 kB this improved the time
for the calls to ff_av1_filter_obus_buf() when writing Matroska from
313319 decicycles to 2368 decicycles; for another file with 1.5mb/s
(average framesize 7.3 kB) it improved from 34539 decicycles to 1922
decicyles. For these files the only units that needed to be stripped
away were temporal unit delimiters at the front.
Signed-off-by: 's avatarAndreas Rheinhardt <andreas.rheinhardt@gmail.com>
Signed-off-by: 's avatarJames Almer <jamrial@gmail.com>
parent 2eee3437
...@@ -29,13 +29,20 @@ ...@@ -29,13 +29,20 @@
#include "avio.h" #include "avio.h"
#include "avio_internal.h" #include "avio_internal.h"
int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) static int av1_filter_obus(AVIOContext *pb, const uint8_t *buf,
int size, int *offset)
{ {
const uint8_t *end = buf + size; const uint8_t *start = buf, *end = buf + size;
int64_t obu_size; int64_t obu_size;
int start_pos, type, temporal_id, spatial_id; int off, start_pos, type, temporal_id, spatial_id;
enum {
size = 0; START_NOT_FOUND,
START_FOUND,
END_FOUND,
OFFSET_IMPOSSIBLE,
} state = START_NOT_FOUND;
off = size = 0;
while (buf < end) { while (buf < end) {
int len = parse_obu_header(buf, end - buf, &obu_size, &start_pos, int len = parse_obu_header(buf, end - buf, &obu_size, &start_pos,
&type, &temporal_id, &spatial_id); &type, &temporal_id, &spatial_id);
...@@ -47,8 +54,16 @@ int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) ...@@ -47,8 +54,16 @@ int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size)
case AV1_OBU_REDUNDANT_FRAME_HEADER: case AV1_OBU_REDUNDANT_FRAME_HEADER:
case AV1_OBU_TILE_LIST: case AV1_OBU_TILE_LIST:
case AV1_OBU_PADDING: case AV1_OBU_PADDING:
if (state == START_FOUND)
state = END_FOUND;
break; break;
default: default:
if (state == START_NOT_FOUND) {
off = buf - start;
state = START_FOUND;
} else if (state == END_FOUND) {
state = OFFSET_IMPOSSIBLE;
}
if (pb) if (pb)
avio_write(pb, buf, len); avio_write(pb, buf, len);
size += len; size += len;
...@@ -57,19 +72,35 @@ int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) ...@@ -57,19 +72,35 @@ int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size)
buf += len; buf += len;
} }
if (offset)
*offset = state != OFFSET_IMPOSSIBLE ? off : -1;
return size; return size;
} }
int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, int *size) int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size)
{
return av1_filter_obus(pb, buf, size, NULL);
}
int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out,
int *size, int *offset)
{ {
AVIOContext pb; AVIOContext pb;
uint8_t *buf; uint8_t *buf;
int len, ret; int len, off, ret;
len = ret = ff_av1_filter_obus(NULL, in, *size); len = ret = av1_filter_obus(NULL, in, *size, &off);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
if (off >= 0) {
*out = (uint8_t *)in;
*size = len;
*offset = off;
return 0;
}
buf = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE); buf = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE);
if (!buf) if (!buf)
...@@ -77,13 +108,14 @@ int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, int *size) ...@@ -77,13 +108,14 @@ int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, int *size)
ffio_init_context(&pb, buf, len, 1, NULL, NULL, NULL, NULL); ffio_init_context(&pb, buf, len, 1, NULL, NULL, NULL, NULL);
ret = ff_av1_filter_obus(&pb, in, *size); ret = av1_filter_obus(&pb, in, *size, NULL);
av_assert1(ret == len); av_assert1(ret == len);
memset(buf + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); memset(buf + len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
*out = buf; *out = buf;
*size = len; *size = len;
*offset = 0;
return 0; return 0;
} }
......
...@@ -56,19 +56,24 @@ typedef struct AV1SequenceParameters { ...@@ -56,19 +56,24 @@ typedef struct AV1SequenceParameters {
int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size); int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size);
/** /**
* Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and write * Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and return
* the resulting bitstream to a newly allocated data buffer. * the result in a data buffer, avoiding allocations and copies if possible.
* *
* @param in input data buffer * @param in input data buffer
* @param out pointer to pointer that will hold the allocated data buffer * @param out pointer to pointer for the returned buffer. In case of success,
* it is independently allocated if and only if `*out` differs from in.
* @param size size of the input data buffer. The size of the resulting output * @param size size of the input data buffer. The size of the resulting output
* data buffer will be written here * data buffer will be written here
* @param offset offset of the returned data inside `*out`: It runs from
* `*out + offset` (inclusive) to `*out + offset + size`
* (exclusive); is zero if `*out` is independently allocated.
* *
* @return 0 in case of success, a negative AVERROR code in case of failure. * @return 0 in case of success, a negative AVERROR code in case of failure.
* On failure, *out and *size are unchanged * On failure, *out and *size are unchanged
* @note *out will be treated as unintialized on input and will not be freed. * @note *out will be treated as unintialized on input and will not be freed.
*/ */
int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, int *size); int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out,
int *size, int *offset);
/** /**
* Parses a Sequence Header from the the provided buffer. * Parses a Sequence Header from the the provided buffer.
......
...@@ -2112,7 +2112,7 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb, ...@@ -2112,7 +2112,7 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
/* extradata is Annex B, assume the bitstream is too and convert it */ /* extradata is Annex B, assume the bitstream is too and convert it */
err = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL); err = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL);
} else if (par->codec_id == AV_CODEC_ID_AV1) { } else if (par->codec_id == AV_CODEC_ID_AV1) {
err = ff_av1_filter_obus_buf(pkt->data, &data, &size); err = ff_av1_filter_obus_buf(pkt->data, &data, &size, &offset);
} else if (par->codec_id == AV_CODEC_ID_WAVPACK) { } else if (par->codec_id == AV_CODEC_ID_WAVPACK) {
err = mkv_strip_wavpack(pkt->data, &data, &size); err = mkv_strip_wavpack(pkt->data, &data, &size);
} else } else
......
...@@ -5374,7 +5374,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -5374,7 +5374,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
AVCodecParameters *par = trk->par; AVCodecParameters *par = trk->par;
AVProducerReferenceTime *prft; AVProducerReferenceTime *prft;
unsigned int samples_in_chunk = 0; unsigned int samples_in_chunk = 0;
int size = pkt->size, ret = 0; int size = pkt->size, ret = 0, offset = 0;
int prft_size; int prft_size;
uint8_t *reformatted_data = NULL; uint8_t *reformatted_data = NULL;
...@@ -5491,7 +5491,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -5491,7 +5491,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
} }
} else if (par->codec_id == AV_CODEC_ID_AV1) { } else if (par->codec_id == AV_CODEC_ID_AV1) {
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) {
ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, &size); ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data,
&size, &offset);
if (ret < 0) if (ret < 0)
return ret; return ret;
avio_write(pb, reformatted_data, size); avio_write(pb, reformatted_data, size);
...@@ -5667,12 +5668,14 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -5667,12 +5668,14 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams)
ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry, ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry,
reformatted_data, size); reformatted_data ? reformatted_data + offset
: NULL, size);
end: end:
err: err:
av_free(reformatted_data); if (pkt->data != reformatted_data)
av_free(reformatted_data);
return ret; return ret;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment