I am creating a Screen recorder with mic recording using FFMPEG library in C++ . The problem is that the output file is playing video with audio in VLC player only . If we play the file in windows media player or any other player (even in mobile also )only video is playing and NO audio we can hear.But if I re-encode the same file using FFMPEG.EXE command line application using the command
ffmpeg -i myfile.mp4 output.mp4
Then the output.mp4 is playing in all players with video and audio correctly.So I analysis the both using ffprobe
ffprobe -show_streams -select_streams a -i myfile.mp4
ffprobe -show_streams -select_streams a -i output.mp4
The main difference I found is that in myfile.mp4 profile=-1 and output.mp4 profile=LC
So currently I am trying to add the profile also to my C++ code but it is not setting. Following is my audio encoder settings part is below
const AVCodec* av_codec_out;
AVCodecParameters* av_codec_par_out;
AVFormatContext* ifmt_ctx;
AVCodecContext* av_cntx_in;
AVCodecContext* av_cntx_out;
av_codec_par_out = avcodec_parameters_alloc();
av_codec_par_out->bit_rate = 128000;
av_codec_par_out->codec_id = AV_CODEC_ID_AAC;
av_codec_par_out->codec_type = avcodec_get_type(AV_CODEC_ID_AAC);
av_codec_par_out->format = AV_SAMPLE_FMT_FLTP;
av_codec_par_out->profile = FF_PROFILE_AAC_LOW; // I tried by assigning here
av_dict_set(&options, "sample_rate", "44100", 0);
av_dict_set(&options, "async", "1", 0);
av_dict_set(&options, "channels", "2", 0);
av_dict_set(&options, "profile", "aac_low", 0); // I tried by assigning here
ifmt = av_find_input_format("dshow");
avformat_open_input(&ifmt_ctx, "my audio device name", ifmt, &options);
av_codec_out = avcodec_find_encoder(av_codec_par_out->codec_id);
av_cntx_out = avcodec_alloc_context3(av_codec_out);
av_stream = avformat_new_stream(ofmt_ctx, av_codec_out);
avcodec_parameters_to_context(av_cntx_out, av_codec_par_out);
av_cntx_out->time_base.num = 1;
av_cntx_out->time_base.den = av_codec_par_in->sample_rate;
av_cntx_out->strict_std_compliance = FF_COMPLIANCE_NORMAL;
av_cntx_out->profile = FF_PROFILE_AAC_LOW; // I tried by assigning here
avcodec_parameters_copy(av_stream->codecpar, av_codec_par_out);
and open the output codec like below
avcodec_open2(av_cntx_out, av_codec_out, NULL);
Reproducible code sample : https://github.com/arunk98475/AudioVideoRecording/blob/main/main.cpp
It looks like we have to call avcodec_parameters_from_context
after avcodec_open2
:
void RecordVars::SetOutputCodecContext(AVDictionary* dictOptions)
{
if (avcodec_open2(av_cntx_out, av_codec_out, (dictOptions == NULL) ? NULL : &dictOptions) < 0)
{
throw exception("Unable to open the av output codec context");
}
avcodec_parameters_from_context(av_stream->codecpar, av_cntx_out); // <--- It looks like we have to copy after avcodec_open2
}
There are few parameters that updated only after executing avcodec_open2
.
Parameters such as extradata
, bits_per_raw_sample
, channel_layout
, channels
, frame_size
...
I suppose that profile=-1
is just a symptom of incorrect stream parameters.
Update code sample (tested with FFmpeg 5.1.2):
#define __STDC_CONSTANT_MACROS
#include<iostream>
#include<string.h>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <stdexcept>
#include <thread>
#include <vector>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libavutil/imgutils.h>
#include <libavutil/audio_fifo.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
using namespace std;
class Timer
{
chrono::time_point<chrono::system_clock> start;
public:
Timer() {};
void StartTimer()
{
start = chrono::system_clock::now();
};
__int64 ElapsedTime()
{
return chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - start).count();
}
__int64 ElapsedTimeM()
{
return chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - start).count();
}
};
enum MediaSettings
{
UNKNOWN = -1,
VIDEO_DEFAULT,
AUDIO_DEFAULT,
AUDIO_FOR_VIDEO,
VIDEO_CUSTOM,
AUDIO_CUSTOM
};
class RecordVars
{
const char* m_strFrameR;
int m_iFrameRate;
const char* m_crf;
public:
const AVOutputFormat* ofmt;
const AVInputFormat* ifmt;
AVFormatContext* ifmt_ctx;
static AVFormatContext* ofmt_ctx;
static mutex m_Mutex;
static bool m_Ready;
condition_variable cv;
static bool m_bLoopBreaker;
Timer* m_Timer;
AVCodecParameters* av_codec_par_in;
AVCodecParameters* av_codec_par_out;
AVCodecContext* av_cntx_in;
AVCodecContext* av_cntx_out;
const AVCodec* av_codec_in;
const AVCodec* av_codec_out;
AVStream* av_stream;
AVDictionary* options;
AVPacket* av_pkt_in;
AVPacket* av_pkt_out;
AVFrame* av_frm_in;
AVFrame* av_frm_out;
AVAudioFifo* fifo;
int stream_idx;
SwsContext* swsCtx;
SwrContext* resampleContext;
MediaSettings* m_MedSettings;
RecordVars(MediaSettings* medSett, string frameRate = "24");
void OpenInputMedia(const char* shortName, const char* mediaFormat);
void FindMediaStreamIndex(AVMediaType av_media_type);
void SetOutPutFilename(const char* short_name = NULL, const char* filename = NULL, const char* mime_type = NULL);
void SetOutputCodecContext(AVDictionary* dictOptions = NULL);
~RecordVars();
void SetOutPutFormatContext(AVFormatContext* outFmtCtx);
void writeFormatHeader();
void SetVideoSwsContext();
void SetAudioSwrContext();
static void VideoAudioThreadDriver(RecordVars* video, RecordVars* audio);
int add_samples_to_fifo(uint8_t** converted_input_samples, const int frame_size);
void SetMaxTimeofRec(int sec);
void ScreenCapThread();
void SetCrFValue(string crf);
private:
void ApplyDefaultVideoInputSettings();
void ApplyDefaultAudioInputSettings(AVCodecID codecType);
const char* CombineConstChars(const char* firstChar, const char* secdChar);
int GOP_SIZE;
int MAX_B_FRAMES;
int TIME_BASE_NUM;
int TIME_BASE_DEN;
const char* OUT_PUT_FILE_NAME;
__int64 m_iMaxTime;
void MicCaptureThread();
int initConvertedSamples(uint8_t*** converted_input_samples, AVCodecContext* output_codec_context, int frame_size);
};
AVFormatContext* RecordVars::ofmt_ctx = NULL;
bool RecordVars::m_bLoopBreaker = false;
mutex RecordVars::m_Mutex;
bool RecordVars::m_Ready = false;
RecordVars::RecordVars(MediaSettings* medSett, string frameRate)
{
avdevice_register_all();
m_strFrameR = frameRate.c_str();
m_iFrameRate = atoi(m_strFrameR);
m_Timer = new Timer();
ofmt = NULL;
ifmt = NULL;
options = NULL;
av_codec_par_in = avcodec_parameters_alloc();
if (av_codec_par_in == NULL)
{
throw exception("Unable to allocate input codec ");
}
m_iMaxTime = 20;
av_codec_par_out = avcodec_parameters_alloc();
if (av_codec_par_out == NULL)
{
throw exception("Unable to allocate output codec ");
}
av_cntx_in = NULL;
av_cntx_out = NULL;
av_stream = NULL;
stream_idx = -1;
av_frm_out = av_frame_alloc();
if (av_frm_out == NULL)
{
throw exception("Unable to allocate output frame ");
}
av_pkt_in = av_packet_alloc();
if (av_pkt_in == NULL)
{
throw exception("Unable to allocate input packet ");
}
memset(av_pkt_in, 0, sizeof(AVPacket));
av_frm_in = av_frame_alloc();
if (av_frm_in == NULL)
{
throw exception("Unable to allocate input frame ");
}
av_pkt_out = av_packet_alloc();
if (av_pkt_out == NULL)
{
throw exception("Unable to allocate output packet ");
}
if (*medSett == UNKNOWN)
{
throw invalid_argument("Unknown Media Input settings");
}
else if (*medSett == VIDEO_DEFAULT)
{
m_MedSettings = medSett;
ApplyDefaultVideoInputSettings();
}
else if (*medSett == AUDIO_FOR_VIDEO)
{
m_MedSettings = medSett;
ApplyDefaultAudioInputSettings(AV_CODEC_ID_AAC);
}
else if (*medSett == AUDIO_DEFAULT)
{
m_MedSettings = medSett;
ApplyDefaultAudioInputSettings(AV_CODEC_ID_MP3);
}
else if (*medSett == VIDEO_CUSTOM || *medSett == AUDIO_CUSTOM)
{
m_MedSettings = medSett;
}
else
{
throw invalid_argument("Please in put valid parameter");
}
}
RecordVars::~RecordVars()
{
m_Ready = false;
m_bLoopBreaker = false;
if (av_cntx_out)
{
avcodec_free_context(&av_cntx_out);
av_cntx_out = NULL;
}
if (ifmt_ctx)
{
avformat_close_input(&ifmt_ctx);
avformat_free_context(ifmt_ctx);
ifmt_ctx = NULL;
}
if (ofmt_ctx)
{
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
ofmt_ctx = NULL;
}
if (m_Timer)
{
delete m_Timer;
m_Timer = NULL;
}
if (av_codec_par_out)
{
avcodec_parameters_free(&av_codec_par_out);
av_codec_par_out = NULL;
}
if (av_cntx_in)
{
avcodec_free_context(&av_cntx_in);
av_cntx_in = NULL;
}
if (av_cntx_out)
{
avcodec_free_context(&av_cntx_out);
av_cntx_out = NULL;
}
if (options)
{
av_dict_free(&options);
options = NULL;
}
if (av_pkt_in)
{
av_packet_unref(av_pkt_in);
av_pkt_in = NULL;
}
if (av_pkt_out)
{
av_packet_unref(av_pkt_out);
av_pkt_out = NULL;
}
if (av_frm_in)
{
av_frame_free(&av_frm_in);
av_frm_in = NULL;
}
if (av_frm_out)
{
av_frame_free(&av_frm_out);
av_frm_out = NULL;
}
if (fifo)
{
av_audio_fifo_free(fifo);
fifo = NULL;
}
if (swsCtx)
{
sws_freeContext(swsCtx);
swsCtx = NULL;
}
if (resampleContext)
{
swr_free(&resampleContext);
resampleContext = NULL;
}
}
void RecordVars::SetOutPutFormatContext(AVFormatContext* outFmtCtx)
{
ofmt_ctx = outFmtCtx;
}
void RecordVars::ApplyDefaultVideoInputSettings()
{
av_codec_par_out->height = 840;
av_codec_par_out->width = 1120;
av_codec_par_out->bit_rate = 900000;
av_codec_par_out->codec_id = AV_CODEC_ID_H264;
av_codec_par_out->codec_type = AVMEDIA_TYPE_VIDEO;
av_codec_par_out->format = AV_PIX_FMT_YUV420P;
av_codec_par_out->sample_aspect_ratio.den = 3;
av_codec_par_out->sample_aspect_ratio.num = 0;
GOP_SIZE = m_iFrameRate;
MAX_B_FRAMES = 2;
TIME_BASE_NUM = 1;
TIME_BASE_DEN = m_iFrameRate;
if (av_dict_set(&options, "framerate", m_strFrameR, 0) < 0)
{
throw exception("Unable to set default frame rate for video input");
}
if (av_dict_set(&options, "probesize", "42M", 0) < 0)
{
throw exception("Unable to set default video size for video input");
}
if (av_dict_set(&options, "preset", "ultrafast", 0) < 0)
{
throw exception("Unable to set ultrafast video capture option");
}
}
void RecordVars::ApplyDefaultAudioInputSettings(AVCodecID codecType)
{
av_codec_par_out->bit_rate = 96000;
av_codec_par_out->codec_id = codecType;
av_codec_par_out->codec_type = AVMEDIA_TYPE_AUDIO;
av_codec_par_out->format = AV_SAMPLE_FMT_FLTP;
av_codec_par_out->profile = FF_PROFILE_AAC_LOW;
//if (av_dict_set(&options, "sample_rate", "44100", 0) < 0)
//{
// throw exception("Error: cannot set sample rate in input option");
//}
if (av_dict_set(&options, "async", "1", 0) < 0)
{
throw exception("Error: cannot set async in input option");
}
//if (av_dict_set(&options, "channels", "2", 0) < 0)
//{
// throw exception("Error: cannot set channels in input option");
//}
const int OUTPUT_CHANNELS = 2;
//av_codec_par_out->channels = OUTPUT_CHANNELS;
//av_codec_par_out->ch_layout = av_get_default_channel_layout(OUTPUT_CHANNELS); //FFmpeg 4 and below
av_channel_layout_default(&av_codec_par_out->ch_layout, OUTPUT_CHANNELS); //FFmpeg 5 and above
av_codec_par_out->sample_rate = 44100;
}
static char tmp_buffer[256]; //Define here (don't allocate on the stack).
const char* RecordVars::CombineConstChars(const char* firstChar, const char* secdChar)
{
char* res = new char[strlen(firstChar) + strlen(secdChar) + 1];
int size = (int)(strlen(firstChar) + strlen(secdChar) + 1);
//char buffer[256];
strcpy_s(tmp_buffer, firstChar);
strcat_s(tmp_buffer, secdChar);
return tmp_buffer; //warning C4172: returning address of local variable or temporary: buffer
}
void RecordVars::OpenInputMedia(const char* shortName, const char* mediaFormat)
{
ifmt = av_find_input_format(shortName);
if (ifmt == NULL)
{
throw exception(CombineConstChars("Unable to find input :", shortName));
}
if (avformat_open_input(&ifmt_ctx, mediaFormat, ifmt, &options) < 0)
{
throw exception(CombineConstChars("Unable to find media format :", mediaFormat));
}
}
void RecordVars::FindMediaStreamIndex(AVMediaType av_media_type)
{
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0)
{
throw exception("Error in Finding Stream");
}
for (int i = 0; i < (int)ifmt_ctx->nb_streams; i++)
{
if (ifmt_ctx->streams[i]->codecpar->codec_type == av_media_type)
{
stream_idx = i;
break;
}
}
if (stream_idx == -1)
{
throw exception("Type of stream in parameter has not found");
}
av_codec_par_in = ifmt_ctx->streams[stream_idx]->codecpar;
if (*m_MedSettings == AUDIO_DEFAULT || *m_MedSettings == AUDIO_FOR_VIDEO)
{
av_codec_par_out->sample_rate = av_codec_par_in->sample_rate;
av_channel_layout_default(&av_codec_par_out->ch_layout, av_codec_par_in->ch_layout.nb_channels);
if (*m_MedSettings == AUDIO_FOR_VIDEO)
av_codec_par_out->frame_size = 1024 * 16 * av_codec_par_in->ch_layout.nb_channels;
}
av_codec_in = avcodec_find_decoder(av_codec_par_in->codec_id);
if (av_codec_in == NULL)
{
throw exception("Unable to find input codec");
}
av_cntx_in = avcodec_alloc_context3(av_codec_in);
if (av_cntx_in == NULL)
{
throw exception("Unable to allocate input codec context");
}
if (avcodec_parameters_to_context(av_cntx_in, av_codec_par_in) < 0)
{
throw exception("Unable to copy input codec parameters to input codec context");
}
AVDictionary* codec_options_in(0);
if (*m_MedSettings == VIDEO_DEFAULT)
{
av_dict_set(&codec_options_in, "preset", "ultrafast", 0);
av_dict_set(&codec_options_in, "rtbufsize", "1500M", 0);
av_dict_set(&codec_options_in, "crf", m_crf, 0);
}
if (avcodec_open2(av_cntx_in, av_codec_in, &codec_options_in) < 0)
{
throw exception("Unable to Initialize the AVCodecContext to use the given AVCodec.");
}
av_codec_par_out->height = av_codec_par_in->height;
av_codec_par_out->width = av_codec_par_in->width;
}
void RecordVars::SetOutPutFilename(const char* short_name, const char* filename, const char* mime_type)
{
ofmt = av_guess_format(short_name, filename, mime_type);
OUT_PUT_FILE_NAME = filename;
if (ofmt == NULL)
{
throw exception("No matching format found . Please try with correct file extension.");
}
if (ofmt_ctx == NULL)
{
if (avformat_alloc_output_context2(&ofmt_ctx, ofmt, NULL, filename) < 0)
{
throw exception("Error in allocating av format output context");
}
}
av_codec_out = avcodec_find_encoder(av_codec_par_out->codec_id);
av_cntx_out = avcodec_alloc_context3(av_codec_out);
if (av_codec_out == NULL)
{
throw exception("Unable to find an encoder");
}
av_stream = avformat_new_stream(ofmt_ctx, av_codec_out);
if (!av_stream)
{
throw exception("Unable to find the corresponding stream");
}
if (!av_cntx_out)
{
throw exception("error in allocating the codec contexts");
}
if (avcodec_parameters_to_context(av_cntx_out, av_codec_par_out) < 0)
{
throw exception("error in converting the codec contexts");
}
if (*m_MedSettings == VIDEO_DEFAULT)
{
av_cntx_out->gop_size = GOP_SIZE;
av_cntx_out->max_b_frames = MAX_B_FRAMES;
av_cntx_out->time_base.num = TIME_BASE_NUM;
av_cntx_out->time_base.den = TIME_BASE_DEN;
av_cntx_out->ticks_per_frame = 1;
}
else if (*m_MedSettings == AUDIO_DEFAULT || *m_MedSettings == AUDIO_FOR_VIDEO)
{
av_cntx_out->time_base.num = 1;
av_cntx_out->time_base.den = av_codec_par_in->sample_rate;
av_cntx_out->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
}
if (*m_MedSettings == AUDIO_FOR_VIDEO)
{
if ((av_codec_out)->supported_samplerates)
{
av_cntx_out->sample_rate = (av_codec_out)->supported_samplerates[0];
for (int i = 0; (av_codec_out)->supported_samplerates[i]; i++)
{
if ((av_codec_out)->supported_samplerates[i] == av_cntx_in->sample_rate)
{
av_cntx_out->sample_rate = av_cntx_in->sample_rate;
}
}
}
}
if (avcodec_parameters_copy(av_stream->codecpar, av_codec_par_out) < 0)
{
throw exception("Codec parameter canot copied");
}
}
void RecordVars::SetOutputCodecContext(AVDictionary* dictOptions)
{
if (avcodec_open2(av_cntx_out, av_codec_out, (dictOptions == NULL) ? NULL : &dictOptions) < 0) //<-- Using &dictOptions when dictOptions=NULL is not recommended.
{
throw exception("Unable to open the av output codec context");
}
avcodec_parameters_from_context(av_stream->codecpar, av_cntx_out); // <--- It looks like we have to copy after avcodec_open2
}
void RecordVars::writeFormatHeader()
{
if (avio_open(&ofmt_ctx->pb, OUT_PUT_FILE_NAME, AVIO_FLAG_READ_WRITE) < 0)
{
throw exception("Unable to Create and initialize a AVIOContext");
}
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
{
av_cntx_out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
if (*m_MedSettings != AUDIO_FOR_VIDEO)
{
if (avformat_write_header(ofmt_ctx, NULL) < 0)
{
throw exception("Unable to write format header");
}
}
}
void RecordVars::SetVideoSwsContext()
{
av_frm_in->width = av_cntx_in->width;
av_frm_in->height = av_cntx_in->height;
av_frm_in->format = av_codec_par_in->format;
av_frm_out->width = av_cntx_out->width;
av_frm_out->height = av_cntx_out->height;
av_frm_out->format = av_codec_par_out->format;
if (av_frame_get_buffer(av_frm_in, 0) < 0)
{
throw exception("Unable allocate input frame buffer");
}
if (av_frame_get_buffer(av_frm_out, 0) < 0)
{
throw exception("Unable allocate output frame buffer");
}
swsCtx = sws_alloc_context();
if (sws_init_context(swsCtx, NULL, NULL) < 0)
{
throw exception("Unable to Initialize the swscaler context sws_context.");
}
swsCtx = sws_getContext(av_cntx_in->width, av_cntx_in->height, av_cntx_in->pix_fmt,
av_cntx_out->width, av_cntx_out->height, av_cntx_out->pix_fmt,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if (swsCtx == NULL)
{
throw exception("Cannot allocate SWC Context");
}
}
void RecordVars::SetAudioSwrContext()
{
fifo = av_audio_fifo_alloc(av_cntx_out->sample_fmt, av_cntx_out->ch_layout.nb_channels, 1);
if (fifo == NULL)
{
throw exception("Could not allocate FIFOn");
}
resampleContext = NULL;
AVChannelLayout outChannelLayout, inChannelLayout;
av_channel_layout_default(&outChannelLayout, av_cntx_out->ch_layout.nb_channels);
av_channel_layout_default(&inChannelLayout, av_cntx_in->ch_layout.nb_channels);
int res = swr_alloc_set_opts2
(
&resampleContext,
&outChannelLayout,
av_cntx_out->sample_fmt,
av_cntx_out->sample_rate,
&inChannelLayout,
av_cntx_in->sample_fmt,
av_cntx_in->sample_rate,
0,
nullptr
);
if (resampleContext == NULL)
{
throw exception("Cannot allocate the resample context");
}
if ((swr_init(resampleContext)) < 0)
{
swr_free(&resampleContext);
throw exception("Could not open resample contextn");
}
}
void RecordVars::VideoAudioThreadDriver(RecordVars* video, RecordVars* audio)
{
if (video != NULL && audio != NULL)
{
vector<thread> threads;
threads.push_back(thread(&RecordVars::MicCaptureThread, audio));
threads.push_back(thread(&RecordVars::ScreenCapThread, video));
for (auto& thread : threads) {
thread.join();
}
}
else if (video != NULL)
{
thread tV(&RecordVars::ScreenCapThread, video);
tV.join();
}
else if (audio != NULL)
{
thread tA(&RecordVars::MicCaptureThread, audio);
tA.join();
}
else
{
return;
}
}
void RecordVars::ScreenCapThread()
{
int ii = 0;
int enc_packet_counter = 0;
m_Timer->StartTimer();
int frameFinished;
//int got_picture;
int frame_index = 0;
av_pkt_out = av_packet_alloc();
int j = 0;
while (av_read_frame(ifmt_ctx, av_pkt_in) >= 0)
{
if (m_Timer->ElapsedTimeM() >= m_iMaxTime * 1000)
{
RecordVars::m_bLoopBreaker = true;
break;
}
//int pts = m_Timer->ElapsedTimeM() * 90;
int64_t pts = m_Timer->ElapsedTimeM() * 90;
if (RecordVars::m_bLoopBreaker)
break;
int ret = avcodec_send_packet(av_cntx_in, av_pkt_in);
if (ret < 0)
{
printf("Error while sending packet");
}
frameFinished = true;
int response = 0;
response = avcodec_receive_frame(av_cntx_in, av_frm_in);
if (response < 0)
{
printf("Error while receiving frame from decoder");
frameFinished = false;
}
if (frameFinished)
{
memset(av_pkt_out, 0, sizeof(AVPacket)); //???
av_pkt_out->data = NULL;
av_pkt_out->size = 0;
av_pkt_out->pts = pts;
av_pkt_out->dts = AV_NOPTS_VALUE;
av_pkt_out->duration = av_rescale_q(1, av_cntx_out->time_base, av_stream->time_base);
av_frm_out->pts = pts;
av_frm_out->pkt_duration = av_rescale_q(1, av_cntx_out->time_base, av_stream->time_base);
enc_packet_counter++;
int sts = sws_scale(swsCtx,
av_frm_in->data,
av_frm_in->linesize,
0,
av_frm_in->height,
av_frm_out->data,
av_frm_out->linesize);
if (sts < 0)
{
printf("Error while executing sws_scale");
}
int ret = 0;
do
{
if (ret == AVERROR(EAGAIN))
{
av_packet_unref(av_pkt_out);
ret = avcodec_receive_packet(av_cntx_out, av_pkt_out);
if (ret) break;
av_pkt_out->duration = av_rescale_q(1, av_cntx_out->time_base, av_stream->time_base); //???
unique_lock<mutex> lk(m_Mutex);
av_interleaved_write_frame(ofmt_ctx, av_pkt_out);
lk.unlock();
}
else if (ret != 0)
{
char str2[] = "";
cout << "\nError :" << av_make_error_string(str2, sizeof(str2), ret);
return;
}
ret = avcodec_send_frame(av_cntx_out, av_frm_out);
} while (ret);
av_packet_unref(av_pkt_in);
av_packet_unref(av_pkt_out);
}
}
int ret = 0;
avcodec_send_frame(av_cntx_out, NULL);
do
{
av_packet_unref(av_pkt_out);
ret = avcodec_receive_packet(av_cntx_out, av_pkt_out);
if (!ret)
{
av_interleaved_write_frame(ofmt_ctx, av_pkt_out);
}
} while (!ret);
if (av_write_trailer(ofmt_ctx) < 0)
{
cout << "\nerror in writing av trailer";
exit(1);
}
}
void RecordVars::MicCaptureThread()
{
uint8_t** resampledData;
int ret;
m_Timer->StartTimer();
int iFrames = 100;
int i(0);
static int64_t pts = 0;
while (av_read_frame(ifmt_ctx, av_pkt_in) >= 0)
{
if (RecordVars::m_bLoopBreaker)
{
break;
}
av_packet_rescale_ts(av_pkt_out, ifmt_ctx->streams[stream_idx]->time_base, av_cntx_in->time_base);
if ((ret = avcodec_send_packet(av_cntx_in, av_pkt_in)) < 0)
{
cout << "Cannot decode current audio packet " << ret << endl;
continue;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(av_cntx_in, av_frm_in);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
cerr << "Error during decoding" << endl;
exit(1);
}
initConvertedSamples(&resampledData, av_cntx_out, av_frm_in->nb_samples);
swr_convert(resampleContext, resampledData, av_frm_in->nb_samples, (const uint8_t**)av_frm_in->extended_data,
av_frm_in->nb_samples);
add_samples_to_fifo(resampledData, av_frm_in->nb_samples);
av_pkt_out->data = nullptr;
av_pkt_out->size = 0;
const int frame_size = FFMAX(av_audio_fifo_size(fifo), av_cntx_out->frame_size);
av_frm_out = av_frame_alloc();
if (!av_frm_out)
{
cerr << "Cannot allocate an AVPacket for encoded video" << endl;
exit(1);
}
av_frm_out->nb_samples = av_cntx_out->frame_size;
av_frm_out->ch_layout = av_cntx_out->ch_layout;
av_frm_out->format = av_cntx_out->sample_fmt;
av_frm_out->sample_rate = av_cntx_out->sample_rate;
av_frame_get_buffer(av_frm_out, 0);
while (av_audio_fifo_size(fifo) >= av_cntx_out->frame_size) {
ret = av_audio_fifo_read(fifo, (void**)(av_frm_out->data), av_cntx_out->frame_size);
av_frm_out->pts = pts;
pts += av_frm_out->nb_samples;
if (avcodec_send_frame(av_cntx_out, av_frm_out) < 0) {
cout << "Cannot encode current audio packet " << endl;
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(av_cntx_out, av_pkt_out);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
cerr << "Error during encoding" << endl;
exit(1);
}
unique_lock<mutex> lk(m_Mutex);
av_packet_rescale_ts(av_pkt_out, av_cntx_out->time_base, ofmt_ctx->streams[av_stream->index]->time_base);
av_pkt_out->stream_index = av_stream->index;
if (av_interleaved_write_frame(ofmt_ctx, av_pkt_out) != 0)
{
cerr << "Error in writing audio frame" << endl;
}
lk.unlock();
av_packet_unref(av_pkt_out);
}
ret = 0;
}
av_frame_free(&av_frm_out);
av_packet_unref(av_pkt_out);
}
}
if (*m_MedSettings != AUDIO_FOR_VIDEO)
{
if (av_write_trailer(ofmt_ctx) < 0)
{
cout << "\nerror in writing av trailer";
exit(1);
}
}
return;
}
int RecordVars::add_samples_to_fifo(uint8_t** converted_input_samples, const int frame_size)
{
int error;
if ((error = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size)) < 0)
{
throw exception("Could not reallocate FIFOn");
}
if (av_audio_fifo_write(fifo, (void**)converted_input_samples, frame_size) < frame_size)
{
throw exception("Could not write data to FIFOn");
}
return 0;
}
int RecordVars::initConvertedSamples(uint8_t*** converted_input_samples, AVCodecContext* output_codec_context, int frame_size)
{
if (!(*converted_input_samples = (uint8_t**)calloc(output_codec_context->ch_layout.nb_channels,
sizeof(**converted_input_samples))))
{
throw exception("Could not allocate converted input sample pointersn");
}
if (av_samples_alloc(*converted_input_samples, nullptr,
output_codec_context->ch_layout.nb_channels,
frame_size,
output_codec_context->sample_fmt, 0) < 0)
{
throw exception("Cannot allocate memory for the samples of all channels in one consecutive block for convenience ");
}
return 0;
}
void RecordVars::SetMaxTimeofRec(int sec)
{
m_iMaxTime = sec;
}
void RecordVars::SetCrFValue(string crf)
{
m_crf = crf.c_str();
}
AVDictionary* OutCodecOptions()
{
AVDictionary* options(0);
av_dict_set(&options, "preset", "ultrafast", 0);
av_dict_set(&options, "rtbufsize", "1500M", 0);
av_dict_set(&options, "crf", "30", 0);
return options;
}
int main()
{
try
{
MediaSettings msV = VIDEO_DEFAULT;
MediaSettings msA = AUDIO_FOR_VIDEO;
RecordVars recVideo(&msV, "10");
RecordVars recAudio(&msA);
recVideo.SetCrFValue("30");
recVideo.OpenInputMedia("gdigrab", "desktop");
recVideo.FindMediaStreamIndex(AVMEDIA_TYPE_VIDEO);
recVideo.SetOutPutFilename(NULL, "out.mp4", NULL);
recVideo.SetOutputCodecContext(OutCodecOptions());
//recAudio.OpenInputMedia("dshow", "audio=Microphone (2- High Definition Audio Device)");
recAudio.OpenInputMedia("dshow", "audio=""@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\\wave_{C7D33711-2FD7-4800-8D99-72ED74527121}"""); //Using Alternative name for my audio
recAudio.FindMediaStreamIndex(AVMEDIA_TYPE_AUDIO);
recAudio.SetOutPutFilename(NULL, "out.mp4", NULL);
recAudio.SetOutputCodecContext(NULL);
recVideo.writeFormatHeader();
recVideo.SetVideoSwsContext();
recAudio.SetAudioSwrContext();
recVideo.SetMaxTimeofRec(5/*30*/);
recAudio.SetMaxTimeofRec(5/*30*/);
RecordVars::VideoAudioThreadDriver(&recVideo, &recAudio);
}
catch (exception& ex)
{
cout << "Error :" << ex.what();
}
}