2015-04-21 3 views
0

Я пытаюсь кодировать входные изображения с встроенной камеры FaceTime HD камеры MacBook Pro в видеопоток H.264 в реальном времени, используя libx264 в Mac OS X 10.9.5.Как кодировать входные изображения с камеры в поток H.264?

Ниже приведены шаги, которые я взял:

  1. Get 1280x720 32BGRA изображения с камеры на 15fps, используя AVFoundation API (AVCaptureDevice класс и т.д.)
  2. Преобразование изображения в 320x180 YUV420P формате с использованием libswscale.
  3. Кодировать изображения в видеопоток H.264 (базовый профиль) с помощью libx264.

Я применяю вышеуказанные шаги каждый раз, когда изображение получается из камеры, полагая, что кодер отслеживает состояние кодирования и генерирует блок NAL, когда он доступен.

Как я хотел получить кодированные кадры, предоставляя входные изображения кодеру, я решил сбросить кодировщик (вызывая x264_encoder_delayed_frames()) каждые 30 кадров (2 секунды).

Однако, когда я перезапускаю кодировку, кодер останавливается через некоторое время (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 (encoder); и initEncoder(); – kuu

ответ

1

Вы не должны смывать отсроченные кадры после каждого кадра, но только один раз, когда больше нет ввода не кадры, т.е. в конце кодирования.

+0

Спасибо, что указали на неправильное использование API. Я изменил код и переместил часть с задержкой кадров в отдельной функции (flushEncoder.) И я заставил функцию вызываться каждые 25 кадров. Теперь я могу получить 28 единиц NAL для первых 25 кадров, состоящих из 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(), но после вызова x264_encoder_encode() с флагом NULL для флеш-кадров, потому что он останавливает поток решений lookahead/slicetype (потому что это сигнал, что больше нет входных кадров), и после этого он становятся меньше смысла отправлять реальные кадры в encoder_encode(), потому что они никогда не будут закодированы и будут только увеличивать число delayed_frames (вот почему для вас бесконечный цикл). Как уже говорилось, вы должны очищать фреймы только один раз в конце до x264_encoder_close(). – nobody555

Смежные вопросы