<div>Hi,<br></div><div>please give me some advice i wrote simple ffmpeg based encoder and i cant correct memory leak in it, so far I have found that it occurs when i am closing the AVFormatContext and AVStreams on the moment I am trying to understand the source code of ffmpeg.c</div>
<div><br></div><div>QVideoEncoder.h partial code:</div><div><br></div><div>class QVideoEncoder : public VirtualEncoder<br>{<br> Q_OBJECT<br> Q_INTERFACES(VirtualPlugin)<br> Q_INTERFACES(VirtualEncoder)<br> <br>
private:<br> <br> static bool _initialized_;<br> <br> long long int _time;<br> long long int _frames;<br> bool _running;<br> float _level;<br> int _fps;<br> QString _format;<br> QString _name;<br>
<br> int _count;<br> unsigned int _width;<br> unsigned int _height;<br> unsigned int _bitrate;<br> unsigned int _gop;<br> VirtualLog *_log;<br> bool _ok;<br><br> //QByteArray <br> <br>
VFSHandle *_handle;<br> <br> VirtualFS *_vfs;<br> <br> QMutex *ptr;<br> <br> // FFmpeg stuff<br> ffmpeg::AVFormatContext *FormatCtx;<br> ffmpeg::AVOutputFormat *OutputFormat;<br> ffmpeg::AVCodecContext *CodecCtx;<br>
ffmpeg::AVStream *VideoStream;<br> ffmpeg::AVCodec *codec;<br> ffmpeg::AVFrame *frame;<br> uint8_t *picture_buf;<br> int outbuf_size;<br> uint8_t* outbuf;<br> ffmpeg::SwsContext *img_convert_ctx;<br>
// ffmpeg::AVPacket *pkt;<br></div><div><br></div><div>//{...}</div><div><br></div><div>};</div><div><br></div><div><br></div><div><br></div><div>QVideoEncoder.cxx partial code:</div><div><br></div><div>bool QVideoEncoder::createFile(QString fileName,unsigned w,unsigned h,unsigned bitrate,unsigned gop)<br>
{<br> close();<br><br> QMutexLocker lock(locker());<br> <br> _width = w;<br> _height = h;<br> _gop = gop;<br> _bitrate = bitrate;<br><br> initVars();<br> _frames = 0;<br> _time = 0;<br> <br> <br> if(!isSizeValid())<br>
{<br> writeLog(QString("QVideoEncoder::createFile: invalid image size"),LogType::ERROR);<br> return false;<br> }<br> <br> if(!(OutputFormat = ffmpeg::av_guess_format(format().toStdString().c_str(), NULL , NULL)))<br>
OutputFormat = ffmpeg::av_guess_format("mpeg", NULL, NULL);<br> <br> if(!(FormatCtx = ffmpeg::avformat_alloc_context()))<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to allocate format context"),LogType::ERROR);<br>
return false;<br> }<br> <br> FormatCtx->oformat = OutputFormat;<br> snprintf(FormatCtx->filename, sizeof(FormatCtx->filename), "%s", fileName.toStdString().c_str());<br><br> if(!(VideoStream = av_new_stream(FormatCtx,0)))<br>
{<br> writeLog(QString("QVideoEncoder::createFile: failed to open stream"),LogType::ERROR);<br> return false;<br> }<br> <br> CodecCtx = VideoStream->codec;<br> <br> CodecCtx->codec_id = OutputFormat->video_codec;<br>
CodecCtx->codec_type = ffmpeg::CODEC_TYPE_VIDEO;<br> CodecCtx->thread_count = threadCount();<br> <br> if(!(codec = avcodec_find_encoder(CodecCtx->codec_id)))<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to find specified encoder"),LogType::ERROR);<br>
return false;<br> }<br> <br> avcodec_get_context_defaults(CodecCtx); //trust me ffmpeg knows it better than ya'all ;-)<br> CodecCtx->width = w;<br> CodecCtx->height = h;<br> <br> int t = fps();<br>
<br> <br> ffmpeg::AVRational fps = (ffmpeg::AVRational){1,t};<br> <br> if (codec && codec->supported_framerates)<br> fps = codec->supported_framerates[ffmpeg::av_find_nearest_q_idx(fps, codec->supported_framerates)];<br>
<br> setFps(fps.den);<br> <br> <br> CodecCtx->time_base.den = fps.den;<br> CodecCtx->time_base.num = fps.num;<br> CodecCtx->pix_fmt = ffmpeg::PIX_FMT_YUV420P;<br>CodecCtx->compression_level = compressionLevel();</div>
<div>CodecCtx->qblur = 0.2;<br> <br> CodecCtx->me_range = 16;<br> CodecCtx->max_qdiff = 4;<br> CodecCtx->qmin = 10;<br> CodecCtx->qmax = 51;<br><br> CodecCtx->gop_size = this->fps();<br>
<br> if(FormatCtx->oformat->flags & AVFMT_GLOBALHEADER)<br> CodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;<br><br><br> if (av_set_parameters(FormatCtx, NULL) < 0)<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to set a/v parameters"),LogType::ERROR);<br>
return false;<br> }<br> <br> if (avcodec_open(CodecCtx, codec) < 0)<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to open a/v codec"),LogType::ERROR);<br> return false;<br> }<br><br>
if(!initOutputBuf())<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to init output buff"),LogType::ERROR);<br> return false;<br> }<br> <br> if(!initFrame())<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to init a/v frame"),LogType::ERROR);<br>
return false;<br> }<br> <br> VFSHandle *handle = _vfs->open(fileName);<br> <br> setVFSHandle(handle);<br> <br> <br> if(avio_open_dyn_buf(&FormatCtx->pb) < 0)<br> {<br> writeLog(QString("QVideoEncoder::createFile: failed to open dynamic buffer"),LogType::ERROR);<br>
return false;<br> }<br> <br> av_write_header(FormatCtx);<br> <br> uint8_t *p = 0;<br> int len = avio_close_dyn_buf(FormatCtx->pb,&p);<br> <br> if(len)<br> {<br> if(handle)<br> handle->write((const char*)p,len);<br>
<br> <br> }<br> <br> if(p)<br> ffmpeg::av_free(p);<br> <br> _ok = true;<br><br> if(!_editor)<br> _editor = /*new GLFrameEditor(width(),height());*/ new BasicFrameEditor(width(),height());<br> <br> return true;<br>
}<br></div><div><br></div><div>int QVideoEncoder::encodeImage(const QImage &i)<br>{ <br> QMutexLocker lock(locker());<br> <br> //QImage i;<br> <br> if(!isOk())<br> return -1;<br> <br> ++_frames;<br> int t = 1000/fps();<br>
_time += t;<br> <br> <br> convertImage_sws(i); // SWS conversion<br> frame->pts = _time*90;<br> <br> int out_size = ffmpeg::avcodec_encode_video(CodecCtx,outbuf,outbuf_size,frame);<br><br> if (out_size > 0)<br>
{ <br> <br> ffmpeg::AVPacket pkt;<br> //ffmpeg::AVPacket *pkt = &_pkt;<br> pkt.destruct = ffmpeg::av_destruct_packet;<br> av_init_packet(&pkt);<br><br> <br> //qDebug() << &pkt;<br> <br> <br>
if (pkt.dts != AV_NOPTS_VALUE)<br> pkt.dts = av_rescale_q(pkt.dts, CodecCtx->time_base, VideoStream->time_base);<br> <br> if (pkt.pts != AV_NOPTS_VALUE)<br> pkt.pts = av_rescale_q(pkt.pts, CodecCtx->time_base, VideoStream->time_base);<br>
<br> pkt.duration = av_rescale_q(pkt.duration, CodecCtx->time_base, VideoStream->time_base);<br> <br> if(CodecCtx->coded_frame->key_frame)<br> pkt.flags |= PKT_FLAG_KEY;<br><br> pkt.stream_index= VideoStream->index;<br>
pkt.data = outbuf;<br> pkt.size = out_size;<br> pkt.duration = 1000/fps();<br><br> <br> if(avio_open_dyn_buf(&FormatCtx->pb) < 0)<br> {<br> writeLog(QString("QVideoEncoder::encodeImage: failed to open dynamic buffer"),LogType::ERROR);<br>
return false;<br> }<br> <br> int ret = av_write_frame(FormatCtx, &pkt);<br> <br> uint8_t *p = 0;<br> int len = avio_close_dyn_buf(FormatCtx->pb,&p);<br><br> if(len)<br> {<br> if(ret < 0)<br> {<br> qDebug() << "dupa";<br>
return -1;<br> }<br> <br> if(vfsHandle())<br> { <br> vfsHandle()->write((const char*)p,len);<br> vfsHandle()->flush();<br> }<br> }<br> <br> if(p)<br> ffmpeg::av_free(p);<br> <br> //if(pkt)<br> //ffmpeg::av_free_packet(&pkt);<br>
}<br><br> return out_size;<br>}<br></div><div><br></div><div>bool QVideoEncoder::convertImage_sws(const QImage &img)<br>{<br> //QMutexLocker lock(locker());<br> <br> if((const unsigned int)img.width() != width() || (const unsigned int) img.height() != height())<br>
return false;<br> <br> if(img.format()!= QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32)<br> return false;<br> <br> ffmpeg::SwsContext *ctx;<br> //img_convert_ctx = /*Cached*/<br> img_convert_ctx = ffmpeg::sws_getContext(/*img_convert_ctx,*/width(),height(),ffmpeg::PIX_FMT_BGRA,width(),height(),ffmpeg::PIX_FMT_YUV420P,SWS_BICUBIC, NULL, NULL, NULL);<br>
//img_convert_ctx = ffmpeg::sws_getCachedContext(img_convert_ctx,getWidth(),getHeight(),ffmpeg::PIX_FMT_BGRA,getWidth(),getHeight(),ffmpeg::PIX_FMT_YUV420P,SWS_FAST_BILINEAR, NULL, NULL, NULL);<br> <br> //qDebug() << "context info" << img_convert_ctx;<br>
<br> //img_convert_ctx = ctx;<br> <br> if (img_convert_ctx == NULL)<br> return false;<br><br> const uint8_t *srcplanes[3];<br> srcplanes[0]= (const uint8_t*) img.bits();<br> srcplanes[1]= 0;<br> srcplanes[2]= 0;<br>
<br> int srcstride[3];<br> srcstride[0]= img.bytesPerLine();<br> srcstride[1]= 0;<br> srcstride[2]= 0;<br><br><br> ffmpeg::sws_scale(img_convert_ctx, srcplanes, srcstride,0, height(), frame->data, frame->linesize);<br>
<br> ffmpeg::sws_freeContext(img_convert_ctx);<br> img_convert_ctx = 0;<br> <br> return true;<br>}<br></div><div><br></div><div>bool QVideoEncoder::close()<br>{<br> QMutexLocker lock(locker());<br> <br> <br> if(!isOk())<br>
return false;<br> <br> if(avio_open_dyn_buf(&FormatCtx->pb) < 0)<br> {<br> writeLog(QString("QVideoEncoder::close: failed to open dynamic buffer"),LogType::ERROR);<br> return false;<br> }<br>
<br> av_write_trailer(FormatCtx);<br> <br> uint8_t *p = 0;<br> <br> int len = avio_close_dyn_buf(FormatCtx->pb,&p);<br> <br> if(len)<br> {<br> //_file.write((const char*) p,len);<br> if(vfsHandle())<br>
{<br> vfsHandle()->write((const char*)p,len); <br> vfsHandle()->flush();<br> }<br> }<br> <br> vfsHandle()->close();<br> delete vfsHandle();<br> setVFSHandle(0);<br> <br> if(p)<br> ffmpeg::av_free(p);<br>
<br> //ffmpeg::av_freep(&VideoStream->codec->stats_in);<br> //avcodec_close(VideoStream->codec);<br> <br> freeFrame();<br> freeOutputBuf();<br><br> for(unsigned int i = 0; i < FormatCtx->nb_streams; i++)<br>
{<br> ffmpeg::av_freep(&FormatCtx->streams[i]->codec->subtitle_header);<br> ffmpeg::av_freep(&FormatCtx->streams[i]->codec->stats_in);<br> ffmpeg::avcodec_close(FormatCtx->streams[i]->codec);<br>
ffmpeg::av_freep(&FormatCtx->streams[i]->codec);<br> ffmpeg::av_freep(&FormatCtx->streams[i]);<br> }<br><br> //avio_close(FormatCtx->pb);<br> //ffmpeg::avformat_free_context(FormatCtx); //sigsegv<br>
av_free(FormatCtx);<br> <br> return true;<br><br>}<br></div>