Windows10,ffmpeg库版本:4.3
某设备实时采集视频和音频数据;
音频数据
频率:动态变化;
样本数:不固定;
格式:AV_SAMPLE_FMT_S16
视频数据
视频数据:RGB32
大小:1200*1200
音频数据格式
typedef struct _SAUDIO_DATA
{
int nSampleRate; // 采样频率
int nSamples; // 采样数
BYTE* pBufferData; // 音频数据内存
int nBufferSize; // 音频数据大小
}SAUDIODATA,*PSAUDIODATA;
视频数据格式
typedef struct _SVIDEO_DATA
{
BYTE* PBufferData; // 视频数据内存
int nBufferSize; // 视频数据大小
}SVIDEODATA,*PSVIDEODATA;
流格式
typedef struct OutPutStream {
AVStream* st;
AVCodecContext* enc;
/* pts of the next frame that will be generated */
int64_t next_pts;
int samples_count;
AVFrame* frame;
AVFrame* tmp_frame;
AVPacket* tmp_pkt;
struct SwsContext* sws_ctx;
struct SwrContext* swr_ctx;
}OutPutStream;
创建媒体文件
bool CMyEncoder::CreateMediaFile(LPCTSTR lpstrFilename)
{
int ret = -1;
m_bHaveVideo = false;
m_bEncodeVideo = false;
m_bHaveAudio = false;
m_bEncodeAudio = false;
avformat_alloc_output_context2(&m_pOutFmtCtx, nullptr, nullptr, CW2A(lpstrFilename));
if (!m_pOutFmtCtx)
{
avformat_alloc_output_context2(&m_pOutFmtCtx, NULL, "avi", CW2A(lpstrFilename));
}
if (!m_pOutFmtCtx)
return false;
m_pOutFmt = m_pOutFmtCtx->oformat;
if (m_pOutFmt->video_codec != AV_CODEC_ID_NONE)
{
// 加入到输出流包装
AddStream(&m_stVideo, m_pOutFmtCtx, &m_pVideoCodec, m_pOutFmt->video_codec);
m_bHaveVideo = true;
m_bEncodeVideo = true;
}
if (m_pOutFmt->audio_codec != AV_CODEC_ID_NONE)
{
// 加入到输出流包装
AddStream(&m_stAudio, m_pOutFmtCtx, &m_pAudioCodec, AV_CODEC_ID_AAC);
m_bHaveAudio = true;
m_bEncodeAudio = true;
}
// 打开编码器
if (m_bHaveVideo)
OpenVideo(m_pOutFmtCtx, m_pVideoCodec, &m_stVideo, nullptr);
if (m_bHaveAudio)
OpenAudio(m_pOutFmtCtx, m_pAudioCodec, &m_stAudio, nullptr);
// 打开媒体文件
if (!(m_pOutFmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&m_pOutFmtCtx->pb, CW2A(lpstrFilename), AVIO_FLAG_WRITE);
if (ret < 0)
{
return false;
}
}
// 写入文件头信息
ret = avformat_write_header(m_pOutFmtCtx, nullptr);
if (ret < 0)
return false;
return true;
}
处理图像帧
bool CMyEncoder::ProcImageFrame(PVOID pImageData, __int64 nSize, int nIndex)
{
bool bRet = false;
AVFrame* pSrcFrame = nullptr;
pSrcFrame = m_stVideo.tmp_frame;
// 同步时钟
if (av_compare_ts(m_stVideo.next_pts, m_stVideo.enc->time_base, m_stAudio.next_pts, m_stAudio.enc->time_base) <= 0)
{
// 填充图像帧原始数据到AVFrame
av_image_fill_arrays(pSrcFrame->data, pSrcFrame->linesize, (const uint8_t*)pImageData, AV_PIX_FMT_RGB32, m_stVideo.enc->width, m_stVideo.enc->height, 1);
// 将原始帧数据转化为编码帧数据
sws_scale(m_stVideo.sws_ctx, (const uint8_t * const*)pSrcFrame->data, pSrcFrame->linesize, 0, m_stVideo.enc->height, m_stVideo.frame->data, m_stVideo.frame->linesize);
// 图像PTS自增
if (m_stVideo.frame)
m_stVideo.frame->pts = m_stVideo.next_pts++;
// 写入帧
bRet = WriteFrame(m_pOutFmtCtx, m_stVideo.enc, m_stVideo.st, m_stVideo.frame, m_stVideo.tmp_pkt);
}
return bRet;
}
处理音频帧
bool CMyEncoder::ProcAudioFrame(int nSampleRate, int nSampleCount, PVOID pAudioData, __int64 nSize)
{
if (!pAudioData)
return false;
CString strInfo;
int ret = 0, dst_nb_samples = 0, max_dst_nb_samples = 0, nLineSize = 0, nBufferSize = 0;
AVCodecContext* audioCodecCtx = nullptr;
audioCodecCtx = m_stAudio.enc;
// 初始化编码器参数
swr_alloc_set_opts(m_stAudio.swr_ctx, audioCodecCtx->channel_layout, audioCodecCtx->sample_fmt, audioCodecCtx->sample_rate, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, nSampleRate, 0, nullptr);
if (swr_init(m_stAudio.swr_ctx) < 0)
{
TRACE(L"初始化音频编码上下文失败\n");
return false;
}
// 当前样本数
m_nCurSampeCount = nSampleCount * 2;
// 最大采样样本数
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(m_nCurSampeCount, audioCodecCtx->sample_rate, nSampleRate, AV_ROUND_UP);
// 获取目标样本数
dst_nb_samples = av_rescale_rnd(swr_get_delay(m_stAudio.swr_ctx, nSampleRate) + m_nCurSampeCount, audioCodecCtx->sample_rate, nSampleRate, AV_ROUND_UP);
if (dst_nb_samples > max_dst_nb_samples)
{
max_dst_nb_samples = dst_nb_samples;
}
// 生成buffer数据
uint8_t* pBufferData = nullptr;
nBufferSize = av_samples_get_buffer_size(&nLineSize, audioCodecCtx->channels, max_dst_nb_samples, audioCodecCtx->sample_fmt, 0);
pBufferData = (uint8_t*)malloc(nBufferSize);
memset(pBufferData, 0, nBufferSize);
// 原始样本转换为目标样本数据
ret = swr_convert(m_stAudio.swr_ctx,
&pBufferData, max_dst_nb_samples,
(const uint8_t * *)& pAudioData, m_nCurSampeCount);
// 重新分配队列大小
int cache_Size = av_audio_fifo_size(m_pAudioFifo);
av_audio_fifo_realloc(m_pAudioFifo, cache_Size + max_dst_nb_samples);
av_audio_fifo_write(m_pAudioFifo, (void**)& pBufferData, max_dst_nb_samples);
free(pBufferData);
ProcAudioSamples();
return true;
}
int CMyEncoder::ProcAudioSamples()
{
bool bRet = false;
AVCodecContext* audioCodecCtx = nullptr;
audioCodecCtx = m_stAudio.enc;
int codec_need_samples = 0;
codec_need_samples = audioCodecCtx->frame_size;
int nSamples = av_audio_fifo_size(m_pAudioFifo);
int nBufferSize = 0, nLineSize = 0;
while (nSamples >= codec_need_samples)
{
// 同步时钟
if (av_compare_ts(m_stAudio.next_pts, audioCodecCtx->time_base, m_stVideo.next_pts, m_stVideo.enc->time_base) <= 0)
{
nSamples = codec_need_samples;
av_audio_fifo_read(m_pAudioFifo, (void**)m_stAudio.frame->data, nSamples);
AVRational oRational = { 1, audioCodecCtx->sample_rate };
// 更新显示时间
m_stAudio.frame->pts = av_rescale_q(m_stAudio.samples_count, oRational, audioCodecCtx->time_base);
m_stAudio.samples_count += audioCodecCtx->frame_size;
m_stAudio.next_pts = m_stAudio.frame->pts;
// 编码一帧数据
bRet = WriteFrame(m_pOutFmtCtx, audioCodecCtx, m_stAudio.st, m_stAudio.frame, m_stAudio.tmp_pkt);
}
nSamples = av_audio_fifo_size(m_pAudioFifo);
}
return nSamples;
}
处理静音帧
bool CMyEncoder::ProcAudioSilenceFrame()
{
AVFrame* pSilenceFrame = nullptr;
AVCodecContext* audioCodecCtx = nullptr;
audioCodecCtx = m_stAudio.enc;
bool bRet = false;
// 同步时钟
if (av_compare_ts(m_stAudio.next_pts, audioCodecCtx->time_base, m_stVideo.next_pts, m_stVideo.enc->time_base) <= 0)
{
pSilenceFrame = AllocSilenceFrame(audioCodecCtx->channels, audioCodecCtx->sample_rate, AV_SAMPLE_FMT_FLT);
if (pSilenceFrame)
{
// 更新pts
AVRational oRational = { 1, audioCodecCtx->sample_rate };
pSilenceFrame->pts = av_rescale_q(m_stAudio.samples_count, oRational, audioCodecCtx->time_base);
m_stAudio.samples_count += audioCodecCtx->frame_size;
m_stAudio.next_pts = pSilenceFrame->pts;
bRet = WriteFrame(m_pOutFmtCtx, audioCodecCtx, m_stAudio.st, pSilenceFrame, m_stAudio.tmp_pkt);
av_frame_free(&pSilenceFrame);
}
}
return bRet;
}
参考:https://ffmpeg.org/doxygen/trunk/muxing_8c-example.html