DASH용 FFmpeg에서 키프레임을 수정하는 올바른 방법은 무엇입니까?

DASH용 FFmpeg에서 키프레임을 수정하는 올바른 방법은 무엇입니까?

DASH 재생을 위해 스트림을 조절할 때 임의 액세스 포인트는 모든 스트림에서 정확히 동일한 소스 스트림 시간에 있어야 합니다. 이를 수행하는 일반적인 방법은 고정된 프레임 속도와 고정된 GOP 길이(예: N 프레임마다 키프레임)를 강제하는 것입니다.

FFmpeg에서는 고정 프레임 속도가 쉽습니다(-r NUMBER).

그러나 고정된 키프레임 위치(GOP 길이)에는 세 가지 방법이 있습니다. 어느 것이 "올바른" 것입니까? FFmpeg 문서는 이에 대해 실망스러울 정도로 모호합니다.

방법 1: libx264의 인수 조작

-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1

장면 컷이 발생할 때 키프레임 "카운터"가 다시 시작되는지 여부가 불분명하기 때문에 장면 컷을 꺼야 하는지 여부에 대해 약간의 논쟁이 있는 것 같습니다.

방법 2: 고정 GOP 크기 설정:

-g GOP_LEN_IN_FRAMES

이는 불행하게도 FFMPEG 문서를 전달할 때만 문서화되어 있으므로 이 주장의 효과는 매우 불분명합니다.

방법 3: N초마다 키프레임 삽입(아마도?):

-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)

이것~이다명시적으로 문서화되어 있습니다. 그러나 모든 키 프레임 후에 "시간 카운터"가 다시 시작되는지는 아직 즉시 명확하지 않습니다. 예를 들어 예상되는 5초 GOP에서 scenecutlibx264에 의해 3초에 키프레임이 삽입된 경우 다음 키프레임은 5초 후일까요, 아니면 2초 후일까요?

실제로 FFmpeg 문서에서는 이 옵션과 -g옵션을 구분하지만 위의 두 옵션이 어떻게 최소한으로 다른지는 밝히지 않습니다(분명히 -g고정 프레임 속도가 필요함).

어느 것이 맞는지?

-force_key_frames가 더 우수할 것 같습니다, 고정된 프레임 속도가 필요하지 않기 때문입니다.그러나 이를 위해서는 다음이 필요합니다.

  • H.264의 GOP 사양을 준수합니다(만약에 어떠한)
  • libx264 키프레임에 관계없이 고정된 케이던스의 키프레임이 있음을 보장합니다 scenecut.

-g고정된 프레임 속도를 강제하지 않으면 작동할 수 없는 것 같습니다 ( -r).ffmpeg, 서로 다른 코덱 인수를 사용하여 여러 번 실행하면 각 해상도에서 동일한 순간 프레임 속도를 제공한다는 보장이 없기 때문입니다 . 고정 프레임 속도는 압축 성능을 저하시킬 수 있습니다(DASH 시나리오에서는 중요합니다!).

마지막으로,keyint방법은 해킹처럼 보입니다.. 이것이 정답이 아니길 바라겠습니다.

참고자료:

-force_key_frames메소드 를 사용한 예

keyint메소드 를 사용한 예

FFmpeg 고급 비디오 옵션 섹션

답변1

TL;DR

나는 다음을 추천합니다:

  • libx264: (선택적으로 추가 )-g X -keyint_min X-force_key_frames "expr:gte(t,n_forced*N)"
  • libx265:-x265-params "keyint=X:min-keyint=X"
  • libvpx-vp9:-g X

여기서 X는 프레임 단위이고 N는 초 단위입니다. 예를 들어 30fps 비디오의 2초 간격의 경우 X= 60 및 N= 2입니다.

다양한 프레임 유형에 대한 참고 사항

이 주제를 제대로 설명하려면 먼저 두 가지 유형의 I-프레임/키프레임을 정의해야 합니다.

  • IDR(순간 디코더 새로 고침) 프레임: IDR 프레임 이전 프레임에 액세스하지 않고도 다음 프레임을 독립적으로 디코딩할 수 있습니다.
  • 비IDR 프레임: 디코딩이 작동하려면 이전 IDR 프레임이 필요합니다. 비IDR 프레임은 GOP(사진 그룹) 중간의 장면 컷에 사용할 수 있습니다.

스트리밍에 권장되는 것은 무엇입니까?

스트리밍 사례의 경우 다음을 수행하려고 합니다.

  • 비디오가 동일한 길이의 세그먼트로 분할될 수 있도록 모든 IDR 프레임이 일반 위치(예: 2, 4, 6, ...초)에 있는지 확인하십시오.
  • 코딩 효율성/품질을 향상시키기 위해 장면 컷 감지를 활성화합니다. 이는 IDR 프레임 사이에 I-프레임을 배치할 수 있음을 의미합니다. 장면 컷 감지를 비활성화한 상태에서도 계속 작업할 수 있지만(여전히 많은 가이드의 일부임) 반드시 그럴 필요는 없습니다.

매개변수의 역할은 무엇인가요?

인코더를 구성하려면 키프레임 매개변수의 역할을 이해해야 합니다. 몇 가지 테스트를 수행한 결과 세 가지 인코더 libx264와 FFmpeg에 libx265대해 다음을 발견했습니다 .libvpx-vp9

  • libx264:

    • -g키프레임 간격을 설정합니다.
    • -keyint_min최소 키프레임 간격을 설정합니다.
    • -x264-params "keyint=x:min-keyint=y"와 같다 -g x -keyint_min y.
    • 메모:둘 다 동일한 값으로 설정하면 내부적으로 최소값이코드 에 표시된 대로 최대 간격에 1을 더한 값입니다 x264.

      h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
      
  • libx265:

    • -g구현되지 않습니다.
    • -x265-params "keyint=x:min-keyint=y"공장.
  • libvpx-vp9:

    • -g키프레임 간격을 설정합니다.
    • -keyint_min최소 키프레임 간격을 설정합니다.
    • 메모:-keyint_minFFmpeg 작동 방식 으로 인해 -g. FFmpeg의 코드에서 다음을 libvpxenc.c찾을 수 있습니다.

      if (avctx->keyint_min >= 0 && avctx->keyint_min == avctx->gop_size)
          enccfg.kf_min_dist = avctx->keyint_min;
      if (avctx->gop_size >= 0)
          enccfg.kf_max_dist = avctx->gop_size;
      

      libvpx에 대해 다른 값 설정을 확실히 지원하므로 이는 버그(또는 기능 부족?)일 수 있습니다 kf_min_dist.

을 사용해야 합니까 -force_key_frames?

해당 -force_key_frames옵션은 지정된 간격(표현식)으로 키프레임을 강제로 삽입합니다. 이는 모든 인코더에서 작동하지만 속도 제어 메커니즘을 망칠 수 있습니다. 특히 VP9의 경우 품질 변동이 심해서 이 경우에는 사용을 권장하지 않습니다.

답변2

여기 그 사건에 대한 내 50센트가 있습니다.

방법 1:

libx264의 주장을 망쳐 놓았습니다.

-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1

원하는 간격으로만 iframe을 생성합니다.

예시 1:

ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-x264opts "keyint=48:min-keyint=48:no-scenecut" \
-c:a copy \
-y test_keyint_48.mp4

다음과 같이 예상대로 iframe을 생성합니다.

Iframes     Seconds
1           0
49          2
97          4
145         6
193         8
241         10
289         12
337         14
385         16
433         18
481         20
529         22
577         24
625         26
673         28
721         30
769         32
817         34
865         36
913         38
961         40
1009        42
1057        44
1105        46
1153        48
1201        50
1249        52
1297        54
1345        56
1393        58

방법 2는 감가 상각됩니다. 생략되었습니다.

방법 3:

N초마다 키프레임을 삽입합니다(아마도):

-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)

실시예 2

ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-force_key_frames "expr:gte(t,n_forced*2)"
-c:a copy \
-y test_fkf_2.mp4

약간 다른 방식으로 iframe을 생성합니다.

Iframes     Seconds
1           0
49          2
97          4
145         6
193         8
241         10
289         12
337         14
385         16
433         18
481         20
519         21.58333333
529         22
577         24
625         26
673         28
721         30
769         32
817         34
865         36
913         38
931         38.75
941         39.16666667
961         40
1008        42
1056        44
1104        46
1152        48
1200        50
1248        52
1296        54
1305        54.375
1344        56
1367        56.95833333
1392        58
1430        59.58333333
1440        60
1475        61.45833333
1488        62
1536        64
1544        64.33333333
1584        66
1591        66.29166667
1632        68
1680        70
1728        72
1765        73.54166667
1776        74
1811        75.45833333
1824        75.95833333
1853        77.16666667
1872        77.95833333
1896        78.95833333
1920        79.95833333
1939        80.75
1968        81.95833333

보시다시피 제 생각에는 비디오 스트림 복잡성에 중요한 iframe을 2초마다 그리고 장면 컷(플로팅 부분이 있는 초)에 배치합니다.

생성된 파일 크기는 거의 동일합니다. 더 많은 키프레임이 있어도 매우 이상합니다.방법 3때로는 표준 x264 라이브러리 알고리즘보다 적은 파일을 생성합니다.

HLS 스트림에 대한 여러 비트 전송률 파일을 생성하기 위해 방법 3을 선택합니다. 청크 간 2초로 완벽하게 정렬되었으며, 모든 청크의 시작 부분에 iframe이 있고 복잡한 장면에 추가 iframe이 있어 오래된 장치를 가지고 있고 x264 높은 프로필을 재생할 수 없는 사용자에게 더 나은 경험을 제공합니다.

누군가에게 도움이 되기를 바랍니다.

답변3

내가 원하는 방식으로 DASH 인코딩을 분할하는 방법을 찾는 방법에 대한 정보를 찾으려고 인터넷 검색을 통해 이 토론을 꽤 많이 끌어냈고 내가 찾은 정보 중 완전히 정확하지 않았기 때문에 여기에 몇 가지 정보를 추가하고 싶었습니다.

먼저 없애야 할 몇 가지 오해:

  1. 모든 I-프레임이 동일한 것은 아닙니다. 큰 "I" 프레임과 작은 "i" 프레임이 있습니다. 또는 올바른 용어를 사용하려면 IDR I-프레임 및 비IDR I-프레임을 사용하세요. IDR I-프레임("키프레임"이라고도 함)은 새 GOP를 생성합니다. 비IDR 프레임은 그렇지 않습니다. 장면이 바뀌는 GOP 내부에 보관하면 편리합니다.

  2. -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE← 이것은 당신이 생각하는 것과는 다릅니다. 이것을 알아내는 데 시간이 조금 걸렸습니다. min-keyint코드에는 제한이 있는 것으로 나타났습니다 . 보다 클 수 없습니다 (keyint / 2) + 1. 따라서 이 두 변수에 동일한 값을 할당하면 min-keyint인코딩 시 값이 절반으로 줄어듭니다.

문제는 다음과 같습니다. 장면 컷은 정말 훌륭합니다. 특히 빠른 하드 컷이 있는 비디오에서는 더욱 그렇습니다. 깔끔하고 선명한 상태를 유지하므로 비활성화하고 싶지 않지만 동시에 활성화된 한 고정된 GOP 크기를 얻을 수 없었습니다. 장면 컷을 활성화하고 싶었지만 IDR이 아닌 I-프레임만 사용하도록 했습니다. 그러나 그것은 작동하지 않았습니다. 내가 오해 #2에 대해 (많은 독서를 통해) 알아낼 때까지.

keyint원하는 GOP 크기를 두 배로 설정해야 했습니다 . 이는 min-keyint내부 코드가 반으로 자르지 않고 원하는 GOP 크기로 설정할 수 있음을 의미합니다. 이는 마지막 IDR I-프레임 이후의 프레임 수가 항상 보다 작습니다 min-keyinit.

마지막으로 force_key_frame옵션을 설정하면 두 배 크기가 무시됩니다 keyint. 작동하는 방법은 다음과 같습니다.

저는 2초 단위의 세그먼트를 선호하므로 GOPSIZE = 프레임 속도 * 2입니다.

ffmpeg <other_options> -force_key_frames "expr:eq(mod(n,<GOPSIZE>),0)" -x264opts rc-lookahead=<GOPSIZE>:keyint=<GOPSIZE * 2>:min-keyint=<GOPSIZE> <other_options>

ffprobe를 사용하여 확인할 수 있습니다.

ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv

생성된 CSV 파일의 각 줄은 다음을 알려줍니다 frame, [is_an_IDR_?], [frame_type], [frame_number].

frame,1,I,60  <-- frame 60, is I frame, 1 means is an IDR I-frame (aka KeyFrame)
frame,0,I,71  <-- frame 71, is I frame, 0 means not an IDR I_frame

결과적으로 고정된 간격으로 IDR I-프레임만 표시 GOPSIZE되고 다른 모든 I 프레임은 장면 컷 감지에 필요에 따라 삽입된 비IDR I-프레임입니다.

답변4

Twitch에 이에 대한 게시물이 있습니다. 그들은 여러 가지 이유로 자체 프로그램을 사용하기로 결정했다고 설명합니다. 그중 하나는 ffmpeg를 사용하면 다른 스레드에서 다른 x264 인스턴스를 실행할 수 없지만 대신 다음 출력으로 이동하기 전에 지정된 모든 스레드를 하나의 출력에서 ​​하나의 프레임에 할당한다는 것입니다.

실시간 스트리밍을 하지 않는다면 더 많은 사치를 누릴 수 있습니다. '올바른' 방법은 아마도 -g로 지정된 GOP 크기를 사용하여 하나의 해상도로 인코딩한 다음 동일한 위치에서 키프레임을 강제하는 다른 해상도를 인코딩하는 것입니다.

그렇게 하고 싶다면 ffprobe를 사용하여 키프레임 시간을 가져온 다음 쉘 스크립트나 실제 프로그래밍 언어를 사용하여 이를 ffmpeg 명령으로 변환할 수 있습니다.

그러나 대부분의 콘텐츠에서는 5초마다 하나의 키프레임을 갖는 것과 5초마다 두 개의 키프레임(강제 하나, 장면 컷에서 하나) 사이에는 거의 차이가 없습니다. 이는 평균 I-프레임 크기와 P-프레임 및 B-프레임의 크기에 대한 것입니다. 일반적인 설정으로 x264를 사용하는 경우(이에 영향을 미치기 위해 무엇이든 해야 한다고 생각하는 유일한 이유는 x264가 쉬운 콘텐츠에서 비트 전송률을 사용하는 것을 방지하는 좋지 않은 방법으로 -qmin을 설정하는 것입니다. 이는 모든 프레임 유형을 동일한 값으로 제한합니다. , 제 생각에는) I-프레임 평균 크기 46kB, P-프레임 24kB, B-프레임 17kB(P-프레임의 절반 빈도)와 같은 결과를 얻은 다음 30fps에서 매초 추가 I-프레임을 얻습니다. 파일 크기는 3%만 증가합니다. h264와 h263의 차이는 3% 감소가 여러 개로 이루어지겠지만, 단 하나도 그다지 중요하지 않습니다.

다른 유형의 콘텐츠에서는 프레임 크기가 다릅니다. 공평하게 말하면 이것은 공간적 복잡성이 아니라 시간적 복잡성에 관한 것이므로 단순히 쉬운 콘텐츠와 어려운 콘텐츠가 아닙니다. 그러나 일반적으로 스트리밍 비디오 사이트에는 비트 전송률 제한이 있으며, 상대적으로 큰 I-프레임이 포함된 콘텐츠는 추가 키프레임 수에 관계없이 고품질로 인코딩되기 쉬운 콘텐츠입니다. 낭비이지만 일반적으로 이러한 낭비는 눈에 띄지 않습니다. 가장 낭비적인 사례는 아마도 노래에 수반되는 정적 이미지일 뿐이고 각 키프레임이 정확히 동일한 비디오일 것입니다.

확실하지 않은 한 가지는 강제 키프레임이 -maxrate 및 -bufsize로 설정된 속도 제한기와 어떻게 상호 작용하는지입니다. 최근 YouTube에서도 일관된 품질을 제공하기 위해 버퍼 설정을 올바르게 구성하는 데 문제가 있었던 것 같습니다. 일부 사이트에서 볼 수 있는 것처럼 평균 비트 전송률 설정을 사용하는 경우(16진수 편집기를 사용하여 헤더/mov 원자에서 x264의 옵션을 검사할 수 있으므로) 버퍼 모델은 문제가 되지 않습니다. 사용자 생성 콘텐츠를 제공하는 경우 평균 비트 전송률은 사용자가 동영상 끝에 검은색 화면을 추가하도록 유도합니다.

Ffmpeg의 -g 옵션 또는 사용하는 다른 인코더 옵션은 인코더별 옵션에 매핑됩니다. 따라서 '-x264-params keyint=GOPSIZE'는 '-g GOPSIZE'와 동일합니다.

장면 감지 사용 시 한 가지 문제는 어떤 이유로든 특정 숫자 근처의 키프레임을 선호하는 경우입니다. 5초마다 키프레임을 지정하고 장면 감지를 사용하는 경우 4.5에서 장면 변경이 있으면 감지되어야 하지만 다음 키프레임은 9.5에 있게 됩니다. 이렇게 시간이 계속 올라가면 키프레임이 40, 45, 50, 55가 아닌 42.5, 47.5, 52.5 등이 될 수 있습니다. 반대로 5.5에서 장면 변경이 있으면 5와 5.5의 키프레임은 다른 키프레임에 비해 너무 이르습니다. Ffmpeg에서는 "다음 30프레임 내에 장면 변경이 없으면 여기에 키프레임 만들기"를 지정할 수 없습니다. 하지만 C를 이해하는 사람이라면 해당 옵션을 추가할 수 있습니다.

가변 프레임 속도 비디오의 경우 Twitch처럼 라이브 스트리밍을 하지 않는 경우 고정 프레임 속도로 영구적으로 변환하지 않고도 장면 변경을 사용할 수 있어야 합니다. ffmpeg에서 'select' 필터를 사용하고 표현식에서 'scene' 상수를 사용하면 디버그 출력(-v 디버그 또는 인코딩하는 동안 '+'를 여러 번 누름)에 장면 변경 번호가 표시됩니다. 이는 아마도 x264에서 사용되는 숫자와 다르고 그만큼 유용하지는 않지만 여전히 유용할 수 있습니다.

그러면 절차는 아마도 키프레임 변경만을 위한 테스트 비디오를 수행하는 것이지만 2패스를 사용하는 경우 속도 제어 데이터에 사용될 수도 있습니다. (생성된 데이터가 다양한 해상도와 설정에 전혀 유용한지 확실하지 않습니다. 매크로블록 트리 데이터는 그렇지 않습니다.) 고정 프레임 속도 비디오로 변환하지만 다음을 참조하세요.이 버그다른 목적으로 fps 필터를 사용하기로 결정한 경우 프레임 속도를 절반으로 줄이면 출력이 끊기는 현상에 대해 알아보세요. 원하는 키프레임과 GOP 설정으로 x264를 통해 실행하세요.

그런 다음 원본 가변 프레임 속도 비디오에 이러한 키프레임 시간을 사용하세요.

프레임 사이에 20초의 간격이 있는 완전히 미친 사용자 생성 콘텐츠를 허용하는 경우 가변 프레임 속도 인코딩의 경우 출력을 분할하고 fps 필터를 사용하고 어떻게든 선택 필터를 사용할 수 있습니다. 모든 키프레임 시간)... 또는 테스트 비디오를 입력으로 사용하고 ffmpeg 옵션이 작동하는 경우 키프레임만 디코딩하거나 선택 필터를 사용하여 키프레임을 선택할 수 있습니다. 그런 다음 올바른 크기로 크기를 조정하고(이를 위한 scale2ref 필터도 있음) 원본 비디오를 그 위에 오버레이합니다. 그런 다음 인터리브 필터를 사용하여 이러한 강제 키프레임을 원본 비디오와 결합합니다. 이로 인해 인터리브 필터가 방지하지 못하는 0.001초 간격의 두 프레임이 발생하는 경우 다른 선택 필터를 사용하여 이 문제를 직접 해결하십시오. 인터리브 필터에 대한 프레임 버퍼 제한을 처리하는 것이 여기서 주요 문제가 될 수 있습니다. 이것들은 모두 작동할 수 있습니다. 일종의 필터를 사용하여 밀도가 높은 스트림을 버퍼링합니다(fifo 필터?). 입력 파일을 여러 번 참조하여 두 번 이상 디코딩되고 프레임을 저장할 필요가 없도록 합니다. 제가 한 번도 해본 적이 없는 'streamselect' 필터를 정확히 키프레임 시간에 사용하세요. 기본 동작을 변경하거나 프레임을 삭제하는 대신 버퍼에서 가장 오래된 프레임을 출력하는 옵션을 추가하여 인터리브 필터를 개선합니다.

관련 정보