FFmpeg는 오디오 및 비디오 동기화의 정확한 스플라이싱을 실현합니다.

오디오 및 비디오 개발 과정에서 여러 클립(또는 "샷"이라고 함)을 연결하는 문제에 자주 직면합니다. 각각 설명하기 위해 여러 가지 예가 아래에 나열됩니다.

이 문서에 나열된 예제는 전제 조건을 충족해야 합니다.

1. 각 클립 자체는 오디오 및 비디오와 동기화되지만 접합 후 동기화되지 않는 문제가 있습니다.

2. 각 세그먼트의 비디오 스트림이 동일함: 사진 너비, 사진 높이, 픽셀 종횡비, 프레임 속도 및 시간 기준 / 3. 각 세그먼트의 오디오 스트림이 동일함: 샘플링 속도, 시간 기준 /4. 각 클립에는 비디오 스트림과 오디오 스트림이 있습니다.

직접 연결

FFmpeg는 비디오뿐만 아니라 오디오도 스티칭할 수 있는 concat 필터를 제공합니다. 예를 들어:

ffmpeg -i demo_1.mp4 -i demo_2.mp4 -filter_complex \
"[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" mix.mp4

이러한 종류의 스플라이싱 후에 오디오와 비디오를 동기화하려는 경우 전제 조건은 다음과 같습니다. 각 세그먼트의 오디오 스트림 길이는 비디오 스트림의 길이와 같아야 합니다 .

이와 같이: 

FFmpeg와 함께 제공되는 ffprobe 도구를 사용하여 파일의 각 스트림에 해당하는 기간 정보를 볼 수 있습니다.

ffprobe -v quiet -show_entries \
stream=index,codec_name,time_base,start_pts,start_time,duration_ts,duration \
-of json demo_1.mp4

출력은 다음과 같습니다.

{
   ...
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "time_base": "1/12800",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 128512,
            "duration": "10.040000"
        },
        {
            "index": 1,
            "codec_name": "aac",
            "time_base": "1/44100",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 442764,
            "duration": "10.040000"
        }
    ]
}

여기서 주요 초점은 기간 필드에 있습니다. 샘플 파일의 오디오 스트림과 비디오 스트림 의 길이가 10.04초로 동일하다는 것을 알 수 있으므로 이러한 비디오 스플라이싱이 몇 개인지 오디오-비디오 동기화 문제가 없을 것입니다. 아래 그림과 같이:

 

예상과 달리 실제 처리에서는 세그먼트의 오디오 스트림과 비디오 스트림의 길이가 같지 않은 경우가 많습니다. 물론 지속 시간의 차이는 매우 작을 수 있으며 수십 밀리초 정도의 클립을 재생하는 것만으로는 지속 시간의 차이를 거의 느낄 수 없습니다. 위의 ffprobe 명령을 통해서만 오디오 및 비디오 스트림의 지속 시간이 같지 않을 수 있음을 알 수 있습니다.

예를 들어:

 

직접 연결하면 출력은 다음과 같이 됩니다.

 

또한 다중 파일 입력을 사용하기 때문에 ffmpeg는 동시에 다중 오디오 및 비디오를 디코딩하므로 많은 CPU와 메모리를 소비합니다.저자는 한때 16G 메모리 환경에서 60개 이상의 비디오 스트림을 연결하려고 시도했으며 몇 개의 프레임을 처리하기 시작했습니다. 오류: thread_get_buffer() 실패, get_buffer() 실패, 그리고 마침내 비정상적으로 종료되었습니다. 따라서 수량을 예측할 수 없는 경우에는 이 필터를 사용하지 않는 것이 좋습니다.

xfade 및 afade 필터 접합

xfade 필터는 특정 특수 효과를 사용하여 두 장면 간의 전환을 더 부드럽게 만들 수 있지만 이 필터는 화면 전환을 위해 비디오에서 일정 시간을 소비합니다.

마찬가지로 afade는 이전 오디오 스트림에서 페이드 아웃을 실현하고 후자의 오디오 스트림에서 페이드 인을 실현하고 페이드 아웃과 페이드 인의 교차점을 병합하므로 특정 오디오 지속 시간도 손실됩니다.

기본적으로 xfade 필터와 afade 필터는 모두 비디오 스트림과 오디오 스트림에서 직접 concat 작업을 수행하며 오디오 및 비디오 동기화를 포함하지 않습니다.

향상된 오디오 및 비디오 동기화

이들 클립을 분석한 결과, 각 클립의 오디오와 비디오가 동기화되어 있는 것으로 나타났으며, 클립의 오디오와 비디오의 지속 시간이 약간 다르기 때문에 문제가 발생합니다.

이 클립을 스티칭할 때 비디오 스트림 지속 시간을 참조 프레임으로 사용할 수 있습니까?

전체 아이디어

오디오 스트림은 더 이상 직접 스플라이싱을 사용하지 않지만 먼저 이전의 모든 하위 클립 비디오 스트림 지속 시간의 합과 같은 기간 동안 하위 클립의 오디오 스트림을 지연시킵니다.

지연 처리 후 모든 클립의 오디오 스트림이 하나의 기본 오디오 스트림으로 혼합되어 재생을 시작하는 오디오 스트림의 타이밍이 더 이상 이전 오디오의 길이와 관련되지 않습니다.

아래 그림과 같이:

 

미리 최종 믹스를 위해 충분히 긴 오디오 스트림을 준비합니다. 일반적으로 배경음악(BGM)을 사용하는데 필요에 따라 배경음악이 필요하지 않다면 대신 무제한 길이의 빈 오디오 스트림을 사용할 수도 있습니다.

-f lavfi -i anullsrc=r=44100:cl=stereo

구현 사례

현재 다음과 같은 세 개의 조각이 있습니다.

위 비디오 클립 파일의 오디오 스트림 채널 레이아웃은 스테레오이고 샘플링 속도는 44100Hz입니다( 여기서 위의 오디오 타임 베이스 1/44100은 샘플링 속도의 역수일 뿐이며 우연의 일치임 ) .

배경 음악이 있습니다.

위의 배경 음악 채널 레이아웃은 스테레오이고 샘플링 속도도 44100Hz입니다.

FFmpeg 스티칭 명령:

ffmpeg -i v_1.mp4 -i v_2.mp4 -i v_3.mp4 -stream_loop -1 -i bgm.mp3 \
-filter_complex "[0:v][1:v][2:v]concat=n=3:v=1:a=0;\
[0:a]anull[a_delay_0]; \
[1:a]adelay=delays=10000:all=1[a_delay_1]; \
[2:a]adelay=delays=16000:all=1[a_delay_2]; \
[3:a]volume=volume=0.2,atrim=end_sample=943740[bgm]; \
[bgm][a_delay_0][a_delay_1][a_delay_2]amix=inputs=4:duration=first" \
-b:v 2M \
-b:a 128k \
-movflags faststart \
concat.mp4

예제에서는 BGM의 길이가 충분하지만 실제 응용 시나리오에서는 배경 음악이 사용자 정의될 수 있으므로 매번 길이가 충분하지 않을 수 있습니다. 문제를 방지하기 위해 BGM 스트림은 무한 루프로 재생됩니다.

첫 번째 입력 오디오에는 지연이 없으므로 anull 필터를 사용하여 통합 스트림 레이블을 통과합니다.

지연 시간이 v_1.mp4의 비디오 스트림 지속 시간(밀리초로 변환된 초)에서 오는 v_2.mp4의 오디오 스트림 [1:a].

v_3.mp4의 오디오 스트림 [2:a], 지연 길이는 v_1.mp4의 비디오 스트림 + v_2.mp4의 비디오 스트림 길이에서 나옵니다.

bgm.mp3의 오디오 스트림[3:a]은 볼륨을 낮추면 비디오의 전체 길이에 맞게 잘립니다. 여기서는 샘플링 속도를 기반으로 한 계산이 사용됩니다.

샘플 속도를 기반으로 클립하는 이유는 무엇입니까? 위에서 볼 수 있듯이 모든 클립의 오디오 샘플링 속도는 동일해야 합니다. 따라서 상수: 44100은 1초에 44100개의 샘플이 있다는 의미입니다. 총 비디오 길이를 알고 나면 오디오에 포함되어야 하는 샘플 수로 변환할 수 있습니다. 문제를 피하기 위해 소스 배경 음악의 시간 기준이 클립의 오디오 스트림의 시간 기준과 일치하지 않을 수 있으므로 시간 기준은 사용되지 않습니다.

지금까지 모든 기본 준비가 완료되었고 나머지는 믹싱, 지연된 오디오 스트림을 모두 bgm 오디오 스트림으로 믹싱합니다. amix 필터의 기간 알고리즘이 먼저 사용하기 때문에 bgm 스트림 태그를 먼저 배치해야 합니다. 즉, 첫 번째 스트림의 기간이 유지됩니다.

지금까지 출력은 오디오와 비디오가 엄격하게 동기화된 비디오입니다.

대기 시간을 보다 정확하게 계산하는 방법

논리를 단순화하기 위해 위의 예제 명령은 단순히 지연 시간(밀리초)을 씁니다. 하지만 이 밀리초를 어떻게 계산해야 할까요?

먼저 동영상 길이를 계산하는 방법을 살펴보세요.

duration = duration_ts * time_base

예를 들어 비디오 스트림 정보는 다음과 같습니다.

"time_base": "1/12800"
"start_pts": 0
"start_time": "0.000000"
"duration_ts": 128000
"duration": "10.000000"

지금 바로:

duration = 128000 * 1 / 12800 = 10(秒)

duration_ts는 정수입니다. 나눗셈 도입으로 인해 기간을 나눌 수 없으며 기간 필드에서 정밀도가 손실됩니다.

이러한 부정확한 값이 계속 누적되면 또 다른 오류를 유발하는 것과 같습니다.

올바른 방법은 int64 유형인 각 조각의 duration_ts를 가져오는 것입니다. concat은 각 하위 클립의 비디오 스트림이 동일한 시간축을 가져야 하므로 duration_ts를 누적할 수 있습니다.

실제로 지연이 추가되면 누적된 duration_ts가 해당 지속 시간으로 변환되므로 오류는 항상 합리적인 범위 내에서 제어됩니다.

지연을 추가하는 이유

동료들과 이 솔루션에 대해 논의할 때 누군가 의심을 제기했습니다. "스플라이싱할 때 오디오와 비디오의 싱크가 맞지 않습니다. 지연과 함께 싱크가 더 맞지 않을까요?" 나중에 그의 생각을 이해한 후에 나는 그것이 매우 기본적인 질문이라는 것을 깨달았습니다. 이 사진을 보세요:

 

FFmpeg에 얼마나 많은 소스가 입력되고 얼마나 많은 스트림이 포함되더라도 demux 후에는 모든 스트림이 시간 0부터 시작되며 입력 스트림은 입력 소스의 순서로 인해 반드시 약간의 지연이 발생하지 않습니다.

또 다른 concat 구현

ffmpeg는 일괄 병합 기능을 제공합니다.먼저 다음 형식으로 접합할 파일을 나열하십시오.

file 'media_1.mp4'
file 'media_2.mp4'
...

파일로 시작하고, 작은따옴표로 파일 이름을 포함하고, 전체 파일 이름과 상대 파일 이름을 지원하고, BOM 없이 UTF-8을 사용하여 manifest.txt와 같은 매니페스트 파일로 저장합니다. 그런 다음 다음 명령을 사용하십시오.

ffmpeg -f concat -i manifest.txt -c copy concat.mp4

이 방법은 오디오와 비디오를 자동으로 정렬합니다 정렬 알고리즘은 위에서 언급한 논리와 매우 유사하며 정렬 오류를 통해서도 이루어집니다. 그러나 차이점은 오디오 부분이 패킷 데이터의 재생 시간(pts)을 직접 수정한다는 것입니다(구체적인 코드는 libavformat/concatdec.c의 concat_read_packet 함수 구현 참조). 그러나 위에서 언급한 예에서와 같이 문제도 노출되었습니다. 두 비디오 파일의 오디오 스트림이 각각의 비디오 스트림보다 짧다고 가정하면 재생 중에 가능할 수 있습니다(여기서는 플레이어 지원에 따라 가능). ) 문제를 표시할 수 없으며 정상적으로 표시됩니다. 그러나 이러한 파일을 Premiere Pro와 같은 편집 소프트웨어로 직접 가져오면 직접 연결하는 것처럼 두 오디오 조각이 나란히 재생되는 것을 볼 수 있습니다. 비디오 편집 소프트웨어에 사용되지 않더라도 이러한 스플라이싱된 비디오에서 오디오 스트림을 추출하기 위해 FFmpeg 명령을 사용하면 동일한 결과를 얻을 수 있습니다. 중간에 조용한 오디오가 없습니다.

솔루션: 오디오 스트림이 처음부터 끝까지 자연스럽게 재생될 수 있도록 두 오디오 세그먼트 사이에 무음 오디오를 보완해야 합니다.

ffmpeg -f concat -i manifest.txt -filter_complex "[0:a]aresample=async=1000" concat.mp4

또는

ffmpeg -f concat -i manifest.txt -async 1000 concat.mp4

둘 다 동일하며 중간의 빈 공간을 자동으로 조용한 오디오로 채웁니다. 그 중 async 매개변수는 조정 가능하며 일반적으로 1000 정도면 요구 사항을 대략적으로 충족할 수 있습니다.

주의할 점은 중간의 빈 공간만 채워지며 마지막 세그먼트의 오디오 스트림도 비디오 스트림보다 짧으면 비디오 끝까지 채워지지 않습니다.

일부 학생들은 예제에 두 개의 비디오 파일이 있고 오디오 스트림이 비디오 스트림보다 짧다고 생각할 수 있습니다. 비디오 스트림보다 길면 어떻게 됩니까? 이 방법을 예로 들면 접합 결과는 다음과 같습니다.

이전 비디오 스트림의 마지막 프레임은 오디오 스트림 재생이 끝날 때까지 계속 복사됩니다(비디오 스트림이 종료되지 않음).

후자의 비디오 스트림은 재생 후 중지되며 기존 플레이어 처리는 일반적으로 정지 상태를 표시하고(그러나 비디오 스트림은 이 시점에서 종료됨) 오디오 스트림은 끝날 때까지 계속 재생됩니다.

원본 FFmpeg는 오디오 및 비디오 동기화의 정확한 조각 접합을 실현합니다_ffmpeg는 음악 리듬 포인트에 따라 샷을 접합_Jack_Chai's Blog-CSDN Blog 

★기사 말미에 있는 명함은 (FFmpeg, webRTC, rtmp, hls, rtsp, ffplay, srs) 및 오디오 및 비디오 학습 로드맵 등을 포함한 오디오 및 비디오 개발 학습 자료를 무료로 받을 수 있습니다.

아래 참조!

 

추천

출처blog.csdn.net/yinshipin007/article/details/130645720