I'm building a C++ application that is able to capture the screen and save the output to a file (.mkv, .mp4, doesn't matter).
I tried to switch codecs and played with the AVCodecContext settings. When subsitute the gdi capture with reading from a static .mp4 file everything works fine.
Here is my gdigrab initalization.
_inputFormat = av_find_input_format("gdigrab");
if (!_inputFormat) {
std::cout << "Unable to open input Format" << std::endl;
exit(1);
}
if (avformat_open_input(&_screenFormatContext, "desktop", _inputFormat, &_recordOptions) < 0) {
std::cout << "Unable to open input stream" << std::endl;
exit(1);
}
if (avformat_find_stream_info(_screenFormatContext, &_recordOptions) < 0) {
std::cout << "Couldn't find input stream" << std::endl;
exit(1);
}
av_dump_format(_screenFormatContext, 0, "GDI Capture", 0);
for (int i = 0; i < _screenFormatContext->nb_streams; i++) {
if (_screenFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
_videoStreamIdx = i;
break;
}
}
if (_videoStreamIdx == -1) {
std::cout << "Unable to find video Stream" << std::endl;
exit(1);
}
_codecPar = _screenFormatContext->streams[_videoStreamIdx]->codecpar;
Here is my setup code for the encoder and decoder.
/*
Decoder
*/
_dCodec = avcodec_find_decoder(_codecPar->codec_id);
if (!_dCodec) {
std::cout << "Unable to find decoder" << std::endl;
exit(1);
}
_dCodecContext = avcodec_alloc_context3(_dCodec);
if (avcodec_parameters_to_context(_dCodecContext, _codecPar) < 0) {
std::cout << "Unable to copy context" << std::endl;
exit(1);
}
if (avcodec_open2(_dCodecContext, _dCodec, NULL) < 0) {
std::cout << "Unable to open dCodec" << std::endl;
exit(1);
}
/*
Encoder
*/
_eCodec = avcodec_find_encoder(AV_CODEC_ID_H265);
if (!_eCodec) {
std::cout << "Unable to find encoder" << std::endl;
exit(1);
}
_eCodecContext = avcodec_alloc_context3(_eCodec);
//width and height have to be divisible by 2
if (_codecPar->width % 2 != 0)
_codecPar->width--;
if (_codecPar->height % 2 != 0)
_codecPar->height--;
_eCodecContext->pix_fmt = AV_PIX_FMT_YUV422P;
_eCodecContext->width = _codecPar->width;
_eCodecContext->height = _codecPar->height;
_eCodecContext->gop_size = 10;
_eCodecContext->max_b_frames = 1;
_eCodecContext->time_base.den = 30;
_eCodecContext->time_base.num = 1;
if (avcodec_open2(_eCodecContext, _eCodec, NULL) < 0) {
std::cout << "Unable to open eCodec" << std::endl;
exit(1);
}
Here is my main loop. Grabbing the packets from gdi, decoding them and encoding them.
while (av_read_frame(_screenFormatContext, _pkt) == 0) {
if (_pkt->stream_index == _videoStreamIdx) {
if (_decodePacket() < 0)
exit(1);
_encodeFrame();
}
av_packet_unref(_pkt);
}
And here the encoding/decoding functions
int ScreenRecorder::_decodePacket(void)
{
//send packet to decoder
int ret = avcodec_send_packet(_dCodecContext, _pkt);
if (ret < 0) {
std::cout << "error while sending packet to decoder" << std::endl;
return ret;
}
ret = avcodec_receive_frame(_dCodecContext, _frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return 0;
//Was there an error?
else if (ret < 0) {
std::cout << "Error with receiving frame" << std::endl;
exit(1);
}
//No errors -> got frame
std::cout << "Got frame " << _dCodecContext->frame_number << " with size " << _frame->pkt_size << std::endl;
char frame_filename[1024];
snprintf(frame_filename, sizeof(frame_filename), "%s-%d.pgm", "frame", _dCodecContext->frame_number);
//_saveGrayFrame(_frame->data[0], _frame->linesize[0], _frame->width, _frame->height, frame_filename);
return 0;
}
void ScreenRecorder::_encodeFrame(void)
{
AVPacket* pkt = av_packet_alloc();
// send frame to encoder - 0 on success
int ret = avcodec_send_frame(_eCodecContext, _frame);
if (ret < 0) {
std::cerr << "Unable to send frame! ERRORCODE: " << ret << std::endl;
exit(1);
}
while (ret == 0) {
ret = avcodec_receive_packet(_eCodecContext, pkt);
//Do I need to send more packets?
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
std::cout << "Needing one more packet " << std::endl;
av_packet_unref(pkt);
return ;
}
//Was there an error?
else if (ret < 0) {
std::cout << "Error with receiving packet" << std::endl;
exit(1);
}
//No errors -> got packet
std::cout << "Got packet " << pkt->pts << " of size " << pkt->size << std::endl;
if (av_write_frame(_outFormatContext, pkt) < 0) {
std::cout << "Unable to write frame" << std::endl;
exit(1);
}
av_packet_unref(pkt);
}
}
In case you still need the header file
#ifndef SCREENRECORDER_HPP
# define SCREENRECORDER_HPP
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
}
class ScreenRecorder
{
private:
AVCodecContext *_dCodecContext;
AVCodecContext *_eCodecContext;
AVFormatContext *_fileFormatContext;
AVFormatContext *_screenFormatContext;
AVFormatContext *_outFormatContext;
AVInputFormat *_inputFormat;
AVCodecParameters *_codecPar;
AVStream *_videoTrack;
AVCodec *_dCodec;
AVCodec *_eCodec;
AVFrame *_frame;
AVPacket *_pkt;
AVDictionary *_options;
AVDictionary *_recordOptions;
int _videoStreamIdx;
int _decodePacket(void);
void _encodeFrame(void);
void _openInputFile(void);
void _initalizeCodec(void);
void _initalizeOutputFile(void);
void _closeCodec(void);
void _initalizeGDI(void);
void _saveGrayFrame(unsigned char* buf, int wrap, int xsize, int ysize, char* filename);
public:
ScreenRecorder();
~ScreenRecorder();
void start();
};
#endif
When I run my code I am getting Exception thrown at 0x00007FF819864A80 (msvcrt.dll) in Streaming.exe: 0xC0000005: Access violation reading location 0x0000000000000EF0. It comes from avcodec_send_frame function. Seems like I'm trying to access memory that wasn't allocated. I am new to the ffmpeg lib and hope that someone can get me on the right track. Thank you very much. If you have any questions please ask them.
User contributions licensed under CC BY-SA 3.0