2010-05-30 2 views
56

RBG 이미지를 H264 프레임으로 인코딩하는 데 x264 C API를 사용하는 방법은 무엇입니까? 이미 RBG 이미지 시퀀스를 만들었는데, 어떻게 그 시퀀스를 H264 프레임 시퀀스로 변환 할 수 있습니까? 특히, 어떻게이 일련의 RGB 이미지를 하나의 초기 H264 키 프레임과 종속 H264 프레임으로 구성된 H264 프레임 시퀀스로 인코딩합니까?x264 C API를 사용하여 일련의 이미지를 H264로 인코딩하는 방법은 무엇입니까?

답변

88

우선 : x264.h 파일을 확인하십시오. 각 기능 및 구조에 대한 참조가 다소 포함되어 있습니다. 다운로드에서 찾을 수있는 x264.c 파일에는 샘플 구현이 포함되어 있습니다. 대부분의 사람들은 자신을 기초로 말하지만, 초보자에게는 다소 복잡하다는 것을 알지만, 다시 낙담하는 것은 좋은 예입니다.

먼저 x264_param_t 유형의 일부 매개 변수를 설정하십시오. 매개 변수를 설명하는 좋은 사이트는 http://mewiki.project357.com/wiki/X264_Settings입니다. x264_param_default_preset 함수를 살펴보면 (때로는 상당히 복잡한) 모든 매개 변수를 이해할 필요없이 일부 기능을 대상으로 삼을 수 있습니다.

x264_t* encoder = x264_encoder_open(&param); 
x264_picture_t pic_in, pic_out; 
x264_picture_alloc(&pic_in, X264_CSP_I420, w, h) 
을 다음과 같이 인코더를 초기화 할 수 있습니다 이후

x264_param_t param; 
x264_param_default_preset(&param, "veryfast", "zerolatency"); 
param.i_threads = 1; 
param.i_width = width; 
param.i_height = height; 
param.i_fps_num = fps; 
param.i_fps_den = 1; 
// Intra refres: 
param.i_keyint_max = fps; 
param.b_intra_refresh = 1; 
//Rate control: 
param.rc.i_rc_method = X264_RC_CRF; 
param.rc.f_rf_constant = 25; 
param.rc.f_rf_constant_max = 35; 
//For streaming: 
param.b_repeat_headers = 1; 
param.b_annexb = 1; 
x264_param_apply_profile(&param, "baseline"); 

: 또한

이 내 코드에서 몇 가지 예제 설정이다 (당신은 아마 "기준"프로필을 할 것입니다) 이후 x264_param_apply_profile를 사용

X264는 YUV420P 데이터를 예상합니다 (다른 사람도 같지만 공통적 인 부분입니다). libswscale (ffmpeg에서)을 사용하여 이미지를 올바른 형식으로 변환 할 수 있습니다. 이것을 초기화하는 것은 (24bpp의 RGB 데이터를 가정합니다).

struct SwsContext* convertCtx = sws_getContext(in_w, in_h, PIX_FMT_RGB24, out_w, out_h, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL); 

인코딩이 각 프레임을 위해 할,이만큼 간단하다 :

//data is a pointer to you RGB structure 
int srcstride = w*3; //RGB stride is just 3*width 
sws_scale(convertCtx, &data, &srcstride, 0, h, pic_in.img.plane, pic_in.img.stride); 
x264_nal_t* nals; 
int i_nals; 
int frame_size = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out); 
if (frame_size >= 0) 
{ 
    // OK 
} 

나는 이것이 당신이 가야 희망), 나 자신 시작에에 긴 시간을 보냈다. X264는 강력하지만 때로는 복잡한 소프트웨어입니다.

편집 : 다른 매개 변수를 사용하면 지연된 프레임이 생길 수 있습니다. 이는 내 매개 변수와 관련이 없습니다 (주로 nolatency 옵션 때문). 이 경우 frame_size가 0이 될 수 있으므로 x264_encoder_delayed_frames 함수가 0을 반환하지 않는 한 x264_encoder_encode을 호출해야합니다.하지만이 기능을 사용하려면 x264.c 및 x264.h를 자세히 살펴야합니다.

+7

매우 도움이됩니다 (+1). 파이썬 커뮤니티는 실제로이 C 스타일 코드를 추상화하는 래퍼가 필요합니다. –

+1

XBMC를 일반 미디어 클라이언트로 스트리밍하거나 AVI 스트림으로 쉽게 감쌀 수 있습니까? – dascandy

+0

DirectShow 소스 필터를 작성할 수 있습니다. AVI가 H.264를위한 최상의 컨테이너 선택이 아닙니다. http://en.wikipedia.org/wiki/Comparison_of_container_formats – fishfood

5

원시 yuv 프레임을 생성 한 다음 x264를 사용하여 인코딩하는 예제를 업로드했습니다. 전체 코드는 여기에서 찾을 수 있습니다 : https://gist.github.com/roxlu/6453908

+4

당신의 솔루션 요약을 여기에 추가하여 링크 수명보다 오래 살 수 있습니다. – krsteeve

2

는 FFmpeg은 실행 가능한 예를 2.8.6가에 대한 균일 한 API를 노출로 x264의 래퍼가, 좋은 생각으로 FFpmeg를 사용

다중 인코더. 따라서 형식을 변경해야하는 경우 새 API를 배우는 대신 하나의 매개 변수 만 변경할 수 있습니다.

이 예제는 generate_rgb에 의해 생성 된 일부 화려한 프레임을 합성하고 인코딩합니다.

프레임 유형 (I, P, B)을 가능한 한 적은 키 프레임 (이상적으로 첫 번째 프레임 만)로 제어하는 ​​방법은 여기에서 설명합니다. https://stackoverflow.com/a/36412909/895245 여기에 언급했듯이 대부분의 응용 프로그램에는 권장하지 않습니다.

여기에 프레임 유형 컨트롤을 키 - 라인

은 다음과 같습니다

/* Minimal distance of I-frames. This is the maximum value allowed, 
or else we get a warning at runtime. */ 
c->keyint_min = 600; 

과 :

if (frame->pts == 1) { 
    frame->key_frame = 1; 
    frame->pict_type = AV_PICTURE_TYPE_I; 
} else { 
    frame->key_frame = 0; 
    frame->pict_type = AV_PICTURE_TYPE_P; 
} 

우리는 다음 프레임 유형으로 확인할 수 있습니다

ffprobe -select_streams v \ 
    -show_frames \ 
    -show_entries frame=pict_type \ 
    -of csv \ 
    tmp.h264 

을 언급 한 바와 같이 at : https://superuser.com/questions/885452/extracting-the-index-of-key-frames-from-a-video-using-ffmpeg

Preview of generated output.

#include <libavcodec/avcodec.h> 
#include <libavutil/imgutils.h> 
#include <libavutil/opt.h> 
#include <libswscale/swscale.h> 

static AVCodecContext *c = NULL; 
static AVFrame *frame; 
static AVPacket pkt; 
static FILE *file; 
struct SwsContext *sws_context = NULL; 

static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { 
    const int in_linesize[1] = { 3 * c->width }; 
    sws_context = sws_getCachedContext(sws_context, 
      c->width, c->height, AV_PIX_FMT_RGB24, 
      c->width, c->height, AV_PIX_FMT_YUV420P, 
      0, 0, 0, 0); 
    sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0, 
      c->height, frame->data, frame->linesize); 
} 

uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) { 
    int x, y, cur; 
    rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width); 
    for (y = 0; y < height; y++) { 
     for (x = 0; x < width; x++) { 
      cur = 3 * (y * width + x); 
      rgb[cur + 0] = 0; 
      rgb[cur + 1] = 0; 
      rgb[cur + 2] = 0; 
      if ((frame->pts/25) % 2 == 0) { 
       if (y < height/2) { 
        if (x < width/2) { 
         /* Black. */ 
        } else { 
         rgb[cur + 0] = 255; 
        } 
       } else { 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
        } else { 
         rgb[cur + 2] = 255; 
        } 
       } 
      } else { 
       if (y < height/2) { 
        rgb[cur + 0] = 255; 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
        } else { 
         rgb[cur + 2] = 255; 
        } 
       } else { 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
         rgb[cur + 2] = 255; 
        } else { 
         rgb[cur + 0] = 255; 
         rgb[cur + 1] = 255; 
         rgb[cur + 2] = 255; 
        } 
       } 
      } 
     } 
    } 
    return rgb; 
} 

/* Allocate resources and write header data to the output file. */ 
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) { 
    AVCodec *codec; 
    int ret; 

    codec = avcodec_find_encoder(codec_id); 
    if (!codec) { 
     fprintf(stderr, "Codec not found\n"); 
     exit(1); 
    } 
    c = avcodec_alloc_context3(codec); 
    if (!c) { 
     fprintf(stderr, "Could not allocate video codec context\n"); 
     exit(1); 
    } 
    c->bit_rate = 400000; 
    c->width = width; 
    c->height = height; 
    c->time_base.num = 1; 
    c->time_base.den = fps; 
    c->keyint_min = 600; 
    c->pix_fmt = AV_PIX_FMT_YUV420P; 
    if (codec_id == AV_CODEC_ID_H264) 
     av_opt_set(c->priv_data, "preset", "slow", 0); 
    if (avcodec_open2(c, codec, NULL) < 0) { 
     fprintf(stderr, "Could not open codec\n"); 
     exit(1); 
    } 
    file = fopen(filename, "wb"); 
    if (!file) { 
     fprintf(stderr, "Could not open %s\n", filename); 
     exit(1); 
    } 
    frame = av_frame_alloc(); 
    if (!frame) { 
     fprintf(stderr, "Could not allocate video frame\n"); 
     exit(1); 
    } 
    frame->format = c->pix_fmt; 
    frame->width = c->width; 
    frame->height = c->height; 
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32); 
    if (ret < 0) { 
     fprintf(stderr, "Could not allocate raw picture buffer\n"); 
     exit(1); 
    } 
} 

/* 
Write trailing data to the output file 
and free resources allocated by ffmpeg_encoder_start. 
*/ 
void ffmpeg_encoder_finish(void) { 
    uint8_t endcode[] = { 0, 0, 1, 0xb7 }; 
    int got_output, ret; 
    do { 
     fflush(stdout); 
     ret = avcodec_encode_video2(c, &pkt, NULL, &got_output); 
     if (ret < 0) { 
      fprintf(stderr, "Error encoding frame\n"); 
      exit(1); 
     } 
     if (got_output) { 
      fwrite(pkt.data, 1, pkt.size, file); 
      av_packet_unref(&pkt); 
     } 
    } while (got_output); 
    fwrite(endcode, 1, sizeof(endcode), file); 
    fclose(file); 
    avcodec_close(c); 
    av_free(c); 
    av_freep(&frame->data[0]); 
    av_frame_free(&frame); 
} 

/* 
Encode one frame from an RGB24 input and save it to the output file. 
Must be called after ffmpeg_encoder_start, and ffmpeg_encoder_finish 
must be called after the last call to this function. 
*/ 
void ffmpeg_encoder_encode_frame(uint8_t *rgb) { 
    int ret, got_output; 
    ffmpeg_encoder_set_frame_yuv_from_rgb(rgb); 
    av_init_packet(&pkt); 
    pkt.data = NULL; 
    pkt.size = 0; 
    if (frame->pts == 1) { 
     frame->key_frame = 1; 
     frame->pict_type = AV_PICTURE_TYPE_I; 
    } else { 
     frame->key_frame = 0; 
     frame->pict_type = AV_PICTURE_TYPE_P; 
    } 
    ret = avcodec_encode_video2(c, &pkt, frame, &got_output); 
    if (ret < 0) { 
     fprintf(stderr, "Error encoding frame\n"); 
     exit(1); 
    } 
    if (got_output) { 
     fwrite(pkt.data, 1, pkt.size, file); 
     av_packet_unref(&pkt); 
    } 
} 

/* Represents the main loop of an application which generates one frame per loop. */ 
static void encode_example(const char *filename, int codec_id) { 
    int pts; 
    int width = 320; 
    int height = 240; 
    uint8_t *rgb = NULL; 
    ffmpeg_encoder_start(filename, codec_id, 25, width, height); 
    for (pts = 0; pts < 100; pts++) { 
     frame->pts = pts; 
     rgb = generate_rgb(width, height, pts, rgb); 
     ffmpeg_encoder_encode_frame(rgb); 
    } 
    ffmpeg_encoder_finish(); 
} 

int main(void) { 
    avcodec_register_all(); 
    encode_example("tmp.h264", AV_CODEC_ID_H264); 
    encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO); 
    return 0; 
} 

컴파일 및 실행 :

gcc -std=c99 -Wextra a.c -lavcodec -lswscale -lavutil 
./a.out 
ffplay tmp.mpg 
ffplay tmp.h264 

우분투 16.04에서 테스트. GitHub upstream.

+0

Downvoters가 설명하여 콘텐츠를 배우고 향상시킬 수 있습니다 :-) –

+0

이 경우 nvcuda.dll 및 해당 종속 항목이 필요합니다. 실행하지 못했습니다. – rosewater

+0

신고에 대한 폐수 감사합니다. 설치 방법을 알려 주시면 알려드립니다. 우분투에서만 테스트 할 수있었습니다. –