这是从采集桌面音视频=>转码=>编码之后的最后一个步骤了,即产生一个视音同步的可播放的文件。那如何将两股同时采集编码的音频和视频合成一个视音同步的可播放的文件呢?本文中用到了ffmpeg的写文件技术。
技术简介
使用ffmpeg的相关技术
使用模块(库)
使用ffmpeg的avformat-58.dll
主要流程和代码
1、初始化视音频融合器(自己取的名称)
int FfmpegMuxer::init(const char* outputFilename, const FILE_MUX_SETTING& setting,
CAPTOR::VideoCaptor* videoCaptor, CAPTOR::AudioCaptor** audioCaptors, const int audioCaptorNumbers)
{
int err = ERROR_CODE_OK;
if ((outputFilename == nullptr || strlen(outputFilename) == 0)
|| (videoCaptor == nullptr && (audioCaptors == nullptr || audioCaptorNumbers <= 0))) {
err = ERROR_CODE_PARAMS_ERROR;
return err;
}
do {
std::string dstOutputFilename;
if (!HELPER::SystemPath::canonicalizePath(outputFilename, dstOutputFilename)) {
err = ERROR_CODE_CANONICALIZE_PATH_ERROR;
break;
}
m_outputFilename = dstOutputFilename;
err = initOutputContext();
HCMDR_ERROR_CODE_BREAK(err);
if (m_outputfmt->video_codec != AV_CODEC_ID_NONE && videoCaptor != nullptr) {
err = createVideoStreamLink(setting, videoCaptor);
HCMDR_ERROR_CODE_BREAK(err);
}
if (m_outputfmt->audio_codec != AV_CODEC_ID_NONE && audioCaptors != nullptr && audioCaptorNumbers > 0) {
err = createAudioStreamLink(setting, audioCaptors, audioCaptorNumbers);
HCMDR_ERROR_CODE_BREAK(err);
}
err = openOutputFile();
HCMDR_ERROR_CODE_BREAK(err);
av_dump_format(m_formatCtx, 0, nullptr, 1);
m_inited = true;
} while (0);
if (err != ERROR_CODE_OK) {
cleanup();
LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] init ffmpeg muxer failed!", __FUNCTION__);
}
return err;
}
初始化输出文件上下文
int FfmpegMuxer::initOutputContext()
{
int err = ERROR_CODE_OK;
do {
int ret = avformat_alloc_output_context2(&m_formatCtx, nullptr, nullptr, m_outputFilename.c_str());
if (ret < 0 || m_formatCtx == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_CONTEXT_FAILED;
break;
}
m_outputfmt = m_formatCtx->oformat;
} while (0);
return err;
}
创建视频流连接,从采集到转码再到编码,其中createVideoTranscoder/createVideoEncoder是多态实现,用来产生对应的子类类型的视频转码器/编码器;参数setting是视频相关配置信息,参数captor是桌面采集器
int FfmpegMuxer::createVideoStreamLink(const FILE_MUX_SETTING& setting, CAPTOR::VideoCaptor* captor)
{
int err = ERROR_CODE_OK;
m_videoStream = new FILE_MUX_STREAM();
SecureZeroMemory(m_videoStream, sizeof(FILE_MUX_STREAM));
m_videoStream->firstPacketPts = (uint64_t)-1;
m_videoStream->vCaptor = captor;
captor->registerCallback(std::bind(&FfmpegMuxer::onVideoCaptureData, this, std::placeholders::_1),
std::bind(&FfmpegMuxer::onVideoCaptureError, this, std::placeholders::_1));
do {
err = TRANSCODER::createVideoTranscoder(setting.vTranscodeType, &m_videoStream->vTranscoder);
HCMDR_ERROR_CODE_BREAK(err);
TRANSCODER::VideoTranscoder* transcoder = m_videoStream->vTranscoder;
CAPTOR::DesktopCaptureRect rect = captor->getRect();
err = transcoder->init(captor->getPixelFormat(), rect.right - rect.left, rect.bottom - rect.top,
setting.pixelFmt, setting.width, setting.height);
HCMDR_ERROR_CODE_BREAK(err);
err = ENCODER::createVideoEncoder(setting.vEncodeType, &m_videoStream->vEncoder);
HCMDR_ERROR_CODE_BREAK(err);
ENCODER::VideoEncoder* encoder = m_videoStream->vEncoder;
err = encoder->init(setting.width, setting.height, setting.framerate, setting.vBitrate, setting.qb, setting.gopTime);
HCMDR_ERROR_CODE_BREAK(err);
encoder->registerCallback(std::bind(&FfmpegMuxer::onVideoEncodeData, this, std::placeholders::_1),
std::bind(&FfmpegMuxer::onVideoEncodeError, this, std::placeholders::_1));
AVCodec* codec = avcodec_find_encoder(encoder->getCodecId());
if (codec == nullptr) {
err = ERROR_CODE_FFMPEG_FIND_ENCODER_FAILED;
break;
}
AVStream* stream = avformat_new_stream(m_formatCtx, codec);
if (stream == nullptr) {
err = ERROR_CODE_FFMPEG_NEW_STREAM_FAILED;
break;
}
AVCodecContext* codecCtx = avcodec_alloc_context3(codec);
if (codecCtx == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_CONTEXT_FAILED;