• av_buffersrc_add_frame使用分析


    ffmpeg——av_buffersrc_add_frame分析

    一、函数功能

    向滤镜源中添加一个frame,源码摘录如下:

    /**
     * Add a frame to the buffer source.
     *
     * @param ctx   an instance of the buffersrc filter
     * @param frame frame to be added. If the frame is reference counted, this
     * function will take ownership of the reference(s) and reset the frame.
     * Otherwise the frame data will be copied. If this function returns an error,
     * the input frame is not touched.
     *
     * @return 0 on success, a negative AVERROR on error.
     *
     * @note the difference between this function and av_buffersrc_write_frame() is
     * that av_buffersrc_write_frame() creates a new reference to the input frame,
     * while this function takes ownership of the reference passed to it.
     *
     * This function is equivalent to av_buffersrc_add_frame_flags() without the
     * AV_BUFFERSRC_FLAG_KEEP_REF flag.
     */
    av_warn_unused_result
    int av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame);
    
    /**
     * Add a frame to the buffer source.
     *
     * By default, if the frame is reference-counted, this function will take
     * ownership of the reference(s) and reset the frame. This can be controlled
     * using the flags.
     *
     * If this function returns an error, the input frame is not touched.
     *
     * @param buffer_src  pointer to a buffer source context
     * @param frame       a frame, or NULL to mark EOF
     * @param flags       a combination of AV_BUFFERSRC_FLAG_*
     * @return            >= 0 in case of success, a negative AVERROR code
     *                    in case of failure
     */
    av_warn_unused_result
    int av_buffersrc_add_frame_flags(AVFilterContext *buffer_src,
                             AVFrame *frame, int flags);

    同时提供的还有个相似的接口av_buffersrc_add_frame_flags,可以指定调用的接口的方式,第三个参数(flags)的类型有下面几种:

    enum {
        
    
       	/**
            * Do not check for format changes.
            */
             AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT = 1,
    
       	/**
            * Immediately push the frame to the output.
            */
             AV_BUFFERSRC_FLAG_PUSH = 4,
    
       	/**
            * Keep a reference to the frame.
            * If the frame if reference-counted, create a new reference; otherwise
            * copy the frame data.
            */
             AV_BUFFERSRC_FLAG_KEEP_REF = 8,
    
    };

    二、使用场景

    对音频或者视频数据解码后,调用该接口,把frame添加进滤镜源,然后由滤镜对数据做一些混合或者裁剪工作。示例代码如下:

       ret = avcodec_decode_audio4(p**AudioCodecCtx, p**Frame, &frameFinished, &packet);
       if (ret < 0) {
        
          LOGD("Decoding failed ret = %d, %s", ret, av_err2str(ret));
       }
       if (frameFinished) {
        
          LOGD("packet pts = %" PRId64", dts = %" PRId64", duration = %d, pos = %" PRId64", stream_index = %d, flags = %d, size = %d",
              packet.pts, packet.dts, packet.duration, packet.pos, packet.stream_index, packet.flags, packet.size);
    
          LOGD("pFrame nb_samples = %d, channel_layout = %" PRId64", channels = %d, format(sample_fmt) = %d, sample_rate = %d, pts = %" PRId64"",
              pFrame->nb_samples, pFrame->channel_layout, pFrame->channels, pFrame->format, pFrame->sample_rate, pFrame->pts);
    
          pFrame->pts = av_frame_get_best_effort_timestamp(pFrame);
          ret = av_buffersrc_add_frame(buffersrc_ctx[0], pFrame);
    //	ret = av_buffersrc_add_frame_flags(buffersrc_ctx[0], pFrame, AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT);
          if (ret < 0) {
        
             LOGD("Error while feeding the filtergraph 0 ret = %d, %s", ret, av_err2str(ret));
          } else {
        
             LOGD_DEBUG("av_buffersrc_add_frame 0 success mediatype = %d", mediatype);
          }
          av_packet_unref(&packet);
          break;
       }

    三、源码分析

    av_buffersrc_add_frame内部调用av_buffersrc_add_frame_flags,且不指定任何flags。

    int attribute_align_arg av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame)
    {
        
       return av_buffersrc_add_frame_flags(ctx, frame, 0);
    }

    av_buffersrc_add_frame_flags函数实现如下:

    int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
    {
        
       AVFrame *copy = NULL;
       int ret = 0;
    
       if (frame && frame->channel_layout &&
          av_get_channel_layout_nb_channels(frame->channel_layout) != frame->channels) {
        
          av_log(ctx, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n");
          return AVERROR(EINVAL);
       }
    
       if (!(flags & AV_BUFFERSRC_FLAG_KEEP_REF) || !frame)
          return av_buffersrc_add_frame_internal(ctx, frame, flags);
    
       if (!(copy = av_frame_alloc()))
          return AVERROR(ENOMEM);
       ret = av_frame_ref(copy, frame);
       if (ret >= 0)
          ret = av_buffersrc_add_frame_internal(ctx, copy, flags);
    
       av_frame_free(&copy);
       return ret;
    }

    可以看出,如果指定了AV_BUFFERSRC_FLAG_KEEP_REF方式,会拷贝一份帧数据进行处理,否则直接使用原来帧数据进行处理。同时调用了av_buffersrc_add_frame_internal接口进行操作。

    static int av_buffersrc_add_frame_internal(AVFilterContext *ctx,
                                     AVFrame *frame, int flags)
    {
        
       BufferSourceContext *s = ctx->priv;
       AVFrame *copy;
       int refcounted, ret;
    
       s->nb_failed_requests = 0;
    
       if (!frame)
          return av_buffersrc_close(ctx, AV_NOPTS_VALUE, flags);
       if (s->eof)
          return AVERROR(EINVAL);
    
       refcounted = !!frame->buf[0];
    
       if (!(flags & AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT)) {
        
    
          switch (ctx->outputs[0]->type) {
        
             case AVMEDIA_TYPE_VIDEO:
                CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height,
                                   frame->format, frame->pts);
                break;
             case AVMEDIA_TYPE_AUDIO:
                /* For layouts unknown on input but known on link after negotiation. */
                if (!frame->channel_layout)
                   frame->channel_layout = s->channel_layout;
                CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
                                   frame->channels, frame->format, frame->pts);
                break;
             default:
                return AVERROR(EINVAL);
          }
    
       }
    
       if (!av_fifo_space(s->fifo) &&
          (ret = av_fifo_realloc2(s->fifo, av_fifo_size(s->fifo) +
                                   sizeof(copy))) < 0)
          return ret;
    
       if (!(copy = av_frame_alloc()))
          return AVERROR(ENOMEM);
    
       if (refcounted) {
        
          av_frame_move_ref(copy, frame);
       } else {
        
          ret = av_frame_ref(copy, frame);
          if (ret < 0) {
        
             av_frame_free(&copy);
             return ret;
          }
       }
    
       if ((ret = av_fifo_generic_write(s->fifo, &copy, sizeof(copy), NULL)) < 0) {
        
          if (refcounted)
             av_frame_move_ref(frame, copy);
          av_frame_free(&copy);
          return ret;
       }
    
       if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0)
          return ret;
    
       if ((flags & AV_BUFFERSRC_FLAG_PUSH)) {
        
          ret = push_frame(ctx->graph);
          if (ret < 0)
             return ret;
       }
    
       return 0;
    }

    如果未指定AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT方式,该函数会对frame的参数进行校验,否则不进行校验,最后添加进过滤镜上下文(AVFilterContext)的待处理缓存中。如果指定了AV_BUFFERSRC_FLAG_PUSH方式,会紧接着交由滤镜滤波器(filtergraph)进行处理。

    AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT的使用注意事项可以参考:https://blog.csdn.net/Martin_chen2/article/details/100052276

  • 相关阅读:
    docker1
    Ubuntu中安装deb包程序
    Linux性能评测工具之一:gprof篇介绍
    Lua在Linux下的安装
    gprof的使用介绍
    Linux性能评测工具之一:gprof篇
    google-perftools 分析JAVA 堆外内存
    NetHogs下载和监控
    Google perf tools for nginx
    ECLIPSE中添加TPTP插件
  • 原文地址:https://www.cnblogs.com/lidabo/p/15955200.html
Copyright © 2020-2023  润新知