구글애드센스


[FFMPEG] av_rescale_q 사용법. STUDY



1. Time Stamp? PTS? DTS?

ffmpeg 으로 데이터를 저장하든 혹은 어디로 쏴주든 할 때 제일 짜증나는 것들 중에 하나가 바로 PTS(Presentation Time Stamp) 와 DTS(Decoding Time Stamp)값 설정이다.
일단, 잘 모르는 사람일 경우 PTS 라는 것 자체가 뭔지 몰라서 뭔가 타임스탬프 값이니까 tick 값 넣으면 되겠지? 하고 (윈도우 환경같으면) GetTickCount() 같은걸 끼얹는다던가 그냥 1씩 순차적으로 증가시키면서 넣는다던가 삽질을 했을것이다. 혹은 아예 안넣어버리던가? 했을테다.
그러면 어떤경우엔 잘 나오는데 (이건 ffmpeg이 알아서 값을 생성) 어떤경우엔 또 잘 나오질 않게된다.
공부를 어느정도 했다면 PTS/DTS 등등이 뭔지는 알것이다. 
인코딩되어있는 데이터를 받아 화면에 뿌리기 위해서 "못해도 이때까지는 디코딩이 되어야한다" 라는 것을 알려주는 것이 바로 DTS 이고, 그리고 또 "못해도 이때까지는 화면에 보여야한다" 라는 것을 알려주는 것이 PTS 이다.
다시 설명하자면, 타임스탬프라는 것은 의미 그대로 "시간도장"이다. 우리가 영상이 움직인다라고 느끼는 것은 정지되어있는 사진이 일정시간의 갭을 갖고 변화할 때이다. 각각의 사진에는 촬영된 시점의 시간정보가 있을 것이고 그것을 우리는 timestamp 라고 부른다. PTS든, DTS든 목적이 좀더 구질구질하게 더 붙는 timestamp 라고 생각하면 된다.

2. 왜 적당한 Time Stamp 가 들어가야 하나?

다시 본론으로 돌아와서..
그럼 알맞은 PTS와 DTS는 어떻게 넣을까?
일단 "알맞다" 라는 뜻은 "미리 정해져있는 룰에 부합하는" 이라고 설명할 수 있겠다. 그렇다면 이 알맞은 시간은 무엇에 좌우될까? 이는 사용하는 코덱의 시스템(?)에 좌우된다. 여기서 나오는 개념이 PCR (Program Clock Reference) 인데 나도 정확하게는 모르니까 설명은 안쓰도록 하겠다.
어쨌거나, 예를 들자면 H.264의 알맞은 timestamp 주기는 몇일까? 90000 이다. 90 khz 이다. (이 정보를 어디서 얻느냐면, 검색하다 보면 주로 영어로 써져있는 문서 (rfc, ietf) 에 스펙에 나와있다.)
이 말은, 1초분량의 영상을 찍었다고 가정했을 때 제일 처음 찍은 정지영상의 timestamp 와 가장 마지막에 찍은 정지영상의 timestamp 단위 차이가 90000 이라는 것이다. (오디오로 치면 sample rate에 해당) 다시말하면 즉, 1초동안 내가 30장의 영상을 저장하거나 쏜다고 하면, 첫번째 장의 timestamp 가 1이라면 마지막 30번째 장의 timestamp 는 90001이 된다는 얘기.
그럼 PTS/DTS는 Tick 값(ms 단위)에 단순히 90만 곱해서 보내면 된다는 것!
근데, 이 Tick 값이라는 것이 아주 정확하면 말을 안하는데 이게 약간의 오차만 있어도 파일이 제대로 플레이가 안된다던가, 스트림으로 쐈는데 받는 쪽에서 이상하게 튄다던가 하는 경우가 종종 발생한다.

3. 정확한 Time Stamp 계산을 위한 av_rescale_q

이를 해결하기 위해 있는 녀석이 바로 av_rescale_q 이다. 아 이거 하나 설명하자고 부가설명이 너무 많았다. 먼저 av_rescale_q 선언을 보도록 하겠다.

1
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const

대충 이렇게 생겼다. ffmpeg의 프로토타입 선언은 좀 불친절하다. 
설명하자면 반환값은 계산된 timestamp 값을 뱉도록 되어있다.
첫 번째 인자인 a는 증가값이다. 내가 ms 단위로 영상의 시간값을 관리했고, 30fps 라면 영상은 (1, 34, 67, 100 ....) 의 시간 값을 가지게 될텐데 (1, 34, 67... ) 등의 값을 넣는다는 것.
두 번째 인자는 첫 번째 인자가 어떤 time base system으로 돌아가는지를 넣는것이다. ms 단위로 찍은거니까 (AVRational){1,1000} 으로 넣어준다.
세 번째 인자는 변환할 timestamp 단위의 time base system을 명시한다. h.264 용으로 할꺼면 (AVRational){1, 90000} 이 되겠다.



덧글

  • aerozepp 2018/04/18 15:55 # 삭제 답글

    av_seek_frame()을 사용하여 원하는 지점 T (예를들어 10초)로 이동하려고 할때

    AVRational bq = new AVRational(){ num = 1, den = ffmpeg.AV_TIME_BASE };

    T = 10 * AV_TIME_BASE;

    T = av_rescale_q(T, bq, inputFmCtx->streams[inputFile.v_index]->time_base);

    ffmpeg.av_seek_frame(inputFmCtx, inputFile.v_index, T, ffmpeg.AVSEEK_FLAG_ANY));

    하게 되면 정확히 10초 00 프레임으로 가는게 아니라 10초 +10 프레임정도 더 가는 이유 혹시 아시나요?
  • muzie 2018/04/24 10:04 #

    정확한건 코드를 봐야 알겠지만, I 프레임 간격이 얼마로 설정되어있는지 체크해보세요.
댓글 입력 영역