11.QT-ffmpeg+QAudioOutput实现音频播放器
- 2.AVFormatContext和AVInputFormat
- 3.AVPacket使用
- 4.FFMPEG-AVFrame
- 5.AVStream和AVCodecParameters
- 6.AVCodecContext和AVCodec
- 7.SwrContext音频重采样使用
- 8.ffmpeg-基础常用知识
- 9.下载ffmpeg、使QT支持同时编译32位和64位
- 10.QT-QAudioOutput类使用



#include "playthread.h" playthread::playthread()
{
audio=NULL;
type = control_none;
} bool playthread::initAudio(int SampleRate)
{
QAudioFormat format; if(audio!=NULL)
return true; format.setSampleRate(SampleRate); //设置采样率
format.setChannelCount(); //设置通道数
format.setSampleSize(); //样本数据16位
format.setCodec("audio/pcm"); //播出格式为pcm格式
format.setByteOrder(QAudioFormat::LittleEndian); //默认小端模式
format.setSampleType(QAudioFormat::UnSignedInt); //无符号整形数 QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); //选择默认输出设备 // foreach(int count,info.supportedChannelCounts())
// {
// qDebug()<<"输出设备支持的通道数:"<<count;
// } // foreach(int count,info.supportedSampleRates())
// {
// qDebug()<<"输出设备支持的采样率:"<<count;
// } // foreach(int count,info.supportedSampleSizes())
// {
// qDebug()<<"输出设备支持的样本数据位数:"<<count;
// } if (!info.isFormatSupported(format))
{
qDebug()<<"输出设备不支持该格式,不能播放音频";
return false;
} audio = new QAudioOutput(format, this); audio->setBufferSize(); return true;
} void playthread::play(QString filePath)
{
this->filePath = filePath;
type = control_play; if(!this->isRunning())
{
this->start();
}
} void playthread::stop()
{ if(this->isRunning())
{
type = control_stop;
} }
void playthread::pause()
{ if(this->isRunning())
{
type = control_pause;
} } void playthread::resume()
{
if(this->isRunning())
{
type = control_resume;
}
} void playthread::seek(int value)
{ if(this->isRunning())
{
seekMs = value;
type = control_seek;
}
} void playthread::debugErr(QString prefix, int err) //根据错误编号获取错误信息并打印
{
char errbuf[]={}; av_strerror(err,errbuf,sizeof(errbuf)); qDebug()<<prefix<<":"<<errbuf; emit ERROR(prefix+":"+errbuf);
} bool playthread::runIsBreak() //处理控制,判断是否需要停止
{ bool ret = false;
//处理播放暂停
if(type == control_pause)
{
while(type == control_pause)
{
audio->suspend();
msleep();
} if(type == control_resume)
{
audio->resume();
}
} if(type == control_play) //重新播放
{
ret = true;
if(audio->state()== QAudio::ActiveState)
audio->stop();
} if(type == control_stop) //停止
{
ret = true;
if(audio->state()== QAudio::ActiveState)
audio->stop();
}
return ret;
} void playthread::runPlay()
{
int ret; int destMs,currentMs; if(audio==NULL)
{
emit ERROR("输出设备不支持该格式,不能播放音频");
return ;
}
//初始化网络库 (可以打开rtsp rtmp http 协议的流媒体视频)
avformat_network_init();
AVFormatContext *pFmtCtx=NULL;
ret = avformat_open_input(&pFmtCtx, this->filePath.toLocal8Bit().data(),NULL, NULL) ; //打开音视频文件并创建AVFormatContext结构体以及初始化.
if (ret!= )
{
debugErr("avformat_open_input",ret);
return ;
}
ret = avformat_find_stream_info(pFmtCtx, NULL); //初始化流信息
if (ret!= )
{
debugErr("avformat_find_stream_info",ret);
return ;
} int audioindex=-; audioindex = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_AUDIO, -, -, NULL, ); qDebug()<<"audioindex:"<<audioindex; AVCodec *acodec = avcodec_find_decoder(pFmtCtx->streams[audioindex]->codecpar->codec_id);//获取codec AVCodecContext *acodecCtx = avcodec_alloc_context3(acodec); //构造AVCodecContext ,并将vcodec填入AVCodecContext中
avcodec_parameters_to_context(acodecCtx, pFmtCtx->streams[audioindex]->codecpar); //初始化AVCodecContext ret = avcodec_open2(acodecCtx, NULL,NULL); //打开解码器,由于之前调用avcodec_alloc_context3(vcodec)初始化了vc,那么codec(第2个参数)可以填NULL
if (ret!= )
{
debugErr("avcodec_open2",ret);
return ;
}
SwrContext *swrctx =NULL;
swrctx=swr_alloc_set_opts(swrctx, av_get_default_channel_layout(),AV_SAMPLE_FMT_S16,,
acodecCtx->channel_layout, acodecCtx->sample_fmt,acodecCtx->sample_rate, NULL,NULL);
swr_init(swrctx); destMs = av_q2d(pFmtCtx->streams[audioindex]->time_base)**pFmtCtx->streams[audioindex]->duration;
qDebug()<<"码率:"<<acodecCtx->bit_rate;
qDebug()<<"格式:"<<acodecCtx->sample_fmt;
qDebug()<<"通道:"<<acodecCtx->channels;
qDebug()<<"采样率:"<<acodecCtx->sample_rate;
qDebug()<<"时长:"<<destMs;
qDebug()<<"解码器:"<<acodec->name; AVPacket * packet =av_packet_alloc();
AVFrame *frame =av_frame_alloc(); audio->stop();
QIODevice*io = audio->start(); while()
{ if(runIsBreak())
break; if(type == control_seek)
{
av_seek_frame(pFmtCtx, audioindex, seekMs/(double)/av_q2d(pFmtCtx->streams[audioindex]->time_base),AVSEEK_FLAG_BACKWARD);
type = control_none;
emit seekOk();
} ret = av_read_frame(pFmtCtx, packet);
if (ret!= )
{
debugErr("av_read_frame",ret);
emit duration(destMs,destMs);
break ;
} //解码一帧数据
ret = avcodec_send_packet(acodecCtx, packet);
av_packet_unref(packet); if (ret != )
{
debugErr("avcodec_send_packet",ret);
continue ;
} if(packet->stream_index==audioindex)
{
while( avcodec_receive_frame(acodecCtx, frame) == )
{ if(runIsBreak())
break;
uint8_t *data[] = { };
int byteCnt=frame->nb_samples * * ; unsigned char *pcm = new uint8_t[byteCnt]; //frame->nb_samples*2*2表示分配样本数据量*两通道*每通道2字节大小 data[] = pcm; //输出格式为AV_SAMPLE_FMT_S16(packet类型),所以转换后的LR两通道都存在data[0]中 ret = swr_convert(swrctx,
data, frame->nb_samples, //输出
(const uint8_t**)frame->data,frame->nb_samples ); //输入 //将重采样后的data数据发送到输出设备,进行播放
while (audio->bytesFree() < byteCnt)
{
if(runIsBreak())
break;
msleep();
} if(!runIsBreak())
io->write((const char *)pcm,byteCnt); currentMs = av_q2d(pFmtCtx->streams[audioindex]->time_base)**frame->pts;
//qDebug()<<"时长:"<<destMs<<currentMs;
emit duration(currentMs,destMs); delete[] pcm;
}
} } //释放内存
av_frame_free(&frame);
av_packet_free(&packet);
swr_free(&swrctx);
avcodec_free_context(&acodecCtx);
avformat_close_input(&pFmtCtx); } void playthread::run()
{ if(!initAudio())
{
emit ERROR("输出设备不支持该格式,不能播放音频");
} while()
{ switch(type)
{
case control_none: msleep(); break;
case control_play : type=control_none;runPlay(); break; //播放
default: type=control_none; break;
}
} }
4.2 widget界面类
而在界面中要处理的就很简单,widget.cpp如下所示:
#include "widget.h"
#include "ui_widget.h"
#include <QDebug> Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this); this->setAcceptDrops(true); thread = new playthread(); connect(thread,SIGNAL(duration(int,int)),this,SLOT(onDuration(int,int))); connect(thread,SIGNAL(seekOk()),this,SLOT(onSeekOk())); void duration(long currentMs,long destMs); //播放时长 thread->start(); sliderSeeking =false;
} Widget::~Widget()
{
delete ui; thread->stop();
} void Widget::onSeekOk()
{
sliderSeeking=false;
} void Widget::onDuration(int currentMs,int destMs) //时长
{
static int currentMs1=-,destMs1=-; if(currentMs1==currentMs&&destMs1==destMs)
{
return;
} currentMs1 = currentMs;
destMs1 = destMs; qDebug()<<"onDuration:"<<currentMs<<destMs<<sliderSeeking; QString currentTime = QString("%1:%2:%3").arg(currentMs1/%,,,QChar('')).arg(currentMs1/%,,,QChar('')).arg(currentMs1/%,,,QChar('')); QString destTime = QString("%1:%2:%3").arg(destMs1/%,,,QChar('')).arg(destMs1/%,,,QChar('')).arg(destMs1/%,,,QChar('')); ui->label_duration->setText(currentTime+"/"+destTime); if(!sliderSeeking) //未滑动
{
ui->slider->setMaximum(destMs);
ui->slider->setValue(currentMs);
} } void Widget::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasUrls()) //判断拖的类型
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
} void Widget::dropEvent(QDropEvent *event)
{
if(event->mimeData()->hasUrls()) //判断放的类型
{ QList<QUrl> List = event->mimeData()->urls(); if(List.length()!=)
{
ui->line_audioPath->setText(List[].toLocalFile());
} }
else
{
event->ignore();
}
} void Widget::on_btn_start_clicked()
{ sliderSeeking=false; thread->play(ui->line_audioPath->text()); } void Widget::on_btn_stop_clicked()
{
thread->stop();
} void Widget::on_btn_pause_clicked()
{
thread->pause();
} void Widget::on_btn_resume_clicked()
{
thread->resume();
} void Widget::on_slider_sliderPressed()
{
sliderSeeking=true;
} void Widget::on_slider_sliderReleased()
{ thread->seek(ui->slider->value()); }
11.QT-ffmpeg+QAudioOutput实现音频播放器的更多相关文章
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (採用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- ffmpeg+SDL2实现的音频播放器V2.0(无杂音)
1. 前言 目前为止,学习了并记录了ffmpeg+SDL2显示视频以及事件(event)的内容. 这篇中记录ffmpeg+SDL2播放音频,没加入事件处理. 接下来加入事件处理并继续学习音视频同步,再 ...
- IOS开发之简单音频播放器
今天第一次接触IOS开发的UI部分,之前学OC的时候一直在模拟的使用Target-Action回调模式,今天算是真正的用了一次.为了熟悉一下基本控件的使用方法,和UI部分的回调,下面开发了一个特别简易 ...
- 与众不同 windows phone (14) - Media(媒体)之音频播放器, 视频播放器, 与 Windows Phone 的音乐和视频中心集成
原文:与众不同 windows phone (14) - Media(媒体)之音频播放器, 视频播放器, 与 Windows Phone 的音乐和视频中心集成 [索引页][源码下载] 与众不同 win ...
- Unity3D音频播放器 动态装载组件
大多数在线Unity有关如何只教程Unity在播放音乐.之后如何通过拖动它们无法继续添加音频文件 但有时在游戏中的对象要玩几个声音.这时候我们就需要使用代码控制,拖动推教程AudioClip颂值的方法 ...
- H.264:FFMpeg 实现简单的播放器
H.264:FFMpeg 实现简单的播放器 FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我 ...
- HTML5 音频播放器-Javascript代码(短小精悍)
直接上干货咯! //HTML5 音频播放器 lzpong 2015/01/19 var wavPlayer = function () { if(window.parent.wavPlayer) re ...
- 【jquery】一款不错的音频播放器——Amazing Audio Player
前段时间分享了一款视频播放器,点击这里.今天介绍一款不错的音频播放器——Amazing Audio Player. 介绍: Amazing Audio Player 是一个使用很方便的 Windows ...
随机推荐
- Python中 *args 和 **kwargs 的含义?
答:在python中,*args和**kwargs通常使用在函数定义里.*args 和 **kwargs 都允许你给函数传不定数量的参数,即使在定义函数的时候不知道调用者会传递几个参数.ps: *ar ...
- java this关键字调用构造方法
一 this调用构造方法 构造方法之间的调用,可以通过this关键字来完成. 格式: this(参数列表); 构造方法的调用举例: class Person { // Person的成员属性 priv ...
- TCP/IP速记
目录 网络协议 OSI七层模型和TCP/IP五层模型 TCP/IP五层模型 TCP的三次握手和四次挥手 三次握手进行连接 四次挥手断开连接 TCP连接的特点 TCP是如何保证安全可靠的 UDP连接的特 ...
- DRF内置认证组件之自定义认证系统
自定义token认证 我们知道,在django项目中不管路由以及对应的视图类是如何写的,都会走到 dispatch 方法,进行路由分发, 在阅读 APIView类中的dispatch 方法的源码中,有 ...
- Kerberos认证原理及基于Kerberos认证的NFS文件共享
目录 Kerberos认证原理 简介 client访问server过程 一.Authentication Service Exchange (AS Exchange) 二.Ticket Grantin ...
- 已废弃_CSDN慕零的黑夜-头条-第一期(必问)[导读:]1.CSDN必问赏金流向何方 2.CSDN必问偷偷做的手脚 3.CSDN必问靠谱吗 4.关于钱于回答的平衡问题:一美元拍卖骗局qq3461896724
[本文有已知的链接差错,懒得改了] 本期是关于CSDN 必问 (biwen.csdn.net)的内容,欢迎评论文末,文中插入有 小姐姐 img(附py代码,1.49G) + coding资料 哟~~~ ...
- XSS的构造技巧
XSS的构造技巧 By:Mirror王宇阳 E-mail:2821319009@qq.com 本文为长期持续维护,一旦遇到新的技术技巧将及时更新本文内容 非常的欢迎技术大佬纠正并补充,也欢迎技术同僚讨 ...
- markdown使用手册
github上找到了一个挺全面的项目,mark一下. https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/ ...
- 【故障公告】阿里云 RDS 数据库突发 CPU 近 100% 引发全站故障
今天晚上9点我们收到阿里云的告警通知: [阿里云监控]华东1(杭州)-云数据库RDS版<cnblogsdb> [instanceId=xxx] 于21:00 发生告警, 前往诊断 CPU使 ...
- 冒泡排序(Bubble Sorting)
基本介绍 时间复杂度O(n^2) 冒泡排序(Bubble Sorting)的基本思想是:通过对待 排序序列从前向后(从下标较小的元素开始),依次比较 相邻元素的值,若发现逆序则交换,使值较大 的元素逐 ...