115 lines
6.4 KiB
C++
115 lines
6.4 KiB
C++
/* Реальные файлы FFmpeg, engine/window и PCM через null-device: EOF не завершает ещё идущее воспроизведение. */
|
|
#include "../src/videoplayer_engine.h"
|
|
#include "../src/videoplayer_window.h"
|
|
#include "../src/sound_manager.h"
|
|
#include <QtTest>
|
|
#include <QProcess>
|
|
#include <QStandardPaths>
|
|
#include <QTemporaryDir>
|
|
#include <QScopeGuard>
|
|
#include <atomic>
|
|
extern "C" {
|
|
#include "debug_config.h"
|
|
}
|
|
|
|
#ifdef TEST_NULL_AUDIO
|
|
extern "C" ma_result __real_ma_context_init(const ma_backend*, ma_uint32, const ma_context_config*, ma_context*);
|
|
extern "C" ma_result __wrap_ma_context_init(const ma_backend*, ma_uint32, const ma_context_config*, ma_context* context) {
|
|
const ma_backend backend = ma_backend_null; return __real_ma_context_init(&backend, 1, nullptr, context);
|
|
}
|
|
#endif
|
|
|
|
class VideoPlaybackTest : public QObject {
|
|
Q_OBJECT
|
|
QTemporaryDir directory;
|
|
QString video(const char* name) const { return directory.filePath(QString::fromLatin1(name) + ".mkv"); }
|
|
bool fixture(const char* name, const QString& videoDuration, const QString& audioDuration = {}) {
|
|
QStringList args{"-hide_banner", "-loglevel", "error", "-f", "lavfi", "-i",
|
|
"testsrc2=size=160x90:rate=25:duration=" + videoDuration};
|
|
if (!audioDuration.isEmpty()) args << "-f" << "lavfi" << "-i" << "sine=frequency=440:sample_rate=48000:duration=" + audioDuration;
|
|
args << "-c:v" << "libx264" << "-preset" << "ultrafast" << "-pix_fmt" << "yuv420p";
|
|
if (!audioDuration.isEmpty()) args << "-c:a" << "pcm_s16le";
|
|
args << video(name);
|
|
QProcess process; process.start(QStandardPaths::findExecutable("ffmpeg"), args);
|
|
if (!process.waitForStarted() || !process.waitForFinished(20000) || process.exitCode()) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "fixture encode failed name=%s error=%s output=%s", name,
|
|
qPrintable(process.errorString()), process.readAllStandardError().constData()); return false;
|
|
}
|
|
return true;
|
|
}
|
|
private slots:
|
|
void initTestCase() {
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
|
|
debug_set_category_level(DEBUG_CATEGORY_VIDEO, DEBUG_LEVEL_DEBUG);
|
|
if (QStandardPaths::findExecutable("ffmpeg").isEmpty()) QSKIP("ffmpeg executable is required for media fixtures");
|
|
QVERIFY(directory.isValid());
|
|
QVERIFY(fixture("long", "4")); QVERIFY(fixture("short", "0.4"));
|
|
QVERIFY(fixture("audio-longer", "0.4", "1.2")); QVERIFY(fixture("video-longer", "1.2", "0.4"));
|
|
QVERIFY(SoundManager::instance()->init());
|
|
}
|
|
void cleanupTestCase() { SoundManager::instance()->shutdown(); }
|
|
|
|
void finishesAfterPresentation_data() {
|
|
QTest::addColumn<QString>("name"); QTest::addColumn<qint64>("duration");
|
|
QTest::addColumn<int>("minimumFrames");
|
|
QTest::newRow("long-video") << "long" << qint64(4000) << 90;
|
|
QTest::newRow("short-video") << "short" << qint64(400) << 10;
|
|
#ifdef TEST_NULL_AUDIO
|
|
QTest::newRow("audio-longer") << "audio-longer" << qint64(1200) << 10;
|
|
QTest::newRow("video-longer") << "video-longer" << qint64(1200) << 25;
|
|
#endif
|
|
}
|
|
void finishesAfterPresentation() {
|
|
QFETCH(QString, name); QFETCH(qint64, duration); QFETCH(int, minimumFrames);
|
|
VideoPlayerEngine engine; QSignalSpy frames(&engine, &VideoPlayerEngine::framePresented);
|
|
QSignalSpy errors(&engine, &VideoPlayerEngine::error), finished(&engine, &VideoPlayerEngine::finished);
|
|
QElapsedTimer elapsed; elapsed.start(); engine.open(video(qPrintable(name)));
|
|
QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, duration + 3000);
|
|
QCOMPARE(errors.count(), 0); QCOMPARE(engine.state(), VideoPlayerEngine::Finished);
|
|
QVERIFY2(elapsed.elapsed() >= duration - 80, "finished arrived before the media ended");
|
|
QVERIFY2(engine.positionMs() >= duration - 40, "finished position precedes the media end");
|
|
QVERIFY2(frames.count() >= minimumFrames, "video tail was not presented");
|
|
QVERIFY(!engine.currentFrame().isNull());
|
|
QTest::qWait(80); QCOMPARE(finished.count(), 1);
|
|
}
|
|
|
|
void pauseAndSeekAfterDecodeEof() {
|
|
VideoPlayerEngine engine; QSignalSpy frames(&engine, &VideoPlayerEngine::framePresented);
|
|
QSignalSpy finished(&engine, &VideoPlayerEngine::finished), errors(&engine, &VideoPlayerEngine::error);
|
|
engine.open(video("short")); QTRY_VERIFY_WITH_TIMEOUT(frames.count() > 0, 1000);
|
|
engine.pause(); QCOMPARE(engine.state(), VideoPlayerEngine::Paused);
|
|
const int held = frames.count(); const qint64 position = engine.positionMs();
|
|
QTest::qWait(500); QCOMPARE(frames.count(), held); QCOMPARE(engine.positionMs(), position); QCOMPARE(finished.count(), 0);
|
|
engine.seekMs(0); engine.play(); QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, 1500);
|
|
QCOMPARE(errors.count(), 0); QVERIFY(frames.count() >= held + 8);
|
|
engine.togglePlayPause(); QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 2, 1500);
|
|
QCOMPARE(errors.count(), 0);
|
|
}
|
|
|
|
void seekAndReopenDiscardQueuedEof() {
|
|
VideoPlayerEngine engine; QSignalSpy finished(&engine, &VideoPlayerEngine::finished);
|
|
QSignalSpy errors(&engine, &VideoPlayerEngine::error);
|
|
engine.open(video("short")); QTest::qWait(60);
|
|
for (qint64 position : {300, 0, 250, 40, 0}) engine.seekMs(position);
|
|
QCOMPARE(engine.state(), VideoPlayerEngine::Playing);
|
|
QTest::qWait(120); QCOMPARE(finished.count(), 0);
|
|
engine.open(video("long"));
|
|
QTest::qWait(500); QCOMPARE(finished.count(), 0); QCOMPARE(engine.state(), VideoPlayerEngine::Playing);
|
|
engine.seekMs(3800); QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, 1500); QCOMPARE(errors.count(), 0);
|
|
}
|
|
|
|
void windowClosesOnlyAfterPlayback() {
|
|
VideoPlayerWindow window; auto* engine = window.findChild<VideoPlayerEngine*>(); QVERIFY(engine);
|
|
QSignalSpy frames(engine, &VideoPlayerEngine::framePresented), closed(&window, &VideoPlayerWindow::closed);
|
|
QSignalSpy errors(engine, &VideoPlayerEngine::error);
|
|
QElapsedTimer elapsed; elapsed.start(); window.playFile(video("short"));
|
|
QTest::qWait(150); QVERIFY(window.isVisible()); QCOMPARE(closed.count(), 0);
|
|
QTRY_COMPARE_WITH_TIMEOUT(closed.count(), 1, 1500);
|
|
QVERIFY(elapsed.elapsed() >= 380); QVERIFY(frames.count() >= 10); QCOMPARE(errors.count(), 0);
|
|
QVERIFY(!window.isVisible());
|
|
}
|
|
};
|
|
|
|
QTEST_MAIN(VideoPlaybackTest)
|
|
#include "test_video_playback.moc"
|