Skip to content

Commit a2d86c8

Browse files
committed
day 28
1 parent 74749ea commit a2d86c8

File tree

11 files changed

+451
-15
lines changed

11 files changed

+451
-15
lines changed

02_代码/29_video_player/29_video_player.pro

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,13 @@ FORMS += \
3535
qnx: target.path = /tmp/$${TARGET}/bin
3636
else: unix:!android: target.path = /opt/$${TARGET}/bin
3737
!isEmpty(target.path): INSTALLS += target
38+
39+
FFMPEG_HOME = F:/Dev/msys64/usr/local/ffmpeg
40+
41+
INCLUDEPATH += $${FFMPEG_HOME}/include
42+
43+
LIBS += -L $${FFMPEG_HOME}/lib \
44+
-lavcodec \
45+
-lavformat \
46+
-lavutil \
47+
-lSDL2

02_代码/29_video_player/mainwindow.cpp

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#include "ui_mainwindow.h"
33
#include <QFileDialog>
44
#include <QDebug>
5+
#include <QMessageBox>
56

7+
#pragma mark - 构造、析构
68
MainWindow::MainWindow(QWidget *parent)
79
: QMainWindow(parent)
810
, ui(new Ui::MainWindow) {
@@ -12,10 +14,30 @@ MainWindow::MainWindow(QWidget *parent)
1214
_player = new VideoPlayer();
1315
connect(_player, &VideoPlayer::stateChanged,
1416
this, &MainWindow::onPlayerStateChanged);
17+
connect(_player, &VideoPlayer::initFinished,
18+
this, &MainWindow::onPlayerInitFinished);
19+
connect(_player, &VideoPlayer::playFailed,
20+
this, &MainWindow::onPlayerPlayFailed);
1521
}
1622

1723
MainWindow::~MainWindow() {
1824
delete ui;
25+
delete _player;
26+
}
27+
28+
#pragma mark - 私有方法
29+
void MainWindow::onPlayerPlayFailed(VideoPlayer *player) {
30+
QMessageBox::critical(nullptr, "提示", "哦豁,播放失败!");
31+
}
32+
33+
void MainWindow::onPlayerInitFinished(VideoPlayer *player) {
34+
int64_t duration = player->getDuration();
35+
36+
// 设置slider的范围
37+
ui->currentSlider->setRange(0, duration);
38+
39+
// 设置label的文字
40+
ui->durationLabel->setText(getTimeText(duration));
1941
}
2042

2143
void MainWindow::onPlayerStateChanged(VideoPlayer *player) {
@@ -33,15 +55,20 @@ void MainWindow::onPlayerStateChanged(VideoPlayer *player) {
3355
ui->volumnSlider->setEnabled(false);
3456
ui->silenceBtn->setEnabled(false);
3557

36-
ui->durationLabel->setText("00:00:00");
58+
ui->durationLabel->setText(getTimeText(0));
3759
ui->currentSlider->setValue(0);
38-
ui->volumnSlider->setValue(ui->volumnSlider->maximum());
60+
61+
// 显示打开文件的页面
62+
ui->playWidget->setCurrentWidget(ui->openFilePage);
3963
} else {
4064
ui->playBtn->setEnabled(true);
4165
ui->stopBtn->setEnabled(true);
4266
ui->currentSlider->setEnabled(true);
4367
ui->volumnSlider->setEnabled(true);
4468
ui->silenceBtn->setEnabled(true);
69+
70+
// 显示播放视频的页面
71+
ui->playWidget->setCurrentWidget(ui->videoPage);
4572
}
4673
}
4774

@@ -52,12 +79,13 @@ void MainWindow::on_stopBtn_clicked() {
5279
void MainWindow::on_openFileBtn_clicked() {
5380
QString filename = QFileDialog::getOpenFileName(nullptr,
5481
"选择多媒体文件",
55-
"/",
82+
"F:/res",
5683
"视频文件 (*.mp4 *.avi *.mkv);;"
5784
"音频文件 (*.mp3 *.aac)");
5885
qDebug() << "打开文件" << filename;
5986
if (filename.isEmpty()) return;
6087

88+
// 开始播放打开的文件
6189
_player->setFilename(filename.toUtf8().data());
6290
_player->play();
6391

@@ -72,7 +100,7 @@ void MainWindow::on_openFileBtn_clicked() {
72100
}
73101

74102
void MainWindow::on_currentSlider_valueChanged(int value) {
75-
103+
ui->currentLabel->setText(getTimeText(value));
76104
}
77105

78106
void MainWindow::on_volumnSlider_valueChanged(int value) {
@@ -87,3 +115,23 @@ void MainWindow::on_playBtn_clicked() {
87115
_player->play();
88116
}
89117
}
118+
119+
QString MainWindow::getTimeText(int value) {
120+
// int h = seconds / 3600;
121+
// int m = (seconds % 3600) / 60;
122+
// int m = (seconds / 60) % 60;
123+
// int s = seconds % 60;
124+
125+
126+
// QString h = QString("0%1").arg(seconds / 3600).right(2);
127+
// QString m = QString("0%1").arg((seconds / 60) % 60).right(2);
128+
// QString s = QString("0%1").arg(seconds % 60).right(2);
129+
// return QString("%1:%2:%3").arg(h).arg(m).arg(s);
130+
131+
int64_t seconds = value / 1000000;
132+
QLatin1Char fill = QLatin1Char('0');
133+
return QString("%1:%2:%3")
134+
.arg(seconds / 3600, 2, 10, fill)
135+
.arg((seconds / 60) % 60, 2, 10, fill)
136+
.arg(seconds % 60, 2, 10, fill);
137+
}

02_代码/29_video_player/mainwindow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class MainWindow : public QMainWindow {
1919

2020
private slots:
2121
void onPlayerStateChanged(VideoPlayer *player);
22+
void onPlayerInitFinished(VideoPlayer *player);
23+
void onPlayerPlayFailed(VideoPlayer *player);
2224

2325
void on_stopBtn_clicked();
2426

@@ -33,5 +35,7 @@ private slots:
3335
private:
3436
Ui::MainWindow *ui;
3537
VideoPlayer *_player;
38+
39+
QString getTimeText(int value);
3640
};
3741
#endif // MAINWINDOW_H

02_代码/29_video_player/mainwindow.ui

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<property name="currentIndex">
2727
<number>0</number>
2828
</property>
29-
<widget class="QWidget" name="page">
29+
<widget class="QWidget" name="openFilePage">
3030
<layout class="QHBoxLayout" name="horizontalLayout_5">
3131
<item>
3232
<widget class="QPushButton" name="openFileBtn">
@@ -43,7 +43,7 @@
4343
</item>
4444
</layout>
4545
</widget>
46-
<widget class="QWidget" name="page_2">
46+
<widget class="QWidget" name="videoPage">
4747
<layout class="QGridLayout" name="gridLayout">
4848
<item row="0" column="0">
4949
<widget class="VideoWidget" name="videoWidget" native="true"/>

02_代码/29_video_player/videoplayer.cpp

Lines changed: 136 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
11
#include "videoplayer.h"
2+
#include <thread>
3+
#include <QDebug>
4+
5+
#define ERROR_BUF \
6+
char errbuf[1024]; \
7+
av_strerror(ret, errbuf, sizeof (errbuf));
8+
9+
#define END(func) \
10+
if (ret < 0) { \
11+
ERROR_BUF; \
12+
qDebug() << #func << "error" << errbuf; \
13+
setState(Stopped); \
14+
emit playFailed(this); \
15+
goto end; \
16+
}
17+
18+
#define RET(func) \
19+
if (ret < 0) { \
20+
ERROR_BUF; \
21+
qDebug() << #func << "error" << errbuf; \
22+
return ret; \
23+
}
224

325
#pragma mark - 构造、析构
426
VideoPlayer::VideoPlayer(QObject *parent) : QObject(parent) {
@@ -14,15 +36,10 @@ void VideoPlayer::play() {
1436
if (_state == Playing) return;
1537
// 状态可能是:暂停、停止、正常完毕
1638

17-
// 解封装、解码、播放,音视频同步
18-
19-
// 多线程的知识
20-
21-
// 创建子线程去解码
22-
23-
// 解码后的格式不一定是我们播放器想要的?
24-
// PCM格式不是SDL支持的 S16 44100
25-
// YUV -> RGB
39+
// 开始线程:读取文件
40+
std::thread([this]() {
41+
readFile();
42+
}).detach();
2643

2744
setState(Playing);
2845
}
@@ -53,7 +70,117 @@ void VideoPlayer::setFilename(const char *filename) {
5370
_filename = filename;
5471
}
5572

73+
int64_t VideoPlayer::getDuration() {
74+
return _fmtCtx ? _fmtCtx->duration : 0;
75+
}
76+
5677
#pragma mark - 私有方法
78+
void VideoPlayer::readFile() {
79+
// 返回结果
80+
int ret = 0;
81+
82+
// 创建解封装上下文、打开文件
83+
ret = avformat_open_input(&_fmtCtx, _filename, nullptr, nullptr);
84+
END(avformat_open_input);
85+
86+
// 检索流信息
87+
ret = avformat_find_stream_info(_fmtCtx, nullptr);
88+
END(avformat_find_stream_info);
89+
90+
// 打印流信息到控制台
91+
av_dump_format(_fmtCtx, 0, _filename, 0);
92+
fflush(stderr);
93+
94+
// 初始化音频信息
95+
if (initAudioInfo() < 0) {
96+
goto end;
97+
}
98+
99+
// 初始化视频信息
100+
if (initVideoInfo() < 0) {
101+
goto end;
102+
}
103+
104+
// 到此为止,初始化完毕
105+
emit initFinished(this);
106+
107+
// 从输入文件中读取数据
108+
AVPacket pkt;
109+
while (av_read_frame(_fmtCtx, &pkt) == 0) {
110+
if (pkt.stream_index == _aStream->index) { // 读取到的是音频数据
111+
112+
} else if (pkt.stream_index == _vStream->index) { // 读取到的是视频数据
113+
114+
}
115+
}
116+
117+
end:
118+
avcodec_free_context(&_aDecodeCtx);
119+
avcodec_free_context(&_vDecodeCtx);
120+
avformat_close_input(&_fmtCtx);
121+
}
122+
123+
// 初始化音频信息
124+
int VideoPlayer::initAudioInfo() {
125+
// 初始化解码器
126+
int ret = initDecoder(&_aDecodeCtx, &_aStream, AVMEDIA_TYPE_AUDIO);
127+
RET(initDecoder);
128+
129+
return 0;
130+
}
131+
132+
// 初始化视频信息
133+
int VideoPlayer::initVideoInfo() {
134+
// 初始化解码器
135+
int ret = initDecoder(&_vDecodeCtx, &_vStream, AVMEDIA_TYPE_VIDEO);
136+
RET(initDecoder);
137+
138+
139+
return 0;
140+
}
141+
142+
// 初始化解码器
143+
int VideoPlayer::initDecoder(AVCodecContext **decodeCtx,
144+
AVStream **stream,
145+
AVMediaType type) {
146+
// 根据type寻找最合适的流信息
147+
// 返回值是流索引
148+
int ret = av_find_best_stream(_fmtCtx, type, -1, -1, nullptr, 0);
149+
RET(av_find_best_stream);
150+
151+
// 检验流
152+
int streamIdx = ret;
153+
*stream = _fmtCtx->streams[streamIdx];
154+
if (!stream) {
155+
qDebug() << "stream is empty";
156+
return -1;
157+
}
158+
159+
// 为当前流找到合适的解码器
160+
AVCodec *decoder = avcodec_find_decoder((*stream)->codecpar->codec_id);
161+
if (!decoder) {
162+
qDebug() << "decoder not found" << (*stream)->codecpar->codec_id;
163+
return -1;
164+
}
165+
166+
// 初始化解码上下文
167+
*decodeCtx = avcodec_alloc_context3(decoder);
168+
if (!decodeCtx) {
169+
qDebug() << "avcodec_alloc_context3 error";
170+
return -1;
171+
}
172+
173+
// 从流中拷贝参数到解码上下文中
174+
ret = avcodec_parameters_to_context(*decodeCtx, (*stream)->codecpar);
175+
RET(avcodec_parameters_to_context);
176+
177+
// 打开解码器
178+
ret = avcodec_open2(*decodeCtx, decoder, nullptr);
179+
RET(avcodec_open2);
180+
181+
return 0;
182+
}
183+
57184
void VideoPlayer::setState(State state) {
58185
if (state == _state) return;
59186

02_代码/29_video_player/videoplayer.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33

44
#include <QObject>
55

6+
extern "C" {
7+
#include <libavcodec/avcodec.h>
8+
#include <libavformat/avformat.h>
9+
#include <libavutil/avutil.h>
10+
}
11+
612
/**
713
* 预处理视频数据(不负责显示、渲染视频)
814
*/
@@ -31,18 +37,42 @@ class VideoPlayer : public QObject {
3137
State getState();
3238
/** 设置文件名 */
3339
void setFilename(const char *filename);
40+
/** 获取总时长(单位是微秒,1秒=10^3毫秒=10^6微秒) */
41+
int64_t getDuration();
3442

3543
signals:
3644
void stateChanged(VideoPlayer *player);
45+
void initFinished(VideoPlayer *player);
46+
void playFailed(VideoPlayer *player);
3747

3848
private:
3949
/** 当前的状态 */
4050
State _state = Stopped;
4151
/** 文件名 */
4252
const char *_filename;
4353

54+
// 解封装上下文
55+
AVFormatContext *_fmtCtx = nullptr;
56+
// 解码上下文
57+
AVCodecContext *_aDecodeCtx = nullptr, *_vDecodeCtx = nullptr;
58+
//
59+
AVStream *_aStream = nullptr, *_vStream = nullptr;
60+
// 存放解码后的数据
61+
AVFrame *_aFrame = nullptr, *_vFrame = nullptr;
62+
63+
/** 初始化视频信息 */
64+
int initVideoInfo();
65+
/** 初始化音频信息 */
66+
int initAudioInfo();
67+
/** 初始化解码器和解码上下文 */
68+
int initDecoder(AVCodecContext **decodeCtx,
69+
AVStream **stream,
70+
AVMediaType type);
71+
4472
/** 改变状态 */
4573
void setState(State state);
74+
/** 读取文件数据 */
75+
void readFile();
4676
};
4777

4878
#endif // VIDEOPLAYER_H

0 commit comments

Comments
 (0)