diff -Nru qtav-1.12.0+ds/appveyor.yml qtav-1.13.0+ds/appveyor.yml --- qtav-1.12.0+ds/appveyor.yml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/appveyor.yml 2019-07-11 00:58:59.000000000 +0000 @@ -8,6 +8,13 @@ environment: matrix: + - arch: x64 + qt: 5.9 + cc: VS2017 + mode: release + QTDIR: C:\Qt\5.9\msvc2017_64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + - arch: x86 cc: VS2015 qt: 5.9 @@ -52,7 +59,11 @@ init: - set vcarch=%arch% - if "%arch%" == "x64" set vcarch=amd64 - - if not %cc%==MinGW call "C:\Program Files (x86)\Microsoft Visual Studio %toolchain_version%.0\VC\vcvarsall.bat" %vcarch% + - if %cc%==VS2017 ( + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %vcarch% + ) else if not %cc%==MinGW ( + call "C:\Program Files (x86)\Microsoft Visual Studio %toolchain_version%.0\VC\vcvarsall.bat" %vcarch% + ) - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS% - echo PROCESSOR_IDENTIFIER=%PROCESSOR_IDENTIFIER% - echo QTDIR=%QTDIR% @@ -90,4 +101,4 @@ active_mode: false on: mode: release - cc: VS2015 + cc: VS2017 diff -Nru qtav-1.12.0+ds/Changelog qtav-1.13.0+ds/Changelog --- qtav-1.12.0+ds/Changelog 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/Changelog 2019-07-11 00:58:59.000000000 +0000 @@ -1,3 +1,20 @@ +version 1.13.0 2019-07-11 + +- add python bindings +- more apis for qml player +- auto rotate video +- apple store +- fix ios plugin not found +- support chapters +- muxer, encoder, transcoder improvements +- compatible with new ffmpeg +- videotoolbox: hevc, +- cuda: new devices +- android: no longer depends on private qt module +- fix opensl error +- mediacodec: 0-copy via a plugin from https://github.com/wang-bin/mdk-sdk + + version 1.12.0 2017-06-20 Changelog diff -Nru qtav-1.12.0+ds/cmake/QtAV.rc.in qtav-1.13.0+ds/cmake/QtAV.rc.in --- qtav-1.12.0+ds/cmake/QtAV.rc.in 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/cmake/QtAV.rc.in 2019-07-11 00:58:59.000000000 +0000 @@ -19,10 +19,10 @@ BEGIN BLOCK "000004b0" BEGIN - VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin->PPTV | wbsecg1@gmail.com" + VALUE "CompanyName", "wbsecg1@gmail.com" VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org" VALUE "FileVersion", QTAV_VERSION_STR ".0" - VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + VALUE "LegalCopyright", "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" VALUE "InternalName", "@MODULE@" VALUE "ProductName", "@MODULE@" VALUE "ProductVersion", QTAV_VERSION_STR ".0" diff -Nru qtav-1.12.0+ds/CMakeLists.txt qtav-1.13.0+ds/CMakeLists.txt --- qtav-1.12.0+ds/CMakeLists.txt 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/CMakeLists.txt 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) + +project(QTAV) + set(QTAV_MAJOR 1) -set(QTAV_MINOR 11) +set(QTAV_MINOR 13) set(QTAV_PATCH 0) set(PROJECT_VERSION ${QTAV_MAJOR}.${QTAV_MINOR}.${QTAV_PATCH}) set(SO_VERSION ${QTAV_MAJOR}) @@ -11,6 +14,9 @@ if(POLICY CMP0063) # visibility. since 3.3 cmake_policy(SET CMP0063 NEW) endif() +if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) +endif() set(CMAKE_CXX_VISIBILITY_PRESET hidden) #use with -fdata-sections -ffunction-sections to reduce target size set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) diff -Nru qtav-1.12.0+ds/common.pri qtav-1.13.0+ds/common.pri --- qtav-1.12.0+ds/common.pri 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/common.pri 2019-07-11 00:58:59.000000000 +0000 @@ -30,15 +30,6 @@ } } -CONFIG += profile -#profiling, -pg is not supported for msvc -debug:!ios:!android:!*msvc*:profile { - QMAKE_CXXFLAGS_DEBUG += -pg - QMAKE_LFLAGS_DEBUG += -pg - QMAKE_CXXFLAGS_DEBUG = $$unique(QMAKE_CXXFLAGS_DEBUG) - QMAKE_LFLAGS_DEBUG = $$unique(QMAKE_LFLAGS_DEBUG) -} - #$$[TARGET_PLATFORM] #$$[QT_ARCH] #windows symbian windowsce arm _OS = diff -Nru qtav-1.12.0+ds/config.tests/videotoolbox/main.cpp qtav-1.13.0+ds/config.tests/videotoolbox/main.cpp --- qtav-1.12.0+ds/config.tests/videotoolbox/main.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/config.tests/videotoolbox/main.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Media play library based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -18,11 +18,14 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ +#ifndef __APPLE__ +#error apple only +#endif extern "C" { #include } int main() { - av_videotoolbox_alloc_context(); + //av_videotoolbox_alloc_context(); return 0; } diff -Nru qtav-1.12.0+ds/debian/changelog qtav-1.13.0+ds/debian/changelog --- qtav-1.12.0+ds/debian/changelog 2018-04-15 02:52:19.000000000 +0000 +++ qtav-1.13.0+ds/debian/changelog 2021-05-25 02:48:18.000000000 +0000 @@ -1,20 +1,36 @@ -qtav (1.12.0+ds-4build3) bionic; urgency=medium +qtav (1.13.0+ds-1+18.04.sav0) bionic; urgency=medium - * No-change rebuild against qtdeclarative-abi-5-9-5. + * Rebuild using libswresample instead of deprecated libavresample: + - d/control: Change libavresample-dev BD to libswresample-dev - -- Simon Quigley Sat, 14 Apr 2018 21:52:19 -0500 + -- Rob Savoury Mon, 24 May 2021 19:48:18 -0700 -qtav (1.12.0+ds-4build2) bionic; urgency=medium +qtav (1.13.0+ds-1~18.04.sav0) bionic; urgency=medium - * No-change rebuild against Qt 5.9.4. + * Backport to Bionic - -- Simon Quigley Sun, 25 Feb 2018 21:47:40 -0600 + -- Rob Savoury Sat, 14 Dec 2019 18:23:51 -0800 -qtav (1.12.0+ds-4build1) bionic; urgency=medium +qtav (1.13.0+ds-1) unstable; urgency=medium - * Rebuild against new qtbase ABI 5.9.3. + [ Steven Robbins ] + * [4ef3ad4] New upstream version 1.13.0+ds - -- Gianfranco Costamagna Wed, 20 Dec 2017 18:18:42 +0100 + -- Steve M. Robbins Sat, 31 Aug 2019 18:20:39 -0500 + +qtav (1.12.0+ds-5) unstable; urgency=medium + + * Team upload. + * Switch Vcs-* fields to salsa.debian.org. + * Bump the debhelper compatibility to 11: + - bump the debhelper build dependency to 11~ + - bump compat to 11 + - remove --parallel for dh, as now done by default + * Bump Standards-Version to 4.1.5, no changes required. + * Fix build with FFmpeg >= 4.0. (Closes: #888367) + * Remove empty line at the end of rules. + + -- Pino Toscano Wed, 18 Jul 2018 09:12:03 +0200 qtav (1.12.0+ds-4) unstable; urgency=medium diff -Nru qtav-1.12.0+ds/debian/compat qtav-1.13.0+ds/debian/compat --- qtav-1.12.0+ds/debian/compat 2017-11-10 19:35:08.000000000 +0000 +++ qtav-1.13.0+ds/debian/compat 2019-12-15 02:23:51.000000000 +0000 @@ -1 +1 @@ -10 +11 diff -Nru qtav-1.12.0+ds/debian/control qtav-1.13.0+ds/debian/control --- qtav-1.12.0+ds/debian/control 2017-11-11 06:08:04.000000000 +0000 +++ qtav-1.13.0+ds/debian/control 2021-05-25 02:43:36.000000000 +0000 @@ -3,7 +3,7 @@ Uploaders: Steve M. Robbins Section: video Priority: optional -Build-Depends: debhelper (>= 10), +Build-Depends: debhelper (>= 11~), qt5-qmake, libqt5opengl5-dev, libqt5svg5-dev, @@ -11,11 +11,11 @@ libqt5x11extras5-dev, libass-dev, libavutil-dev, - libavresample-dev, libavcodec-dev, libavformat-dev, libavdevice-dev, libavfilter-dev, + libswresample-dev, libswscale-dev, libopenal-dev, libpulse-dev, @@ -23,10 +23,10 @@ libva-dev [!hurd-any], libegl1-mesa-dev, libuchardet-dev -Standards-Version: 4.1.1 +Standards-Version: 4.1.5 Homepage: http://qtav.org -Vcs-Browser: https://anonscm.debian.org/cgit/pkg-kde/qt-extras/qtav.git/ -Vcs-Git: https://anonscm.debian.org/git/pkg-kde/qt-extras/qtav.git +Vcs-Browser: https://salsa.debian.org/qt-kde-team/extras/qtav +Vcs-Git: https://salsa.debian.org/qt-kde-team/extras/qtav.git Package: libqtav1 Architecture: any diff -Nru qtav-1.12.0+ds/debian/patches/0001-Create-install-files-with-soversion.patch qtav-1.13.0+ds/debian/patches/0001-Create-install-files-with-soversion.patch --- qtav-1.12.0+ds/debian/patches/0001-Create-install-files-with-soversion.patch 2017-11-23 18:53:50.000000000 +0000 +++ qtav-1.13.0+ds/debian/patches/0001-Create-install-files-with-soversion.patch 2019-08-31 23:20:39.000000000 +0000 @@ -9,10 +9,10 @@ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libQtAV.pro b/src/libQtAV.pro -index d2e94c4..0ad5f67 100644 +index 8e49668..e677953 100644 --- a/src/libQtAV.pro +++ b/src/libQtAV.pro -@@ -618,7 +618,7 @@ icon.path = /usr/share/icons/hicolor/scalable/apps +@@ -625,7 +625,7 @@ icon.path = /usr/share/icons/hicolor/scalable/apps INSTALLS += icon #debian DEB_INSTALL_LIST = .$$[QT_INSTALL_LIBS]/libQt*AV.so.* @@ -22,7 +22,7 @@ QMAKE_EXTRA_TARGETS += libqtav target.depends *= $${libqtav.target} diff --git a/widgets/libQtAVWidgets.pro b/widgets/libQtAVWidgets.pro -index 7c60148..62809b9 100644 +index 599aa90..01c5fec 100644 --- a/widgets/libQtAVWidgets.pro +++ b/widgets/libQtAVWidgets.pro @@ -107,7 +107,7 @@ config_x11 { diff -Nru qtav-1.12.0+ds/debian/patches/0002-QMLPlayer-should-be-QApplication.patch qtav-1.13.0+ds/debian/patches/0002-QMLPlayer-should-be-QApplication.patch --- qtav-1.12.0+ds/debian/patches/0002-QMLPlayer-should-be-QApplication.patch 2017-11-23 18:53:50.000000000 +0000 +++ qtav-1.13.0+ds/debian/patches/0002-QMLPlayer-should-be-QApplication.patch 2019-08-31 23:20:39.000000000 +0000 @@ -26,7 +26,7 @@ # Add more folders to ship with the application, here folder_01.source = qml/QMLPlayer diff --git a/examples/QMLPlayer/main.cpp b/examples/QMLPlayer/main.cpp -index f7e5743..f29a531 100644 +index 841a63b..e08bb35 100644 --- a/examples/QMLPlayer/main.cpp +++ b/examples/QMLPlayer/main.cpp @@ -21,7 +21,7 @@ @@ -38,7 +38,7 @@ #include #include #include -@@ -45,7 +45,7 @@ int main(int argc, char *argv[]) +@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) Config::setName(QString::fromLatin1("QMLPlayer")); do_common_options_before_qapp(options); diff -Nru qtav-1.12.0+ds/debian/patches/0003-Rename-the-qtav_dev-and-qtav_dev_links-target-as-lib.patch qtav-1.13.0+ds/debian/patches/0003-Rename-the-qtav_dev-and-qtav_dev_links-target-as-lib.patch --- qtav-1.12.0+ds/debian/patches/0003-Rename-the-qtav_dev-and-qtav_dev_links-target-as-lib.patch 2017-11-23 18:53:50.000000000 +0000 +++ qtav-1.13.0+ds/debian/patches/0003-Rename-the-qtav_dev-and-qtav_dev_links-target-as-lib.patch 2019-08-31 23:20:39.000000000 +0000 @@ -9,10 +9,10 @@ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libQtAV.pro b/src/libQtAV.pro -index 0ad5f67..29015ff 100644 +index e677953..bf30abd 100644 --- a/src/libQtAV.pro +++ b/src/libQtAV.pro -@@ -627,7 +627,7 @@ DEB_INSTALL_LIST = $$join(SDK_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/, .$$[QT_INSTA +@@ -634,7 +634,7 @@ DEB_INSTALL_LIST = $$join(SDK_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/, .$$[QT_INSTA DEB_INSTALL_LIST += .$$[QT_INSTALL_LIBS]/libQt*AV.prl .$$[QT_INSTALL_LIBS]/libQt*AV.so MKSPECS_DIR=$$[QT_HOST_DATA]/mkspecs # we only build deb for qt5, so QT_HOST_DATA is fine. qt4 can use $$[QMAKE_MKSPECS] DEB_INSTALL_LIST += .$${MKSPECS_DIR}/features/av.prf .$${MKSPECS_DIR}/modules/qt_lib_av.pri @@ -21,7 +21,7 @@ qtav_dev.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${qtav_dev.target} QMAKE_EXTRA_TARGETS += qtav_dev target.depends *= $${qtav_dev.target} -@@ -640,7 +640,7 @@ QMAKE_EXTRA_TARGETS += qtav_private_dev +@@ -647,7 +647,7 @@ QMAKE_EXTRA_TARGETS += qtav_private_dev target.depends *= $${qtav_private_dev.target} greaterThan(QT_MAJOR_VERSION, 4) { @@ -31,7 +31,7 @@ QMAKE_EXTRA_TARGETS *= qtav_dev_links target.depends *= $${qtav_dev_links.target} diff --git a/widgets/libQtAVWidgets.pro b/widgets/libQtAVWidgets.pro -index 62809b9..3176d09 100644 +index 01c5fec..c05f108 100644 --- a/widgets/libQtAVWidgets.pro +++ b/widgets/libQtAVWidgets.pro @@ -152,13 +152,13 @@ DEB_INSTALL_LIST = $$join(SDK_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/, .$$[QT_INSTA diff -Nru qtav-1.12.0+ds/debian/patches/0004-Rename-qtav-private-dev.install-libqtav-private-dev..patch qtav-1.13.0+ds/debian/patches/0004-Rename-qtav-private-dev.install-libqtav-private-dev..patch --- qtav-1.12.0+ds/debian/patches/0004-Rename-qtav-private-dev.install-libqtav-private-dev..patch 2017-11-23 18:53:50.000000000 +0000 +++ qtav-1.13.0+ds/debian/patches/0004-Rename-qtav-private-dev.install-libqtav-private-dev..patch 2019-08-31 23:20:39.000000000 +0000 @@ -7,10 +7,10 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libQtAV.pro b/src/libQtAV.pro -index 29015ff..9b1ff97 100644 +index bf30abd..63b4a87 100644 --- a/src/libQtAV.pro +++ b/src/libQtAV.pro -@@ -634,7 +634,7 @@ target.depends *= $${qtav_dev.target} +@@ -641,7 +641,7 @@ target.depends *= $${qtav_dev.target} DEB_INSTALL_LIST = $$join(SDK_PRIVATE_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/QtAV/*/, .$$[QT_INSTALL_HEADERS]/QtAV/*/) DEB_INSTALL_LIST += .$${MKSPECS_DIR}/modules/qt_lib_av_private.pri diff -Nru qtav-1.12.0+ds/debian/patches/0005-Disable-warnings-with-Wno-expansion-to-defined-Wno-u.patch qtav-1.13.0+ds/debian/patches/0005-Disable-warnings-with-Wno-expansion-to-defined-Wno-u.patch --- qtav-1.12.0+ds/debian/patches/0005-Disable-warnings-with-Wno-expansion-to-defined-Wno-u.patch 2017-11-23 18:54:05.000000000 +0000 +++ qtav-1.13.0+ds/debian/patches/0005-Disable-warnings-with-Wno-expansion-to-defined-Wno-u.patch 2019-08-31 23:20:39.000000000 +0000 @@ -1,6 +1,7 @@ From: "Steve M. Robbins" Date: Sun, 27 Aug 2017 22:53:55 -0500 -Subject: Disable warnings with -Wno-expansion-to-defined -Wno-unused-parameter +Subject: Disable warnings with -Wno-expansion-to-defined + -Wno-unused-parameter --- src/libQtAV.pro | 1 + @@ -8,10 +9,10 @@ 2 files changed, 2 insertions(+) diff --git a/src/libQtAV.pro b/src/libQtAV.pro -index 9b1ff97..beaa28c 100644 +index 63b4a87..21bf5cc 100644 --- a/src/libQtAV.pro +++ b/src/libQtAV.pro -@@ -617,6 +617,7 @@ icon.files = $$PWD/$${TARGET}.svg +@@ -624,6 +624,7 @@ icon.files = $$PWD/$${TARGET}.svg icon.path = /usr/share/icons/hicolor/scalable/apps INSTALLS += icon #debian @@ -20,7 +21,7 @@ libqtav.target = libqtav1.install libqtav.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${libqtav.target} diff --git a/widgets/libQtAVWidgets.pro b/widgets/libQtAVWidgets.pro -index 3176d09..958b041 100644 +index c05f108..ad9c352 100644 --- a/widgets/libQtAVWidgets.pro +++ b/widgets/libQtAVWidgets.pro @@ -142,6 +142,7 @@ mac { diff -Nru qtav-1.12.0+ds/debian/rules qtav-1.13.0+ds/debian/rules --- qtav-1.12.0+ds/debian/rules 2017-11-10 21:27:00.000000000 +0000 +++ qtav-1.13.0+ds/debian/rules 2019-08-31 23:20:39.000000000 +0000 @@ -6,8 +6,7 @@ export DEB_LDFLAGS_MAINT_APPEND := -Wl,--as-needed %: - dh $@ --parallel --buildsystem qmake + dh $@ --buildsystem qmake override_dh_auto_configure: dh_auto_configure -Sqmake -- CONFIG+="no_rpath recheck config_libass_link config_openal_link no-tests" - diff -Nru qtav-1.12.0+ds/examples/CMakeLists.txt qtav-1.13.0+ds/examples/CMakeLists.txt --- qtav-1.12.0+ds/examples/CMakeLists.txt 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/CMakeLists.txt 2019-07-11 00:58:59.000000000 +0000 @@ -101,7 +101,7 @@ set(MODULE QMLPlayer) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) - configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) + configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_executable(QMLPlayer ${EXE_TYPE} QMLPlayer/main.cpp @@ -120,7 +120,7 @@ set(MODULE Player) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) - configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) + configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_executable(Player ${EXE_TYPE} ${PLAYER_SRC} ${PLAYER_HEADERS} ${PLAYER_RES} ${RC_FILE}) target_link_libraries(Player QtAVWidgets common) diff -Nru qtav-1.12.0+ds/examples/common/common.cpp qtav-1.13.0+ds/examples/common/common.cpp --- qtav-1.12.0+ds/examples/common/common.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/common/common.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -35,6 +35,7 @@ #include #endif #include +#include #ifdef Q_OS_WINRT #include @@ -98,8 +99,13 @@ return qInstallMsgHandler(MsgHandlerWrapper::handler); } #endif + +QMutex loggerMutex; void Logger(QtMsgType type, const QMessageLogContext &, const QString& qmsg) { + // QFile is not thread-safe + QMutexLocker locker(&loggerMutex); + const QByteArray msgArray = qmsg.toUtf8(); const char* msg = msgArray.constData(); switch (type) { diff -Nru qtav-1.12.0+ds/examples/common/Config.cpp qtav-1.13.0+ds/examples/common/Config.cpp --- qtav-1.12.0+ds/examples/common/Config.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/common/Config.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV Player Demo: this file is part of QtAV examples - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) @@ -304,7 +304,7 @@ setForceFrameRate(settings.value(QString::fromLatin1("force_fps"), 0.0).toReal()); settings.beginGroup(QString::fromLatin1("decoder")); settings.beginGroup(QString::fromLatin1("video")); - QString decs_default(QString::fromLatin1("FFmpeg")); + QString decs_default(QString::fromLatin1("HW FFmpeg")); // HW is ignored setDecoderPriorityNames(settings.value(QString::fromLatin1("priority"), decs_default).toString().split(QString::fromLatin1(" "), QString::SkipEmptyParts)); setZeroCopy(settings.value(QString::fromLatin1("zeroCopy"), true).toBool()); settings.endGroup(); //video diff -Nru qtav-1.12.0+ds/examples/common/ScreenSaver.cpp qtav-1.13.0+ds/examples/common/ScreenSaver.cpp --- qtav-1.12.0+ds/examples/common/ScreenSaver.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/common/ScreenSaver.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -21,8 +21,14 @@ static QLibrary xlib; #endif //Q_OS_LINUX #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) +#include +# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 //http://www.cocoachina.com/macdev/cocoa/2010/0201/453.html #include +# else +/* MAC OSX 10.8+ has deprecated the UpdateSystemActivity stuff in favor of a new API.. */ +# include +# endif #endif //Q_OS_MAC #ifdef Q_OS_WIN #include @@ -149,6 +155,7 @@ } #endif //Q_OS_LINUX ssTimerId = 0; + osxIOPMAssertionId = 0U; // mac >=10.8 only retrieveState(); } @@ -158,6 +165,13 @@ #ifdef Q_OS_LINUX if (xlib.isLoaded()) xlib.unload(); +#elif defined(Q_OS_MAC) && !defined(Q_OS_IOS) +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 + if (osxIOPMAssertionId) { + IOPMAssertionRelease((IOPMAssertionID)osxIOPMAssertionId); + osxIOPMAssertionId = 0U; + } +# endif #endif } @@ -310,7 +324,15 @@ if (e->timerId() != ssTimerId) return; #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) +# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 UpdateSystemActivity(OverallAct); +# else // OSX >= 10.8, use new API + IOPMAssertionID assertionId = osxIOPMAssertionId; + IOReturn r = IOPMAssertionDeclareUserActivity(CFSTR("QtAVScreenSaver"),kIOPMUserActiveLocal,&assertionId); + if (r == kIOReturnSuccess) { + osxIOPMAssertionId = assertionId; + } +# endif return; #endif //Q_OS_MAC #ifdef Q_OS_LINUX diff -Nru qtav-1.12.0+ds/examples/common/ScreenSaver.h qtav-1.13.0+ds/examples/common/ScreenSaver.h --- qtav-1.12.0+ds/examples/common/ScreenSaver.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/common/ScreenSaver.h 2019-07-11 00:58:59.000000000 +0000 @@ -33,6 +33,7 @@ int allowExposures; #endif //Q_OS_LINUX int ssTimerId; //for mac + quint32 osxIOPMAssertionId; // for mac OSX >= 10.8 }; #endif // SCREENSAVER_H diff -Nru qtav-1.12.0+ds/examples/player/EventFilter.cpp qtav-1.13.0+ds/examples/player/EventFilter.cpp --- qtav-1.12.0+ds/examples/player/EventFilter.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/player/EventFilter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -297,6 +297,21 @@ } } break; + + case QEvent::MouseButtonDblClick: { + QMouseEvent *me = static_cast(event); + Qt::MouseButton mbt = me->button(); + QWidget *mpWindow = static_cast(player->parent()); + if (mbt == Qt::LeftButton) { + if (Qt::WindowFullScreen ==mpWindow->windowState()){ + mpWindow->setWindowState(mpWindow->windowState() ^ Qt::WindowFullScreen); + }else{ + mpWindow->showFullScreen(); + } + } + break; + } + case QEvent::GraphicsSceneContextMenu: { QGraphicsSceneContextMenuEvent *e = static_cast(event); showMenu(e->screenPos()); @@ -346,6 +361,7 @@ } return false; } + if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *me = static_cast(event); Qt::MouseButton mbt = me->button(); diff -Nru qtav-1.12.0+ds/examples/player/ios/Info.plist qtav-1.13.0+ds/examples/player/ios/Info.plist --- qtav-1.12.0+ds/examples/player/ios/Info.plist 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/player/ios/Info.plist 2019-07-11 00:58:59.000000000 +0000 @@ -19,9 +19,9 @@ CFBundleName QtAV Player CFBundleShortVersionString - 1.12 + 1.13 CFBundleVersion - 1.12.0 + 1.13.0 LSRequiresIPhoneOS UIFileSharingEnabled diff -Nru qtav-1.12.0+ds/examples/player/MainWindow.cpp qtav-1.13.0+ds/examples/player/MainWindow.cpp --- qtav-1.12.0+ds/examples/player/MainWindow.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/player/MainWindow.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -111,6 +111,7 @@ , mpSubtitle(0) , m_preview(0) , m_shader(NULL) + , m_glsl(NULL) { #if defined(Q_OS_MACX) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QApplication::setStyle(QStyleFactory::create("Fusion")); @@ -549,6 +550,7 @@ connect(mpTimeSlider, SIGNAL(onLeave()), SLOT(onTimeSliderLeave())); connect(mpTimeSlider, SIGNAL(onHover(int,int)), SLOT(onTimeSliderHover(int,int))); connect(&Config::instance(), SIGNAL(userShaderEnabledChanged()), SLOT(onUserShaderChanged())); + connect(&Config::instance(), SIGNAL(intermediateFBOChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragHeaderChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragSampleChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragPostProcessChanged()), SLOT(onUserShaderChanged())); @@ -1325,7 +1327,7 @@ m_preview->preview(); const int w = Config::instance().previewWidth(); const int h = Config::instance().previewHeight(); - m_preview->setWindowFlags(m_preview->windowFlags() |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint); + m_preview->setWindowFlags(Qt::Tool |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint); m_preview->resize(w, h); m_preview->move(gpos - QPoint(w/2, h)); m_preview->show(); @@ -1333,8 +1335,18 @@ void MainWindow::onTimeSliderLeave() { - if (m_preview && m_preview->isVisible()) - m_preview->hide(); + /*if (m_preview && m_preview->isVisible()) + m_preview->hide();*/ + if (!m_preview) + { + return; + } + if (m_preview->isVisible()) + { + m_preview->close(); + } + delete m_preview; + m_preview = NULL; } void MainWindow::handleError(const AVError &e) @@ -1533,6 +1545,14 @@ return; #ifndef QT_NO_OPENGL if (Config::instance().userShaderEnabled()) { + if (Config::instance().intermediateFBO()) { + if (!m_glsl) + m_glsl = new GLSLFilter(this); + m_glsl->installTo(mpRenderer); + } else { + if (m_glsl) + m_glsl->uninstall(); + } if (!m_shader) m_shader = new DynamicShaderObject(this); m_shader->setHeader(Config::instance().fragHeader()); diff -Nru qtav-1.12.0+ds/examples/player/MainWindow.h qtav-1.13.0+ds/examples/player/MainWindow.h --- qtav-1.12.0+ds/examples/player/MainWindow.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/player/MainWindow.h 2019-07-11 00:58:59.000000000 +0000 @@ -39,6 +39,7 @@ class SubtitleFilter; class VideoPreviewWidget; class DynamicShaderObject; +class GLSLFilter; } QT_BEGIN_NAMESPACE class QMenu; @@ -213,6 +214,7 @@ QtAV::SubtitleFilter *mpSubtitle; QtAV::VideoPreviewWidget *m_preview; QtAV::DynamicShaderObject *m_shader; + QtAV::GLSLFilter *m_glsl; }; diff -Nru qtav-1.12.0+ds/examples/player/player_sdk.pro qtav-1.13.0+ds/examples/player/player_sdk.pro --- qtav-1.12.0+ds/examples/player/player_sdk.pro 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/player/player_sdk.pro 2019-07-11 00:58:59.000000000 +0000 @@ -31,7 +31,7 @@ RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org" - QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV $$1" export(RC_ICONS) export(QMAKE_TARGET_COMPANY) diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/android/AndroidManifest.xml qtav-1.13.0+ds/examples/QMLPlayer/android/AndroidManifest.xml --- qtav-1.12.0+ds/examples/QMLPlayer/android/AndroidManifest.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/android/AndroidManifest.xml 2019-07-11 00:58:59.000000000 +0000 @@ -1,5 +1,5 @@ - + @@ -48,7 +48,7 @@ - + + + Binary files /tmp/tmpfkt1lm2r/v3SM4BAUMf/qtav-1.12.0+ds/examples/QMLPlayer/android/libs/android-support-v4.jar and /tmp/tmpfkt1lm2r/H92y6_mT31/qtav-1.13.0+ds/examples/QMLPlayer/android/libs/android-support-v4.jar differ diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/android/src/org/qtav/qmlplayer/QMLPlayerActivity.java qtav-1.13.0+ds/examples/QMLPlayer/android/src/org/qtav/qmlplayer/QMLPlayerActivity.java --- qtav-1.12.0+ds/examples/QMLPlayer/android/src/org/qtav/qmlplayer/QMLPlayerActivity.java 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/android/src/org/qtav/qmlplayer/QMLPlayerActivity.java 2019-07-11 00:58:59.000000000 +0000 @@ -4,9 +4,14 @@ import android.content.Intent; import android.app.PendingIntent; import android.util.Log; +import android.os.Build; import android.os.Bundle; import android.view.WindowManager; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.support.v4.content.ContextCompat; +import android.support.v4.app.ActivityCompat; +import android.widget.Toast; public class QMLPlayerActivity extends QtActivity { @@ -23,6 +28,10 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!checkPermission()) + requestPermission(); + } //getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); Intent intent = getIntent(); @@ -35,4 +44,19 @@ } setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } + + protected boolean checkPermission() { + int result = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (result == PackageManager.PERMISSION_GRANTED) + return true; + return false; + } + protected void requestPermission() { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + Toast.makeText(this, "Please allow Write External Storage permission to play your local videos", Toast.LENGTH_LONG).show(); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); + } + } } diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/ios/Info.plist qtav-1.13.0+ds/examples/QMLPlayer/ios/Info.plist --- qtav-1.12.0+ds/examples/QMLPlayer/ios/Info.plist 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/ios/Info.plist 2019-07-11 00:58:59.000000000 +0000 @@ -19,9 +19,9 @@ CFBundleName ${PRODUCT_NAME} CFBundleShortVersionString - 1.12 + 1.13 CFBundleVersion - 1.12.0 + 1.13.0 LSRequiresIPhoneOS UIFileSharingEnabled diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/main.cpp qtav-1.13.0+ds/examples/QMLPlayer/main.cpp --- qtav-1.12.0+ds/examples/QMLPlayer/main.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/main.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -41,6 +41,7 @@ options.add(QLatin1String("QMLPlayer options")) ("scale", 1.0, QLatin1String("scale of graphics context. 0: auto")) ; + qputenv("QTAV_MEDIACODEC_KEY", ")A0aZ!WIgo2DdCsc#EnR?NAsFtWrnENONeeiiED^OM@6gI+Hew6s5)77p^>$K(Fe"); options.parse(argc, argv); Config::setName(QString::fromLatin1("QMLPlayer")); do_common_options_before_qapp(options); @@ -159,9 +160,12 @@ if (player && !file.isEmpty()) { if (!file.startsWith(QLatin1String("file:")) && QFile(file).exists()) file.prepend(QLatin1String("file:")); //qml use url and will add qrc: if no scheme +#ifdef Q_OS_WIN file.replace(QLatin1String("\\"), QLatin1String("/")); //qurl +#endif //QMetaObject::invokeMethod(player, "play", Q_ARG(QUrl, QUrl(file))); - player->setProperty("source", QUrl(file)); + QUrl url; + player->setProperty("source", QUrl(file.toUtf8().toPercentEncoding())); } #endif QObject::connect(&Config::instance(), SIGNAL(changed()), &Config::instance(), SLOT(save())); diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/qml/QMLPlayer/About.qml qtav-1.13.0+ds/examples/QMLPlayer/qml/QMLPlayer/About.qml --- qtav-1.12.0+ds/examples/QMLPlayer/qml/QMLPlayer/About.qml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/qml/QMLPlayer/About.qml 2019-07-11 00:58:59.000000000 +0000 @@ -19,10 +19,10 @@ font.pixelSize: Utils.scaled(15) onContentHeightChanged: parent.contentHeight = contentHeight + 2*anchors.margins onLinkActivated: Qt.openUrlExternally(link) - text: "

QMLPlayer for " + Qt.platform.os + " " + qsTr("based on") + " QtAV 1.11.0

" + text: "

QMLPlayer for " + Qt.platform.os + " " + qsTr("based on") + " QtAV 1.12.0

" + "

" + qsTr("QtAV is a cross platform, high performance multimedia framework") + "

" + "

Distributed under the terms of LGPLv2.1 or later.

" - + "

Copyright (C) 2012-2016 Wang Bin (aka. Lucas Wang) wbsecg1@gmail.com

" + + "

Copyright (C) 2012-2017 Wang Bin (aka. Lucas Wang) wbsecg1@gmail.com

" + qsTr("Home page") + ": http://qtav.org

" + "

"+qsTr("Install QtAV for Windows desktop/store, macOS, Linux and Android")+"

" + "\n

" + qsTr("Double click") + ": " + qsTr("show/hide control bar") + "

" diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/qml/QMLPlayer/VideoCodec.qml qtav-1.13.0+ds/examples/QMLPlayer/qml/QMLPlayer/VideoCodec.qml --- qtav-1.12.0+ds/examples/QMLPlayer/qml/QMLPlayer/VideoCodec.qml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/qml/QMLPlayer/VideoCodec.qml 2019-07-11 00:58:59.000000000 +0000 @@ -6,6 +6,7 @@ title: qsTr("Video Codec") height: titleHeight + detail.height + listView.height + copyMode.height + Utils.kSpacing*4 signal zeroCopyChanged(bool value) + property var defaultDecoders: [] QtObject { id: d @@ -49,6 +50,7 @@ ListModel { id: codecMode + ListElement { name: "Auto"; hardware: true; zcopy: true; description: qsTr("Try hardware decoders first") } ListElement { name: "FFmpeg"; hardware: false; zcopy: false; description: "FFmpeg/Libav" } } @@ -72,6 +74,11 @@ onStateChanged: { if (state != "selected") return + if (name === "Auto") { + PlayerConfig.decoderPriorityNames = defaultDecoders + d.detail = description + return + } d.detail = description + " " + (hardware ? qsTr("hardware decoding") : qsTr("software decoding")) if (name === "FFmpeg") { copyMode.visible = false @@ -95,23 +102,40 @@ } Component.onCompleted: { if (Qt.platform.os == "windows") { + defaultDecoders.push("DXVA") + defaultDecoders.push("D3D11") + defaultDecoders.push("CUDA") codecMode.append({ name: "DXVA", hardware: true, zcopy: true, description: "DirectX Video Acceleration (Windows)\nUse OpenGLES(ANGLE) + D3D to support 0-copy" }) codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration\n0-copy is supported under OpenGLES(ANGLE)" }) codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux).\nH264 10bit support."}) } else if (Qt.platform.os == "winrt" || Qt.platform.os == "winphone") { + defaultDecoders.push("D3D11") codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration" }) } else if (Qt.platform.os == "osx") { - codecMode.append({ name: "VDA", hardware: true, zcopy: true, description: "VDA (OSX)" }) + defaultDecoders.push("VideoToolbox") + defaultDecoders.push("VDA") codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (OSX)" }) + codecMode.append({ name: "VDA", hardware: true, zcopy: true, description: "VDA (OSX)" }) } else if (Qt.platform.os == "ios") { + defaultDecoders.append("VideoToolbox") codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (iOS)" }) } else if(Qt.platform.os == "android") { - codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android 5.0 MediaCodec (H.264)" }) + defaultDecoders.push("MediaCodec") + codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android(>=4.1) MediaCodec (H.264)" }) } else if (Qt.platform.os == "linux") { + defaultDecoders.push("VAAPI") + defaultDecoders.push("CUDA") codecMode.append({ name: "VAAPI", hardware: true, zcopy: true, description: "VA-API (Linux) " }) codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux)"}) } - for (var i = 0; i < codecMode.count; ++i) { + defaultDecoders.push("FFmpeg") + if (PlayerConfig.decoderPriorityNames.length > 1) { + listView.currentIndex = 0; + d.selectedItem = listView.currentItem + listView.currentItem.state = "selected" + return + } + for (var i = 1; i < codecMode.count; ++i) { if (codecMode.get(i).name === PlayerConfig.decoderPriorityNames[0]) { listView.currentIndex = i; d.selectedItem = listView.currentItem diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/QMLPlayer_sdk.pro qtav-1.13.0+ds/examples/QMLPlayer/QMLPlayer_sdk.pro --- qtav-1.12.0+ds/examples/QMLPlayer/QMLPlayer_sdk.pro 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/QMLPlayer_sdk.pro 2019-07-11 00:58:59.000000000 +0000 @@ -56,7 +56,7 @@ RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia playback framework. http://qtav.org" - QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV $$1" export(RC_ICONS) export(QMAKE_TARGET_COMPANY) diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/winrt/WinPhone8.Package.appxmanifest qtav-1.13.0+ds/examples/QMLPlayer/winrt/WinPhone8.Package.appxmanifest --- qtav-1.12.0+ds/examples/QMLPlayer/winrt/WinPhone8.Package.appxmanifest 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/winrt/WinPhone8.Package.appxmanifest 2019-07-11 00:58:59.000000000 +0000 @@ -4,7 +4,7 @@ Name=\"org.qtav.qmlplayer\" ProcessorArchitecture=\"$$lower($$VCPROJ_ARCH)\" Publisher=\"CN=930FB10A-C50C-4801-84BA-255229D27D44\" - Version=\"1.12.0.0\" /> + Version=\"1.13.0.0\" /> diff -Nru qtav-1.12.0+ds/examples/QMLPlayer/winrt/WinRT10.Package.appxmanifest qtav-1.13.0+ds/examples/QMLPlayer/winrt/WinRT10.Package.appxmanifest --- qtav-1.12.0+ds/examples/QMLPlayer/winrt/WinRT10.Package.appxmanifest 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/examples/QMLPlayer/winrt/WinRT10.Package.appxmanifest 2019-07-11 00:58:59.000000000 +0000 @@ -10,7 +10,7 @@ + Version=\"1.13.0.0\" /> QtAV Video Player Lucas Wang diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/main.cpp qtav-1.13.0+ds/examples/qmlvumeter/main.cpp --- qtav-1.12.0+ds/examples/qmlvumeter/main.cpp 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/main.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,17 @@ +#include "VUMeterFilter.h" + +#include +#include +#include +#include +#include +int main(int argc, char** argv) +{ + qmlRegisterType("com.qtav.vumeter", 1, 0, "VUMeterFilter"); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/main.qml qtav-1.13.0+ds/examples/qmlvumeter/main.qml --- qtav-1.12.0+ds/examples/qmlvumeter/main.qml 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/main.qml 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,49 @@ +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Dialogs 1.0 +import QtAV 1.7 +import com.qtav.vumeter 1.0 +Window { + visible: true + width: 640 + height: 480 + title: qsTr("QtAV VU Meter Filter. Press key 'O' to open a file") + + VUMeterFilter { + id: vu + } + + AudioFilter { + id: afilter + type: AudioFilter.UserFilter + userFilter: vu + } + + Video { + id: video + autoPlay: true + anchors.fill: parent + audioFilters: [afilter] + Text { + anchors.fill: parent + color: "white" + text: "dB left=" + vu.leftLevel + " right=" + vu.rightLevel + } + } + + Item { + anchors.fill: parent + focus: true + Keys.onPressed: { + switch (event.key) { + case Qt.Key_O: + fileDialog.open() + break + } + } + } + FileDialog { + id: fileDialog + onAccepted: video.source = fileUrl + } +} diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/qml.qrc qtav-1.13.0+ds/examples/qmlvumeter/qml.qrc --- qtav-1.12.0+ds/examples/qmlvumeter/qml.qrc 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/qml.qrc 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,5 @@ + + + main.qml + + diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/qmlvumeter.pro qtav-1.13.0+ds/examples/qmlvumeter/qmlvumeter.pro --- qtav-1.12.0+ds/examples/qmlvumeter/qmlvumeter.pro 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/qmlvumeter.pro 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,32 @@ +TEMPLATE = app +CONFIG -= app_bundle + +QT += av qml quick +CONFIG += c++11 + +HEADERS = VUMeterFilter.h +SOURCES = main.cpp VUMeterFilter.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/VUMeterFilter.cpp qtav-1.13.0+ds/examples/qmlvumeter/VUMeterFilter.cpp --- qtav-1.12.0+ds/examples/qmlvumeter/VUMeterFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/VUMeterFilter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,64 @@ +#include "VUMeterFilter.h" +#include +#include + +VUMeterFilter::VUMeterFilter(QObject *parent) + : AudioFilter(parent) + , mLeft(0) + , mRight(0) +{ +} + +void VUMeterFilter::process(Statistics *statistics, AudioFrame *frame) +{ + if (!frame) + return; + const AudioFormat& af = frame->format(); + int step = frame->channelCount(); + const quint8* data[2]; + data[0] = frame->constBits(0); + if (frame->planeCount() > 0) { + step = 1; + data[1] = frame->constBits(1); + } else { + data[1] = data[0] + step*af.sampleSize(); + } + const int s = frame->samplesPerChannel(); + const int r = 1; + float level[2]; + if (af.isFloat()) { + float max[2]; + max[0] = max[1] = 0; + const float *p0 = (float*)data[0]; + const float *p1 = (float*)data[1]; + for (int i = 0; i < s; i+=step) { + max[0] = qMax(max[0], qAbs(p0[i])); + max[1] = qMax(max[1], qAbs(p1[i])); + } + level[0] = 20.0f * log10(max[0]); + level[1] = 20.0f * log10(max[1]); + } else if (!af.isUnsigned()) { + const int b = af.sampleSize(); + if (b == 2) { + qint16 max[2]; + max[0] = max[1] = 0; + const qint16 *p0 = (qint16*)data[0]; + const qint16 *p1 = (qint16*)data[1]; + for (int i = 0; i < s; i+=step) { + max[0] = qMax(max[0], qAbs(p0[i])); + max[1] = qMax(max[1], qAbs(p1[i])); + } + level[0] = 20.0f * log10((double)max[0]/(double)INT16_MAX); + level[1] = 20.0f * log10((double)max[1]/(double)INT16_MAX); + } + } + if (!qFuzzyCompare(level[0], mLeft)) { + mLeft = level[0]; + Q_EMIT leftLevelChanged(mLeft); + } + if (!qFuzzyCompare(level[1], mRight)) { + mRight = level[1]; + Q_EMIT rightLevelChanged(mRight); + } + //qDebug("db: %d --- %d", mLeft, mRight); +} diff -Nru qtav-1.12.0+ds/examples/qmlvumeter/VUMeterFilter.h qtav-1.13.0+ds/examples/qmlvumeter/VUMeterFilter.h --- qtav-1.12.0+ds/examples/qmlvumeter/VUMeterFilter.h 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/examples/qmlvumeter/VUMeterFilter.h 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,27 @@ +#ifndef VUMeterFilter_H +#define VUMeterFilter_H +#include + +using namespace QtAV; +class VUMeterFilter : public AudioFilter +{ + Q_OBJECT + Q_PROPERTY(int leftLevel READ leftLevel WRITE setLeftLevel NOTIFY leftLevelChanged) + Q_PROPERTY(int rightLevel READ rightLevel WRITE setRightLevel NOTIFY rightLevelChanged) +public: + VUMeterFilter(QObject* parent = NULL); + int leftLevel() const {return mLeft;} + void setLeftLevel(int value) {mLeft = value;} + int rightLevel() const {return mRight;} + void setRightLevel(int value) {mRight = value;} +Q_SIGNALS: + void leftLevelChanged(int value); + void rightLevelChanged(int value); +protected: + void process(Statistics* statistics, AudioFrame* frame = 0) Q_DECL_OVERRIDE; +private: + int mLeft; + int mRight; +}; + +#endif // VUMeterFilter_H diff -Nru qtav-1.12.0+ds/.github/ISSUE_TEMPLATE qtav-1.13.0+ds/.github/ISSUE_TEMPLATE --- qtav-1.12.0+ds/.github/ISSUE_TEMPLATE 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/.github/ISSUE_TEMPLATE 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,17 @@ +### QtAV, Qt version and platform + +or commit id if not using release version + +### Reproduction steps + +### Expected behavior + +### Actual behavior + +### Log file + +set environment var `QTAV_LOG=all` or C++ api `QtAV::setLogLevel(All)` to enable log. + +For Player and QMLPlayer example app, choose log level `all` in config page. + +### Sample files (optional) diff -Nru qtav-1.12.0+ds/.gitignore qtav-1.13.0+ds/.gitignore --- qtav-1.12.0+ds/.gitignore 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/.gitignore 2019-07-11 00:58:59.000000000 +0000 @@ -1,9 +1,18 @@ CVS .#* +*.patch +*.diff +*.orig +#*.rej +contrib/include +*~ +*.mak + # Hidden files are ignored by default with exceptions .* !/.gitignore +!/.github !/.gitmodules !/.qmake.conf !/.travis.yml @@ -61,6 +70,14 @@ *.Debug *.Release +# Ignore temporary Python files +python/PyQtAV.api +python/PyQtAV.pro +python/QtAV.pyi +python/QtAVWidgets.pyi +python/QtAV +python/QtAVWidgets + *.fuse* #qt diff -Nru qtav-1.12.0+ds/.gitmodules qtav-1.13.0+ds/.gitmodules --- qtav-1.12.0+ds/.gitmodules 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/.gitmodules 2019-07-11 00:58:59.000000000 +0000 @@ -7,3 +7,6 @@ [submodule "contrib/uchardet"] path = contrib/uchardet url = https://github.com/BYVoid/uchardet.git +[submodule "src/jmi"] + path = src/jmi + url = https://github.com/wang-bin/JMI.git diff -Nru qtav-1.12.0+ds/python/configure.py qtav-1.13.0+ds/python/configure.py --- qtav-1.12.0+ds/python/configure.py 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/configure.py 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,1915 @@ +# Copyright (c) 2017, Riverbank Computing Limited +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# This is v2.3 of this boilerplate. + + +from distutils import sysconfig +import glob +import os +import optparse +import sys + + +############################################################################### +# You shouldn't need to modify anything above this line. +############################################################################### + + +class QtAVConfiguration(object): + """ This class encapsulates all the module specific information needed by + the rest of this script to implement a configure.py script for modules that + build on top of PyQt. Functions implemented by the rest of this script + that begin with an underscore are considered internal and shouldn't be + called from here. + """ + + # The name of the module as it would be used in an import statement. + name = 'QtAV' + + # Set if support for C++ exceptions can be disabled. + no_exceptions = True + + # The name (without the .pyi extension) of the name of the PEP 484 stub + # file to be generated. If it is None or an empty string then a stub file + # is not generated. + pep484_stub_file = 'QtAV' + + @staticmethod + def get_sip_file(target_configuration): + """ Return the name of the module's .sip file. target_configuration is + the target configuration. + """ + + return 'sip/QtAV/QtAVmod.sip' + + @staticmethod + def get_sip_installs(target_configuration): + """ Return a tuple of the installation directory of the module's .sip + files and a sequence of the names of each of the .sip files relative to + the directory containing this configuration script. None is returned + if the module's .sip files are not to be installed. + target_configuration is the target configuration. + """ + + if target_configuration.qtav_sip_dir == '': + return None + + path = os.path.join(target_configuration.qtav_sip_dir, 'QtAV') + files = glob.glob('sip/QtAV/*.sip') + + return path, files + + @staticmethod + def get_qmake_configuration(target_configuration): + """ Return a dict of qmake configuration values for CONFIG, DEFINES, + INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either + 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding + version of Qt. target_configuration is the target configuration. + """ + libs = r"-L%s/lib " % (target_configuration.qtav_base_dir) + if sys.platform == "win32": + libs += "-lQtAV1 -lQtAVWidgets1" + else: + libs += "-lQtAV -lQtAVWidgets" + + return {'QT': 'widgets opengl', + 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"), + 'LIBS': libs} + + @staticmethod + def get_mac_wrapped_library_file(target_configuration): + """ Return the full pathname of the file that implements the library + being wrapped by the module as it would be called on OS/X so that the + module will reference it explicitly without DYLD_LIBRARY_PATH being + set. If it is None or an empty string then the default is used. + target_configuration is the target configuration. + """ + + return None + + +class QtAVWidgetsConfiguration(object): + """ This class encapsulates all the module specific information needed by + the rest of this script to implement a configure.py script for modules that + build on top of PyQt. Functions implemented by the rest of this script + that begin with an underscore are considered internal and shouldn't be + called from here. + """ + + # The name of the module as it would be used in an import statement. + name = 'QtAVWidgets' + + # Set if support for C++ exceptions can be disabled. + no_exceptions = True + + # The name (without the .pyi extension) of the name of the PEP 484 stub + # file to be generated. If it is None or an empty string then a stub file + # is not generated. + pep484_stub_file = 'QtAVWidgets' + + @staticmethod + def get_sip_file(target_configuration): + """ Return the name of the module's .sip file. target_configuration is + the target configuration. + """ + + return 'sip/QtAVWidgets/QtAVWidgetsmod.sip' + + @staticmethod + def get_sip_installs(target_configuration): + """ Return a tuple of the installation directory of the module's .sip + files and a sequence of the names of each of the .sip files relative to + the directory containing this configuration script. None is returned + if the module's .sip files are not to be installed. + target_configuration is the target configuration. + """ + + if target_configuration.qtav_sip_dir == '': + return None + + path = os.path.join(target_configuration.qtav_sip_dir, 'QtAVWidgets') + files = glob.glob('sip/QtAVWidgets/*.sip') + + return path, files + + @staticmethod + def get_qmake_configuration(target_configuration): + """ Return a dict of qmake configuration values for CONFIG, DEFINES, + INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either + 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding + version of Qt. target_configuration is the target configuration. + """ + libs = r"-L%s/lib " % (target_configuration.qtav_base_dir) + if sys.platform == "win32": + libs += "-lQtAV1 -lQtAVWidgets1" + else: + libs += "-lQtAV -lQtAVWidgets" + + return {'QT': 'widgets opengl', + 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"), + 'LIBS': libs} + + @staticmethod + def get_mac_wrapped_library_file(target_configuration): + """ Return the full pathname of the file that implements the library + being wrapped by the module as it would be called on OS/X so that the + module will reference it explicitly without DYLD_LIBRARY_PATH being + set. If it is None or an empty string then the default is used. + target_configuration is the target configuration. + """ + + return None + + +class PackageConfiguration(object): + """ This class encapsulates all the package specific information needed by + the rest of this script to implement a configure.py script for modules that + build on top of PyQt. Functions implemented by the rest of this script + that begin with an underscore are considered internal and shouldn't be + called from here. + """ + + # The descriptive name of the module. This is used in help text and error + # messages. + descriptive_name = "PyQtAV" + + # The version of the module as a string. Set it to None if you don't + # provide version information. + version = '1.12.0' + + # The sequence of module configurations that make up the package. + modules = (QtAVConfiguration(), QtAVWidgetsConfiguration()) + + # Set if a configuration script is provided that handles versions of PyQt4 + # prior to v4.10 (i.e. versions where the pyqtconfig.py module is + # available). If provided the script must be called configure-old.py and + # be in the same directory as this script. + legacy_configuration_script = False + + # The minimum version of SIP that is required. This should be a + # dot-separated string of two or three integers (e.g. '1.0', '4.10.3'). If + # it is None or an empty string then the version is not checked. + minimum_sip_version = '4.19.1' + + # Set if the module supports redefining 'protected' as 'public'. + protected_is_public_is_supported = True + + # Set if the module supports PyQt4. + pyqt4_is_supported = False + + # Set if the module supports PyQt5. + pyqt5_is_supported = True + + # Set if the PyQt5 support is the default. It is ignored unless both + # 'pyqt4_is_supported' and 'pyqt5_is_supported' are set. + pyqt5_is_default = True + + # The name (without the .api extension) of the name of the QScintilla API + # file to be generated. If it is None or an empty string then an API file + # is not generated. + qscintilla_api_file = 'PyQtAV' + + # The email address that will be included when an error in the script is + # detected. Leave it blank if you don't want to include an address. + support_email_address = 'support@riverbankcomputing.com' + + # Set if the user can provide a configuration file. It is normally only + # used if cross-compilation is supported. + user_configuration_file_is_supported = True + + # Set if the user is allowed to pass PyQt sip flags on the command line. + # It is normally only used if cross-compilation is supported. It is + # ignored unless at least one of 'pyqt4_is_supported' or + # 'pyqt5_is_supported' is set. + user_pyqt_sip_flags_is_supported = True + + @staticmethod + def init_target_configuration(target_configuration): + """ Perform any module specific initialisation of the target + target configuration. Typically this is the initialisation of module + specific attributes. To avoid name clashes attributes should be given + a module specific prefix. target_configuration is the target + configuration. + """ + + target_configuration.qtav_version = None + target_configuration.qtav_base_dir = None + target_configuration.qtav_sip_dir = None + + @staticmethod + def init_optparser(optparser, target_configuration): + """ Perform any module specific initialisation of the command line + option parser. To avoid name clashes destination attributes should be + given a module specific prefix. optparser is the option parser. + target_configuration is the target configuration. + """ + + optparser.add_option('--qtav-version', dest='qtav_version', + type='string', metavar="VERSION", + help="the QtAV version number (eg. 1.12.0)") + + optparser.add_option('--qtav-base-dir', '-v', + dest='qtav_base_dir', type='string', default=None, + action='callback', callback=optparser_store_abspath_dir, + metavar="DIR", + help="the base directory that contains QtAV installation ") + + optparser.add_option("--no-sip-files", action="store_true", + default=False, dest="qtav_no_sip_files", + help="disable the installation of the .sip files " + "[default: enabled]") + + @staticmethod + def apply_options(target_configuration, options): + """ Apply the module specific command line options to the target + configuration. target_configuration is the target configuration. + options are the parsed options. + """ + + # Historically Digia have been incapable of remembering to update the + # version number in a new release of add-on libraries so we allow the + # user to specify it while defaulting to the Qt version. + qtav_version_str = options.qtav_version + if qtav_version_str is None: + qtav_version_str = target_configuration.qt_version_str + + target_configuration.qtav_version = version_from_string( + qtav_version_str) + + if target_configuration.qtav_version is None: + error("%s is not a valid QtAV version." % qtav_version_str) + + if target_configuration.qtav_version >= 0x020000 and target_configuration.pyqt_package != 'PyQt5': + error("QtAV v%s requires PyQt5." % options.qtav_version) + + if options.qtav_base_dir is not None: + target_configuration.qtav_base_dir = options.qtav_base_dir + else: + error("--qtav-base-dir must be spcified") + + if options.qtav_no_sip_files: + target_configuration.qtav_sip_dir = '' + else: + target_configuration.qtav_sip_dir = os.path.join(target_configuration.qtav_base_dir, 'share', 'sip') + + @staticmethod + def check_package(target_configuration): + """ Perform any package specific checks now that the target + configuration is complete. target_configuration is the target + configuration. + """ + + # Nothing to do. + + @staticmethod + def inform_user(target_configuration): + """ Inform the user about module specific configuration information. + target_configuration is the target configuration. + """ + + major = (target_configuration.qtav_version >> 16) & 0xff + minor = (target_configuration.qtav_version >> 8) & 0xff + patch = target_configuration.qtav_version & 0xff + version = '%d.%d.%d' % (major, minor, patch) + + inform("QtAV %s is being used." % version) + + if target_configuration.qtav_sip_dir != '': + inform( + "The QtAV .sip files will be installed in %s." % + target_configuration.qtav_sip_dir) + + @staticmethod + def pre_code_generation(target_config): + """ Perform any module specific initialisation prior to generating the + code. target_config is the target configuration. + """ + + # Nothing to do. + + @staticmethod + def get_sip_flags(target_configuration): + """ Return the list of module-specific flags to pass to SIP. + target_configuration is the target configuration. + """ + + major = (target_configuration.qtav_version >> 16) & 0xff + minor = (target_configuration.qtav_version >> 8) & 0xff + patch = target_configuration.qtav_version & 0xff + version_tag = 'QtAV_%d_%d_%d' % (major, minor, patch) + + return ['-t', version_tag] + + +############################################################################### +# You shouldn't need to modify anything below this line. +############################################################################### + + +def error(msg): + """ Display an error message and terminate. msg is the text of the error + message. + """ + + sys.stderr.write(_format("Error: " + msg) + "\n") + sys.exit(1) + + +def inform(msg): + """ Display an information message. msg is the text of the error message. + """ + + sys.stdout.write(_format(msg) + "\n") + + +def quote(path): + """ Return a path with quotes added if it contains spaces. path is the + path. + """ + + if ' ' in path: + path = '"%s"' % path + + return path + + +def optparser_store_abspath(option, opt_str, value, parser): + """ An optparser callback that saves an option as an absolute pathname. """ + + setattr(parser.values, option.dest, os.path.abspath(value)) + + +def optparser_store_abspath_dir(option, opt_str, value, parser): + """ An optparser callback that saves an option as the absolute pathname + of an existing directory. + """ + + if not os.path.isdir(value): + raise optparse.OptionValueError("'%s' is not a directory" % value) + + setattr(parser.values, option.dest, os.path.abspath(value)) + + +def optparser_store_abspath_exe(option, opt_str, value, parser): + """ An optparser callback that saves an option as the absolute pathname + of an existing executable. + """ + + if not os.access(value, os.X_OK): + raise optparse.OptionValueError("'%s' is not an executable" % value) + + setattr(parser.values, option.dest, os.path.abspath(value)) + + +def read_define(filename, define): + """ Read the value of a #define from a file. filename is the name of the + file. define is the name of the #define. None is returned if there was no + such #define. + """ + + f = open(filename) + + for l in f: + wl = l.split() + if len(wl) >= 3 and wl[0] == "#define" and wl[1] == define: + # Take account of embedded spaces. + value = ' '.join(wl[2:])[1:-1] + break + else: + value = None + + f.close() + + return value + + +def version_from_string(version_str): + """ Convert a version string of the form m, m.n or m.n.o to an encoded + version number (or None if it was an invalid format). version_str is the + version string. + """ + + parts = version_str.split('.') + if not isinstance(parts, list): + return None + + if len(parts) == 1: + parts.append('0') + + if len(parts) == 2: + parts.append('0') + + if len(parts) != 3: + return None + + version = 0 + + for part in parts: + try: + v = int(part) + except ValueError: + return None + + version = (version << 8) + v + + return version + + +def _format(msg, left_margin=0, right_margin=78): + """ Format a message by inserting line breaks at appropriate places. msg + is the text of the message. left_margin is the position of the left + margin. right_margin is the position of the right margin. Returns the + formatted message. + """ + + curs = left_margin + fmsg = " " * left_margin + + for w in msg.split(): + l = len(w) + if curs != left_margin and curs + l > right_margin: + fmsg = fmsg + "\n" + (" " * left_margin) + curs = left_margin + + if curs > left_margin: + fmsg = fmsg + " " + curs = curs + 1 + + fmsg = fmsg + w + curs = curs + l + + return fmsg + + +class _ConfigurationFileParser: + """ A parser for configuration files. """ + + def __init__(self, config_file): + """ Read and parse a configuration file. """ + + self._config = {} + self._extrapolating = [] + + cfg = open(config_file) + line_nr = 0 + last_name = None + + section = '' + section_config = {} + self._config[section] = section_config + + for l in cfg: + line_nr += 1 + + # Strip comments. + l = l.split('#')[0] + + # See if this might be part of a multi-line. + multiline = (last_name is not None and len(l) != 0 and l[0] == ' ') + + l = l.strip() + + if l == '': + last_name = None + continue + + # See if this is a new section. + if l[0] == '[' and l[-1] == ']': + section = l[1:-1].strip() + if section == '': + error( + "%s:%d: Empty section name." % ( + config_file, line_nr)) + + if section in self._config: + error( + "%s:%d: Section '%s' defined more than once." % ( + config_file, line_nr, section)) + + section_config = {} + self._config[section] = section_config + + last_name = None + continue + + parts = l.split('=', 1) + if len(parts) == 2: + name = parts[0].strip() + value = parts[1].strip() + elif multiline: + name = last_name + value = section_config[last_name] + value += ' ' + l + else: + name = value = '' + + if name == '' or value == '': + error("%s:%d: Invalid line." % (config_file, line_nr)) + + section_config[name] = value + last_name = name + + cfg.close() + + def sections(self): + """ Return the list of sections, excluding the default one. """ + + return [s for s in self._config.keys() if s != ''] + + def preset(self, name, value): + """ Add a preset value to the configuration. """ + + self._config[''][name] = value + + def get(self, section, name, default=None): + """ Get a configuration value while extrapolating. """ + + # Get the name from the section, or the default section. + value = self._config[section].get(name) + if value is None: + value = self._config[''].get(name) + if value is None: + if default is None: + error( + "Configuration file references non-existent name " + "'%s'." % name) + + return default + + # Handle any extrapolations. + parts = value.split('%(', 1) + while len(parts) == 2: + prefix, tail = parts + + parts = tail.split(')', 1) + if len(parts) != 2: + error( + "Configuration file contains unterminated " + "extrapolated name '%s'." % tail) + + xtra_name, suffix = parts + + if xtra_name in self._extrapolating: + error( + "Configuration file contains a recursive reference to " + "'%s'." % xtra_name) + + self._extrapolating.append(xtra_name) + xtra_value = self.get(section, xtra_name) + self._extrapolating.pop() + + value = prefix + xtra_value + suffix + + parts = value.split('%(', 1) + + return value + + def getboolean(self, section, name, default): + """ Get a boolean configuration value while extrapolating. """ + + value = self.get(section, name, default) + + # In case the default was returned. + if isinstance(value, bool): + return value + + if value in ('True', 'true', '1'): + return True + + if value in ('False', 'false', '0'): + return False + + error( + "Configuration file contains invalid boolean value for " + "'%s'." % name) + + def getlist(self, section, name, default): + """ Get a list configuration value while extrapolating. """ + + value = self.get(section, name, default) + + # In case the default was returned. + if isinstance(value, list): + return value + + return value.split() + + +class _HostPythonConfiguration: + """ A container for the host Python configuration. """ + + def __init__(self): + """ Initialise the configuration. """ + + self.platform = sys.platform + self.version = sys.hexversion >> 8 + + self.inc_dir = sysconfig.get_python_inc() + self.venv_inc_dir = sysconfig.get_python_inc(prefix=sys.prefix) + self.module_dir = sysconfig.get_python_lib(plat_specific=1) + self.debug = hasattr(sys, 'gettotalrefcount') + + if sys.platform == 'win32': + try: + # Python v3.3 and later. + base_prefix = sys.base_prefix + + except AttributeError: + try: + # virtualenv for Python v2. + base_prefix = sys.real_prefix + + except AttributeError: + # We can't detect the base prefix in Python v3 prior to + # v3.3. + base_prefix = sys.prefix + + self.data_dir = sys.prefix + self.lib_dir = base_prefix + '\\libs' + else: + self.data_dir = sys.prefix + '/share' + self.lib_dir = sys.prefix + '/lib' + + +class _TargetQtConfiguration: + """ A container for the target Qt configuration. """ + + def __init__(self, qmake): + """ Initialise the configuration. qmake is the full pathname of the + qmake executable that will provide the configuration. + """ + + pipe = os.popen(quote(qmake) + ' -query') + + for l in pipe: + l = l.strip() + + tokens = l.split(':', 1) + if isinstance(tokens, list): + if len(tokens) != 2: + error("Unexpected output from qmake: '%s'\n" % l) + + name, value = tokens + else: + name = tokens + value = None + + name = name.replace('/', '_') + + setattr(self, name, value) + + pipe.close() + + +class _TargetConfiguration: + """ A container for the target configuration. """ + + def __init__(self, pkg_config): + """ Initialise the configuration with default values. pkg_config is + the package configuration. + """ + + # Values based on the host Python configuration. + py_config = _HostPythonConfiguration() + self.py_debug = py_config.debug + self.py_platform = py_config.platform + self.py_version = py_config.version + self.py_module_dir = py_config.module_dir + self.py_inc_dir = py_config.inc_dir + self.py_venv_inc_dir = py_config.venv_inc_dir + self.py_pylib_dir = py_config.lib_dir + self.py_sip_dir = os.path.join(py_config.data_dir, 'sip') + self.sip_inc_dir = py_config.venv_inc_dir + + # Remaining values. + self.debug = False + self.pyqt_sip_flags = None + self.pyqt_version_str = '' + self.qmake = self._find_exe('qmake') + self.qmake_spec = '' + self.qt_version = 0 + self.qt_version_str = '' + self.sip = self._find_exe('sip5', 'sip') + self.sip_version = None + self.sip_version_str = None + self.sysroot = '' + self.stubs_dir = '' + + self.prot_is_public = (self.py_platform.startswith('linux') or self.py_platform == 'darwin') + + if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: + pyqt = 'PyQt5' if pkg_config.pyqt5_is_default else 'PyQt4' + elif pkg_config.pyqt5_is_supported and not pkg_config.pyqt4_is_supported: + pyqt = 'PyQt5' + elif not pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: + pyqt = 'PyQt4' + else: + pyqt = None + + if pyqt is not None: + self.module_dir = os.path.join(py_config.module_dir, pyqt) + self.pyqt_sip_dir = os.path.join(self.py_sip_dir, pyqt) + else: + self.module_dir = py_config.module_dir + self.pyqt_sip_dir = None + + self.pyqt_package = pyqt + + pkg_config.init_target_configuration(self) + + def update_from_configuration_file(self, config_file): + """ Update the configuration with values from a file. config_file + is the name of the configuration file. + """ + + inform("Reading configuration from %s..." % config_file) + + parser = _ConfigurationFileParser(config_file) + + # Populate some presets from the command line. + parser.preset('py_major', str(self.py_version >> 16)) + parser.preset('py_minor', str((self.py_version >> 8) & 0xff)) + parser.preset('sysroot', self.sysroot) + + if self.pyqt_package is None: + section = '' + else: + # At the moment we only need to distinguish between PyQt4 and + # PyQt5. If that changes we may need a --target-pyqt-version + # command line option. + pyqt_version = 0x050000 if self.pyqt_package == 'PyQt5' else 0x040000 + + # Find the section corresponding to the version of PyQt. + section = None + latest_section = -1 + + for name in parser.sections(): + parts = name.split() + if len(parts) != 2 or parts[0] != 'PyQt': + continue + + section_pyqt_version = version_from_string(parts[1]) + if section_pyqt_version is None: + continue + + # Major versions must match. + if section_pyqt_version >> 16 != pyqt_version >> 16: + continue + + # It must be no later that the version of PyQt. + if section_pyqt_version > pyqt_version: + continue + + # Save it if it is the latest so far. + if section_pyqt_version > latest_section: + section = name + latest_section = section_pyqt_version + + if section is None: + error( + "%s does not define a section that covers PyQt " + "v%s." % (config_file, self.pyqt_version_str)) + + self.py_platform = parser.get(section, 'py_platform', self.py_platform) + self.py_inc_dir = parser.get(section, 'py_inc_dir', self.py_inc_dir) + self.py_venv_inc_dir = self.py_inc_dir + self.py_pylib_dir = parser.get(section, 'py_pylib_dir', + self.py_pylib_dir) + + self.sip_inc_dir = self.py_venv_inc_dir + + self.module_dir = parser.get(section, 'module_dir', self.module_dir) + + if self.pyqt_package is not None: + self.py_sip_dir = parser.get(section, 'py_sip_dir', + self.py_sip_dir) + + # Construct the SIP flags. + flags = [] + + flags.append('-t') + flags.append(self._get_platform_tag()) + + if self.pyqt_package == 'PyQt5': + if self.qt_version < 0x050000: + error("PyQt5 requires Qt v5.0 or later.") + + if self.qt_version > 0x060000: + self.qt_version = 0x060000 + else: + if self.qt_version > 0x050000: + self.qt_version = 0x050000 + + major = (self.qt_version >> 16) & 0xff + minor = (self.qt_version >> 8) & 0xff + patch = self.qt_version & 0xff + + flags.append('-t') + flags.append('Qt_%d_%d_%d' % (major, minor, patch)) + + for feat in parser.getlist(section, 'pyqt_disabled_features', []): + flags.append('-x') + flags.append(feat) + + self.pyqt_sip_flags = ' '.join(flags) + + def _get_platform_tag(self): + """ Return the tag for the target platform. """ + + # This replicates the logic in PyQt's configure scripts. + if self.py_platform == 'win32': + plattag = 'WS_WIN' + elif self.py_platform == 'darwin': + plattag = 'WS_MACX' + else: + plattag = 'WS_X11' + + return plattag + + def introspect_pyqt(self, pkg_config): + """ Introspect PyQt to determine the sip flags required. pkg_config + is the package configuration. + """ + + if self.pyqt_package == 'PyQt5': + try: + from PyQt5 import QtCore + except ImportError: + error( + "Unable to import PyQt5.QtCore. Make sure PyQt5 is " + "installed.") + else: + try: + from PyQt4 import QtCore + except ImportError: + error( + "Unable to import PyQt4.QtCore. Make sure PyQt4 is " + "installed.") + + self.pyqt_version_str = QtCore.PYQT_VERSION_STR + self.qt_version_str = QtCore.qVersion() + + # See if we have a PyQt that embeds its configuration. + try: + pyqt_config = QtCore.PYQT_CONFIGURATION + except AttributeError: + pyqt_config = None + + if pyqt_config is None: + if pkg_config.legacy_configuration_script: + # Fallback to the old configuration script. + config_script = sys.argv[0].replace('configure', 'configure-old') + args = [sys.executable, config_script] + sys.argv[1:] + + try: + os.execv(sys.executable, args) + except OSError: + pass + + error("Unable to execute '%s'" % config_script) + + error("PyQt v4.10 or later is required.") + + self.pyqt_sip_flags = pyqt_config['sip_flags'] + + def apply_sysroot(self): + """ Apply sysroot where necessary. """ + + if self.sysroot != '': + self.py_inc_dir = self._apply_sysroot(self.py_inc_dir) + self.py_venv_inc_dir = self._apply_sysroot(self.py_venv_inc_dir) + self.py_pylib_dir = self._apply_sysroot(self.py_pylib_dir) + self.py_sip_dir = self._apply_sysroot(self.py_sip_dir) + self.module_dir = self._apply_sysroot(self.module_dir) + self.sip_inc_dir = self._apply_sysroot(self.sip_inc_dir) + + def _apply_sysroot(self, dir_name): + """ Replace any leading sys.prefix of a directory name with sysroot. + """ + + if dir_name.startswith(sys.prefix): + dir_name = self.sysroot + dir_name[len(sys.prefix):] + + return dir_name + + def get_qt_configuration(self, opts): + """ Get the Qt configuration that can be extracted from qmake. opts + are the command line options. + """ + + # Query qmake. + qt_config = _TargetQtConfiguration(self.qmake) + + self.qt_version_str = getattr(qt_config, 'QT_VERSION', '') + self.qt_version = version_from_string(self.qt_version_str) + if self.qt_version is None: + error("Unable to determine the version of Qt.") + + # On Windows for Qt versions prior to v5.9.0 we need to be explicit + # about the qmake spec. + if self.qt_version < 0x050900 and self.py_platform == 'win32': + if self.py_version >= 0x030500: + self.qmake_spec = 'win32-msvc2015' + elif self.py_version >= 0x030300: + self.qmake_spec = 'win32-msvc2010' + elif self.py_version >= 0x020600: + self.qmake_spec = 'win32-msvc2008' + elif self.py_version >= 0x020400: + self.qmake_spec = 'win32-msvc.net' + else: + self.qmake_spec = 'win32-msvc' + else: + # Otherwise use the default. + self.qmake_spec = '' + + # The binary MacOS/X Qt installer used to default to XCode. If so then + # use macx-clang (Qt v5) or macx-g++ (Qt v4). + if sys.platform == 'darwin': + try: + # Qt v5. + if qt_config.QMAKE_SPEC == 'macx-xcode': + # This will exist (and we can't check anyway). + self.qmake_spec = 'macx-clang' + else: + # No need to explicitly name the default. + self.qmake_spec = '' + except AttributeError: + # Qt v4. + self.qmake_spec = 'macx-g++' + + self.api_dir = os.path.join(qt_config.QT_INSTALL_DATA, 'qsci') + self.qt_inc_dir = qt_config.QT_INSTALL_HEADERS + self.qt_lib_dir = qt_config.QT_INSTALL_LIBS + + if self.sysroot == '': + self.sysroot = getattr(qt_config, 'QT_SYSROOT', '') + + def apply_pre_options(self, opts): + """ Apply options from the command line that influence subsequent + configuration. opts are the command line options. + """ + + # On Windows the interpreter must be a debug build if a debug version + # is to be built and vice versa. + if sys.platform == 'win32': + if opts.debug: + if not self.py_debug: + error( + "A debug version of Python must be used when " + "--debug is specified.") + elif self.py_debug: + error( + "--debug must be specified when a debug version of " + "Python is used.") + + self.debug = opts.debug + + # Get the system root. + if opts.sysroot is not None: + self.sysroot = opts.sysroot + + # Determine how to run qmake. + if opts.qmake is not None: + self.qmake = opts.qmake + + # On Windows add the directory that probably contains the Qt DLLs + # to PATH. + if sys.platform == 'win32': + path = os.environ['PATH'] + path = os.path.dirname(self.qmake) + ';' + path + os.environ['PATH'] = path + + if self.qmake is None: + error( + "Use the --qmake argument to explicitly specify a working " + "Qt qmake.") + + if opts.qmakespec is not None: + self.qmake_spec = opts.qmakespec + + if self.pyqt_package is not None: + try: + self.pyqt_package = opts.pyqt_package + except AttributeError: + # Multiple PyQt versions are not supported. + pass + + self.module_dir = os.path.join(self.py_module_dir, + self.pyqt_package) + + def apply_post_options(self, opts, pkg_config): + """ Apply options from the command line that override the previous + configuration. opts are the command line options. pkg_config is the + package configuration. + """ + + if self.pyqt_package is not None: + if pkg_config.user_pyqt_sip_flags_is_supported: + if opts.pyqt_sip_flags is not None: + self.pyqt_sip_flags = opts.pyqt_sip_flags + + if opts.pyqt_sip_dir is not None: + self.pyqt_sip_dir = opts.pyqt_sip_dir + else: + self.pyqt_sip_dir = os.path.join(self.py_sip_dir, + self.pyqt_package) + + if _has_stubs(pkg_config): + if opts.stubsdir is not None: + self.stubs_dir = opts.stubsdir + + if opts.no_stubs: + self.stubs_dir = '' + elif self.stubs_dir == '': + self.stubs_dir = self.module_dir + + if pkg_config.qscintilla_api_file: + if opts.apidir is not None: + self.api_dir = opts.apidir + + if opts.no_qsci_api: + self.api_dir = '' + + if opts.destdir is not None: + self.module_dir = opts.destdir + + if pkg_config.protected_is_public_is_supported: + if opts.prot_is_public is not None: + self.prot_is_public = opts.prot_is_public + else: + self.prot_is_public = False + + if opts.sip_inc_dir is not None: + self.sip_inc_dir = opts.sip_inc_dir + + if opts.sip is not None: + self.sip = opts.sip + + pkg_config.apply_options(self, opts) + + @staticmethod + def _find_exe(*exes): + """ Find an executable, ie. the first on the path. """ + + path_dirs = os.environ.get('PATH', '').split(os.pathsep) + + for exe in exes: + # Strip any surrounding quotes. + if exe.startswith('"') and exe.endswith('"'): + exe = exe[1:-1] + + if sys.platform == 'win32': + exe = exe + '.exe' + + for d in path_dirs: + exe_path = os.path.join(d, exe) + + if os.access(exe_path, os.X_OK): + return exe_path + + return None + + +def _create_optparser(target_config, pkg_config): + """ Create the parser for the command line. target_config is the target + configuration containing default values. pkg_config is the package + configuration. + """ + + pkg_name = pkg_config.descriptive_name + + p = optparse.OptionParser(usage="python %prog [options]", + version=pkg_config.version) + + p.add_option('--spec', dest='qmakespec', default=None, action='store', + metavar="SPEC", + help="pass -spec SPEC to qmake") + + if _has_stubs(pkg_config): + p.add_option('--stubsdir', dest='stubsdir', type='string', + default=None, action='callback', + callback=optparser_store_abspath, metavar="DIR", + help="the PEP 484 stubs will be installed in DIR [default: " + "with the module]") + p.add_option('--no-stubs', dest='no_stubs', default=False, + action='store_true', + help="disable the installation of the PEP 484 stubs " + "[default: enabled]") + + if pkg_config.qscintilla_api_file: + p.add_option('--apidir', '-a', dest='apidir', type='string', + default=None, action='callback', + callback=optparser_store_abspath, metavar="DIR", + help="the QScintilla API file will be installed in DIR " + "[default: QT_INSTALL_DATA/qsci]") + p.add_option('--no-qsci-api', dest='no_qsci_api', default=False, + action='store_true', + help="disable the installation of the QScintilla API file " + "[default: enabled]") + + if pkg_config.user_configuration_file_is_supported: + p.add_option('--configuration', dest='config_file', type='string', + default=None, action='callback', + callback=optparser_store_abspath, metavar="FILE", + help="FILE defines the target configuration") + + p.add_option('--destdir', '-d', dest='destdir', type='string', + default=None, action='callback', callback=optparser_store_abspath, + metavar="DIR", + help="install %s in DIR [default: %s]" % + (pkg_name, target_config.module_dir)) + + if pkg_config.protected_is_public_is_supported: + p.add_option('--protected-is-public', dest='prot_is_public', + default=None, action='store_true', + help="enable building with 'protected' redefined as 'public' " + "[default: %s]" % target_config.prot_is_public) + p.add_option('--protected-not-public', dest='prot_is_public', + action='store_false', + help="disable building with 'protected' redefined as 'public'") + + if target_config.pyqt_package is not None: + pyqt = target_config.pyqt_package + + if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: + p.add_option('--pyqt', dest='pyqt_package', type='choice', + choices=['PyQt4', 'PyQt5'], default=pyqt, + action='store', metavar="PyQtn", + help="configure for PyQt4 or PyQt5 [default: %s]" % pyqt) + + if pkg_config.user_pyqt_sip_flags_is_supported: + p.add_option('--pyqt-sip-flags', dest='pyqt_sip_flags', + default=None, action='store', metavar="FLAGS", + help="the sip flags used to build PyQt [default: query PyQt]") + + p.add_option('--qmake', '-q', dest='qmake', type='string', default=None, + action='callback', callback=optparser_store_abspath_exe, + metavar="FILE", + help="the pathname of qmake is FILE [default: %s]" % ( + target_config.qmake or "search PATH")) + + p.add_option('--sip', dest='sip', type='string', default=None, + action='callback', callback=optparser_store_abspath_exe, + metavar="FILE", + help="the pathname of sip is FILE [default: " + "%s]" % (target_config.sip or "None")) + p.add_option('--sip-incdir', dest='sip_inc_dir', type='string', + default=None, action='callback', + callback=optparser_store_abspath_dir, metavar="DIR", + help="the directory containing the sip.h header file file is DIR " + "[default: %s]" % target_config.sip_inc_dir) + + if target_config.pyqt_package is not None: + p.add_option('--pyqt-sipdir', dest='pyqt_sip_dir', type='string', + default=None, action='callback', + callback=optparser_store_abspath_dir, metavar="DIR", + help="the directory containing the PyQt .sip files is DIR " + "[default: %s]" % target_config.pyqt_sip_dir) + + p.add_option('--concatenate', '-c', dest='concat', default=False, + action='store_true', + help="concatenate the C++ source files") + p.add_option('--concatenate-split', '-j', dest='split', type='int', + default=1, metavar="N", + help="split the concatenated C++ source files into N pieces " + "[default: 1]") + p.add_option('--static', '-k', dest='static', default=False, + action='store_true', + help="build a static %s" % pkg_name) + p.add_option("--sysroot", dest='sysroot', type='string', action='callback', + callback=optparser_store_abspath_dir, metavar="DIR", + help="DIR is the target system root directory") + p.add_option('--no-docstrings', dest='no_docstrings', default=False, + action='store_true', + help="disable the generation of docstrings") + p.add_option('--trace', '-r', dest='tracing', default=False, + action='store_true', + help="build %s with tracing enabled" % pkg_name) + p.add_option('--debug', '-u', default=False, action='store_true', + help="build %s with debugging symbols" % pkg_name) + p.add_option('--verbose', '-w', dest='verbose', default=False, + action='store_true', + help="enable verbose output during configuration") + + pkg_config.init_optparser(p, target_config) + + return p + + +def _has_stubs(pkg_config): + """ See if a stub file for any of the modules will be generated. + pkg_config is the package configuration. + """ + + for module_config in pkg_config.modules: + if module_config.pep484_stub_file: + return True + + return False + + +def _inform_user(target_config, pkg_config): + """ Tell the user the values that are going to be used. target_config is + the target configuration. pkg_config is the package configuration. + """ + + pkg_name = pkg_config.descriptive_name + + inform("Configuring %s %s..." % (pkg_name, pkg_config.version)) + + pkg_config.inform_user(target_config) + + inform("%s will be installed in %s." % + (pkg_name, target_config.module_dir)) + + if target_config.debug: + inform("A debug version of %s will be built." % pkg_name) + + if target_config.py_debug: + inform("A debug build of Python is being used.") + + if target_config.pyqt_version_str != '': + inform("PyQt %s is being used." % target_config.pyqt_version_str) + else: + inform("%s is being used." % target_config.pyqt_package) + + if target_config.qt_version_str != '': + inform("Qt %s is being used." % target_config.qt_version_str) + + if target_config.sysroot != '': + inform("The system root directory is %s." % target_config.sysroot) + + inform("sip %s is being used." % target_config.sip_version_str) + inform("The sip executable is %s." % target_config.sip) + + if target_config.prot_is_public: + inform("%s is being built with 'protected' redefined as 'public'." % + pkg_name) + + if target_config.stubs_dir != '': + inform("The PEP 484 stubs will be installed in %s." % + target_config.stubs_dir) + + if pkg_config.qscintilla_api_file and target_config.api_dir != '': + inform("The QScintilla API file will be installed in %s." % + os.path.join(target_config.api_dir, 'api', 'python')) + + +def _generate_code(target_config, opts, pkg_config, module_config): + """ Generate the code for the module. target_config is the target + configuration. opts are the command line options. pkg_config is the + package configuration. module_config is the module configuration. + """ + + inform( + "Generating the C++ source for the %s module..." % + module_config.name) + + # Generate the code in a module-specific sub-directory. + try: + os.mkdir(module_config.name) + except: + pass + + # Build the SIP command line. + argv = [quote(target_config.sip)] + + # Tell SIP if this is a debug build of Python (SIP v4.19.1 and later). + if target_config.sip_version >= 0x041301 and target_config.py_debug: + argv.append('-D') + + # Add the module-specific flags. + argv.extend(pkg_config.get_sip_flags(target_config)) + + if target_config.pyqt_package is not None: + # Get the flags used for the main PyQt module. + argv.extend(target_config.pyqt_sip_flags.split()) + + # Add the backstop version. + argv.append('-B') + argv.append('Qt_6_0_0' if target_config.pyqt_package == 'PyQt5' + else 'Qt_5_0_0') + + # Add PyQt's .sip files to the search path. + argv.append('-I') + argv.append(quote(target_config.pyqt_sip_dir)) + + if target_config.stubs_dir != '': + # Generate the stub file. + argv.append('-y') + argv.append(quote(module_config.pep484_stub_file + '.pyi')) + + if pkg_config.qscintilla_api_file and target_config.api_dir != '': + # Generate the API file. + argv.append('-a') + argv.append(quote(module_config.name + '.api')) + + if target_config.prot_is_public: + argv.append('-P'); + + if not opts.no_docstrings: + argv.append('-o'); + + if opts.concat: + argv.append('-j') + argv.append(str(opts.split)) + + if opts.tracing: + argv.append('-r') + + argv.append('-c') + argv.append(os.path.abspath(module_config.name)) + + # This assumes that, for multi-module packages, all modules's .sip files + # will be rooted in a common root directory. + sip_file = module_config.get_sip_file(target_config) + + head, tail = os.path.split(sip_file) + while head: + head, tail = os.path.split(head) + + if tail != sip_file: + argv.append('-I') + argv.append(quote(tail)) + + argv.append(sip_file) + + check_file = os.path.join(module_config.name, + 'sipAPI%s.h' % module_config.name) + _remove_file(check_file) + + _run_command(' '.join(argv), opts.verbose) + + if not os.access(check_file, os.F_OK): + error("Unable to create the C++ code.") + + # Generate the .pro file. + _generate_pro(target_config, opts, module_config) + + +def _get_qt_qmake_config(qmake_config, qt_version): + """ Return a dict of qmake configuration values for a specific Qt version. + """ + + qt_qmake_config = {} + + for name, value in qmake_config.items(): + name_parts = name.split(':') + if len(name_parts) == 2 and name_parts[0] == qt_version: + qt_qmake_config[name_parts[1]] = value + + return qt_qmake_config + + +def _write_qt_qmake_config(qt_qmake_config, pro): + """ Write the qmake configuration values to a .pro file. """ + + for name in ('QT', 'CONFIG', 'DEFINES', 'INCLUDEPATH', 'LIBS'): + value = qt_qmake_config.get(name) + if value: + pro.write(' %s += %s\n' % (name, value)) + + +def _generate_pro(target_config, opts, module_config): + """ Generate the .pro file for the module. target_config is the target + configuration. opts are the command line options. module_config is the + module configuration. + """ + + inform("Generating the .pro file for the %s module..." % module_config.name) + + # Without the 'no_check_exist' magic the target.files must exist when qmake + # is run otherwise the install and uninstall targets are not generated. + + qmake_config = module_config.get_qmake_configuration(target_config) + + pro = open(os.path.join(module_config.name, module_config.name + '.pro'), + 'w') + + pro.write('TEMPLATE = lib\n') + + qt = qmake_config.get('QT') + if qt: + pro.write('QT += %s\n' % qt) + + pro.write('CONFIG += %s\n' % ('debug' if target_config.debug else 'release')) + pro.write('CONFIG += %s\n' % ('staticlib' if opts.static else 'plugin plugin_bundle')) + + config = qmake_config.get('CONFIG') + if config: + pro.write('CONFIG += %s\n' % config) + + # Work around QTBUG-39300. + pro.write('CONFIG -= android_install\n') + + qt5_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt5') + qt4_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt4') + + if qt5_qmake_config or qt4_qmake_config: + pro.write(''' +greaterThan(QT_MAJOR_VERSION, 4) { +''') + + if qt5_qmake_config: + _write_qt_qmake_config(qt5_qmake_config, pro) + + if qt4_qmake_config: + pro.write('} else {\n') + _write_qt_qmake_config(qt4_qmake_config, pro) + + pro.write('}\n') + + mname = module_config.name + + pro.write('TARGET = %s\n' % mname) + + if not opts.static: + pro.write(''' +win32 { + PY_MODULE = %s.pyd + PY_MODULE_SRC = $(DESTDIR_TARGET) + + LIBS += -L%s +} else { + PY_MODULE = %s.so + + macx { + PY_MODULE_SRC = $(TARGET).plugin/Contents/MacOS/$(TARGET) + + QMAKE_LFLAGS += "-undefined dynamic_lookup" + + equals(QT_MAJOR_VERSION, 5) { + equals(QT_MINOR_VERSION, 5) { + QMAKE_RPATHDIR += $$[QT_INSTALL_LIBS] + } + } + } else { + PY_MODULE_SRC = $(TARGET) + } +} + +QMAKE_POST_LINK = $(COPY_FILE) $$PY_MODULE_SRC $$PY_MODULE + +target.CONFIG = no_check_exist +target.files = $$PY_MODULE +''' % (mname, quote(target_config.py_pylib_dir), mname)) + + pro.write(''' +target.path = %s +INSTALLS += target +''' % quote(target_config.module_dir)) + + sip_installs = module_config.get_sip_installs(target_config) + if sip_installs is not None: + path, files = sip_installs + + pro.write(''' +sip.path = %s +sip.files =''' % quote(path)) + + for f in files: + pro.write(' \\\n ../%s' % f) + + pro.write(''' +INSTALLS += sip +''') + + pro.write('\n') + + # These optimisations could apply to other platforms. + if module_config.no_exceptions: + if target_config.py_platform.startswith('linux') or target_config.py_platform == 'darwin': + pro.write('QMAKE_CXXFLAGS += -fno-exceptions\n') + + if target_config.py_platform.startswith('linux') and not opts.static: + if target_config.py_version >= 0x030000: + entry_point = 'PyInit_%s' % mname + else: + entry_point = 'init%s' % mname + + exp = open(os.path.join(mname, mname + '.exp'), 'wt') + exp.write('{ global: %s; local: *; };' % entry_point) + exp.close() + + pro.write('QMAKE_LFLAGS += -Wl,--version-script=%s.exp\n' % mname) + + if target_config.prot_is_public: + pro.write('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public\n') + + defines = qmake_config.get('DEFINES') + if defines: + pro.write('DEFINES += %s\n' % defines) + + includepath = qmake_config.get('INCLUDEPATH') + if includepath: + pro.write('INCLUDEPATH += %s\n' % includepath) + + # Make sure the SIP include directory is searched before the Python include + # directory if they are different. + pro.write('INCLUDEPATH += %s\n' % quote(target_config.sip_inc_dir)) + if target_config.py_inc_dir != target_config.sip_inc_dir: + pro.write('INCLUDEPATH += %s\n' % quote(target_config.py_inc_dir)) + + libs = qmake_config.get('LIBS') + if libs: + pro.write('LIBS += %s\n' % libs) + + if not opts.static: + dylib = module_config.get_mac_wrapped_library_file(target_config) + + if dylib: + pro.write(''' +macx { + QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\\\n\\\\t)$$quote(install_name_tool -change %s %s $$PY_MODULE) +} +''' % (os.path.basename(dylib), dylib)) + + pro.write('\n') + pro.write('HEADERS = sipAPI%s.h\n' % mname) + + pro.write('SOURCES =') + for s in os.listdir(module_config.name): + if s.endswith('.cpp'): + pro.write(' \\\n %s' % s) + pro.write('\n') + + pro.close() + + +def _run_qmake(target_config, verbose, pro_name): + """ Run qmake against a .pro file. target_config is the target + configuration. verbose is set if the output is to be displayed. pro_name + is the name of the .pro file. + """ + + inform("Generating the Makefiles...") + + # qmake doesn't behave consistently if it is not run from the directory + # containing the .pro file - so make sure it is. + pro_dir, pro_file = os.path.split(pro_name) + if pro_dir != '': + cwd = os.getcwd() + os.chdir(pro_dir) + else: + cwd = None + + mf = 'Makefile' + + _remove_file(mf) + + args = [quote(target_config.qmake)] + + # Make sure all Makefiles are generated now in case qmake has been + # configured with environment variables. + args.append('-recursive') + + if target_config.qmake_spec != '': + args.append('-spec') + args.append(target_config.qmake_spec) + + args.append(pro_file) + + _run_command(' '.join(args), verbose) + + if not os.access(mf, os.F_OK): + error( + "%s failed to create a Makefile from %s." % + (target_config.qmake, pro_name)) + + # Restore the current directory. + if cwd is not None: + os.chdir(cwd) + + +def _run_command(cmd, verbose): + """ Run a command and display the output if requested. cmd is the command + to run. verbose is set if the output is to be displayed. + """ + + if verbose: + sys.stdout.write(cmd + "\n") + + fout = _get_command_output(cmd) + + # Read stdout and stderr until there is no more output. + lout = fout.readline() + while lout: + if verbose: + if sys.hexversion >= 0x03000000: + sys.stdout.write(str(lout, encoding=sys.stdout.encoding)) + else: + sys.stdout.write(lout) + + lout = fout.readline() + + fout.close() + + try: + os.wait() + except: + pass + + +def _get_command_output(cmd): + """ Return a pipe from which a command's output can be read. cmd is the + command. + """ + + try: + import subprocess + except ImportError: + _, sout = os.popen4(cmd) + + return sout + + p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + return p.stdout + + +def _remove_file(fname): + """ Remove a file which may or may not exist. fname is the name of the + file. + """ + + try: + os.remove(fname) + except OSError: + pass + + +def _check_sip(target_config, pkg_config): + """ Check that the version of sip is good enough. target_config is the + target configuration. pkg_config is the package configuration. + """ + + if target_config.sip is None: + error( + "Make sure you have a working sip on your PATH or use the " + "--sip argument to explicitly specify a working sip.") + + pipe = os.popen(' '.join([quote(target_config.sip), '-V'])) + + for l in pipe: + version_str = l.strip() + break + else: + error("'%s -V' did not generate any output." % target_config.sip) + + pipe.close() + + if '.dev' in version_str or 'snapshot' in version_str: + version = 0 + else: + version = version_from_string(version_str) + if version is None: + error( + "'%s -V' generated unexpected output: '%s'." % ( + target_config.sip, version_str)) + + min_sip_version = pkg_config.minimum_sip_version + if min_sip_version: + min_version = version_from_string(min_sip_version) + if version < min_version: + error( + "This version of %s requires sip %s or later." % + (pkg_config.descriptive_name, min_sip_version)) + + target_config.sip_version = version + target_config.sip_version_str = version_str + + +def _main(argv, pkg_config): + """ Create the configured package. argv is the list of command line + arguments. pkg_config is the package configuration. + """ + + # Create the default target configuration. + target_config = _TargetConfiguration(pkg_config) + + # Parse the command line. + p = _create_optparser(target_config, pkg_config) + opts, args = p.parse_args() + + if args: + p.print_help() + sys.exit(2) + + target_config.apply_pre_options(opts) + + # Query qmake for the basic configuration information. + target_config.get_qt_configuration(opts) + + # Update the target configuration. + if pkg_config.user_configuration_file_is_supported: + config_file = opts.config_file + else: + config_file = None + + if config_file is not None: + target_config.update_from_configuration_file(config_file) + else: + target_config.apply_sysroot() + + target_config.apply_post_options(opts, pkg_config) + + if target_config.pyqt_package is not None: + if target_config.pyqt_sip_flags is None: + target_config.introspect_pyqt(pkg_config) + + # Check SIP is new enough. + _check_sip(target_config, pkg_config) + + # Perform any package specific checks now that all other information has + # been captured. + pkg_config.check_package(target_config) + + # Tell the user what's been found. + _inform_user(target_config, pkg_config) + + # Allow for module specific hacks. + pkg_config.pre_code_generation(target_config) + + # Generate the code. + for module_config in pkg_config.modules: + _generate_code(target_config, opts, pkg_config, module_config) + + # Concatenate any .api files. + if pkg_config.qscintilla_api_file and target_config.api_dir != '': + inform("Generating the QScintilla API file...") + f = open(pkg_config.qscintilla_api_file + '.api', 'w') + + for module_config in pkg_config.modules: + api = open(module_config.name + '.api') + + for l in api: + if target_config.pyqt_package is not None: + l = target_config.pyqt_package + '.' + l + + f.write(l) + + api.close() + os.remove(module_config.name + '.api') + + f.close() + + # Generate the top-level .pro file. + inform("Generating the top-level .pro file...") + + pro_name = pkg_config.descriptive_name + '.pro' + pro = open(pro_name, 'w') + + pro.write('''TEMPLATE = subdirs +CONFIG += ordered nostrip +SUBDIRS = %s +''' % ' '.join([module.name for module in pkg_config.modules])) + + if target_config.stubs_dir != '': + stubs = [module.pep484_stub_file + '.pyi' for module in pkg_config.modules if module.pep484_stub_file] + + if stubs: + pro.write(''' +pep484_stubs.path = %s +pep484_stubs.files = %s +INSTALLS += pep484_stubs +''' % (target_config.stubs_dir, ' '.join(stubs))) + + if pkg_config.qscintilla_api_file and target_config.api_dir != '': + pro.write(''' +api.path = %s/api/python +api.files = %s.api +INSTALLS += api +''' % (target_config.api_dir, pkg_config.qscintilla_api_file)) + + pro.close() + + # Generate the Makefile. + _run_qmake(target_config, opts.verbose, pro_name) + + +############################################################################### +# The script starts here. +############################################################################### + +if __name__ == '__main__': + # Assume the product is a package containing multiple modules. If it isn't + # then create a dummy package containing the single module. + try: + pkg_config_type = PackageConfiguration + except NameError: + pkg_config_type = type('PackageConfiguration', (object, ), {}) + + if not hasattr(pkg_config_type, 'modules'): + mod_config_type = ModuleConfiguration + + # Extract the package-specific attributes and methods. + pkg_config_type.descriptive_name = mod_config_type.descriptive_name + pkg_config_type.legacy_configuration_script = mod_config_type.legacy_configuration_script + pkg_config_type.minimum_sip_version = mod_config_type.minimum_sip_version + pkg_config_type.protected_is_public_is_supported = mod_config_type.protected_is_public_is_supported + pkg_config_type.pyqt4_is_supported = mod_config_type.pyqt4_is_supported + pkg_config_type.pyqt5_is_supported = mod_config_type.pyqt5_is_supported + pkg_config_type.pyqt5_is_default = mod_config_type.pyqt5_is_default + pkg_config_type.qscintilla_api_file = mod_config_type.qscintilla_api_file + pkg_config_type.support_email_address = mod_config_type.support_email_address + pkg_config_type.user_configuration_file_is_supported = mod_config_type.user_configuration_file_is_supported + pkg_config_type.user_pyqt_sip_flags_is_supported = mod_config_type.user_pyqt_sip_flags_is_supported + pkg_config_type.version = mod_config_type.version + + pkg_config_type.init_target_configuration = staticmethod( + mod_config_type.init_target_configuration) + pkg_config_type.init_optparser = staticmethod( + mod_config_type.init_optparser) + pkg_config_type.apply_options = staticmethod( + mod_config_type.apply_options) + pkg_config_type.inform_user = staticmethod( + mod_config_type.inform_user) + pkg_config_type.pre_code_generation = staticmethod( + mod_config_type.pre_code_generation) + pkg_config_type.get_sip_flags = staticmethod( + mod_config_type.get_sip_flags) + + # Note the name change. + pkg_config_type.check_package = staticmethod( + mod_config_type.check_module) + + pkg_config_type.modules = [mod_config_type()] + + pkg_config = pkg_config_type() + + try: + _main(sys.argv, pkg_config) + except SystemExit: + raise + except: + if pkg_config.support_email_address: + sys.stderr.write( +"""An internal error occured. Please report all the output from the program, +including the following traceback, to %s. +""" % pkg_config.support_email_address) + + raise diff -Nru qtav-1.12.0+ds/python/examples/simpleplayer.py qtav-1.13.0+ds/python/examples/simpleplayer.py --- qtav-1.12.0+ds/python/examples/simpleplayer.py 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/examples/simpleplayer.py 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,83 @@ +import sys + +from PyQt5 import QtCore, QtGui, QtWidgets +import QtAV, QtAVWidgets + + +class PlayerWindow(QtWidgets.QWidget): + def __init__(self, parent = None): + QtWidgets.QWidget.__init__(self, parent) + + self.m_unit = 1000.0 + self.setWindowTitle("QtAV simple player example") + self.m_player = QtAV.AVPlayer(self) + vl = QtWidgets.QVBoxLayout() + self.setLayout(vl) + self.m_vo = QtAV.VideoOutput(self) + + if self.m_vo.widget() is None: + QtWidgets.QMessageBox.warning(0, "QtAV error", "Can not create video renderer") + return + + self.m_player.setRenderer(self.m_vo) + vl.addWidget(self.m_vo.widget()) + self.m_slider = QtWidgets.QSlider() + self.m_slider.setOrientation(QtCore.Qt.Horizontal) + self.m_slider.sliderMoved[int].connect(self.seekBySliderVal) + self.m_slider.sliderPressed.connect(self.seekBySlider) + self.m_player.positionChanged.connect(self.updateSliderVal) + self.m_player.started.connect(self.updateSlider) + self.m_player.notifyIntervalChanged.connect(self.updateSliderUnit) + + vl.addWidget(self.m_slider) + hb = QtWidgets.QHBoxLayout() + vl.addLayout(hb) + self.m_openBtn = QtWidgets.QPushButton("Open") + self.m_playBtn = QtWidgets.QPushButton("Play/Pause") + self.m_stopBtn = QtWidgets.QPushButton("Stop") + hb.addWidget(self.m_openBtn) + hb.addWidget(self.m_playBtn) + hb.addWidget(self.m_stopBtn) + self.m_openBtn.clicked.connect(self.openMedia) + self.m_playBtn.clicked.connect(self.playPause) + self.m_stopBtn.clicked.connect(self.m_player.stop) + + def openMedia(self): + fname = QtWidgets.QFileDialog.getOpenFileName(None, "Open a video")[0] + if not fname: + return + self.m_player.play(fname) + + def seekBySliderVal(self, value): + if not self.m_player.isPlaying(): + return + self.m_player.seek(int(value * self.m_unit)) + + def seekBySlider(self): + self.seekBySliderVal(self.m_slider.value()) + + def playPause(self): + if not self.m_player.isPlaying(): + self.m_player.play() + return + self.m_player.pause(not self.m_player.isPaused()) + + def updateSliderVal(self, value): + self.m_slider.setRange(0, int(self.m_player.duration() / self.m_unit)) + self.m_slider.setValue(int(value / self.m_unit)) + + def updateSlider(self): + self.updateSliderVal(self.m_player.position()) + + def updateSliderUnit(self): + self.m_unit = float(self.m_player.notifyInterval()) + self.updateSlider() + + +if __name__ == "__main__": + QtAVWidgets.registerRenderers() + app = QtWidgets.QApplication(sys.argv) + player = PlayerWindow() + player.show() + player.resize(800, 600) + app.exec_() diff -Nru qtav-1.12.0+ds/python/sip/QtAV/AVError.sip qtav-1.13.0+ds/python/sip/QtAV/AVError.sip --- qtav-1.12.0+ds/python/sip/QtAV/AVError.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/AVError.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,60 @@ +namespace QtAV { + +class AVError +{ +%TypeHeaderCode +#include +%End + +public: + enum ErrorCode { + NoError, + + //open/read/seek network stream error. value must be less then ResourceError because of correct_error_by_ffmpeg + NetworkError, // all above and before NoError are NetworkError + + OpenTimedout, + OpenError, + ParseStreamTimedOut, + FindStreamInfoTimedout = ParseStreamTimedOut, + ParseStreamError, + FindStreamInfoError = ParseStreamError, + StreamNotFound, //a,v,s? + ReadTimedout, + ReadError, + SeekError, + ResourceError, // all above and before NetworkError are ResourceError + + OpenCodecError, + CloseCodecError, + AudioCodecNotFound, + VideoCodecNotFound, + SubtitleCodecNotFound, + CodecError, // all above and before NoError are CodecError + + FormatError, // all above and before CodecError are FormatError + + // decrypt error. Not implemented + AccessDenied, // all above and before NetworkError are AccessDenied + + UnknowError + }; + + AVError(); + AVError(ErrorCode code, int ffmpegError = 0); + AVError(ErrorCode code, const QString& detail, int ffmpegError = 0); + AVError(const AVError& other); + + //AVError &operator=(const AVError &other); + bool operator==(const AVError &other) const; + bool operator!=(const AVError &other) const; + + void setError(ErrorCode ec); + ErrorCode error() const; + QString string() const; + + int ffmpegErrorCode() const; + QString ffmpegErrorString() const; +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/AVOutput.sip qtav-1.13.0+ds/python/sip/QtAV/AVOutput.sip --- qtav-1.12.0+ds/python/sip/QtAV/AVOutput.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/AVOutput.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,18 @@ +namespace QtAV +{ + +class AVOutput +{ +%TypeHeaderCode +#include +%End + +public: + explicit AVOutput(); + virtual ~AVOutput(); + +private: + AVOutput(const AVOutput& o); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/AVPlayer.sip qtav-1.13.0+ds/python/sip/QtAV/AVPlayer.sip --- qtav-1.12.0+ds/python/sip/QtAV/AVPlayer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/AVPlayer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,204 @@ +namespace QtAV +{ + +class AVPlayer : public QObject +{ +%TypeHeaderCode +#include +%End + +public: + enum State { + StoppedState, + PlayingState, + PausedState + }; + + static const QStringList& supportedProtocols(); + + explicit AVPlayer(QObject *parent /TransferThis/ = 0); + ~AVPlayer(); + +// AVClock* masterClock(); + void setFile(const QString& path); + QString file() const; + void setIODevice(QIODevice* device); +// void setInput(MediaIO* in); +// MediaIO* input() const; + + bool isLoaded() const; + void setAsyncLoad(bool value = true); + bool isAsyncLoad() const; + void setAutoLoad(bool value = true); // NOT implemented + bool isAutoLoad() const; // NOT implemented + + MediaStatus mediaStatus() const; + bool relativeTimeMode() const; + + qint64 absoluteMediaStartPosition() const; + qreal durationF() const; + qint64 duration() const; + qint64 mediaStartPosition() const; + qint64 mediaStopPosition() const; + qreal mediaStartPositionF() const; + qint64 startPosition() const; + qint64 stopPosition() const; + qint64 position() const; + int repeat() const; + int currentRepeat() const; + bool setExternalAudio(const QString& file); + QString externalAudio() const; + const QVariantList& externalAudioTracks() const; + const QVariantList& internalAudioTracks() const; + const QVariantList& internalVideoTracks() const; + bool setAudioStream(const QString& file, int n = 0); + bool setAudioStream(int n); + bool setVideoStream(int n); + const QVariantList& internalSubtitleTracks() const; + bool setSubtitleStream(int n); + int currentAudioStream() const; + int currentVideoStream() const; + int currentSubtitleStream() const; + int audioStreamCount() const; + int videoStreamCount() const; + int subtitleStreamCount() const; +// VideoCapture *videoCapture() const; + void play(const QString& path); + bool isPlaying() const; + bool isPaused() const; + State state() const; + void setState(State value); + + void addVideoRenderer(VideoRenderer *renderer); + void removeVideoRenderer(VideoRenderer *renderer); + void clearVideoRenderers(); + void setRenderer(VideoRenderer* renderer); + VideoRenderer* renderer(); + QList videoOutputs(); +// AudioOutput* audio(); + void setSpeed(qreal speed); + qreal speed() const; + + void setInterruptTimeout(qint64 ms); + qint64 interruptTimeout() const; + void setInterruptOnTimeout(bool value); + bool isInterruptOnTimeout() const; + void setFrameRate(qreal value); + qreal forcedFrameRate() const; +// const Statistics& statistics() const; +// bool installFilter(AudioFilter* filter, int index = 0x7FFFFFFF); +// bool installFilter(VideoFilter* filter, int index = 0x7FFFFFFF); +// bool uninstallFilter(AudioFilter* filter); +// bool uninstallFilter(VideoFilter* filter); +// QList audioFilters() const; +// QList videoFilters() const; +// void setPriority(const QVector& ids); + void setVideoDecoderPriority(const QStringList& names); + QStringList videoDecoderPriority() const; + //void setPriority(const QVector& ids); + int brightness() const; + int contrast() const; + int hue() const; //not implemented + int saturation() const; + unsigned int chapters() const; + void setOptionsForFormat(const QVariantHash &dict); + QVariantHash optionsForFormat() const; + void setOptionsForAudioCodec(const QVariantHash &dict); + QVariantHash optionsForAudioCodec() const; + void setOptionsForVideoCodec(const QVariantHash& dict); + QVariantHash optionsForVideoCodec() const; + +// MediaEndAction mediaEndAction() const; +// void setMediaEndAction(MediaEndAction value); + + + +public Q_SLOTS: + bool load(); + + void togglePause(); + void pause(bool p = true); + void play(); + void stop(); + void stepForward(); + void stepBackward(); + + void setRelativeTimeMode(bool value); + void setRepeat(int max); + void setStartPosition(qint64 pos); +// void setStopPosition(qint64 pos = std::numeric_limits::max()); +// void setTimeRange(qint64 start, qint64 stop = std::numeric_limits::max()); + + bool isSeekable() const; + void setPosition(qint64 position); + void seek(qreal r); // r: [0, 1] + void seek(qint64 pos); //ms. same as setPosition(pos) + void seekForward(); + void seekBackward(); + void seekNextChapter(); + void seekPreviousChapter(); +// void setSeekType(SeekType type); +// SeekType seekType() const; + + qreal bufferProgress() const; + qreal bufferSpeed() const; + qint64 buffered() const; +// void setBufferMode(BufferMode mode); +// BufferMode bufferMode() const; + void setBufferValue(qint64 value); + int bufferValue() const; + + void setNotifyInterval(int msec); + int notifyInterval() const; + void updateClock(qint64 msecs); //update AVClock's external clock + void setBrightness(int val); + void setContrast(int val); + void setHue(int val); //not implemented + void setSaturation(int val); + +Q_SIGNALS: + void bufferProgressChanged(qreal); + void relativeTimeModeChanged(); + void autoLoadChanged(); + void asyncLoadChanged(); + void muteChanged(); + void sourceChanged(); + void loaded(); // == mediaStatusChanged(QtAV::LoadedMedia) + void mediaStatusChanged(QtAV::MediaStatus status); +// void mediaEndActionChanged(QtAV::MediaEndAction action); + void durationChanged(qint64); +// void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax + void paused(bool p); + void started(); + void stopped(); + void stoppedAt(qint64 position); + void stateChanged(QtAV::AVPlayer::State state); + void speedChanged(qreal speed); + void repeatChanged(int r); + void currentRepeatChanged(int r); + void startPositionChanged(qint64 position); + void stopPositionChanged(qint64 position); + void seekableChanged(); + void seekFinished(qint64 position); + void positionChanged(qint64 position); + void interruptTimeoutChanged(); + void interruptOnTimeoutChanged(); + void notifyIntervalChanged(); + void brightnessChanged(int val); + void contrastChanged(int val); + void hueChanged(int val); + void saturationChanged(int val); + void chaptersChanged(unsigned int val); + void subtitleStreamChanged(int value); + void internalAudioTracksChanged(const QVariantList& tracks); + void internalVideoTracksChanged(const QVariantList& tracks); + void externalAudioTracksChanged(const QVariantList& tracks); + void internalSubtitleTracksChanged(const QVariantList& tracks); + void internalSubtitleHeaderRead(const QByteArray& codec, const QByteArray& data); +// void internalSubtitlePacketRead(int track, const QtAV::Packet& packet); + +protected: + virtual void timerEvent(QTimerEvent *); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/Frame.sip qtav-1.13.0+ds/python/sip/QtAV/Frame.sip --- qtav-1.12.0+ds/python/sip/QtAV/Frame.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/Frame.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,44 @@ +namespace QtAV +{ + +class Frame +{ +%TypeHeaderCode +#include +%End + +public: + Frame(const Frame& other); + virtual ~Frame() = 0; + + // NOTE: We don't expose the assignment operators. + //Frame& operator =(const Frame &other); + + int planeCount() const; + virtual int channelCount() const; + + //int bytesPerLine(int plane = 0) const; + //QByteArray frameData() const; + //int dataAlignment() const; + //uchar* frameDataPtr(int* size = NULL) const; + //QByteArray data(int plane = 0) const; + //uchar* bits(int plane = 0); + //const uchar *bits(int plane = 0) const; + //const uchar* constBits(int plane = 0) const; + //void setBits(uchar *b, int plane = 0); + //void setBits(const QVector& b); + //void setBits(quint8 *slice[]); + + //void setBytesPerLine(int lineSize, int plane = 0); + //void setBytesPerLine(const QVector& lineSize); + //void setBytesPerLine(int stride[]); + + QVariantMap availableMetaData() const; + QVariant metaData(const QString& key) const; + void setMetaData(const QString &key, const QVariant &value); + void setTimestamp(qreal ts); + qreal timestamp() const; + void swap(Frame &other); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/global.sip qtav-1.13.0+ds/python/sip/QtAV/global.sip --- qtav-1.12.0+ds/python/sip/QtAV/global.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/global.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,105 @@ +%ModuleCode +#include +%End + +namespace QtAV { + +unsigned Version(); +%MethodCode +::QtAV_Version(); +%End + +QString Version_String(); +%MethodCode +::QtAV_Version_String(); +%End + +QString Version_String_Long(); +%MethodCode +::QtAV_Version_String_Long(); +%End + +enum LogLevel { + LogOff, + LogDebug, // log all + LogWarning, // log warning, critical, fatal + LogCritical, // log critical, fatal + LogFatal, // log fatal + LogAll +}; +QString aboutFFmpeg_PlainText(); +QString aboutFFmpeg_HTML(); +QString aboutQtAV_PlainText(); +QString aboutQtAV_HTML(); +void setLogLevel(LogLevel value); +LogLevel logLevel(); +//void setFFmpegLogHandler(void(*)(void *, int, const char *, va_list)); +void setFFmpegLogLevel(const QByteArray& level); + +QString avformatOptions(); +QString avcodecOptions(); + +enum MediaStatus +{ + UnknownMediaStatus, + NoMedia, + LoadingMedia, // when source is set + LoadedMedia, // if auto load and source is set. player is stopped state + StalledMedia, // insufficient buffering or other interruptions (timeout, user interrupt) + BufferingMedia, // NOT IMPLEMENTED + BufferedMedia, // when playing //NOT IMPLEMENTED + EndOfMedia, // Playback has reached the end of the current media. The player is in the StoppedState. + InvalidMedia // what if loop > 0 or stopPosition() is not mediaStopPosition()? +}; + +enum BufferMode { + BufferTime, + BufferBytes, + BufferPackets +}; + +enum MediaEndActionFlag { + MediaEndAction_Default, + MediaEndAction_KeepDisplay, + MediaEndAction_Pause +}; + +//Q_DECLARE_FLAGS(MediaEndAction, MediaEndActionFlag) + +enum SeekUnit { + SeekByTime, // only this is supported now + SeekByByte, + SeekByFrame +}; +enum SeekType { + AccurateSeek, // slow + KeyFrameSeek, // fast + AnyFrameSeek +}; + + +enum ColorSpace { + ColorSpace_Unknown, + ColorSpace_RGB, + ColorSpace_GBR, // for planar gbr format(e.g. video from x264) used in glsl + ColorSpace_BT601, + ColorSpace_BT709, + ColorSpace_XYZ +}; + + +enum ColorRange { + ColorRange_Unknown, + ColorRange_Limited, // TV, MPEG + ColorRange_Full // PC, JPEG +}; + + +enum SurfaceType { + HostMemorySurface, + GLTextureSurface, + SourceSurface, + UserSurface = 0xffff +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/OpenGLRendererBase.sip qtav-1.13.0+ds/python/sip/QtAV/OpenGLRendererBase.sip --- qtav-1.12.0+ds/python/sip/QtAV/OpenGLRendererBase.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/OpenGLRendererBase.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,29 @@ +namespace QtAV +{ + +class OpenGLRendererBase : public QtAV::VideoRenderer +{ +%TypeHeaderCode +#include +%End + +public: + virtual ~OpenGLRendererBase(); + bool isSupported(VideoFormat::PixelFormat pixfmt) const; + OpenGLVideo* opengl() const; + +protected: + virtual bool receiveFrame(const VideoFrame& frame); + virtual void drawBackground(); + virtual void drawFrame(); + void onInitializeGL(); + void onPaintGL(); + void onResizeGL(int w, int h); + void onResizeEvent(int w, int h); + void onShowEvent(); + +private: + OpenGLRendererBase(); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/OpenGLVideo.sip qtav-1.13.0+ds/python/sip/QtAV/OpenGLVideo.sip --- qtav-1.12.0+ds/python/sip/QtAV/OpenGLVideo.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/OpenGLVideo.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,42 @@ +namespace QtAV +{ + +class OpenGLVideo : public QObject +{ +%TypeHeaderCode +#include +%End + +public: + enum MeshType { + RectMesh, + SphereMesh + }; + + static bool isSupported(VideoFormat::PixelFormat pixfmt); + OpenGLVideo(); + void setOpenGLContext(QOpenGLContext *ctx); + QOpenGLContext* openGLContext(); + void setCurrentFrame(const VideoFrame& frame); + void fill(const QColor& color); + void render(const QRectF& target = QRectF(), const QRectF& roi = QRectF(), const QMatrix4x4& transform = QMatrix4x4()); + void setProjectionMatrixToRect(const QRectF& v); + void setViewport(const QRectF& r); + + void setBrightness(qreal value); + void setContrast(qreal value); + void setHue(qreal value); + void setSaturation(qreal value); + + // TODO: Add after we expose VideoShader + //void setUserShader(VideoShader* shader); + //VideoShader* userShader() const; + + void setMeshType(MeshType value); + MeshType meshType() const; +signals: + void beforeRendering(); + void afterRendering(); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/QPainterRenderer.sip qtav-1.13.0+ds/python/sip/QtAV/QPainterRenderer.sip --- qtav-1.12.0+ds/python/sip/QtAV/QPainterRenderer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/QPainterRenderer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,19 @@ +namespace QtAV +{ + +class QPainterRenderer : public QtAV::VideoRenderer /Abstract/ +{ +%TypeHeaderCode +#include +%End + +public: + QPainterRenderer(); + bool isSupported(VideoFormat::PixelFormat pixfmt) const; +protected: + bool preparePixmap(const VideoFrame& frame); + void drawBackground(); + void drawFrame(); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/QtAVmod.sip qtav-1.13.0+ds/python/sip/QtAV/QtAVmod.sip --- qtav-1.12.0+ds/python/sip/QtAV/QtAVmod.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/QtAVmod.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,48 @@ +%Module(name=QtAV, keyword_arguments="Optional") + +%Import QtGui/QtGuimod.sip +%Import QtWidgets/QtWidgetsmod.sip +%Import QtOpenGL/QtOpenGLmod.sip + +%Timeline {QtAV_1_11_0 QtAV_1_12_0} + +%Feature FULL_API + +%Copying +QtAV: Multimedia framework based on Qt and FFmpeg +Copyright (C) 2012-2016 Wang Bin + +This file is part of QtAV (from 2015) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +%End + +%HideNamespace QtAV + +%Include global.sip +%Include VideoOutput.sip +%Include AVError.sip +%Include AVPlayer.sip + +%If (FULL_API) +%Include VideoFormat.sip +%Include Frame.sip +%Include VideoFrame.sip +%Include QPainterRenderer.sip +%Include AVOutput.sip +%Include VideoRenderer.sip +%Include OpenGLRendererBase.sip +%Include OpenGLVideo.sip +%End diff -Nru qtav-1.12.0+ds/python/sip/QtAV/VideoFormat.sip qtav-1.13.0+ds/python/sip/QtAV/VideoFormat.sip --- qtav-1.12.0+ds/python/sip/QtAV/VideoFormat.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/VideoFormat.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,177 @@ +namespace QtAV +{ + +class VideoFormat +{ +%TypeHeaderCode +#include +%End + +public: + enum PixelFormat { + Format_Invalid = -1, + Format_ARGB32, // AARRGGBB or 00RRGGBB, check hasAlpha is required + Format_BGRA32, //BBGGRRAA + Format_ABGR32, // QImage.RGBA8888 le + Format_RGBA32, // QImage. no + Format_RGB32, // 0xAARRGGBB native endian. same as QImage::Format_ARGB32. be: ARGB32, le: BGRA32 + Format_BGR32, // 0xAABBGGRR native endian + Format_RGB24, + Format_BGR24, + Format_RGB565, + Format_BGR565, + Format_RGB555, + Format_BGR555, + + //http://www.fourcc.org/yuv.php + Format_AYUV444, + Format_YUV444P, + Format_YUV422P, + Format_YUV420P, + Format_YUV411P, + Format_YUV410P, + Format_YV12, + Format_UYVY, //422 + Format_VYUY, //not in ffmpeg. OMX_COLOR_FormatCrYCbY + Format_YUYV, //422, aka yuy2 + Format_YVYU, //422 + Format_NV12, + Format_NV21, + Format_IMC1, + Format_IMC2, + Format_IMC3, //same as IMC1, swap U V + Format_IMC4, //same as IMC2, swap U V + Format_Y8, //GREY. single 8 bit Y plane + Format_Y16, //single 16 bit Y plane. LE + + Format_Jpeg, //yuvj + + //Format_CameraRaw, + //Format_AdobeDng, + + Format_YUV420P9LE, + Format_YUV422P9LE, + Format_YUV444P9LE, + Format_YUV420P10LE, + Format_YUV422P10LE, + Format_YUV444P10LE, + Format_YUV420P12LE, + Format_YUV422P12LE, + Format_YUV444P12LE, + Format_YUV420P14LE, + Format_YUV422P14LE, + Format_YUV444P14LE, + Format_YUV420P16LE, + Format_YUV422P16LE, + Format_YUV444P16LE, + Format_YUV420P9BE, + Format_YUV422P9BE, + Format_YUV444P9BE, + Format_YUV420P10BE, + Format_YUV422P10BE, + Format_YUV444P10BE, + Format_YUV420P12BE, + Format_YUV422P12BE, + Format_YUV444P12BE, + Format_YUV420P14BE, + Format_YUV422P14BE, + Format_YUV444P14BE, + Format_YUV420P16BE, + Format_YUV422P16BE, + Format_YUV444P16BE, + + Format_RGB48, // native endian + Format_RGB48LE, + Format_RGB48BE, + Format_BGR48, + Format_BGR48LE, + Format_BGR48BE, + Format_RGBA64, //native endian + Format_RGBA64LE, + Format_RGBA64BE, + Format_BGRA64, //native endian + Format_BGRA64LE, + Format_BGRA64BE, + + Format_VYU, // for rgb422_apple texture, the layout is like rgb24: (v, y, u, ) + Format_XYZ12, + Format_XYZ12LE, + Format_XYZ12BE, + Format_User + }; + + static PixelFormat pixelFormatFromImageFormat(QImage::Format format); + static QImage::Format imageFormatFromPixelFormat(PixelFormat format); + static PixelFormat pixelFormatFromFFmpeg(int ff); //AVPixelFormat + static int pixelFormatToFFmpeg(PixelFormat fmt); + static QVector pixelFormatsFFmpeg(); + + VideoFormat(PixelFormat format = Format_Invalid); + VideoFormat(int formatFF /Constrained/); + VideoFormat(QImage::Format fmt /Constrained/); + VideoFormat(const QString& name); + VideoFormat(const VideoFormat &other /Constrained/); + ~VideoFormat(); + + // NOTE: We don't expose the assignment operators. + //VideoFormat& operator=(const VideoFormat &other); + //VideoFormat& operator=(VideoFormat::PixelFormat pixfmt); + //VideoFormat& operator=(QImage::Format qpixfmt); + //VideoFormat& operator=(int ffpixfmt); + bool operator==(const VideoFormat &other) const; + bool operator==(VideoFormat::PixelFormat pixfmt /Constrained/) const; + bool operator==(QImage::Format qpixfmt /Constrained/) const; + bool operator==(int ffpixfmt /Constrained/) const; + bool operator!=(const VideoFormat &other) const; + bool operator!=(VideoFormat::PixelFormat pixfmt /Constrained/) const; + bool operator!=(QImage::Format qpixfmt /Constrained/) const; + bool operator!=(int ffpixfmt /Constrained/) const; + + bool isValid() const; + + PixelFormat pixelFormat() const; + int pixelFormatFFmpeg() const; + QImage::Format imageFormat() const; + QString name() const; + + void setPixelFormat(PixelFormat format); + void setPixelFormatFFmpeg(int format); + + int channels() const; + int channels(int plane) const; + int planeCount() const; + int bitsPerPixel() const; + /// nv12: 16 for uv plane + int bitsPerPixel(int plane) const; + /// bgr24 is 24 not 32 + int bitsPerPixelPadded() const; + int bytesPerPixel() const; + int bytesPerPixel(int plane) const; + int bitsPerComponent() const; + + int bytesPerLine(int width, int plane) const; + int chromaWidth(int lumaWidth) const; + int chromaHeight(int lumaHeight) const; + int width(int lumaWidth, int plane) const; + int height(int lumaHeight, int plane) const; + qreal normalizedWidth(int plane) const; + qreal normalizedHeight(int plane) const; + bool isBigEndian() const; + bool hasPalette() const; + bool isPseudoPaletted() const; + bool isBitStream() const; + bool isHWAccelerated() const; + bool isPlanar() const; + bool isRGB() const; + bool isXYZ() const; + bool hasAlpha() const; + + static bool isPlanar(PixelFormat pixfmt); + static bool isRGB(PixelFormat pixfmt); + static bool hasAlpha(PixelFormat pixfmt); +}; + +//QDebug operator<<(QDebug debug, const VideoFormat &fmt); +//QDebug operator<<(QDebug debug, VideoFormat::PixelFormat pixFmt); + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/VideoFrame.sip qtav-1.13.0+ds/python/sip/QtAV/VideoFrame.sip --- qtav-1.12.0+ds/python/sip/QtAV/VideoFrame.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/VideoFrame.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,79 @@ +namespace QtAV +{ + +class VideoFrame : public QtAV::Frame +{ +%TypeHeaderCode +#include +%End + +public: + //static VideoFrame fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized = true, bool swapUV = false); + //static void copyPlane(quint8 *dst, size_t dst_stride, const quint8 *src, size_t src_stride, unsigned byteWidth, unsigned height); + +public: + + VideoFrame(); + VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray(), int alignment = 1); + VideoFrame(const QImage& image); + VideoFrame(const VideoFrame &other); + ~VideoFrame(); + + // NOTE: We don't expose the assignment operators. + //VideoFrame &operator=(const VideoFrame &other); + + int channelCount() const; + VideoFrame clone() const; + VideoFormat format() const; + VideoFormat::PixelFormat pixelFormat() const; + QImage::Format imageFormat() const; + int pixelFormatFFmpeg() const; + + bool isValid() const; + operator bool() const; + + QSize size() const; + int width() const; + int height() const; + int effectiveBytesPerLine(int plane) const; + int planeWidth(int plane) const; + int planeHeight(int plane) const; + float displayAspectRatio() const; + void setDisplayAspectRatio(float displayAspectRatio); + + // TODO: After we expose ColorSpace and ColorRange exposed. + //ColorSpace colorSpace() const; + //void setColorSpace(ColorSpace value); + //ColorRange colorRange() const; + //void setColorRange(ColorRange value); + + QImage toImage(QImage::Format fmt = QImage::Format_ARGB32, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + VideoFrame to(VideoFormat::PixelFormat pixfmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + VideoFrame to(const VideoFormat& fmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + + // TODO + //bool to(VideoFormat::PixelFormat pixfmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + //bool to(const VideoFormat& fmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + + //void* map(SurfaceType type, void* handle, int plane = 0); + //void* map(SurfaceType type, void* handle, const VideoFormat& fmt, int plane = 0); + + void unmap(void* handle); + + //void* createInteropHandle(void* handle, SurfaceType type, int plane); +}; + + +class VideoFrameConverter +{ +public: + VideoFrameConverter(); + ~VideoFrameConverter(); + void setEq(int brightness, int contrast, int saturation); + VideoFrame convert(const VideoFrame& frame, const VideoFormat& fmt /Constrained/) const; + VideoFrame convert(const VideoFrame& frame, VideoFormat::PixelFormat fmt /Constrained/) const; + VideoFrame convert(const VideoFrame& frame, QImage::Format fmt /Constrained/) const; + VideoFrame convert(const VideoFrame& frame, int fffmt /Constrained/) const; +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/VideoOutput.sip qtav-1.13.0+ds/python/sip/QtAV/VideoOutput.sip --- qtav-1.12.0+ds/python/sip/QtAV/VideoOutput.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/VideoOutput.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,20 @@ +namespace QtAV +{ + +class VideoOutput : public QObject, public QtAV::VideoRenderer +{ +%TypeHeaderCode +#include +%End + +public: + VideoOutput(QWidget* parent /TransferThis/ = 0); + VideoOutput(VideoRendererId rendererId, QObject *parent /TransferThis/ = 0); + ~VideoOutput(); + + VideoRendererId id() const; + + virtual QWidget* widget() final; +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAV/VideoRenderer.sip qtav-1.13.0+ds/python/sip/QtAV/VideoRenderer.sip --- qtav-1.12.0+ds/python/sip/QtAV/VideoRenderer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAV/VideoRenderer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,28 @@ +namespace QtAV +{ + +typedef int VideoRendererId /NoTypeName/; + +class VideoRenderer : public QtAV::AVOutput /Abstract/ +{ +%TypeHeaderCode +#include +%End + +public: + enum OutAspectRatioMode { + RendererAspectRatio //Use renderer's aspect ratio, i.e. stretch to fit the renderer rect + , VideoAspectRatio //Use video's aspect ratio and align center in renderer. + , CustomAspectRation //Use the ratio set by setOutAspectRatio(qreal). Mode will be set to this if that function is called + //, AspectRatio4_3, AspectRatio16_9 + }; + + VideoRenderer(); + virtual ~VideoRenderer(); + virtual VideoRendererId id() const = 0; + +private: + VideoRenderer(const VideoRenderer& r); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/global.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/global.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/global.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/global.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,29 @@ +%ModuleCode +#include +%End + + +namespace QtAV { + +// Use custom code to allow QtAVWidgets.registerRenderers() instead of +// QtAVWidgets.Widgets.registerRenderers() +void registerRenderers(); +%MethodCode +QtAV::Widgets::registerRenderers(); +%End + +VideoRendererId VideoRendererId_Widget; +VideoRendererId VideoRendererId_GraphicsItem; +VideoRendererId VideoRendererId_GLWidget; +VideoRendererId VideoRendererId_GDI; +VideoRendererId VideoRendererId_Direct2D; +VideoRendererId VideoRendererId_XV; +VideoRendererId VideoRendererId_X11; +VideoRendererId VideoRendererId_GLWidget2; +VideoRendererId VideoRendererId_OpenGLWidget; + +void about(); +void aboutFFmpeg(); +void aboutQtAV(); + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/GLWidgetRenderer2.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/GLWidgetRenderer2.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/GLWidgetRenderer2.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/GLWidgetRenderer2.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,64 @@ +namespace QtAV +{ + +class GLWidgetRenderer2 : public QGLWidget, public QtAV::OpenGLRendererBase +{ +%TypeHeaderCode +#include +%End + +public: + void setBrightness(qreal brightness); + qreal brightness(); + void setContrast(qreal contrast); + qreal contrast(); + void setHue(qreal hue); + qreal hue(); + void setSaturation(qreal saturation); + qreal saturation(); + void setBackgroundColor(QColor color); + QColor backgroundColor(); + + QRectF regionOfInterest(); + void setRegionOfInterest(QRectF region); + qreal sourceAspectRatio(); + qreal outAspectRatio(); + void setOutAspectRatio(qreal ratio); + OutAspectRatioMode outAspectRatioMode(); + void setOutAspectRatioMode(OutAspectRatioMode mode); + + int orientation(); + void setOrientation(int orientation); + QRect videoRect(); + QSize videoFrameSize(); + +public: + GLWidgetRenderer2(QWidget* parent /TransferThis/ = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0); + virtual VideoRendererId id(); + virtual QWidget* widget(); + +signals: + void sourceAspectRatioChanged(qreal value) final; + void regionOfInterestChanged(); + void outAspectRatioChanged(); + void outAspectRatioModeChanged(); + void brightnessChanged(qreal value); + void contrastChanged(qreal); + void hueChanged(qreal); + void saturationChanged(qreal); + void orientationChanged(); + void videoRectChanged(); + void videoFrameSizeChanged(); + void backgroundColorChanged(); + +protected: + virtual void initializeGL(); + virtual void paintGL(); + virtual void resizeGL(int w, int h); + virtual void resizeEvent(QResizeEvent *); // not virtual in QGLWidget (Qt<5.5) + virtual void showEvent(QShowEvent *); + +}; +typedef GLWidgetRenderer2 VideoRendererGLWidget2; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/GraphicsItemRenderer.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/GraphicsItemRenderer.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/GraphicsItemRenderer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/GraphicsItemRenderer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,73 @@ +namespace QtAV +{ + +class GraphicsItemRenderer : public QGraphicsObject, public QtAV::QPainterRenderer +{ +%TypeHeaderCode +#include +%End + +public: + void setBrightness(qreal brightness); + qreal brightness(); + void setContrast(qreal contrast); + qreal contrast(); + void setHue(qreal hue); + qreal hue(); + void setSaturation(qreal saturation); + qreal saturation(); + void setBackgroundColor(QColor color); + QColor backgroundColor(); + + QRectF regionOfInterest(); + void setRegionOfInterest(QRectF region); + qreal sourceAspectRatio(); + qreal outAspectRatio(); + void setOutAspectRatio(qreal ratio); + OutAspectRatioMode outAspectRatioMode(); + void setOutAspectRatioMode(OutAspectRatioMode mode); + + int orientation(); + void setOrientation(int orientation); + QRect videoRect(); + QSize videoFrameSize(); + +public: + GraphicsItemRenderer(QGraphicsItem* parent /TransferThis/ = 0); + virtual VideoRendererId id(); + + bool isSupported(VideoFormat::PixelFormat pixfmt); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QGraphicsItem* graphicsItem(); + + bool isOpenGL() const; + void setOpenGL(bool o); + + OpenGLVideo* opengl() const; + +signals: + void sourceAspectRatioChanged(qreal value) final; + void regionOfInterestChanged(); + void outAspectRatioChanged(); + void outAspectRatioModeChanged(); + void brightnessChanged(qreal value); + void contrastChanged(qreal); + void hueChanged(qreal); + void saturationChanged(qreal); + void backgroundColorChanged(); + void orientationChanged(); + void videoRectChanged(); + void videoFrameSizeChanged(); + void openGLChanged(); + +protected: + bool receiveFrame(const VideoFrame& frame); + void drawBackground(); + void drawFrame(); + bool event(QEvent *event); +}; +typedef GraphicsItemRenderer VideoRendererGraphicsItem; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/OpenGLWidgetRenderer.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/OpenGLWidgetRenderer.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/OpenGLWidgetRenderer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/OpenGLWidgetRenderer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,64 @@ +namespace QtAV +{ + +class OpenGLWidgetRenderer : public QOpenGLWidget, public QtAV::OpenGLRendererBase +{ +%TypeHeaderCode +#include +%End + +public: + void setBrightness(qreal brightness); + qreal brightness(); + void setContrast(qreal contrast); + qreal contrast(); + void setHue(qreal hue); + qreal hue(); + void setSaturation(qreal saturation); + qreal saturation(); + void setBackgroundColor(QColor color); + QColor backgroundColor(); + + QRectF regionOfInterest(); + void setRegionOfInterest(QRectF region); + qreal sourceAspectRatio(); + qreal outAspectRatio(); + void setOutAspectRatio(qreal ratio); + OutAspectRatioMode outAspectRatioMode(); + void setOutAspectRatioMode(OutAspectRatioMode mode); + + int orientation(); + void setOrientation(int orientation); + QRect videoRect(); + QSize videoFrameSize(); + +public: + explicit OpenGLWidgetRenderer(QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0); + virtual VideoRendererId id(); + virtual QWidget* widget(); + +signals: + void sourceAspectRatioChanged(qreal value) final; + void regionOfInterestChanged(); + void outAspectRatioChanged(); + void outAspectRatioModeChanged(); + void brightnessChanged(qreal value); + void contrastChanged(qreal); + void hueChanged(qreal); + void saturationChanged(qreal); + void orientationChanged(); + void videoRectChanged(); + void videoFrameSizeChanged(); + void backgroundColorChanged(); + +protected: + virtual void initializeGL(); + virtual void paintGL(); + virtual void resizeGL(int w, int h); + virtual void resizeEvent(QResizeEvent *); // not virtual in QGLWidget (Qt<5.5) + virtual void showEvent(QShowEvent *); + +}; +typedef OpenGLWidgetRenderer VideoRendererOpenGLWidget; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/QtAVWidgetsmod.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/QtAVWidgetsmod.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/QtAVWidgetsmod.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/QtAVWidgetsmod.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,44 @@ +%Module(name=QtAVWidgets, keyword_arguments="Optional") + +%Import QtGui/QtGuimod.sip +%Import QtWidgets/QtWidgetsmod.sip +%Import QtOpenGL/QtOpenGLmod.sip +%Import QtAV/QtAVmod.sip + +%Timeline {QtAVWidgets_1_11_0 QtAVWidgets_1_12_0} + +%Feature FULL_QTWIDGETS_API + +%Copying +QtAV: Multimedia framework based on Qt and FFmpeg +Copyright (C) 2012-2016 Wang Bin + +This file is part of QtAV (from 2015) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +%End + +%HideNamespace QtAV + +%Include global.sip + +%If (FULL_QTWIDGETS_API) +%Include GLWidgetRenderer2.sip +%Include GraphicsItemRenderer.sip +// NOTE: We don't expose QOpenGLWidget because it is present in Qt >= 5.4.0 +%Include OpenGLWidgetRenderer.sip +%Include VideoPreviewWidget.sip +%Include WidgetRenderer.sip +%End diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/VideoPreviewWidget.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/VideoPreviewWidget.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/VideoPreviewWidget.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/VideoPreviewWidget.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,34 @@ +namespace QtAV +{ + +class VideoPreviewWidget : public QWidget +{ +%TypeHeaderCode +#include +%End + +public: + explicit VideoPreviewWidget(QWidget *parent /TransferThis/ = 0); + void setTimestamp(qint64 msec); + qint64 timestamp() const; + void preview(); + void setFile(const QString& file); + QString file() const; + void setKeepAspectRatio(bool value = true); + bool isKeepAspectRatio() const; + bool isAutoDisplayFrame() const; + void setAutoDisplayFrame(bool b=true); +public slots: + void displayFrame(const QtAV::VideoFrame& frame); + void displayNoFrame(); +signals: + void timestampChanged(); + void fileChanged(); + void gotError(const QString &); + void gotAbort(const QString &); + void gotFrame(const QtAV::VideoFrame & frame); +protected: + virtual void resizeEvent(QResizeEvent *); +}; + +}; diff -Nru qtav-1.12.0+ds/python/sip/QtAVWidgets/WidgetRenderer.sip qtav-1.13.0+ds/python/sip/QtAVWidgets/WidgetRenderer.sip --- qtav-1.12.0+ds/python/sip/QtAVWidgets/WidgetRenderer.sip 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/python/sip/QtAVWidgets/WidgetRenderer.sip 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,53 @@ +namespace QtAV +{ + +class WidgetRenderer : public QWidget, public QtAV::QPainterRenderer +{ +%TypeHeaderCode +#include +%End + +public: + QColor backgroundColor(); + void setBackgroundColor(QColor color); + QRectF regionOfInterest(); + void setRegionOfInterest(QRectF region); + qreal sourceAspectRatio(); + qreal outAspectRatio(); + void setOutAspectRatio(qreal ratio); + OutAspectRatioMode outAspectRatioMode(); + void setOutAspectRatioMode(OutAspectRatioMode mode); + int orientation(); + void setOrientation(int orientation); + QRect videoRect(); + QSize videoFrameSize(); + +public: + WidgetRenderer(QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0); + virtual VideoRendererId id(); + virtual QWidget* widget(); + +signals: + void sourceAspectRatioChanged(qreal value) final; + void regionOfInterestChanged(); + void outAspectRatioChanged(); + void outAspectRatioModeChanged(); + void brightnessChanged(qreal value); + void contrastChanged(qreal); + void hueChanged(qreal); + void saturationChanged(qreal); + void backgroundColorChanged(); + void orientationChanged(); + void videoRectChanged(); + void videoFrameSizeChanged(); + void imageReady(); + +protected: + bool receiveFrame(const VideoFrame& frame); + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + bool onSetOrientation(int value); +}; +typedef WidgetRenderer VideoRenderWidget; + +}; diff -Nru qtav-1.12.0+ds/.qmake.conf qtav-1.13.0+ds/.qmake.conf --- qtav-1.12.0+ds/.qmake.conf 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/.qmake.conf 2019-07-11 00:58:59.000000000 +0000 @@ -1,5 +1,5 @@ QTAV_MAJOR_VERSION = 1 -QTAV_MINOR_VERSION = 12 +QTAV_MINOR_VERSION = 13 QTAV_PATCH_VERSION = 0 QTAV_VERSION = $${QTAV_MAJOR_VERSION}.$${QTAV_MINOR_VERSION}.$${QTAV_PATCH_VERSION} diff -Nru qtav-1.12.0+ds/qml/CMakeLists.txt qtav-1.13.0+ds/qml/CMakeLists.txt --- qtav-1.12.0+ds/qml/CMakeLists.txt 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/CMakeLists.txt 2019-07-11 00:58:59.000000000 +0000 @@ -32,7 +32,7 @@ # add HEADERS for moc if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) - configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) + configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) target_link_libraries(${MODULE} diff -Nru qtav-1.12.0+ds/qml/libQmlAV.pro qtav-1.13.0+ds/qml/libQmlAV.pro --- qtav-1.12.0+ds/qml/libQmlAV.pro 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/libQmlAV.pro 2019-07-11 00:58:59.000000000 +0000 @@ -13,10 +13,17 @@ preparePaths($$OUT_PWD/../out) #https://github.com/wang-bin/QtAV/issues/368#issuecomment-73246253 #http://qt-project.org/forums/viewthread/38438 -# mkspecs/features/qml_plugin.prf -URI = QtAV #uri used in QtAVQmlPlugin::registerTypes(uri) -qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI # not sure what moc does -#DESTDIR = $$BUILD_DIR/bin/QtAV +# mkspecs/features/qml_plugin.prf mkspecs/features/qml_module.prf +TARGETPATH = QtAV +URI = $$replace(TARGETPATH, "/", ".") +qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI + +static: CONFIG += builtin_resources + +builtin_resources { + RESOURCES += libQmlAV.qrc +} + qtav_qml.files = qmldir Video.qml plugins.qmltypes !static { #static lib copy error before ranlib. copy only in sdk_install plugin.files = $$DESTDIR/$$qtSharedLib($$NAME) @@ -63,9 +70,9 @@ EXTRA_COPY_FILES = $$qtav_qml.files QMAKE_WRITE_DEFAULT_RC = 1 -QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" +QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV QML module. QtAV Multimedia framework. http://qtav.org" -QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" +QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV QML" SOURCES += \ diff -Nru qtav-1.12.0+ds/qml/libQmlAV.qrc qtav-1.13.0+ds/qml/libQmlAV.qrc --- qtav-1.12.0+ds/qml/libQmlAV.qrc 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/qml/libQmlAV.qrc 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,6 @@ + + + qmldir + Video.qml + + diff -Nru qtav-1.12.0+ds/qml/plugin.cpp qtav-1.13.0+ds/qml/plugin.cpp --- qtav-1.12.0+ds/qml/plugin.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/plugin.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -32,6 +32,12 @@ #include "QmlAV/QuickFBORenderer.h" #endif +inline void initResources() { +#ifdef BUILD_QMLAV_STATIC + Q_INIT_RESOURCE(libQmlAV); +#endif +} + namespace QtAV { class QtAVQmlPlugin : public QQmlExtensionPlugin @@ -39,6 +45,9 @@ Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: + QtAVQmlPlugin() { + initResources(); + } void registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtAV")); diff -Nru qtav-1.12.0+ds/qml/plugins.qmltypes qtav-1.13.0+ds/qml/plugins.qmltypes --- qtav-1.12.0+ds/qml/plugins.qmltypes 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/plugins.qmltypes 2019-07-11 00:58:59.000000000 +0000 @@ -188,6 +188,14 @@ } } Enum { + name: "MediaEndAction" + values: { + "MediaEndAction_Default": 0, + "MediaEndAction_KeepDisplay": 1, + "MediaEndAction_Pause": 2 + } + } + Enum { name: "ChannelLayout" values: { "ChannelLayoutAuto": 0, @@ -208,6 +216,7 @@ Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true } Property { name: "autoPlay"; type: "bool" } Property { name: "autoLoad"; type: "bool" } + Property { name: "mediaEndAction"; type: "MediaEndAction" } Property { name: "playbackRate"; type: "double" } Property { name: "source"; type: "QUrl" } Property { name: "loops"; type: "int" } diff -Nru qtav-1.12.0+ds/qml/QmlAV/QmlAVPlayer.h qtav-1.13.0+ds/qml/QmlAV/QmlAVPlayer.h --- qtav-1.12.0+ds/qml/QmlAV/QmlAVPlayer.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QmlAV/QmlAVPlayer.h 2019-07-11 00:58:59.000000000 +0000 @@ -63,8 +63,10 @@ Q_ENUMS(Status) Q_ENUMS(Error) Q_ENUMS(ChannelLayout) + Q_ENUMS(BufferMode) // not supported by QtMultimedia Q_ENUMS(PositionValue) + Q_ENUMS(MediaEndAction) Q_PROPERTY(int startPosition READ startPosition WRITE setStartPosition NOTIFY startPositionChanged) Q_PROPERTY(int stopPosition READ stopPosition WRITE setStopPosition NOTIFY stopPositionChanged) Q_PROPERTY(bool fastSeek READ isFastSeek WRITE setFastSeek NOTIFY fastSeekChanged) @@ -80,6 +82,8 @@ Q_PROPERTY(int audioTrack READ audioTrack WRITE setAudioTrack NOTIFY audioTrackChanged) Q_PROPERTY(int videoTrack READ videoTrack WRITE setVideoTrack NOTIFY videoTrackChanged) Q_PROPERTY(int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged) + Q_PROPERTY(BufferMode bufferMode READ bufferMode WRITE setBufferMode NOTIFY bufferModeChanged) + Q_PROPERTY(qreal frameRate READ frameRate WRITE setFrameRate NOTIFY frameRateChanged) Q_PROPERTY(QUrl externalAudio READ externalAudio WRITE setExternalAudio NOTIFY externalAudioChanged) Q_PROPERTY(QVariantList internalAudioTracks READ internalAudioTracks NOTIFY internalAudioTracksChanged) Q_PROPERTY(QVariantList internalVideoTracks READ internalVideoTracks NOTIFY internalVideoTracksChanged) @@ -87,6 +91,7 @@ Q_PROPERTY(QVariantList internalSubtitleTracks READ internalSubtitleTracks NOTIFY internalSubtitleTracksChanged) // internal subtitle, e.g. mkv embedded subtitles Q_PROPERTY(int internalSubtitleTrack READ internalSubtitleTrack WRITE setInternalSubtitleTrack NOTIFY internalSubtitleTrackChanged) + Q_PROPERTY(MediaEndAction mediaEndAction READ mediaEndAction WRITE setMediaEndAction NOTIFY mediaEndActionChanged) Q_PROPERTY(QQmlListProperty audioFilters READ audioFilters) Q_PROPERTY(QQmlListProperty videoFilters READ videoFilters) @@ -129,6 +134,17 @@ Mono, Stereo }; + enum BufferMode + { + BufferTime = QtAV::BufferTime, + BufferBytes = QtAV::BufferBytes, + BufferPackets = QtAV::BufferPackets + }; + enum MediaEndAction { + MediaEndAction_Default, /// stop playback (if loop end) and clear video renderer + MediaEndAction_KeepDisplay = 1, /// stop playback but video renderer keeps the last frame + MediaEndAction_Pause = 1 << 1 /// pause playback. Currently AVPlayer repeat mode will not work if this flag is set + }; explicit QmlAVPlayer(QObject *parent = 0); //derived @@ -225,7 +241,6 @@ void setAudioTrack(int value); QVariantList internalAudioTracks() const; /*! - /*! * \brief videoTrack * The video stream number in current media. * Value can be: 0, 1, 2.... 0 means the 1st video stream in current media @@ -236,6 +251,15 @@ int bufferSize() const; void setBufferSize(int value); + + BufferMode bufferMode() const; + void setBufferMode(BufferMode value); + + qreal frameRate() const; + void setFrameRate(qreal value); + + MediaEndAction mediaEndAction() const; + void setMediaEndAction(MediaEndAction value); /*! * \brief externalAudio * If externalAudio url is valid, player will use audioTrack of external audio as audio source. @@ -307,6 +331,9 @@ void internalSubtitleTrackChanged(); void internalSubtitleTracksChanged(); void bufferSizeChanged(); + void bufferModeChanged(); + void frameRateChanged(); + void mediaEndActionChanged(); void errorChanged(); void error(Error error, const QString &errorString); diff -Nru qtav-1.12.0+ds/qml/QmlAV/QQuickItemRenderer.h qtav-1.13.0+ds/qml/QmlAV/QQuickItemRenderer.h --- qtav-1.12.0+ds/qml/QmlAV/QQuickItemRenderer.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QmlAV/QQuickItemRenderer.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin theoribeiro * This file is part of QtAV (from 2013) @@ -60,6 +60,10 @@ bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; QObject *source() const; + /*! + * \brief setSource + * \param source nullptr, an object of type AVPlayer or QmlAVPlayer + */ void setSource(QObject *source); FillMode fillMode() const; diff -Nru qtav-1.12.0+ds/qml/QmlAV/QuickFBORenderer.h qtav-1.13.0+ds/qml/QmlAV/QuickFBORenderer.h --- qtav-1.12.0+ds/qml/QmlAV/QuickFBORenderer.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QmlAV/QuickFBORenderer.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2015) @@ -66,6 +66,10 @@ OpenGLVideo* opengl() const Q_DECL_OVERRIDE; QObject *source() const; + /*! + * \brief setSource + * \param source nullptr, an object of type AVPlayer or QmlAVPlayer + */ void setSource(QObject *source); FillMode fillMode() const; diff -Nru qtav-1.12.0+ds/qml/QmlAVPlayer.cpp qtav-1.13.0+ds/qml/QmlAVPlayer.cpp --- qtav-1.12.0+ds/qml/QmlAVPlayer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QmlAVPlayer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -425,6 +425,51 @@ } } +QmlAVPlayer::BufferMode QmlAVPlayer::bufferMode() const +{ + return static_cast(mpPlayer->bufferMode()); +} + +void QmlAVPlayer::setBufferMode(QmlAVPlayer::BufferMode value) +{ + QtAV::BufferMode mode = static_cast(value); + if(mpPlayer->bufferMode() == mode) + return; + if(mpPlayer) { + mpPlayer->setBufferMode(mode); + Q_EMIT bufferModeChanged(); + } +} + +qreal QmlAVPlayer::frameRate() const +{ + return mpPlayer->forcedFrameRate(); +} + +void QmlAVPlayer::setFrameRate(qreal value) +{ + if(mpPlayer) { + mpPlayer->setFrameRate(value); + Q_EMIT frameRateChanged(); + } +} + +QmlAVPlayer::MediaEndAction QmlAVPlayer::mediaEndAction() const +{ + return static_cast(int(mpPlayer->mediaEndAction())); +} + +void QmlAVPlayer::setMediaEndAction(QmlAVPlayer::MediaEndAction value) +{ + QtAV::MediaEndAction action = static_cast(value); + if (mpPlayer->mediaEndAction() == action) + return; + if (mpPlayer) { + mpPlayer->setMediaEndAction(action); + Q_EMIT mediaEndActionChanged(); + } +} + QUrl QmlAVPlayer::externalAudio() const { return m_audio; diff -Nru qtav-1.12.0+ds/qml/QQuickItemRenderer.cpp qtav-1.13.0+ds/qml/QQuickItemRenderer.cpp --- qtav-1.12.0+ds/qml/QQuickItemRenderer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QQuickItemRenderer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin theoribeiro * This file is part of QtAV (from 2013) @@ -156,7 +156,16 @@ Q_EMIT sourceChanged(); if (!source) return; - ((QmlAVPlayer*)source)->player()->addVideoRenderer(this); + AVPlayer* p = qobject_cast(source); + if (!p) { + QmlAVPlayer* qp = qobject_cast(source); + if (!qp) { + qWarning("source MUST be of type AVPlayer or QmlAVPlayer"); + return; + } + p = qp->player(); + } + p->addVideoRenderer(this); } QQuickItemRenderer::FillMode QQuickItemRenderer::fillMode() const @@ -194,10 +203,10 @@ { if (videoFrameSize().isEmpty()) return QPointF(); - + DPTR_D(const QQuickItemRenderer); // Just normalize and use that function // m_nativeSize is transposed in some orientations - if (orientation()%180 == 0) + if (d.rotation()%180 == 0) return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height())); else return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width())); @@ -213,7 +222,8 @@ { qreal dx = point.x(); qreal dy = point.y(); - if (orientation()%180 == 0) { + DPTR_D(const QQuickItemRenderer); + if (d.rotation()%180 == 0) { dx *= contentRect().width(); dy *= contentRect().height(); } else { @@ -221,7 +231,7 @@ dy *= contentRect().width(); } - switch (orientation()) { + switch (d.rotation()) { case 0: default: return contentRect().topLeft() + QPointF(dx, dy); @@ -243,7 +253,7 @@ QPointF QQuickItemRenderer::mapPointToSource(const QPointF &point) const { QPointF norm = mapPointToSourceNormalized(point); - if (orientation()%180 == 0) + if (d_func().rotation()%180 == 0) return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height()); else return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width()); @@ -263,7 +273,7 @@ // Normalize the item source point qreal nx = (point.x() - contentRect().x()) / contentRect().width(); qreal ny = (point.y() - contentRect().y()) / contentRect().height(); - switch (orientation()) { + switch (d_func().rotation()) { case 0: default: return QPointF(nx, ny); @@ -340,7 +350,7 @@ if (d.frame_changed) sgvn->setCurrentFrame(d.video_frame); d.frame_changed = false; - sgvn->setTexturedRectGeometry(d.out_rect, normalizedROI(), d.orientation); + sgvn->setTexturedRectGeometry(d.out_rect, normalizedROI(), d.rotation()); return; } if (!d.frame_changed) { @@ -357,9 +367,9 @@ if (d.texture) delete d.texture; - if (d.orientation == 0) { + if (d.rotation() == 0) { d.texture = window()->createTextureFromImage(d.image); - } else if (d.orientation == 180) { + } else if (d.rotation() == 180) { d.texture = window()->createTextureFromImage(d.image.mirrored(true, true)); } static_cast(d.node)->setTexture(d.texture); diff -Nru qtav-1.12.0+ds/qml/QuickFBORenderer.cpp qtav-1.13.0+ds/qml/QuickFBORenderer.cpp --- qtav-1.12.0+ds/qml/QuickFBORenderer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QuickFBORenderer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -77,10 +77,10 @@ void setupAspectRatio() { //TODO: call when out_rect, renderer_size, orientation changed matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); - if (orientation) - matrix.rotate(orientation, 0, 0, 1); // Z axis + if (rotation()) + matrix.rotate(rotation(), 0, 0, 1); // Z axis // FIXME: why x/y is mirrored? - if (orientation%180) + if (rotation()%180) matrix.scale(-1, 1); else matrix.scale(1, -1); @@ -151,11 +151,31 @@ DPTR_D(QuickFBORenderer); if (d.source == source) return; + + AVPlayer* p0 = 0; + p0 = qobject_cast(d.source); + if (!p0) { + QmlAVPlayer* qp0 = qobject_cast(d.source); + if (qp0) + p0 = qp0->player(); + } + if(p0) + p0->removeVideoRenderer(this); + d.source = source; Q_EMIT sourceChanged(); if (!source) return; - ((QmlAVPlayer*)source)->player()->addVideoRenderer(this); + AVPlayer* p = qobject_cast(source); + if (!p) { + QmlAVPlayer* qp = qobject_cast(source); + if (!qp) { + qWarning("source MUST be of type AVPlayer or QmlAVPlayer"); + return; + } + p = qp->player(); + } + p->addVideoRenderer(this); } QuickFBORenderer::FillMode QuickFBORenderer::fillMode() const @@ -190,7 +210,7 @@ // Just normalize and use that function // m_nativeSize is transposed in some orientations - if (orientation()%180 == 0) + if (d_func().rotation()%180 == 0) return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height())); else return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width())); @@ -206,7 +226,7 @@ { qreal dx = point.x(); qreal dy = point.y(); - if (orientation()%180 == 0) { + if (d_func().rotation()%180 == 0) { dx *= contentRect().width(); dy *= contentRect().height(); } else { @@ -214,7 +234,7 @@ dy *= contentRect().width(); } - switch (orientation()) { + switch (d_func().rotation()) { case 0: default: return contentRect().topLeft() + QPointF(dx, dy); @@ -236,7 +256,7 @@ QPointF QuickFBORenderer::mapPointToSource(const QPointF &point) const { QPointF norm = mapPointToSourceNormalized(point); - if (orientation()%180 == 0) + if (d_func().rotation()%180 == 0) return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height()); else return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width()); @@ -256,7 +276,7 @@ // Normalize the item source point qreal nx = (point.x() - contentRect().x()) / contentRect().width(); qreal ny = (point.y() - contentRect().y()) / contentRect().height(); - switch (orientation()) { + switch (d_func().rotation()) { case 0: default: return QPointF(nx, ny); diff -Nru qtav-1.12.0+ds/qml/QuickFilter.cpp qtav-1.13.0+ds/qml/QuickFilter.cpp --- qtav-1.12.0+ds/qml/QuickFilter.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QuickFilter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,3 +1,24 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2017 Wang Bin + +* This file is part of QtAV (from 2016) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + #include "QmlAV/QuickFilter.h" #include "QtAV/private/Filter_p.h" #include "QtAV/LibAVFilter.h" @@ -11,6 +32,7 @@ { public: QuickVideoFilterPrivate() : type(QuickVideoFilter::AVFilter) + , user_filter(NULL) , avfilter(new LibAVFilterVideo()) , glslfilter(new GLSLFilter()) { @@ -19,7 +41,7 @@ QuickVideoFilter::FilterType type; VideoFilter *filter; - QScopedPointer user_filter; + VideoFilter *user_filter; // life time is managed by qml QScopedPointer avfilter; QScopedPointer glslfilter; }; @@ -55,7 +77,7 @@ else if (value == AVFilter) d.filter = d.avfilter.data(); else - d.filter = d.user_filter.data(); + d.filter = d.user_filter; Q_EMIT typeChanged(); } @@ -76,15 +98,17 @@ VideoFilter* QuickVideoFilter::userFilter() const { - return d_func().user_filter.data(); + return d_func().user_filter; } void QuickVideoFilter::setUserFilter(VideoFilter *f) { DPTR_D(QuickVideoFilter); - if (d.user_filter.data() == f) + if (d.user_filter == f) return; - d.user_filter.reset(f); + d.user_filter = f; + if (d.type == UserFilter) + d.filter = d.user_filter; Q_EMIT userFilterChanged(); } @@ -115,6 +139,7 @@ public: QuickAudioFilterPrivate() : AudioFilterPrivate() , type(QuickAudioFilter::AVFilter) + , user_filter(NULL) , avfilter(new LibAVFilterAudio()) { filter = avfilter.data(); @@ -122,7 +147,7 @@ QuickAudioFilter::FilterType type; AudioFilter *filter; - QScopedPointer user_filter; + AudioFilter* user_filter; // life time is managed by qml QScopedPointer avfilter; }; @@ -147,7 +172,7 @@ if (value == AVFilter) d.filter = d.avfilter.data(); else - d.filter = d.user_filter.data(); + d.filter = d.user_filter; Q_EMIT typeChanged(); } @@ -168,15 +193,17 @@ AudioFilter *QuickAudioFilter::userFilter() const { - return d_func().user_filter.data(); + return d_func().user_filter; } void QuickAudioFilter::setUserFilter(AudioFilter *f) { DPTR_D(QuickAudioFilter); - if (d.user_filter.data() == f) + if (d.user_filter == f) return; - d.user_filter.reset(f); + d.user_filter = f; + if (d.type == UserFilter) + d.filter = d.user_filter; Q_EMIT userFilterChanged(); } diff -Nru qtav-1.12.0+ds/qml/QuickVideoPreview.cpp qtav-1.13.0+ds/qml/QuickVideoPreview.cpp --- qtav-1.12.0+ds/qml/QuickVideoPreview.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/QuickVideoPreview.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -28,7 +28,8 @@ { connect(&m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged())); connect(&m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame))); - connect(&m_extractor, SIGNAL(error()), SLOT(displayNoFrame())); + connect(&m_extractor, SIGNAL(error(const QString &)), SLOT(displayNoFrame())); + connect(&m_extractor, SIGNAL(aborted(const QString &)), SLOT(displayNoFrame())); connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame())); } diff -Nru qtav-1.12.0+ds/qml/Video.qml qtav-1.13.0+ds/qml/Video.qml --- qtav-1.12.0+ds/qml/Video.qml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qml/Video.qml 2019-07-11 00:58:59.000000000 +0000 @@ -89,6 +89,8 @@ property alias internalAudioTracks: player.internalAudioTracks property alias externalAudioTracks: player.externalAudioTracks property alias internalVideoTracks: player.internalVideoTracks + property alias internalSubtitleTracks: player.internalSubtitleTracks + property alias internalSubtitleTrack: player.internalSubtitleTrack /*** Properties of VideoOutput ***/ /*! \qmlproperty enumeration Video::fillMode diff -Nru qtav-1.12.0+ds/qtc_packaging/common/changelog qtav-1.13.0+ds/qtc_packaging/common/changelog --- qtav-1.12.0+ds/qtc_packaging/common/changelog 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/common/changelog 2019-07-11 00:58:59.000000000 +0000 @@ -1,3 +1,20 @@ +version 1.13.0 2019-07-11 + +- add python bindings +- more apis for qml player +- auto rotate video +- apple store +- fix ios plugin not found +- support chapters +- muxer, encoder, transcoder improvements +- compatible with new ffmpeg +- videotoolbox: hevc, +- cuda: new devices +- android: no longer depends on private qt module +- fix opensl error +- mediacodec: 0-copy via a plugin from https://github.com/wang-bin/mdk-sdk + + version 1.12.0 2017-06-20 Changelog diff -Nru qtav-1.12.0+ds/qtc_packaging/common/README qtav-1.13.0+ds/qtc_packaging/common/README --- qtav-1.12.0+ds/qtc_packaging/common/README 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/common/README 2019-07-11 00:58:59.000000000 +0000 @@ -33,7 +33,7 @@ - Multiple video outputs for 1 player - Video eq(software and OpenGL): brightness, contrast, saturation, hue - QML support. Most playback APIs are compatible with QtMultimedia module -- Compatiblity: QtAV can be built with both Qt4 and Qt5, FFmpeg(>=1.0) and [Libav](http://libav.org) (>=9.0). Latest FFmpeg release is recommended. +- Compatibility: QtAV can be built with both Qt4 and Qt5, FFmpeg(>=1.0) and [Libav](http://libav.org) (>=9.0). Latest FFmpeg release is recommended. ### Extensible Framework @@ -155,6 +155,4 @@ > Copyright © Wang Bin wbsecg1@gmail.com -> Shanghai University->S3 Graphics->Deepin, Shanghai, China - > 2013-01-21 diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/config/config.xml qtav-1.13.0+ds/qtc_packaging/ifw/config/config.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/config/config.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/config/config.xml 2019-07-11 00:58:59.000000000 +0000 @@ -1,7 +1,7 @@ QtAV - 1.12.0 + 1.13.0 QtAV Multimedia framework WangBin wbsecg1@gmail.com http://qtav.org diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product/meta/package.xml qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product/meta/package.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product/meta/package.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product/meta/package.xml 2019-07-11 00:58:59.000000000 +0000 @@ -2,8 +2,8 @@ QtAV Install QtAV multimedia library - 1.12.0-0 - 2017-06-20 + 1.13.0-0 + 2019-07-11 com.qtav.product diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.bat qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.bat --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.bat 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.bat 2019-07-11 00:58:59.000000000 +0000 @@ -23,8 +23,12 @@ copy /y bin\*portaudio*.dll %QTDIR%\bin xcopy /syi include %QTDIR%\include copy /y lib\*Qt*AV*.lib %QTDIR%\lib +copy /y lib\QtAV1.lib %QTDIR%\lib\Qt5AV.lib +copy /y lib\QtAVWidgets1.lib %QTDIR%\lib\Qt5AVWidgets.lib copy /y lib\*Qt*AV*.a %QTDIR%\lib -xcopy /syi qml\QtAV %QTDIR%\qml\QtAV +copy /y lib\libQtAV1.a %QTDIR%\lib\libQt5AV.a +copy /y lib\libQtAVWidgets1.a %QTDIR%\lib\libQt5AVWidgets.a +xcopy /syi bin\QtAV %QTDIR%\qml\QtAV xcopy /syi mkspecs %QTDIR%\mkspecs @echo QtAV SDK is installed goto END diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/package.xml qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/package.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/package.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/package.xml 2019-07-11 00:58:59.000000000 +0000 @@ -2,8 +2,8 @@ Development files Install QtAV headers and lib. - 1.12.0-0 - 2017-06-20 + 1.13.0-0 + 2019-07-11 com.qtav.product.dev script diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/package.xml qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/package.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/package.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/package.xml 2019-07-11 00:58:59.000000000 +0000 @@ -2,8 +2,8 @@ Examples Install QtAV examples. - 1.12.0-0 - 2017-06-20 + 1.13.0-0 + 2019-07-11 com.qtav.product.examples script diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.player/meta/package.xml qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.player/meta/package.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.player/meta/package.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.player/meta/package.xml 2019-07-11 00:58:59.000000000 +0000 @@ -2,8 +2,8 @@ Player Default player. - 1.12.0-0 - 2017-06-20 + 1.13.0-0 + 2019-07-11 com.qtav.product.player true true diff -Nru qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/package.xml qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/package.xml --- qtav-1.12.0+ds/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/package.xml 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/package.xml 2019-07-11 00:58:59.000000000 +0000 @@ -2,8 +2,8 @@ Runtime library Install QtAV runtime library. - 1.12.0-0 - 2017-06-20 + 1.13.0-0 + 2019-07-11 com.qtav.product.runtime zh_CN.qm diff -Nru qtav-1.12.0+ds/README.md qtav-1.13.0+ds/README.md --- qtav-1.12.0+ds/README.md 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/README.md 2019-07-11 00:58:59.000000000 +0000 @@ -33,7 +33,7 @@ - Multiple video outputs for 1 player - Video eq(software and OpenGL): brightness, contrast, saturation, hue - QML support. Most playback APIs are compatible with QtMultimedia module -- Compatiblity: QtAV can be built with both Qt4 and Qt5, FFmpeg(>=1.0) and [Libav](http://libav.org) (>=9.0). Latest FFmpeg release is recommended. +- Compatibility: QtAV can be built with both Qt4 and Qt5, FFmpeg(>=1.0) and [Libav](http://libav.org) (>=9.0). Latest FFmpeg release is recommended. ### Extensible Framework @@ -155,6 +155,4 @@ > Copyright © Wang Bin wbsecg1@gmail.com -> Shanghai University->S3 Graphics->Deepin, Shanghai, China - > 2013-01-21 diff -Nru qtav-1.12.0+ds/src/AudioFormat.cpp qtav-1.13.0+ds/src/AudioFormat.cpp --- qtav-1.12.0+ds/src/AudioFormat.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AudioFormat.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -31,8 +31,6 @@ namespace QtAV { -const qint64 kHz = 1000000LL; - typedef struct { AVSampleFormat avfmt; AudioFormat::SampleFormat fmt; diff -Nru qtav-1.12.0+ds/src/AudioFrame.cpp qtav-1.13.0+ds/src/AudioFrame.cpp --- qtav-1.12.0+ds/src/AudioFrame.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AudioFrame.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -133,26 +133,71 @@ AudioFrame AudioFrame::clone() const { + return mid(0, -1); +} + +AudioFrame AudioFrame::mid(int pos, int len) const +{ Q_D(const AudioFrame); + if (d->format.sampleFormatFFmpeg() == AV_SAMPLE_FMT_NONE - || d->format.channels() <= 0) + || d->format.channels() <= 0) { return AudioFrame(); - if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0) + } + + if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0 || len == 0) { return AudioFrame(format()); - QByteArray buf(bytesPerLine()*planeCount(), 0); + } + + int bufSize = bytesPerLine(); + int posBytes = 0; + if (pos > 0) { + posBytes = pos * d->format.bytesPerSample(); + bufSize -= posBytes; + } else { + pos = 0; + } + + int lenBytes = len * d->format.bytesPerSample(); + if (len > 0 && lenBytes < bufSize) { + bufSize = lenBytes; + } else { + lenBytes = bufSize; + } + + QByteArray buf(bufSize * planeCount(), 0); char *dst = buf.data(); //must before buf is shared, otherwise data will be detached. + for (int i = 0; i < planeCount(); ++i) { - const int plane_size = bytesPerLine(i); - memcpy(dst, constBits(i), plane_size); - dst += plane_size; + memcpy(dst, constBits(i) + posBytes, lenBytes); + dst += lenBytes; } + AudioFrame f(d->format, buf); - f.setSamplesPerChannel(samplesPerChannel()); - f.setTimestamp(timestamp()); + f.setSamplesPerChannel(bufSize / d->format.bytesPerSample()); + f.setTimestamp(d->timestamp + (qreal) d->format.durationForBytes(posBytes) / AudioFormat::kHz); // meta data? return f; } +void AudioFrame::prepend(AudioFrame &other) +{ + Q_D(AudioFrame); + + if (d->format != other.format()) { + qWarning() << "To prepend a frame it must have the same audio format"; + return; + } + + d->data.prepend(other.data()); + d->samples_per_ch += other.samplesPerChannel(); + d->timestamp = other.timestamp(); + + for (int i = 0; i < planeCount(); i++) { + d->line_sizes[i] += other.bytesPerLine(i); + } +} + AudioFormat AudioFrame::format() const { return d_func()->format; diff -Nru qtav-1.12.0+ds/src/AudioThread.cpp qtav-1.13.0+ds/src/AudioThread.cpp --- qtav-1.12.0+ds/src/AudioThread.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AudioThread.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -78,7 +78,7 @@ //No decoder or output. No audio output is ok, just display picture if (!d.dec || !d.dec->isAvailable() || !d.outputSet) return; - resetState(); + // resetState(); // we can't reset the thread state from here Q_ASSERT(d.clock != 0); d.init(); Packet pkt; diff -Nru qtav-1.12.0+ds/src/AVDemuxer.cpp qtav-1.13.0+ds/src/AVDemuxer.cpp --- qtav-1.12.0+ds/src/AVDemuxer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVDemuxer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2017 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -326,11 +326,15 @@ class AVInitializer { public: AVInitializer() { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); +#endif #if QTAV_HAVE(AVDEVICE) avdevice_register_all(); #endif +#if !AVFORMAT_STATIC_REGISTER av_register_all(); +#endif avformat_network_init(); } ~AVInitializer() { @@ -352,10 +356,16 @@ static QStringList exts; static QStringList fmts; if (exts.isEmpty() && fmts.isEmpty()) { - av_register_all(); // MUST register all input/output formats - AVInputFormat *i = NULL; QStringList e, f; +#if AVFORMAT_STATIC_REGISTER + const AVInputFormat *i = NULL; + void* it = NULL; + while ((i = av_demuxer_iterate(&it))) { +#else + AVInputFormat *i = NULL; + av_register_all(); // MUST register all input/output formats while ((i = av_iformat_next(i))) { +#endif if (i->extensions) e << QString::fromLatin1(i->extensions).split(QLatin1Char(','), QString::SkipEmptyParts); if (i->name) @@ -400,7 +410,9 @@ #if QTAV_HAVE(AVDEVICE) protocols << QStringLiteral("avdevice"); #endif +#if !AVFORMAT_STATIC_REGISTER av_register_all(); // MUST register all input/output formats +#endif void* opq = 0; const char* protocol = avio_enum_protocols(&opq, 0); while (protocol) { diff -Nru qtav-1.12.0+ds/src/AVDemuxThread.cpp qtav-1.13.0+ds/src/AVDemuxThread.cpp --- qtav-1.12.0+ds/src/AVDemuxThread.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVDemuxThread.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -211,37 +211,48 @@ newSeekRequest(new stepBackwardTask(this, pre_pts)); } -void AVDemuxThread::seek(qint64 pos, SeekType type) +void AVDemuxThread::seek(qint64 external_pos, qint64 pos, SeekType type) { - end = false; - // queue maybe blocked by put() - if (audio_thread) { - audio_thread->packetQueue()->clear(); - } - if (video_thread) { - video_thread->packetQueue()->clear(); - } class SeekTask : public QRunnable { public: - SeekTask(AVDemuxThread *dt, qint64 t, SeekType st) + SeekTask(AVDemuxThread *dt, qint64 external_pos, qint64 t, SeekType st) : demux_thread(dt) , type(st) , position(t) + , external_pos(external_pos) {} void run() { + // queue maybe blocked by put() + if (demux_thread->audio_thread) { + demux_thread->audio_thread->packetQueue()->clear(); + } + if (demux_thread->video_thread) { + demux_thread->video_thread->packetQueue()->clear(); + } if (demux_thread->video_thread) demux_thread->video_thread->setDropFrameOnSeek(true); - demux_thread->seekInternal(position, type); + demux_thread->seekInternal(position, type, external_pos); } private: AVDemuxThread *demux_thread; SeekType type; qint64 position; + qint64 external_pos; }; - newSeekRequest(new SeekTask(this, pos, type)); + + end = false; + // queue maybe blocked by put() + // These must be here or seeking while paused will not update the video frame + if (audio_thread) { + audio_thread->packetQueue()->clear(); + } + if (video_thread) { + video_thread->packetQueue()->clear(); + } + newSeekRequest(new SeekTask(this, external_pos, pos, type)); } -void AVDemuxThread::seekInternal(qint64 pos, SeekType type) +void AVDemuxThread::seekInternal(qint64 pos, SeekType type, qint64 external_pos) { AVThread* av[] = { audio_thread, video_thread}; qDebug("seek to %s %lld ms (%f%%)", QTime(0, 0, 0).addMSecs(pos).toString().toUtf8().constData(), pos, double(pos - demuxer->startTime())/double(demuxer->duration())*100.0); @@ -264,6 +275,9 @@ Q_ASSERT(sync_id != 0); qDebug("demuxer sync id: %d/%d", sync_id, t->clock()->syncId()); t->packetQueue()->clear(); + if (external_pos != std::numeric_limits < qint64 >::min() ) + t->clock()->updateExternalClock(qMax(qint64(0), external_pos)); + t->clock()->updateValue(double(pos)/1000.0); t->requestSeek(); // TODO: the first frame (key frame) will not be decoded correctly if flush() is called. //PacketBuffer *pb = t->packetQueue(); diff -Nru qtav-1.12.0+ds/src/AVDemuxThread.h qtav-1.13.0+ds/src/AVDemuxThread.h --- qtav-1.12.0+ds/src/AVDemuxThread.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVDemuxThread.h 2019-07-11 00:58:59.000000000 +0000 @@ -46,7 +46,7 @@ AVThread* videoThread(); void stepForward(); // show next video frame and pause void stepBackward(); - void seek(qint64 pos, SeekType type); //ms + void seek(qint64 external_pos, qint64 pos, SeekType type); //ms //AVDemuxer* demuxer bool isPaused() const; bool isEnd() const; @@ -84,7 +84,7 @@ void setAVThread(AVThread *&pOld, AVThread* pNew); void newSeekRequest(QRunnable *r); void processNextSeekTask(); - void seekInternal(qint64 pos, SeekType type); //must call in AVDemuxThread + void seekInternal(qint64 pos, SeekType type, qint64 external_pos = std::numeric_limits < qint64 >::min()); //must call in AVDemuxThread void pauseInternal(bool value); bool paused; diff -Nru qtav-1.12.0+ds/src/AVMuxer.cpp qtav-1.13.0+ds/src/AVMuxer.cpp --- qtav-1.12.0+ds/src/AVMuxer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVMuxer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2015) @@ -43,6 +43,7 @@ , started(false) , eof(false) , media_changed(true) + , open(false) , format_ctx(0) , format(0) , io(0) @@ -50,7 +51,9 @@ , aenc(0) , venc(0) { +#if !AVFORMAT_STATIC_REGISTER av_register_all(); +#endif } ~Private() { //delete interrupt_hanlder; @@ -73,6 +76,7 @@ bool started; bool eof; bool media_changed; + bool open; AVFormatContext *format_ctx; //copy the info, not parse the file when constructed, then need member vars QString file; @@ -122,7 +126,7 @@ c->time_base = s->time_base; /* Some formats want stream headers to be separate. */ if (ctx->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // expose avctx to encoder and set properties in encoder? // list codecs for a given format in ui return s; @@ -143,6 +147,10 @@ c->height = venc->height(); /// MUST set after encoder is open to ensure format is valid and the same c->pix_fmt = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(venc->pixelFormat()); + + // Set avg_frame_rate based on encoder frame_rate + s->avg_frame_rate = av_d2q(venc->frameRate(), venc->frameRate()*1001.0+2); + video_streams.push_back(s->id); } } @@ -157,6 +165,18 @@ c->channel_layout = aenc->audioFormat().channelLayoutFFmpeg(); c->channels = aenc->audioFormat().channels(); c->bits_per_raw_sample = aenc->audioFormat().bytesPerSample()*8; // need?? + + AVCodecContext *avctx = (AVCodecContext *) aenc->codecContext(); +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56,5,100) + c->initial_padding = avctx->initial_padding; +#else + c->delay = avctx->delay; +#endif + if (avctx->extradata_size) { + c->extradata = avctx->extradata; + c->extradata_size = avctx->extradata_size; + } + audio_streams.push_back(s->id); } } @@ -168,10 +188,16 @@ static QStringList exts; static QStringList fmts; if (exts.isEmpty() && fmts.isEmpty()) { + QStringList e, f; +#if AVFORMAT_STATIC_REGISTER + const AVOutputFormat *o = NULL; + void* it = NULL; + while ((o = av_muxer_iterate(&it))) { +#else av_register_all(); // MUST register all input/output formats AVOutputFormat *o = NULL; - QStringList e, f; while ((o = av_oformat_next(o))) { +#endif if (o->extensions) e << QString::fromLatin1(o->extensions).split(QLatin1Char(','), QString::SkipEmptyParts); if (o->name) @@ -221,7 +247,9 @@ #if QTAV_HAVE(AVDEVICE) protocols << QStringLiteral("avdevice"); #endif +#if !AVFORMAT_STATIC_REGISTER av_register_all(); // MUST register all input/output formats +#endif void* opq = 0; const char* protocol = avio_enum_protocols(&opq, 1); while (protocol) { @@ -323,6 +351,11 @@ d->format_forced.clear(); } d->io->setProperty("device", QVariant::fromValue(device)); //open outside? + + if (device->isWritable()) { + d->io->setAccessMode(MediaIO::Write); + } + return d->media_changed; } @@ -392,6 +425,7 @@ // d->format_ctx->start_time_realtime AV_ENSURE_OK(avformat_write_header(d->format_ctx, &d->dict), false); d->started = false; + d->open = true; return true; } @@ -400,6 +434,7 @@ { if (!isOpen()) return true; + d->open = false; av_write_trailer(d->format_ctx); // close AVCodecContext* in encoder // custom io will call avio_close in ~MediaIO() @@ -421,7 +456,7 @@ bool AVMuxer::isOpen() const { - return d->format_ctx; + return d->open; } bool AVMuxer::writeAudio(const QtAV::Packet& packet) diff -Nru qtav-1.12.0+ds/src/AVPlayer.cpp qtav-1.13.0+ds/src/AVPlayer.cpp --- qtav-1.12.0+ds/src/AVPlayer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVPlayer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,4 +1,4 @@ -/****************************************************************************** +/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin @@ -45,6 +45,9 @@ #include "QtAV/private/AVCompat.h" #include "utils/internal.h" #include "utils/Logger.h" +extern "C" { +#include +} #define EOF_ISSUE_SOLVED 0 namespace QtAV { @@ -680,6 +683,7 @@ d->video_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::VideoStream); Q_EMIT internalVideoTracksChanged(d->video_tracks); Q_EMIT durationChanged(duration()); + Q_EMIT chaptersChanged(chapters()); // setup parameters from loaded media d->media_start_pts = d->demuxer.startTime(); // TODO: what about other proctols? some vob duration() == 0 @@ -715,6 +719,7 @@ d->vdec = 0; } d->demuxer.unload(); + Q_EMIT chaptersChanged(0); Q_EMIT durationChanged(0LL); // for ui, slider is invalid. use stopped instead, and remove this signal here? // ?? d->audio_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::AudioStream); @@ -857,9 +862,7 @@ if (relativeTimeMode()) pos_pts += absoluteMediaStartPosition(); d->seeking = true; - masterClock()->updateValue(double(pos_pts)/1000.0); //what is duration == 0 - masterClock()->updateExternalClock(pos_pts); //in msec. ignore usec part using t/1000 - d->read_thread->seek(pos_pts, seekType()); + d->read_thread->seek(position,pos_pts, seekType()); Q_EMIT positionChanged(position); //emit relative position } @@ -1231,13 +1234,14 @@ d->vthread->start(); } - d->read_thread->setMediaEndAction(mediaEndAction()); - d->read_thread->start(); - if (d->demuxer.audioCodecContext() && d->athread) d->athread->waitForStarted(); if (d->demuxer.videoCodecContext() && d->vthread) d->vthread->waitForStarted(); + + d->read_thread->setMediaEndAction(mediaEndAction()); + d->read_thread->start(); + /// demux thread not started, seek tasks will be cleared d->read_thread->waitForStarted(); if (d->timer_id < 0) { @@ -1385,6 +1389,42 @@ } } +void AVPlayer::seekChapter(int incr) +{ + if (!chapters()) + return; + + qint64 pos = masterClock()->value() * AV_TIME_BASE; + int i = 0; + + AVFormatContext *ic = d->demuxer.formatContext(); + + AVRational av_time_base_q; + av_time_base_q.num = 1; + av_time_base_q.den = AV_TIME_BASE; + + /* find the current chapter */ + for (i = 0; i < chapters(); ++i) { + AVChapter *ch = ic->chapters[i]; + if (av_compare_ts(pos, av_time_base_q, ch->start, ch->time_base) < 0) { + --i; + break; + } + } + + i += incr; + //i = FFMAX(i, 0); + if (i <= 0) + i = 0; + if (i >= chapters()) + return; + + //av_log(NULL, AV_LOG_VERBOSE, "Seeking to chapter %d.\n", i); + qDebug() << QString::fromLatin1("Seeking to chapter : ") << QString::number(i); + setPosition(av_rescale_q(ic->chapters[i]->start, ic->chapters[i]->time_base, + av_time_base_q) / 1000); +} + void AVPlayer::stop() { // check d->timer_id, <0 return? @@ -1532,6 +1572,20 @@ seek(position() - kSeekMS); } +void AVPlayer::seekNextChapter() +{ + if (chapters() <= 1) + return; + seekChapter(1); +} + +void AVPlayer::seekPreviousChapter() +{ + if (chapters() <= 1) + return; + seekChapter(-1); +} + void AVPlayer::setSeekType(SeekType type) { d->seek_type = type; @@ -1635,6 +1689,11 @@ return d->saturation; } +unsigned int AVPlayer::chapters() const +{ + return d->demuxer.formatContext()->nb_chapters; +} + void AVPlayer::setSaturation(int val) { if (d->saturation == val) diff -Nru qtav-1.12.0+ds/src/AVPlayerPrivate.cpp qtav-1.13.0+ds/src/AVPlayerPrivate.cpp --- qtav-1.12.0+ds/src/AVPlayerPrivate.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVPlayerPrivate.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) @@ -28,6 +28,11 @@ #include "QtAV/MediaIO.h" #include "QtAV/VideoCapture.h" #include "QtAV/private/AVCompat.h" +#if AV_MODULE_CHECK(LIBAVFORMAT, 55, 18, 0, 39, 100) +extern "C" { +#include +} +#endif #include "utils/Logger.h" namespace QtAV { @@ -325,6 +330,15 @@ statistics.video_only.pix_fmt = QLatin1String(av_get_pix_fmt_name(avctx->pix_fmt)); statistics.video_only.height = avctx->height; statistics.video_only.width = avctx->width; + statistics.video_only.rotate = 0; +#if AV_MODULE_CHECK(LIBAVFORMAT, 55, 18, 0, 39, 100) + quint8 *sd = av_stream_get_side_data(demuxer.formatContext()->streams[s], AV_PKT_DATA_DISPLAYMATRIX, NULL); + if (sd) { + double r = av_display_rotation_get((qint32*)sd); + if (!qIsNaN(r)) + statistics.video_only.rotate = ((int)r + 360) % 360; + } +#endif } // notify statistics change after audio/video thread is set bool AVPlayer::Private::setupAudioThread(AVPlayer *player) @@ -414,6 +428,10 @@ } } } + + // we set the thre state before the thread start, + // as it maybe clear after by AVDemuxThread starting + athread->resetState(); athread->setDecoder(adec); setAVOutput(ao, ao, athread); updateBufferValue(athread->packetQueue()); @@ -445,6 +463,8 @@ QVariantMap t; t[QStringLiteral("id")] = info.size(); t[QStringLiteral("file")] = demuxer->fileName(); + t[QStringLiteral("stream_index")] = QVariant(s); + AVStream *stream = demuxer->formatContext()->streams[s]; AVCodecContext *ctx = stream->codec; if (ctx) { @@ -589,6 +609,10 @@ } QObject::connect(vthread, SIGNAL(finished()), player, SLOT(tryClearVideoRenderers()), Qt::DirectConnection); } + + // we set the thre state before the thread start + // as it maybe clear after by AVDemuxThread starting + vthread->resetState(); vthread->setDecoder(vdec); vthread->setBrightness(brightness); diff -Nru qtav-1.12.0+ds/src/AVThread.cpp qtav-1.13.0+ds/src/AVThread.cpp --- qtav-1.12.0+ds/src/AVThread.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVThread.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -364,7 +364,7 @@ void AVThread::waitAndCheck(ulong value, qreal pts) { DPTR_D(AVThread); - if (value <= 0) + if (value <= 0 || pts < 0) return; value += d.wait_err; d.wait_timer.restart(); @@ -382,7 +382,12 @@ us = qMin(us, ulong((double)(qMax(0, pts - d.clock->value()))*1000000.0)); //qDebug("us: %lu/%lu, pts: %f, clock: %f", us, ms-et.elapsed(), pts, d.clock->value()); processNextTask(); - us = qMin(us, (ms-d.wait_timer.elapsed())*1000); + const qint64 left = qint64(ms) - d.wait_timer.elapsed(); + if (left <= 0) { + us = 0; + break; + } + us = qMin(us, left*1000); } if (us > 0) usleep(us); diff -Nru qtav-1.12.0+ds/src/AVThread.h qtav-1.13.0+ds/src/AVThread.h --- qtav-1.12.0+ds/src/AVThread.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVThread.h 2019-07-11 00:58:59.000000000 +0000 @@ -81,6 +81,7 @@ qreal decodeFrameRate() const; //move to statistics? void setDropFrameOnSeek(bool value); + void resetState(); public slots: virtual void stop(); /*change pause state. the pause/continue action will do in the next loop*/ @@ -100,7 +101,6 @@ void onFinished(); protected: AVThread(AVThreadPrivate& d, QObject *parent = 0); - void resetState(); /* * If the pause state is true setted by pause(true), then block the thread and wait for pause state changed, i.e. pause(false) * and return true. Otherwise, return false immediatly. diff -Nru qtav-1.12.0+ds/src/AVTranscoder.cpp qtav-1.13.0+ds/src/AVTranscoder.cpp --- qtav-1.12.0+ds/src/AVTranscoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/AVTranscoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -286,7 +286,9 @@ // uninstall encoder filters first then encoders can be closed safely if (sourcePlayer()) { sourcePlayer()->uninstallFilter(d->afilter); + disconnect(sourcePlayer(), SIGNAL(stopped()), d->afilter, SLOT(finish())); sourcePlayer()->uninstallFilter(d->vfilter); + disconnect(sourcePlayer(), SIGNAL(stopped()), d->vfilter, SLOT(finish())); } if (d->afilter) d->afilter->finish(); //FIXME: thread of sync mode diff -Nru qtav-1.12.0+ds/src/CMakeLists.txt qtav-1.13.0+ds/src/CMakeLists.txt --- qtav-1.12.0+ds/src/CMakeLists.txt 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/CMakeLists.txt 2019-07-11 00:58:59.000000000 +0000 @@ -12,8 +12,8 @@ include_directories(${QTDIR}/include) #TODO: remove. use external/include get_filename_component(QTDIR "${QTDIR}" ABSOLUTE) -list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/external/include) -list(APPEND EXTRA_LIBS ${CMAKE_LIBRARY_PATH_FLAG}${CMAKE_SOURCE_DIR}/external/lib) +list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/external/include) +list(APPEND EXTRA_LIBS ${CMAKE_LIBRARY_PATH_FLAG}${QTAV_SOURCE_DIR}/external/lib) if(APPLE) if(IOS) #set_xcode_property(myioslib IPHONEOS_DEPLOYMENT_TARGET "8.0") @@ -22,9 +22,9 @@ list(APPEND EXTRA_LIBS -L/usr/local/lib) endif() endif() -if(EXISTS ${CMAKE_SOURCE_DIR}/contrib/capi/capi.h) +if(EXISTS ${QTAV_SOURCE_DIR}/contrib/capi/capi.h) set(HAVE_CAPI 1) - list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/capi) # TODO: only files use capi.h + list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/contrib/capi) # TODO: only files use capi.h list(APPEND EXTRA_DEFS -DQTAV_HAVE_CAPI=1) endif() @@ -324,7 +324,7 @@ if(WIN32 OR WindowsStore OR WindowsPhone) check_include_files(XAudio2.h HAVE_XAUDIO2_H) if(NOT HAVE_XAUDIO2_H) - list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/dxsdk) + list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/contrib/dxsdk) endif() message("Qt5Gui_EGL_INCLUDE_DIRS: ${Qt5Gui_EGL_INCLUDE_DIRS}") list(APPEND HEADERS @@ -473,7 +473,7 @@ set_source_files_properties(${RESOURCES_SOURCES} PROPERTIES GENERATED ON) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) - configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) + configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() # add HEADERS for moc add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) diff -Nru qtav-1.12.0+ds/src/codec/audio/AudioDecoder.cpp qtav-1.13.0+ds/src/codec/audio/AudioDecoder.cpp --- qtav-1.12.0+ds/src/codec/audio/AudioDecoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/audio/AudioDecoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -43,9 +43,14 @@ static QStringList codecs; if (!codecs.isEmpty()) return codecs; + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else avcodec_register_all(); - AVCodec* c = NULL; - while ((c=av_codec_next(c))) { + while ((c = av_codec_next(c))) { +#endif if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_AUDIO) continue; codecs.append(QString::fromLatin1(c->name)); diff -Nru qtav-1.12.0+ds/src/codec/audio/AudioDecoderFFmpeg.cpp qtav-1.13.0+ds/src/codec/audio/AudioDecoderFFmpeg.cpp --- qtav-1.12.0+ds/src/codec/audio/AudioDecoderFFmpeg.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/audio/AudioDecoderFFmpeg.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -63,7 +63,9 @@ : AudioDecoderPrivate() , frame(av_frame_alloc()) { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); +#endif } ~AudioDecoderFFmpegPrivate() { if (frame) { diff -Nru qtav-1.12.0+ds/src/codec/audio/AudioEncoder.cpp qtav-1.13.0+ds/src/codec/audio/AudioEncoder.cpp --- qtav-1.12.0+ds/src/codec/audio/AudioEncoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/audio/AudioEncoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2015) @@ -45,9 +45,14 @@ static QStringList codecs; if (!codecs.isEmpty()) return codecs; + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else avcodec_register_all(); - AVCodec* c = NULL; - while ((c=av_codec_next(c))) { + while ((c = av_codec_next(c))) { +#endif if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_AUDIO) continue; codecs.append(QString::fromLatin1(c->name)); @@ -80,4 +85,8 @@ return d_func().format_used; } +int AudioEncoder::frameSize() const +{ + return d_func().frame_size; +} } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/codec/audio/AudioEncoderFFmpeg.cpp qtav-1.13.0+ds/src/codec/audio/AudioEncoderFFmpeg.cpp --- qtav-1.12.0+ds/src/codec/audio/AudioEncoderFFmpeg.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/audio/AudioEncoderFFmpeg.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -53,7 +53,6 @@ public: AudioEncoderFFmpegPrivate() : AudioEncoderPrivate() - , frame_size(0) { avcodec_register_all(); // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264). @@ -62,7 +61,6 @@ bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; - int frame_size; // used if avctx->frame_size == 0 QByteArray buffer; }; @@ -153,8 +151,8 @@ } else { buffer_size = frame_size*format_used.bytesPerSample()*format_used.channels()*2+200; } - if (buffer_size < FF_MIN_BUFFER_SIZE) - buffer_size = FF_MIN_BUFFER_SIZE; + if (buffer_size < AV_INPUT_BUFFER_MIN_SIZE) + buffer_size = AV_INPUT_BUFFER_MIN_SIZE; buffer.resize(buffer_size); return true; } diff -Nru qtav-1.12.0+ds/src/codec/AVDecoder.cpp qtav-1.13.0+ds/src/codec/AVDecoder.cpp --- qtav-1.12.0+ds/src/codec/AVDecoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/AVDecoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -47,7 +47,9 @@ AVDecoder::AVDecoder(AVDecoderPrivate &d) :DPTR_INIT(&d) { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); // avcodec_find_decoder will always be used +#endif } AVDecoder::~AVDecoder() diff -Nru qtav-1.12.0+ds/src/codec/video/SurfaceInteropCV.cpp qtav-1.13.0+ds/src/codec/video/SurfaceInteropCV.cpp --- qtav-1.12.0+ds/src/codec/video/SurfaceInteropCV.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/SurfaceInteropCV.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) @@ -61,16 +61,18 @@ InteropResource* InteropResource::create(InteropType type) { if (type == InteropAuto) { -#ifdef Q_OS_MACX - type = InteropIOSurface; -#else - type = InteropCVPixelBuffer; type = InteropCVOpenGLES; +#if defined(Q_OS_MACX) + type = InteropIOSurface; +#endif +#if defined(__builtin_available) + if (__builtin_available(iOS 11, macOS 10.6, *)) + type = InteropIOSurface; #endif } switch (type) { case InteropCVPixelBuffer: return CreateInteropCVPixelbuffer(); -#ifdef Q_OS_MACX +#if defined(Q_OS_MACX) || defined(__IPHONE_11_0) case InteropIOSurface: return CreateInteropIOSurface(); //case InteropCVOpenGL: return CreateInteropCVOpenGL(); #else diff -Nru qtav-1.12.0+ds/src/codec/video/SurfaceInteropIOSurface.cpp qtav-1.13.0+ds/src/codec/video/SurfaceInteropIOSurface.cpp --- qtav-1.12.0+ds/src/codec/video/SurfaceInteropIOSurface.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/SurfaceInteropIOSurface.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -/****************************************************************************** - QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin - -* This file is part of QtAV (from 2016) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -******************************************************************************/ - -#include "SurfaceInteropCV.h" -#include -#include "QtAV/VideoFrame.h" -#include "opengl/OpenGLHelper.h" - -namespace QtAV { -namespace cv { -VideoFormat::PixelFormat format_from_cv(int cv); - -// https://www.opengl.org/registry/specs/APPLE/rgb_422.txt -// https://www.opengl.org/registry/specs/APPLE/ycbcr_422.txt uyvy: UNSIGNED_SHORT_8_8_REV_APPLE, yuy2: GL_UNSIGNED_SHORT_8_8_APPLE -// check extension GL_APPLE_rgb_422 and rectangle? -class InteropResourceIOSurface Q_DECL_FINAL : public InteropResource -{ -public: - bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE; - bool mapToTexture2D() const Q_DECL_OVERRIDE { return false;} - bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE; - GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE - { - Q_UNUSED(fmt); - Q_UNUSED(plane); - Q_UNUSED(planeWidth); - Q_UNUSED(planeHeight); - GLuint tex = 0; - DYGL(glGenTextures(1, &tex)); - return tex; - } -}; - -InteropResource* CreateInteropIOSurface() -{ - return new InteropResourceIOSurface(); -} - -bool InteropResourceIOSurface::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt) -{ - switch (cvfmt) { - case '2vuy': - case 'yuvs': { - *outFmt = VideoFormat::Format_VYU; - if (strides[0] <= 0) - strides[0] = 4*width; //RGB layout: BRGX - else - strides[0] *= 2; - } - break; - default: - return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt); - } - return true; -} - -bool InteropResourceIOSurface::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) -{ - Q_UNUSED(w); - Q_UNUSED(h); - const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf); - GLint iformat; - GLenum format, dtype; - getParametersGL(pixfmt, &iformat, &format, &dtype, plane); - switch (pixfmt) { - case '2vuy': - case 'yuvs': - iformat = GL_RGB8; // ES2 requires internal format and format are the same. OSX can use internal format GL_RGB or sized GL_RGB8 - format = GL_RGB_422_APPLE; - dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE; - break; - // macOS: GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV - // GL_YCBCR_422_APPLE: convert to rgb texture internally (bt601). only supports OSX - // GL_RGB_422_APPLE: raw yuv422 texture - case 'BGRA': - iformat = GL_RGBA8; - format = GL_BGRA; - dtype = GL_UNSIGNED_INT_8_8_8_8_REV; - break; - default: - break; - } - const GLenum target = GL_TEXTURE_RECTANGLE; - DYGL(glBindTexture(target, *tex)); - const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane); - const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane); - //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype); - - const IOSurfaceRef surface = CVPixelBufferGetIOSurface(buf); - CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, planeW, planeH, format, dtype, surface, plane); - if (err != kCGLNoError) { - qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err)); - } - DYGL(glBindTexture(target, 0)); - return true; -} -} // namespace cv -} // namespace QtAV diff -Nru qtav-1.12.0+ds/src/codec/video/SurfaceInteropIOSurface.mm qtav-1.13.0+ds/src/codec/video/SurfaceInteropIOSurface.mm --- qtav-1.12.0+ds/src/codec/video/SurfaceInteropIOSurface.mm 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/SurfaceInteropIOSurface.mm 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,164 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2017 Wang Bin + +* This file is part of QtAV (from 2016) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ +#define IOS_USE_PRIVATE 0 // private symbols are forbidden by app store +#include "SurfaceInteropCV.h" +#ifdef Q_OS_IOS +#import +#include +# ifdef __IPHONE_11_0 // always defined in new sdk +# import +# endif //__IPHONE_11_0 +# if COREVIDEO_SUPPORTS_IOSURFACE && IOS_USE_PRIVATE +// declare the private API if IOSurface is supported in SDK. Thus we can use IOSurface on any iOS version even with old SDK and compiler +@interface EAGLContext() +- (BOOL)texImageIOSurface:(IOSurfaceRef)ioSurface target:(NSUInteger)target internalFormat:(NSUInteger)internalFormat width:(uint32_t)width height:(uint32_t)height format:(NSUInteger)format type:(NSUInteger)type plane:(uint32_t)plane invert:(BOOL)invert NS_AVAILABLE_IOS(4_0); // confirmed in iOS5.1 +@end +# endif //COREVIDEO_SUPPORTS_IOSURFACE +#else +#include +#endif //Q_OS_IOS + +#include "QtAV/VideoFrame.h" +#include "opengl/OpenGLHelper.h" + +namespace QtAV { +namespace cv { +VideoFormat::PixelFormat format_from_cv(int cv); + +// https://www.opengl.org/registry/specs/APPLE/rgb_422.txt +// https://www.opengl.org/registry/specs/APPLE/ycbcr_422.txt uyvy: UNSIGNED_SHORT_8_8_REV_APPLE, yuy2: GL_UNSIGNED_SHORT_8_8_APPLE +// check extension GL_APPLE_rgb_422 and rectangle? +class InteropResourceIOSurface Q_DECL_FINAL : public InteropResource +{ +public: + bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE; + bool mapToTexture2D() const Q_DECL_OVERRIDE { return false;} + bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE; + GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE + { + Q_UNUSED(fmt); + Q_UNUSED(plane); + Q_UNUSED(planeWidth); + Q_UNUSED(planeHeight); + GLuint tex = 0; + DYGL(glGenTextures(1, &tex)); + return tex; + } +}; + +InteropResource* CreateInteropIOSurface() +{ + return new InteropResourceIOSurface(); +} + +bool InteropResourceIOSurface::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt) +{ + switch (cvfmt) { + case '2vuy': + case 'yuvs': { + *outFmt = VideoFormat::Format_VYU; + if (strides[0] <= 0) + strides[0] = 4*width; //RGB layout: BRGX + else + strides[0] *= 2; + } + break; + default: + return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt); + } + return true; +} + +bool InteropResourceIOSurface::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) +{ + Q_UNUSED(w); + Q_UNUSED(h); +#if COREVIDEO_SUPPORTS_IOSURFACE // IOSurface header is not included otherwise + const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf); + GLint iformat; + GLenum format, dtype; + getParametersGL(pixfmt, &iformat, &format, &dtype, plane); + switch (pixfmt) { + case '2vuy': + case 'yuvs': + // ES2 requires internal format and format are the same. desktop can use internal format GL_RGB or sized GL_RGB8 +#ifdef Q_OS_IOS + iformat = GL_RGB_422_APPLE; +#else + iformat = GL_RGB8; +#endif + format = GL_RGB_422_APPLE; + dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE; + break; + // macOS: GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV + // GL_YCBCR_422_APPLE: convert to rgb texture internally (bt601). only supports OSX + // GL_RGB_422_APPLE: raw yuv422 texture + case 'BGRA': +#ifdef Q_OS_IOS + iformat = GL_RGBA; + format = GL_RGBA; +#else + iformat = GL_RGBA8; + format = GL_BGRA; + dtype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + default: + break; + } + const GLenum target = GL_TEXTURE_RECTANGLE; + DYGL(glBindTexture(target, *tex)); + const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane); + const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane); + //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype); + + const IOSurfaceRef surface = CVPixelBufferGetIOSurface(buf); // available in ios11 sdk, ios4 runtime +#ifdef Q_OS_IOS + BOOL ok = false; +# ifdef __IPHONE_11_0 +# if __has_builtin(__builtin_available) // xcode9: runtime check @available is required, or -Wno-unguarded-availability-new + if (@available(iOS 11, *)) //symbol is available in iOS5+, but crashes at runtime +# else + if (false) +# endif // __has_builtin(__builtin_available) + ok = [[EAGLContext currentContext] texImageIOSurface:surface target:target internalFormat:iformat width:planeW height:planeH format:format type:dtype plane:plane]; + else // fallback to old private api if runtime version < 11 +# endif //__IPHONE_11_0 + { +#if IOS_USE_PRIVATE + ok = [[EAGLContext currentContext] texImageIOSurface:surface target:target internalFormat:iformat width:planeW height:planeH format:format type:dtype plane:plane invert:NO]; +#endif //IOS_USE_PRIVATE + } + if (!ok) { + qWarning("error creating IOSurface texture at plane %d", plane); + } +#else + CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, planeW, planeH, format, dtype, surface, plane); + if (err != kCGLNoError) { + qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err)); + } +#endif // Q_OS_IOS + DYGL(glBindTexture(target, 0)); +#endif //COREVIDEO_SUPPORTS_IOSURFACE + return true; +} +} // namespace cv +} // namespace QtAV diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoder.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoder.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -93,9 +93,14 @@ static QStringList codecs; if (!codecs.isEmpty()) return codecs; + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else avcodec_register_all(); - AVCodec* c = NULL; - while ((c=av_codec_next(c))) { + while ((c = av_codec_next(c))) { +#endif if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_VIDEO) continue; codecs.append(QString::fromLatin1(c->name)); diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderD3D.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderD3D.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderD3D.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderD3D.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Media play library based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2016) @@ -34,7 +34,9 @@ static bool check_ffmpeg_hevc_dxva2() { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); +#endif AVHWAccel *hwa = av_hwaccel_next(0); while (hwa) { if (strncmp("hevc_dxva2", hwa->name, 10) == 0) diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegBase.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegBase.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegBase.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegBase.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) @@ -132,7 +132,7 @@ //qDebug("pic_type=%c", av_get_picture_type_char(d.frame->pict_type)); d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size()); if (ret < 0) { - qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret)); + //qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret)); return false; } if (!got_frame_ptr) { @@ -148,4 +148,23 @@ return true; } +VideoFrame VideoDecoderFFmpegBase::frame() +{ + DPTR_D(VideoDecoderFFmpegBase); + if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) + return VideoFrame(); + // it's safe if width, height, pixfmt will not change, only data change + VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); + frame.setDisplayAspectRatio(d.getDAR(d.frame)); + frame.setBits(d.frame->data); + frame.setBytesPerLine(d.frame->linesize); + // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) + frame.setTimestamp((double)d.frame->pkt_pts/1000.0); + frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); + d.updateColorDetails(&frame); + if (frame.format().hasPalette()) { + frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4)); + } + return frame; +} } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegBase.h qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegBase.h --- qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegBase.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegBase.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2014) @@ -35,6 +35,7 @@ DPTR_DECLARE_PRIVATE(VideoDecoderFFmpegBase) public: virtual bool decode(const Packet& packet) Q_DECL_OVERRIDE; + virtual VideoFrame frame() Q_DECL_OVERRIDE; protected: VideoDecoderFFmpegBase(VideoDecoderFFmpegBasePrivate &d); private: @@ -50,7 +51,9 @@ , width(0) , height(0) { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); +#endif frame = av_frame_alloc(); } virtual ~VideoDecoderFFmpegBasePrivate() { diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpeg.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpeg.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpeg.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpeg.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) @@ -147,7 +147,6 @@ .arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav")) .arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch); } - virtual VideoFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL; // TODO: av_opt_set in setter void setSkipLoopFilter(DiscardType value); @@ -283,26 +282,6 @@ return VideoDecoderId_FFmpeg; } -VideoFrame VideoDecoderFFmpeg::frame() -{ - DPTR_D(VideoDecoderFFmpeg); - if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) - return VideoFrame(); - // it's safe if width, height, pixfmt will not change, only data change - VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); - frame.setDisplayAspectRatio(d.getDAR(d.frame)); - frame.setBits(d.frame->data); - frame.setBytesPerLine(d.frame->linesize); - // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) - frame.setTimestamp((double)d.frame->pkt_pts/1000.0); - frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); - d.updateColorDetails(&frame); - if (frame.format().hasPalette()) { - frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4)); - } - return frame; -} - void VideoDecoderFFmpeg::setSkipLoopFilter(DiscardType value) { DPTR_D(VideoDecoderFFmpeg); diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegHW.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegHW.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegHW.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegHW.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) @@ -155,6 +155,9 @@ AVPixelFormat VideoDecoderFFmpegHWPrivate::getFormat(struct AVCodecContext *avctx, const AVPixelFormat *pi_fmt) { +#ifdef AV_HWACCEL_FLAG_ALLOW_SOFTWARE + avctx->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_SOFTWARE; +#endif bool can_hwaccel = false; for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) { const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]); @@ -192,6 +195,9 @@ end: qWarning("hardware acceleration is not available" ); /* Fallback to default behaviour */ +#if QTAV_HAVE(AVBUFREF) + avctx->get_buffer2 = avcodec_default_get_buffer2; +#endif return avcodec_default_get_format(avctx, pi_fmt); } diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegHW_p.h qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegHW_p.h --- qtav-1.12.0+ds/src/codec/video/VideoDecoderFFmpegHW_p.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderFFmpegHW_p.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) @@ -63,6 +63,9 @@ codec_ctx->reget_buffer = reget_buffer; #endif //QTAV_HAVE(AVBUFREF) } + + virtual bool open() Q_DECL_OVERRIDE { return prepare();} + virtual void close() Q_DECL_OVERRIDE {restore();} // return hwaccel_context or null virtual void* setup(AVCodecContext* avctx) = 0; diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderMediaCodec.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderMediaCodec.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderMediaCodec.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderMediaCodec.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Media play library based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) @@ -19,7 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ -#include "VideoDecoderFFmpegBase.h" +#include "VideoDecoderFFmpegHW.h" +#include "VideoDecoderFFmpegHW_p.h" #if FFMPEG_MODULE_CHECK(LIBAVCODEC, 57, 28, 100) #define QTAV_HAVE_MEDIACODEC 1 #endif @@ -29,32 +30,92 @@ #include extern "C" { #include +#include } +// QtAV's fastest mediacodec decoding/rendering code is private because all other projects I know require java code or is opengl incompatible(chrome, firefox, xbmc, vlc etc.). Maybe my code is the best implementation. +#ifdef MEDIACODEC_TEXTURE +#include "QtAV/SurfaceInterop.h" +#include "opengl/OpenGLHelper.h" +#include "MediaCodecTextureStandalone.h" +#endif + namespace QtAV { class VideoDecoderMediaCodecPrivate; -class VideoDecoderMediaCodec : public VideoDecoderFFmpegBase +class VideoDecoderMediaCodec Q_DECL_FINAL : public VideoDecoderFFmpegHW { DPTR_DECLARE_PRIVATE(VideoDecoderMediaCodec) public: VideoDecoderMediaCodec(); + VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; + + void flush() Q_DECL_OVERRIDE { // workaroudn for EAGAIN error in avcodec_receive_frame + if (copyMode() == ZeroCopy) { + + } else { + VideoDecoderFFmpegHW::flush(); + } + } }; extern VideoDecoderId VideoDecoderId_MediaCodec; FACTORY_REGISTER(VideoDecoder, MediaCodec, "MediaCodec") -class VideoDecoderMediaCodecPrivate Q_DECL_FINAL : public VideoDecoderFFmpegBasePrivate +class VideoDecoderMediaCodecPrivate Q_DECL_FINAL : public VideoDecoderFFmpegHWPrivate { public: + ~VideoDecoderMediaCodecPrivate() { +#ifdef MEDIACODEC_TEXTURE + if (pool_) + api_->texture_pool_release(&pool_); +#endif + } + void close() Q_DECL_OVERRIDE { + restore(); +#ifdef MEDIACODEC_TEXTURE + if (codec_ctx) + av_mediacodec_default_free(codec_ctx); +#endif + } + bool enableFrameRef() const Q_DECL_OVERRIDE { return true;} + + void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE { + if (copy_mode != VideoDecoderFFmpegHW::ZeroCopy) + return nullptr; +#ifdef MEDIACODEC_TEXTURE + api_->set_jvm(QAndroidJniEnvironment::javaVM()); + if (!pool_) + pool_ = api_->texture_pool_create(); + av_mediacodec_default_free(avctx); + AVMediaCodecContext *mc = av_mediacodec_alloc_context(); + AV_ENSURE(av_mediacodec_default_init(avctx, mc, api_->texture_pool_ensure_surface(pool_)), nullptr); +#endif + return avctx->hwaccel_context; // set in av_mediacodec_default_init + } + + bool getBuffer(void **, uint8_t **data) {return true;} + void releaseBuffer(void *, uint8_t *) {} + AVPixelFormat vaPixelFormat() const { + if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy) + return AV_PIX_FMT_MEDIACODEC; + return AV_PIX_FMT_NONE; + } +#ifdef MEDIACODEC_TEXTURE + const MdkMediaCodecTextureAPI* api_ = mdk_mediacodec_get_api("qtav.nonfree.mediacodec"); + MdkMediaCodecTextureAPI::TexturePool *pool_ = nullptr; +#endif }; VideoDecoderMediaCodec::VideoDecoderMediaCodec() - : VideoDecoderFFmpegBase(*new VideoDecoderMediaCodecPrivate()) + : VideoDecoderFFmpegHW(*new VideoDecoderMediaCodecPrivate()) { - setCodecName("h264_mediacodec"); + setProperty("hwaccel", "mediacodec"); +#ifdef MEDIACODEC_TEXTURE + setProperty("copyMode", "ZeroCopy"); +#endif av_jni_set_java_vm(QAndroidJniEnvironment::javaVM(), NULL); } @@ -68,20 +129,64 @@ return QStringLiteral("MediaCodec"); } +static void av_mediacodec_render_buffer(void *buf) +{ + av_mediacodec_release_buffer((AVMediaCodecBuffer*)buf, 1); +} + +static void av_mediacodec_buffer_unref(void* buf) +{ + av_buffer_unref((AVBufferRef**)&buf); +} + VideoFrame VideoDecoderMediaCodec::frame() { DPTR_D(VideoDecoderMediaCodec); if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) return VideoFrame(); // it's safe if width, height, pixfmt will not change, only data change - VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); + if (copyMode() != ZeroCopy) { + VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); + frame.setDisplayAspectRatio(d.getDAR(d.frame)); + frame.setBits(d.frame->data); + frame.setBytesPerLine(d.frame->linesize); + // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) + frame.setTimestamp((double)d.frame->pkt_pts/1000.0); + frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); + d.updateColorDetails(&frame); + return frame; + } +// print width height + VideoFrame frame(d.frame->width, d.frame->height, VideoFormat::Format_RGB32); + frame.setBytesPerLine(d.frame->width*4); frame.setDisplayAspectRatio(d.getDAR(d.frame)); - frame.setBits(d.frame->data); - frame.setBytesPerLine(d.frame->linesize); - // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) - frame.setTimestamp((double)d.frame->pkt_pts/1000.0); - frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); - d.updateColorDetails(&frame); + frame.setTimestamp(d.frame->pkt_pts/1000.0); +#ifdef MEDIACODEC_TEXTURE + class MediaCodecTextureInterop : public VideoSurfaceInterop + { + const MdkMediaCodecTextureAPI* api_ = nullptr; + MdkMediaCodecTextureAPI::Texture *tex_ = nullptr; + public: + MediaCodecTextureInterop(const MdkMediaCodecTextureAPI *api, MdkMediaCodecTextureAPI::Texture *mt) : api_(api), tex_(mt) {} + ~MediaCodecTextureInterop() { + api_->texture_release(&tex_); + } + + void* map(SurfaceType, const VideoFormat &, void *handle, int plane) { + Q_UNUSED(plane); + GLuint* t = reinterpret_cast(handle); + *t = api_->texture_to_gl(tex_, nullptr, nullptr); + return t; + } + }; + assert(d.frame->buf[0] && d.frame->data[3] && "No AVMediaCodecBuffer or ref in AVFrame"); + AVBufferRef* bufref = av_buffer_ref(d.frame->buf[0]); + AVMediaCodecBuffer *mcbuf = (AVMediaCodecBuffer*)d.frame->data[3]; + MdkMediaCodecTextureAPI::Texture* mt = d.api_->texture_pool_feed_avbuffer(d.pool_, d.frame->width, d.frame->height, av_mediacodec_buffer_unref, bufref, av_mediacodec_render_buffer, mcbuf); + + MediaCodecTextureInterop *interop = new MediaCodecTextureInterop(d.api_, mt); + frame.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr((interop)))); +#endif return frame; } } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/codec/video/VideoDecoderVideoToolbox.cpp qtav-1.13.0+ds/src/codec/video/VideoDecoderVideoToolbox.cpp --- qtav-1.12.0+ds/src/codec/video/VideoDecoderVideoToolbox.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoDecoderVideoToolbox.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2015) @@ -148,7 +148,9 @@ VideoDecoderVideoToolbox::VideoDecoderVideoToolbox() : VideoDecoderFFmpegHW(*new VideoDecoderVideoToolboxPrivate()) { +#if 1//!AV_MODULE_CHECK(LIBAVCODEC, 57, 30, 1, 89, 100) // ffmpeg3.3 setProperty("threads", 1); // to avoid crash at av_videotoolbox_alloc_context/av_videotoolbox_default_free. I have no idea how the are called +#endif // dynamic properties about static property details. used by UI setProperty("detail_format", tr("Output pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV.\nOSX < 10.7 only supports UYVY, BGRA and YUV420p")); setProperty("detail_interop" @@ -174,6 +176,9 @@ VideoFrame VideoDecoderVideoToolbox::frame() { DPTR_D(VideoDecoderVideoToolbox); + if (!d.codec_ctx->hwaccel_context) { + return VideoDecoderFFmpegBase::frame(); + } CVPixelBufferRef cv_buffer = (CVPixelBufferRef)d.frame->data[3]; if (!cv_buffer) { qDebug("Frame buffer is empty."); @@ -286,6 +291,7 @@ bool VideoDecoderVideoToolboxPrivate::getBuffer(void **opaque, uint8_t **data) { + qDebug("vt getbuffer"); *data = (uint8_t *)1; // dummy. it's AVFrame.data[0], must be non null required by ffmpeg Q_UNUSED(opaque); return true; @@ -314,6 +320,7 @@ break; } switch (codec_ctx->codec_id) { + case AV_CODEC_ID_HEVC: case AV_CODEC_ID_H264: case AV_CODEC_ID_H263: case AV_CODEC_ID_MPEG4: diff -Nru qtav-1.12.0+ds/src/codec/video/VideoEncoder.cpp qtav-1.13.0+ds/src/codec/video/VideoEncoder.cpp --- qtav-1.12.0+ds/src/codec/video/VideoEncoder.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoEncoder.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2015) @@ -46,9 +46,14 @@ static QStringList codecs; if (!codecs.isEmpty()) return codecs; + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else avcodec_register_all(); - AVCodec* c = NULL; - while ((c=av_codec_next(c))) { + while ((c = av_codec_next(c))) { +#endif if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_VIDEO) continue; codecs.append(QString::fromLatin1(c->name)); diff -Nru qtav-1.12.0+ds/src/codec/video/VideoEncoderFFmpeg.cpp qtav-1.13.0+ds/src/codec/video/VideoEncoderFFmpeg.cpp --- qtav-1.12.0+ds/src/codec/video/VideoEncoderFFmpeg.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/codec/video/VideoEncoderFFmpeg.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2015) @@ -90,7 +90,9 @@ : VideoEncoderPrivate() , nb_encoded(0) { +#if !AVCODEC_STATIC_REGISTER avcodec_register_all(); +#endif // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264). avctx = avcodec_alloc_context3(NULL); } @@ -245,7 +247,7 @@ applyOptionsForContext(); AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false); // from mpv ao_lavc - const int buffer_size = qMax(qMax(width*height*6+200, FF_MIN_BUFFER_SIZE), sizeof(AVPicture));//?? + const int buffer_size = qMax(qMax(width*height*6+200, AV_INPUT_BUFFER_MIN_SIZE), sizeof(AVPicture));//?? buffer.resize(buffer_size); return true; } diff -Nru qtav-1.12.0+ds/src/cuda/helper_cuda.h qtav-1.13.0+ds/src/cuda/helper_cuda.h --- qtav-1.12.0+ds/src/cuda/helper_cuda.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/cuda/helper_cuda.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2017 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -110,6 +110,11 @@ { 0x37, 192}, // Kepler Generation (SM 3.7) GK21x class { 0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class { 0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class //enc: h264 yuv444p, hevc (libavcodec/nvenc.c) + { 0x53, 128}, // Maxwell Generation (SM 5.3) GM20x class + { 0x60, 64 }, // Pascal Generation (SM 6.0) GP100 class + { 0x61, 128}, // Pascal Generation (SM 6.1) GP10x class + { 0x62, 128}, // Pascal Generation (SM 6.2) GP10x class + { 0x70, 64 }, // Volta Generation (SM 7.0) GV100 class { -1, -1 } }; int index = 0; @@ -120,7 +125,7 @@ ++index; } // If we don't find the values, we default use the previous one to run properly - printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[7].Cores); + printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[index-1].Cores); return nGpuArchCoresPerSM[index - 1].Cores; } diff -Nru qtav-1.12.0+ds/src/directx/DXVAHDVP.cpp qtav-1.13.0+ds/src/directx/DXVAHDVP.cpp --- qtav-1.12.0+ds/src/directx/DXVAHDVP.cpp 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/src/directx/DXVAHDVP.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,170 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2017 Wang Bin + +* This file is part of QtAV (from 2017) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +#include "DXVAHDVP.h" +#define DX_LOG_COMPONENT "D3D9VP" +#include "utils/DirectXHelper.h" +#include "utils/Logger.h" +#include "directx/DXVAHDVP.h" + +namespace QtAV { +namespace dx { + +DXVAHDVP::DXVAHDVP(ComPtr dev) + : m_dev(NULL) + , m_w(0) + , m_h(0) + , m_cs(ColorSpace_BT709) + , m_range(ColorRange_Limited) + , fDXVAHD_CreateDevice(NULL) +{ + DX_ENSURE(dev.As(&m_dev)); + fDXVAHD_CreateDevice = (PDXVAHD_CreateDevice)GetProcAddress(GetModuleHandle(TEXT("dxva2.dll")), "DXVAHD_CreateDevice"); +} + +void DXVAHDVP::setOutput(IDirect3DSurface9 *surface) +{ + m_out = surface; +} + +void DXVAHDVP::setSourceRect(const QRect &r) +{ + m_srcRect = r; +} + +void DXVAHDVP::setColorSpace(ColorSpace value) +{ + m_cs = value; +} + +void DXVAHDVP::setColorRange(ColorRange value) +{ + m_range = value; +} + +bool DXVAHDVP::process(IDirect3DSurface9 *surface) +{ + if (!surface || !m_out) + return false; + D3DSURFACE_DESC desc; + surface->GetDesc(&desc); + if (!ensureResource(desc.Width, desc.Height, desc.Format)) + return false; + + if (!m_srcRect.isEmpty()) { + const RECT sr = {m_srcRect.x(), m_srcRect.y(), m_srcRect.width(), m_srcRect.height()}; + DXVAHD_STREAM_STATE_SOURCE_RECT_DATA r; + r.Enable = 1; + r.SourceRect = sr; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_SOURCE_RECT, sizeof(r), &r), false); + } +#if 0 + DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3df = {m_fmt}; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_D3DFORMAT, sizeof(d3df), &d3df), false); + DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA ff = {DXVAHD_FRAME_FORMAT_PROGRESSIVE}; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_FRAME_FORMAT, sizeof(ff), &ff), false); + DXVAHD_STREAM_STATE_DESTINATION_RECT_DATA dstr = { true, {0, 0, desc.Width, desc.Height} }; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_DESTINATION_RECT, sizeof(dstr), &dstr), false); + DXVAHD_BLT_STATE_TARGET_RECT_DATA tgtr = {true, {0, 0, desc.Width, desc.Height} }; + DX_ENSURE(m_vp->SetVideoProcessBltState(DXVAHD_BLT_STATE_TARGET_RECT, sizeof(tgtr), &tgtr), false); + for (int i = 0; i < 7; ++i) { + DXVAHD_STREAM_STATE_FILTER_DATA flt = {false, DXVAHD_FILTER(i)}; + DXVAHD_STREAM_STATE state = static_cast(DXVAHD_STREAM_STATE_FILTER_BRIGHTNESS + i); + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, state, sizeof(flt), &flt), false); + } +#endif + + DXVAHD_STREAM_STATE_OUTPUT_RATE_DATA rate; + ZeroMemory(&rate, sizeof(rate)); + rate.OutputRate = DXVAHD_OUTPUT_RATE_NORMAL; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_OUTPUT_RATE, sizeof(rate), &rate), false); + + DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA ics; + ZeroMemory(&ics, sizeof(ics)); + ics.Type = 0; // 0: video, 1: graphics + ics.RGB_Range = 0; // full + ics.YCbCr_Matrix = m_cs == ColorSpace_BT601 ? 0 : 1; //0: bt601, 1: bt709 + ics.YCbCr_xvYCC = m_range == ColorRange_Full ? 1 : 0; + DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE, sizeof(ics), &ics), false); + + DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA cs; + ZeroMemory(&cs, sizeof(cs)); + cs.Usage = 0; // output usage. 0: for playback (default), 1: video processing (e.g. video editor) + cs.RGB_Range = 1; // + cs.YCbCr_Matrix = 1; //0: bt601, 1: bt709 + cs.YCbCr_xvYCC = 1; + DX_ENSURE(m_vp->SetVideoProcessBltState(DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE, sizeof(cs), &cs), false); + + DXVAHD_STREAM_DATA stream; + ZeroMemory(&stream, sizeof(stream)); + stream.Enable = true; + stream.OutputIndex = 0; + stream.InputFrameOrField = 0; + stream.pInputSurface = surface; + DX_ENSURE(m_vp->VideoProcessBltHD(m_out.Get(), 0, 1, &stream), false); + return true; +} + +bool DXVAHDVP::ensureResource(UINT width, UINT height, D3DFORMAT format) +{ + const bool dirty = width != m_w || height != m_h || m_fmt != format; + if (dirty || !m_viddev) { + if (!fDXVAHD_CreateDevice) + return false; + DXVAHD_RATIONAL fps = {0, 0}; + DXVAHD_CONTENT_DESC desc; + desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE; + desc.InputFrameRate = fps; + desc.InputWidth = width; + desc.InputHeight = height; + desc.OutputFrameRate = fps; + desc.OutputWidth = width; + desc.OutputHeight = height; + DX_ENSURE(fDXVAHD_CreateDevice(m_dev.Get(), &desc, DXVAHD_DEVICE_USAGE_PLAYBACK_NORMAL, NULL, &m_viddev), false); + } + + // TODO: check when format is changed, or record supported formats + DXVAHD_VPDEVCAPS caps; + DX_ENSURE(m_viddev->GetVideoProcessorDeviceCaps(&caps), false); + QScopedPointer> pVPCaps(new (std::nothrow) DXVAHD_VPCAPS[caps.VideoProcessorCount]); + DX_ENSURE(m_viddev->GetVideoProcessorCaps(caps.VideoProcessorCount, pVPCaps.data()), false); + QScopedPointer> fmts(new (std::nothrow) D3DFORMAT[caps.InputFormatCount]); + DX_ENSURE(m_viddev->GetVideoProcessorInputFormats(caps.InputFormatCount, fmts.data()), false); + bool fmt_found = false; + for (UINT i = 0; i < caps.InputFormatCount; ++i) { + if (fmts.data()[i] == format) { + fmt_found = true; + break; + } + } + if (!fmt_found) { + qDebug("input format is not supported by DXVAHD"); + return false; + } + if (dirty || !m_vp) + DX_ENSURE(m_viddev->CreateVideoProcessor(&pVPCaps.data()[0].VPGuid, &m_vp), false); + m_w = width; + m_h = height; + m_fmt = format; + return true; +} +} //namespace dx +} //namespace QtAV diff -Nru qtav-1.12.0+ds/src/directx/DXVAHDVP.h qtav-1.13.0+ds/src/directx/DXVAHDVP.h --- qtav-1.12.0+ds/src/directx/DXVAHDVP.h 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/src/directx/DXVAHDVP.h 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,63 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2017 Wang Bin + +* This file is part of QtAV (from 2017) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +#ifndef QTAV_D3D9VPP_H +#define QTAV_D3D9VPP_H +#include +#include "directx/dxcompat.h" +#include +#include +#include +#include +using namespace Microsoft::WRL; +// https://msdn.microsoft.com/en-us/library/windows/desktop/ee663581(v=vs.85).aspx + +namespace QtAV { +namespace dx { + +class DXVAHDVP +{ +public: + // brightness, contrast, hue, saturation, rotation, source/dest rect + DXVAHDVP(ComPtr dev); + void setOutput(IDirect3DSurface9* surface); + void setSourceRect(const QRect& r); + // input color space and range + void setColorSpace(ColorSpace value); + void setColorRange(ColorRange value); + bool process(IDirect3DSurface9 *surface); +private: + bool ensureResource(UINT width, UINT height, D3DFORMAT format); + + ComPtr m_dev; + ComPtr m_viddev; + ComPtr m_vp; + ComPtr m_out; + UINT m_w, m_h; //enumerator + ColorSpace m_cs; + ColorRange m_range; + QRect m_srcRect; + PDXVAHD_CreateDevice fDXVAHD_CreateDevice; + D3DFORMAT m_fmt; +}; +} //namespace dx +} //namespace QtAV +#endif //QTAV_D3D9VPP_H diff -Nru qtav-1.12.0+ds/src/directx/SurfaceInteropD3D11EGL.cpp qtav-1.13.0+ds/src/directx/SurfaceInteropD3D11EGL.cpp --- qtav-1.12.0+ds/src/directx/SurfaceInteropD3D11EGL.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/directx/SurfaceInteropD3D11EGL.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -51,6 +51,7 @@ EGLInteropResource() : egl(new EGL()) , vp(0) + , boundTex(0) {} ~EGLInteropResource(); VideoFormat::PixelFormat format(DXGI_FORMAT) const Q_DECL_OVERRIDE { return VideoFormat::Format_RGB32;} @@ -63,6 +64,7 @@ EGL* egl; dx::D3D11VP *vp; ComPtr d3dtex; + GLuint boundTex; }; InteropResource* CreateInteropEGL() { return new EGLInteropResource();} @@ -164,9 +166,14 @@ vp->setSourceRect(QRect(0, 0, w, h)); if (!vp->process(surface.Get(), index)) return false; + if (boundTex == tex) + return true; DYGL(glBindTexture(GL_TEXTURE_2D, tex)); - eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); + if (boundTex) + EGL_WARN(eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER)); + EGL_WARN(eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER)); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); + boundTex = tex; return true; } } //namespace d3d11 diff -Nru qtav-1.12.0+ds/src/filter/EncodeFilter.cpp qtav-1.13.0+ds/src/filter/EncodeFilter.cpp --- qtav-1.12.0+ds/src/filter/EncodeFilter.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/filter/EncodeFilter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -34,7 +34,7 @@ class AudioEncodeFilterPrivate Q_DECL_FINAL : public AudioFilterPrivate { public: - AudioEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0) {} + AudioEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0), leftOverAudio() {} ~AudioEncodeFilterPrivate() { if (enc) { enc->close(); @@ -47,12 +47,17 @@ bool async; QAtomicInt finishing; QThread enc_thread; + AudioFrame leftOverAudio; }; AudioEncodeFilter::AudioEncodeFilter(QObject *parent) : AudioFilter(*new AudioEncodeFilterPrivate(), parent) { +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) connect(this, SIGNAL(requestToEncode(QtAV::AudioFrame)), this, SLOT(encode(QtAV::AudioFrame))); +#else + connect(this, &AudioEncodeFilter::requestToEncode, this, &AudioEncodeFilter::encode); +#endif connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit())); } @@ -170,16 +175,36 @@ AudioFrame f(frame); if (f.format() != d.enc->audioFormat()) f = f.to(d.enc->audioFormat()); - if (!d.enc->encode(f)) { - if (f.timestamp() == std::numeric_limits::max()) { - Q_EMIT finished(); - d.finishing = 0; + + if (d.leftOverAudio.isValid()) { + f.prepend(d.leftOverAudio); + d.leftOverAudio = AudioFrame(); + } + + int frameSizeEncoder = d.enc->frameSize() ? d.enc->frameSize() : f.samplesPerChannel(); + int frameSize = f.samplesPerChannel(); + + QList audioFrames; + for (int i = 0; i < frameSize; i += frameSizeEncoder) { + if (frameSize - i >= frameSizeEncoder) { + audioFrames.append(f.mid(i, frameSizeEncoder)); + } else { + d.leftOverAudio = f.mid(i); } - return; } - if (!d.enc->encoded().isValid()) - return; - Q_EMIT frameEncoded(d.enc->encoded()); + + for (int i = 0; i < audioFrames.length(); i++) { + if (!d.enc->encode(audioFrames.at(i))) { + if (f.timestamp() == std::numeric_limits::max()) { + Q_EMIT finished(); + d.finishing = 0; + } + return; + } + if (!d.enc->encoded().isValid()) + return; + Q_EMIT frameEncoded(d.enc->encoded()); + } } @@ -204,7 +229,11 @@ VideoEncodeFilter::VideoEncodeFilter(QObject *parent) : VideoFilter(*new VideoEncodeFilterPrivate(), parent) { - connect(this, SIGNAL(requestToEncode(QtAV::VideoFrame)), this, SLOT(encode(QtAV::VideoFrame))); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + connect(this, SIGNAL(requestToEncode(QtAV::VideoFrame)), this, SLOT(encode(QtAV::VideoFrame))); +#else + connect(this, &VideoEncodeFilter::requestToEncode, this, &VideoEncodeFilter::encode); +#endif connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit())); } @@ -292,8 +321,12 @@ return; // encode delayed frames can pass an invalid frame if (!d.enc->isOpen() && frame.isValid()) { - d.enc->setWidth(frame.width()); - d.enc->setHeight(frame.height()); + if (d.enc->width() == 0) { + d.enc->setWidth(frame.width()); + } + if (d.enc->height() == 0) { + d.enc->setHeight(frame.height()); + } if (!d.enc->open()) { // TODO: error() qWarning("Failed to open video encoder"); return; @@ -310,16 +343,12 @@ d.finishing = 0; return; } - if (d.enc->width() != frame.width() || d.enc->height() != frame.height()) { - qWarning("Frame size (%dx%d) and video encoder size (%dx%d) mismatch! Close encoder please.", d.enc->width(), d.enc->height(), frame.width(), frame.height()); - return; - } if (frame.timestamp()*1000.0 < startTime()) return; // TODO: async VideoFrame f(frame); - if (f.pixelFormat() != d.enc->pixelFormat()) - f = f.to(d.enc->pixelFormat()); + if (f.pixelFormat() != d.enc->pixelFormat() || d.enc->width() != f.width() || d.enc->height() != f.height()) + f = f.to(d.enc->pixelFormat(), QSize(d.enc->width(), d.enc->height())); if (!d.enc->encode(f)) { if (f.timestamp() == std::numeric_limits::max()) { Q_EMIT finished(); diff -Nru qtav-1.12.0+ds/src/filter/FilterManager.cpp qtav-1.13.0+ds/src/filter/FilterManager.cpp --- qtav-1.12.0+ds/src/filter/FilterManager.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/filter/FilterManager.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -122,50 +122,57 @@ { DPTR_D(FilterManager); QList& fs = d.afilter_player_map[player]; + bool ret = false; if (fs.removeAll(filter) > 0) { - if (fs.isEmpty()) - d.afilter_player_map.remove(player); - return true; + ret = true; } - return false; + if (fs.isEmpty()) + d.afilter_player_map.remove(player); + return ret; } bool FilterManager::unregisterVideoFilter(Filter *filter, AVPlayer *player) { DPTR_D(FilterManager); QList& fs = d.vfilter_player_map[player]; + bool ret = false; if (fs.removeAll(filter) > 0) { - if (fs.isEmpty()) - d.vfilter_player_map.remove(player); - return true; + ret = true; } - return false; + if (fs.isEmpty()) + d.vfilter_player_map.remove(player); + return ret; } bool FilterManager::unregisterFilter(Filter *filter, AVOutput *output) { DPTR_D(FilterManager); QList& fs = d.filter_out_map[output]; - return fs.removeAll(filter) > 0; + bool ret = fs.removeAll(filter) > 0; + if (fs.isEmpty()) d.filter_out_map.remove(output); + return ret; } bool FilterManager::uninstallFilter(Filter *filter) { DPTR_D(FilterManager); - QMap >::iterator it = d.vfilter_player_map.begin(); - while (it != d.vfilter_player_map.end()) { + QMap > map1(d.vfilter_player_map); // NB: copy it for iteration because called code may modify map -- which caused crashes + QMap >::iterator it = map1.begin(); + while (it != map1.end()) { if (uninstallVideoFilter(filter, it.key())) return true; ++it; } - it = d.afilter_player_map.begin(); - while (it != d.afilter_player_map.end()) { + QMap > map2(d.afilter_player_map); // copy to avoid crashes when called-code modifies map + it = map2.begin(); + while (it != map2.end()) { if (uninstallAudioFilter(filter, it.key())) return true; ++it; } - QMap >::iterator it2 = d.filter_out_map.begin(); - while (it2 != d.filter_out_map.end()) { + QMap > map3(d.filter_out_map); // copy to avoid crashes + QMap >::iterator it2 = map3.begin(); + while (it2 != map3.end()) { if (uninstallFilter(filter, it2.key())) return true; ++it2; diff -Nru qtav-1.12.0+ds/src/filter/LibAVFilter.cpp qtav-1.13.0+ds/src/filter/LibAVFilter.cpp --- qtav-1.12.0+ds/src/filter/LibAVFilter.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/filter/LibAVFilter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -120,7 +120,10 @@ // pixel_aspect==sar, pixel_aspect is more compatible QString buffersrc_args = args; qDebug("buffersrc_args=%s", buffersrc_args.toUtf8().constData()); - AVFilter *buffersrc = avfilter_get_by_name(video ? "buffer" : "abuffer"); +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(7,0,0) + const +#endif + AVFilter *buffersrc = avfilter_get_by_name(video ? "buffer" : "abuffer"); Q_ASSERT(buffersrc); AV_ENSURE_OK(avfilter_graph_create_filter(&in_filter_ctx, buffersrc, @@ -128,6 +131,9 @@ filter_graph) , false); /* buffer video sink: to terminate the filter chain. */ +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(7,0,0) + const +#endif AVFilter *buffersink = avfilter_get_by_name(video ? "buffersink" : "abuffersink"); Q_ASSERT(buffersink); AV_ENSURE_OK(avfilter_graph_create_filter(&out_filter_ctx, buffersink, "out", diff -Nru qtav-1.12.0+ds/src/Frame.cpp qtav-1.13.0+ds/src/Frame.cpp --- qtav-1.12.0+ds/src/Frame.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/Frame.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -59,6 +59,11 @@ return d_func()->data; } +int Frame::dataAlignment() const +{ + return d_func()->data_align; +} + QByteArray Frame::data(int plane) const { if (plane < 0 || plane >= planeCount()) { diff -Nru qtav-1.12.0+ds/src/FrameReader.cpp qtav-1.13.0+ds/src/FrameReader.cpp --- qtav-1.12.0+ds/src/FrameReader.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/FrameReader.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -353,7 +353,7 @@ d->vframes.put(frame); Q_EMIT frameRead(frame); //qDebug("frame got @%.3f, queue enough: %d", frame.timestamp(), vframes.isEnough()); - if (d->vframes.isEnough()) + if (d->vframes.isFull()) break; } else { qDebug("dec error, continue to decoder"); diff -Nru qtav-1.12.0+ds/src/ImageConverter.cpp qtav-1.13.0+ds/src/ImageConverter.cpp --- qtav-1.12.0+ds/src/ImageConverter.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/ImageConverter.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** ImageConverter: Base class for image resizing & color model convertion - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -45,7 +45,8 @@ QByteArray ImageConverter::outData() const { - return d_func().data_out; + DPTR_D(const ImageConverter); + return d.data_out; } bool ImageConverter::check() const @@ -215,8 +216,8 @@ const int nb_planes = qMax(av_pix_fmt_count_planes(d.fmt_out), 0); d.bits.resize(nb_planes); d.pitchs.resize(nb_planes); - // alignment is 16. sws in ffmpeg is 16, libav10 is 8 - const int kAlign = 16; + // alignment is 16. sws in ffmpeg is 16, libav10 is 8. if not aligned sws will print warnings and go slow code paths + const int kAlign = DataAlignment; AV_ENSURE(av_image_fill_linesizes((int*)d.pitchs.constData(), d.fmt_out, kAlign > 7 ? FFALIGN(d.w_out, 8) : d.w_out), false); for (int i = 0; i < d.pitchs.size(); ++i) d.pitchs[i] = FFALIGN(d.pitchs[i], kAlign); @@ -224,8 +225,8 @@ if (s < 0) return false; d.data_out.resize(s + kAlign-1); - const int offset = (kAlign - ((uintptr_t)d.data_out.constData() & (kAlign-1))) & (kAlign-1); - AV_ENSURE(av_image_fill_pointers((uint8_t**)d.bits.constData(), d.fmt_out, d.h_out, (uint8_t*)d.data_out.constData()+offset, d.pitchs.constData()), false); + d.out_offset = (kAlign - ((uintptr_t)d.data_out.constData() & (kAlign-1))) & (kAlign-1); + AV_ENSURE(av_image_fill_pointers((uint8_t**)d.bits.constData(), d.fmt_out, d.h_out, (uint8_t*)d.data_out.constData()+d.out_offset, d.pitchs.constData()), false); // TODO: special formats //if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) // avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt); diff -Nru qtav-1.12.0+ds/src/ImageConverter.h qtav-1.13.0+ds/src/ImageConverter.h --- qtav-1.12.0+ds/src/ImageConverter.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/ImageConverter.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** ImageConverter: Base class for image resizing & color model convertion - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -35,9 +35,12 @@ { DPTR_DECLARE_PRIVATE(ImageConverter) public: + enum { DataAlignment = 16 }; + ImageConverter(); virtual ~ImageConverter(); + // the real data starts with DataAlignment (16bit) aligned address QByteArray outData() const; // return false if i/o format not supported, or size is not valid. // TODO: use isSupported(i/o format); diff -Nru qtav-1.12.0+ds/src/ImageConverter_p.h qtav-1.13.0+ds/src/ImageConverter_p.h --- qtav-1.12.0+ds/src/ImageConverter_p.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/ImageConverter_p.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -43,6 +43,7 @@ , contrast(0) , saturation(0) , update_data(true) + , out_offset(0) { bits.reserve(8); pitchs.reserve(8); @@ -57,6 +58,7 @@ ColorRange range_in, range_out; int brightness, contrast, saturation; bool update_data; + int out_offset; QByteArray data_out; QVector bits; QVector pitchs; diff -Nru qtav-1.12.0+ds/src/io/AndroidIO.cpp qtav-1.13.0+ds/src/io/AndroidIO.cpp --- qtav-1.12.0+ds/src/io/AndroidIO.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/io/AndroidIO.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2015) @@ -24,10 +24,10 @@ #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include -#include #include #include #include "utils/Logger.h" +#include "jmi/jmi.h" // TODO: how to get filename and find subtitles? //http://stackoverflow.com/questions/5657411/android-getting-a-file-uri-from-a-content-uri @@ -46,7 +46,7 @@ QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} const QStringList& protocols() const Q_DECL_OVERRIDE { - static QStringList p = QStringList() << QStringLiteral("content"); // "file:" is supported too but we use QFile + static QStringList p = QStringList() << QStringLiteral("content") << QStringLiteral("android.resource"); // "file:" is supported too but we use QFile return p; } virtual bool isSeekable() const Q_DECL_OVERRIDE; @@ -63,7 +63,6 @@ void onUrlChanged() Q_DECL_OVERRIDE; private: - QAndroidJniObject app_ctx; QFile qt_file; // if use Java.io.InputStream, record pos }; @@ -74,9 +73,7 @@ AndroidIO::AndroidIO() : MediaIO() { - QPlatformNativeInterface *interface = QGuiApplication::platformNativeInterface(); - jobject activity = (jobject)interface->nativeResourceForIntegration("QtActivity"); - app_ctx = QAndroidJniObject(activity).callObjectMethod("getApplicationContext","()Landroid/content/Context;"); + jmi::javaVM(QAndroidJniEnvironment::javaVM()); // nativeResourceForIntegration("javaVM") } bool AndroidIO::isSeekable() const @@ -106,22 +103,45 @@ void AndroidIO::onUrlChanged() { qt_file.close(); - QAndroidJniObject content_resolver = app_ctx.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;"); - if (!content_resolver.isValid()) { - qWarning("getContentResolver error"); + if (url().isEmpty()) + return; + struct Application final: jmi::ClassTag { static std::string name() {return "android/app/Application";}}; + jmi::JObject app_ctx(jmi::android::application()); + + struct ContentResolver final: jmi::ClassTag { static std::string name() { return "android/content/ContentResolver";}}; + struct GetContentResolver final: jmi::MethodTag { static const char* name() {return "getContentResolver";}}; + jmi::JObject cr = app_ctx.call, GetContentResolver>(); + if (!cr.error().empty()) { + qWarning("getContentResolver error: %s", cr.error().data()); + return; + } + struct Uri final: jmi::ClassTag { static std::string name() { return "android/net/Uri";}}; + struct Parse final: jmi::MethodTag { static const char* name() {return "parse";}}; + jmi::JObject uri = jmi::JObject::callStatic, Parse>(url().toUtf8().constData()); // move? + // openInputStream? + struct ParcelFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/os/ParcelFileDescriptor";}}; + // AssetFileDescriptor supported schemes: content, android.resource, file + // ParcelFileDescriptor supported schemes: content, file +#if 1 + struct AssetFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/content/res/AssetFileDescriptor";}}; + struct OpenAssetFileDescriptor final: jmi::MethodTag { static const char* name() {return "openAssetFileDescriptor";}}; + jmi::JObject afd = cr.call, OpenAssetFileDescriptor>(std::move(uri), "r"); // TODO: rw + if (!afd.error().empty()) { + qWarning("openAssetFileDescriptor error: %s", afd.error().data()); return; } - QAndroidJniObject s = QAndroidJniObject::fromString(url()); - QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", s.object()); - //input_stream = content_resolver.callObjectMethod("openInputStream", "(Landroid.net.Uri;)Ljava.io.InputStream;", uri.object()); // TODO: why error? - //qDebug() << "onUrlChanged InputStream: " << input_stream.toString(); - s = QAndroidJniObject::fromString(QStringLiteral("r")); - QAndroidJniObject pfd = content_resolver.callObjectMethod("openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", uri.object(), s.object()); - if (!pfd.isValid()) { - qWarning("openFileDescriptor error"); + struct GetParcelFileDescriptor final: jmi::MethodTag { static const char* name() {return "getParcelFileDescriptor";}}; + jmi::JObject pfd = afd.call, GetParcelFileDescriptor>(); +#else + struct OpenFileDescriptor final: jmi::MethodTag { static const char* name() {return "openFileDescriptor";}}; + jmi::JObject pfd = cr.call, OpenFileDescriptor>(std::move(uri), "r"); +#endif + if (!pfd.error().empty()) { + qWarning("get ParcelFileDescriptor error: %s", pfd.error().data()); return; } - int fd = pfd.callMethod("detachFd", "()I"); + struct DetachFd final: jmi::MethodTag { static const char* name() {return "detachFd";}}; + int fd = pfd.call(); qt_file.open(fd, QIODevice::ReadOnly); } } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/libQtAV.pro qtav-1.13.0+ds/src/libQtAV.pro --- qtav-1.12.0+ds/src/libQtAV.pro 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/libQtAV.pro 2019-07-11 00:58:59.000000000 +0000 @@ -51,9 +51,9 @@ !rc_file { RC_ICONS = QtAV.ico - QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" + QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org" - QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV" } else:win32 { RC_FILE = QtAV.rc @@ -118,10 +118,15 @@ DEFINES += __STDC_CONSTANT_MACROS android { CONFIG *= config_opensl - !no_gui_private:qtHaveModule(androidextras) { #qt5.2 has QAndroidJniObject - QT *= androidextras gui-private #QPlatformNativeInterface get "QtActivity" + SOURCES *= jmi/jmi.cpp + qtHaveModule(androidextras) { #qt5.2 has QAndroidJniObject + QT *= androidextras #QPlatformNativeInterface get "QtActivity" SOURCES *= io/AndroidIO.cpp SOURCES *= codec/video/VideoDecoderMediaCodec.cpp + exists($$[QT_INSTALL_HEADERS]/MediaCodecTextureStandalone.h) { + DEFINES *= MEDIACODEC_TEXTURE + LIBS *= -lqtav-mediacodec + } } } config_x11 { @@ -152,7 +157,6 @@ ios { LIBS += -framework AVFoundation } else { - LIBS += -framework QTKit # assume avdevice targets to the same version as Qt and always >= 10.6 !isEqual(QMAKE_MACOSX_DEPLOYMENT_TARGET, 10.6): LIBS += -framework AVFoundation } @@ -162,6 +166,7 @@ config_avfilter { DEFINES += QTAV_HAVE_AVFILTER=1 LIBS += -lavfilter + mac:!ios:static_ffmpeg: LIBS += -framework AppKit } config_ipp { DEFINES += QTAV_HAVE_IPP=1 @@ -182,6 +187,9 @@ CONFIG *= config_openal SOURCES += output/audio/AudioOutputAudioToolbox.cpp LIBS += -framework AudioToolbox + LIBS += -Wl,-unexported_symbols_list,$$PWD/unexport.list +} else:!win32 { + #LIBS += -Wl,--exclude-libs,ALL } win32: { HEADERS += output/audio/xaudio2_compat.h @@ -321,14 +329,13 @@ } mac { HEADERS *= codec/video/SurfaceInteropCV.h - SOURCES *= codec/video/SurfaceInteropCV.cpp + SOURCES *= codec/video/SurfaceInteropCV.cpp \ + codec/video/SurfaceInteropIOSurface.mm ios { OBJECTIVE_SOURCES *= codec/video/SurfaceInteropCVOpenGLES.mm } else { - CONFIG += config_vda - SOURCES *= codec/video/SurfaceInteropIOSurface.cpp + #CONFIG += config_vda #SOURCES *= codec/video/SurfaceInteropCVOpenGL.cpp - LIBS += -framework IOSurface } LIBS += -framework CoreVideo -framework CoreFoundation } diff -Nru qtav-1.12.0+ds/src/opengl/OpenGLVideo.cpp qtav-1.13.0+ds/src/opengl/OpenGLVideo.cpp --- qtav-1.12.0+ds/src/opengl/OpenGLVideo.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/opengl/OpenGLVideo.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2017 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2014) @@ -173,7 +173,12 @@ gr->updateGeometry(geometry); } -OpenGLVideo::OpenGLVideo() {} +OpenGLVideo::OpenGLVideo() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +// TODO: system resolution change + connect(QGuiApplication::instance(), SIGNAL(primaryScreenChanged(QScreen*)), this, SLOT(updateViewport())); +#endif +} bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt) { @@ -206,16 +211,8 @@ d.material->setSaturation(s); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = ctx->findChild(QStringLiteral("__qtav_shader_manager")); - QSizeF surfaceSize = ctx->surface()->size(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) - surfaceSize *= ctx->screen()->devicePixelRatio(); -#else - surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's #endif -#else - QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height()); -#endif - setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); + updateViewport(); if (d.manager) return; // TODO: what if ctx is delete? @@ -349,7 +346,6 @@ DPTR_D(OpenGLVideo); Q_ASSERT(d.manager); Q_EMIT beforeRendering(); - DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) const qint64 mt = d.material->type(); if (d.material_type != mt) { qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); @@ -360,6 +356,7 @@ VideoShader *shader = d.user_shader; if (!shader) shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) + DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) shader->update(d.material); shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); // uniform end. attribute begin @@ -387,4 +384,21 @@ d_func().resetGL(); } +void OpenGLVideo::updateViewport() +{ + DPTR_D(OpenGLVideo); + if (!d.ctx) + return; +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QSizeF surfaceSize = d.ctx->surface()->size(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + surfaceSize *= d.ctx->screen()->devicePixelRatio(); +#else + surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's +#endif +#else + QSizeF surfaceSize = QSizeF(d.ctx->device()->width(), d.ctx->device()->height()); +#endif + setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); +} } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/output/audio/AudioOutputDSound.cpp qtav-1.13.0+ds/src/output/audio/AudioOutputDSound.cpp --- qtav-1.12.0+ds/src/output/audio/AudioOutputDSound.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/audio/AudioOutputDSound.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -221,17 +221,17 @@ //} // buffers_free.ref(); } - DWORD status; - stream_buf->GetStatus(&status); + //DWORD status; + //stream_buf->GetStatus(&status); //qDebug("status: %lu", status); - return; - if (status & DSBSTATUS_LOOPING) { + //return; + //if (status & DSBSTATUS_LOOPING) { // sound will loop even if buffer is finished - DX_ENSURE(stream_buf->Stop()); + //DX_ENSURE(stream_buf->Stop()); // reset positions to ensure the notification positions and played buffer matches - DX_ENSURE(stream_buf->SetCurrentPosition(0)); - write_offset = 0; - } + //DX_ENSURE(stream_buf->SetCurrentPosition(0)); + //write_offset = 0; + //} } bool AudioOutputDSound::write(const QByteArray &data) diff -Nru qtav-1.12.0+ds/src/output/audio/AudioOutputOpenSL.cpp qtav-1.13.0+ds/src/output/audio/AudioOutputOpenSL.cpp --- qtav-1.12.0+ds/src/output/audio/AudioOutputOpenSL.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/audio/AudioOutputOpenSL.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -27,6 +27,7 @@ #include #include #include +#include #endif #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" @@ -324,7 +325,11 @@ // Volume interface //SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false); - sem.release(buffer_count - sem.available()); + /* + * DO NOT call sem.release(buffer_count - sem.available()) because playInitialData() will enqueue buffers and then sem.release() is called in callback. + * Otherwise, Enqueue() the 1st(or more) real buffer may report SL_RESULT_BUFFER_INSUFFICIENT and noise will be played. + * Can not Enqueue() internally here because buffers are managed in AudioOutput to ensure 0-copy + */ return true; } diff -Nru qtav-1.12.0+ds/src/output/video/OpenGLRendererBase.cpp qtav-1.13.0+ds/src/output/video/OpenGLRendererBase.cpp --- qtav-1.12.0+ds/src/output/video/OpenGLRendererBase.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/video/OpenGLRendererBase.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) @@ -49,8 +49,8 @@ { matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); - if (orientation) - matrix.rotate(orientation, 0, 0, 1); // Z axis + if (rotation()) + matrix.rotate(rotation(), 0, 0, 1); // Z axis } OpenGLRendererBase::OpenGLRendererBase(OpenGLRendererBasePrivate &d) diff -Nru qtav-1.12.0+ds/src/output/video/QPainterRenderer.cpp qtav-1.13.0+ds/src/output/video/QPainterRenderer.cpp --- qtav-1.12.0+ds/src/output/video/QPainterRenderer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/video/QPainterRenderer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV @@ -100,7 +100,7 @@ if (d.pixmap.isNull()) return; QRect roi = realROI(); - if (orientation() == 0) { + if (d.rotation() == 0) { //assume that the image data is already scaled to out_size(NOT renderer size!) if (roi.size() == d.out_rect.size()) { d.painter->drawPixmap(d.out_rect.topLeft(), d.pixmap, roi); @@ -116,11 +116,11 @@ d.painter->save(); d.painter->translate(rendererWidth()/2, rendererHeight()/2); // TODO: why rotate then scale gives wrong result? - if (orientation() % 180) + if (d.rotation() % 180) d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererHeight(), (qreal)d.out_rect.height()/(qreal)rendererWidth()); else d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererWidth(), (qreal)d.out_rect.height()/(qreal)rendererHeight()); - d.painter->rotate(orientation()); + d.painter->rotate(d.rotation()); d.painter->translate(-rendererWidth()/2, -rendererHeight()/2); d.painter->drawPixmap(QRect(0, 0, rendererWidth(), rendererHeight()), d.pixmap, roi); d.painter->restore(); diff -Nru qtav-1.12.0+ds/src/output/video/VideoOutput.cpp qtav-1.13.0+ds/src/output/video/VideoOutput.cpp --- qtav-1.12.0+ds/src/output/video/VideoOutput.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/video/VideoOutput.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) @@ -40,7 +40,11 @@ #if defined(Q_OS_DARWIN) avwidgets.setFileName(QStringLiteral("QtAVWidgets.framework/QtAVWidgets")); //no dylib check #elif defined(Q_OS_WIN) - avwidgets.setFileName(QStringLiteral("QtAVWidgets").append(QString::number(QTAV_VERSION_MAJOR(QtAV_Version())))); + avwidgets.setFileName(QStringLiteral("QtAVWidgets") +# ifndef QT_NO_DEBUG + .append("d") +# endif + .append(QString::number(QTAV_VERSION_MAJOR(QtAV_Version())))); #else avwidgets.setFileNameAndVersion(QStringLiteral("QtAVWidgets"), QTAV_VERSION_MAJOR(QtAV_Version())); #endif diff -Nru qtav-1.12.0+ds/src/output/video/VideoRenderer.cpp qtav-1.13.0+ds/src/output/video/VideoRenderer.cpp --- qtav-1.12.0+ds/src/output/video/VideoRenderer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/output/video/VideoRenderer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV @@ -288,11 +288,11 @@ void VideoRenderer::setOrientation(int value) { + DPTR_D(VideoRenderer); // currently only supports a multiple of 90 value = (value + 360) % 360; if (value % 90) return; - DPTR_D(VideoRenderer); if (d.orientation == value) return; int old = orientation(); @@ -312,7 +312,8 @@ int VideoRenderer::orientation() const { - return d_func().orientation; + DPTR_D(const VideoRenderer); + return d.orientation; } // only qpainter and opengl based renderers support orientation. @@ -505,6 +506,7 @@ */ if (d.video_frame.isValid()) { drawFrame(); + //qDebug("render elapsed: %lld", et.elapsed()); if (d.statistics) { d.statistics->video_only.frameDisplayed(d.video_frame.timestamp()); d.statistics->video.current_time = QTime(0, 0, 0).addMSecs(int(d.video_frame.timestamp() * 1000.0)); diff -Nru qtav-1.12.0+ds/src/QtAV/AudioEncoder.h qtav-1.13.0+ds/src/QtAV/AudioEncoder.h --- qtav-1.12.0+ds/src/QtAV/AudioEncoder.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/AudioEncoder.h 2019-07-11 00:58:59.000000000 +0000 @@ -64,6 +64,8 @@ */ const AudioFormat& audioFormat() const; void setAudioFormat(const AudioFormat& format); + + int frameSize() const; Q_SIGNALS: void audioFormatChanged(); public: diff -Nru qtav-1.12.0+ds/src/QtAV/AudioFormat.h qtav-1.13.0+ds/src/QtAV/AudioFormat.h --- qtav-1.12.0+ds/src/QtAV/AudioFormat.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/AudioFormat.h 2019-07-11 00:58:59.000000000 +0000 @@ -68,6 +68,9 @@ ChannelLayout_Stereo, ChannelLayout_Unsupported //ok. now it's not complete }; + + static const qint64 kHz = 1000000LL; + //typedef qint64 ChannelLayout; //currently use latest FFmpeg's // TODO: constexpr friend int RawSampleSize(SampleFormat fmt) { return fmt & ((1<<(kSize+1)) - 1); } diff -Nru qtav-1.12.0+ds/src/QtAV/AudioFrame.h qtav-1.13.0+ds/src/QtAV/AudioFrame.h --- qtav-1.12.0+ds/src/QtAV/AudioFrame.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/AudioFrame.h 2019-07-11 00:58:59.000000000 +0000 @@ -57,6 +57,8 @@ * then you can use clone(). */ AudioFrame clone() const; + AudioFrame mid(int pos, int len = -1) const; + void prepend(AudioFrame &other); AudioFormat format() const; void setSamplesPerChannel(int samples); // may change after resampling diff -Nru qtav-1.12.0+ds/src/QtAV/AVPlayer.h qtav-1.13.0+ds/src/QtAV/AVPlayer.h --- qtav-1.12.0+ds/src/QtAV/AVPlayer.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/AVPlayer.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,4 +1,4 @@ -/****************************************************************************** +/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin @@ -82,6 +82,7 @@ Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged) Q_PROPERTY(QtAV::MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged) Q_PROPERTY(QtAV::MediaEndAction mediaEndAction READ mediaEndAction WRITE setMediaEndAction NOTIFY mediaEndActionChanged) + Q_PROPERTY(unsigned int chapters READ chapters NOTIFY chaptersChanged) Q_ENUMS(State) public: /*! @@ -199,12 +200,30 @@ /*! * \brief externalAudioTracks * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support. - * [ {id: 0, file: abc.dts, language: eng, title: xyz}, ...] + * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...] * id: used for setAudioStream(id) + * stream_index: the actual muxer stream index -- not used by API to this class but possibly + * useful for interop with external libs that require absolute stream_index * \sa externalAudioTracksChanged */ const QVariantList& externalAudioTracks() const; + /*! + * \brief internalAudioTracks + * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support. + * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...] + * id: used for setAudioStream(id) + * stream_index: the actual muxer stream index -- not used by API to this class but possibly + * useful for interop with external libs that require absolute stream_index + */ const QVariantList& internalAudioTracks() const; + /*! + * \brief internalVideoTracks + * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support. + * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...] + * id: used for setAudioStream(id) + * stream_index: the actual muxer stream index -- not used by API to this class but possibly + * useful for interop with external libs that require absolute stream_index + */ const QVariantList& internalVideoTracks() const; /*! * \brief setAudioStream @@ -356,6 +375,7 @@ int contrast() const; int hue() const; //not implemented int saturation() const; + unsigned int chapters() const; /*! * \sa AVDemuxer::setOptions() * example: @@ -471,6 +491,8 @@ void seek(qint64 pos); //ms. same as setPosition(pos) void seekForward(); void seekBackward(); + void seekNextChapter(); + void seekPreviousChapter(); void setSeekType(SeekType type); SeekType seekType() const; @@ -561,6 +583,7 @@ void contrastChanged(int val); void hueChanged(int val); void saturationChanged(int val); + void chaptersChanged(unsigned int val); void subtitleStreamChanged(int value); /*! * \brief internalAudioTracksChanged @@ -593,6 +616,7 @@ void updateMediaStatus(QtAV::MediaStatus status); void onSeekFinished(qint64 value); void tryClearVideoRenderers(); + void seekChapter(int incr); protected: // TODO: set position check timer interval virtual void timerEvent(QTimerEvent *); diff -Nru qtav-1.12.0+ds/src/QtAV/EncodeFilter.h qtav-1.13.0+ds/src/QtAV/EncodeFilter.h --- qtav-1.12.0+ds/src/QtAV/EncodeFilter.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/EncodeFilter.h 2019-07-11 00:58:59.000000000 +0000 @@ -82,7 +82,7 @@ void frameEncoded(const QtAV::Packet& packet); void startTimeChanged(qint64 value); // internal use only - void requestToEncode(const AudioFrame& frame); + void requestToEncode(const QtAV::AudioFrame& frame); protected Q_SLOTS: void encode(const QtAV::AudioFrame& frame = AudioFrame()); protected: diff -Nru qtav-1.12.0+ds/src/QtAV/Frame.h qtav-1.13.0+ds/src/QtAV/Frame.h --- qtav-1.12.0+ds/src/QtAV/Frame.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/Frame.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -61,7 +61,17 @@ */ int bytesPerLine(int plane = 0) const; // the whole frame data. may be empty unless clone() or allocate is called + // real data starts with dataAlignment() aligned address QByteArray frameData() const; + int dataAlignment() const; + uchar* frameDataPtr(int* size = NULL) const { + const int a = dataAlignment(); + uchar* p = (uchar*)frameData().constData(); + const int offset = (a - ((quintptr)p & (a-1))) & (a-1); + if (size) + *size = frameData().size() - offset; + return p+offset; + } // deep copy 1 plane data QByteArray data(int plane = 0) const; uchar* bits(int plane = 0); diff -Nru qtav-1.12.0+ds/src/QtAV/OpenGLVideo.h qtav-1.13.0+ds/src/QtAV/OpenGLVideo.h --- qtav-1.12.0+ds/src/QtAV/OpenGLVideo.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/OpenGLVideo.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2014) @@ -115,6 +115,7 @@ * shader manager and material will be reset */ void resetGL(); + void updateViewport(); }; diff -Nru qtav-1.12.0+ds/src/QtAV/private/AVCompat.h qtav-1.13.0+ds/src/QtAV/private/AVCompat.h --- qtav-1.12.0+ds/src/QtAV/private/AVCompat.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/private/AVCompat.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,7 +1,7 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg solve the version problem and diffirent api in FFmpeg and libav - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -59,6 +59,10 @@ #include #include #include +#include + +#define AVCODEC_STATIC_REGISTER FFMPEG_MODULE_CHECK(LIBAVCODEC, 58, 10, 100) +#define AVFORMAT_STATIC_REGISTER FFMPEG_MODULE_CHECK(LIBAVFORMAT, 58, 9, 100) #if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 51, 73, 101) #include @@ -79,8 +83,11 @@ #endif //QTAV_HAVE(AVRESAMPLE) #if QTAV_HAVE(AVFILTER) +#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(3,8,0) #include /*code is here for old version*/ +#else #include +#endif #include #include #endif //QTAV_HAVE(AVFILTER) @@ -456,3 +463,15 @@ } } while(0) #endif //QTAV_COMPAT_H + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,33,0) +#define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER +#endif + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,56,100) +#define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE +#endif + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,56,100) +#define AV_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE +#endif diff -Nru qtav-1.12.0+ds/src/QtAV/private/AVEncoder_p.h qtav-1.13.0+ds/src/QtAV/private/AVEncoder_p.h --- qtav-1.12.0+ds/src/QtAV/private/AVEncoder_p.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/private/AVEncoder_p.h 2019-07-11 00:58:59.000000000 +0000 @@ -71,6 +71,7 @@ public: AudioEncoderPrivate() : AVEncoderPrivate() + , frame_size(0) { bit_rate = 64000; } @@ -79,6 +80,8 @@ AudioResampler *resampler; AudioFormat format, format_used; + + int frame_size; // used if avctx->frame_size == 0 }; class Q_AV_PRIVATE_EXPORT VideoEncoderPrivate : public AVEncoderPrivate diff -Nru qtav-1.12.0+ds/src/QtAV/private/Frame_p.h qtav-1.13.0+ds/src/QtAV/private/Frame_p.h --- qtav-1.12.0+ds/src/QtAV/private/Frame_p.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/private/Frame_p.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2013) @@ -36,6 +36,7 @@ public: FramePrivate() : timestamp(0) + , data_align(1) {} virtual ~FramePrivate() {} @@ -44,6 +45,7 @@ QVariantMap metadata; QByteArray data; qreal timestamp; + int data_align; }; } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/QtAV/private/VideoRenderer_p.h qtav-1.13.0+ds/src/QtAV/private/VideoRenderer_p.h --- qtav-1.12.0+ds/src/QtAV/private/VideoRenderer_p.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/private/VideoRenderer_p.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Media play library based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV @@ -28,7 +28,7 @@ #include #include #include - +#include "QtAV/Statistics.h" /*TODO: * Region of Interest(ROI) * use matrix to compute out rect, mapped point etc @@ -54,7 +54,6 @@ , out_aspect_ratio_mode(VideoRenderer::VideoAspectRatio) , out_aspect_ratio(0) , quality(VideoRenderer::QualityBest) - , orientation(0) , preferred_format(VideoFormat::Format_RGB32) , force_preferred(false) , brightness(0) @@ -62,6 +61,7 @@ , hue(0) , saturation(0) , bg_color(0, 0, 0) + , orientation(0) { //conv.setInFormat(PIX_FMT_YUV420P); //conv.setOutFormat(PIX_FMT_BGR32); //TODO: why not RGB32? @@ -79,7 +79,11 @@ return out_rect0 != out_rect; } // dar: displayed aspect ratio in video renderer orientation - const qreal dar = (orientation % 180) ? 1.0/outAspectRatio : outAspectRatio; + int rotate = orientation; + if (statistics) { + rotate += int(statistics->video_only.rotate); + } + const qreal dar = (rotate % 180) ? 1.0/outAspectRatio : outAspectRatio; //qDebug("out rect: %f %dx%d ==>", out_aspect_ratio, out_rect.width(), out_rect.height()); if (rendererAspectRatio >= dar) { //equals to original video aspect ratio here, also equals to out ratio //renderer is too wide, use renderer's height, horizonal align center @@ -97,6 +101,11 @@ return out_rect0 != out_rect; } virtual void setupQuality() {} + int rotation() const { + if (!statistics) + return orientation; + return statistics->video_only.rotate + orientation; + } //draw background when necessary, for example, renderer is resized. Then set to false bool update_background; @@ -114,7 +123,6 @@ //out_rect: the displayed video frame out_rect in the renderer QRect out_rect; //TODO: out_out_rect QRectF roi; - int orientation; VideoFrame video_frame; VideoFormat::PixelFormat preferred_format; @@ -122,6 +130,9 @@ qreal brightness, contrast, hue, saturation; QColor bg_color; +private: + int orientation; + friend class VideoRenderer; }; } //namespace QtAV diff -Nru qtav-1.12.0+ds/src/QtAV/QtAV_Global.h qtav-1.13.0+ds/src/QtAV/QtAV_Global.h --- qtav-1.12.0+ds/src/QtAV/QtAV_Global.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/QtAV_Global.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -168,11 +168,9 @@ #ifndef QByteArrayLiteral #define QByteArrayLiteral(str) QByteArray(str, sizeof(str) - 1) #endif -/* - * msvc sucks! can not deal with (defined(QTAV_HAVE_##FEATURE) && QTAV_HAVE_##FEATURE) - */ + // TODO: internal use. move to a private header -#define QTAV_HAVE(FEATURE) (defined QTAV_HAVE_##FEATURE && QTAV_HAVE_##FEATURE) +#define QTAV_HAVE(FEATURE) (QTAV_HAVE_##FEATURE+0) #ifndef Q_DECL_OVERRIDE #define Q_DECL_OVERRIDE diff -Nru qtav-1.12.0+ds/src/QtAV/Statistics.h qtav-1.13.0+ds/src/QtAV/Statistics.h --- qtav-1.12.0+ds/src/QtAV/Statistics.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/Statistics.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) @@ -106,6 +106,7 @@ */ int gop_size; QString pix_fmt; + int rotate; /// return current absolute time (seconds since epcho qint64 frameDisplayed(qreal pts); // used to compute currentDisplayFPS() private: diff -Nru qtav-1.12.0+ds/src/QtAV/version.h qtav-1.13.0+ds/src/QtAV/version.h --- qtav-1.12.0+ds/src/QtAV/version.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/version.h 2019-07-11 00:58:59.000000000 +0000 @@ -23,7 +23,7 @@ #define QTAV_VERSION_H #define QTAV_MAJOR 1 //((QTAV_VERSION&0xff0000)>>16) -#define QTAV_MINOR 12 //((QTAV_VERSION&0xff00)>>8) +#define QTAV_MINOR 13 //((QTAV_VERSION&0xff00)>>8) #define QTAV_PATCH 0 //(QTAV_VERSION&0xff) diff -Nru qtav-1.12.0+ds/src/QtAV/VideoFrameExtractor.h qtav-1.13.0+ds/src/QtAV/VideoFrameExtractor.h --- qtav-1.12.0+ds/src/QtAV/VideoFrameExtractor.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/VideoFrameExtractor.h 2019-07-11 00:58:59.000000000 +0000 @@ -67,12 +67,12 @@ void setPosition(qint64 value); qint64 position() const; - virtual bool event(QEvent *e); Q_SIGNALS: void frameExtracted(const QtAV::VideoFrame& frame); // parameter: VideoFrame, bool changed? void sourceChanged(); void asyncChanged(); - void error(); // clear preview image in a slot + void error(const QString &errorMessage); ///< emitted with a helpful error message -- connect to this to show empty image in preview widget + void aborted(const QString &abortMessage); ///< emitted when aborting the current preview -- if user requested a new preview this usually gets emitted. connect to this to show empty preview void autoExtractChanged(); /*! * \brief positionChanged @@ -81,8 +81,6 @@ void positionChanged(); void precisionChanged(); - void aboutToExtract(qint64 pos); - public Q_SLOTS: /*! * \brief extract diff -Nru qtav-1.12.0+ds/src/QtAV/VideoFrame.h qtav-1.13.0+ds/src/QtAV/VideoFrame.h --- qtav-1.12.0+ds/src/QtAV/VideoFrame.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV/VideoFrame.h 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -52,7 +52,8 @@ VideoFrame(); //must set planes and linesize manually if data is empty // must set planes and linesize manually - VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray()); + // alignment: data ptr alignment + VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray(), int alignment = 1); VideoFrame(const QImage& image); VideoFrame(const VideoFrame &other); ~VideoFrame(); diff -Nru qtav-1.12.0+ds/src/QtAV_Global.cpp qtav-1.13.0+ds/src/QtAV_Global.cpp --- qtav-1.12.0+ds/src/QtAV_Global.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV_Global.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2017 Wang Bin + Copyright (C) 2012-2019 Wang Bin * This file is part of QtAV @@ -190,13 +190,13 @@ { static QString about = QString::fromLatin1("

QtAV " QTAV_VERSION_STR_LONG "

\n" "

%1

%2

%3

" - "

Copyright (C) 2012-2016 Wang Bin (aka. Lucas Wang) wbsecg1@gmail.com

\n" + "

Copyright (C) 2012-2019 Wang Bin (aka. Lucas Wang) wbsecg1@gmail.com

\n" "

%4: http://qtav.org/donate.html

\n" "

%5: https://github.com/wang-bin/QtAV

\n" "

%6: http://qtav.org

" ).arg(QObject::tr("Multimedia framework base on Qt and FFmpeg.\n")) .arg(QObject::tr("Distributed under the terms of LGPLv2.1 or later.\n")) - .arg(QObject::tr("Shanghai University->S3 Graphics->Deepin->PPTV, Shanghai, China")) + .arg(QObject::tr("Shanghai, China")) .arg(QObject::tr("Donate")) .arg(QObject::tr("Source")) .arg(QObject::tr("Home page")); @@ -275,9 +275,15 @@ void* obj = const_cast(reinterpret_cast(avformat_get_class())); opts = Internal::optionsToString((void*)&obj); opts.append(ushort('\n')); - av_register_all(); +#if AVFORMAT_STATIC_REGISTER + const AVInputFormat *i = NULL; + void* it = NULL; + while ((i = av_demuxer_iterate(&it))) { +#else AVInputFormat *i = NULL; + av_register_all(); // MUST register all input/output formats while ((i = av_iformat_next(i))) { +#endif QString opt(Internal::optionsToString((void*)&i->priv_class).trimmed()); if (opt.isEmpty()) continue; @@ -285,8 +291,15 @@ .arg(QLatin1String(i->name)) .arg(opt)); } +#if AVFORMAT_STATIC_REGISTER + const AVOutputFormat *o = NULL; + it = NULL; + while ((o = av_muxer_iterate(&it))) { +#else + av_register_all(); // MUST register all input/output formats AVOutputFormat *o = NULL; while ((o = av_oformat_next(o))) { +#endif QString opt(Internal::optionsToString((void*)&o->priv_class).trimmed()); if (opt.isEmpty()) continue; @@ -305,9 +318,14 @@ void* obj = const_cast(reinterpret_cast(avcodec_get_class())); opts = Internal::optionsToString((void*)&obj); opts.append(ushort('\n')); + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else avcodec_register_all(); - AVCodec* c = NULL; - while ((c=av_codec_next(c))) { + while ((c = av_codec_next(c))) { +#endif QString opt(Internal::optionsToString((void*)&c->priv_class).trimmed()); if (opt.isEmpty()) continue; diff -Nru qtav-1.12.0+ds/src/QtAV.rc qtav-1.13.0+ds/src/QtAV.rc --- qtav-1.12.0+ds/src/QtAV.rc 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/QtAV.rc 2019-07-11 00:58:59.000000000 +0000 @@ -19,10 +19,10 @@ BEGIN BLOCK "000004b0" BEGIN - VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" + VALUE "CompanyName", "wbsecg1@gmail.com" VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org" VALUE "FileVersion", QTAV_VERSION_STR ".0" - VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + VALUE "LegalCopyright", "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" VALUE "InternalName", "QtAV" VALUE "OriginalFilename", "QtAV.dll" VALUE "ProductName", "QtAV" diff -Nru qtav-1.12.0+ds/src/Statistics.cpp qtav-1.13.0+ds/src/Statistics.cpp --- qtav-1.12.0+ds/src/Statistics.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/Statistics.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) @@ -56,6 +56,7 @@ , coded_width(0) , coded_height(0) , gop_size(0) + , rotate(0) , d(new Private()) { } @@ -66,6 +67,7 @@ , coded_width(v.coded_width) , coded_height(v.coded_height) , gop_size(v.gop_size) + , rotate(v.rotate) , d(v.d) { } @@ -77,6 +79,7 @@ coded_width = v.coded_width; coded_height = v.coded_height; gop_size = v.gop_size; + rotate = v.rotate; d = v.d; return *this; } diff -Nru qtav-1.12.0+ds/src/subtitle/SubtitleProcessorFFmpeg.cpp qtav-1.13.0+ds/src/subtitle/SubtitleProcessorFFmpeg.cpp --- qtav-1.12.0+ds/src/subtitle/SubtitleProcessorFFmpeg.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/subtitle/SubtitleProcessorFFmpeg.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV (from 2014) @@ -80,15 +80,26 @@ QStringList ffmpeg_supported_sub_extensions_by_codec() { QStringList exts; - AVCodec *c = av_codec_next(NULL); - while (c) { - if (c->type != AVMEDIA_TYPE_SUBTITLE) { - c = av_codec_next(c); + const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + void* it = NULL; + while ((c = av_codec_iterate(&it))) { +#else + avcodec_register_all(); + while ((c = av_codec_next(c))) { +#endif + if (c->type != AVMEDIA_TYPE_SUBTITLE) continue; - } qDebug("sub codec: %s", c->name); - AVInputFormat *i = av_iformat_next(NULL); - while (i) { +#if AVFORMAT_STATIC_REGISTER + const AVInputFormat *i = NULL; + void* it2 = NULL; + while ((i = av_demuxer_iterate(&it2))) { +#else + av_register_all(); // MUST register all input/output formats + AVInputFormat *i = NULL; + while ((i = av_iformat_next(i))) { +#endif if (!strcmp(i->name, c->name)) { qDebug("found iformat"); if (i->extensions) { @@ -99,13 +110,11 @@ } break; } - i = av_iformat_next(i); } if (!i) { //qDebug("codec name '%s' is not found in AVInputFormat, just append codec name", c->name); //exts.append(c->name); } - c = av_codec_next(c); } return exts; } @@ -113,8 +122,15 @@ QStringList ffmpeg_supported_sub_extensions() { QStringList exts; +#if AVFORMAT_STATIC_REGISTER + const AVInputFormat *i = NULL; + void* it = NULL; + while ((i = av_demuxer_iterate(&it))) { +#else + av_register_all(); // MUST register all input/output formats AVInputFormat *i = NULL; while ((i = av_iformat_next(i))) { +#endif // strstr parameters can not be null if (i->long_name && strstr(i->long_name, "subtitle")) { if (i->extensions) { @@ -127,7 +143,13 @@ // AVCodecDescriptor.name and AVCodec.name may be different. avcodec_get_name() use AVCodecDescriptor if possible QStringList codecs; const AVCodec* c = NULL; +#if AVCODEC_STATIC_REGISTER + it = NULL; + while ((c = av_codec_iterate(&it))) { +#else + avcodec_register_all(); while ((c = av_codec_next(c))) { +#endif if (c->type == AVMEDIA_TYPE_SUBTITLE) codecs.append(QString::fromLatin1(c->name)); } @@ -249,7 +271,7 @@ codec_ctx->time_base.den = 1000; if (!data.isEmpty()) { av_free(codec_ctx->extradata); - codec_ctx->extradata = (uint8_t*)av_mallocz(data.size() + FF_INPUT_BUFFER_PADDING_SIZE); + codec_ctx->extradata = (uint8_t*)av_mallocz(data.size() + AV_INPUT_BUFFER_PADDING_SIZE); if (!codec_ctx->extradata) return false; codec_ctx->extradata_size = data.size(); diff -Nru qtav-1.12.0+ds/src/unexport.list qtav-1.13.0+ds/src/unexport.list --- qtav-1.12.0+ds/src/unexport.list 1970-01-01 00:00:00.000000000 +0000 +++ qtav-1.13.0+ds/src/unexport.list 2019-07-11 00:58:59.000000000 +0000 @@ -0,0 +1,8 @@ +_av* +_ff* +_sw* +_uchardet_* +_ass_* +_lzma_* +_al* +_rgb* diff -Nru qtav-1.12.0+ds/src/utils/BlockingQueue.h qtav-1.13.0+ds/src/utils/BlockingQueue.h --- qtav-1.12.0+ds/src/utils/BlockingQueue.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/utils/BlockingQueue.h 2019-07-11 00:58:59.000000000 +0000 @@ -48,8 +48,26 @@ */ void setThreshold(int min); //wake up and enqueue - void put(const T& t); - T take(); + /*! \brief put + * put t into the queue. Will block if blockFull is set to true (and optionally can specify a wait timeout in ms). + * \param t the item to copy into the queue + * \param wait_timeout_ms this parameter is used if blockFull == true (the default). + * If the queue is full, optionally wait for the queue to not be full a maximum of wait_timeout_ms milliseconds, + * and return false if timeout expires. ULONG_MAX means wait indefinitely. + * \return true if item was successfully placed in the queue and the queue was not full. + * false is returned if the queue is (still) full. The item is still placed into a full queue! + * Note that even a 'full' queue will accept new items and t WILL be placed in the queue regardless of return value. + */ + bool put(const T& t, unsigned long wait_timeout_ms = ULONG_MAX); + /*! \brief take + * Dequeue 1 item from queue, optionally blocking. + * \param wait_timeout_ms this parameter is used if blockEmpty == true (the default). + * If the queue is empty, optionally wait for the queue to not be empty a maximum of wait_timeout_ms milliseconds. + * ULONG_MAX means wait indefinitely. + * \param isValid a pointer to a bool (optional). If isValid is set to true after a call, the returned item is valid. False means the queue was empty or the timeout expired. + * \return the item taken. It may not be valid if the queue was empty and timeout expired. Check optional isValid flag to determine if that is the case. + */ + T take(unsigned long wait_timeout_ms = ULONG_MAX, bool *isValid = 0); void setBlocking(bool block); //will wake if false. called when no more data can enqueue void blockEmpty(bool block); void blockFull(bool block); @@ -133,17 +151,21 @@ } template class Container> -void BlockingQueue::put(const T& t) +bool BlockingQueue::put(const T& t, unsigned long timeout_ms) { + bool ret = true; QWriteLocker locker(&lock); Q_UNUSED(locker); if (checkFull()) { + ret = false; //qDebug("queue full"); //too frequent if (full_callback) { full_callback->call(); } if (block_full) - cond_full.wait(&lock); + ret = cond_full.wait(&lock, timeout_ms); + // uncomment here to reject placing items into a full queue -- update API docs if you do this. + // if (!ret) return false; } queue.enqueue(t); onPut(t); // emit bufferProgressChanged here if buffering @@ -153,11 +175,13 @@ } else { //qDebug("buffering: %d/%d~%d", queue.size(), thres, cap); } + return ret; } template class Container> -T BlockingQueue::take() +T BlockingQueue::take(unsigned long timeout_ms, bool *isValid) { + if (isValid) *isValid = false; QWriteLocker locker(&lock); Q_UNUSED(locker); if (checkEmpty()) {//TODO:always block? @@ -166,7 +190,7 @@ empty_callback->call(); } if (block_empty) - cond_empty.wait(&lock); //block when empty only + cond_empty.wait(&lock,timeout_ms); //block when empty only } if (checkEmpty()) { //qWarning("Queue is still empty"); @@ -176,6 +200,7 @@ return T(); } T t(queue.dequeue()); + if (isValid) *isValid = true; cond_full.wakeOne(); onTake(t); // emit start buffering here if empty return t; diff -Nru qtav-1.12.0+ds/src/utils/DirectXHelper.cpp qtav-1.13.0+ds/src/utils/DirectXHelper.cpp --- qtav-1.12.0+ds/src/utils/DirectXHelper.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/utils/DirectXHelper.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -84,8 +84,8 @@ D3DPRESENT_PARAMETERS d3dpp; InitParameters(&d3dpp); // D3DCREATE_MULTITHREADED is required by gl interop. https://www.opengl.org/registry/specs/NV/DX_interop.txt - // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING in cuda samples - DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_MIXED_VERTEXPROCESSING; + // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING is required by cuda in cuD3D9CtxCreate() + DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING; IDirect3DDevice9Ex *d3d9dev = NULL; // mpv: /* Direct3D needs a HWND to create a device, even without using ::Present diff -Nru qtav-1.12.0+ds/src/VideoCapture.cpp qtav-1.13.0+ds/src/VideoCapture.cpp --- qtav-1.12.0+ds/src/VideoCapture.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/VideoCapture.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** VideoCapture.cpp: description - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -84,7 +84,9 @@ QMetaObject::invokeMethod(cap, "failed"); return; } - if (file.write(frame.frameData()) <= 0) { + int sz = 0; + const char* data = (const char*)frame.frameDataPtr(&sz); + if (file.write(data, sz) <= 0) { qWarning("VideoCapture is failed to write captured frame with original format"); QMetaObject::invokeMethod(cap, "failed"); file.close(); diff -Nru qtav-1.12.0+ds/src/VideoFrame.cpp qtav-1.13.0+ds/src/VideoFrame.cpp --- qtav-1.12.0+ds/src/VideoFrame.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/VideoFrame.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2018 Wang Bin * This file is part of QtAV @@ -154,11 +154,12 @@ { } -VideoFrame::VideoFrame(int width, int height, const VideoFormat &format, const QByteArray& data) +VideoFrame::VideoFrame(int width, int height, const VideoFormat &format, const QByteArray& data, int alignment) : Frame(new VideoFramePrivate(width, height, format)) { Q_D(VideoFrame); d->data = data; + d->data_align = alignment; } VideoFrame::VideoFrame(const QImage& image) @@ -353,7 +354,7 @@ VideoFrame f(to(VideoFormat(VideoFormat::pixelFormatFromImageFormat(fmt)), dstSize, roi)); if (!f) return QImage(); - QImage image((const uchar*)f.frameData().constData(), f.width(), f.height(), f.bytesPerLine(0), fmt); + QImage image(f.frameDataPtr(), f.width(), f.height(), f.bytesPerLine(0), fmt); return image.copy(); } @@ -395,7 +396,7 @@ qWarning() << "VideoFrame::to error: " << format() << "=>" << fmt; return VideoFrame(); } - VideoFrame f(w, h, fmt, conv.outData()); + VideoFrame f(w, h, fmt, conv.outData(), ImageConverter::DataAlignment); f.setBits(conv.outPlanes()); f.setBytesPerLine(conv.outLineSizes()); if (fmt.isRGB()) { diff -Nru qtav-1.12.0+ds/src/VideoFrameExtractor.cpp qtav-1.13.0+ds/src/VideoFrameExtractor.cpp --- qtav-1.12.0+ds/src/VideoFrameExtractor.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/VideoFrameExtractor.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "QtAV/VideoCapture.h" #include "QtAV/VideoDecoder.h" #include "QtAV/AVDemuxer.h" @@ -33,19 +35,18 @@ #include "utils/BlockingQueue.h" #include "utils/Logger.h" -// TODO: event and signal do not work -#define ASYNC_SIGNAL 0 -#define ASYNC_EVENT 0 -#define ASYNC_TASK 1 namespace QtAV { class ExtractThread : public QThread { public: ExtractThread(QObject *parent = 0) : QThread(parent) + , timeout_ms(50UL) , stop(false) { - tasks.setCapacity(1); // avoid too frequent + // avoid too frequent -- we only care about the latest request + // whether it be for a frame or to stop thread or whatever else. + tasks.setCapacity(1); } ~ExtractThread() { waitStop(); @@ -57,19 +58,37 @@ wait(); } + unsigned long timeout_ms; + void addTask(QRunnable* t) { - if (tasks.size() >= tasks.capacity()) { - QRunnable *task = tasks.take(); //clear only for seek task - if (task->autoDelete()) + // Note that while a simpler solution would have been to not use + // a custom 'Task' mechanism but rather to use signals + // or QEvent posting to this thread -- the approach here has a very + // advantageous property. Namely, duplicate repeated requests for + // a frame end up getting dropped and only the latest request gets + // serviced. This interoperates well with eg VideoPreviewWidget + // which really only cares about the latest frame requested by the user. + while (tasks.size() >= tasks.capacity() && tasks.capacity() > 0) { + // Race condition existed here -- to avoid it we need to take() + // with a timeout (to make sure it doesn't hang) and check return value. + // This is because extractor thread is also calling .take() at the same time, + // and in rare cases the above expression in the while loop evaluates to true + // while by the time the below line executes the underlying queue may become empty. + // This led to very occasional hangs. Hence this .take() call was modified to include + // a timeout. + QRunnable *task = tasks.take(timeout_ms); //clear for seek & stop task + if (task && task->autoDelete()) delete task; } - tasks.put(t); + if (!tasks.put(t,timeout_ms)) { + qWarning("ExtractThread::addTask -- added a task to an already-full queue! FIXME!"); + } } void scheduleStop() { class StopTask : public QRunnable { public: StopTask(ExtractThread* t) : thread(t) {} - void run() { thread->stop = true;} + void run() { thread->stop = true; } private: ExtractThread *thread; }; @@ -78,18 +97,15 @@ protected: virtual void run() { -#if ASYNC_TASK while (!stop) { QRunnable *task = tasks.take(); - if (!task) - return; - task->run(); - if (task->autoDelete()) - delete task; + if (task) { + task->run(); + if (task->autoDelete()) + delete task; + } } -#else - exec(); -#endif //ASYNC_TASK + qDebug("ExtractThread exiting..."); } public: volatile bool stop; @@ -103,8 +119,7 @@ { public: VideoFrameExtractorPrivate() - : extracted(false) - , abort_seek(false) + : abort_seek(false) , async(true) , has_video(true) , auto_extract(true) @@ -135,7 +150,10 @@ #if QTAV_HAVE(VDA) // << QStringLiteral("VDA") // only 1 app can use VDA at a given time #endif //QTAV_HAVE(VDA) - << QStringLiteral("FFmpeg"); +#if QTAV_HAVE(VIDEOTOOLBOX) + // << QStringLiteral("VideoToolbox") +#endif //QTAV_HAVE(VIDEOTOOLBOX) + << QStringLiteral("FFmpeg"); } ~VideoFrameExtractorPrivate() { // stop first before demuxer and decoder close to avoid running new seek task after demuxer is closed. @@ -168,13 +186,15 @@ else precision = kDefaultPrecision; } + demuxer.setStreamIndex(AVDemuxer::VideoStream, 0); foreach (const QString& c, codecs) { VideoDecoder *vd = VideoDecoder::create(c.toUtf8().constData()); if (!vd) continue; decoder.reset(vd); - decoder->setCodecContext(demuxer.videoCodecContext()); - if (!decoder->open()) { + AVCodecContext *cctx = demuxer.videoCodecContext(); + if (cctx) decoder->setCodecContext(demuxer.videoCodecContext()); + if (!cctx || !decoder->open()) { decoder.reset(0); continue; } @@ -189,8 +209,10 @@ } // return the key frame position - bool extractInPrecision(qint64 value, int range) { + bool extractInPrecision(qint64 value, int range, QString & err, bool & aborted) { abort_seek = false; + err = ""; + aborted = false; frame = VideoFrame(); if (value < demuxer.startTime()) value += demuxer.startTime(); @@ -202,7 +224,9 @@ bool warn_out_of_range = true; while (!demuxer.atEnd()) { if (abort_seek) { + err = "abort seek before read"; qDebug("VideoFrameExtractor abort seek before read"); + aborted = true; return false; } if (!demuxer.readFrame()) @@ -236,6 +260,7 @@ } if (!pkt.isValid()) { qWarning("VideoFrameExtractor failed to get a packet at %lld", value); + err = QString().sprintf("failed to get a packet at %lld",value); return false; } decoder->flush(); //must flush otherwise old frames will be decoded at the beginning @@ -245,6 +270,8 @@ while (k < 2 && !frame.isValid()) { if (abort_seek) { qDebug("VideoFrameExtractor abort seek before decoding key frames"); + err = "abort seek before decoding key frames"; + aborted = true; return false; } //qWarning("invalid key frame!!!!! undecoded: %d", decoder->undecodedSize()); @@ -268,6 +295,8 @@ while (!demuxer.atEnd()) { if (abort_seek) { qDebug("VideoFrameExtractor abort seek after key frame before read"); + err = "abort seek after key frame before read"; + aborted = true; return false; } if (!demuxer.readFrame()) @@ -298,6 +327,7 @@ if (!decoder->decode(pkt)) { qWarning("!!!!!!!!!decode failed!!!!"); frame = VideoFrame(); + err = "decode failed"; return false; } // store the last decoded frame because next frame may be out of range @@ -320,16 +350,18 @@ if (diff > range && t > pts) { qWarning("out pts out of range. diff=%lld, range=%d", diff, range); frame = VideoFrame(); + err = QString().sprintf("out pts out of range. diff=%lld, range=%d", diff, range); return false; } } ++seek_count; // now we get the final frame if (demuxer.atEnd()) - releaseResourceInternal(); + releaseResourceInternal(false); return true; } - void releaseResourceInternal() { + void releaseResourceInternal(bool releaseFrame = true) { + if (releaseFrame) frame = VideoFrame(); seek_count = 0; // close codec context first. decoder.reset(0); @@ -348,7 +380,6 @@ thread.addTask(new Cleaner(this)); } - bool extracted; volatile bool abort_seek; bool async; bool has_video; @@ -356,12 +387,12 @@ bool auto_extract; bool auto_precision; int seek_count; - qint64 position; - int precision; - QString source; + qint64 position; ///< only read/written by this->thread(), never read by extractor thread so no lock necessary + volatile int precision; ///< is volatile because may be written by this->thread() and read by extractor thread but typical use is to not modify it while extract is running + QString source; ///< is written-to by this->thread() but may be read by extractor thread; important: apparently two threads accessing QString is supported by Qt according to wang-bin, so we aren't guarding this with a lock AVDemuxer demuxer; QScopedPointer decoder; - VideoFrame frame; + VideoFrame frame; ///< important: we only allow the extract thread to modify this value QStringList codecs; ExtractThread thread; static QVariantHash dec_opt_framedrop, dec_opt_normal; @@ -374,9 +405,7 @@ QObject(parent) { DPTR_D(VideoFrameExtractor); - moveToThread(&d.thread); d.thread.start(); - connect(this, SIGNAL(aboutToExtract(qint64)), SLOT(extractInternal(qint64))); } void VideoFrameExtractor::setSource(const QString url) @@ -387,7 +416,6 @@ d.source = url; d.has_video = true; Q_EMIT sourceChanged(); - d.frame = VideoFrame(); d.safeReleaseResource(); } @@ -432,8 +460,6 @@ if (qAbs(value - d.position) < precision()) { return; } - d.frame = VideoFrame(); - d.extracted = false; d.position = value; Q_EMIT positionChanged(); if (!autoExtract()) @@ -454,8 +480,9 @@ d.auto_precision = value < 0; // explain why value (p0) is used but not the actual decoded position (p) // it's key frame finding rule - if (value >= 0) + if (value >= 0) { d.precision = value; + } Q_EMIT precisionChanged(); } @@ -464,15 +491,6 @@ return d_func().precision; } -bool VideoFrameExtractor::event(QEvent *e) -{ - //qDebug("event: %d", e->type()); - if (e->type() != QEvent::User) - return QObject::event(e); - extractInternal(position()); // FIXME: wrong position - return true; -} - void VideoFrameExtractor::extract() { DPTR_D(VideoFrameExtractor); @@ -480,13 +498,6 @@ extractInternal(position()); return; } -#if ASYNC_SIGNAL - else { - Q_EMIT aboutToExtract(position()); - return; - } -#endif -#if ASYNC_TASK class ExtractTask : public QRunnable { public: ExtractTask(VideoFrameExtractor *e, qint64 t) @@ -500,13 +511,14 @@ VideoFrameExtractor *extractor; qint64 position; }; + // We want to abort the previous extract() since we are + // only interested in the latest request. + // So, abort_seek is a 'previous frame extract abort mechanism' + // -- this flag is repeatedly checked by extractInPrecision() + // (called by extractInternal()) and if true, method returns early. + // Note if seek/decode is aborted, aborted() signal will be emitted. d.abort_seek = true; d.thread.addTask(new ExtractTask(this, position())); - return; -#endif -#if ASYNC_EVENT - qApp->postEvent(this, new QEvent(QEvent::User)); -#endif //ASYNC_EVENT } void VideoFrameExtractor::extractInternal(qint64 pos) @@ -514,16 +526,21 @@ DPTR_D(VideoFrameExtractor); int precision_old = precision(); if (!d.checkAndOpen()) { - Q_EMIT error(); + Q_EMIT error("Cannot open file"); //qWarning("can not open decoder...."); - return; // error handling + return; } if (precision_old != precision()) { Q_EMIT precisionChanged(); } - d.extracted = d.extractInPrecision(pos, precision()); - if (!d.extracted) { - Q_EMIT error(); + bool extractOk = false, isAborted = true; + QString err; + extractOk = d.extractInPrecision(pos, precision(), err, isAborted); + if (!extractOk) { + if (isAborted) + Q_EMIT aborted(QString().sprintf("Abort at position %lld: %s",pos,err.toLatin1().constData())); + else + Q_EMIT error(QString().sprintf("Cannot extract frame at position %lld: %s",pos,err.toLatin1().constData())); return; } Q_EMIT frameExtracted(d.frame); diff -Nru qtav-1.12.0+ds/src/VideoThread.cpp qtav-1.13.0+ds/src/VideoThread.cpp --- qtav-1.12.0+ds/src/VideoThread.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/src/VideoThread.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV @@ -243,7 +243,7 @@ DPTR_D(VideoThread); if (!d.dec || !d.dec->isAvailable() || !d.outputSet) return; - resetState(); + // resetState(); // we can't reset the thread state from here if (d.capture->autoSave()) { d.capture->setCaptureName(QFileInfo(d.statistics->url).completeBaseName()); } @@ -320,7 +320,7 @@ if (!pkt.isValid()) { // may be we should check other information. invalid packet can come from wait_key_frame = true; - qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size()); + qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size()); d.dec->flush(); //d.dec instead of dec because d.dec maybe changed in processNextTask() but dec is not d.render_pts0 = pkt.pts; sync_id = pkt.position; @@ -488,7 +488,7 @@ dec->setOptions(*dec_opt); if (!dec->decode(pkt)) { d.pts_history.push_back(d.pts_history.back()); - qWarning("Decode video failed. undecoded: %d/%d", dec->undecodedSize(), pkt.data.size()); + //qWarning("Decode video failed. undecoded: %d/%d", dec->undecodedSize(), pkt.data.size()); if (pkt.isEOF()) { Q_EMIT eofDecoded(); qDebug("video decode eof done. d.render_pts0: %.3f", d.render_pts0); @@ -523,7 +523,7 @@ continue; } pkt_data = pkt.data.constData(); - if (frame.timestamp() <= 0) + if (frame.timestamp() < 0) frame.setTimestamp(pkt.pts); // pkt.pts is wrong. >= real timestamp const qreal pts = frame.timestamp(); d.pts_history.push_back(pts); diff -Nru qtav-1.12.0+ds/widgets/CMakeLists.txt qtav-1.13.0+ds/widgets/CMakeLists.txt --- qtav-1.12.0+ds/widgets/CMakeLists.txt 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/CMakeLists.txt 2019-07-11 00:58:59.000000000 +0000 @@ -43,7 +43,7 @@ set(HEADERS ${SDK_HEADERS}) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) - configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) + configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() # add HEADERS for moc add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) diff -Nru qtav-1.12.0+ds/widgets/global.cpp qtav-1.13.0+ds/widgets/global.cpp --- qtav-1.12.0+ds/widgets/global.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/global.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "QtAVWidgets/WidgetRenderer.h" diff -Nru qtav-1.12.0+ds/widgets/GraphicsItemRenderer.cpp qtav-1.13.0+ds/widgets/GraphicsItemRenderer.cpp --- qtav-1.12.0+ds/widgets/GraphicsItemRenderer.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/GraphicsItemRenderer.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg - Copyright (C) 2012-2016 Wang Bin + Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV @@ -22,7 +22,11 @@ #include "QtAVWidgets/GraphicsItemRenderer.h" #include "QtAV/private/QPainterRenderer_p.h" #include "QtAV/FilterContext.h" -#define QTAV_HAVE_OPENGL (!defined QT_NO_OPENGL && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) || defined(QT_OPENGL_LIB))) +#if !defined QT_NO_OPENGL && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) || defined(QT_OPENGL_LIB)) +#define QTAV_HAVE_OPENGL 1 +#else +#define QTAV_HAVE_OPENGL 0 +#endif #if QTAV_HAVE(OPENGL) #include "QtAV/OpenGLVideo.h" #else @@ -34,6 +38,7 @@ #include #include #include +#include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif @@ -51,8 +56,8 @@ void setupAspectRatio() { matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); - if (orientation) - matrix.rotate(orientation, 0, 0, 1); // Z axis + if (rotation()) + matrix.rotate(rotation(), 0, 0, 1); // Z axis } // return true if opengl is enabled and context is ready. may called by non-rendering thread bool checkGL() { @@ -126,7 +131,9 @@ { preparePixmap(frame); } - scene()->update(sceneBoundingRect()); //TODO: thread? + // moved to event + // scene()->update(sceneBoundingRect()); //TODO: thread? + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); //update(); //does not cause an immediate paint. my not redraw. return true; } @@ -173,7 +180,16 @@ } else { qWarning("FilterContext not available!"); } + // save painter state, switch to native opengl painting + painter->save(); + painter->beginNativePainting(); + handlePaintEvent(); + + // end native painting, restore state + painter->endNativePainting(); + painter->restore(); + d.painter = 0; //painter may be not available outside this function if (ctx) ctx->painter = 0; @@ -290,15 +306,27 @@ #if CONFIG_GRAPHICSWIDGET bool GraphicsItemRenderer::event(QEvent *event) { - setFocus(); //WHY: Force focus - QEvent::Type type = event->type(); - qDebug("GraphicsItemRenderer event type = %d", type); - if (type == QEvent::KeyPress) { - qDebug("KeyPress Event. key=%d", static_cast(event)->key()); + if (e->type() == QEvent::User) { + scene()->update(sceneBoundingRect()); } + else { + setFocus(); //WHY: Force focus + QEvent::Type type = event->type(); + qDebug("GraphicsItemRenderer event type = %d", type); + if (type == QEvent::KeyPress) { + qDebug("KeyPress Event. key=%d", static_cast(event)->key()); + } + } return true; } #else +bool GraphicsItemRenderer::event(QEvent *event) +{ + if (event->type() != QEvent::User) + return GraphicsWidget::event(event); + scene()->update(sceneBoundingRect()); + return true; +} /*simply passes event to QGraphicsWidget::event(). you should not have to *reimplement sceneEvent() in a subclass of QGraphicsWidget. */ diff -Nru qtav-1.12.0+ds/widgets/libQtAVWidgets.pro qtav-1.13.0+ds/widgets/libQtAVWidgets.pro --- qtav-1.12.0+ds/widgets/libQtAVWidgets.pro 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/libQtAVWidgets.pro 2019-07-11 00:58:59.000000000 +0000 @@ -24,9 +24,9 @@ !rc_file { RC_ICONS = $$PROJECTROOT/src/QtAV.ico - QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" + QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAVWidgets module. QtAV Multimedia framework. http://qtav.org" - QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" + QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV Widgets" } else:win32 { RC_FILE = QtAVWidgets.rc diff -Nru qtav-1.12.0+ds/widgets/QtAVWidgets/GraphicsItemRenderer.h qtav-1.13.0+ds/widgets/QtAVWidgets/GraphicsItemRenderer.h --- qtav-1.12.0+ds/widgets/QtAVWidgets/GraphicsItemRenderer.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/QtAVWidgets/GraphicsItemRenderer.h 2019-07-11 00:58:59.000000000 +0000 @@ -104,6 +104,7 @@ #if CONFIG_GRAPHICSWIDGET bool event(QEvent *event) Q_DECL_OVERRIDE; #else + bool event(QEvent *event) Q_DECL_OVERRIDE; //bool sceneEvent(QEvent *event) Q_DECL_OVERRIDE; #endif //CONFIG_GRAPHICSWIDGET diff -Nru qtav-1.12.0+ds/widgets/QtAVWidgets/VideoPreviewWidget.h qtav-1.13.0+ds/widgets/QtAVWidgets/VideoPreviewWidget.h --- qtav-1.12.0+ds/widgets/QtAVWidgets/VideoPreviewWidget.h 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/QtAVWidgets/VideoPreviewWidget.h 2019-07-11 00:58:59.000000000 +0000 @@ -45,19 +45,35 @@ void setFile(const QString& file); QString file() const; // default is false - void setKeepAspectRatio(bool value = true); - bool isKeepAspectRatio() const; + void setKeepAspectRatio(bool value = true) { m_keep_ar = value; } + bool isKeepAspectRatio() const { return m_keep_ar; } + /// AutoDisplayFrame -- default is true. if true, new frames from underlying extractor will update display widget automatically. + bool isAutoDisplayFrame() const { return m_auto_display; } + /// If false, new frames (or frame errors) won't automatically update widget + /// (caller must ensure to call displayFrame()/displayFrame(frame) for this if false). + /// set to false only if you want to do your own frame caching magic with preview frames. + void setAutoDisplayFrame(bool b=true); +public Q_SLOTS: // these were previously private but made public to allow calling code to cache some preview frames and directly display frames to this class + void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame + void displayNoFrame(); Q_SIGNALS: void timestampChanged(); void fileChanged(); + /// emitted on real decode error -- in that case displayNoFrame() will be automatically called + void gotError(const QString &); + /// usually emitted when a new request for a frame came in and current request was aborted. displayNoFrame() will be automatically called + void gotAbort(const QString &); + /// useful if calling code is interested in keeping stats on good versus bad frame counts, + /// or if it wants to cache preview frames. Keeping counts helps caller decide if + /// preview is working reliably or not for the designated file. + /// parameter frame will always have: frame.isValid() == true, and will be + /// already-scaled and in the right format to fit in the preview widget + void gotFrame(const QtAV::VideoFrame & frame); protected: virtual void resizeEvent(QResizeEvent *); -private Q_SLOTS: - void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame - void displayNoFrame(); private: - bool m_keep_ar; + bool m_keep_ar, m_auto_display; QString m_file; VideoFrameExtractor *m_extractor; VideoOutput *m_out; diff -Nru qtav-1.12.0+ds/widgets/VideoPreviewWidget.cpp qtav-1.13.0+ds/widgets/VideoPreviewWidget.cpp --- qtav-1.12.0+ds/widgets/VideoPreviewWidget.cpp 2017-06-21 01:47:15.000000000 +0000 +++ qtav-1.13.0+ds/widgets/VideoPreviewWidget.cpp 2019-07-11 00:58:59.000000000 +0000 @@ -29,6 +29,7 @@ VideoPreviewWidget::VideoPreviewWidget(QWidget *parent) : QWidget(parent) , m_keep_ar(false) + , m_auto_display(false) // set to false initially to trigger connections in setAutoDisplayFrame() below -- will default to true , m_extractor(new VideoFrameExtractor(this)) , m_out(new VideoOutput(VideoRendererId_Widget, this)) // FIXME: opengl may crash, so use software renderer here @@ -37,10 +38,27 @@ Q_ASSERT_X(m_out->widget(), "VideoPreviewWidget()", "widget based renderer is not found"); m_out->widget()->setParent(this); connect(m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged())); - connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame))); - connect(m_extractor, SIGNAL(error()), SLOT(displayNoFrame())); - connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame())); + connect(m_extractor, SIGNAL(error(const QString &)), this, SIGNAL(gotError(const QString &))); + connect(m_extractor, SIGNAL(aborted(const QString &)), this, SIGNAL(gotAbort(const QString &))); m_extractor->setAutoExtract(false); + m_auto_display = false; + setAutoDisplayFrame(true); // set up frame-related connections, defaulting autoDisplayFrame to true +} + +void VideoPreviewWidget::setAutoDisplayFrame(bool b) +{ + if (!!b == !!m_auto_display) return; // avoid reduntant connect/disconnect calls + if (b) { + connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame))); + connect(m_extractor, SIGNAL(error(const QString &)), SLOT(displayNoFrame())); + connect(m_extractor, SIGNAL(aborted(const QString &)), SLOT(displayNoFrame())); + connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame())); + } else { + disconnect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), this, SLOT(displayFrame(QtAV::VideoFrame))); + disconnect(m_extractor, SIGNAL(error(const QString &)), this, SLOT(displayNoFrame())); + disconnect(m_extractor, SIGNAL(aborted(const QString &)), this, SLOT(displayNoFrame())); + disconnect(this, SIGNAL(fileChanged()), this, SLOT(displayNoFrame())); + } } void VideoPreviewWidget::resizeEvent(QResizeEvent *e) @@ -83,8 +101,8 @@ if (diff > m_extractor->precision()) { //qWarning("timestamp difference (%d/%lld) is too large! ignore", diff); } - if (m_out->isSupported(frame.format().pixelFormat())) { - m_out->receive(frame); + if (!frame.isValid()) { + displayNoFrame(); return; } QSize s = m_out->widget()->rect().size(); @@ -93,10 +111,17 @@ fs.scale(s, Qt::KeepAspectRatio); s = fs; } - VideoFrame f(frame.to(VideoFormat::Format_RGB32, s)); - if (!f.isValid()) + // make sure frame is the same size as the output widget size, and of the desired format + // if not, convert & scale + VideoFrame f(frame.pixelFormat() == m_out->preferredPixelFormat() && frame.size() == s + ? frame + : frame.to(m_out->preferredPixelFormat(), s) + ); + if (!f.isValid()) { return; + } m_out->receive(f); + Q_EMIT gotFrame(f); } void VideoPreviewWidget::displayNoFrame()