f89f78c1 |
/*
* HEVC MP4 to Annex B byte stream format filter
* copyright (c) 2015 Anton Khirnov
* |
15bcbc9d |
* This file is part of FFmpeg. |
f89f78c1 |
* |
15bcbc9d |
* FFmpeg is free software; you can redistribute it and/or |
f89f78c1 |
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* |
15bcbc9d |
* FFmpeg is distributed in the hope that it will be useful, |
f89f78c1 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public |
15bcbc9d |
* License along with FFmpeg; if not, write to the Free Software |
f89f78c1 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "avcodec.h" |
33d18982 |
#include "bsf.h" |
f89f78c1 |
#include "bytestream.h" |
c359d624 |
#include "hevc.h" |
f89f78c1 |
#define MIN_HEVCC_LENGTH 23
typedef struct HEVCBSFContext {
uint8_t length_size;
int extradata_parsed;
} HEVCBSFContext;
|
33d18982 |
static int hevc_extradata_to_annexb(AVBSFContext *ctx) |
f89f78c1 |
{
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
uint8_t *new_extradata = NULL; |
f080a01f |
size_t new_extradata_size = 0; |
f89f78c1 |
|
33d18982 |
bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size); |
f89f78c1 |
bytestream2_skip(&gb, 21);
length_size = (bytestream2_get_byte(&gb) & 3) + 1;
num_arrays = bytestream2_get_byte(&gb);
for (i = 0; i < num_arrays; i++) {
int type = bytestream2_get_byte(&gb) & 0x3f;
int cnt = bytestream2_get_be16(&gb);
|
c359d624 |
if (!(type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS ||
type == HEVC_NAL_SEI_PREFIX || type == HEVC_NAL_SEI_SUFFIX)) { |
33d18982 |
av_log(ctx, AV_LOG_ERROR, "Invalid NAL unit type in extradata: %d\n", |
f89f78c1 |
type);
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (j = 0; j < cnt; j++) {
int nalu_len = bytestream2_get_be16(&gb);
|
059a9348 |
if (4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) { |
f89f78c1 |
ret = AVERROR_INVALIDDATA;
goto fail;
} |
059a9348 |
ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + AV_INPUT_BUFFER_PADDING_SIZE); |
f89f78c1 |
if (ret < 0)
goto fail;
AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode
bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, nalu_len);
new_extradata_size += 4 + nalu_len; |
059a9348 |
memset(new_extradata + new_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); |
f89f78c1 |
}
}
|
33d18982 |
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = new_extradata;
ctx->par_out->extradata_size = new_extradata_size; |
f89f78c1 |
if (!new_extradata_size) |
33d18982 |
av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n"); |
f89f78c1 |
return length_size;
fail:
av_freep(&new_extradata);
return ret;
}
|
33d18982 |
static int hevc_mp4toannexb_init(AVBSFContext *ctx) |
f89f78c1 |
{ |
33d18982 |
HEVCBSFContext *s = ctx->priv_data;
int ret;
if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
AV_RB24(ctx->par_in->extradata) == 1 ||
AV_RB32(ctx->par_in->extradata) == 1) {
av_log(ctx, AV_LOG_VERBOSE,
"The input looks like it is Annex B already\n");
} else {
ret = hevc_extradata_to_annexb(ctx);
if (ret < 0)
return ret;
s->length_size = ret;
s->extradata_parsed = 1;
}
return 0;
}
static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) |
f89f78c1 |
{ |
33d18982 |
HEVCBSFContext *s = ctx->priv_data;
AVPacket *in; |
f89f78c1 |
GetByteContext gb;
int got_irap = 0;
int i, ret = 0;
|
33d18982 |
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret; |
f89f78c1 |
|
33d18982 |
if (!s->extradata_parsed) {
av_packet_move_ref(out, in);
av_packet_free(&in);
return 0; |
f89f78c1 |
}
|
33d18982 |
bytestream2_init(&gb, in->data, in->size); |
f89f78c1 |
while (bytestream2_get_bytes_left(&gb)) {
uint32_t nalu_size = 0;
int nalu_type; |
33d18982 |
int is_irap, add_extradata, extra_size, prev_size; |
f89f78c1 |
|
33d18982 |
for (i = 0; i < s->length_size; i++) |
f89f78c1 |
nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
/* prepend extradata to IRAP frames */
is_irap = nalu_type >= 16 && nalu_type <= 23;
add_extradata = is_irap && !got_irap; |
33d18982 |
extra_size = add_extradata * ctx->par_out->extradata_size; |
f89f78c1 |
got_irap |= is_irap;
|
33d18982 |
if (SIZE_MAX - nalu_size < 4 ||
SIZE_MAX - 4 - nalu_size < extra_size) { |
f89f78c1 |
ret = AVERROR_INVALIDDATA;
goto fail;
}
|
33d18982 |
prev_size = out->size;
ret = av_grow_packet(out, 4 + nalu_size + extra_size); |
f89f78c1 |
if (ret < 0)
goto fail;
if (add_extradata) |
33d18982 |
memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
AV_WB32(out->data + prev_size + extra_size, 1);
bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size); |
f89f78c1 |
}
|
33d18982 |
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail; |
f89f78c1 |
fail: |
33d18982 |
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
|
f89f78c1 |
return ret;
}
|
33d18982 |
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE,
}; |
0b8b18b4 |
|
33d18982 |
const AVBitStreamFilter ff_hevc_mp4toannexb_bsf = {
.name = "hevc_mp4toannexb",
.priv_data_size = sizeof(HEVCBSFContext),
.init = hevc_mp4toannexb_init,
.filter = hevc_mp4toannexb_filter,
.codec_ids = codec_ids, |
f89f78c1 |
}; |