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

news/2024/7/10 19:12:08 标签: ffmpeg, 视频编解码

        在使用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将过滤器集成到了读入视频的过程,不需要额外处理了。

/*------------------------------------------------------------------------------
--       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版本上改改测试了下,是可行的。后期再试试新版。


http://www.niftyadmin.cn/n/5226471.html

相关文章

Linux下Docker 离线安装详细步骤,亲测成功

1.离线原因&#xff1a;公司新创不能使用开元linux&#xff0c;使用了一个变种centOS&#xff0c;致使yum被禁 2.步骤&#xff1a; 2.1 下载docker tar包&#xff0c;下载地址&#xff1a;Index of linux/https://download.docker.com/linux/ 2.2 新建自己的软件目录&am…

Golang WebSocket 心跳

WebSocket是一种在客户端和服务器之间实现全双工通信的协议&#xff0c;它允许实时地传输数据&#xff0c;并且比传统的HTTP请求更加高效。在使用Golang构建WebSocket应用程序时&#xff0c;一个重要的考虑因素是如何实现心跳机制&#xff0c;以确保连接的稳定性和可靠性。本文…

【物联网与大数据应用】Hadoop数据处理

Hadoop是目前最成熟的大数据处理技术。Hadoop利用分而治之的思想为大数据提供了一整套解决方案&#xff0c;如分布式文件系统HDFS、分布式计算框架MapReduce、NoSQL数据库HBase、数据仓库工具Hive等。 Hadoop的两个核心解决了数据存储问题&#xff08;HDFS分布式文件系统&#…

目标检测——R-CNN算法解读

论文&#xff1a;Rich feature hierarchies for accurate object detection and semantic segmentation 作者&#xff1a;Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik 链接&#xff1a;https://arxiv.org/abs/1311.2524 代码&#xff1a;http://www.cs.berke…

西工大网络空间安全学院计算机系统基础实验一(9, 10, 11, 12, 13)

还是那句话&#xff0c;专心做好你自己的&#xff0c;老老实实把基础打好&#xff0c;不要被其他人带跑节奏&#xff0c;不要跟他打&#xff0c;跟着这系列博客&#xff0c;稳扎稳打一步一步来。即使你VMware workstation没下载好&#xff0c;即使你Ubuntu虚拟机没配好&#xf…

wvp如果确认音频udp端口开放成功

用到工具 在服务器上开启端口监听 选中udp server&#xff0c;点击创建按钮 设置服务器监听端口 在客户端连接服务器端口 选中udp客户端&#xff0c;点击创建 输入服务器地址 远程端口和本地端口&#xff0c;本地端口只要没被占用都可以使用 &#xff0c;点击确认 发送数据 …

开源播放器GSYVideoPlayer + ViewPager2 源码解析

开源播放器GSYVideoPlayer ViewPager2 源码解析 前言一、GSYVideoPlayer&#x1f525;&#x1f525;&#x1f525;是什么&#xff1f;二、源码解析1.ViewPager2Activity 总结 前言 本文介绍GSYVideoPlayer源码中关于ViewPager2 GSYVideoPlayer 实现的滑动播放列表的实现原理。…

企业微信http协议接口调用,根据手机号搜索联系人

产品说明 一、 hook版本&#xff1a;企业微信hook接口是指将企业微信的功能封装成dll&#xff0c;并提供简易的接口给程序调用。通过hook技术&#xff0c;可以在不修改企业微信客户端源代码的情况下&#xff0c;实现对企业微信客户端的功能进行扩展和定制化。企业微信hook接口…