mirror of
https://github.com/isledecomp/SIEdit.git
synced 2025-02-17 00:40:42 -05:00
app: allow playing multiple files at once
This commit is contained in:
parent
8858b6ddf6
commit
a05c99d495
3 changed files with 235 additions and 116 deletions
|
@ -268,16 +268,7 @@ void MainWindow::SelectionChanged(const QModelIndex &index)
|
|||
Object *c = dynamic_cast<Object*>(static_cast<Core*>(index.internalPointer()));
|
||||
|
||||
if (c) {
|
||||
switch (c->filetype()) {
|
||||
case MxOb::STL:
|
||||
case MxOb::WAV:
|
||||
case MxOb::SMK:
|
||||
case MxOb::FLC:
|
||||
p = panel_media_;
|
||||
break;
|
||||
case MxOb::OBJ:
|
||||
break;
|
||||
}
|
||||
p = panel_media_;
|
||||
}
|
||||
|
||||
if (p != config_stack_->currentWidget() || c != last_set_data_) {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#include "mediapanel.h"
|
||||
|
||||
#include <object.h>
|
||||
#include <othertypes.h>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QGroupBox>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollArea>
|
||||
|
||||
MediaPanel::MediaPanel(QWidget *parent) :
|
||||
Panel(parent),
|
||||
m_AudioOutput(nullptr),
|
||||
m_SliderPressed(false),
|
||||
m_vflip(false)
|
||||
m_SliderPressed(false)
|
||||
{
|
||||
int row = 0;
|
||||
|
||||
|
@ -20,14 +20,19 @@ MediaPanel::MediaPanel(QWidget *parent) :
|
|||
|
||||
auto preview_layout = new QVBoxLayout(wav_group);
|
||||
|
||||
m_ImgViewer = new QLabel();
|
||||
m_ImgViewer->setAlignment(Qt::AlignCenter);
|
||||
m_ImgViewer->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_ImgViewer, &QWidget::customContextMenuRequested, this, &MediaPanel::LabelContextMenuTriggered);
|
||||
preview_layout->addWidget(m_ImgViewer);
|
||||
auto viewer_scroll = new QScrollArea();
|
||||
viewer_scroll->setWidgetResizable(true);
|
||||
|
||||
auto wav_layout = new QHBoxLayout();
|
||||
preview_layout->addLayout(wav_layout);
|
||||
auto viewer_inner = new QWidget();
|
||||
viewer_scroll->setWidget(viewer_inner);
|
||||
|
||||
preview_layout->addWidget(viewer_scroll, 1);
|
||||
|
||||
m_viewerLayout = new QVBoxLayout(viewer_inner);
|
||||
m_viewerLayout->setMargin(0);
|
||||
|
||||
auto ctrl_layout = new QHBoxLayout();
|
||||
preview_layout->addLayout(ctrl_layout);
|
||||
|
||||
m_PlayheadSlider = new ClickableSlider(Qt::Horizontal);
|
||||
m_PlayheadSlider->setMinimum(0);
|
||||
|
@ -35,23 +40,18 @@ MediaPanel::MediaPanel(QWidget *parent) :
|
|||
connect(m_PlayheadSlider, &QSlider::sliderPressed, this, &MediaPanel::SliderPressed);
|
||||
connect(m_PlayheadSlider, &QSlider::sliderMoved, this, &MediaPanel::SliderMoved);
|
||||
connect(m_PlayheadSlider, &QSlider::sliderReleased, this, &MediaPanel::SliderReleased);
|
||||
wav_layout->addWidget(m_PlayheadSlider);
|
||||
ctrl_layout->addWidget(m_PlayheadSlider);
|
||||
|
||||
m_PlayBtn = new QPushButton(tr("Play"));
|
||||
m_PlayBtn->setCheckable(true);
|
||||
connect(m_PlayBtn, &QPushButton::clicked, this, &MediaPanel::Play);
|
||||
wav_layout->addWidget(m_PlayBtn);
|
||||
ctrl_layout->addWidget(m_PlayBtn);
|
||||
|
||||
FinishLayout();
|
||||
//FinishLayout();
|
||||
|
||||
m_PlaybackTimer = new QTimer(this);
|
||||
m_PlaybackTimer->setInterval(10);
|
||||
connect(m_PlaybackTimer, &QTimer::timeout, this, &MediaPanel::TimerUpdate);
|
||||
|
||||
m_mediaInstance = new MediaInstance(this);
|
||||
connect(m_mediaInstance, &MediaInstance::EndOfFile, this, &MediaPanel::EndOfFile);
|
||||
|
||||
m_AudioNotifyDevice = new MediaAudioDevice(m_mediaInstance, this);
|
||||
}
|
||||
|
||||
MediaPanel::~MediaPanel()
|
||||
|
@ -138,16 +138,7 @@ int64_t SeekData(void *opaque, int64_t offset, int whence)
|
|||
|
||||
void MediaPanel::OnOpeningData(void *data)
|
||||
{
|
||||
si::Object *o = static_cast<si::Object*>(data);
|
||||
|
||||
m_mediaInstance->Open(o->ExtractToMemory());
|
||||
|
||||
if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
// Heuristic to flip phoneme flics vertically
|
||||
m_vflip = (o->name().find("_Pho_") != std::string::npos);
|
||||
|
||||
VideoUpdate(0);
|
||||
}
|
||||
OpenMediaInstance(static_cast<si::Object*>(data));
|
||||
}
|
||||
|
||||
void MediaPanel::OnClosingData(void *data)
|
||||
|
@ -159,13 +150,13 @@ void MediaPanel::Close()
|
|||
{
|
||||
Play(false);
|
||||
|
||||
m_mediaInstance->Close();
|
||||
qDeleteAll(m_mediaInstances);
|
||||
m_mediaInstances.clear();
|
||||
|
||||
m_PlayheadSlider->setValue(0);
|
||||
|
||||
m_ImgViewer->setPixmap(QPixmap());
|
||||
|
||||
m_vflip = false;
|
||||
qDeleteAll(m_imgViewers);
|
||||
m_imgViewers.clear();
|
||||
}
|
||||
|
||||
QImage MediaInstance::GetVideoFrame(float t)
|
||||
|
@ -200,10 +191,6 @@ QImage MediaInstance::GetVideoFrame(float t)
|
|||
int ret = GetNextFrame(f);
|
||||
if (ret < 0) {
|
||||
av_frame_free(&f);
|
||||
|
||||
if (ret == AVERROR_EOF) {
|
||||
emit EndOfFile();
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
AVFrame *previous = nullptr;
|
||||
|
@ -241,23 +228,29 @@ QImage MediaInstance::GetVideoFrame(float t)
|
|||
|
||||
int MediaInstance::GetNextFrame(AVFrame *frame)
|
||||
{
|
||||
m_eof = false;
|
||||
int ret;
|
||||
av_frame_unref(frame);
|
||||
while ((ret = avcodec_receive_frame(m_CodecCtx, frame)) == AVERROR(EAGAIN)) {
|
||||
av_packet_unref(m_Packet);
|
||||
ret = av_read_frame(m_FmtCtx, m_Packet);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_Packet->stream_index == m_Stream->index) {
|
||||
ret = avcodec_send_packet(m_CodecCtx, m_Packet);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == AVERROR_EOF) {
|
||||
m_eof = true;
|
||||
emit EndOfFile();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -266,23 +259,38 @@ void MediaPanel::StartAudioPlayback()
|
|||
auto output_dev = QAudioDeviceInfo::defaultOutputDevice();
|
||||
auto fmt = output_dev.preferredFormat();
|
||||
|
||||
if (m_mediaInstance->StartPlayingAudio(output_dev, fmt)) {
|
||||
m_AudioOutput = new QAudioOutput(output_dev, fmt, this);
|
||||
m_AudioNotifyDevice->open(QIODevice::ReadOnly);
|
||||
connect(m_AudioOutput, &QAudioOutput::stateChanged, this, &MediaPanel::AudioStateChanged);
|
||||
m_AudioOutput->start(m_AudioNotifyDevice);
|
||||
for (auto it=m_mediaInstances.cbegin(); it!=m_mediaInstances.cend(); it++) {
|
||||
auto m = *it;
|
||||
if (m->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
if (!m->StartPlayingAudio(output_dev, fmt)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_AudioOutput = new QAudioOutput(output_dev, fmt, this);
|
||||
auto device = new MediaAudioDevice(this, m_AudioOutput);
|
||||
device->open(QIODevice::ReadOnly);
|
||||
m_AudioOutput->start(device);
|
||||
}
|
||||
|
||||
void MediaPanel::VideoUpdate(float t)
|
||||
{
|
||||
QImage img = m_mediaInstance->GetVideoFrame(t);
|
||||
if (!img.isNull()) {
|
||||
if (m_vflip) {
|
||||
img = img.mirrored(false, true);
|
||||
}
|
||||
for (size_t i=0; i<m_mediaInstances.size(); i++) {
|
||||
auto m = m_mediaInstances.at(i);
|
||||
|
||||
m_ImgViewer->setPixmap(QPixmap::fromImage(img));
|
||||
if (m->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
QImage img = m->GetVideoFrame(t);
|
||||
if (!img.isNull()) {
|
||||
auto v = m_imgViewers.at(i);
|
||||
|
||||
if (v->property("vflip").toBool()) {
|
||||
img = img.mirrored(false, true);
|
||||
}
|
||||
|
||||
v->setPixmap(QPixmap::fromImage(img));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,26 +304,83 @@ int MediaPanel::GetFakeSliderValueFromReal(float t) const
|
|||
return t * m_PlayheadSlider->maximum();
|
||||
}
|
||||
|
||||
void MediaPanel::OpenMediaInstance(si::Object *o)
|
||||
{
|
||||
switch (o->type()) {
|
||||
case si::MxOb::Presenter:
|
||||
for (auto it=o->GetChildren().cbegin(); it!=o->GetChildren().cend(); it++) {
|
||||
OpenMediaInstance(static_cast<si::Object*>(*it));
|
||||
}
|
||||
break;
|
||||
case si::MxOb::Video:
|
||||
case si::MxOb::Sound:
|
||||
case si::MxOb::Bitmap:
|
||||
{
|
||||
auto m = new MediaInstance(this);
|
||||
|
||||
m->Open(o->ExtractToMemory());
|
||||
|
||||
m_mediaInstances.push_back(m);
|
||||
|
||||
if (m->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
// Heuristic to flip phoneme flics vertically
|
||||
if (m_imgViewers.size() < m_mediaInstances.size()) {
|
||||
m_imgViewers.resize(m_mediaInstances.size());
|
||||
}
|
||||
|
||||
auto v = new QLabel();
|
||||
v->setAlignment(Qt::AlignCenter);
|
||||
v->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
v->setProperty("vflip", (o->name().find("_Pho_") != std::string::npos));
|
||||
connect(v, &QWidget::customContextMenuRequested, this, &MediaPanel::LabelContextMenuTriggered);
|
||||
m_viewerLayout->addWidget(v);
|
||||
m_imgViewers[m_mediaInstances.size()-1] = v;
|
||||
|
||||
VideoUpdate(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case si::MxOb::Null:
|
||||
case si::MxOb::World:
|
||||
case si::MxOb::Event:
|
||||
case si::MxOb::Animation:
|
||||
case si::MxOb::Object:
|
||||
case si::MxOb::TYPE_COUNT:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPanel::Play(bool e)
|
||||
{
|
||||
if (m_AudioOutput) {
|
||||
m_AudioOutput->stop();
|
||||
delete m_AudioOutput;
|
||||
m_AudioOutput = nullptr;
|
||||
|
||||
m_AudioNotifyDevice->close();
|
||||
}
|
||||
|
||||
if (e) {
|
||||
if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
m_PlaybackOffset = GetRealSliderValue();
|
||||
} else {
|
||||
m_PlaybackOffset = 0;
|
||||
if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
StartAudioPlayback();
|
||||
bool has_video = false;
|
||||
bool has_audio = false;
|
||||
|
||||
m_PlaybackOffset = GetRealSliderValue();
|
||||
|
||||
for (auto it=m_mediaInstances.cbegin(); it!=m_mediaInstances.cend(); it++) {
|
||||
auto m = *it;
|
||||
|
||||
m->ResetEOF();
|
||||
|
||||
if (m->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
has_video = true;
|
||||
} else if (m->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
has_audio = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_audio) {
|
||||
StartAudioPlayback();
|
||||
}
|
||||
|
||||
m_PlaybackStart = QDateTime::currentMSecsSinceEpoch();
|
||||
m_PlaybackTimer->start();
|
||||
} else {
|
||||
|
@ -326,15 +391,48 @@ void MediaPanel::Play(bool e)
|
|||
|
||||
void MediaPanel::TimerUpdate()
|
||||
{
|
||||
if (!m_SliderPressed && m_mediaInstance->GetStreamPosition() != AV_NOPTS_VALUE) {
|
||||
m_PlayheadSlider->setValue(GetFakeSliderValueFromReal(m_mediaInstance->GetTime()));
|
||||
bool all_eof = true;
|
||||
|
||||
// Don't set slider if slider pressed
|
||||
bool set_slider = m_SliderPressed;
|
||||
|
||||
for (size_t i=0; i<m_mediaInstances.size(); i++) {
|
||||
auto m = m_mediaInstances.at(i);
|
||||
|
||||
if (all_eof && !m->IsEndOfFile()) {
|
||||
all_eof = false;
|
||||
}
|
||||
|
||||
if (m->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
float now_seconds = float(QDateTime::currentMSecsSinceEpoch() - m_PlaybackStart) * 0.001f;
|
||||
float now = m->SecondsToPercent(now_seconds);
|
||||
now += m_PlaybackOffset;
|
||||
|
||||
VideoUpdate(now);
|
||||
|
||||
if (!set_slider) {
|
||||
m_PlayheadSlider->setValue(GetFakeSliderValueFromReal(now));
|
||||
set_slider = true;
|
||||
}
|
||||
} else if (m->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
if (!set_slider) {
|
||||
m_PlayheadSlider->setValue(GetFakeSliderValueFromReal(m->GetTime()));
|
||||
set_slider = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
float now_seconds = float(QDateTime::currentMSecsSinceEpoch() - m_PlaybackStart) * 0.001f;
|
||||
float now = m_mediaInstance->SecondsToPercent(now_seconds);
|
||||
if (all_eof) {
|
||||
// Detach audio output so that it flushes itself
|
||||
if (m_AudioOutput) {
|
||||
connect(m_AudioOutput, &QAudioOutput::stateChanged, m_AudioOutput, &QAudioOutput::deleteLater);
|
||||
m_AudioOutput = nullptr;
|
||||
}
|
||||
|
||||
VideoUpdate(now + m_PlaybackOffset);
|
||||
Play(false);
|
||||
if (!m_SliderPressed) {
|
||||
m_PlayheadSlider->setValue(m_PlayheadSlider->maximum());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,13 +448,16 @@ void MediaPanel::SliderPressed()
|
|||
|
||||
void MediaPanel::SliderMoved(int i)
|
||||
{
|
||||
if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
float f = GetRealSliderValue();
|
||||
m_PlaybackOffset = f;
|
||||
m_PlaybackStart = QDateTime::currentMSecsSinceEpoch();
|
||||
VideoUpdate(f);
|
||||
} else if (m_mediaInstance->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
m_mediaInstance->Seek(GetRealSliderValue());
|
||||
for (auto it=m_mediaInstances.cbegin(); it!=m_mediaInstances.cend(); it++) {
|
||||
auto m = *it;
|
||||
if (m->codec_type() == AVMEDIA_TYPE_VIDEO) {
|
||||
float f = GetRealSliderValue();
|
||||
m_PlaybackOffset = f;
|
||||
m_PlaybackStart = QDateTime::currentMSecsSinceEpoch();
|
||||
VideoUpdate(f);
|
||||
} else if (m->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
m->Seek(GetRealSliderValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,36 +478,39 @@ void MediaPanel::LabelContextMenuTriggered(const QPoint &pos)
|
|||
{
|
||||
QMenu m(this);
|
||||
|
||||
QObject *s = sender();
|
||||
|
||||
auto vert_flip = m.addAction(tr("Flip Vertically"));
|
||||
vert_flip->setCheckable(true);
|
||||
vert_flip->setChecked(m_vflip);
|
||||
connect(vert_flip, &QAction::triggered, this, [this](bool e){
|
||||
m_vflip = e;
|
||||
vert_flip->setChecked(s->property("vflip").toBool());
|
||||
connect(vert_flip, &QAction::triggered, this, [this, s](bool e){
|
||||
s->setProperty("vflip", e);
|
||||
UpdateVideo();
|
||||
});
|
||||
|
||||
m.exec(static_cast<QWidget*>(sender())->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MediaPanel::EndOfFile()
|
||||
{
|
||||
if (IsPlaying()) {
|
||||
Play(false);
|
||||
if (!m_SliderPressed) {
|
||||
m_PlayheadSlider->setValue(m_PlayheadSlider->maximum());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaAudioDevice::MediaAudioDevice(MediaInstance *o, QObject *parent) :
|
||||
MediaAudioDevice::MediaAudioDevice(MediaPanel *panel, QAudioFormat::SampleType type, QObject *parent) :
|
||||
QIODevice(parent)
|
||||
{
|
||||
m_mediaInstance = o;
|
||||
m_mediaPanel = panel;
|
||||
m_sampleType = type;
|
||||
}
|
||||
|
||||
qint64 MediaAudioDevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
return m_mediaInstance->ReadAudio(data, maxSize);
|
||||
qint64 len = 0;
|
||||
for (auto it=m_mediaPanel->GetMediaInstances().cbegin(); it!=m_mediaPanel->GetMediaInstances().cend(); it++) {
|
||||
auto m = *it;
|
||||
if (m->codec_type() == AVMEDIA_TYPE_AUDIO) {
|
||||
qint64 l = m->ReadAudio(data, maxSize);
|
||||
|
||||
len = std::max(l, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
qint64 MediaAudioDevice::writeData(const char *data, qint64 maxSize)
|
||||
|
@ -443,10 +547,9 @@ MediaInstance::MediaInstance(QObject *parent) :
|
|||
m_SwrCtx(nullptr),
|
||||
m_IoCtx(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MediaInstance::Open(const si::MemoryBuffer &buf)
|
||||
void MediaInstance::Open(const si::bytearray &buf)
|
||||
{
|
||||
static const size_t buf_sz = 4096;
|
||||
|
||||
|
@ -486,6 +589,13 @@ void MediaInstance::Open(const si::MemoryBuffer &buf)
|
|||
|
||||
m_Stream = m_FmtCtx->streams[0];
|
||||
|
||||
m_duration = m_Stream->duration;
|
||||
if (m_Stream->codecpar->codec_id == AV_CODEC_ID_FLIC) {
|
||||
// FFmpeg can't retrieve the FLIC duration, but we can
|
||||
si::FLIC *flic = (si::FLIC *) buf.data();
|
||||
m_duration = flic->frames;
|
||||
}
|
||||
|
||||
const AVCodec *decoder = avcodec_find_decoder(m_Stream->codecpar->codec_id);
|
||||
if (!decoder) {
|
||||
qWarning() << "Failed to find decoder for type" << avcodec_get_name(m_Stream->codecpar->codec_id);
|
||||
|
@ -562,6 +672,10 @@ void MediaInstance::Close()
|
|||
|
||||
bool MediaInstance::StartPlayingAudio(const QAudioDeviceInfo &output_dev, const QAudioFormat &fmt)
|
||||
{
|
||||
if (m_SwrCtx) {
|
||||
swr_free(&m_SwrCtx);
|
||||
}
|
||||
|
||||
AVSampleFormat smp_fmt = AV_SAMPLE_FMT_S16;
|
||||
switch (fmt.sampleType()) {
|
||||
case QAudioFormat::Unknown:
|
||||
|
@ -643,15 +757,15 @@ void MediaInstance::ClearQueue()
|
|||
|
||||
float MediaInstance::PercentToSeconds(float t) const
|
||||
{
|
||||
return t * (float(m_Stream->time_base.num) * float(m_Stream->duration) / float(m_Stream->time_base.den));
|
||||
return t * (float(m_Stream->time_base.num) * float(m_duration) / float(m_Stream->time_base.den));
|
||||
}
|
||||
|
||||
float MediaInstance::SecondsToPercent(float t) const
|
||||
{
|
||||
return t / (float(m_Stream->time_base.num) * float(m_Stream->duration) / float(m_Stream->time_base.den));
|
||||
return t / (float(m_Stream->time_base.num) * float(m_duration) / float(m_Stream->time_base.den));
|
||||
}
|
||||
|
||||
int64_t MediaInstance::PercentToTimestamp(float t) const
|
||||
{
|
||||
return std::floor(t * float(m_Stream->duration));
|
||||
return std::floor(t * float(m_duration));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ extern "C" {
|
|||
}
|
||||
|
||||
#include <file.h>
|
||||
#include <object.h>
|
||||
#include <QAudioOutput>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
|
@ -24,7 +25,7 @@ class MediaInstance : public QObject
|
|||
public:
|
||||
MediaInstance(QObject *parent = nullptr);
|
||||
|
||||
void Open(const si::MemoryBuffer &buf);
|
||||
void Open(const si::bytearray &buf);
|
||||
|
||||
void Close();
|
||||
|
||||
|
@ -43,14 +44,9 @@ public:
|
|||
|
||||
QImage GetVideoFrame(float f);
|
||||
|
||||
int64_t GetStreamPosition() const
|
||||
{
|
||||
return m_Frame->pts;
|
||||
}
|
||||
|
||||
float GetTime() const
|
||||
{
|
||||
return float(m_Frame->pts) / m_Stream->duration;
|
||||
return float(m_Frame->pts) / m_duration;
|
||||
}
|
||||
|
||||
float PercentToSeconds(float t) const;
|
||||
|
@ -58,6 +54,16 @@ public:
|
|||
|
||||
int64_t PercentToTimestamp(float t) const;
|
||||
|
||||
bool IsEndOfFile()
|
||||
{
|
||||
return m_eof;
|
||||
}
|
||||
|
||||
void ResetEOF()
|
||||
{
|
||||
m_eof = false;
|
||||
}
|
||||
|
||||
signals:
|
||||
void EndOfFile();
|
||||
|
||||
|
@ -86,6 +92,10 @@ private:
|
|||
QAudioFormat m_playbackFormat;
|
||||
AVSampleFormat m_AudioOutputSampleFmt;
|
||||
|
||||
bool m_eof;
|
||||
|
||||
int64_t m_duration;
|
||||
|
||||
};
|
||||
|
||||
class MediaPanel : public Panel
|
||||
|
@ -100,6 +110,11 @@ public:
|
|||
return m_PlaybackTimer->isActive();
|
||||
}
|
||||
|
||||
const std::vector<MediaInstance *> &GetMediaInstances() const
|
||||
{
|
||||
return m_mediaInstances;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnOpeningData(void *data) override;
|
||||
virtual void OnClosingData(void *data) override;
|
||||
|
@ -114,19 +129,19 @@ private:
|
|||
float GetRealSliderValue() const;
|
||||
int GetFakeSliderValueFromReal(float t) const;
|
||||
|
||||
QLabel *m_ImgViewer;
|
||||
void OpenMediaInstance(si::Object *o);
|
||||
|
||||
MediaInstance *m_mediaInstance;
|
||||
std::vector<QLabel *> m_imgViewers;
|
||||
std::vector<MediaInstance *> m_mediaInstances;
|
||||
|
||||
QAudioOutput *m_AudioOutput;
|
||||
QIODevice *m_AudioNotifyDevice;
|
||||
QSlider *m_PlayheadSlider;
|
||||
QPushButton *m_PlayBtn;
|
||||
QTimer *m_PlaybackTimer;
|
||||
qint64 m_PlaybackStart;
|
||||
float m_PlaybackOffset;
|
||||
bool m_SliderPressed;
|
||||
bool m_vflip;
|
||||
QVBoxLayout *m_viewerLayout;
|
||||
|
||||
private slots:
|
||||
void Play(bool e);
|
||||
|
@ -143,22 +158,21 @@ private slots:
|
|||
|
||||
void LabelContextMenuTriggered(const QPoint &pos);
|
||||
|
||||
void EndOfFile();
|
||||
|
||||
};
|
||||
|
||||
class MediaAudioDevice : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MediaAudioDevice(MediaInstance *o, QObject *parent = nullptr);
|
||||
MediaAudioDevice(MediaPanel *panel, QAudioFormat::SampleType type, QObject *parent);
|
||||
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxSize) override;
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize) override;
|
||||
|
||||
private:
|
||||
MediaInstance *m_mediaInstance;
|
||||
MediaPanel *m_mediaPanel;
|
||||
QAudioFormat::SampleType m_sampleType;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue