PonyPlayer
playback.hpp
浏览该文件的文档.
1//
2// Created by ColorsWind on 2022/5/5.
3//
4#pragma once
5#ifndef PONYPLAYER_VIDEOWORKER_H
6#define PONYPLAYER_VIDEOWORKER_H
7
8#include <QObject>
9#include <QThread>
10#include <QDebug>
11#include <QCoreApplication>
12#include <utility>
13#include "demuxer.hpp"
14#include "audiosink.hpp"
15#include "frame.hpp"
16
22class Playback : public QObject {
23Q_OBJECT
24private:
25 QThread *m_affinityThread;
26 Demuxer *m_demuxer;
27 VideoFrameRef cacheVideoFrame;
28
29
30 PonyAudioSink *m_audioSink = nullptr;
31 std::atomic<bool> m_isInterrupt;
32 std::atomic<bool> m_isPlaying;
33 std::mutex m_interruptMutex;
34 std::mutex m_workMutex;
35 std::condition_variable m_interruptCond;
36
37 // playback 的速度, 不受倍速限制
38 qreal m_speedFactor = 1.0;
39
40 std::atomic<qreal> m_preferablePos = 0.0;
41
42 inline void changeState(bool isPlaying) {
43 m_isPlaying = isPlaying;
45 }
46
47 inline void syncTo(qreal current) {
48 bool backward = m_demuxer->isBackward();
49 double duration;
50 if (!m_demuxer->hasVideo()) {
51 // 纯音频
52 duration = 1. / 30;
53 m_preferablePos = m_audioSink->getProcessSecs(backward);
54 } else {
55 qreal pos = m_demuxer->frontPicture();
56 if (isnan(pos)) { return; }
57 m_preferablePos = current;
58 if (m_audioSink->isBlock()) {
59 // 由于没有音频
60 duration = (current - pos) / m_audioSink->speed();
61 } else {
62 if (m_audioSink->speed() > 2 - 1e-5) {
63 if (!backward) {
64 m_demuxer->skipPicture([this, backward](qreal framePos) {
65 return framePos < m_audioSink->getProcessSecs(backward);
66 });
67 } else {
68 m_demuxer->skipPicture([this, backward](qreal framePos) {
69 return framePos > m_audioSink->getProcessSecs(backward);
70 });
71 }
72 }
73 duration = m_demuxer->frontPicture() - m_audioSink->getProcessSecs(backward);
74 }
75 if (backward) { duration = -duration; }
76 }
77 if (duration > 0) {
78 if (duration > 1) {
79 qWarning() << "Sleep long duration" << duration << "s";
80 }
81 std::unique_lock lock(m_interruptMutex);
82 if (!m_isInterrupt) {
83 m_interruptCond.wait_for(lock, std::chrono::duration<double>(duration));
84 }
85 } else {
86 qWarning() << "Sleep negative duration" << duration << "s";
87 }
88 }
89
90 inline bool writeAudio(int batch) {
91 if (m_audioSink->isBlock()) { return true; }
92 for (int i = 0; i < batch && m_audioSink->freeByte() > MAX_AUDIO_FRAME_SIZE; ++i) {
93 AudioFrame sample = m_demuxer->getSample();
94 if (!sample.isValid()) { return false; }
95 m_audioSink->write(reinterpret_cast<const char *>(sample.getSampleData()), sample.getDataLen());
96 }
97 return true;
98 }
99
101
102 VideoFrameRef getVideoFrame() {
103 if (cacheVideoFrame.isValid()) {
104 VideoFrameRef ret = std::move(cacheVideoFrame);
105 cacheVideoFrame = {};
106 return ret;
107 } else {
108 return m_demuxer->getPicture();
109 }
110 }
111
112public:
113 Playback(Demuxer *demuxer, QObject *parent) : QObject(nullptr), m_demuxer(demuxer) {
114 m_affinityThread = new QThread;
115 m_affinityThread->setObjectName(PonyPlayer::PLAYBACK);
116 this->moveToThread(m_affinityThread);
117 connect(this, &Playback::startWork, this, &Playback::onWork);
118 connect(this, &Playback::stopWork, this, [this] { this->m_audioSink->stop(); });
119 connect(this, &Playback::setAudioStartPoint, this, [this](qreal t) { this->m_audioSink->setStartPoint(t); });
120 connect(this, &Playback::setAudioVolume, this, [this](qreal volume) { this->m_audioSink->setVolume(volume); });
121 connect(this, &Playback::setAudioPitch, this, [this](qreal pitch) { this->m_audioSink->setPitch(pitch); });
122 connect(this, &Playback::setAudioSpeed, this, [this](qreal speed) {
123 m_speedFactor = speed;
124 this->m_audioSink->setSpeed(speed);
126 if (this->m_audioSink->isBlock()) { return; }
127 // 需要禁用音频
128 this->m_audioSink->setBlockState(true);
129 emit requestResynchronization(false, false); // queue connection
130 } else if (speed <= PonyAudioSink::MAX_SPEED_FACTOR) {
131 if (!this->m_audioSink->isBlock()) { return; }
132 // 需要重新启动音频
133 this->m_audioSink->setBlockState(false);
134 emit requestResynchronization(true, false); // queue connection
135 }
136 });
137 connect(this, &Playback::showFirstVideoFrame, this, [this] {
138 if (!cacheVideoFrame.isValid()) { cacheVideoFrame = m_demuxer->getPicture(); }
139 emit setPicture(cacheVideoFrame);
140 });
141 connect(this, &Playback::showFirstVideoFrame, this, [this] {
142 cacheVideoFrame = {};
143 });
144 connect(this, &Playback::clearRingBuffer, this, [this] { this->m_audioSink->clear(); });
145 connect(m_affinityThread, &QThread::started, [this] {
146 // 在 Playback 线程上初始化
147 this->m_audioSink = new PonyAudioSink(PonyPlayer::DEFAULT_AUDIO_FORMAT);
148 connect(m_audioSink, &PonyAudioSink::signalDeviceSwitched, this, [this] {
150 emit requestResynchronization(!this->m_audioSink->isBlock(), true);
151 }, Qt::QueuedConnection);
152 connect(this, &Playback::signalSetSelectedAudioOutputDevice, m_audioSink,
154 connect(m_audioSink, &PonyAudioSink::signalAudioOutputDeviceListChanged, this, [this] {
156 });
158 });
159 m_affinityThread->start();
160 }
161
162 PONY_THREAD_SAFE qreal getPreferablePos() {
163 return m_preferablePos;
164 }
165
167 std::unique_lock lock(m_workMutex);
168 return m_audioSink->getCurrentDeviceFormat();
169 }
170
171 [[nodiscard]] qreal getAudioPos(bool backward) const {
172 if (m_speedFactor < PonyAudioSink::MAX_SPEED_FACTOR) {
173 return m_audioSink->getProcessSecs(backward);
174 } else {
175 ILLEGAL_STATE("AudioPos not available.");
176 }
177 }
178
179
180 void setDesiredFormat(const PonyAudioFormat& format) {
181 m_audioSink->setFormat(format);
182 }
183
184// qreal getVideoPos() const {
185// return m_preferablePos.load();
186// }
187
188 virtual ~Playback() {
189 m_affinityThread->quit();
190 }
191
192 void setVolume(qreal volume) {
193 emit setAudioVolume(volume, QPrivateSignal());
194 }
195
196
197 void setPitch(qreal pitch) {
198 emit setAudioPitch(pitch, QPrivateSignal());
199 }
200
201 void setSpeed(qreal speed) {
202 emit setAudioSpeed(speed, QPrivateSignal());
203 }
204
205 void setSelectedAudioOutputDevice(QString deviceName) {
206 emit signalSetSelectedAudioOutputDevice(std::move(deviceName));
207 }
208
210 return m_audioSink ? m_audioSink->getSelectedOutputDevice() : "";
211 }
212
213 void showFrame() {
214 emit showFirstVideoFrame(QPrivateSignal());
215 }
216
218 emit clearCacheVideoFrame(QPrivateSignal());
219 }
220
225 bool isPlaying() { return m_isPlaying; }
226
231 bool isInterrupted() { return m_isInterrupt; }
232
233 void setStartPoint(qreal startPoint) {
234 m_isInterrupt = false;
235// m_preferablePos = startPoint;
236 qDebug() << "SetStartPoint" << startPoint;
237 emit setAudioStartPoint(startPoint, QPrivateSignal());
238 }
239
243 void start() {
244// std::unique_lock lock(m_workMutex);
245 m_isInterrupt = false;
246 emit startWork(QPrivateSignal());
247 }
248
252 void clear() {
253 emit clearRingBuffer(QPrivateSignal());
254 }
255
259 void pause() {
260 std::unique_lock cond_lock(m_interruptMutex);
261 m_isInterrupt = true;
262 m_interruptCond.notify_all();
263 cond_lock.unlock();
264 std::unique_lock lock(m_workMutex);
265 }
266
270 void stop() {
271 std::unique_lock cond_lock(m_interruptMutex);
272 m_isInterrupt = true;
273 m_interruptCond.notify_all();
274 cond_lock.unlock();
275 std::unique_lock lock(m_workMutex); // make sure stop
276 emit stopWork(QPrivateSignal());
277 emit setAudioStartPoint(0.0, QPrivateSignal());
278 emit clearRingBuffer(QPrivateSignal());
279 }
280
281 QStringList getAudioDeviceList() { return m_audioSink ? m_audioSink->getAudioDeviceList() : QStringList(); };
282
283 qreal getPitch() { return m_audioSink ? m_audioSink->pitch() : 1.0; }
284
285
286private slots:
287
291 void onWork() {
292 std::unique_lock lock(m_workMutex, std::defer_lock);
293 if (!lock.try_lock()) { return; } // not allow neat run
294 changeState(true);
295 writeAudio(5);
296 m_audioSink->start();
297 while (!m_isInterrupt) {
298 VideoFrameRef pic = getVideoFrame();
299 if (!pic.isValid()) {
300 m_audioSink->waitComplete();
301 emit resourcesEnd();
302 break;
303 }
304// m_videoPos = pic.getPTS();
305 emit setPicture(pic);
306 if (!writeAudio(static_cast<int>(10 * m_audioSink->speed()))) {
307 m_audioSink->waitComplete();
308 emit resourcesEnd();
309 break;
310 }
311 QCoreApplication::processEvents(); // process setVolume setSpeed etc
312 syncTo(pic.getPTS());
313 }
314 m_audioSink->pause();
315 changeState(false);
316 lock.unlock();
317 };
318
319
320
321signals:
322
323 void startWork(QPrivateSignal);
324
325 void stopWork(QPrivateSignal);
326
327 void clearRingBuffer(QPrivateSignal);
328
329 void setAudioStartPoint(qreal startPoint, QPrivateSignal);
330
331 void setAudioVolume(qreal volume, QPrivateSignal);
332
333 void setAudioPitch(qreal pitch, QPrivateSignal);
334
335 void setAudioSpeed(qreal speed, QPrivateSignal);
336
338
340
341 void showFirstVideoFrame(QPrivateSignal);
342
343 void clearCacheVideoFrame(QPrivateSignal);
344
346
348
350
352
356 void requestResynchronization(bool enableAudio, bool updateAudioFormat);
357
358
359};
360
361#endif //PONYPLAYER_VIDEOWORKER_H
Definition: frame.hpp:145
int getDataLen() const
Definition: frame.hpp:163
std::byte * getSampleData() const
Definition: frame.hpp:159
bool isValid()
Definition: frame.hpp:155
Definition: demuxer.hpp:14
PONY_THREAD_SAFE int skipPicture(const std::function< bool(qreal)> &predicate)
Definition: demuxer.hpp:53
qreal frontPicture()
Definition: demuxer.hpp:48
AudioFrame getSample()
Definition: demuxer.hpp:60
PONY_THREAD_SAFE bool isBackward()
Definition: demuxer.hpp:108
VideoFrameRef getPicture()
Definition: demuxer.hpp:41
PONY_THREAD_SAFE bool hasVideo()
Definition: demuxer.hpp:114
负责输出视频和音频(不含视频预览).
Definition: playback.hpp:22
void pause()
Definition: playback.hpp:259
bool isPlaying()
Definition: playback.hpp:225
void start()
Definition: playback.hpp:243
void setSpeed(qreal speed)
Definition: playback.hpp:201
void signalDeviceSwitched()
void setAudioVolume(qreal volume, QPrivateSignal)
qreal getPitch()
Definition: playback.hpp:283
QString getSelectedAudioOutputDevice()
Definition: playback.hpp:209
void stop()
Definition: playback.hpp:270
void stateChanged(bool isPlaying)
void showFirstVideoFrame(QPrivateSignal)
PONY_THREAD_SAFE qreal getPreferablePos()
Definition: playback.hpp:162
void stopWork(QPrivateSignal)
void clearCacheVideoFrame(QPrivateSignal)
qreal getAudioPos(bool backward) const
Definition: playback.hpp:171
void clearCacheFrame()
Definition: playback.hpp:217
Playback(Demuxer *demuxer, QObject *parent)
Definition: playback.hpp:113
void startWork(QPrivateSignal)
bool isInterrupted()
Definition: playback.hpp:231
QStringList getAudioDeviceList()
Definition: playback.hpp:281
void setAudioPitch(qreal pitch, QPrivateSignal)
void clear()
Definition: playback.hpp:252
void setPitch(qreal pitch)
Definition: playback.hpp:197
void resourcesEnd()
void signalSetSelectedAudioOutputDevice(QString)
void setDesiredFormat(const PonyAudioFormat &format)
Definition: playback.hpp:180
void setSelectedAudioOutputDevice(QString deviceName)
Definition: playback.hpp:205
void setAudioStartPoint(qreal startPoint, QPrivateSignal)
virtual ~Playback()
Definition: playback.hpp:188
void requestResynchronization(bool enableAudio, bool updateAudioFormat)
void setPicture(VideoFrameRef pic)
PonyAudioFormat getDeviceFormat()
Definition: playback.hpp:166
void setVolume(qreal volume)
Definition: playback.hpp:192
void clearRingBuffer(QPrivateSignal)
void signalAudioOutputDevicesListChanged()
void showFrame()
Definition: playback.hpp:213
void setStartPoint(qreal startPoint)
Definition: playback.hpp:233
void setAudioSpeed(qreal speed, QPrivateSignal)
Definition: audioformat.hpp:97
播放音频裸流, 用于代替QAudioSink.
Definition: audiosink.hpp:38
void pause()
Definition: audiosink.hpp:303
void setVolume(qreal newVolume)
Definition: audiosink.hpp:452
void setSpeed(qreal newSpeed)
Definition: audiosink.hpp:470
qreal pitch() const
Definition: audiosink.hpp:487
int64_t freeByte() const
Definition: audiosink.hpp:357
PonyAudioFormat getCurrentDeviceFormat()
Definition: audiosink.hpp:584
void stop()
Definition: audiosink.hpp:326
QStringList getAudioDeviceList()
Definition: audiosink.hpp:533
void requestDeviceSwitch(const QString &device)
Definition: audiosink.hpp:567
void start()
Definition: audiosink.hpp:281
void signalDeviceSwitched()
qreal speed() const
Definition: audiosink.hpp:483
void setPitch(qreal newPitch)
Definition: audiosink.hpp:461
void waitComplete()
Definition: audiosink.hpp:317
qreal getProcessSecs(bool backward) const
Definition: audiosink.hpp:421
size_t clear()
Definition: audiosink.hpp:407
bool isBlock()
Definition: audiosink.hpp:341
constexpr static const qreal MAX_SPEED_FACTOR
Definition: audiosink.hpp:230
void setStartPoint(double t=0.0)
Definition: audiosink.hpp:435
void setFormat(const PonyAudioFormat &format)
Definition: audiosink.hpp:537
void signalAudioOutputDeviceListChanged()
QString getSelectedOutputDevice()
Definition: audiosink.hpp:506
bool write(const char *buf, qint32 origLen)
Definition: audiosink.hpp:369
Definition: frame.hpp:47
bool isValid() const
Definition: frame.hpp:97
double getPTS() const
Definition: frame.hpp:102
const int MAX_AUDIO_FRAME_SIZE
Definition: helper.hpp:15
const PonyAudioFormat DEFAULT_AUDIO_FORMAT
Definition: audioformat.hpp:164
constexpr PonyThread PLAYBACK
Definition: ponyplayer.h:45
#define ILLEGAL_STATE(msg)
Definition: ponyplayer.h:40
#define PONY_GUARD_BY(...)
Definition: ponyplayer.h:89