How to reduce the latency of FFmpeg h264_qsv encoder? - ffmpeg

I use FFmpeg(3.4) h264_qsv encoder in my camera-live software, I found that the encoded data recieved after sending 40 frames! There is about 1.5 seconds latency, it is not sutiable for realtime situation.How to config the encoder? Thanks a lot.
my code:
AVCodecContext* OpenH264Codec(int width, int height, int bitrate, int framerate)
{
AVCodecContext* ctx = 0;
AVCodec* c = 0;
c = avcodec_find_encoder_by_name("h264_qsv");
if (c == NULL) return NULL;
ctx = avcodec_alloc_context3(c);
if (ctx == NULL) return NULL;
ctx->width = width;
ctx->height = height;
ctx->pix_fmt = c->pix_fmts[0];
ctx->bit_rate = bitrate;
ctx->bit_rate_tolerance = ctx->bit_rate / 2;
ctx->rc_min_rate = 32000;
ctx->rc_max_rate = ctx->bit_rate*1.5;
ctx->time_base.den = framerate;
ctx->time_base.num = 1;
ctx->framerate.den = 1;
ctx->framerate.num = framerate;
ctx->gop_size = framerate*5;
ctx->max_b_frames = 0;
av_opt_set(ctx->priv_data, "preset", "veryfast", 0);
av_opt_set(ctx->priv_data, "avbr_accuracy", "1", 0);
av_opt_set(ctx->priv_data, "async_depth", "1", 0);
av_opt_set(ctx->priv_data, "profile", "main", 0);
ctx->flags |= AV_CODEC_FLAG_QSCALE;
if (avcodec_open2(ctx, c, 0) < 0)
{
avcodec_free_context(&ctx);
return NULL;
}
return ctx;
}

Thanks for Mulvya's tip. There is only 5 frames latency now.
AVCodecContext* OpenH264Codec(int width, int height, int bitrate, int framerate)
{
AVCodecContext* ctx = 0;
AVCodec* c = 0;
c = avcodec_find_encoder_by_name("h264_qsv");
if (c == NULL) return NULL;
ctx = avcodec_alloc_context3(c);
if (ctx == NULL) return NULL;
ctx->width = width;
ctx->height = height;
ctx->pix_fmt = c->pix_fmts[0];
ctx->bit_rate = bitrate;
ctx->bit_rate_tolerance = ctx->bit_rate / 2;
ctx->time_base.den = framerate;
ctx->time_base.num = 1;
ctx->framerate.den = 1;
ctx->framerate.num = framerate;
ctx->gop_size = framerate*5;
ctx->max_b_frames = 0;
av_opt_set(ctx->priv_data, "preset", "medium", 0);
av_opt_set(ctx->priv_data, "look_ahead", "0", 0);
//av_opt_set(ctx->priv_data, "look_ahead_depth", "8", 0);
if (avcodec_open2(ctx, c, 0) < 0)
{
avcodec_free_context(&ctx);
return NULL;
}
return ctx;
}

Related

How to get IDXGISurface data from IDXGISwapChain1

I use dxgi to capture window, since the windows maybe resized, so I use IDXGISwapChain1 and recreate framepool, but now I can't get the surface since GetRestrictToOutput always return null.
void OnFrameArrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,winrt::Windows::Foundation::IInspectable const &)
{
const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame = sender.TryGetNextFrame();
auto swapChainResizedToFrame = TryResizeSwapChain(frame);
winrt::com_ptr<ID3D11Texture2D> backBuffer;
winrt::check_hresult(m_swapChain->GetBuffer(0, winrt::guid_of<ID3D11Texture2D>(), backBuffer.put_void()));
winrt::com_ptr<ID3D11Texture2D> frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get());
DXGI_PRESENT_PARAMETERS presentParameters{};
m_swapChain->Present1(1, 0, &presentParameters);
//winrt::com_ptr<IDXGIOutput> dxgiOutput = nullptr;
IDXGIOutput* dxgiOutput = nullptr;
BOOL full;
m_swapChain->GetRestrictToOutput(&dxgiOutput);
if(dxgiOutput!=nullptr){
std::cerr <<"33333333333333"<<std::endl;
...
}
m_framePool.Recreate(m_device, m_pixelFormat, 2, m_lastSize);
If I use getbuffer to get the image data, if the window size is changed, then the image is black, the code is as below:
winrt::com_ptr<ID3D11Texture2D> renderBuffer;
winrt::check_hresult(m_swapChain->GetBuffer(0, winrt::guid_of<ID3D11Texture2D>(), renderBuffer.put_void()));
if(renderBuffer != nullptr){
D3D11_TEXTURE2D_DESC desc;
renderBuffer->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
winrt::com_ptr<ID3D11Texture2D> textureCopy;
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
winrt::check_hresult(d3dDevice->CreateTexture2D(&desc, nullptr, textureCopy.put()));
m_d3dContext->CopyResource(textureCopy.get(), renderBuffer.get());
winrt::com_ptr<IDXGISurface> dxgi_surface = nullptr;
HRESULT hr = textureCopy->QueryInterface(__uuidof(IDXGISurface), (void **)(&dxgi_surface));
DXGI_MAPPED_RECT mapped_rect;
hr = dxgi_surface->Map(&mapped_rect, DXGI_MAP_READ);
unsigned int imgSize = desc.Width * desc.Height * 4;
uint8_t* buffer = new uint8_t[imgSize];
int dst_rowpitch = desc.Width * 4;
for (unsigned int h = 0; h < desc.Height; h++) {
memcpy_s(buffer + h * dst_rowpitch, dst_rowpitch, (BYTE*)mapped_rect.pBits + h * mapped_rect.Pitch, min(mapped_rect.Pitch, dst_rowpitch));
}
dxgi_surface->Unmap();

How to set h264_qsv encoder based on ffmpeg4.3.1

I upgrade ffmpeg from 4.1 to 4.3.1 in my project, and i found that video quality get very low(1920x1080 25fps 2048000).
How to config encoder parameters? Thanks a lot.
AVCodecContext* OpenH264Codec_QSV(int width, int height, int frameRate, int bitRate)
{
AVCodec* c = avcodec_find_encoder_by_name("h264_qsv");
AVCodecContext* ctx = avcodec_alloc_context3(c);
ctx->width = width;
ctx->height = height;
ctx->bit_rate = bitRate;
ctx->time_base.num = 1;
ctx->time_base.den = frameRate;
ctx->gop_size = frameRate;
ctx->bit_rate_tolerance = ctx->bit_rate;
ctx->rc_max_rate = ctx->bit_rate * 2.0;
ctx->rc_min_rate = ctx->bit_rate * 0.1;
//ctx->max_b_frames = 0;
ctx->max_b_frames = 4;
ctx->pix_fmt = c->pix_fmts[0];
av_opt_set(ctx->priv_data, "preset", "medium", 0);
av_opt_set(ctx->priv_data, "profile", "main", 0);
av_opt_set(ctx->priv_data, "look_ahead", "0", 0);
if (avcodec_open2(ctx, c, nullptr) < 0)
{
avcodec_free_context(&ctx);
return nullptr;
}
return ctx;
}

ffmpeg filter dev get_video_buffer function usage

I'm writing a mosaic ffmpeg filter.
copy code from vf_vflip.c, I register a callback function get_video_buffer
and implement it as follows:
static AVFrame *get_video_buffer(AVFilterLink *link, int w, int h)
{
MosaicContext *s = link->dst->priv;
AVFrame *frame;
int i;
int j,k;
frame = ff_get_video_buffer(link->dst->outputs[0], w, h);
if (!frame)
return NULL;
if (s->w == 0 || s->h == 0)
return frame;
for (i = 0; i < 3; i ++) {
// some trick code
}
return frame;
}
static const AVFilterPad avfilter_vf_mosaic_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
//.get_video_buffer = get_video_buffer,
.filter_frame = filter_frame,
.config_props = config_input,
},
{ NULL }
};
But after read some filter dev guide, I found that only a few filter implement get_video_buffer funcition.The default function is f_default_get_video_buffer()
I make some log find that ffmpeg call function like
get_video_buffer
filter_frame
get_video_buffer
filter_frame
get_video_buffer
filter_frame
...
I'm confused about get_video_buffer do what job.
Only know that some filter will hold frame cache.
Can I juse comment out the get_video_buffer hook?
I see flip job both in get_video_buffer() and filter_frame() in vf_vflip.c
Is it a waste of time?
static AVFrame *get_video_buffer(AVFilterLink *link, int w, int h)
{
FlipContext *flip = link->dst->priv;
AVFrame *frame;
int i;
frame = ff_get_video_buffer(link->dst->outputs[0], w, h);
if (!frame)
return NULL;
for (i = 0; i < 4; i ++) {
int vsub = i == 1 || i == 2 ? flip->vsub : 0;
int height = AV_CEIL_RSHIFT(h, vsub);
if (frame->data[i]) {
frame->data[i] += (height - 1) * frame->linesize[i];
frame->linesize[i] = -frame->linesize[i];
}
}
return frame;
}
static int filter_frame(AVFilterLink *link, AVFrame *frame)
{
FlipContext *flip = link->dst->priv;
int i;
for (i = 0; i < 4; i ++) {
int vsub = i == 1 || i == 2 ? flip->vsub : 0;
int height = AV_CEIL_RSHIFT(link->h, vsub);
if (frame->data[i]) {
frame->data[i] += (height - 1) * frame->linesize[i];
frame->linesize[i] = -frame->linesize[i];
}
}
return ff_filter_frame(link->dst->outputs[0], frame);
}

Recording audio in wav format on windows

I'm trying to record audio in a wav format on windows. However the recorded bytes are always equal to zero. Am I misinterpreting somethings from the windows API?
void RecordSound(void)
{
static int ChannelOn = 0;
int err;
int fp;
static WAVEFORMATEX fmt;
static WAVEHDR hdr;
HWAVEIN hwi = 1;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 16;
fmt.wFormatTag= WAVE_FORMAT_PCM;
fmt.nChannels= 2;
fmt.nBlockAlign = fmt.wBitsPerSample*fmt.nChannels/8;
fmt.nAvgBytesPerSec= fmt.nSamplesPerSec*fmt.nBlockAlign;
hdr.lpData = (LPSTR)buf;
hdr.dwBufferLength = BUF_SIZE;
if(!ChannelOn) {
err = waveInOpen(&hwi, 0, &fmt, 0, 0, 0);
printf("\nerr = %x\n", err);
if(!err) {
waveInAddBuffer(hwi, &hdr, 0);
waveInStart(hwi);
puts("Record channel on\n");
ChannelOn = 1;
}
} else {
waveInClose(hwi);
puts("Record channel off\n");
ChannelOn = 0;
puts("Writing wav to test.wav");
fp = open("test.wav" , "w");
write(fp, &fmt, sizeof(WAVEFORMATEX));
write(fp, &buf, hdr.dwBytesRecorded);
memset(&hdr, 0 , sizeof(WAVEHDR));
memset(&fmt, 0 , sizeof(WAVEFORMATEX));
}
}

record and play file using wrapper ffmpeg

I am using C# wrapper for ffmpeg from ffmpeg
I want record rtsp stream and play it but I can not decoder frame from file
using this code I write file test.avi
unsafe
{
AVFormatContext* context = FFmpegInvoke.avformat_alloc_context();
int video_stream_index=0;
FFmpegInvoke.av_register_all();
FFmpegInvoke.avcodec_register_all();
FFmpegInvoke.avformat_network_init();
//open rtsp
if (FFmpegInvoke.avformat_open_input(&context, "rtsp://admin:admin#192.168.0.71:554", null, null) != 0)
{
return ;
}
if (FFmpegInvoke.avformat_find_stream_info(context, null) < 0)
{
return ;
}
//search video stream
for (int i = 0; i < context->nb_streams; i++)
{
if (context->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
}
AVPacket packet;
FFmpegInvoke.av_init_packet(&packet);
//open output file
AVOutputFormat* fmt = FFmpegInvoke.av_guess_format("h264", null, null);
AVFormatContext* oc = FFmpegInvoke.avformat_alloc_context();
oc->oformat = fmt;
FFmpegInvoke.avio_open2(&oc->pb, "test.mkv", FFmpegInvoke.AVIO_FLAG_WRITE, null, null);
AVStream* stream = null;
int cnt = 0;
//start reading packets from stream and write them to file
/// FFmpegInvoke.av_read_play(context);//play RTSP
while (FFmpegInvoke.av_read_frame(context, &packet) >= 0 && cnt < 1000)
{//read 100 frames
if (packet.stream_index == video_stream_index)
{//packet is video
if (stream == null)
{//create stream in file
stream = FFmpegInvoke.avformat_new_stream(oc, context->streams[video_stream_index]->codec->codec);
FFmpegInvoke.avcodec_copy_context(stream->codec, context->streams[video_stream_index]->codec);
stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio;
FFmpegInvoke.avformat_write_header(oc, null);
}
packet.stream_index = stream->id;
var p1 = new FileInfo("test.mkv").Length;
FFmpegInvoke.av_write_frame(oc, &packet);
cnt++;
}
FFmpegInvoke.av_free_packet(&packet);
FFmpegInvoke.av_init_packet(&packet);
}
FFmpegInvoke.av_read_pause(context);
FFmpegInvoke.av_write_trailer(oc);
FFmpegInvoke.avio_close(oc->pb);
FFmpegInvoke.avformat_free_context(oc);
}
and using this code I want to play me file
unsafe{
string url = "test.mkv";
FFmpegInvoke.av_register_all();
FFmpegInvoke.avcodec_register_all();
FFmpegInvoke.avformat_network_init();
AVFormatContext* pFormatContext = FFmpegInvoke.avformat_alloc_context();
if (FFmpegInvoke.avformat_open_input(&pFormatContext, url, null, null) != 0)
throw new Exception("Could not open file");
if (FFmpegInvoke.avformat_find_stream_info(pFormatContext, null) != 0)
throw new Exception("Could not find stream info");
AVStream* pStream = null;
for (int i = 0; i < pFormatContext->nb_streams; i++)
{
if (pFormatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
{
pStream = pFormatContext->streams[i];
break;
}
}
var packet = new AVPacket();
AVPacket* pPacket = &packet;
FFmpegInvoke.av_init_packet(pPacket);
AVCodecContext codecContext = *(pStream->codec);
int width = codecContext.width;
int height = codecContext.height;
AVPixelFormat sourcePixFmt = codecContext.pix_fmt;
AVCodecID codecId = codecContext.codec_id;
var convertToPixFmt = AVPixelFormat.PIX_FMT_BGR24;
SwsContext* pConvertContext = FFmpegInvoke.sws_getContext(width, height, sourcePixFmt,
width, height, convertToPixFmt,
FFmpegInvoke.SWS_FAST_BILINEAR, null, null, null);
if (pConvertContext == null)
throw new Exception("Could not initialize the conversion context");
var pConvertedFrame = (AVPicture*)FFmpegInvoke.avcodec_alloc_frame();
int convertedFrameBufferSize = FFmpegInvoke.avpicture_get_size(convertToPixFmt, width, height);
var pConvertedFrameBuffer = (byte*)FFmpegInvoke.av_malloc((uint)convertedFrameBufferSize);
FFmpegInvoke.avpicture_fill(pConvertedFrame, pConvertedFrameBuffer, convertToPixFmt, width, height);
AVCodec* pCodec = FFmpegInvoke.avcodec_find_decoder(codecId);
if (pCodec == null)
throw new Exception("Unsupported codec");
// Reusing codec context from stream info,
// as an alternative way it could look like this: (but it works not for all kind of codecs)
// AVCodecContext* pCodecContext = FFmpegInvoke.avcodec_alloc_context3(pCodec);
AVCodecContext* pCodecContext = &codecContext;
if ((pCodec->capabilities & FFmpegInvoke.CODEC_CAP_TRUNCATED) == FFmpegInvoke.CODEC_CAP_TRUNCATED)
pCodecContext->flags |= FFmpegInvoke.CODEC_FLAG_TRUNCATED;
AVFrame* pDecodedFrame = FFmpegInvoke.avcodec_alloc_frame();
if (FFmpegInvoke.av_read_frame(pFormatContext, pPacket) < 0)
throw new System.IO.EndOfStreamException();
int gotPicture = 0;
int size = FFmpegInvoke.avcodec_decode_video2(pCodecContext, pDecodedFrame, &gotPicture, pPacket);
if (size < 0)
throw new Exception(string.Format("Error while decoding frame "));
if (gotPicture == 1)
{
}
size =-22.Why? what is wrong?How play my file using ffmpeg?

Resources