索引地址:系列教程索引地址
上一篇:FFmpeg4入门19:pcm编码为mp3
音视频结合
将FFmpeg4入门8:软解并使用QtWidget播放视频(YUV420P->RGB32)和FFmpeg4入门17:软件解码音频并使用QAudioOutput播放结合起来,就是最简单的视频播放器了。
先将第8篇教程的源码复制过来,并改名为audio_video_sync
。
然后将第17篇中的源码合进来,不同的是第17篇是直接打开文件,而在此需要读取音频流。
将音频处理的部分拆分为一个类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| FFmpegAudio::FFmpegAudio() { fmtCtx = avformat_alloc_context(); pkt = av_packet_alloc(); audioFrame = av_frame_alloc();
QAudioFormat audioFmt; audioFmt.setSampleRate(44100); audioFmt.setChannelCount(2); audioFmt.setSampleSize(16); audioFmt.setCodec("audio/pcm"); audioFmt.setByteOrder(QAudioFormat::LittleEndian); audioFmt.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice(); if(!info.isFormatSupported(audioFmt)){ audioFmt = info.nearestFormat(audioFmt); } audioOutput = new QAudioOutput(audioFmt); audioOutput->setVolume(100);
streamOut = audioOutput->start(); }
FFmpegAudio::~FFmpegAudio() { if(streamOut->isOpen()){ audioOutput->stop(); streamOut->close(); } if(!pkt) av_packet_free(&pkt); if(!audioCodecCtx) avcodec_free_context(&audioCodecCtx); if(!audioCodecCtx) avcodec_close(audioCodecCtx); if(!fmtCtx) avformat_close_input(&fmtCtx); }
void FFmpegAudio::setUrl(QString url) { _url = url; }
int FFmpegAudio::open_input_file() { if(_url.isEmpty()) return -1;
if(avformat_open_input(&fmtCtx,_url.toLocal8Bit().data(),NULL,NULL)<0){ printf("Cannot open input file.\n"); return -1; }
if(avformat_find_stream_info(fmtCtx,NULL)<0){ printf("Cannot find any stream in file.\n"); return -1; }
int streamCnt=fmtCtx->nb_streams; for(int i=0;i<streamCnt;i++){ if(fmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){ audioStreamIndex=(int)i; continue; } }
if(audioStreamIndex==-1){ printf("Cannot find any stream in file.\n"); return -1; }
AVCodecParameters *audioCodecPara = fmtCtx->streams[audioStreamIndex]->codecpar;
if(!(audioCodec = avcodec_find_decoder(audioCodecPara->codec_id))){ printf("Cannot find valid audio decode codec.\n"); return -1; }
if(!(audioCodecCtx = avcodec_alloc_context3(audioCodec))){ printf("Cannot find valid audio decode codec context.\n"); return -1; }
if(avcodec_parameters_to_context(audioCodecCtx,audioCodecPara)<0){ printf("Cannot initialize audio parameters.\n"); return -1; }
audioCodecCtx->pkt_timebase = fmtCtx->streams[audioStreamIndex]->time_base;
if(avcodec_open2(audioCodecCtx,audioCodec,NULL)<0){ printf("Cannot open audio codec.\n"); return -1; }
uint64_t out_channel_layout = audioCodecCtx->channel_layout; out_sample_rate = audioCodecCtx->sample_rate; out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
audio_out_buffer = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
swr_ctx = swr_alloc_set_opts(NULL, out_channel_layout, out_sample_fmt, out_sample_rate, audioCodecCtx->channel_layout, audioCodecCtx->sample_fmt, audioCodecCtx->sample_rate, 0,NULL); swr_init(swr_ctx);
return true; }
void FFmpegAudio::run() { if(!open_input_file()){ qDebug()<<"Please open file first."; return; }
qDebug()<<"Open file "<<_url<<"done.";
double sleep_time=0;
while(av_read_frame(fmtCtx,pkt)>=0){ qDebug()<<"read audio frame"; if(pkt->stream_index==audioStreamIndex){ qDebug()<<"audio"; if(avcodec_send_packet(audioCodecCtx,pkt)>=0){ while(avcodec_receive_frame(audioCodecCtx,audioFrame)>=0){ qDebug()<<"receive"; if(av_sample_fmt_is_planar(audioCodecCtx->sample_fmt)){ int len = swr_convert(swr_ctx, &audio_out_buffer, MAX_AUDIO_FRAME_SIZE*2, (const uint8_t**)audioFrame->data, audioFrame->nb_samples); if(len<=0){ continue; }
int out_size = av_samples_get_buffer_size(0, out_channels, len, out_sample_fmt, 1);
sleep_time=(out_sample_rate*16*2/8)/out_size;
if(audioOutput->bytesFree()<out_size){ msleep(sleep_time); streamOut->write((char*)audio_out_buffer,out_size); }else { streamOut->write((char*)audio_out_buffer,out_size); } } } } av_packet_unref(pkt); } }
qDebug()<<"All video play done"; }
|
然后在播放视频的时候启动这个线程
| fa = new FFmpegAudio; connect(fa,&FFmpegAudio::finished,fa,&FFmpegAudio::deleteLater); fa->start();
|
效果为:

最最简单的播放器(没有进度条、没有快进快退、只有最简单的视频播放功能),至于音视频同步的更高级用法需要自己去探索,毕竟只是入门。
主要是本系列持续的时间太长了,看久了有点腻,而且我还有别的工作要做,等到我有空再深入研究吧。
完整代码在ffmpeg_Beginner中的20.audio_video_sync
中。
下一篇:FFmpeg4入门21:视频添加滤镜