如何将输入图像从摄像机编码为​​H.264流?

时间:2015-04-21 13:07:40

标签: h.264 x264 swscale

我正在尝试使用Mac OS X 10.9.5上的libx264将来自MacBook Pro内置FaceTime HD Camera的输入图像实时编码为H.264视频流。

以下是我采取的步骤:

  1. 使用AVFoundation API(AVCaptureDevice类等)以15fps从相机获取1280x720 32BGRA图像
  2. 使用libswscale将图像转换为320x180 YUV420P格式。
  3. 使用libx264将图像编码为H.264视频流(基线配置文件)。
  4. 每次从相机获取图像时,我都应用上述步骤,相信编码器会跟踪编码状态,并在可用时生成NAL单元。

    由于我想在向编码器提供输入图像的同时获取编码帧,我决定每隔30帧(2秒)刷新编码器(调用x264_encoder_delayed_frames())。

    然而,当我重新开始编码时,编码器会在一段时间后停止(x264_encoder_encode()永远不会返回。)我尝试在刷新前更改帧数,但情况没有改变。

    以下是相关代码(我省略了图像捕获代码,因为它看起来没问题。)

    你能指出我可能做错的事吗?

    x264_t *encoder;
    x264_param_t param;
    
    // Will be called only first time.
    int initEncoder() {
      int ret;
    
      if ((ret = x264_param_default_preset(&param, "medium", NULL)) < 0) {
        return ret;
      }
    
      param.i_csp = X264_CSP_I420;
      param.i_width  = 320;
      param.i_height = 180;
      param.b_vfr_input = 0;
      param.b_repeat_headers = 1;
      param.b_annexb = 1;
    
      if ((ret = x264_param_apply_profile(&param, "baseline")) < 0) {
        return ret;
      }
    
      encoder = x264_encoder_open(&param);
      if (!encoder) {
        return AVERROR_UNKNOWN;
      }
    
      return 0;
    }
    
    // Will be called from encodeFrame() defined below.
    int convertImage(const enum AVPixelFormat srcFmt, const int srcW, const int srcH, const uint8_t *srcData, const enum AVPixelFormat dstFmt, const int dstW, const int dstH, x264_image_t *dstData) {
      struct SwsContext *sws_ctx;
      int ret;
      int src_linesize[4];
      uint8_t *src_data[4];
    
      sws_ctx = sws_getContext(srcW, srcH, srcFmt,
                           dstW, dstH, dstFmt,
                           SWS_BILINEAR, NULL, NULL, NULL);
    
      if (!sws_ctx) {
        return AVERROR_UNKNOWN;
      }
    
      if ((ret = av_image_fill_linesizes(src_linesize, srcFmt, srcW)) < 0) {
        sws_freeContext(sws_ctx);
        return ret;
      }
    
      if ((ret = av_image_fill_pointers(src_data, srcFmt, srcH, (uint8_t *) srcData, src_linesize)) < 0) {
        sws_freeContext(sws_ctx);
        return ret;
      }
    
      sws_scale(sws_ctx, (const uint8_t * const*)src_data, src_linesize, 0, srcH, dstData->plane, dstData->i_stride);
      sws_freeContext(sws_ctx);
      return 0;
    }
    
    // Will be called for each frame.
    int encodeFrame(const uint8_t *data, const int width, const int height) {
      int ret;
      x264_picture_t pic;
      x264_picture_t pic_out;
      x264_nal_t *nal;
      int i_nal;
    
      if ((ret = x264_picture_alloc(&pic, param.i_csp, param.i_width, param.i_height)) < 0) {
        return ret;
      }
    
      if ((ret = convertImage(AV_PIX_FMT_RGB32, width, height, data, AV_PIX_FMT_YUV420P, 320, 180, &pic.img)) < 0) {
        x264_picture_clean(&pic);
        return ret;
      }
    
      if ((ret = x264_encoder_encode(encoder, &nal, &i_nal, &pic, &pic_out)) < 0) {
        x264_picture_clean(&pic);
        return ret;
      }
    
      if(ret) {
        for (int i = 0; i < i_nal; i++) {
          printNAL(nal + i);
        }
      }
    
      x264_picture_clean(&pic);
      return 0;
    }
    
    // Will be called every 30 frames.
    int flushEncoder() {
      int ret;
      x264_nal_t *nal;
      int i_nal;
      x264_picture_t pic_out;
    
      /* Flush delayed frames */
      while (x264_encoder_delayed_frames(encoder)) {
        if ((ret = x264_encoder_encode(encoder, &nal, &i_nal, NULL, &pic_out)) < 0) {
          return ret;
        }
    
        if (ret) {
          for (int j = 0; j < i_nal; j++) {
            printNAL(nal + j);
          }
        }
      }
    }
    

1 个答案:

答案 0 :(得分:1)

你不应该在每一帧之后刷新延迟的帧,但是当没有更多的输入帧时,即在编码结束时,不应该只刷一次。