Videos encoded with FFmpeg play too fast

2019-02-26 03:43发布

I've scoured the Google/SO/Zeranoe results and tried to integrate everything I found into making my program generate videos correctly but I still can't get it to work right.

I needed a good video to use as a reference so I ran:

ffmpeg -t 5 -f dshow -i "video=Logitech HD Pro WebcamC910" CLI.mpg

Which generated a five-second video which can be played in VLC and shows the duration and plays correctly. You can get the video here: https://drive.google.com/file/d/0B1VGi1Ts7D7TR3c4VUlyM2dIcFk/edit?usp=sharing

I then generated a five-second video with my own code. I've tried to pare it down to only what I think is needed to record. You can find the code here: https://drive.google.com/file/d/0B1VGi1Ts7D7TMnFxSE1HX2FKbEU/edit?usp=sharing

It generated a video also, with similar video quality, but VLC doesn't show the video duration and the video seems to play too quickly. I mean that what I see on the screen looks like it's moving slightly too fast. You can find the video my code generated here: https://drive.google.com/file/d/0B1VGi1Ts7D7TSzFGUFZEMHJwQ0U/edit?usp=sharing

Even when you click on the links to the videos, you can see that Google is having trouble with mine but no problem with the one generated by the FFmpeg CLI. Sorry about posting everything off-site but I didn't want to spam my code into the post and I wanted to provide as much information as I could.

I've seen a number of posts about this but I can't seem to find a definitive solution.

EDIT: So I implemented your suggestions and I'm pretty sure that your answer fixed the timing issue but now I consistently get 20 non-strictly-monotonic errors before the first successful call encode_video. This happens whether I use

gFrame->pts = gFrameIndex;

or

gFrame->pts = av_rescale_q(gFrameIndex, gCodecContext->time_base, gStream->codec->time_base);

before

ret = avcodec_encode_video2(gCodecContext, &pkt, gFrame, &got_output);
if (ret < 0) {
  fprintf(stderr, "Error encoding frame\n");
  return false;
}

This seems to coincide with video artifacts at the start of the video file during playback. gFrameIndex starts at 1.

I believe my webcam auto-focuses at the start of recordings, is it possible this is related?

I've uploaded the generated .h264 file at https://drive.google.com/file/d/0B1VGi1Ts7D7TRjRzbzZZemRaRTA/edit?usp=sharing and my most recent code to https://drive.google.com/file/d/0B1VGi1Ts7D7TbmlyYncxYzRQUTA/edit?usp=sharing.

I really appreciate the help. Sadly, I can't use the FFmpeg CLI directly in my software, so I have to use the library. I'm going to have to keep trucking along with it unless you can suggest a better alternative.

标签: video ffmpeg
1条回答
Lonely孤独者°
2楼-- · 2019-02-26 04:10

Usually before you call avcodec_encode_video2() you set the frame's timestamp, e.g.:

gFrame->pts = gFrameIndex;

gFrameIndex is incremented by 1 each encoded frame, which should be correct in your case because your time_base is 1/30 and each frame represents 1/30th second duration.

Then be careful here:

  if (pkt.pts != AV_NOPTS_VALUE) pkt.pts = av_rescale_q(gCodecContext->coded_frame->pts, gCodecContext->time_base, gStream->time_base);
  if (pkt.dts != AV_NOPTS_VALUE) pkt.dts = av_rescale_q(gFrameIndex, gCodecContext->time_base, gStream->time_base);

Are you having problems because you are using libx264 to encode? I noticed that in that case, you have to rescale the timestamps before and after calling avcodec_encode_video2(), e.g.:

gFrame->pts = av_rescale_q(gFrameIndex, gCodecContext->time_base, gStream->codec->time_base);
[...]
avcodec_encode_video2()
[...]
pkt.pts = av_rescale_q(pkt.pts, gStream->codec->time_base, gStream->time_base);
pkt.dts = av_rescale_q(pkt.dts, gStream->codec->time_base, gStream->time_base);

It's because the ffmpeg interface with libx264 is not of very high quality.

Is your webcam dropping frames? If so then you'll need to give the frames real timestamps. Create a function that returns the elapsed time since you started the capture in milliseconds (an integer). Then set time_base to {1,1000} and set gFrame->pts to the return value of your function. But be careful: you can't have a timestamp that is <= a previous timestamp. So you will need to drop frames if you get several all at once (or write another mechanism for dealing with this situation). BTW, this is all done for you in the ffmpeg CLI program, which is why so few people try to use ffmpeg the library ...

查看更多
登录 后发表回答