2013-10-18 4 views
2

libavcodec과 libavcodec을 사용하여 비디오를 인코딩하는 C++ 응용 프로그램을 작성하고 있습니다. 그러나 인코딩 된 데이터는 예상보다 훨씬 커졌습니다. 결과를 분석 한 결과 내 인코딩이 B 프레임을 생성하지 않았으며 I 프레임과 P 프레임 만 생성 한 사실을 발견했습니다.libavcodec/libx264는 B 프레임을 생성하지 않습니다.

저는 ffmpeg 소스 코드와 예제를 기반으로 독립형 유틸리티를 만들고 인코더 설정을 테스트했습니다. H.264 파일을 읽고, 디코딩 된 프레임을 다시 인코딩 한 다음 ITU H.264 Annex B 형식을 사용하여 파일에 결과를 출력합니다. 또한 ffmpeg를 사용하여 동일한 작업을 수행하므로 알려진 구현과 비교할 수 있습니다. ffmpeg가하는 반면 내 유틸리티는 B- 프레임을 출력하지 않습니다.

나는 이후 ffmpeg가 내 코드에서하지 않는 것을 알아 내려고 노력했다. 먼저 B 프레임과 관련된 인코더 설정을 수동으로 지정하려고했습니다. 이것은 아무런 효과가 없었다.

그런 다음 gdb에서 ffmpeg와 내 유틸리티를 모두 실행하고 AVStream, AVCodecContext 및 X264Context의 내용을 비교하여 인코더를 열고 다른 것처럼 보이는 필드를 수동으로 설정하려고했습니다. 동일한 설정으로도 I- 프레임과 P- 프레임 만 생성합니다.

마지막으로 문제는 아마도 타임 스탬프 처리 때문일 것이라고 생각했습니다. 필자는 테스트 유틸리티를 수정하여 ffmpeg에서 사용하는 파이프 라인을 모방하고 ffmpeg처럼 타임 스탬프 디버깅 출력을 출력했습니다. ffmpeg와 동일한 타임 스탬프를 사용하더라도 B 프레임을 얻지 못합니다.

이 시점에서 나는 무엇을 시도해야할지 모르겠다. ffmpeg를 실행할 때 아래 명령 줄을 사용하여 실행합니다. "초고속"사전 설정을 제외하고는 기본값을 거의 사용합니다.

ffmpeg -v debug -i ~/annexb.264 -codec:v libx264 -preset superfast -g 30 -f h264 ./out.264 

인코더를 구성하는 코드는 다음과 같습니다. "초고속"프리셋도 지정합니다.

static AVStream *add_video_stream(AVFormatContext *output_ctx, AVCodec **output_codec, enum AVCodecID codec_id) 
{ 
    *output_codec = avcodec_find_encoder(codec_id); 
    if (*output_codec == NULL) { 
     printf("Could not find encoder for '%s' (%d)\n", avcodec_get_name(codec_id), codec_id); 
     return NULL; 
    } 

    AVStream *output_stream = avformat_new_stream(output_ctx, *output_codec); 
    if (output_stream == NULL) { 
     printf("Could not create video stream.\n"); 
     return NULL; 
    } 
    output_stream->id = output_ctx->nb_streams - 1; 
    AVCodecContext *codec_ctx = output_stream->codec; 

    avcodec_get_context_defaults3(codec_ctx, *output_codec); 

    codec_ctx->width = 1280; 
    codec_ctx->height = 720; 

    codec_ctx->time_base.den = 15000; 
    codec_ctx->time_base.num = 1001; 

/* codec_ctx->gop_size = 30;*/ 
    codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; 

    // try to force B-frame output 
/* codec_ctx->max_b_frames = 3;*/ 
/* codec_ctx->b_frame_strategy = 2;*/ 

    output_stream->sample_aspect_ratio.num = 1; 
    output_stream->sample_aspect_ratio.den = 1; 

    codec_ctx->sample_aspect_ratio.num = 1; 
    codec_ctx->sample_aspect_ratio.den = 1; 

    codec_ctx->chroma_sample_location = AVCHROMA_LOC_LEFT; 

    codec_ctx->bits_per_raw_sample = 8; 

    if ((output_ctx->oformat->flags & AVFMT_GLOBALHEADER) != 0) { 
     codec_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; 
    } 

    return output_stream; 
} 


int main(int argc, char **argv) 
{ 
    // ... open input file 

    avformat_alloc_output_context2(&output_ctx, NULL, "h264", output_path); 
    if (output_ctx == NULL) { 
     fprintf(stderr, "Unable to allocate output context.\n"); 
     return 1; 
    } 

    AVCodec *output_codec = NULL; 
    output_stream = add_video_stream(output_ctx, &output_codec, output_ctx->oformat->video_codec); 
    if (output_stream == NULL) { 
     fprintf(stderr, "Error adding video stream to output context.\n"); 
     return 1; 
    } 
    encode_ctx = output_stream->codec; 

    // seems to have no effect 
#if 0 
    if (decode_ctx->extradata_size != 0) { 
     size_t extradata_size = decode_ctx->extradata_size; 
     printf("extradata_size: %zu\n", extradata_size); 
     encode_ctx->extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); 
     memcpy(encode_ctx->extradata, decode_ctx->extradata, extradata_size); 
     encode_ctx->extradata_size = extradata_size; 
    } 
#endif // 0 

    AVDictionary *opts = NULL; 
    av_dict_set(&opts, "preset", "superfast", 0); 
    // av_dict_set(&opts, "threads", "auto", 0); // seems to have no effect 

    ret = avcodec_open2(encode_ctx, output_codec, &opts); 
    if (ret < 0) { 
     fprintf(stderr, "Unable to open output video cocec: %s\n", av_err2str(ret)); 
     return 1; 
    } 

    // ... decoding/encoding loop, clean up, etc. 

    return 0; 
} 

내 테스트 유틸리티를 사용하면 생산에는 B 프레임이없는 볼 수있는 다음과 같은 디버그 출력이 생성

[libx264 @ 0x1b8c9c0] using mv_range_thread = 56 
[libx264 @ 0x1b8c9c0] using SAR=1/1 
[libx264 @ 0x1b8c9c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX 
[libx264 @ 0x1b8c9c0] profile High, level 3.1 
Output #0, h264, to './out.264': 
    Stream #0:0, 0, 1/90000: Video: h264, yuvj420p, 1280x720 [SAR 1:1 DAR 16:9], 1001/15000, q=-1--1, 90k tbn, 14.99 tbc 

<SNIP> 

[libx264 @ 0x1b8c9c0] frame= 0 QP=17.22 NAL=3 Slice:I Poc:0 I:3600 P:0 SKIP:0 size=122837 bytes 
[libx264 @ 0x1b8c9c0] frame= 1 QP=18.03 NAL=2 Slice:P Poc:2 I:411 P:1825 SKIP:1364 size=25863 bytes 
[libx264 @ 0x1b8c9c0] frame= 2 QP=17.03 NAL=2 Slice:P Poc:4 I:369 P:2159 SKIP:1072 size=37880 bytes 
[libx264 @ 0x1b8c9c0] frame= 3 QP=16.90 NAL=2 Slice:P Poc:6 I:498 P:2330 SKIP:772 size=50509 bytes 
[libx264 @ 0x1b8c9c0] frame= 4 QP=16.68 NAL=2 Slice:P Poc:8 I:504 P:2233 SKIP:863 size=50791 bytes 
[libx264 @ 0x1b8c9c0] frame= 5 QP=16.52 NAL=2 Slice:P Poc:10 I:513 P:2286 SKIP:801 size=51820 bytes 
[libx264 @ 0x1b8c9c0] frame= 6 QP=16.49 NAL=2 Slice:P Poc:12 I:461 P:2293 SKIP:846 size=51311 bytes 
[libx264 @ 0x1b8c9c0] frame= 7 QP=16.65 NAL=2 Slice:P Poc:14 I:476 P:2287 SKIP:837 size=51196 bytes 
[libx264 @ 0x1b8c9c0] frame= 8 QP=16.66 NAL=2 Slice:P Poc:16 I:508 P:2240 SKIP:852 size=51577 bytes 
[libx264 @ 0x1b8c9c0] frame= 9 QP=16.55 NAL=2 Slice:P Poc:18 I:477 P:2278 SKIP:845 size=51531 bytes 
[libx264 @ 0x1b8c9c0] frame= 10 QP=16.67 NAL=2 Slice:P Poc:20 I:517 P:2233 SKIP:850 size=51946 bytes 

<SNIP> 

[libx264 @ 0x1b8c9c0] frame I:7  Avg QP:13.71 size:152207 
[libx264 @ 0x1b8c9c0] frame P:190 Avg QP:16.66 size: 50949 
[libx264 @ 0x1b8c9c0] mb I I16..4: 27.1% 30.8% 42.1% 
[libx264 @ 0x1b8c9c0] mb P I16..4: 6.8% 6.0% 0.8% P16..4: 61.8% 0.0% 0.0% 0.0% 0.0% skip:24.7% 
[libx264 @ 0x1b8c9c0] 8x8 transform intra:41.2% inter:86.9% 
[libx264 @ 0x1b8c9c0] coded y,uvDC,uvAC intra: 92.2% 28.3% 5.4% inter: 50.3% 1.9% 0.0% 
[libx264 @ 0x1b8c9c0] i16 v,h,dc,p: 7% 7% 77% 8% 
[libx264 @ 0x1b8c9c0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 7% 15% 49% 6% 4% 3% 5% 3% 8% 
[libx264 @ 0x1b8c9c0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 19% 25% 24% 6% 7% 4% 6% 3% 6% 
[libx264 @ 0x1b8c9c0] i8c dc,h,v,p: 72% 14% 10% 4% 
[libx264 @ 0x1b8c9c0] Weighted P-Frames: Y:0.0% UV:0.0% 
[libx264 @ 0x1b8c9c0] kb/s:6539.11 

는 FFmpeg을, 다른 한편으로는, 거의 동일 다음과 같은 출력을 생성 하지만 B 프레임을 포함

[libx264 @ 0x20b9c40] using mv_range_thread = 56 
[libx264 @ 0x20b9c40] using SAR=1/1 
[libx264 @ 0x20b9c40] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX 
[libx264 @ 0x20b9c40] profile High, level 3.1 
[h264 @ 0x20b8160] detected 4 logical cores 
Output #0, h264, to './out.264': 
    Metadata: 
    encoder   : Lavf54.63.104 
    Stream #0:0, 0, 1/90000: Video: h264, yuvj420p, 1280x720 [SAR 1:1 DAR 16:9], 1001/15000, q=-1--1, 90k tbn, 14.99 tbc 
Stream mapping: 
    Stream #0:0 -> #0:0 (h264 -> libx264) 

<SNIP> 

[libx264 @ 0x20b9c40] frame= 0 QP=17.22 NAL=3 Slice:I Poc:0 I:3600 P:0 SKIP:0 size=122835 bytes 
[libx264 @ 0x20b9c40] frame= 1 QP=18.75 NAL=2 Slice:P Poc:8 I:984 P:2045 SKIP:571 size=54208 bytes 
[libx264 @ 0x20b9c40] frame= 2 QP=19.40 NAL=2 Slice:B Poc:4 I:447 P:1581 SKIP:1572 size=24930 bytes 
[libx264 @ 0x20b9c40] frame= 3 QP=19.78 NAL=0 Slice:B Poc:2 I:199 P:1002 SKIP:2399 size=10717 bytes 
[libx264 @ 0x20b9c40] frame= 4 QP=20.19 NAL=0 Slice:B Poc:6 I:204 P:1155 SKIP:2241 size=15937 bytes 
[libx264 @ 0x20b9c40] frame= 5 QP=18.11 NAL=2 Slice:P Poc:16 I:990 P:2221 SKIP:389 size=64240 bytes 
[libx264 @ 0x20b9c40] frame= 6 QP=19.35 NAL=2 Slice:B Poc:12 I:439 P:1784 SKIP:1377 size=34048 bytes 
[libx264 @ 0x20b9c40] frame= 7 QP=19.88 NAL=0 Slice:B Poc:10 I:275 P:1035 SKIP:2290 size=16911 bytes 
[libx264 @ 0x20b9c40] frame= 8 QP=19.91 NAL=0 Slice:B Poc:14 I:257 P:1270 SKIP:2073 size=19172 bytes 
[libx264 @ 0x20b9c40] frame= 9 QP=17.90 NAL=2 Slice:P Poc:24 I:962 P:2204 SKIP:434 size=67439 bytes 
[libx264 @ 0x20b9c40] frame= 10 QP=18.84 NAL=2 Slice:B Poc:20 I:474 P:1911 SKIP:1215 size=37742 bytes 

<SNIP> 

[libx264 @ 0x20b9c40] frame I:7  Avg QP:15.95 size:130124 
[libx264 @ 0x20b9c40] frame P:52 Avg QP:17.78 size: 64787 
[libx264 @ 0x20b9c40] frame B:138 Avg QP:19.32 size: 26231 
[libx264 @ 0x20b9c40] consecutive B-frames: 6.6% 0.0% 0.0% 93.4% 
[libx264 @ 0x20b9c40] mb I I16..4: 30.2% 35.2% 34.6% 
[libx264 @ 0x20b9c40] mb P I16..4: 13.9% 11.4% 0.3% P16..4: 60.4% 0.0% 0.0% 0.0% 0.0% skip:13.9% 
[libx264 @ 0x20b9c40] mb B I16..4: 5.7% 3.3% 0.0% B16..8: 15.8% 0.0% 0.0% direct:25.7% skip:49.5% L0:43.2% L1:37.3% BI:19.5% 
[libx264 @ 0x20b9c40] 8x8 transform intra:39.4% inter:77.2% 
[libx264 @ 0x20b9c40] coded y,uvDC,uvAC intra: 90.7% 26.6% 3.0% inter: 34.0% 4.1% 0.0% 
[libx264 @ 0x20b9c40] i16 v,h,dc,p: 7% 7% 77% 9% 
[libx264 @ 0x20b9c40] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 7% 16% 51% 5% 4% 3% 5% 3% 7% 
[libx264 @ 0x20b9c40] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 27% 20% 6% 6% 3% 6% 3% 6% 
[libx264 @ 0x20b9c40] i8c dc,h,v,p: 71% 15% 11% 3% 
[libx264 @ 0x20b9c40] Weighted P-Frames: Y:0.0% UV:0.0% 
[libx264 @ 0x20b9c40] kb/s:4807.16 

은 내가 간단한 뭔가를 누락 확신 해요,하지만 난 내 인생이 무엇인지 볼 수 없습니다하십시오. 어떤 도움이라도 대단히 감사하겠습니다.

답변

3

많은 디버깅 후, 나는 결국 해결책을 발견했다. 위의 스트림 및 인코더 설정에 문제가 없었지만 프레임을 인코더로 전달하는 방법에 문제가있었습니다.

데이터를 트랜스 코딩하려했기 때문에 단순히 디코더에서 방출되어 인코더로 전달 된 그림 만 찍었습니다. 그러나 디코더는 AVFrame structures에 대한 pict_type 값을 AV_PICTURE_TYPE_P으로 설정합니다. 인코더는 차례로 pict_type 값을 사용하여 생성 할 프레임 유형을 결정합니다. 따라서 인코더는 입력에서 항상 지정되는 P 프레임 만 생성합니다.

pict_type 필드를 AV_PICTURE_TYPE_NONE으로 재설정하면 인코더가 가장 적합한 프레임 유형을 생성 할 수 있습니다. 이렇게하기 위해 코드를 업데이트하고 나면 테스트 유틸리티에서 B 프레임을 생성하고 ffmpeg에서 출력 한 것과 동일한 파일을 생성했습니다.

0

동일한 문제점이 있지만 x264가 있습니다. "superfast"가 bframes = 0을 변경한다는 것을 알았습니다.x264_param_default_preset (& param, "fast", "zerolatency") : x264의 은 다음과 같습니다. x264_param_default_preset (& param, "slow", "zerolatency")로 변경하십시오. 그러면 B 프레임을 얻을 수 있습니다.

이 함수에서 매개 변수를 변경할 수 있습니다. 또는 그 정의를 확인할 수 있습니다. av_dict_set (& opts, "preset", "superfast", 0);

+0

같은 결과가있는 다른 인코딩 사전 설정을 시도했지만 다시 시도해 보겠습니다. 제안 해 주셔서 감사합니다. –

+0

프리셋을 "veryslow"로 설정했지만 B- 프레임이 없습니다. 테스트 프로그램을 실행하는 데 시간이 오래 걸렸고 디버깅 결과가 훨씬 작은 패킷을 보여 주었지만 불행히도 B 프레임이 없었기 때문에 변경 사항이 적용되었습니다. –

+0

나는 ffmpeg lib에 대해 모른다. codec_ctx-> max_b_frames = 3; codec_ctx-> b_frame_strategy = 1; codec_ctx-> refs = 6; FLAGS2 (CODEC_FLAG2_WPRED) 플래그를 추가하면 약간의 차이가 있습니다. 또한 다른 기능으로 인해 해당 매개 변수가 변경되지 않도록해야합니다. 중단 점을 추가하고 해당 매개 변수를 확인할 수 있습니다. –