使用FFmpeg开发2-比特流过滤器

在使用FFmpeg处理视频文件时,会遇到需要提取视频流的情况。例如有一个H264编码的mp4文件,需要提取帧数据送到解码器解码,这时候过滤器就派上用场了。

为什么要这么干,因为我用的是自有的硬件解码器,还未集成到FFmpeg中,没法在FFmpeg中直接调用。

对于H264裸流文件,一般使用的AnnexB格式,每一帧由NAL单元加上一个起始码组成(三字节00 00 01或者四字节00 00 00 01)。解码器就是根据这个头来分割数据的。

在mp4(H264编码)文件中,一般使用的是AVVC格式,因为封装了同步信息,就去掉了上述的起始码,如果见数据直接丢到解码器,解码器是没法找到数据头进行解码的,因此需要过滤器来为数据加上这个起始码。h264_mp4toannexb码流过滤器就解决了这个问题。它识别文件头,并提取里面的extradata,extradata这个数据就是封装信息。然后给每个NAL单元添加上起始码,再把数据送到硬件解码器。

对于mp4(H265编码)文件,则使用hevc_mp4toannexb。

在wrap的使用博文中介绍了裸流解码,这时候如果搭配了FFmpeg的过滤器,即可以实现读取mp4文件,通过过滤器分离出裸H264数据,然后送到解码器解码,实现对封装数据的解码。

imx VPU解码分析3-wrap的示例-CSDN博客

这里给出一个实现参考,从imx的hantro中抠出来的,从中也可以一窥用法,支持的FFmpeg版本比较旧,有时间再整理下新版的代码。貌似新版的FFmpeg将过滤器集成到了读入视频的过程,不需要额外处理了。

cpp 复制代码
/*------------------------------------------------------------------------------
--       Copyright (c) 2015-2017, VeriSilicon Inc. All rights reserved        --
--         Copyright (c) 2011-2014, Google Inc. All rights reserved.          --
--         Copyright (c) 2007-2010, Hantro OY. All rights reserved.           --
--                                                                            --
-- This software is confidential and proprietary and may be used only as      --
--   expressly authorized by VeriSilicon in a written licensing agreement.    --
--                                                                            --
--         This entire notice must be reproduced on all copies                --
--                       and may not be removed.                              --
--                                                                            --
--------------------------------------------------------------------------------
-- Redistribution and use in source and binary forms, with or without         --
-- modification, are permitted provided that the following conditions are met:--
--   * Redistributions of source code must retain the above copyright notice, --
--       this list of conditions and the following disclaimer.                --
--   * Redistributions in binary form must reproduce the above copyright      --
--       notice, this list of conditions and the following disclaimer in the  --
--       documentation and/or other materials provided with the distribution. --
--   * Neither the names of Google nor the names of its contributors may be   --
--       used to endorse or promote products derived from this software       --
--       without specific prior written permission.                           --
--------------------------------------------------------------------------------
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"--
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  --
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE --
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE  --
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR        --
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF       --
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS   --
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN    --
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)    --
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE --
-- POSSIBILITY OF SUCH DAMAGE.                                                --
--------------------------------------------------------------------------------
------------------------------------------------------------------------------*/

#include "libav-wrapper.h"

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavcodec/bsf.h"
#include "libavutil/mem.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static AVFormatContext *p_format_ctx = NULL;
static AVCodecContext *p_codec_ctx = NULL;
static AVCodecParameters *origin_par = NULL;
//gan 20230907 AVBitStreamFilterContext is instand by AVBSFContext
//static AVBitStreamFilterContext *bsfc = NULL;
static AVBSFContext *bsfc = NULL;

static int video_stream = -1;
static int raw_h264 = 0;

uint8_t *byte_buffer = NULL;
int byte_buffer_size = 0;
static int num;
void libav_init() {
  //av_register_all();

  //av_log(NULL, AV_LOG_INFO, "Using %s\nConfig: %s\n\n", LIBAVCODEC_IDENT,
  //       avcodec_configuration());
}

void libav_release() {
  if (bsfc)
    //av_bitstream_filter_close(bsfc);
    av_bsf_free(bsfc);
  if (p_format_ctx)
    avformat_close_input(&p_format_ctx);
}

int libav_open(const char *fname) {
  printf("libav_open\n");
  num = 1;
  /* Open video file */
  if (avformat_open_input(&p_format_ctx, fname, NULL, NULL) != 0) {
    av_log(NULL, AV_LOG_ERROR, "Couldn't open file!\n");
    return -1;
  }

  /* Retrieve stream information */
  if (avformat_find_stream_info(p_format_ctx, NULL) < 0) {
    /* this is not fatal error, yet */
    av_log(NULL, AV_LOG_ERROR, "Couldn't find stream information!\n");
  }

  /* Dump information about file onto standard error */
  av_dump_format(p_format_ctx, 0, fname, 0);

  /* Find the video stream */
  video_stream = av_find_best_stream(p_format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

  if (video_stream < 0) {
    av_log(NULL, AV_LOG_ERROR, "Didn't find a video stream!\n");
    return -1;
  }
  origin_par = p_format_ctx->streams[video_stream]->codecpar;
  /* Get a pointer to the codec context for the video stream */
  //p_codec_ctx = p_format_ctx->streams[video_stream]->codec;
  p_codec_ctx = avcodec_alloc_context3(avcodec_find_decoder(origin_par->codec_id));  
  avcodec_parameters_to_context(p_codec_ctx, origin_par); 
  printf("p_format_ctx->iformat->long_name,%s\n", p_format_ctx->iformat->long_name);
  if (!av_strcasecmp(p_format_ctx->iformat->long_name, "raw H.264 video")) {
    raw_h264 = 1;
    printf("raw_h264\n");
  } else {
    /* have to use a filter to get the byte stream out */
    //bsfc = av_bitstream_filter_init("h264_mp4toannexb");
    printf("not h264,have to filter\n");
    const AVBitStreamFilter *pfilter = av_bsf_get_by_name("h264_mp4toannexb");
    av_bsf_alloc(pfilter, &bsfc);
    if (!bsfc) {
      av_log(p_codec_ctx, AV_LOG_ERROR,
             "Couldn't open the h264_mp4toannexb BSF!\n");
      return -1;
    }
  }

  //byte_buffer_size = av_image_get_buffer_size(p_codec_ctx->pix_fmt, p_codec_ctx->width, p_codec_ctx->height, 16);
  //byte_buffer = av_malloc(byte_buffer_size);
  //printf("**byte_buffer_size=%d\n", byte_buffer_size);
  return 0;
}

//return -1 ,over;0, not video;other, ok 
int libav_read_frame(char *buffer) {
  //printf("libav_read_frame\n");

  int ret;
  uint8_t *frame_data = NULL;  // Initialize to NULL

  AVPacket *pkt = av_packet_alloc();
  pkt->data = NULL;
  pkt->size = 0;
  ret = av_read_frame(p_format_ctx, pkt);
  //read over
  if (ret != 0) {
    return 0;
  }
  for(int i=0;i<pkt->size;i++)
    printf("%2d ", *(pkt->data+i));

  if(ret == 0 && pkt->stream_index == video_stream){
    
    printf("av_read_frame, num=%d, pkt->size=%d\n", num++, pkt->size);

    if (!raw_h264) {
      uint8_t *orig_extradata = NULL;
      int orig_extradata_size = 0;

      orig_extradata_size = p_codec_ctx->extradata_size;
      orig_extradata = av_mallocz(orig_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
      memcpy(orig_extradata, p_codec_ctx->extradata, orig_extradata_size);

      //ret = av_bitstream_filter_filter(bsfc, p_codec_ctx, NULL, &frame_data, &frame_size, pkt->data, pkt->size, 0);
      av_bsf_send_packet(bsfc, pkt);
      printf("av_read_frame, 1, pkt->size=%d\n", pkt->size);
      ret = av_bsf_receive_packet(bsfc, pkt);
      printf("av_read_frame, 2, pkt->size=%d\n", pkt->size);
      for(int i=0;i<pkt->size;i++)
        printf("%2d ", *(pkt->data+i));

      if (p_codec_ctx->extradata == NULL) {
        p_codec_ctx->extradata = orig_extradata;
        p_codec_ctx->extradata_size = orig_extradata_size;
      } else {
        av_free(orig_extradata);
      }
      av_bsf_free(&bsfc);
    }

    memcpy(buffer, pkt->data, pkt->size);

    return pkt->size;
  }
  else{
    printf("av_read_frame, no video\n");
    return 0;
  }
}

从中可以看出主要是 这二个函数:

av_bsf_send_packet(bsfc, pkt);

av_bsf_receive_packet(bsfc, pkt);

av_bitstream_filter_filter函数已经被废弃。

在4.2版本上改改测试了下,是可行的。后期再试试新版。

如果不想使用FFmepg来读取H264裸流文件,可以参考这篇博文,使用纯C简单实现。

纯C读取文件实现解析H264裸流每一帧数据-CSDN博客

相关推荐
扶尔魔ocy7 小时前
【QT window】ffmpeg实现录音功能之无损格式--PCM
ffmpeg·pcm
止礼8 小时前
FFmpeg8.0.1 源代码的深入分析
ffmpeg
小曾同学.com9 小时前
音视频中的“透传”与“DTS音频”
ffmpeg·音视频·透传·dts
vivo互联网技术9 小时前
数字人动画云端渲染方案
前端·ffmpeg·puppeteer·web3d
止礼10 小时前
FFmpeg8.0.1 编解码流程
ffmpeg
qs701611 小时前
c直接调用FFmpeg命令无法执行问题
c语言·开发语言·ffmpeg
止礼11 小时前
FFmpeg8.0.1 Mac环境 CMake本地调试配置
macos·ffmpeg
简鹿视频1 天前
视频转mp4格式具体作步骤
ffmpeg·php·音视频·实时音视频