/* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * 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. * * FFmpeg is distributed in the hope that it will be useful, * 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 * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * This bitstream filter splits VP9 superframes into packets containing * just one frame. */ #include #include "avcodec.h" #include "bsf.h" #include "bytestream.h" #include "get_bits.h" typedef struct VP9SFSplitContext { AVPacket *buffer_pkt; int nb_frames; int next_frame; size_t next_frame_offset; int sizes[8]; } VP9SFSplitContext; static int vp9_superframe_split_filter(AVBSFContext *ctx, AVPacket *out) { VP9SFSplitContext *s = ctx->priv_data; AVPacket *in; int i, j, ret, marker; int is_superframe = !!s->buffer_pkt->data; if (!s->buffer_pkt->data) { ret = ff_bsf_get_packet_ref(ctx, s->buffer_pkt); if (ret < 0) return ret; in = s->buffer_pkt; marker = in->data[in->size - 1]; if ((marker & 0xe0) == 0xc0) { int length_size = 1 + ((marker >> 3) & 0x3); int nb_frames = 1 + (marker & 0x7); int idx_size = 2 + nb_frames * length_size; if (in->size >= idx_size && in->data[in->size - idx_size] == marker) { GetByteContext bc; int64_t total_size = 0; bytestream2_init(&bc, in->data + in->size + 1 - idx_size, nb_frames * length_size); for (i = 0; i < nb_frames; i++) { int frame_size = 0; for (j = 0; j < length_size; j++) frame_size |= bytestream2_get_byte(&bc) << (j * 8); total_size += frame_size; if (frame_size < 0 || total_size > in->size - idx_size) { av_log(ctx, AV_LOG_ERROR, "Invalid frame size in a superframe: %d\n", frame_size); ret = AVERROR(EINVAL); goto fail; } s->sizes[i] = frame_size; } s->nb_frames = nb_frames; s->next_frame = 0; s->next_frame_offset = 0; is_superframe = 1; } } } if (is_superframe) { GetBitContext gb; int profile, invisible = 0; ret = av_packet_ref(out, s->buffer_pkt); if (ret < 0) goto fail; out->data += s->next_frame_offset; out->size = s->sizes[s->next_frame]; s->next_frame_offset += out->size; s->next_frame++; if (s->next_frame >= s->nb_frames) av_packet_unref(s->buffer_pkt); ret = init_get_bits8(&gb, out->data, out->size); if (ret < 0) goto fail; get_bits(&gb, 2); // frame_marker profile = get_bits1(&gb); profile |= get_bits1(&gb) << 1; if (profile == 3) get_bits1(&gb); if (!get_bits1(&gb)) { get_bits1(&gb); invisible = !get_bits1(&gb); } if (invisible) out->pts = AV_NOPTS_VALUE; } else { av_packet_move_ref(out, s->buffer_pkt); } return 0; fail: if (ret < 0) av_packet_unref(out); av_packet_unref(s->buffer_pkt); return ret; } static int vp9_superframe_split_init(AVBSFContext *ctx) { VP9SFSplitContext *s = ctx->priv_data; s->buffer_pkt = av_packet_alloc(); if (!s->buffer_pkt) return AVERROR(ENOMEM); return 0; } static void vp9_superframe_split_flush(AVBSFContext *ctx) { VP9SFSplitContext *s = ctx->priv_data; av_packet_unref(s->buffer_pkt); } static void vp9_superframe_split_uninit(AVBSFContext *ctx) { VP9SFSplitContext *s = ctx->priv_data; av_packet_free(&s->buffer_pkt); } const AVBitStreamFilter ff_vp9_superframe_split_bsf = { .name = "vp9_superframe_split", .priv_data_size = sizeof(VP9SFSplitContext), .init = vp9_superframe_split_init, .flush = vp9_superframe_split_flush, .close = vp9_superframe_split_uninit, .filter = vp9_superframe_split_filter, .codec_ids = (const enum AVCodecID []){ AV_CODEC_ID_VP9, AV_CODEC_ID_NONE }, };