2014-11-30 1:45 GMT+01:00 Christophe Gisquet <christophe.gisq...@gmail.com>: > From: Zongyi Zhou <zho...@os.pku.edu.cn>
Squashed and updated patch attached.
From a49425e6af4e3e488c390d94d8f86c5d1745dda3 Mon Sep 17 00:00:00 2001 From: Zongyi Zhou <zho...@os.pku.edu.cn> Date: Sat, 29 Nov 2014 15:04:37 +0100 Subject: [PATCH 2/4] h264_changesps_bsf: import code Mostly unverified, but seems to have been extensively tested here: http://forum.doom9.org/showthread.php?t=152419 The code has been slightly edited to avoid hackish solutions to passing arguments and using the H.264 aspect ratio. Signed-off-by: Christophe Gisquet <christophe.gisq...@gmail.com> --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/h264_changesps_bsf.c | 604 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 606 insertions(+) create mode 100644 libavcodec/h264_changesps_bsf.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index fa0f53d..8eecf51 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -820,6 +820,7 @@ OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += aac_adtstoasc_bsf.o aacadtsdec.o \ mpeg4audio.o OBJS-$(CONFIG_CHOMP_BSF) += chomp_bsf.o OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o +OBJS-$(CONFIG_H264_CHANGESPS_BSF) += h264_changesps_bsf.o OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o mjpeg.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 0d39d33..0380824 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -581,6 +581,7 @@ void avcodec_register_all(void) REGISTER_BSF(AAC_ADTSTOASC, aac_adtstoasc); REGISTER_BSF(CHOMP, chomp); REGISTER_BSF(DUMP_EXTRADATA, dump_extradata); + REGISTER_BSF(H264_CHANGESPS, h264_changesps); REGISTER_BSF(H264_MP4TOANNEXB, h264_mp4toannexb); REGISTER_BSF(IMX_DUMP_HEADER, imx_dump_header); REGISTER_BSF(MJPEG2JPEG, mjpeg2jpeg); diff --git a/libavcodec/h264_changesps_bsf.c b/libavcodec/h264_changesps_bsf.c new file mode 100644 index 0000000..a166748 --- /dev/null +++ b/libavcodec/h264_changesps_bsf.c @@ -0,0 +1,604 @@ +/* + * H.264 change sps filter + * Copyright (c) 2010 Zongyi Zhou <zho...@os.pku.edu.cn> + * + * 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 + */ + +#include "avcodec.h" +#include "golomb.h" +#include "h264.h" +#include "h264data.h" + +typedef struct H264SPSContext { + uint8_t state; + uint8_t bs_type; // 1=annexb 2=mp4 + int8_t fps_mode; + int8_t remove_hrd; + int32_t fps_den, fps_num; + int32_t predefined_sar; + int32_t level; + int32_t refs; + uint32_t remove_nal_flags, delay_remove_flags; + int16_t sar_x, sar_y; + int16_t crop_x, crop_y; + int8_t fullrange, colorprim, transfer, colmatrix; +} H264SPSContext; + +static int parse_args(struct H264SPSContext *c, const char *args) +{ + int r = 0; + int32_t sarx = -1, sary = -1, cropx = -1, cropy = -1, level = -1, + refs = -1, colorprim = -1, transfer = -1, colmatrix = -1; + if (!args) return 0; + c->fps_den = c->fps_num = c->fps_mode = -1; + c->fullrange = -1; + c->remove_nal_flags = c->delay_remove_flags = 0; + while (*args) { + if (sscanf(args, "fps=%u:%u", &c->fps_num, &c->fps_den) == 2 || + sscanf(args, "sar=%u:%u", &sarx, &sary) == 2 || + sscanf(args, "crop=%u:%u", &cropx, &cropy) == 2 || + sscanf(args, "level=%u", &level) == 1 || + sscanf(args, "ref=%u", &refs) == 1 || + sscanf(args, "colorprim=%u", &colorprim) == 1 || + sscanf(args, "transfer=%u", &transfer) == 1 || + sscanf(args, "colormatrix=%u", &colmatrix) == 1) + r++; + else if (!strncmp(args, "vfr", 3)) { + r++; + c->fps_mode = 0; + } else if (!strncmp(args, "cfr", 3)) { + r++; + c->fps_mode = 1; + } else if (!strncmp(args, "fullrange", 9)) { + r++; + c->fullrange = 1; + } else if (!strncmp(args, "removesei", 9)) { + r++; + c->remove_nal_flags |= 1 << NAL_SEI; + } else if (!strncmp(args, "removeaud", 9)) { + r++; + c->remove_nal_flags |= 1 << NAL_AUD; + } else if (!strncmp(args, "limitedrange", 12)) { + r++; + c->fullrange = 0; + } else if (!strncmp(args, "removedupheader", 15)) { + r++; + c->delay_remove_flags |= (1 << NAL_SPS) + (1 << NAL_PPS); + } + if (!(args = strchr(args, '/'))) break; + args++; + } + if (sary == -1) sarx = -1; + if (cropy == -1) cropx = -1; + c->sar_x = sarx; c->sar_y = sary; + c->crop_x = cropx; c->crop_y = cropy; + c->level = level; + c->refs = refs; + c->colorprim = colorprim; + c->transfer = transfer; + c->colmatrix = colmatrix; + if (sarx && sary) { + int i; + c->predefined_sar = 255; + for (i = 1; i < 17; i++) + if (ff_h264_pixel_aspect[i].den * sarx == ff_h264_pixel_aspect[i].num * sary) { + c->predefined_sar = i; + break; + } + } + return r; +} + +static void skip_hrd_param(GetBitContext *pgb) +{ + int t = get_ue_golomb_31(pgb); + skip_bits(pgb, 8); + for (t++;t;t--) { + get_ue_golomb(pgb); + get_ue_golomb(pgb); + skip_bits1(pgb); + } + skip_bits(pgb, 20); +} + +static int nal_enc(uint8_t *dst, const uint8_t *src, int size) +{ + int i = 0; + uint8_t *dst_start = dst; + for(;size;size--) { + if( i == 2 && *src <= 3 ) { + *dst++ = 3; + i = 0; + } + if (*src == 0) + i++; + else + i = 0; + *dst++ = *src++; + } + return dst - dst_start; +} + +static int nal_dec(uint8_t *dst, const uint8_t *src, int size) +{ + int i = 0; + uint8_t *dst_start = dst; + for(;size;size--) { + uint8_t t = *src++; + if (i == 2) { + i = 0; + if (t == 3) + continue; + } + if (t == 0) + i++; + else + i = 0; + *dst++ = t; + } + return dst - dst_start; +} + +static void sl_copy(GetBitContext *pgb, PutBitContext *ppb, int size) +{ + int delta, next = 8, j; + for(j = 0;j < size && next;j++) { + delta = get_se_golomb(pgb); + set_se_golomb(ppb, delta); + next = (next + delta + 256) & 255; + } +} + +static int h264_modify(uint8_t *outbuf, const uint8_t *inbuf, H264SPSContext *ctx, int insize) +{ + GetBitContext gb; + PutBitContext pb; +#define COPYUE set_ue_golomb(&pb, get_ue_golomb(&gb)) +#define COPYUE31 set_ue_golomb(&pb, get_ue_golomb_31(&gb)) +#define COPYSE set_se_golomb(&pb, get_se_golomb(&gb)) +#define COPYBITS1 put_bits(&pb, 1, get_bits1(&gb)) + int p, t, i; + init_get_bits(&gb, inbuf, insize * 8); + init_put_bits(&pb, outbuf, (insize + 10) * 8); + p = get_bits(&gb, 8); //profile_idc + put_bits(&pb, 8, p); + put_bits(&pb, 8, get_bits(&gb, 8)); //constraint_set + t = get_bits(&gb, 8); //level_idc + if (ctx->level != -1) + t = ctx->level; + put_bits(&pb, 8, t); + COPYUE31; + if (p >= 100) { + t = get_ue_golomb(&gb); //chroma_format_idc + set_ue_golomb(&pb, t); + if (t == 3) + COPYBITS1; //residue_transform_flag + COPYUE; //bit_depth_luma_minus8 + COPYUE; //bit_depth_chroma_minus8 + COPYBITS1; //qpprime_y_zero_transform_bypass_flag + t = get_bits1(&gb); //seq_scaling_matrix_present_flag + put_bits(&pb, 1, t); + if (t) { + //copy scaling list + for (i = 0;i < 8;i++) { + t = get_bits1(&gb); + put_bits(&pb, 1, t); + if (t) + sl_copy(&gb, &pb, i < 6? 16 : 64); + } + } + } + COPYUE; //log2_max_frame_num-4 + t = get_ue_golomb_31(&gb); //poc_type + set_ue_golomb(&pb, t); + if (t == 0) COPYUE; //log2_max_poc_lsb + else if (t == 1) { + COPYBITS1; //delta_pic_order_always_zero + COPYSE; //offset_for_non_ref_pic + COPYSE; //offset_for_top_to_bottom_field + t = get_ue_golomb(&gb); //num_ref_frames_in_poc_cycle + set_ue_golomb(&pb, t); + for (;t;t--) COPYUE; + } + //num_ref_frames + t = get_ue_golomb_31(&gb); + if (ctx->refs != -1) + t = ctx->refs; + set_ue_golomb(&pb,t); + COPYBITS1; + COPYUE; + COPYUE; + i = get_bits1(&gb); //frame_mbs_only + put_bits(&pb, 1, i); + if (!i) COPYBITS1; + COPYBITS1; + + t = get_bits1(&gb); + if (ctx->crop_x == -1) + put_bits(&pb, 1, t); + if (t) { + int t1, t2, t3, t4; + t1 = get_ue_golomb(&gb), + t2 = get_ue_golomb(&gb), + t3 = get_ue_golomb(&gb), + t4 = get_ue_golomb(&gb); + if (ctx->crop_x == -1) { + set_ue_golomb(&pb, t1); + set_ue_golomb(&pb, t2); + set_ue_golomb(&pb, t3); + set_ue_golomb(&pb, t4); + } + } + if (ctx->crop_x != -1) { + if (ctx->crop_x || ctx->crop_y) { + put_bits(&pb, 1, 1); + set_ue_golomb(&pb, 0); + set_ue_golomb(&pb, (ctx->crop_x + 1) >> 1); + set_ue_golomb(&pb, 0); + set_ue_golomb(&pb, (ctx->crop_y + 1) >> (2 >> i)); + } else put_bits(&pb, 1, 0); + } + t = get_bits1(&gb); + put_bits(&pb, 1, t); + if (t) { + int ch = ctx->sar_x != -1; + t = get_bits1(&gb); + if (!ch) put_bits(&pb, 1, t); + if (t) { + t = get_bits(&gb, 8); + if (!ch) put_bits(&pb, 8, t); + if (t == 255) { + t = get_bits(&gb, 16); + i = get_bits(&gb, 16); + if (!ch) { + put_bits(&pb, 16, t); + put_bits(&pb, 16, i); + } + } + } + if (ch) { + if (ctx->predefined_sar == 0) put_bits(&pb, 1, 0); + else { + put_bits(&pb, 1, 1); + if (ctx->predefined_sar < 17) put_bits(&pb, 8, ctx->predefined_sar); + else { + put_bits(&pb, 8, 255); + put_bits(&pb, 16, ctx->sar_x); + put_bits(&pb, 16, ctx->sar_y); + } + } + } + + t = get_bits1(&gb); //overscan_info_present_flag + put_bits(&pb, 1, t); + if (t) COPYBITS1; + + p = ch = -1; i = 0; + t = get_bits1(&gb); //video_signal_type_present_flag + if (t) { + p = get_bits(&gb, 4); //video_format + video_full_range_flag + i = get_bits1(&gb); //colour_description_present_flag + if (i) + ch = get_bits(&gb, 24); + } + if (ctx->fullrange != -1) + p = ctx->fullrange | (p == -1? 5<<1 : p&~1); + if (ctx->colorprim != -1) + ch = (ctx->colorprim<<16) | (ch == -1? (2<<8)|2 : ch&0xFFFF); + if (ctx->transfer != -1) + ch = (ctx->transfer<<8) | (ch == -1? (2<<16)|2 : ch&0xFF00FF); + if (ctx->colmatrix != -1) + ch = ctx->colmatrix | (ch == -1? (2<<16)|(2<<8) : ch&0xFFFF00); + if (p != -1 || ch != -1) + t = 1; + put_bits(&pb, 1, t); + if (t) { + if (p == -1) + p = 5; //undef + put_bits(&pb, 4 ,p); + if (ch != -1) + i = 1; + put_bits(&pb, 1, i); + if (i) + put_bits(&pb, 24, ch); + } + + t = get_bits1(&gb); //chroma_location_info_present_flag + put_bits(&pb, 1, t); + if (t) { + COPYUE; + COPYUE; + } + + t = get_bits1(&gb); //timing_info_present_flag + ch = ctx->fps_den != -1; + if (t) { + t = get_bits_long(&gb, 32), + i = get_bits_long(&gb, 32); + p = get_bits1(&gb); + } + if (ctx->fps_mode >= 0) + p = ctx->fps_mode; + if (ch) { + t = ctx->fps_den; + i = ctx->fps_num * 2; + } + if (t || p >= 0) { + put_bits(&pb, 1, 1); + put_bits(&pb, 32, t); + put_bits(&pb, 32, i); + put_bits(&pb, 1, p); + } else put_bits(&pb, 1, 0); + p = get_bits_count(&gb); + i = get_bits1(&gb); //nal_hrd_parameters_present_flag + if (i) skip_hrd_param(&gb); + t = get_bits1(&gb); //vcl_hrd_parameters_present_flag + if (t) skip_hrd_param(&gb); + if (i || t) skip_bits1(&gb); //low_delay_hrd_flag + p = get_bits_count(&gb) - p; + gb.index -= p; + for (;p > 24;p -= 24) + put_bits(&pb, 24, get_bits(&gb, 24)); + put_bits(&pb, p, get_bits(&gb, p)); + COPYBITS1; //pic_struct_present_flag + t = get_bits1(&gb); //bitstream_restriction_flag + put_bits(&pb, 1, t); + if (t) { + COPYBITS1; + COPYUE; + COPYUE; + COPYUE; + COPYUE; + COPYUE; + COPYUE; + } + } + COPYBITS1; //rbsp trailing + t = get_bits_count(&gb); + flush_put_bits(&pb); + t = (t + 7) >> 3; + i = put_bits_count(&pb) >> 3; + if (insize > t) + memcpy(outbuf + i, inbuf + t, insize - t); + return insize + i - t; +} + +static int find_next_nal_annexb(const uint8_t *src, int *p, int size) +{ + int i = 0; + int j = *p; + for(;j<size;j++) { + uint8_t t = src[j]; + if (i == 2) { + if (t == 1) { + *p = j + 1; + return src[j+1] & 0x1f; + } else if (t) + i = 0; + } else if (t == 0) + i++; + else + i = 0; + } + return 0; +} + +static int h264_changesps_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int keyframe) +{ + H264SPSContext *ctx = bsfc->priv_data; + static const uint8_t profile_level[256] = { + [66] = 1, [77] = 1, [88] = 1, [100] = 1, + [110] = 1, [122] = 1, [144] = 1, [244] = 1, + [10] = 2, [11] = 2, [12] = 2, [13] = 2, [16] = 2, [20] = 2, + [21] = 2, [22] = 2, [30] = 2, [31] = 2, [32] = 2, [40] = 2, + [41] = 2, [42] = 2, [50] = 2, [51] = 2 + }; + uint8_t spsbuf0[64], spsbuf1[64]; // should be big enough + uint32_t spslen = 0; + + *poutbuf = buf; + *poutbuf_size = buf_size; + + if (avctx->codec_id != CODEC_ID_H264) + return 0; + + if (!ctx->state) { + if (parse_args(ctx, args)) ctx->state = 1; + else { + ctx->state = 16; + return 0; + } + } + + /* filter extradata */ + if ((ctx->state & 1) && avctx->extradata && avctx->extradata_size > 5) { + uint8_t *data = avctx->extradata, *ndata; + int i = (data[5] & 0x1f)? 8 : 9; + int l = avctx->extradata_size; + + if (AV_RB16(data)) { + for (;i < l-1;i++) { + if ((data[i] & 0x1f) == 7) { + int t = data[i + 1]; + if (profile_level[t] > 0) break; + } + } + if (i < l-1) { + int r, m, d; + ndata = av_mallocz(l + 16); + i++; + memcpy(ndata, data, i); + d = l; + l = nal_dec(data + i, data + i, l - i) + i; + r = h264_modify(ndata + 8 + i, data + i, ctx, l - i); + r = nal_enc(ndata + i, ndata + 8 + i, r); + d = r + i - d; + avctx->extradata_size = r + i; + av_free(avctx->extradata); + avctx->extradata = ndata; + if (ctx->level != -1) + ndata[3] = ctx->level; + m = (ndata[5] & 0x1f)? 6 : 7; + AV_WB16(ndata + m, AV_RB16(ndata + m) + d); + if (ctx->crop_x != -1) { + avctx->width = ((avctx->width + 15) & ~15) - ctx->crop_x; + avctx->height = ((avctx->height + 15) & ~15) - ctx->crop_y; + } + r = ctx->fps_num; + if (r > 0) { + avctx->time_base.den = r; + avctx->time_base.num = ctx->fps_den; + } + ctx->state = 6; + } + else ctx->state = 2; + } + + } + + // check bitstream type + if (!ctx->bs_type) { + if (buf && buf_size > 5) { + uint32_t t = AV_RB32(buf); + ctx->bs_type = 2; + if (t == 1) + ctx->bs_type = 1; + else if ((t & 0xFFFFFF00) == 0x0100) { + int i = 4; + if (find_next_nal_annexb(buf, &i, buf_size) > 0) + ctx->bs_type = 1; + } + } + else + return 0; + } + + if (ctx->remove_nal_flags || ctx->delay_remove_flags) { + int needmove = 0; + uint32_t i = 0, b = 0; + if (ctx->bs_type == 1) { + uint32_t t, j = 0; + int skip = 0; + while ((t = find_next_nal_annexb(buf, &i, buf_size)) && t != NAL_SLICE && t != NAL_IDR_SLICE) { + if (!skip){ + if (needmove) + memmove(buf + b, buf + j, i - j); + b += i - j; + } + if (ctx->remove_nal_flags & (1<<t)) { + skip = 1; + needmove = 1; + } else { + skip = 0; + if (ctx->delay_remove_flags & (1<<t)) + ctx->remove_nal_flags |= 1<<t; + } + j = i; + } + i = j; + } else { + uint32_t t, r; + ctx->remove_nal_flags |= ctx->delay_remove_flags; + do { + r = AV_RB32(buf+i); + t = buf[i+4] & 0x1f; + if (ctx->remove_nal_flags & (1<<t)) + needmove = 1; + else { + if (needmove) + memmove(buf + b, buf + i, r + 4); + b += r + 4; + } + i += r + 4; + } while(i < buf_size); + } + if (needmove) { + memmove(buf + b, buf + i, buf_size - i); + buf_size -= i - b; + *poutbuf_size = buf_size; + } + } + + if (keyframe) { + int i = 0; + uint32_t t, r, b; + if (ctx->bs_type == 1) { + while (t = find_next_nal_annexb(buf, &i, buf_size)) { + if (t == NAL_SPS) { + b = i + 1; + if (!find_next_nal_annexb(buf, &i, buf_size)) + break; + for(i-=2;!buf[i];i--); + spslen = i - b + 1; + break; + } else if (t == NAL_SLICE || t == NAL_IDR_SLICE) + break; + } + } else { + do { + r = AV_RB32(buf + i) - 1; + t = buf[i+4] & 0x1f; + if (t == NAL_SPS) { + spslen = r; + b = i + 5; + break; + } + else if (t == NAL_SLICE || t == NAL_IDR_SLICE) + break; + i += r + 5; + } while(i < buf_size); + } + + if (spslen) { + r = nal_dec(spsbuf0, buf + b, spslen); + r = h264_modify(spsbuf1, spsbuf0, ctx, r); + r = nal_enc(spsbuf0, spsbuf1, r); + ctx->state |= 8; + if (spslen == r) + memcpy(buf + b, spsbuf0, spslen); + else if (r < spslen) { + *poutbuf_size = buf_size + r - spslen; + if (ctx->bs_type == 2) + AV_WB32(buf + b - 5, r + 1); + memcpy(buf + b, spsbuf0, r); + memmove(buf + b + r - 1, buf + b + spslen - 1, buf_size - b - spslen); + } else { + *poutbuf = av_mallocz(buf_size + r - spslen); + *poutbuf_size = buf_size + r - spslen; + memcpy(*poutbuf, buf, b); + if (ctx->bs_type == 2) + AV_WB32(*poutbuf + b - 5, r + 1); + memcpy(*poutbuf + b, spsbuf0, r); + memcpy(*poutbuf + b + r - 1, buf + b + spslen - 1, buf_size - b - spslen); + return 1; + } + } + } + + return 0; +} + +AVBitStreamFilter ff_h264_changesps_bsf = { + "h264_changesps", + sizeof(H264SPSContext), + h264_changesps_filter, +}; -- 1.9.2.msysgit.0
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel