ffmpeg 下载视频,ffmpeg 播放
前言通过前面的实战,我们实现音视频解封装提取、音视频解码、音视频编码、音频重采样等的功能,今天我们就结合之前所做的功能,
来做一个短视频应用中常见的功能:
1、提取多个mp3文件中的音频,重新编码为合并为自动幅度控制(自动振幅控制的缩写)
2、提取mp4中的视频,重新编码合并为h264
3、h264与自动幅度控制(自动振幅控制的缩写)合并成新的mp4文件
因为我们的目的是以实战为主,为了囊括之前所学的一些知识点,在这个实战中我们不仅仅需要实现音视频解封装提取、音视频解码、音视频编码、音频重采样这些功能,
我们还需要结合多线程同步等知识点做好生产者消费者队列缓冲控制。还包含例如类成员函数作为线程执行函数的使用等知识点。
大致框架这里要说明一个常识就是如果音频如果需要合并的话要保证两个音频的采样率、采样格式以及通道数一致,所以需要重采样,为了测试,笔者把音频都重采样为22050赫兹。
同时视频也一样,如果视频需要合并也需要保证两个视频的分辨率是一样的,这里笔者统一把尺寸转换为720x1280。
笔者文笔不好,经常一句卧槽走天下,直接看图吧。
代码实现本来笔者想追求简单,希望用一个卡片打印处理机(卡片打印处理器的缩写)文件实现的,后面写着写着发现代码量有点多,所以就拆分成了三个卡片打印处理机(卡片打印处理器的缩写)文件,下面是代码详情:
音频句柄。卡片打印处理机(Card Print Processor的缩写)
/**
* 音频处理
* 解码音频,并且重采样为22050,然后编码成自动幅度控制(自动振幅控制的缩写)
*/
#ifndef目标音频采样速率
//采样率
#定义目标音频采样速率22050
#endif
#包括输入输出流
#包含矢量
外部C {
#包含libavcodec/avcodec.h
#包含libavformat/avformat.h
#包含libavutil/opt.h
#包含libavutil/channel _ layout。h
#include libavutil/audio_fifo.h
# include libswresample/swresample。h
};
类别音频句柄{
公共:
void句柄_音频(STD:vector char * MP3 _路径
标准:函数void(const AVCodecContext *、AVPacket *、bool)回调){
//音频编码器相关
const AV CODEC * AV CODEC=AV CODEC _ find _ encoder(AV _ CODEC _ ID _ AAC);
audio _ encoder _ context=av编解码器_ alloc _ context 3(av编解码器);
AUDIO _ encoder _ context-SAMPLE _ RATE=TARGET _ AUDIO _ SAMPLE _ RATE;
//默认的自动幅度控制(自动振幅控制的缩写)编码器输入的脉冲编码调制格式为:FLTP FMT AV _ SAMPLE
audio _ encoder _ context-SAMPLE _ fmt=AV _ SAMPLE _ FMT _ FLTP;
audio _ encoder _ context-channel _ LAYOUT=AV _ CH _ LAYOUT _ STEREO;
//audio _ encoder _ context-bit _ rate=128 * 1024;
音频_编码器_上下文-编解码器_类型=av媒体_类型_音频;
audio _ encoder _ context-channels=av _ get _ channel _ layout _ nb _ channels(audio _ encoder _ context-channel _ layout);
audio _ encoder _ context-PROFILE=FF _ PROFILE _ AAC _ LOW;
//ffmpeg默认的自动幅度控制(自动振幅控制的缩写)是不带adts,而fdk_aac默认带adts,这里我们强制不带
audio _ encoder _ context-flags=AV _ CODEC _ FLAG _ GLOBAL _ HEADER;
int ret=avcodec _ open 2(audio _ encoder _ context,av codec,nullptr);
if (ret 0) {
std:cout 音频编码器打开失败STD:endl;
返回;
}
//初始化音频先入先出
音频FIFO=av _ audio _ FIFO _ alloc(audio _ encoder _ context-sample _ fmt,audio_encoder_context- channels
音频_编码器_上下文-帧_大小);
AVFormatContext * AVFormatContext=nullptr;
AVCodecContext * decoder _ context=nullptr;
av包* av包=av _ packet _ alloc();
av帧* av帧=av _ frame _ alloc();
STD:vector av packet * pack _ vector=STD:vector av packet *
而(!mp3_paths.empty()) {
//先释放旧的
av编解码器_自由_上下文(解码器_上下文);
avformat _ free _ context(avFormatContext);
const char * MP3=MP3 _ paths。在(0);
MP3 _路径。擦除(MP3 _ paths。c begin());
avFormatContext=av format _ alloc _ context();
ret=av format _ open _ input(avFormatContext,mp3,nullptr,nullptr);
if (ret 0) {
std:cout 音频文件打开失败STD:endl;
打破;
}
int AUDIO _ index=av _ find _ best _ stream(avFormatContext,AVMEDIA_TYPE_AUDIO,-1,-1,nullptr,0);
if (audio_index 0) {
for(int I=0;I avFormatContext-nb _ streams;i) {
if(av media _ TYPE _ AUDIO==avFormatContext-streams[I]-codec par-codec _ TYPE){
audio _ index=I;
std:cout 找到音频流,audio _ index: audio _ index STD:endl;
打破;
}
}
if (audio_index 0) {
std:cout 没有找到音频流STD:endl;
打破;
}
}
const av codec * av codec=av codec _ find _ decoder(avFormatContext-streams[audio _ index]-codec par-codec _ id);
decoder _ context=av编解码器_ alloc _ context 3(av编解码器);
avcodec _ parameters _ to _ context(decoder _ context,avFormatContext-streams[audio _ index]-codecpar);
ret=avcodec _ open 2(decoder _ context,av codec,nullptr);
if (ret 0) {
std:cout 音频解码器打开失败STD:endl;
打破;
}
while (true) {
ret=av _ read _ frame(avFormatContext,av packet);
if (ret 0) {
std:cout 音频包读取完毕STD:endl;
打破;
}
if (avPacket- stream_index!=音频索引){
av _ packet _ unref(av包);
继续;
}
ret=av codec _ send _ packet(解码器_上下文,av包);
if (ret 0) {
std:cout 音频包发送解码失败STD:endl;
打破;
}
while (true) {
ret=av编解码_接收_帧(解码器_上下文,av帧);
if (ret==AVERROR(EAGAIN)) {
std:cout 音频包获取解码帧:EAGAIN STD:endl;
打破;
} else if (ret 0) {
std:cout 音频包获取解码帧:fail STD:endl;
打破;
}否则{
std:cout 重新编码音频STD:endl;
//先进行重采样
重采样_音频(av帧);
包_向量。clear();
编码音频(包_矢量,出_帧);
而(!pack_vector.empty()) {
av包*包=包_向量。在(0);
包_向量。擦除(pack _ vector。c begin());
//回调
回调(音频_编码器_上下文,包,假);
}
}
}
av _ packet _ unref(av包);
}
}
av编解码器_自由_上下文(解码器_上下文);
avformat _ free _ context(avFormatContext);
//回调结束
回调(audio_encoder_context,nullptr,true);
}
私人:
//视频编码器
AVCodecContext * audio _ encoder _ context=nullptr;
AVFrame * encode _ frame=nullptr
AVAudioFifo * audiofifo=nullptr
int 64 _ t cur _ pts=0;
//重采样相关
SwrContext * swrContext=nullptr
AVFrame * out _ frame=nullptr
int64 _ t max _ dst _ nb _ samples
void init _ out _ frame(int 64 _ t dst _ nb _ samples){
av _ frame _ free(out _ frame);
out _ frame=av _ frame _ alloc();
out _ frame-SAMPLE _ RATE=TARGET _ AUDIO _ SAMPLE _ RATE;
out _ frame-format=AV _ SAMPLE _ FMT _ FLTP;
out _ frame-channel _ LAYOUT=AV _ CH _ LAYOUT _ STEREO;
out _ frame-nb _ samples=dst _ nb _ samples;
//分配缓冲器
av_frame_get_buffer(out_frame,0);
av _ frame _ make _ writable(out _ frame);
}
/**
* 重采样
* @param avFrame
*/
空的重采样_音频(AVFrame *avFrame){
if (nullptr==swrContext) {
/**
* 以下可以使用swr_alloc、av_opt_set_channel_layout、av_opt_set_int、av_opt_set_sample_fmt
* 等应用程序接口设置,更加灵活
*/
SWR context=SWR _ alloc _ set _ opts(null ptr,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_FLTP,目标_音频_采样_速率
avFrame- channel_layout,static _ cast AVSampleFormat(av frame-format),
avFrame- sample_rate,0,null ptr);
SWR _ init(SWR上下文);
}
//进行音频重采样
int src _ nb _ sample=av frame-nb _ samples;
//为了保持从采样后dst _ nb _ samples/dest _ sample=src _ nb _ sample/src _ sample _ rate
max _ dst _ nb _ samples=AV _ rescale _ rnd(src _ nb _ SAMPLE,TARGET_AUDIO_SAMPLE_RATE,avFrame- sample_rate,AV _ ROUND _ UP);
//从采样器中会缓存一部分,获取缓存的长度
int 64 _ t delay=SWR _获取_延迟(SWR上下文,av帧-采样率);
//相当于a*b/c
int 64 _ t dst _ nb _ samples=av _ rescale _ rnd(延时av帧-nb _ samples,TARGET_AUDIO_SAMPLE_RATE,avFrame- sample_rate,
AV _ ROUND _ UP);
if(nullptr==out_frame){
初始化输出帧(dst _ nb _ samples);
}
if(dst _ nb _ samples max _ dst _ nb _ samples){
//需要重新分配缓冲器
std:cout 需要重新分配buffer STD:endl;
初始化输出帧(dst _ nb _ samples);
max _ dst _ nb _ samples=dst _ nb _ samples;
}
//重采样
int ret=swr_convert(swrContext,out_frame- data,dst_nb_samples,
const _ cast const uint 8 _ t * *(av帧-数据),av帧-nb _ samples);
if(ret 0){
std:cout 重采样失败STD:endl;
}否则{
//每帧音频数据量的大小
int data _ size=av _ get _ bytes _ per _ sample(static _ cast AVSampleFormat(out _ frame-format));
//返回值才是真正的重采样点数
out _ frame-nb _ samples=ret;
std:cout 重采样成功: ret -dst _ nb _ samples: dst _ nb _ samples -data _ size: data _ size STD:endl;
}
}
void encode _ audio(STD:vector av packet * pack _ vector,AVFrame *avFrame) {
int cache _ size=av _ audio _ FIFO _ size(音频FIFO);
STD:cout cache _ size: cache _ size STD:endl;
av _ audio _ FIFO _ realloc(音频FIFO,cache _ size av frame-nb _ samples);
av_audio_fifo_write(audiofifo,reinterpret _ cast void strong(av frame-data),av frame-nb _ samples);
if (nullptr==encode_frame) {
encode _ frame=av _ frame _ alloc();
encode _ frame-nb _ samples=audio _ encoder _ context-frame _ size;
encode _ frame-sample _ rate=audio _ encoder _ context-sample _ rate;
encode _ frame-channel _ layout=audio _ encoder _ context-channel _ layout;
encode _ frame-channel=audio _ encoder _ context-channel;
encode _ frame-format=audio _ encoder _ context-sample _ fmt;
av _ frame _ get _ buffer(encode _ frame,0);
}
av _ frame _ make _ writable(encode _ frame);
//todo如果是冲刷最后几帧数据,不够的可以填充静音av_samples_set_silence
while(av _ audio _ FIFO _ size(音频FIFO)audio _ encoder _ context-frame _ size){
int ret=av _ audio _ FIFO _ read(音频FIFO,reinterpret _ cast void/strong(encode _ frame-data),
音频_编码器_上下文-帧_大小);
if (ret 0) {
std:cout audiofifo读取数据失败STD:endl;
返回;
}
//修改香港警察训练学校(警察训练学校的缩写)
cur _ pts=encode _ frame-nb _ samples;
encode _ frame-pts=cur _ pts;
ret=av编解码器_发送_帧(音频_编码器_上下文,编码_帧);
if (ret 0) {
std:cout 发送编码失败STD:endl;
返回;
}
while (true) {
av packet * out _ pack=av _ packet _ alloc();
ret=av编解码器_接收_包(音频_编码器_上下文,输出_包);
if(ret==AVERROR(EAGAIN) ret==AVERROR _ EOF){
STD:cout av codec _ receive _ packet end: ret STD:endl;
打破;
} else if (ret 0) {
STD:cout av codec _ receive _ packet fail: ret STD:endl;
返回;
}否则{
包_向量。push _ back(out _ pack);
}
}
}
}
};视频手柄。卡片打印处理机(Card Print Processor的缩写)
/**
* 视频处理
* 解码视频,然后转换成720x1080,然后编码成h264
*/
#ifndef TARGET_VIDEO_WIDTH
#定义目标视频宽度720
#定义目标_视频_高度1280
#endif
#包括输入输出流
#包含矢量
外部C {
#包含libavcodec/avcodec.h
#包含libavformat/avformat.h
#包含libavutil/opt.h
#包含libswscale/swscale.h
};
类别视频手柄{
公共:
void handle _ video(STD:vector char * MP4 _ paths,
标准:函数void(const AVCodecContext *、AVPacket *、bool)回调){
//视频编码器相关
const AV CODEC * video _ CODEC=AV CODEC _ find _ encoder(AV _ CODEC _ ID _ H264);
video _ encoder _ context=av编解码器_ alloc _ context 3(video _ codec);
视频_编码器_上下文-PIX _ fmt=AV _ PIX _ FMT _ YUV 420 p;
视频_编码器_上下文-宽度=目标_视频_宽度;
视频_编码器_上下文-高度=目标_视频_高度;
视频_编码器_上下文-比特率=2000 * 1024;
video _ encoder _ context-GOP _ size=10;
video _ encoder _ context-time _ base={ 1,25 };
video _ encoder _ context-framerate={ 25,1 };
//b帧的数量
video _ encoder _ context-max _ b _ frames=1;
//设置H264的编码器参数为延迟模式,提高编码质量,但是会造成编码速度下降
//av _ opt _ set(video _ encoder _ context-priv _ data, preset , slow ,0);
int ret=av codec _ open 2(video _ encoder _ context,video_codec,nullptr);
if (ret 0) {
std:cout 视频编码器打开失败STD:endl;
返回;
}
AVFormatContext * AVFormatContext=nullptr;
AVCodecContext * decoder _ context=nullptr;
av包* av包=av _ packet _ alloc();
av帧* av帧=av _ frame _ alloc();
STD:vector av packet * pack _ vector=STD:vector av packet *
//前面视频的香港警察训练学校(警察训练学校的缩写)累计
int 64 _ t previous _ pts=0;
//但前视频最后的香港警察训练学校(警察训练学校的缩写)
int 64 _ t last _ pts=0;
而(!mp4_paths.empty()) {
//先释放旧的
前一个点数=后一个pts
av编解码器_自由_上下文(解码器_上下文);
avformat _ free _ context(avFormatContext);
const char * MP4=MP4 _ paths。在(0);
MP4 _路径。擦除(MP4 _路径。c begin());
avFormatContext=av format _ alloc _ context();
ret=av format _ open _ input(avFormatContext,mp4,nullptr,nullptr);
if (ret 0) {
std:cout 视频文件打开失败STD:endl;
打破;
}
int VIDEO _ index=av _ find _ best _ stream(avFormatContext,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
if (video_index 0) {
std:cout 没有找到视频流STD:endl;
打破;
}
const av codec * av codec=av codec _ find _ decoder(avFormatContext-streams[video _ index]-codec par-codec _ id);
decoder _ context=av编解码器_ alloc _ context 3(av编解码器);
avcodec _ parameters _ to _ context(解码器_上下文,avFormatContext-流[视频_索引]-编解码器解析);
ret=avcodec _ open 2(decoder _ context,av codec,nullptr);
if (ret 0) {
std:cout 视频解码器打开失败STD:endl;
打破;
}
while (true) {
ret=av _ read _ frame(avFormatContext,av packet);
if (ret 0) {
std:cout 视频包读取完毕STD:endl;
打破;
}
if(avPacket- stream_index!=视频_索引){
av _ packet _ unref(av包);
继续;
}
ret=av codec _ send _ packet(解码器_上下文,av包);
if (ret 0) {
字符错误[1024];
av_strerror(ret,error,1024);
std:cout 视频包发送解码失败错误STD:endl;
打破;
}
while (true) {
ret=av编解码_接收_帧(解码器_上下文,av帧);
if (ret==AVERROR(EAGAIN)) {
std:cout 视频包获取解码帧:EAGAIN STD:endl;
打破;
} else if (ret 0) {
std:cout 视频包获取解码帧:fail STD:endl;
}否则{
std:cout 重新编码视频STD:endl;
包_向量。clear();
//转换成统一的香港警察训练学校(警察训练学校的缩写)
last _ pts=AV _ rescale _ Q(AV frame-pts,avFormatContext-streams[video _ index]-TIME _ BASE,AV _ TIME _ BASE _ Q);
av帧-pts=previous _ pts last _ pts;
//尺寸转换
scale _ YUV(av帧);
//重新编码
encode_video(pack_vector,out _ frame);
而(!pack_vector.empty()){
av包*包=包_向量。在(0);
//回调
回调(视频_编码器_上下文,包,假);
包_向量。擦除(pack _ vector。c begin());
}
}
}
av _ packet _ unref(av包);
}
}
av编解码器_自由_上下文(解码器_上下文);
avformat _ free _ context(avFormatContext);
//回调结束
回调(video_encoder_context,nullptr,true);
}
void scale_yuv(AVFrame *frame){
SWS context=SWS _ getCachedContext(SWS context,帧宽,帧高,AV_PIX_FMT_YUV420P,TARGET_VIDEO_WIDTH,TARGET_VIDEO_HEIGHT,AV_PIX_FMT_YUV420P,
SWS _双线性,
nullptr,nullptr,nullptr);
if(nullptr==out_frame){
out _ frame=av _ frame _ alloc();
out _ frame-format=AV _ PIX _ FMT _ YUV 420 p;
out_frame- width=目标_视频_宽度;
out _ frame-HEIGHT=TARGET _ VIDEO _ HEIGHT;
av_frame_get_buffer(out_frame,0);
}
//转换
int ret=sws_scale(swsContext,frame- data,frame- linesize,0,frame- height,out_frame- data,out _ frame-linesize);
//pts
STD:cout frame-pts: frame-pts STD:endl;
out_frame- pts=帧-
if(ret 0){
std:cout 图像缩放失败STD:endl;
返回;
}
}
void encode _ video(STD:vector av pack * pack _ vector,AVFrame *frame) {
int ret=av编解码器_发送_帧(视频_编码器_上下文,帧);
if (ret 0) {
std:cout 视频发送编码失败STD:endl;
返回;
}
while (true) {
av包*包=av _ packet _ alloc();
ret=avcodec_receive_packet(视频_编码器_上下文,包);
if(ret==AVERROR(EAGAIN)){
std:cout 视频编码:EAGAIN STD:endl;
打破;
} else if(ret 0){
std:cout 视频编码:fail STD:endl;
打破;
}否则{
pack_vector.push_back数据包);
}
}
}
//视频编码器
AVCodecContext * video _ encoder _ context=nullptr;
//视频转换专用
SwsContext * swsContext=nullptr
AVFrame * out _ frame=nullptr
};ComplexMuxerCore.cpp
/**
* 将视频和音频合并成mp4输出文件
*/
#ifndef MAX_QUEUE_SIZE
//队列缓存最大值
#定义最大队列大小6
#endif
#包括输入输出流
#包含矢量
#包含线程
#包含音频句柄. cpp
#包含视频手柄. cpp
外部C {
#包含libavformat/avformat.h
}
ComplexMuxerCore类{
公共:
complex muxercore():audio _ queue(新STD:vector av packet *)、video _ queue(新STD:vector av packet *){
}
/**
* 这个是主函数,也就是说在主要的中调用这个函数即可
* @param mp3_paths
* @param mp4_paths
* @param mp4_out
*/
void muxer _ media(const STD:vector char * MP3 _ paths,const std:vector char * mp4_paths,
const char *mp4_out) {
音频句柄=新音频句柄();
auto a _ call _ back=std:bind(复杂muxercore:audio _ callback,this,std:占位符:_1,
标准:占位符:_2,标准:占位符:_ 3);
audio _ thread=new STD:thread(AudioHandle:handle _ audio,audio handle,mp3_paths,a _ call _ back);
video handle=new video handle();
auto v _ call _ back=std:bind(complex muxer core:video _ callback,this,STD:占位符:_1,
标准:占位符:_2,标准:占位符:_ 3);
video _ thread=new STD:thread(VideoHandle:handle _ video,video handle,mp4_paths,v _ call _ back);
muxer _ thread=new STD:thread(复杂muxer core:muxer _ out,this,MP4 _ out);
muxer _ thread-join();
}
~ComplexMuxerCore() {
//todo释放资源
}
私人:
视频句柄*视频句柄=nullptr
音频句柄*音频句柄=nullptr
//音频处理线程
STD:thread * audio _ thread=nullptr;
//视频处理线程
STD:thread * video _ thread=nullptr;
//合并线程
STD:thread * mux er _ thread=nullptr;
//音频队列
STD:vector av packet * * audio _ queue=nullptr;
//视频队列
STD:vector av packet * * video _ queue=nullptr;
//音频线程同步互斥量
std:互斥体音频_互斥体
//视频线程同步互斥量
标准:互斥视频_互斥
//合并线程同步互斥量
STD:mutex muxer _ mutex;
//音频条件变量
标准:条件_变量音频_条件变量
//视频条件变量
标准:条件_变量视频_条件变量;
//合并条件变量
标准:条件_变量条件变量;
//输入音频是否处理完毕
易变布尔is _ audio _ end
//输入视频是否处理完毕
易变布尔is _ video _ end
//输出视频流的索引
int out _ video _ stream _ index=-1;
//输入视频流索引
int out _ audio _ stream _ index=-1;
//视频香港警察训练学校(警察训练学校的缩写)
double video _ pts=0;
//音频香港警察训练学校(警察训练学校的缩写)
double audio _ pts=0;
AVFormatContext * out _ format _ context=nullptr;
void muxer _ out(const char * MP4 _ out){
out _ format _ context=av format _ alloc _ context();
const AVOutputFormat * AVOutputFormat=av _ guess _ format(nullptr,mp4_out,nullptr);
out _ format _ context-of format=avoutput format;
while(out _ video _ stream _ index 0 out _ audio _ stream _ index 0){
std:cout 视频流或音频流还没创建好,陷入等待STD:endl;
STD:unique _ lock STD:mutex muxer _ lock(muxer _ mutex);
条件变量。wait(mux er _ lock);
}
int ret=AVIO _ open(out _ format _ context-Pb,mp4_out,AVIO _ FLAG _ WRITE);
if (ret 0) {
std:cout 输出流打开失败STD:endl;
返回;
}
ret=av format _ write _ header(out _ format _ context,null ptr);
if (ret 0) {
std:cout 文件头写入失败STD:endl;
返回;
}
而(!is_handle_end()) {
STD:cout muxer while STD:endl;
//视频包的香港警察训练学校(警察训练学校的缩写)大于音频包或者视频包写完了则写音频包
如果((video_pts audio_pts!audio_queue- empty())
(is _ video _ end video _ queue-empty()){
//写入音频包
write_audio()。
}否则{
//写入视频包
write _ video();
}
}
std:cout 开始写入文件头STD:endl;
ret=av _ write _ trailer(out _ format _ context);
if (ret 0) {
std:cout 文件尾写入失败STD:endl;
}否则{
std:cout 合并完成STD:endl;
}
}
void write_audio(){
而(音频_队列-空()!is_audio_end) {
std:cout 等待音频包生产STD:endl;
STD:unique _ lock STD:mutex唯一锁(audio _ mutex);
音频条件变量。等待(唯一锁);
}
如果(!音频队列空())
//锁住
std:lock_guard std:互斥锁卫士(音频_互斥);
av packet * pack=audio _ queue-at(0);
//pts转换
//av_packet_rescale_ts(pack,pack- time_base,out _ format _ context-streams[out _ audio _ stream _ index]-time _ base);
audio _ pts=pack-pts * av _ q2d(out _ format _ context-streams[out _ audio _ stream _ index]-time _ base);
std:cout 写入音频包audio _ pts: audio _ pts STD:endl;
av _ write _ frame(out _ format _ context,pack);
av _ packet _ free(pack);
audio _ queue-erase(音频_ queue-c begin());
}
//唤醒
音频条件变量。notify _ all();
//休眠一下,模拟消费比生产慢
STD:this _ thread:sleep _ for(STD:chrono:毫秒(10));
}
void write_video(){
while (video_queue- empty()!is_video_end) {
std:cout 等待视频包生产STD:endl;
STD:unique _ lock STD:mutex唯一锁(video _ mutex);
视频条件变量。等待(唯一锁);
}
//大括号括起来可以及时释放锁
如果(!视频_队列-空()){
//加锁
std:lock_guard std:互斥锁卫士(视频_互斥);
av packet * pack=video _ queue-at(0);
//之前在视频手柄中转换了统一的pts,现在要转换回去
av_packet_rescale_ts(pack,AV_TIME_BASE_Q,out _ format _ context-streams[out _ video _ stream _ index]-TIME _ BASE);
video _ pts=pack-pts * av _ q2d(out _ format _ context-streams[out _ video _ stream _ index]-time _ base);
std:cout 写入视频包 video _ pts: video _ pts STD:endl;
//pts转换
//av_packet_rescale_ts(pack,pack- time_base,out _ format _ context-streams[out _ video _ stream _ index]-time _ base);
av _ write _ frame(out _ format _ context,pack);
av _ packet _ free(pack);
video _ queue-erase(video _ queue-c begin());
}
//唤醒
视频条件变量。notify _ all();
//休眠一下,模拟消费比生产慢
STD:this _ thread:sleep _ for(STD:chrono:毫秒(10));
}
void audio _ callback(const AVCodecContext * codecContext,AVPacket *avPacket,bool is_end) {
if(null ptr==out _ format _ context){
//复用器还没初始化好
std:cout 复用器还没初始化STD:endl;
返回;
}
if (out_audio_stream_index 0) {
//加锁
STD:cout audio _ callback STD:endl;
STD:lock _ guard STD:mutex锁守卫(muxer _ mutex);
av stream * audio _ stream=av format _ new _ stream(out _ format _ context,编解码器上下文-编解码器);
av codec _ parameters _ from _ context(audio _ stream-codec par,编解码器上下文);
out _ audio _ stream _ index=audio _ stream-index;
//唤醒
条件变量。notify _ all();
}
//队列超了就阻塞在这里
while(audio _ QUEUE-SIZE()=MAX _ QUEUE _ SIZE){
std:cout 音频队列超出缓存,等待消费STD:endl;
STD:unique _ lock STD:mutex唯一锁(audio _ mutex);
音频条件变量。等待(唯一锁);
}
{
if (nullptr!=avPacket) {
STD:lock _ guard STD:mutex video _ lock(音频_互斥);
av包流索引=输出音频流索引;
audio _ queue-push _ back(av包);
}
}
是_音频_结束=是_结束
//唤醒消费队列
音频条件变量。notify _ all();
}
void video _ callback(const AVCodecContext * codecContext,AVPacket *avPacket,bool is_end) {
STD:cout video _ callback STD:endl;
//队列超了就阻塞在这里
if (nullptr==out_format_conte
xt) {
// 复用器还没初始化好
return;
}
if (out_video_stream_index 0) {
// 加锁
std::cout "video_callback" std::endl;
std::lock_guard std::mutex lockGuard(muxer_mutex);
AVStream *video_stream = avformat_new_stream(out_format_context, codecContext- codec);
avcodec_parameters_from_context(video_stream- codecpar, codecContext);
out_video_stream_index = video_stream- index;
std::cout "创建视频输出流:" out_video_stream_index std::endl;
// 唤醒
conditionVariable.notify_all();
}
std::cout "video_callback:" video_queue- size() std::endl;
while (video_queue- size() = MAX_QUEUE_SIZE) {
std::cout "视频队列超出缓存,等待消费" std::endl;
std::unique_lock std::mutex uniqueLock(video_mutex);
video_conditionVariable.wait(uniqueLock);
}
{
if (nullptr != avPacket) {
std::lock_guard std::mutex video_lock(video_mutex);
avPacket- stream_index = out_video_stream_index;
video_queue- push_back(avPacket);
}
}
is_video_end = is_end;
// 唤醒消费队列
video_conditionVariable.notify_all();
}
/**
* 是否处理完毕
* @return
*/
bool is_handle_end() {
return is_video_end
is_audio_end
(nullptr == audio_queue audio_queue- empty())
(nullptr == video_queue video_queue- empty());
}
};不得不说写技术博客是一个很耗时耗力的事情,本来为了更方便理解应该详细讲每个实现文件的细节问题的,由于时间问题和文笔表达不大好还是算了,有兴趣的私聊吧...
思考上面的代码在视频合并的过程中我们通过pts的比较交叉写入音频包或视频包。为什么需要这样做呢?假设在不考虑多线程性能的前提下,能先写完音频再写入视频,或者能先写完视频再写入音频吗?
欢迎大家沟通交流...
系列推荐FFmpeg连载1-开发环境搭建
FFmpeg连载2-分离视频和音频
FFmpeg连载3-视频解码
FFmpeg连载4-音频解码
FFmpeg连载5-音视频编码
FFmpeg连载6-音频重采样
FFmpeg连载7-mp3转码aac
关注我,一起进步,人生不止coding!!!
©
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。