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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章