Setting bitrate of video in FFmpeg - c++

I use FFmpeg to record videos from a RTSP stream (the codec is H.264). It works. But I face a problem with the bitrate value. First, I set bitrate like below, but it doesn't work:
AVCodecContext *m_c;
m_c->bit_rate = bitrate_value;
Following this question I can set bitrate manually with this command:
av_opt_set(m_c->priv_data, "crf", "39", AV_OPT_SEARCH_CHILDREN);
But I have to test several times to choose value '39', which creates acceptable video quality. It's hard to do it again if I use another camera setting (image width, height, etc). Is there a way to set bitrate more easily, and adaptively?

Related

How to read alpha channel from .webm video using ffmpeg in c++

Background
I have a .webm file (pix_fmt: yuva420p) converted from .mov video file in order to reduce file size and I would like to read the video data using c++, so I followed using this repo as a reference.
This works perfectly on .mov video.
Problem
By using same repo, however, there is no alpha channel data (pure zeros on that channel) for .webm video but I can get the alpha data from .mov video.
Apparently many people already noticed that after the video conversion, ffmpeg somehow detect video as yuv420p + alpha_mode : 1 and thus alpha channel is not used but there is no one discuss about workaround of this.
I tried forcing pixel format during this part to use yuva420p but that just broke the whole program.
// Set up sws scaler
if (!sws_scaler_ctx) {
auto source_pix_fmt = correct_for_deprecated_pixel_format(av_codec_ctx->pix_fmt);
sws_scaler_ctx = sws_getContext(width, height, source_pix_fmt,
width, height, AV_PIX_FMT_RGB0,
SWS_BILINEAR, NULL, NULL, NULL);
}
I also verified my video that it contains alpha channel using other source so I am sure there is alpha channel in my webm video but I cannot fetch the data using ffmpeg.
Is there a way to fetch the alpha data? Other video format or using other libraries work as well as long as it does have some file compression but I need to access the data in c++.
Note: This is the code I used for converting video to webm
ffmpeg -i input.mov -c:v libvpx-vp9 -pix_fmt yuva420p output.webm
You have to force the decoder.
Set the following before avformat_open_input()
AVCodec *vcodec;
vcodec = avcodec_find_decoder_by_name("libvpx-vp9");
av_fmt_ctx->video_codec = vcodec;
av_fmt_ctx->video_codec_id = vcodec->id;
You don't need to set pixel format or any scaler args.
This assumes that your libavcodec is linked with libvpx.

Opencv VideoWriter with Gstreamer backend produce low quality video file

I am trying to write frames (cv::Mat) into a video file using cv::VideoWriter. I need maximum speed (latency should be minimum). So, I was trying to use Gstreamer with x264 encoding. I wrote the following code:
std::string command = "appsrc ! videoconvert ! x264enc ! filesink location=output.mp4";
auto writer_ =
cv::VideoWriter(command, cv::CAP_GSTREAMER, 0, frameRate_, cv::Size(frameWidth_, frameHeight_), true);
// frameRate_ = 25
// frameWidth_ = 1920
// frameHeight_ = 1080
//...
// for example
// auto frame = cv::Mat(cv::Size(1920, 1080), CV_8UC3, 0);
writer_.write(frame);
Everything works fine, but the output video has very low quality (not in terms of resolution, the resolution is still the same). The frames are pixelated. I have searched the Internet but could not find the reason why this is happening.
What can be the reason for this?
Suggestions on a faster video writing method (in OpenCV) are also appreciated!
Edit: I tried #Micka's suggestion and changed the bitrate (to >10000 to achieve the required quality) but the latency increased significantly. Is there a faster way to save videos without losing quality much?

Media Foundation: H264 Encoder Dropping Frames

I am trying to encode frame data from a monitor onto an MP4 file using MFVideoFormat_H264 and a sink writer on Media Foundation using MFCreateSinkWriterFromURL. I configured my input IMFMediaType to contain
inputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
inputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
inputMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
MFSetAttributeRatio(inputMediaType, MF_MT_FRAME_RATE, 60, 1);
MFSetAttributeRatio(inputMediaType, MF_MT_FRAME_RATE_RANGE_MAX, 120, 1);
FSetAttributeRatio(inputMediaType, MF_MT_FRAME_RATE_RANGE_MIN, 1, 1);
MFSetAttributeRatio(inputMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
MFSetAttributeSize(inputMediaType, MF_MT_FRAME_SIZE, 1920, 1080);
all on a 1080p monitor at 60Hz refresh rate. My outputMediaType is similar other than
outMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
outputMediaType->SetUINT32(MF_MT_AVG_BITRATE, 10000000);
The sink writer itself is also configured such that MF_SINK_WRITER_DISABLE_THROTTLING=TRUE and
MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS=TRUE to get the best possible performance out of it using hardware acceleration when available. Everything works and videos get created successfully. However, each video seems to have stutter across the entire duration. I've attempted lowering the bitrate and raising the average FPS to try and compensate but its more putting a bandaid on it. My assumption is that there are dropped frames causing this stutter as a result of a bucket overflowing?
Is anyone aware of a fix for this issue of frame drops/stuttering in the final video file while retaining the h264 encoding format?
EDIT: I've also tinkered with the different attributes on the input and output types setting
hr = MFSetAttributeRatio(pMediaTypeIN/OUT, MF_MT_FRAME_RATE_RANGE_MIN, 60, 1); but to no avail.

Handling Image data from IMFSourceReader and IMFSample

I am attempting to use the IMFSourceReader to read and decode a .mp4 file. I have configured the source reader to decode to MFVideoFormat_NV12 by setting a partial media type and calling IMFSourceReader::SetCurrentMediaType and loaded a video with dimensions of 1266x544.
While processing I receive the MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED flag with a new dimension of 1280x544 and a MF_MT_MINIMUM_DISPLAY_APERTURE of 1266x544.
I believe the expectation is to then use either the video resizer dsp or video processor mft. However it is my understanding that the video processor mft requires windows 8.1 while I am on windows 7, and the video resizer dsp does not support MFVideoFormat_NV12.
What is the correct way to crop out the extra data added by the source reader to display only the data within the minimum display aperture for MFVideoFormat_NV12?
New media type says this: "video is 1266x544 and you expected/requested, but I have to carry it in 1280x544 textures because this is how GPU wanted it to work".
Generally speaking this does not require further scaling or cropping you already have the frames you need. If you are reading them out of sample objects - which is what I believe you are trying to do - just use increased stride (1280 bytes between consecutive rows).
If you are using this as a texture, presenting it somewhere or using it as a part of rendering, you would just use adjusted coordinates (0, 0) - (1266, 544) ignoring the remainder, as opposed to using full texture.

Synchronizing FFMPEG video frames using PTS

I'm attempting to synchronize the frames decoded from an MP4 video. I'm using the FFMPEG libraries. I've decoded and stored each frame and successfully displayed the video over an OPENGL plane.
I've started a timer just before cycling through the frames; the aim being to synchronize the Video correctly. I then compare the PTS of each frame against this timer. I stored the PTS received from the packet during decoding.
What is displayed within my application does not seem to play at the rate I expect. It plays faster than the original video file would within a media player.
I am inexperienced with FFMPEG and programming video in general. Am I tackling this the wrong way?
Here is an example of what I'm attempting to do
FrameObject frameObject = frameQueue.front();
AVFrame frame = *frameObject.pFrame;
videoClock += dt;
if(videoClock >= globalPTS)
{
//Draw the Frame to a texture
DrawFrame(&frame, frameObject.m_pts);
frameQueue.pop_front();
globalPTS = frameObject.m_pts;
}
Please note I'm using C++, Windows, Opengl, FFMPEG and the VS2010 IDE
First off, Use int64_t pts = av_frame_get_best_effort_timestamp(pFrame) to get the pts. Second you must make sure both streams you are syncing use the same time base. The easiest way to do this is convert everything to AV_TIME_BASE_Q. pts = av_rescale_q ( pts, formatCtx->streams[videoStream]->time_base, AV_TIME_BASE_Q ); In this format, pts is in nanoseconds.