2015-04-21 81 views
0

我试图使用Mac OS X 10.9.5上的libx264实时将来自MacBook Pro内置FaceTime高清摄像头的输入图像实时编码为H.264视频流。如何将来自摄像机的输入图像编码为H.264流?

下面是我所采取的步骤:在15fps的使用AVFoundation API(AVCaptureDevice类等)

  • 使用libswscale将图像转换成看见320×180 YUV420P格式

    1. 获取1280×720从相机32BGRA图像。
    2. 使用libx264将图像编码为H.264视频流(基线配置文件)。

    我每次从相机获取图像时都会应用上述步骤,相信编码器会跟踪编码状态并在可用时生成NAL单元。因为我希望在向编码器提供输入图像的同时获得编码帧,所以我决定每隔30帧(2秒)刷新一次编码器(调用x264_encoder_delayed_frames())以调用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); 
         } 
        } 
        } 
    } 
    
  • +0

    在flushEncoder()的末尾插入两行后,上面的代码就开始工作了。我添加了x264_encoder_close(编码器);和initEncoder(); – kuu

    回答

    1

    当不存在更多输入帧时,即在编码结束时,您不应该在每帧之后刷新延迟帧,而只能刷新一次。

    +0

    感谢您指出API的滥用情况。我已经改变了代码,并将冲洗延迟帧部分放在一个单独的函数中(flushEncoder)。并且我让函数每25帧调用一次。现在,我可以在前25帧中获得28个NAL单元,其中包括SPS,PPS,SEI和25个切片(1个IDR + 24个其他切片)。但是,一旦我开始对接下来的25个帧进行编码,编码器将停止并且x264_encoder_encode ()永远不会返回。你能再看看更新的代码吗? – kuu

    +0

    啊,每当我刷新编码器时,我改变了重置编码器的代码(通过调用x264_encoder_close()和x264_encoder_open()),问题就消失了。我已经知道,一旦我调用x264_encoder_delayed_frames(),编码器就没用了。谢谢。 – kuu

    +1

    不是在调用x264_encoder_delayed_frames()之后,而是在用NULL帧调用x264_encoder_encode()之后刷新帧,因为它停止了lookahead/slicetype判断线程(因为它表示没有更多输入帧),并且在此之后它变得“无用”因为它们永远不会被编码,并且只会增加delayed_frames数量(这就是为什么你会无限循环的原因),所以变成将真实帧发送到encoder_encode()的意义更小。如前所述,您应该在x264_encoder_close()前最后一次刷新帧。 – nobody555