IMediaObject.ProcessOutput returning S_FALSE - c++

I am trying to write a simple audio capturing application in c++. and i found it is a very hard job to capture audio using c++ library, i couldn't find no more than a few library and I came up with ImediaObject. I found everything is okay in some other pc, but in my pc it can't record audio. When I call ImediaObject.ProcessOutput, it returns S_FALSE. I don't know what's wrong with my pc. I am using Visual Studio 2019.
Can anyone give me any clue ? what is wrong here ?
For your understanding I am sharing my main code here
#include <windows.h>
#include <dmo.h>
#include <Mmsystem.h>
#include <objbase.h>
#include <mediaobj.h>
#include <uuids.h>
#include <propidl.h>
#include <wmcodecdsp.h>
#include <atlbase.h>
#include <ATLComCli.h>
#include <audioclient.h>
#include <MMDeviceApi.h>
#include <AudioEngineEndPoint.h>
#include <DeviceTopology.h>
#include <propkey.h>
#include <strsafe.h>
#include <conio.h>
#include "AecKsBinder.h"
#include "mediabuf.h"
#define SAFE_ARRAYDELETE(p) {if (p) delete[] (p); (p) = NULL;}
#define SAFE_RELEASE(p) {if (NULL != p) {(p)->Release(); (p) = NULL;}}
#define VBFALSE (VARIANT_BOOL)0
#define VBTRUE (VARIANT_BOOL)-1
#define STREAM_BUFFER_LENGTH 0.1f //streaming buffer is 0.1 second long.
#define CHECK_RET(hr, message) if (FAILED(hr)) { puts(message); goto exit;}
#define CHECKHR(x) hr = x; if (FAILED(hr)) {printf("%d: %08X\n", __LINE__, hr); goto exit;}
#define CHECK_ALLOC(pb, message) if (NULL == pb) { puts(message); goto exit;}
class CStaticMediaBuffer : public CBaseMediaBuffer {
public:
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
void Init(BYTE* pData, ULONG ulSize, ULONG ulData) {
m_pData = pData;
m_ulSize = ulSize;
m_ulData = ulData;
}
};
void OutputUsage();
int __cdecl _tmain()
//int __cdecl _tmain()
{
HRESULT hr = S_OK;
CoInitialize(NULL);
IMediaObject* pDMO = NULL;
IPropertyStore* pPS = NULL;
CStaticMediaBuffer outputBuffer;
DMO_OUTPUT_DATA_BUFFER OutputBufferStruct = { 0 };
OutputBufferStruct.pBuffer = &outputBuffer;
DMO_MEDIA_TYPE mt = { 0 };
ULONG cbProduced = 0;
DWORD dwStatus;
// Parameters to config DMO
int iSystemMode = MODE_NOT_SET; // AEC-MicArray DMO system mode
int iOutFileIdx = -1; // argument index for otuput file name
int iMicDevIdx = -2; // microphone device index
int iSpkDevIdx = -2; // speaker device index
BOOL bFeatrModeOn = 0; // turn feature mode on/off
BOOL bNoiseSup = 1; // turn noise suppression on/off
BOOL bAGC = 0; // turn digital auto gain control on/off
BOOL bCntrClip = 0; // turn center clippng on/off
// control how long the Demo runs
int iDuration = 60; // seconds
int cTtlToGo = 0;
FILE* pfMicOutPCM; // dump output signal using PCM format
DWORD cOutputBufLen = 0;
BYTE* pbOutputBuffer = NULL;
UINT uCapDevCount = 0;
UINT uRenDevCount = 0;
char pcScanBuf[256] = { 0 };
WAVEFORMATEX wfxOut = { WAVE_FORMAT_PCM, 1, 22050, 44100, 2, 16, 0 };
AUDIO_DEVICE_INFO* pCaptureDeviceInfo = NULL, * pRenderDeviceInfo = NULL;
int i;
iMicDevIdx = 0;
iSpkDevIdx = 0;
iSystemMode = 0;
bFeatrModeOn = 1;
bNoiseSup = 1;
bAGC = 1;
bCntrClip = 1;
HANDLE currThread;
HANDLE currProcess;
BOOL iRet;
currProcess = GetCurrentProcess();
currThread = GetCurrentThread();
iRet = SetPriorityClass(currProcess, HIGH_PRIORITY_CLASS);
if (0 == iRet)
{
// call getLastError.
puts("failed to set process priority\n");
goto exit;
}
// DMO initialization
CHECKHR(CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER, IID_IMediaObject, (void**)&pDMO));
CHECKHR(pDMO->QueryInterface(IID_IPropertyStore, (void**)&pPS));
// Select capture device
hr = GetCaptureDeviceNum(uCapDevCount);
CHECK_RET(hr, "GetCaptureDeviceNum failed");
pCaptureDeviceInfo = new AUDIO_DEVICE_INFO[uCapDevCount];
hr = EnumCaptureDevice(uCapDevCount, pCaptureDeviceInfo);
CHECK_RET(hr, "EnumCaptureDevice failed");
printf("\nSystem has totally %d capture devices\n", uCapDevCount);
for (i = 0; i < (int)uCapDevCount; i++)
{
_tprintf(_T("Device %d is %s"), i, pCaptureDeviceInfo[i].szDeviceName);
if (pCaptureDeviceInfo[i].bIsMicArrayDevice)
_tprintf(_T(" -- Mic Array Device \n"));
else
_tprintf(_T("\n"));
}
if (iMicDevIdx < -1 || iMicDevIdx >= (int)uCapDevCount)
{
do {
printf("Select device ");
scanf_s("%255s", pcScanBuf, 255);
iMicDevIdx = atoi(pcScanBuf);
if (iMicDevIdx < -1 || iMicDevIdx >= (int)uCapDevCount)
printf("Invalid Capture Device ID \n");
else
break;
} while (1);
}
if (iMicDevIdx == -1)
_tprintf(_T("\n Default device will be used for capturing \n"));
else
_tprintf(_T("\n %s is selected for capturing\n"), pCaptureDeviceInfo[iMicDevIdx].szDeviceName);
SAFE_ARRAYDELETE(pCaptureDeviceInfo);
// Select render device
if (iSystemMode == SINGLE_CHANNEL_AEC ||
iSystemMode == ADAPTIVE_ARRAY_AND_AEC ||
iSystemMode == OPTIBEAM_ARRAY_AND_AEC)
{
hr = GetRenderDeviceNum(uRenDevCount);
CHECK_RET(hr, "GetRenderDeviceNum failed");
pRenderDeviceInfo = new AUDIO_DEVICE_INFO[uRenDevCount];
hr = EnumRenderDevice(uRenDevCount, pRenderDeviceInfo);
CHECK_RET(hr, "EnumRenderDevice failed");
printf("\nSystem has totally %d render devices\n", uRenDevCount);
for (i = 0; i < (int)uRenDevCount; i++)
{
_tprintf(_T("Device %d is %s \n"), i, pRenderDeviceInfo[i].szDeviceName);
}
if (iSpkDevIdx < -1 || iSpkDevIdx >= (int)uRenDevCount)
{
do {
printf("Select device ");
scanf_s("%255s", pcScanBuf, 255);
iSpkDevIdx = atoi(pcScanBuf);
if (iSpkDevIdx < -1 || iSpkDevIdx >= (int)uRenDevCount)
printf("Invalid Render Device ID \n");
else
break;
} while (1);
}
if (iSpkDevIdx == -1)
_tprintf(_T("\n Default device will be used for rendering \n"));
else
_tprintf(_T("\n %s is selected for rendering \n"), pRenderDeviceInfo[iSpkDevIdx].szDeviceName);
}
else {
iSpkDevIdx = -1;
}
SAFE_ARRAYDELETE(pRenderDeviceInfo);
TCHAR* fileName;
fileName = (TCHAR*)"test.raw";
// --- PREPARE OUTPUT --- //
if (NULL != _tfopen_s(&pfMicOutPCM, fileName, _T("wb")))
{
puts("cannot open file for output.\n");
goto exit;
}
// Set AEC mode and other parameters
// Not all user changeable options are given in this sample code.
// Please refer to readme.txt for more options.
// Set AEC-MicArray DMO system mode.
// This must be set for the DMO to work properly
puts("\nAEC settings:");
PROPVARIANT pvSysMode;
PropVariantInit(&pvSysMode);
pvSysMode.vt = VT_I4;
pvSysMode.lVal = (LONG)(iSystemMode);
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, pvSysMode));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, &pvSysMode));
printf("%20s %5d \n", "System Mode is", pvSysMode.lVal);
PropVariantClear(&pvSysMode);
// Tell DMO which capture and render device to use
// This is optional. If not specified, default devices will be used
if (iMicDevIdx >= 0 || iSpkDevIdx >= 0)
{
PROPVARIANT pvDeviceId;
PropVariantInit(&pvDeviceId);
pvDeviceId.vt = VT_I4;
pvDeviceId.lVal = (unsigned long)(iSpkDevIdx << 16) + (unsigned long)(0x0000ffff & iMicDevIdx);
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, pvDeviceId));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, &pvDeviceId));
PropVariantClear(&pvDeviceId);
}
if (bFeatrModeOn)
{
// Turn on feature modes
PROPVARIANT pvFeatrModeOn;
PropVariantInit(&pvFeatrModeOn);
pvFeatrModeOn.vt = VT_BOOL;
pvFeatrModeOn.boolVal = bFeatrModeOn ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATURE_MODE, pvFeatrModeOn));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATURE_MODE, &pvFeatrModeOn));
printf("%20s %5d \n", "Feature Mode is", pvFeatrModeOn.boolVal);
PropVariantClear(&pvFeatrModeOn);
// Turn on/off noise suppression
PROPVARIANT pvNoiseSup;
PropVariantInit(&pvNoiseSup);
pvNoiseSup.vt = VT_I4;
pvNoiseSup.lVal = (LONG)bNoiseSup;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_NS, pvNoiseSup));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_NS, &pvNoiseSup));
printf("%20s %5d \n", "Noise suppresion is", pvNoiseSup.lVal);
PropVariantClear(&pvNoiseSup);
// Turn on/off AGC
PROPVARIANT pvAGC;
PropVariantInit(&pvAGC);
pvAGC.vt = VT_BOOL;
pvAGC.boolVal = bAGC ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_AGC, pvAGC));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_AGC, &pvAGC));
printf("%20s %5d \n", "AGC is", pvAGC.boolVal);
PropVariantClear(&pvAGC);
// Turn on/off center clip
PROPVARIANT pvCntrClip;
PropVariantInit(&pvCntrClip);
pvCntrClip.vt = VT_BOOL;
pvCntrClip.boolVal = bCntrClip ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_CENTER_CLIP, pvCntrClip));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_CENTER_CLIP, &pvCntrClip));
printf("%20s %5d \n", "Center clip is", (BOOL)pvCntrClip.boolVal);
PropVariantClear(&pvCntrClip);
}
// Set DMO output format
hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
CHECK_RET(hr, "MoInitMediaType failed");
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.lSampleSize = 0;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.formattype = FORMAT_WaveFormatEx;
memcpy(mt.pbFormat, &wfxOut, sizeof(WAVEFORMATEX));
hr = pDMO->SetOutputType(0, &mt, 0);
CHECK_RET(hr, "SetOutputType failed");
MoFreeMediaType(&mt);
// Allocate streaming resources. This step is optional. If it is not called here, it
// will be called when first time ProcessInput() is called. However, if you want to
// get the actual frame size being used, it should be called explicitly here.
hr = pDMO->AllocateStreamingResources();
CHECK_RET(hr, "AllocateStreamingResources failed");
// Get actually frame size being used in the DMO. (optional, do as you need)
int iFrameSize;
PROPVARIANT pvFrameSize;
PropVariantInit(&pvFrameSize);
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_FRAME_SIZE, &pvFrameSize));
iFrameSize = pvFrameSize.lVal;
PropVariantClear(&pvFrameSize);
// allocate output buffer
cOutputBufLen = wfxOut.nSamplesPerSec * wfxOut.nBlockAlign;
pbOutputBuffer = new BYTE[cOutputBufLen];
CHECK_ALLOC(pbOutputBuffer, "out of memory.\n");
// number of frames to play
cTtlToGo = iDuration * 100;
// main loop to get mic output from the DMO
puts("\nAEC-MicArray is running ... Press \"s\" to stop");
while (1)
{
Sleep(10); //sleep 10ms
if (cTtlToGo-- <= 0)
break;
do {
outputBuffer.Init((byte*)pbOutputBuffer, cOutputBufLen, 0);
OutputBufferStruct.dwStatus = 0;
hr = pDMO->ProcessOutput(0, 1, &OutputBufferStruct, &dwStatus);
CHECK_RET(hr, "ProcessOutput failed");
if (hr == S_FALSE) {
cbProduced = 0;
}
else {
hr = outputBuffer.GetBufferAndLength(NULL, &cbProduced);
CHECK_RET(hr, "GetBufferAndLength failed");
}
// dump output data into a file with PCM format.
if (fwrite(pbOutputBuffer, 1, cbProduced, pfMicOutPCM) != cbProduced)
{
puts("write error");
goto exit;
}
} while (OutputBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE);
// check keyboard input to stop
if (_kbhit())
{
int ch = _getch();
if (ch == 's' || ch == 'S')
break;
}
}
exit:
SAFE_ARRAYDELETE(pbOutputBuffer);
SAFE_ARRAYDELETE(pCaptureDeviceInfo);
SAFE_ARRAYDELETE(pRenderDeviceInfo);
SAFE_RELEASE(pDMO);
SAFE_RELEASE(pPS);
CoUninitialize();
return hr;
}
void OutputUsage()
{
printf("MFWMAAEC (Aec-MicArray DMO) Demo. \n");
printf("Copyright (c) 2004-2006, Microsoft Corporation. All rights reserved. \n\n");
printf("Usage: AecSDKDemo.exe -out mic_out.pcm -mod 0 [-feat 1] [-ns 1] [-agc 0] \n");
printf(" [-cntrclip 0] [-micdev 0] [-spkdev 0] [-duration 60]\n");
return;
}
NOTE: My main concern is to record audio and use the native noise and echo canceler. If there is any suggestion regarding this, I would very much appriciate it. Thanks.

Related

Trying to cancel execution and delete file using ffmpeg C API

The code below is a class that handle the conversion of multiples images, through add_frame() method, into a GIF with encode(). It also use a filter to generate and apply the palette. The usage is like this:
Code call example
std::unique_ptr<px::GIF::FFMPEG> gif_obj = nullptr;
try
{
gif_obj = std::make_unique<px::GIF::FFMPEG>({1000,1000}, 12, "C:/out.gif",
"format=pix_fmts=rgb24,split [a][b];[a]palettegen[p];[b][p]paletteuse");
// Example: a simple vector of images (usually process internally)
for(auto img : image_vector)
gif_obj->add_frame(img);
// Once all frame were added, encode the final GIF with the filter applied.
gif_obj->encode();
}
catch(const std::exception& e)
{
// An error occured! We must close FFMPEG properly and delete the created file.
gif_obj->cancel();
}
I have the following issue. If the code for any reason throw an exception, I call ffmpeg->cancel() and it supposes to delete the GIF file on disk. But this is never working, I assume there is a lock on the file or something like that. So here are my question:
What is the proper way to close/free ffmpeg object in order to remove the file afterward ?
Full class code below
Header
// C++ Standard includes
#include <memory>
#include <string>
#include <vector>
// 3rd Party incldues
#ifdef __cplusplus
extern "C" {
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/opt.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}
#endif
#define FFMPEG_MSG_LEN 2000
namespace px
{
namespace GIF
{
class FFMPEG
{
public:
FFMPEG(const px::Point2D<int>& dim,
const int framerate,
const std::string& filename,
const std::string& filter_cmd);
~FFMPEG();
void add_frame(pxImage * const img);
void encode();
void cancel();
private:
void init_filters(); // Init everything that needed to filter the input frame.
void init_muxer(); // The muxer that creates the output file.
void muxing_one_frame(AVFrame* frame);
void release();
int _ret = 0; // status code from FFMPEG.
char _err_msg[FFMPEG_MSG_LEN]; // Error message buffer.
int m_width = 0; // The width that all futur images must have to be accepted.
int m_height = 0; // The height that all futur images must have to be accepted.
int m_framerate = 0; // GIF Framerate.
std::string m_filename = ""; // The GIF filename (on cache?)
std::string m_filter_desc = ""; // The FFMPEG filter to apply over the frames.
bool as_frame = false;
AVFrame* picture_rgb24 = nullptr; // Temporary frame that will hold the pxImage in an RGB24 format (NOTE: TOP-LEFT origin)
AVFormatContext* ofmt_ctx = nullptr; // ouput format context associated to the
AVCodecContext* o_codec_ctx = nullptr; // output codec for the GIF
AVFilterGraph* filter_graph = nullptr; // filter graph associate with the string we want to execute
AVFilterContext* buffersrc_ctx = nullptr; // The buffer that will store all the frames in one place for the palette generation.
AVFilterContext* buffersink_ctx = nullptr; // The buffer that will store the result afterward (once the palette are used).
int64_t m_pts_increment = 0;
};
};
};
ctor
px::GIF::FFMPEG::FFMPEG(const px::Point2D<int>& dim,
const int framerate,
const std::string& filename,
const std::string& filter_cmd) :
m_width(dim.x()),
m_height(dim.y()),
m_framerate(framerate),
m_filename(filename),
m_filter_desc(filter_cmd)
{
#if !_DEBUG
av_log_set_level(AV_LOG_QUIET); // Set the FFMPEG log to quiet to avoid too much logs.
#endif
// Allocate the temporary buffer that hold the ffmpeg image (pxImage to AVFrame conversion).
picture_rgb24 = av_frame_alloc();
picture_rgb24->pts = 0;
picture_rgb24->data[0] = NULL;
picture_rgb24->linesize[0] = -1;
picture_rgb24->format = AV_PIX_FMT_RGB24;
picture_rgb24->height = m_height;
picture_rgb24->width = m_width;
if ((_ret = av_image_alloc(picture_rgb24->data, picture_rgb24->linesize, m_width, m_height, (AVPixelFormat)picture_rgb24->format, 24)) < 0)
throw px::GIF::Error("Failed to allocate the AVFrame for pxImage conversion with error: " +
std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
"GIF::FFMPEG CTOR");
//printf("allocated picture of size %d, linesize %d %d %d %d\n", _ret, picture_rgb24->linesize[0], picture_rgb24->linesize[1], picture_rgb24->linesize[2], picture_rgb24->linesize[3]);
init_muxer(); // Prepare the GIF encoder (open it on disk).
init_filters(); // Prepare the filter that will be applied over the frame.
// Instead of hardcoder {1,100} which is the GIF tbn, we collect it from its stream.
// This will avoid future problem if the codec change in ffmpeg.
if (ofmt_ctx && ofmt_ctx->nb_streams > 0)
m_pts_increment = av_rescale_q(1, { 1, m_framerate }, ofmt_ctx->streams[0]->time_base);
else
m_pts_increment = av_rescale_q(1, { 1, m_framerate }, { 1, 100 });
}
FFMPEG Initialization (Filter and muxer)
void px::GIF::FFMPEG::init_filters()
{
const AVFilter* buffersrc = avfilter_get_by_name("buffer");
const AVFilter* buffersink = avfilter_get_by_name("buffersink");
AVRational time_base = { 1, m_framerate };
AVRational aspect_pixel = { 1, 1 };
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
filter_graph = avfilter_graph_alloc();
try
{
if (!outputs || !inputs || !filter_graph)
throw px::GIF::Error("Failed to 'init_filters' could not allocated the graph/filters.", "GIF::FFMPEG init_filters");
char args[512];
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
m_width, m_height,
picture_rgb24->format,
time_base.num, time_base.den,
aspect_pixel.num, aspect_pixel.den);
if (avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, nullptr, filter_graph) < 0)
throw px::GIF::Error("Failed to create the 'source buffer' in init_filer method.", "GIF::FFMPEG init_filters");
if (avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph) < 0)
throw px::GIF::Error("Failed to create the 'sink buffer' in init_filer method.", "GIF::FFMPEG init_filters");
// GIF has possible output of PAL8.
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE };
if (av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) < 0)
throw px::GIF::Error("Failed to set the output pixel format.", "GIF::FFMPEG init_filters");
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
// GIF has possible output of PAL8.
if (avfilter_graph_parse_ptr(filter_graph, m_filter_desc.c_str(), &inputs, &outputs, nullptr) < 0)
throw px::GIF::Error("Failed to parse the filter graph (bad string!).", "GIF::FFMPEG init_filters");
if (avfilter_graph_config(filter_graph, nullptr) < 0)
throw px::GIF::Error("Failed to configure the filter graph (bad string!).", "GIF::FFMPEG init_filters");
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
}
catch (const std::exception& e)
{
// Catch exception to delete element.
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
throw e; // re-throuw
}
}
void px::GIF::FFMPEG::init_muxer()
{
AVOutputFormat* o_fmt = av_guess_format("gif", m_filename.c_str(), "video/gif");
if ((_ret = avformat_alloc_output_context2(&ofmt_ctx, o_fmt, "gif", m_filename.c_str())) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " allocate output format.", "GIF::FFMPEG init_muxer");
AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_GIF);
if (!codec) throw px::GIF::Error("Could to find the 'GIF' codec.", "GIF::FFMPEG init_muxer");
#if 0
const AVPixelFormat* p = codec->pix_fmts;
while (p != NULL && *p != AV_PIX_FMT_NONE) {
printf("supported pix fmt: %s\n", av_get_pix_fmt_name(*p));
++p;
}
#endif
AVStream* stream = avformat_new_stream(ofmt_ctx, codec);
AVCodecParameters* codec_paramters = stream->codecpar;
codec_paramters->codec_tag = 0;
codec_paramters->codec_id = codec->id;
codec_paramters->codec_type = AVMEDIA_TYPE_VIDEO;
codec_paramters->width = m_width;
codec_paramters->height = m_height;
codec_paramters->format = AV_PIX_FMT_PAL8;
o_codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(o_codec_ctx, codec_paramters);
o_codec_ctx->time_base = { 1, m_framerate };
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
o_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if ((_ret = avcodec_open2(o_codec_ctx, codec, NULL)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " open output codec.", "GIF::FFMPEG init_muxer");
if ((_ret = avio_open(&ofmt_ctx->pb, m_filename.c_str(), AVIO_FLAG_WRITE)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " avio open error.", "GIF::FFMPEG init_muxer");
if ((_ret = avformat_write_header(ofmt_ctx, NULL)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " write GIF header", "GIF::FFMPEG init_muxer");
#if _DEBUG
// This print the stream/output format.
av_dump_format(ofmt_ctx, -1, m_filename.c_str(), 1);
#endif
}
Add frame (usually in a loop)
void px::GIF::FFMPEG::add_frame(pxImage * const img)
{
if (img->getImageType() != PXT_BYTE || img->getNChannels() != 4)
throw px::GIF::Error("Failed to 'add_frame' since image is not PXT_BYTE and 4-channels.", "GIF::FFMPEG add_frame");
if (img->getWidth() != m_width || img->getHeight() != m_height)
throw px::GIF::Error("Failed to 'add_frame' since the size is not same to other inputs.", "GIF::FFMPEG add_frame");
const int pitch = picture_rgb24->linesize[0];
auto px_ptr = getImageAccessor<pxUChar_C4>(img);
for (int y = 0; y < m_height; y++)
{
const int px_row = img->getOrigin() == ORIGIN_BOT_LEFT ? m_height - y - 1 : y;
for (int x = 0; x < m_width; x++)
{
const int idx = y * pitch + 3 * x;
picture_rgb24->data[0][idx] = px_ptr[px_row][x].ch[PX_RE];
picture_rgb24->data[0][idx + 1] = px_ptr[px_row][x].ch[PX_GR];
picture_rgb24->data[0][idx + 2] = px_ptr[px_row][x].ch[PX_BL];
}
}
// palettegen need a whole stream, just add frame to buffer.
if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, picture_rgb24, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
throw px::GIF::Error("Failed to 'add_frame' to global buffer with error: " +
std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
"GIF::FFMPEG add_frame");
// Increment the FPS of the picture for the next add-up to the buffer.
picture_rgb24->pts += m_pts_increment;
as_frame = true;
}
Encoder (final step)
void px::GIF::FFMPEG::encode()
{
if (!as_frame)
throw px::GIF::Error("Please 'add_frame' before running the Encoding().", "GIF::FFMPEG encode");
// end of buffer
if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
throw px::GIF::Error("error add frame to buffer source: " + std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)), "GIF::FFMPEG encode");
do {
AVFrame* filter_frame = av_frame_alloc();
_ret = av_buffersink_get_frame(buffersink_ctx, filter_frame);
if (_ret == AVERROR(EAGAIN) || _ret == AVERROR_EOF) {
av_frame_unref(filter_frame);
break;
}
// write the filter frame to output file
muxing_one_frame(filter_frame);
av_frame_unref(filter_frame);
} while (_ret >= 0);
av_write_trailer(ofmt_ctx);
}
void px::GIF::FFMPEG::muxing_one_frame(AVFrame* frame)
{
int ret = avcodec_send_frame(o_codec_ctx, frame);
AVPacket *pkt = av_packet_alloc();
av_init_packet(pkt);
while (ret >= 0) {
ret = avcodec_receive_packet(o_codec_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
av_write_frame(ofmt_ctx, pkt);
}
av_packet_unref(pkt);
}
DTOR, Release and Cancel
px::GIF::FFMPEG::~FFMPEG()
{
release();
}
void px::GIF::FFMPEG::release()
{
// Muxer stuffs
if (ofmt_ctx != nullptr) avformat_free_context(ofmt_ctx);
if (o_codec_ctx != nullptr) avcodec_close(o_codec_ctx);
if (o_codec_ctx != nullptr) avcodec_free_context(&o_codec_ctx);
ofmt_ctx = nullptr;
o_codec_ctx = nullptr;
// Filter stuffs
if (buffersrc_ctx != nullptr) avfilter_free(buffersrc_ctx);
if (buffersink_ctx != nullptr) avfilter_free(buffersink_ctx);
if (filter_graph != nullptr) avfilter_graph_free(&filter_graph);
buffersrc_ctx = nullptr;
buffersink_ctx = nullptr;
filter_graph = nullptr;
// Conversion image.
if (picture_rgb24 != nullptr) av_frame_free(&picture_rgb24);
picture_rgb24 = nullptr;
}
void px::GIF::FFMPEG::cancel()
{
// In-case of failure we must close ffmpeg and exit.
av_write_trailer(ofmt_ctx);
// Release and close all elements.
release();
// Delete the file on disk.
if (remove(m_filename.c_str()) != 0)
PX_LOG0(PX_LOGLEVEL_ERROR, "GIF::FFMPEG - On 'cancel' failed to remove the file.");
}
Took me a while but finally get it!
I was missing a avio_close(ofmt_ctx->pb); in my cancel method.
Once the file is released from ffmpeg, the std::remove() works like a charm.
Note, the av_write_trailer and avio_close should only be called if the init_muxer successfully executed, so I had a member variable to flag success or not. Then, I do the appropriate call in the cancel.

How I change the resolution one monitor only

For example, my computer has two monitors, the primary monitor's resolution is 800*600, the other monitor's resolution is 1600*900.
I would like define the resolution on one monitor only ?
The function 'SetDisplayConfig' change the resolution screen on the two minotors...
https://learn.microsoft.com/fr-fr/windows-hardware/drivers/display/ccd-apis
https://learn.microsoft.com/fr-fr/windows/win32/api/winuser/nf-winuser-setdisplayconfig
[DllImport("User32.dll")]
public static extern int SetDisplayConfig(
uint numPathArrayElements,
[In] DisplayConfigPathInfo[] pathArray,
uint numModeInfoArrayElements,
[In] DisplayConfigModeInfo[] modeInfoArray,
SdcFlags flags
);
private static Boolean SetDisplaySettings(int Id_Monitor, uint Width, uint Height, uint Scaling)
{
try
{
CCDWrapper.DisplayConfigPathInfo[] pathInfoArray = new CCDWrapper.DisplayConfigPathInfo[0] { };
CCDWrapper.DisplayConfigModeInfo[] modeInfoArray = new CCDWrapper.DisplayConfigModeInfo[0] { };
CCDWrapper.MonitorAdditionalInfo[] additionalInfo = new CCDWrapper.MonitorAdditionalInfo[0] { };
bool Status = GetDisplaySettings(ref pathInfoArray, ref modeInfoArray, ref additionalInfo, true);
CCDWrapper.DisplayConfigPathInfo[] pathInfoArrayCurrent = new CCDWrapper.DisplayConfigPathInfo[0] { };
CCDWrapper.DisplayConfigModeInfo[] modeInfoArrayCurrent = new CCDWrapper.DisplayConfigModeInfo[0] { };
CCDWrapper.MonitorAdditionalInfo[] additionalInfoCurrent = new CCDWrapper.MonitorAdditionalInfo[0] { };
bool StatusCurrent = GetDisplaySettings(ref pathInfoArrayCurrent, ref modeInfoArrayCurrent, ref additionalInfoCurrent, false);
if (StatusCurrent)
{
for (int iPathInfo = 0; iPathInfo <= pathInfoArray.Length-1; iPathInfo++)
//for (int iPathInfo = 0; iPathInfo <= pathInfoArray.Length - 1; iPathInfo++)
{
for (int iPathInfoCurrent = 0; iPathInfoCurrent <= pathInfoArrayCurrent.Length - 1; iPathInfoCurrent++)
{
if ((pathInfoArray[iPathInfo].sourceInfo.id == pathInfoArrayCurrent[iPathInfoCurrent].sourceInfo.id) && (pathInfoArray[iPathInfo].targetInfo.id == pathInfoArrayCurrent[iPathInfoCurrent].targetInfo.id))
{
pathInfoArray[iPathInfo].sourceInfo.adapterId.LowPart = pathInfoArrayCurrent[iPathInfoCurrent].sourceInfo.adapterId.LowPart;
pathInfoArray[iPathInfo].targetInfo.adapterId.LowPart = pathInfoArrayCurrent[iPathInfoCurrent].targetInfo.adapterId.LowPart;
pathInfoArray[iPathInfo].targetInfo.scaling = (CCDWrapper.DisplayConfigScaling)Scaling;
break;
}
}
}
for (int iModeInfo = 0; iModeInfo <= modeInfoArray.Length - 1; iModeInfo++)
{
for (int iPathInfo = 0; iPathInfo <= pathInfoArray.Length - 1; iPathInfo++)
{
if ((modeInfoArray[iModeInfo].id == pathInfoArray[iPathInfo].targetInfo.id) && (modeInfoArray[iModeInfo].infoType == CCDWrapper.DisplayConfigModeInfoType.Target))
{
for (int iModeInfoSource = 0; iModeInfoSource <= modeInfoArray.Length - 1; iModeInfoSource++)
{
if ((modeInfoArray[iModeInfoSource].id == pathInfoArray[iPathInfo].sourceInfo.id) && (modeInfoArray[iModeInfoSource].adapterId.LowPart == modeInfoArray[iModeInfo].adapterId.LowPart) && (modeInfoArray[iModeInfoSource].infoType == CCDWrapper.DisplayConfigModeInfoType.Source))
{
modeInfoArray[iModeInfoSource].adapterId.LowPart = pathInfoArray[iPathInfo].sourceInfo.adapterId.LowPart;
modeInfoArray[iModeInfoSource].sourceMode.height = Height;
modeInfoArray[iModeInfoSource].sourceMode.width = Width;
break;
}
}
modeInfoArray[iModeInfo].adapterId.LowPart = pathInfoArray[iPathInfo].targetInfo.adapterId.LowPart;
break;
}
}
}
uint numPathArrayElements = System.Convert.ToUInt32(pathInfoArray.Length);
uint numModeInfoArrayElements = System.Convert.ToUInt32(modeInfoArray.Length);
long Result = CCDWrapper.SetDisplayConfig(numPathArrayElements, pathInfoArray, numModeInfoArrayElements, modeInfoArray, CCDWrapper.SdcFlags.Apply | CCDWrapper.SdcFlags.UseSuppliedDisplayConfig | CCDWrapper.SdcFlags.SaveToDatabase | CCDWrapper.SdcFlags.NoOptimization | CCDWrapper.SdcFlags.AllowChanges);
if (Result == 0)
return true;
else
return false;
}
else
return false;
}
catch (Exception ex)
{
EventLog.WriteEntry("ResolutionEcran", "Erreur SetDisplaySettings : " + ex.Message, EventLogEntryType.Error);
return false;
}
}
private static Boolean GetDisplaySettings(ref CCDWrapper.DisplayConfigPathInfo[] pathInfoArray, ref CCDWrapper.DisplayConfigModeInfo[] modeInfoArray, ref CCDWrapper.MonitorAdditionalInfo[] additionalInfo, Boolean ActiveOnly, [System.Runtime.InteropServices.Optional] int ID_Monitor)
{
uint numPathArrayElements;
uint numModeInfoArrayElements;
CCDWrapper.QueryDisplayFlags queryFlags = CCDWrapper.QueryDisplayFlags.AllPaths;
if (ActiveOnly)
{
//queryFlags = CCDWrapper.QueryDisplayFlags.OnlyActivePaths;
queryFlags = CCDWrapper.QueryDisplayFlags.OnlyActivePaths;
}
var status = CCDWrapper.GetDisplayConfigBufferSizes(queryFlags, out numPathArrayElements, out numModeInfoArrayElements);
if (status == 0)
{
pathInfoArray = new CCDWrapper.DisplayConfigPathInfo[numPathArrayElements];
modeInfoArray = new CCDWrapper.DisplayConfigModeInfo[numModeInfoArrayElements];
additionalInfo = new CCDWrapper.MonitorAdditionalInfo[numModeInfoArrayElements];
status = CCDWrapper.QueryDisplayConfig(queryFlags, ref numPathArrayElements, pathInfoArray, ref numModeInfoArrayElements, modeInfoArray, IntPtr.Zero);
if (status == 0)
{
for (var iMode = 0; iMode < numModeInfoArrayElements; iMode++)
{
if (modeInfoArray[iMode].infoType == CCDWrapper.DisplayConfigModeInfoType.Target)
{
try
{
additionalInfo[iMode] = CCDWrapper.GetMonitorAdditionalInfo(modeInfoArray[iMode].adapterId, modeInfoArray[iMode].id);
}
catch (Exception)
{
additionalInfo[iMode].valid = false;
}
}
}
return true;
}
else
{
//Erreur : Querying display;
}
}
else
{
//Erreur : Taille Buffer;
}
return false;
}
Use ChangeDisplaySettingsEx function to change the settings of the specified display device to the specified graphics mode. The following is an example you can refer to.
#include <windows.h>
int main()
{
for (DWORD devNum = 0; ; devNum++)
{
DISPLAY_DEVICE dev = {0};
dev.cb = sizeof(DISPLAY_DEVICE);
if (!EnumDisplayDevices(NULL, devNum, &dev, EDD_GET_DEVICE_INTERFACE_NAME))
break;
wprintf(L"Display name: %s \n", dev.DeviceName);
DEVMODE dMode = { 0 };
dMode.dmSize = sizeof(dMode);
if (!EnumDisplaySettings(dev.DeviceName, ENUM_CURRENT_SETTINGS, &dMode))
{
wprintf(L"EnumDisplaySettings error: %d \n", GetLastError());
continue;
}
wprintf(L"Display old settings: \n");
wprintf(L"dmBitsPerPel: %d \n", dMode.dmBitsPerPel);
wprintf(L"dmPelsWidth: %d \n", dMode.dmPelsWidth);
wprintf(L"dmPelsHeight: %d \n", dMode.dmPelsHeight);
wprintf(L"dmDisplayFlags: %x \n", dMode.dmDisplayFlags);
wprintf(L"dmDisplayFrequency: %d \n", dMode.dmDisplayFrequency);
dMode.dmPelsWidth = 800;
dMode.dmPelsHeight = 600;
ChangeDisplaySettingsEx(dev.DeviceName, &dMode, NULL, 0, NULL);
DEVMODE dModeNew = { 0 };
dModeNew.dmSize = sizeof(DEVMODE);
if (!EnumDisplaySettings(dev.DeviceName, ENUM_CURRENT_SETTINGS, &dModeNew))
{
wprintf(L"EnumDisplaySettings error: %d \n", GetLastError());
continue;
}
wprintf(L"Display new settings: \n");
wprintf(L"dmBitsPerPel: %d \n", dModeNew.dmBitsPerPel);
wprintf(L"dmPelsWidth: %d \n", dModeNew.dmPelsWidth);
wprintf(L"dmPelsHeight: %d \n", dModeNew.dmPelsHeight);
wprintf(L"dmDisplayFlags: %x \n", dModeNew.dmDisplayFlags);
wprintf(L"dmDisplayFrequency: %d \n", dModeNew.dmDisplayFrequency);
}
getchar();
}
I set dwflags to 0 let the graphics mode for the current screen change dynamically. Refer to dwflags parameter part of ChangeDisplaySettingsEx's document for deciding how you would like the graphics mode should be changed.
Sorry for my late response...I was absent.
I also need to change the scaling of monitor (Black bars removed on certain resolutions).
[Flags]
public enum DisplayConfigScaling : uint
{
Zero = 0x0,
Identity = 1,
Centered = 2,
Stretched = 3,
Aspectratiocenteredmax = 4,
Custom = 5,
Preferred = 128,
ForceUint32 = 0xFFFFFFFF
}
The function 'ChangeDisplaySettingsEx' may change the scale of monitor ?
Thank you very for your help.

rapid TS fragment ffmpeg decoding - memory leak

Environment:
Ubuntu 16.04 (x64)
C++
ffmpeg
Use-case
Multiple MPEG-TS fragments are rapidly decoded ( numerous every sec )
The format of the TS fragments is dynamic and can't be known ahead of time
The first A/V frames of each fragment are needed to be extracted
Problem statement
The code bellow successfully decodes A/V, BUT, has a huge memory leak ( MBytes/sec )
According to the docs seems all memory is freed as it should ( does it... ? )
Why do I get this huge mem leak, what am I missing in the following code snap ?
struct MEDIA_TYPE {
ffmpeg::AVMediaType eType;
union {
struct {
ffmpeg::AVPixelFormat colorspace;
int width, height;
float fFPS;
} video;
struct : WAVEFORMATEX {
short sSampleFormat;
} audio;
} format;
};
struct FRAME {
enum { MAX_PALNES = 3 + 1 };
int iStrmId;
int64_t pts; // Duration in 90Khz clock resolution
uint8_t** ppData; // Null terminated
int32_t* pStride;// Zero terminated
};
HRESULT ProcessTS(IN Operation op, IN uint8_t* pTS, IN uint32_t uiBytes, bool(*cb)(IN const MEDIA_TYPE& mt, IN FRAME& frame, IN PVOID pCtx), IN PVOID pCbCtx)
{
uiBytes -= uiBytes % 188;// align to 188 packet size
struct CONTEXT {
uint8_t* pTS;
uint32_t uiBytes;
int32_t iPos;
} ctx = { pTS, uiBytes, 0 };
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this);
ffmpeg::AVFormatContext* pFmtCtx = 0;
if (0 == (pFmtCtx = ffmpeg::avformat_alloc_context()))
return E_OUTOFMEMORY;
ffmpeg::AVIOContext* pIoCtx = ffmpeg::avio_alloc_context(pTS, uiBytes, 0, &ctx
, [](void *opaque, uint8_t *buf, int buf_size)->int {
auto pCtx = (CONTEXT*)opaque;
int size = pCtx->uiBytes;
if (pCtx->uiBytes - pCtx->iPos < buf_size)
size = pCtx->uiBytes - pCtx->iPos;
if (size > 0) {
memcpy(buf, pCtx->pTS + pCtx->iPos, size);
pCtx->iPos += size;
}
return size;
}
, 0
, [](void* opaque, int64_t offset, int whence)->int64_t {
auto pCtx = (CONTEXT*)opaque;
switch (whence)
{
case SEEK_SET:
pCtx->iPos = offset;
break;
case SEEK_CUR:
pCtx->iPos += offset;
break;
case SEEK_END:
pCtx->iPos = pCtx->uiBytes - offset;
break;
case AVSEEK_SIZE:
return pCtx->uiBytes;
}
return pCtx->iPos;
});
pFmtCtx->pb = pIoCtx;
int iRet = ffmpeg::avformat_open_input(&pFmtCtx, "fakevideo.ts", m_pInputFmt, 0);
if (ERROR_SUCCESS != iRet) {
assert(false);
pFmtCtx = 0;// a user-supplied AVFormatContext will be freed on failure.
return E_FAIL;
}
struct DecodeContext {
ffmpeg::AVStream* pStream;
ffmpeg::AVCodec* pDecoder;
int iFramesProcessed;
};
HRESULT hr = S_OK;
int iStreamsProcessed = 0;
bool bVideoFound = false;
int64_t ptsLast = 0;
int64_t dtsLast = 0;
auto pContext = (DecodeContext*)alloca(sizeof(DecodeContext) * pFmtCtx->nb_streams);
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++) {
assert(pFmtCtx->streams[i]->index == i);
pContext[i].pStream = pFmtCtx->streams[i];
pContext[i].pDecoder = ffmpeg::avcodec_find_decoder(pFmtCtx->streams[i]->codec->codec_id);
pContext[i].iFramesProcessed= 0;
if (0 == pContext[i].pDecoder)
continue;
if ((iRet = ffmpeg::avcodec_open2(pFmtCtx->streams[i]->codec, pContext[i].pDecoder, NULL)) < 0) {
_ASSERT(FALSE);
hr = E_FAIL;
goto ErrExit;
}
}
while (S_OK == hr) {
ffmpeg::AVFrame* pFrame = 0;
ffmpeg::AVPacket pkt;
ffmpeg::av_init_packet(&pkt);
if (ERROR_SUCCESS != (iRet = ffmpeg::av_read_frame(pFmtCtx, &pkt))) {
hr = E_FAIL;
break;
}
if ((0 == dtsLast) && (0 != pkt.dts))
dtsLast = pkt.dts;
if ((0 == ptsLast) && (0 != pkt.pts))
ptsLast = pkt.pts;
DecodeContext& ctx = pContext[pkt.stream_index];
if (Operation::DECODE_FIRST_FRAME_OF_EACH_STREAM == op) {
if (iStreamsProcessed == pFmtCtx->nb_streams) {
hr = S_FALSE;
goto Next;
}
if (ctx.iFramesProcessed > 0)
goto Next;
iStreamsProcessed++;
}
if (0 == ctx.pDecoder)
goto Next;
if (0 == (pFrame = ffmpeg::av_frame_alloc())) {
hr = E_OUTOFMEMORY;
goto Next;
}
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x, decode, S:%d, T:%d\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this, pkt.stream_index, ctx.pStream->codec->codec_type);
int bGotFrame = false;
int iBytesUsed = 0;
MEDIA_TYPE mt;
memset(&mt, 0, sizeof(mt));
mt.eType = ctx.pStream->codec->codec_type;
switch (mt.eType) {
case ffmpeg::AVMediaType::AVMEDIA_TYPE_AUDIO:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
// FFMPEG AAC decoder oddity, first call to 'avcodec_decode_audio4' results mute audio where the second result the expected audio
bGotFrame = false;
if ((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (false == bGotFrame)
goto Next;
iBytesUsed = ctx.pStream->codec->frame_size;
mt.format.audio.nChannels = ctx.pStream->codec->channels;
mt.format.audio.nSamplesPerSec = ctx.pStream->codec->sample_rate;
mt.format.audio.wBitsPerSample = ffmpeg::av_get_bytes_per_sample(ctx.pStream->codec->sample_fmt) * 8;
mt.format.audio.nBlockAlign = mt.format.audio.nChannels * mt.format.audio.wBitsPerSample / 8;
mt.format.audio.sSampleFormat = (short)pFrame->format;
break;
case ffmpeg::AVMediaType::AVMEDIA_TYPE_VIDEO:
if ((iRet = ffmpeg::avcodec_decode_video2(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
break;
}
if (false == bGotFrame)
goto Next;
assert(ffmpeg::AVPixelFormat::AV_PIX_FMT_YUV420P == ctx.pStream->codec->pix_fmt);// Thats is the only color space currently supported
iBytesUsed = (ctx.pStream->codec->width * ctx.pStream->codec->height * 3) / 2;
mt.format.video.width = ctx.pStream->codec->width;
mt.format.video.height = ctx.pStream->codec->height;
mt.format.video.colorspace = ctx.pStream->codec->pix_fmt;
mt.format.video.fFPS = (float)ctx.pStream->codec->framerate.num / ctx.pStream->codec->framerate.den;
bVideoFound = true;
break;
default:
goto Next;
}
ctx.iFramesProcessed++;
{
FRAME f = { ctx.pStream->index, ((0 == ptsLast) ? dtsLast : ptsLast), (uint8_t**)pFrame->data, (int32_t*)pFrame->linesize };
if ((iRet > 0) && (false == cb(mt, f, pCbCtx)))
hr = S_FALSE;// Breaks the loop
}
Next:
ffmpeg::av_free_packet(&pkt);
if (0 != pFrame) {
//ffmpeg::av_frame_unref(pFrame);
ffmpeg::av_frame_free(&pFrame);
pFrame = 0;
}
}
ErrExit:
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++)
ffmpeg::avcodec_close(pFmtCtx->streams[i]->codec);
pIoCtx->buffer = 0;// We have allocated the buffer, no need for ffmpeg to free it 4 us
pFmtCtx->pb = 0;
ffmpeg::av_free(pIoCtx);
ffmpeg::avformat_close_input(&pFmtCtx);
ffmpeg::avformat_free_context(pFmtCtx);
return hr;
}
You need to unref the packets before reusing them. And there's no need to allocate and deallocate them all the time.
Here's how I do it which might help you:
// Initialise a packet queue
std::list<AVPacket *> packets;
...
for (int c = 0; c < MAX_PACKETS; c++) {
ff->packets.push_back(av_packet_alloc());
}
while (!quit) {
... get packet from queue
int err = av_read_frame(ff->context, packet);
... process packet (audio, video, etc)
av_packet_unref(packet); // add back to queue for reuse
}
// Release packets
while (ff->packets.size()) { // free packets
AVPacket *packet = ff->packets.front();
av_packet_free(&packet);
ff->packets.pop_front();
}
In your code you've freed a packet which wasn't allocated in the first place.

What to pass to avcodec_decode_video2 for H.264 Transport Stream?

I want to decode H.264 video from a collection of MPEG-2 Transport Stream packets but I am not clear what to pass to avcodec_decode_video2
The documentation says to pass "the input AVPacket containing the input buffer."
But what should be in the input buffer?
A PES packet will be spread across the payload portion of several TS packets, with NALU(s) inside the PES. So pass a TS fragment? The entire PES? PES payload only?
This Sample Code mentions:
BUT some other codecs (msmpeg4, mpeg4) are inherently frame based, so
you must call them with all the data for one frame exactly. You must
also initialize 'width' and 'height' before initializing them.
But I can find no info on what "all the data" means...
Passing a fragment of a TS packet payload is not working:
AVPacket avDecPkt;
av_init_packet(&avDecPkt);
avDecPkt.data = inbuf_ptr;
avDecPkt.size = esBufSize;
len = avcodec_decode_video2(mpDecoderContext, mpFrameDec, &got_picture, &avDecPkt);
if (len < 0)
{
printf(" TS PKT #%.0f. Error decoding frame #%04d [rc=%d '%s']\n",
tsPacket.pktNum, mDecodedFrameNum, len, av_make_error_string(errMsg, 128, len));
return;
}
output
[h264 # 0x81cd2a0] no frame!
TS PKT #2973. Error decoding frame #0001 [rc=-1094995529 'Invalid data found when processing input']
EDIT
Using the excellent hits from WLGfx, I made this simple program to try decoding TS packets. As input, I prepared a file containing only TS packets from the Video PID.
It feels close but I don't know how to set up the FormatContext. The code below segfaults at av_read_frame() (and internally at ret = s->iformat->read_packet(s, pkt)). s->iformat is zero.
Suggestions?
EDIT II - Sorry, for got post source code **
**EDIT III - Sample code updated to simulate reading TS PKT Queue
/*
* Test program for video decoder
*/
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern "C" {
#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
#include <stdint.h>
#endif
}
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
}
class VideoDecoder
{
public:
VideoDecoder();
bool rcvTsPacket(AVPacket &inTsPacket);
private:
AVCodec *mpDecoder;
AVCodecContext *mpDecoderContext;
AVFrame *mpDecodedFrame;
AVFormatContext *mpFmtContext;
};
VideoDecoder::VideoDecoder()
{
av_register_all();
// FORMAT CONTEXT SETUP
mpFmtContext = avformat_alloc_context();
mpFmtContext->flags = AVFMT_NOFILE;
// ????? WHAT ELSE ???? //
// DECODER SETUP
mpDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!mpDecoder)
{
printf("Could not load decoder\n");
exit(11);
}
mpDecoderContext = avcodec_alloc_context3(NULL);
if (avcodec_open2(mpDecoderContext, mpDecoder, NULL) < 0)
{
printf("Cannot open decoder context\n");
exit(1);
}
mpDecodedFrame = av_frame_alloc();
}
bool
VideoDecoder::rcvTsPacket(AVPacket &inTsPkt)
{
bool ret = true;
if ((av_read_frame(mpFmtContext, &inTsPkt)) < 0)
{
printf("Error in av_read_frame()\n");
ret = false;
}
else
{
// success. Decode the TS packet
int got;
int len = avcodec_decode_video2(mpDecoderContext, mpDecodedFrame, &got, &inTsPkt);
if (len < 0)
ret = false;
if (got)
printf("GOT A DECODED FRAME\n");
}
return ret;
}
int
main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: %s tsInFile\n", argv[0]);
exit(1);
}
FILE *tsInFile = fopen(argv[1], "r");
if (!tsInFile)
{
perror("Could not open TS input file");
exit(2);
}
unsigned int tsPktNum = 0;
uint8_t tsBuffer[256];
AVPacket tsPkt;
av_init_packet(&tsPkt);
VideoDecoder vDecoder;
while (!feof(tsInFile))
{
tsPktNum++;
tsPkt.size = 188;
tsPkt.data = tsBuffer;
fread(tsPkt.data, 188, 1, tsInFile);
vDecoder.rcvTsPacket(tsPkt);
}
}
I've got some code snippets that might help you out as I've been working with MPEG-TS also.
Starting with my packet thread which checks each packet against the stream ID's which I've already found and got the codec contexts:
void *FFMPEG::thread_packet_function(void *arg) {
FFMPEG *ffmpeg = (FFMPEG*)arg;
for (int c = 0; c < MAX_PACKETS; c++)
ffmpeg->free_packets[c] = &ffmpeg->packet_list[c];
ffmpeg->packet_pos = MAX_PACKETS;
Audio.start_decoding();
Video.start_decoding();
Subtitle.start_decoding();
while (!ffmpeg->thread_quit) {
if (ffmpeg->packet_pos != 0 &&
Audio.okay_add_packet() &&
Video.okay_add_packet() &&
Subtitle.okay_add_packet()) {
pthread_mutex_lock(&ffmpeg->packet_mutex); // get free packet
AVPacket *pkt = ffmpeg->free_packets[--ffmpeg->packet_pos]; // pre decrement
pthread_mutex_unlock(&ffmpeg->packet_mutex);
if ((av_read_frame(ffmpeg->fContext, pkt)) >= 0) { // success
int id = pkt->stream_index;
if (id == ffmpeg->aud_stream.stream_id) Audio.add_packet(pkt);
else if (id == ffmpeg->vid_stream.stream_id) Video.add_packet(pkt);
else if (id == ffmpeg->sub_stream.stream_id) Subtitle.add_packet(pkt);
else { // unknown packet
av_packet_unref(pkt);
pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back
ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt;
pthread_mutex_unlock(&ffmpeg->packet_mutex);
//LOGI("Dumping unknown packet, id %d", id);
}
} else {
av_packet_unref(pkt);
pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back
ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt;
pthread_mutex_unlock(&ffmpeg->packet_mutex);
//LOGI("No packet read");
}
} else { // buffers full so yield
//LOGI("Packet reader on hold: Audio-%d, Video-%d, Subtitle-%d",
// Audio.packet_pos, Video.packet_pos, Subtitle.packet_pos);
usleep(1000);
//sched_yield();
}
}
return 0;
}
Each decoder for audio, video and subtitles have their own threads which receive the packets from the above thread in ring buffers. I've had to separate the decoders into their own threads because CPU usage was increasing when I started using the deinterlace filter.
My video decoder reads the packets from the buffers and when it has finished with the packet sends it back to be unref'd and can be used again. Balancing the packet buffers doesn't take that much time once everything is running.
Here's the snipped from my video decoder:
void *VideoManager::decoder(void *arg) {
LOGI("Video decoder started");
VideoManager *mgr = (VideoManager *)arg;
while (!ffmpeg.thread_quit) {
pthread_mutex_lock(&mgr->packet_mutex);
if (mgr->packet_pos != 0) {
// fetch first packet to decode
AVPacket *pkt = mgr->packets[0];
// shift list down one
for (int c = 1; c < mgr->packet_pos; c++) {
mgr->packets[c-1] = mgr->packets[c];
}
mgr->packet_pos--;
pthread_mutex_unlock(&mgr->packet_mutex); // finished with packets array
int got;
AVFrame *frame = ffmpeg.vid_stream.frame;
avcodec_decode_video2(ffmpeg.vid_stream.context, frame, &got, pkt);
ffmpeg.finished_with_packet(pkt);
if (got) {
#ifdef INTERLACE_ALL
if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0);
else {
if (!mgr->filter_initialised) mgr->init_filter_graph(frame);
av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
int c = 0;
while (true) {
AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame;
int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame);
if (result == AVERROR(EAGAIN) ||
result == AVERROR(AVERROR_EOF) ||
result < 0)
break;
mgr->add_av_frame(filter_frame, c++);
av_frame_unref(filter_frame);
}
//LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos);
}
#elif defined(INTERLACE_HALF)
if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0);
else {
if (!mgr->filter_initialised) mgr->init_filter_graph(frame);
av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
int c = 0;
while (true) {
AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame;
int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame);
if (result == AVERROR(EAGAIN) ||
result == AVERROR(AVERROR_EOF) ||
result < 0)
break;
mgr->add_av_frame(filter_frame, c++);
av_frame_unref(filter_frame);
}
//LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos);
}
#else
mgr->add_av_frame(frame, 0);
#endif
}
//LOGI("decoded video packet");
} else {
pthread_mutex_unlock(&mgr->packet_mutex);
}
}
LOGI("Video decoder ended");
}
As you can see, I'm using a mutex when passing packets back and forth.
Once a frame has been got I just copy the YUV buffers from the frame for later use into another buffer list. I don't convert the YUV, I use a shader which converts the YUV to RGB on the GPU.
The next snippet adds my decoded frame to my buffer list. This may help understand how to deal with the data.
void VideoManager::add_av_frame(AVFrame *frame, int field_num) {
int y_linesize = frame->linesize[0];
int u_linesize = frame->linesize[1];
int hgt = frame->height;
int y_buffsize = y_linesize * hgt;
int u_buffsize = u_linesize * hgt / 2;
int buffsize = y_buffsize + u_buffsize + u_buffsize;
VideoBuffer *buffer = &buffers[decode_pos];
if (ffmpeg.is_network && playback_pos == decode_pos) { // patched 25/10/16 wlgfx
buffer->used = false;
if (!buffer->data) buffer->data = (char*)mem.alloc(buffsize);
if (!buffer->data) {
LOGI("Dropped frame, allocation error");
return;
}
} else if (playback_pos == decode_pos) {
LOGI("Dropped frame, ran out of decoder frame buffers");
return;
} else if (!buffer->data) {
buffer->data = (char*)mem.alloc(buffsize);
if (!buffer->data) {
LOGI("Dropped frame, allocation error.");
return;
}
}
buffer->y_frame = buffer->data;
buffer->u_frame = buffer->y_frame + y_buffsize;
buffer->v_frame = buffer->y_frame + y_buffsize + u_buffsize;
buffer->wid = frame->width;
buffer->hgt = hgt;
buffer->y_linesize = y_linesize;
buffer->u_linesize = u_linesize;
int64_t pts = av_frame_get_best_effort_timestamp(frame);
buffer->pts = pts;
buffer->buffer_size = buffsize;
double field_add = av_q2d(ffmpeg.vid_stream.context->time_base) * field_num;
buffer->frame_time = av_q2d(ts_stream) * pts + field_add;
memcpy(buffer->y_frame, frame->data[0], (size_t) (buffer->y_linesize * buffer->hgt));
memcpy(buffer->u_frame, frame->data[1], (size_t) (buffer->u_linesize * buffer->hgt / 2));
memcpy(buffer->v_frame, frame->data[2], (size_t) (buffer->u_linesize * buffer->hgt / 2));
buffer->used = true;
decode_pos = (++decode_pos) % MAX_VID_BUFFERS;
//if (field_num == 0) LOGI("Video %.2f, %d - %d",
// buffer->frame_time - Audio.pts_start_time, decode_pos, playback_pos);
}
If there's anything else that I may be able to help with just give me a shout. :-)
EDIT:
The snippet how I open my video stream context which automatically determines the codec, whether it is h264, mpeg2, or another:
void FFMPEG::open_video_stream() {
vid_stream.stream_id = av_find_best_stream(fContext, AVMEDIA_TYPE_VIDEO,
-1, -1, &vid_stream.codec, 0);
if (vid_stream.stream_id == -1) return;
vid_stream.context = fContext->streams[vid_stream.stream_id]->codec;
if (!vid_stream.codec || avcodec_open2(vid_stream.context,
vid_stream.codec, NULL) < 0) {
vid_stream.stream_id = -1;
return;
}
vid_stream.frame = av_frame_alloc();
vid_stream.filter_frame = av_frame_alloc();
}
EDIT2:
This is how I've opened the input stream, whether it be file or URL. The AVFormatContext is the main context for the stream.
bool FFMPEG::start_stream(char *url_, float xtrim, float ytrim, int gain) {
aud_stream.stream_id = -1;
vid_stream.stream_id = -1;
sub_stream.stream_id = -1;
this->url = url_;
this->xtrim = xtrim;
this->ytrim = ytrim;
Audio.volume = gain;
Audio.init();
Video.init();
fContext = avformat_alloc_context();
if ((avformat_open_input(&fContext, url_, NULL, NULL)) != 0) {
stop_stream();
return false;
}
if ((avformat_find_stream_info(fContext, NULL)) < 0) {
stop_stream();
return false;
}
// network stream will overwrite packets if buffer is full
is_network = url.substr(0, 4) == "udp:" ||
url.substr(0, 4) == "rtp:" ||
url.substr(0, 5) == "rtsp:" ||
url.substr(0, 5) == "http:"; // added for wifi broadcasting ability
// determine if stream is audio only
is_mp3 = url.substr(url.size() - 4) == ".mp3";
LOGI("Stream: %s", url_);
if (!open_audio_stream()) {
stop_stream();
return false;
}
if (is_mp3) {
vid_stream.stream_id = -1;
sub_stream.stream_id = -1;
} else {
open_video_stream();
open_subtitle_stream();
if (vid_stream.stream_id == -1) { // switch to audio only
close_subtitle_stream();
is_mp3 = true;
}
}
LOGI("Audio: %d, Video: %d, Subtitle: %d",
aud_stream.stream_id,
vid_stream.stream_id,
sub_stream.stream_id);
if (aud_stream.stream_id != -1) {
LOGD("Audio stream time_base {%d, %d}",
aud_stream.context->time_base.num,
aud_stream.context->time_base.den);
}
if (vid_stream.stream_id != -1) {
LOGD("Video stream time_base {%d, %d}",
vid_stream.context->time_base.num,
vid_stream.context->time_base.den);
}
LOGI("Starting packet and decode threads");
thread_quit = false;
pthread_create(&thread_packet, NULL, &FFMPEG::thread_packet_function, this);
Display.set_overlay_timout(3.0);
return true;
}
EDIT: (constructing an AVPacket)
Construct an AVPacket to send to the decoder...
AVPacket packet;
av_init_packet(&packet);
packet.data = myTSpacketdata; // pointer to the TS packet
packet.size = 188;
You should be able to reuse the packet. And it might need unref'ing.
You must first use the avcodec library to get the compressed frames out of the file. Then you can decode them using avcodec_decode_video2. look at this tutorial http://dranger.com/ffmpeg/

Why do I get a crash only sometimes when closing input file with ffmpeg

I have a problem where only sometimes when I call avformat_close_input(&pFormatCtx) and it results in malloc check failed and my application crashes.
I really need to use ffmpeg because I need to grab a thumbnail of a video to show in a list and I cannot find an alternative library.
Can anybody see something in my code where I am doing something wrong when using this library which may cause this malloc check failed problem?
bool MuteCamera::PullFrame( )
{
pMJPEGCodec = avcodec_find_encoder(CODEC_ID_MJPEG );
bool bRet = false;
int videoStream = -1;
AVFrame *pFrame=NULL;
AVFrame *pFrameRGB=NULL;
AVPacket packet;
int frameFinished=0;
//AVDictionary *optionsDict = NULL;
AVInputFormat *pFormat = NULL;
const char formatName[] = "mp4";
if (!(pFormat = av_find_input_format(formatName))) {
printf("can't find input format %s\n", formatName);
return -1;
}
AVFormatContext *pFormatCtx = NULL;
pFormatCtx=avformat_alloc_context();
if(pFormatCtx == NULL)
{
printf("\n NULL CONTEXT \n ");
return -1;
}
if(avformat_open_input (&pFormatCtx, capturedUrl.data(), pFormat, NULL) == 0 )
{
for(int i=0; i<(int)pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStream=i;
break;
}
}
if(videoStream >= 0 )
{
AVCodecContext *pCodecCtx = pFormatCtx->streams[videoStream]->codec;
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec != NULL)
{
if( avcodec_open2(pCodecCtx, pCodec, NULL) >= 0 )
{
pFrame=avcodec_alloc_frame();
if(pFrame != NULL)
{
frameFinished = 0;
while(av_read_frame(pFormatCtx, &packet)>=0)
{
if(packet.stream_index==videoStream)
{
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if(frameFinished)
{
printf("\n FRAMEFINISHED \n ");
QString *uu = new QString(capturedUrl.data());//
uu->replace(".mp4", "thumbnail.jpg");
WriteJPEG(pCodecCtx, pFrame, uu->toLatin1().data(), PIX_FMT_YUVJ420P);
if(viewingVideos && viewingFromDifferent)
{
QVariantMap map = QVariantMap();
map["title"] = actualFilename;
map["path"] = actualFilename.replace(".mp4", "thumbnail.jpg");// QString("asset:///white_photo.png");
m_listDataModel << map;
}
delete uu;
av_free_packet(&packet);
break;
}
else
{
printf("\n FRAMENOTFINISHED \n ");
}
}
av_free_packet(&packet);
}
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
//av_free(pCodecCtx);
cout << "\n before free formatctx \n";
cout.flush();
if(pFormatCtx)
avformat_close_input(&pFormatCtx);
cout << "\n after free formatctx \n";
cout.flush();
}
else
bRet = false;
}
else
bRet = false;
}
else
bRet = false;
}
else
bRet = false;
}
return bRet;
}
bool WriteJPEG (AVCodecContext *pCodecCtx, AVFrame *pFrame, char cFileName[], PixelFormat pix)
{
int complete = 0;
bool bRet = false;
int out_buf_size;
uint8_t *out_buf;
AVCodecContext *pMJPEGCtx = avcodec_alloc_context3(pMJPEGCodec);
if( pMJPEGCtx )
{
pMJPEGCtx->bit_rate = pCodecCtx->bit_rate;
pMJPEGCtx->width = pCodecCtx->width;
pMJPEGCtx->height = pCodecCtx->height;
pMJPEGCtx->pix_fmt = pix;
pMJPEGCtx->codec_id = CODEC_ID_MJPEG;
pMJPEGCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pMJPEGCtx->time_base.num = pCodecCtx->time_base.num;
pMJPEGCtx->time_base.den = pCodecCtx->time_base.den;
pMJPEGCtx->time_base= (AVRational){1,29.7};
if( pMJPEGCodec && (avcodec_open2( pMJPEGCtx, pMJPEGCodec, NULL) >= 0) )
{
AVFrame *oframe;
oframe = avcodec_alloc_frame();
if(oframe == NULL)
{
printf("\n (oframe == NULL");
fflush(stdout);
}
/* calculate the bytes needed for the output image and create buffer for the output image */
out_buf_size = avpicture_get_size(pMJPEGCtx->pix_fmt,
pMJPEGCtx->width,
pMJPEGCtx->height);
out_buf = (uint8_t *)av_malloc(out_buf_size * sizeof(uint8_t));
if (out_buf == NULL) {
fprintf(stderr, "cannot allocate output data buffer!\n");
//ret = -ENOMEM;
}
avpicture_alloc((AVPicture *)oframe, pMJPEGCtx->pix_fmt, pMJPEGCtx->width, pMJPEGCtx->height);
struct SwsContext *sws;
sws = sws_getContext(pMJPEGCtx->width, pMJPEGCtx->height, pCodecCtx->pix_fmt,
pMJPEGCtx->width, pMJPEGCtx->height, pMJPEGCtx->pix_fmt, SWS_BILINEAR,
NULL, NULL, NULL);
sws_scale(sws, (const uint8_t **)pFrame->data, pFrame->linesize,
0, pMJPEGCtx->height, &oframe->data[0], &oframe->linesize[0]);
sws_freeContext(sws);
AVPacket pp2;
av_init_packet(&pp2);
pp2.data = NULL;
pp2.size = 0;
avcodec_encode_video2(pMJPEGCtx, &pp2, oframe, &complete);
if(complete)
{
printf("\n packet recieved");
fflush(stdout);
}
else
{
printf("\n packet NOT recieved");
fflush(stdout);
}
if( SaveFrameJpeg(pp2.size, pp2.data, cFileName ) )
bRet = true;
av_free(oframe);
avcodec_close(pMJPEGCtx);
av_free_packet(&pp2);
av_free(out_buf);
av_free(pMJPEGCtx);
}
else
{
printf("\n problem!!");
fflush(stdout);
}
return bRet;
}
}
bool SaveFrameJpeg(int nszBuffer, uint8_t *buffer, char cOutFileName[])
{
bool bRet = false;
FILE *pFile;
if( nszBuffer > 0 )
{
if(0 == 0 )
{
printf("\n start SaveFrameJpeg=%d",nszBuffer );
fflush(stdout);
pFile= fopen(cOutFileName, "wb");
fwrite(buffer, sizeof(uint8_t), nszBuffer, pFile);
bRet = true;
fclose(pFile);
printf("\n end SaveFrameJpeg=%d",nszBuffer );
fflush(stdout);
}
}
return bRet;
}
bool newPullFrame(const std::string& capturedUrl)
{
AVCodec* pMJPEGCodec = avcodec_find_encoder(CODEC_ID_MJPEG );
int videoStream = -1;
AVDictionary *optionsDict = NULL;
AVInputFormat *pFormat = NULL;
const char formatName[] = "mp4";
if (!(pFormat = av_find_input_format(formatName)))
{
std::cout << "can't find input format " << formatName << "\n";
return false;
}
AVFormatContextHandle FormatCtx(avformat_alloc_context());
if(!FormatCtx.is_valid())
{
std::cout << "\n NULL CONTEXT \n ";
return false;
}
if(avformat_open_input (&FormatCtx, capturedUrl.c_str(), pFormat, NULL))
return false;
for(int i=0; i<(int)FormatCtx->nb_streams; i++)
{
if(FormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStream=i;
break;
}
}
if(videoStream < 0 )
return false;
CodecContextHandle CodecCtx(FormatCtx->streams[videoStream]->codec, avcodec_close);
AVCodec *pCodec = avcodec_find_decoder(CodecCtx->codec_id);
if(pCodec == NULL)
return false;
if( avcodec_open2(CodecCtx, pCodec, &optionsDict) < 0 )
return false;
FrameHandle Frame(avcodec_alloc_frame(), av_free);
if(!Frame.is_valid())
return false;
int frameFinished=0;
AVPacket packet;
while(av_read_frame(FormatCtx, &packet)>=0)
{
if(packet.stream_index==videoStream)
{
avcodec_decode_video2(CodecCtx, Frame, &frameFinished, &packet);
if(frameFinished)
{
std::string uu (capturedUrl);
size_t pos = capturedUrl.rfind(".mp4");
uu.replace(pos, 4, "thumbnail.jpg");
// save the frame to file
int Bytes = avpicture_get_size(PIX_FMT_YUVJ420P, CodecCtx->width, CodecCtx->height);
BufferHandle buffer((uint8_t*)av_malloc(Bytes*sizeof(uint8_t)), av_free);
CodecContextHandle OutContext(avcodec_alloc_context3(NULL), free_context);
OutContext->bit_rate = CodecCtx->bit_rate;
OutContext->width = CodecCtx->width;
OutContext->height = CodecCtx->height;
OutContext->pix_fmt = PIX_FMT_YUVJ420P;
OutContext->codec_id = CODEC_ID_MJPEG;
OutContext->codec_type = AVMEDIA_TYPE_VIDEO;
OutContext->time_base.num = CodecCtx->time_base.num;
OutContext->time_base.den = CodecCtx->time_base.den;
OutContext->time_base= (AVRational){1,29.7};
AVCodec *OutCodec = avcodec_find_encoder(OutContext->codec_id);
avcodec_open2(OutContext, OutCodec, NULL);
OutContext->mb_lmin = OutContext->lmin = OutContext->qmin * 118;
OutContext->mb_lmax = OutContext->lmax = OutContext->qmax * 118;
OutContext->flags = 2;
OutContext->global_quality = OutContext->qmin * 118;
Frame->pts = 1;
Frame->quality = OutContext->global_quality;
int ActualSize = avcodec_encode_video(OutContext, buffer, Bytes, Frame);
std::ofstream file(uu.data(), std::ios_base::binary | std::ios_base::out);
file.write((const char*)(uint8_t*)buffer, ActualSize);
file.close();
av_free_packet(&packet);
av_free(Frame);
break;
}
else
{
std::cout << " new pullframe frameNOTfinished\n";
cout.flush();
}
//if(CodecCtx->refcounted_frames == 1)
av_free(Frame);
}
av_free_packet(&packet);
}
return true;
}
It looks to me like you need to move your call to av_free_packet(&packet) inside your while loop. So you currently have:
while(av_read_frame(pFormatCtx, &packet)>=0)
{
// A bunch of operations here
}
// this is not the right place for this
av_free_packet(&packet);
Instead you should have
while(av_read_frame(pFormatCtx, &packet)>=0)
{
// A bunch of operations here
// this needs to be called for every call to av_read_frame()
// so it must be inside the while loop
av_free_packet(&packet);
}
See here for further details.
Considering that you writing this in C++ you could consider creating some simple RAII wrappers around these resources to make your resource management much easier.
EDIT
Based on your feedback it seems that this change that I recommended didn't do it. So I revisited the code. I implemented my own save to Jpeg as I could not see yours - though for test purposes it simply keeps over-writing the same file.
In order to simplify the code and get a handle on the resource management I implemented some "smart pointers" for the ffmpeg resources. These automatically clean up at scope exit. I cannot see a resource leak when running through this code and it correctly generates each frame as a jpeg file.
See if you get any value out of this:
extern "C" {
#include <libavdevice\avdevice.h>
#include <libavformat\avformat.h>
#include <libavfilter\avfilter.h>
#include <libavcodec\avcodec.h>
#include <libswscale\swscale.h>
}
#include <iostream>
#include <fstream>
#include <ios>
#include <type_traits>
template<typename T, typename D>
class AVHandle
{
T *val;
typedef D* deleter_t;
deleter_t deleter;
// not default constructible
AVHandle();
// non copiable
AVHandle(const AVHandle&);
AVHandle& operator=(const AVHandle&);
public:
AVHandle(T *in, deleter_t del) : val(in), deleter(del)
{}
operator T *()
{
return val;
}
T* operator->()
{
return val;
}
bool is_valid()
{
return val != 0;
}
~AVHandle()
{
deleter(val);
}
};
typedef AVHandle<AVFrame, void (void*)> FrameHandle;
typedef AVHandle<AVCodecContext, int (AVCodecContext*)> CodecContextHandle;
typedef AVHandle<uint8_t, void(void*)> BufferHandle;
class AVFormatContextHandle
{
AVFormatContext *val;
// not default constrcutible
AVFormatContextHandle();
// non copiable
AVFormatContextHandle(const AVFormatContextHandle&);
AVFormatContextHandle& operator=(const AVFormatContextHandle&);
public:
AVFormatContextHandle(AVFormatContext *ctx) : val(ctx)
{}
operator AVFormatContext *()
{
return val;
}
AVFormatContext* operator ->()
{
return val;
}
AVFormatContext** operator&()
{
return &val;
}
bool is_valid()
{
return val != 0;
}
~AVFormatContextHandle()
{
if(val)
avformat_close_input(&val);
}
};
int free_context(AVCodecContext* c)
{
int ret = avcodec_close(c);
av_free(c);
return ret;
}
bool PullFrame(const std::string& capturedUrl)
{
AVCodec* pMJPEGCodec = avcodec_find_encoder(CODEC_ID_MJPEG );
int videoStream = -1;
AVDictionary *optionsDict = NULL;
AVInputFormat *pFormat = NULL;
const char formatName[] = "mp4";
if (!(pFormat = av_find_input_format(formatName)))
{
std::cout << "can't find input format " << formatName << "\n";
return false;
}
AVFormatContextHandle FormatCtx(avformat_alloc_context());
if(!FormatCtx.is_valid())
{
std::cout << "\n NULL CONTEXT \n ";
return false;
}
if(avformat_open_input (&FormatCtx, capturedUrl.c_str(), pFormat, NULL))
return false;
for(int i=0; i<(int)FormatCtx->nb_streams; i++)
{
if(FormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStream=i;
break;
}
}
if(videoStream < 0 )
return false;
CodecContextHandle CodecCtx(FormatCtx->streams[videoStream]->codec, avcodec_close);
AVCodec *pCodec = avcodec_find_decoder(CodecCtx->codec_id);
if(pCodec == NULL)
return false;
if( avcodec_open2(CodecCtx, pCodec, &optionsDict) < 0 )
return false;
FrameHandle Frame(avcodec_alloc_frame(), av_free);
if(!Frame.is_valid())
return false;
int frameFinished=0;
AVPacket packet;
while(av_read_frame(FormatCtx, &packet)>=0)
{
if(packet.stream_index==videoStream)
{
avcodec_decode_video2(CodecCtx, Frame, &frameFinished, &packet);
if(frameFinished)
{
std::string uu (capturedUrl);
size_t pos = capturedUrl.rfind(".mp4");
uu.replace(pos, 4, "thumbnail.jpg");
// save the frame to file
int Bytes = avpicture_get_size(PIX_FMT_YUVJ420P, CodecCtx->width, CodecCtx->height);
BufferHandle buffer((uint8_t*)av_malloc(Bytes*sizeof(uint8_t)), av_free);
CodecContextHandle OutContext(avcodec_alloc_context3(NULL), free_context);
OutContext->bit_rate = CodecCtx->bit_rate;
OutContext->width = CodecCtx->width;
OutContext->height = CodecCtx->height;
OutContext->pix_fmt = PIX_FMT_YUVJ420P;
OutContext->codec_id = CODEC_ID_MJPEG;
OutContext->codec_type = AVMEDIA_TYPE_VIDEO;
OutContext->time_base.num = CodecCtx->time_base.num;
OutContext->time_base.den = CodecCtx->time_base.den;
AVCodec *OutCodec = avcodec_find_encoder(OutContext->codec_id);
avcodec_open2(OutContext, OutCodec, NULL);
OutContext->mb_lmin = OutContext->lmin = OutContext->qmin * 118;
OutContext->mb_lmax = OutContext->lmax = OutContext->qmax * 118;
OutContext->flags = 2;
OutContext->global_quality = OutContext->qmin * 118;
Frame->pts = 1;
Frame->quality = OutContext->global_quality;
int ActualSize = avcodec_encode_video(OutContext, buffer, Bytes, Frame);
std::ofstream file("c:\\temp\\output.jpg", std::ios_base::binary | std::ios_base::out);
file.write((const char*)(uint8_t*)buffer, ActualSize);
}
if(CodecCtx->refcounted_frames == 1)
av_frame_unref(Frame);
}
av_free_packet(&packet);
}
return true;
}
int main()
{
av_register_all();
while(true)
PullFrame("c:\\temp\\sample_mpeg4.mp4");
return 0;
}