How to get correct frame duration using ffmpeg? - c++

In my file "Video.avi", all pkt_duration attributes in video AVFrame are equal to 1.
But the best_effort_timestamp attributes in AVFrame for the first video frames are : 0, then 3, then 4, then 5, then 7 .. etc, which means for example that the duration of the first frame is 3 and not 1. (the time_base is 1/30)
Is there a better way to know the correct duration of a frame than reading the next frame and getting its best_effort_timestamp and computing the difference between the two best_effort_timestamp?
If not, is there a way to read only the header of the next packet just to get its best_effort_timestamp and not wasting time to decompress anything ?
Thank you for your help.

Related

C++ ffmpeg get the current frame

I'm trying to seek to a certain part of a video using ffmpeg. So far I've got this:
int64_t pts = (int64_t)( ((float) timestamp_to_go / 1000)* (double)time_base.den / (double)time_base.num);
if(av_seek_frame(av_format_ctx, video_stream_index, pts, AVSEEK_FLAG_BACKWARD) < 0 )
exit(0);
This allows me to seek to the closest IFrame. For example if I try to seek to the 10th second of a video, it seeks to the 8.5th second of a video. This is fine since I can just decode till I reach the 10th seconds and go on with my day.
However I couldn't figure out how to get the current frame index. After I seek to the frame using the code above, I need to figure out which frame/timestamp I'm currently at so I can decode until I reach the timestamp desired.
For example: If I try to seek to 10 and get 8.5 like example above, for a video with 30 fps I need to get 255 so I can decode untill I reach the 300th frame, which corresponds to the 10th second.

FFmpeg returns negative PTS and DTS for first non key frame

We have some envelope of FFmpeg, that processes the video.
The tree first frames of video are: B -> B -> I as shown below:
PTS and DTS returns negative for first frame:
We have some code, that skips the frames that are below some timepoint (0 for first frame).
Is it possible to ask FFmpeg to start from first frame and not from first I frame?
I found that we modified AVContext::strat_time manualy, and set value based on DTS of a first valid video packet. The problem solved, when I set value based on PTS of a first valid packet as a start_time.

Get the frame from Video by the time (openCV)

I have a video and I have important times in this video
For example:
"frameTime1": "00:00:01.00"
"frameTime2": "00:00:02.50"
"frameTime2": "00:00:03.99"
.
.
.
I get the FPS, and I get the totalFrameCount
If I want to get the frames in that's times for example the frame that's happen in this time "frameTime2": "00:00:02.50" I will do the following code
FrameIndex = (Time*FPS)/1000; //1000 Because 1 second = 100 milli second
In this case 00:00:02.50 = 2500 milli second, and the FPS = 29
So the FrameIndex in this case is 72.5, in this case I will choose either frameNO: 72 or 73, but I feel that's not accurate enough, any better solution?
What's the best and accurate way to do this?
The most accurate thing you have at your disposal is the frame time. When you say that an event occurred at 2500ms, where is this time coming from? Why is it not aligned with your framerate? You only have video data points at 2483ms and 2517ms, no way around that.
If you are tracking an object on the video, and you want its position at t=2500, then you can interpolate the position from the known data points. You can do that either by doing linear interpolation between the neighboring frames, or possibly by fitting a curve on the object trajectory and solving for the target time.
If you want to rebuild a complete frame at t=2500 then it's much more complicated and still an open problem.

How to find and decode efficiently Nth frame with libavcodec?

Please, this is not duplicate of similar posts!
I want to find and to decode Nth frame, for example 7th frame.
As I understood, using time_base I can calculate how many ticks is each frame and by multiplying it with 7 we will get position of 7th frame. To calculate the ticks I do
AVStream inStream = getStreamFromAVFormatContext();
int fps = inStream->r_frame_rate.num;
AVRational timeBase = inStream->time_base;
int ticks_per_frame = (1/fps) / timeBase;
int _7thFramePos = ticks_per_frame * 7;
Did I calculated correctly position of 7th frame? If I did, so to go to that frame I just do av_seek_frame(pFormatCtx, -1, _7thFramePos, AVSEEK_FLAG_ANY), right?
What happens if the 7th frame was P-Frame or B-Frame, how I decode it?
I noticed that the calculated value differs from inStream->codec->ticks_per_frame, why? Shouldn't they be the same? What is the difference?
This post explains the issue nicely.
http://www.hackerfactor.com/blog/index.php?/archives/307-Picture-Go-Back.html
[1] comment for AVStream structure clearly mentions that "r_frame_rate" is a guess and may not be accurate, because even if I have frame-rate of (say) 25fps, in term of base_time I may have 24 or 26 frames in a second.
[2] To find the exact frame number you need to decode frame from the start and keep a counter, but that is very in-efficient, this can be optimized for some file-formats like MP4 where information about every frame is present in file-header.

Retrieving the current frame number in OpenCV

How can I retrieve the current frame number of a video using OpenCV? Does OpenCV have any built-in function for getting the current frame or I have to do it manually?
You can use the "get" method of your capture object like below :
capture.get(CV_CAP_PROP_POS_FRAMES); // retrieves the current frame number
and also :
capture.get(CV_CAP_PROP_FRAME_COUNT); // returns the number of total frames
Btw, these methods return a double value.
You can also use cvGetCaptureProperty method (if you use old C interface).
cvGetCaptureProperty(CvCapture* capture,int property_id);
property_id options are below with definitions:
CV_CAP_PROP_POS_MSEC 0
CV_CAP_PROP_POS_FRAME 1
CV_CAP_PROP_POS_AVI_RATIO 2
CV_CAP_PROP_FRAME_WIDTH 3
CV_CAP_PROP_FRAME_HEIGHT 4
CV_CAP_PROP_FPS 5
CV_CAP_PROP_FOURCC 6
CV_CAP_PROP_FRAME_COUNT 7
POS_MSEC is the current position in a video file, measured in
milliseconds.
POS_FRAME is the position of current frame in video (like 55th frame of video).
POS_AVI_RATIO is the current position given as a number between 0 and 1
(this is actually quite useful when you want to position a trackbar
to allow folks to navigate around your video).
FRAME_WIDTH and FRAME_HEIGHT are the dimensions of the individual
frames of the video to be read (or to be captured at the camera’s
current settings).
FPS is specific to video files and indicates the number of frames
per second at which the video was captured. You will need to know
this if you want to play back your video and have it come out at the
right speed.
FOURCC is the four-character code for the compression codec to be
used for the video you are currently reading.
FRAME_COUNT should be the total number of frames in video, but
this figure is not entirely reliable.
(from Learning OpenCV book )
In openCV version 3.4, the correct flag is:
cap.get(cv2.CAP_PROP_POS_FRAMES)
The way of doing it in OpenCV python is like this:
import cv2
cam = cv2.VideoCapture(<filename>);
print cam.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)