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 ...
随机推荐
- MySQL数据库——连接查询
1.基本含义 连接就是指两个或2个以上的表(数据源)“连接起来成为一个数据源”. 实际上,两个表的完全的连接是这样的一个过程: 左边的表的每一行,跟右边的表的每一行,两两互相“横向对接”后所得到的所有 ...
- C#LeetCode刷题之#720-词典中最长的单词(Longest Word in Dictionary)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4120 访问. 给出一个字符串数组words组成的一本英语词典.从 ...
- Jsp内置对象application之统计浏览网页的次数
<% Object obj = application.getAttribute("count"); if(obj !=null){ Integer sum = (Integ ...
- 《Java从入门到失业》第二章:Java环境(三):Java命令行工具
2.3Java命令行工具 2.3.1编译运行 到了这里,是不是开始膨胀了,想写一段代码来秀一下?好吧,满足你!国际惯例,我们写一段HelloWorld.我们在某个目录下记事本,编写一段代码如下: 保存 ...
- put数据到topic
基于python3.6 # -*-coding:utf-8 *- __author__ = 'lc_yy' from pykafka import KafkaClient import logging ...
- PAT 2-06. 数列求和(20)
题目意思:给定某数字A(1<=A<=9)以及非负整数N(0<=N<=100000),求数列之和S = A + AA + AAA + … + AA…A(N个A) 最开始一想还以为 ...
- Go 中的内联优化
文讨论 Go 编译器是如何实现内联的以及这种优化方法如何影响你的 Go 代码. 请注意:本文重点讨论 gc,实际上是 golang.org 的 Go 编译器.讨论到的概念可以广泛用于其他 Go 编译器 ...
- idea工程在maven projects中显示灰色的解决办法
原文链接:https://blog.csdn.net/qq_30507287/article/details/83515461 在Mac上使用idea进行开发的过程中,一般在MavenProject中 ...
- 经典的 Fork 炸弹解析
原文出处: saymagic Jaromil 在 2002 年设计了最为精简的一个Linux Fork炸弹,整个代码只有13个字符,在 shell 中运行后几秒后系统就会宕机: ::(){:|:&am ...
- python基础 Day8
python Day8 文件操作的识 利用python代码写一个脚本操作文件的过程 文件的路径:path 打开方式:读,写,追加,读写,写读 编码方式:utf-8,gbk,gb2312 简单文件读取( ...