Codebase list qtav / upstream/1.13.0+ds
New upstream version 1.13.0+ds Steven Robbins 4 years ago
168 changed file(s) with 5386 addition(s) and 614 deletion(s). Raw diff Collapse all Expand all
0 ### QtAV, Qt version and platform
1
2 or commit id if not using release version
3
4 ### Reproduction steps
5
6 ### Expected behavior
7
8 ### Actual behavior
9
10 ### Log file
11
12 set environment var `QTAV_LOG=all` or C++ api `QtAV::setLogLevel(All)` to enable log.
13
14 For Player and QMLPlayer example app, choose log level `all` in config page.
15
16 ### Sample files (optional)
00 CVS
11 .#*
2
3 *.patch
4 *.diff
5 *.orig
6 #*.rej
7 contrib/include
8 *~
9 *.mak
210
311 # Hidden files are ignored by default with exceptions
412 .*
513 !/.gitignore
14 !/.github
615 !/.gitmodules
716 !/.qmake.conf
817 !/.travis.yml
6069 *.Debug
6170 *.Release
6271
72 # Ignore temporary Python files
73 python/PyQtAV.api
74 python/PyQtAV.pro
75 python/QtAV.pyi
76 python/QtAVWidgets.pyi
77 python/QtAV
78 python/QtAVWidgets
79
6380 *.fuse*
6481
6582 #qt
66 [submodule "contrib/uchardet"]
77 path = contrib/uchardet
88 url = https://github.com/BYVoid/uchardet.git
9 [submodule "src/jmi"]
10 path = src/jmi
11 url = https://github.com/wang-bin/JMI.git
00 QTAV_MAJOR_VERSION = 1
1 QTAV_MINOR_VERSION = 12
1 QTAV_MINOR_VERSION = 13
22 QTAV_PATCH_VERSION = 0
33
44 QTAV_VERSION = $${QTAV_MAJOR_VERSION}.$${QTAV_MINOR_VERSION}.$${QTAV_PATCH_VERSION}
00 cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
1
2 project(QTAV)
3
14 set(QTAV_MAJOR 1)
2 set(QTAV_MINOR 11)
5 set(QTAV_MINOR 13)
36 set(QTAV_PATCH 0)
47 set(PROJECT_VERSION ${QTAV_MAJOR}.${QTAV_MINOR}.${QTAV_PATCH})
58 set(SO_VERSION ${QTAV_MAJOR})
912
1013 if(POLICY CMP0063) # visibility. since 3.3
1114 cmake_policy(SET CMP0063 NEW)
15 endif()
16 if(POLICY CMP0071)
17 cmake_policy(SET CMP0071 NEW)
1218 endif()
1319 set(CMAKE_CXX_VISIBILITY_PRESET hidden) #use with -fdata-sections -ffunction-sections to reduce target size
1420 set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
0 version 1.13.0 2019-07-11
1
2 - add python bindings
3 - more apis for qml player
4 - auto rotate video
5 - apple store
6 - fix ios plugin not found
7 - support chapters
8 - muxer, encoder, transcoder improvements
9 - compatible with new ffmpeg
10 - videotoolbox: hevc,
11 - cuda: new devices
12 - android: no longer depends on private qt module
13 - fix opensl error
14 - mediacodec: 0-copy via a plugin from https://github.com/wang-bin/mdk-sdk
15
16
017 version 1.12.0 2017-06-20
118
219 Changelog
3232 - Multiple video outputs for 1 player
3333 - Video eq(software and OpenGL): brightness, contrast, saturation, hue
3434 - QML support. Most playback APIs are compatible with QtMultimedia module
35 - 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.
35 - 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.
3636
3737
3838 ### Extensible Framework
154154
155155 > Copyright © Wang Bin wbsecg1@gmail.com
156156
157 > Shanghai University->S3 Graphics->Deepin, Shanghai, China
158
159157 > 2013-01-21
77 environment:
88
99 matrix:
10 - arch: x64
11 qt: 5.9
12 cc: VS2017
13 mode: release
14 QTDIR: C:\Qt\5.9\msvc2017_64
15 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
16
1017 - arch: x86
1118 cc: VS2015
1219 qt: 5.9
5158 init:
5259 - set vcarch=%arch%
5360 - if "%arch%" == "x64" set vcarch=amd64
54 - if not %cc%==MinGW call "C:\Program Files (x86)\Microsoft Visual Studio %toolchain_version%.0\VC\vcvarsall.bat" %vcarch%
61 - if %cc%==VS2017 (
62 call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %vcarch%
63 ) else if not %cc%==MinGW (
64 call "C:\Program Files (x86)\Microsoft Visual Studio %toolchain_version%.0\VC\vcvarsall.bat" %vcarch%
65 )
5566 - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS%
5667 - echo PROCESSOR_IDENTIFIER=%PROCESSOR_IDENTIFIER%
5768 - echo QTDIR=%QTDIR%
89100 active_mode: false
90101 on:
91102 mode: release
92 cc: VS2015
103 cc: VS2017
1818 BEGIN
1919 BLOCK "000004b0"
2020 BEGIN
21 VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin->PPTV | wbsecg1@gmail.com"
21 VALUE "CompanyName", "wbsecg1@gmail.com"
2222 VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org"
2323 VALUE "FileVersion", QTAV_VERSION_STR ".0"
24 VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
24 VALUE "LegalCopyright", "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
2525 VALUE "InternalName", "@MODULE@"
2626 VALUE "ProductName", "@MODULE@"
2727 VALUE "ProductVersion", QTAV_VERSION_STR ".0"
2727 } else:win* {
2828 QMAKE_EXTENSION_SHLIB = dll
2929 }
30 }
31
32 CONFIG += profile
33 #profiling, -pg is not supported for msvc
34 debug:!ios:!android:!*msvc*:profile {
35 QMAKE_CXXFLAGS_DEBUG += -pg
36 QMAKE_LFLAGS_DEBUG += -pg
37 QMAKE_CXXFLAGS_DEBUG = $$unique(QMAKE_CXXFLAGS_DEBUG)
38 QMAKE_LFLAGS_DEBUG = $$unique(QMAKE_LFLAGS_DEBUG)
3930 }
4031
4132 #$$[TARGET_PLATFORM]
00 /******************************************************************************
11 QtAV: Media play library based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
1717 License along with this library; if not, write to the Free Software
1818 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1919 ******************************************************************************/
20 #ifndef __APPLE__
21 #error apple only
22 #endif
2023 extern "C" {
2124 #include <libavcodec/videotoolbox.h>
2225 }
2326 int main()
2427 {
25 av_videotoolbox_alloc_context();
28 //av_videotoolbox_alloc_context();
2629 return 0;
2730 }
100100 set(MODULE QMLPlayer)
101101 if(WIN32)
102102 set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc)
103 configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
103 configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
104104 endif()
105105 add_executable(QMLPlayer ${EXE_TYPE}
106106 QMLPlayer/main.cpp
119119 set(MODULE Player)
120120 if(WIN32)
121121 set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc)
122 configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
122 configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
123123 endif()
124124 add_executable(Player ${EXE_TYPE} ${PLAYER_SRC} ${PLAYER_HEADERS} ${PLAYER_RES} ${RC_FILE})
125125 target_link_libraries(Player QtAVWidgets common)
5555 RC_ICONS = $$PROJECTROOT/src/QtAV.ico
5656 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
5757 QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia playback framework. http://qtav.org"
58 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
58 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
5959 QMAKE_TARGET_PRODUCT = "QtAV $$1"
6060 export(RC_ICONS)
6161 export(QMAKE_TARGET_COMPANY)
00 <?xml version="1.0"?>
1 <manifest package="org.qtav.qmlplayer" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.12.0" android:versionCode="7" android:installLocation="auto">
1 <manifest package="org.qtav.qmlplayer" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.13.0" android:versionCode="16" android:installLocation="auto">
22 <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="QMLPlayer" android:icon="@drawable/icon">
33 <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtav.qmlplayer.QMLPlayerActivity" android:label="QtAV QMLPlayer" android:screenOrientation="unspecified" android:launchMode="singleTop">
44 <intent-filter>
4747 <!-- Background running -->
4848 </activity>
4949 </application>
50 <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="19"/>
50 <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
5151 <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
5252
5353 <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
6161 <!-- %%INSERT_FEATURES -->
6262 <uses-permission android:name="android.permission.INTERNET"/>
6363 <uses-permission android:name="android.permission.WAKE_LOCK"/>
64 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
65 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
6466 </manifest>
33 import android.content.Intent;
44 import android.app.PendingIntent;
55 import android.util.Log;
6 import android.os.Build;
67 import android.os.Bundle;
78 import android.view.WindowManager;
89 import android.content.pm.ActivityInfo;
10 import android.content.pm.PackageManager;
11 import android.support.v4.content.ContextCompat;
12 import android.support.v4.app.ActivityCompat;
13 import android.widget.Toast;
914
1015 public class QMLPlayerActivity extends QtActivity
1116 {
2227 @Override
2328 public void onCreate(Bundle savedInstanceState) {
2429 super.onCreate(savedInstanceState);
30 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
31 if (!checkPermission())
32 requestPermission();
33 }
2534 //getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
2635 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2736 Intent intent = getIntent();
3443 }
3544 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
3645 }
46
47 protected boolean checkPermission() {
48 int result = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
49 if (result == PackageManager.PERMISSION_GRANTED)
50 return true;
51 return false;
52 }
53 protected void requestPermission() {
54 if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
55 Toast.makeText(this, "Please allow Write External Storage permission to play your local videos", Toast.LENGTH_LONG).show();
56 } else {
57 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
58 requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
59 }
60 }
3761 }
1818 <key>CFBundleName</key>
1919 <string>${PRODUCT_NAME}</string>
2020 <key>CFBundleShortVersionString</key>
21 <string>1.12</string>
21 <string>1.13</string>
2222 <key>CFBundleVersion</key>
23 <string>1.12.0</string>
23 <string>1.13.0</string>
2424 <key>LSRequiresIPhoneOS</key>
2525 <true/>
2626 <key>UIFileSharingEnabled</key>
4040 options.add(QLatin1String("QMLPlayer options"))
4141 ("scale", 1.0, QLatin1String("scale of graphics context. 0: auto"))
4242 ;
43 qputenv("QTAV_MEDIACODEC_KEY", ")A0aZ!WIgo2DdCsc#EnR?NAsFtWrnENONeeiiED^OM@6gI+Hew6s5)77p^>$K(Fe");
4344 options.parse(argc, argv);
4445 Config::setName(QString::fromLatin1("QMLPlayer"));
4546 do_common_options_before_qapp(options);
158159 if (player && !file.isEmpty()) {
159160 if (!file.startsWith(QLatin1String("file:")) && QFile(file).exists())
160161 file.prepend(QLatin1String("file:")); //qml use url and will add qrc: if no scheme
162 #ifdef Q_OS_WIN
161163 file.replace(QLatin1String("\\"), QLatin1String("/")); //qurl
164 #endif
162165 //QMetaObject::invokeMethod(player, "play", Q_ARG(QUrl, QUrl(file)));
163 player->setProperty("source", QUrl(file));
166 QUrl url;
167 player->setProperty("source", QUrl(file.toUtf8().toPercentEncoding()));
164168 }
165169 #endif
166170 QObject::connect(&Config::instance(), SIGNAL(changed()), &Config::instance(), SLOT(save()));
1818 font.pixelSize: Utils.scaled(15)
1919 onContentHeightChanged: parent.contentHeight = contentHeight + 2*anchors.margins
2020 onLinkActivated: Qt.openUrlExternally(link)
21 text: "<img src='qrc:/QtAV.svg'><h3>QMLPlayer for " + Qt.platform.os + " " + qsTr("based on") + " QtAV 1.11.0</h3>"
21 text: "<img src='qrc:/QtAV.svg'><h3>QMLPlayer for " + Qt.platform.os + " " + qsTr("based on") + " QtAV 1.12.0</h3>"
2222 + "<p>" + qsTr("QtAV is a cross platform, high performance multimedia framework") + "</p>"
2323 + "<p>Distributed under the terms of LGPLv2.1 or later.</p>"
24 + "<p>Copyright (C) 2012-2016 Wang Bin (aka. Lucas Wang) <a href='mailto:wbsecg1@gmail.com'>wbsecg1@gmail.com</a></p><p>"
24 + "<p>Copyright (C) 2012-2017 Wang Bin (aka. Lucas Wang) <a href='mailto:wbsecg1@gmail.com'>wbsecg1@gmail.com</a></p><p>"
2525 + qsTr("Home page") + ": <a href='http://qtav.org'>http://qtav.org</a></p>"
2626 + "<p><a href='http://qtav.org/install.html'>"+qsTr("Install QtAV for Windows desktop/store, macOS, Linux and Android")+"</a></p>"
2727 + "\n<p>" + qsTr("Double click") + ": " + qsTr("show/hide control bar") + "</p><p>"
55 title: qsTr("Video Codec")
66 height: titleHeight + detail.height + listView.height + copyMode.height + Utils.kSpacing*4
77 signal zeroCopyChanged(bool value)
8 property var defaultDecoders: []
89
910 QtObject {
1011 id: d
4849
4950 ListModel {
5051 id: codecMode
52 ListElement { name: "Auto"; hardware: true; zcopy: true; description: qsTr("Try hardware decoders first") }
5153 ListElement { name: "FFmpeg"; hardware: false; zcopy: false; description: "FFmpeg/Libav" }
5254 }
5355
7173 onStateChanged: {
7274 if (state != "selected")
7375 return
76 if (name === "Auto") {
77 PlayerConfig.decoderPriorityNames = defaultDecoders
78 d.detail = description
79 return
80 }
7481 d.detail = description + " " + (hardware ? qsTr("hardware decoding") : qsTr("software decoding"))
7582 if (name === "FFmpeg") {
7683 copyMode.visible = false
94101 }
95102 Component.onCompleted: {
96103 if (Qt.platform.os == "windows") {
104 defaultDecoders.push("DXVA")
105 defaultDecoders.push("D3D11")
106 defaultDecoders.push("CUDA")
97107 codecMode.append({ name: "DXVA", hardware: true, zcopy: true, description: "DirectX Video Acceleration (Windows)\nUse OpenGLES(ANGLE) + D3D to support 0-copy" })
98108 codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration\n0-copy is supported under OpenGLES(ANGLE)" })
99109 codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux).\nH264 10bit support."})
100110 } else if (Qt.platform.os == "winrt" || Qt.platform.os == "winphone") {
111 defaultDecoders.push("D3D11")
101112 codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration" })
102113 } else if (Qt.platform.os == "osx") {
114 defaultDecoders.push("VideoToolbox")
115 defaultDecoders.push("VDA")
116 codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (OSX)" })
103117 codecMode.append({ name: "VDA", hardware: true, zcopy: true, description: "VDA (OSX)" })
104 codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (OSX)" })
105118 } else if (Qt.platform.os == "ios") {
119 defaultDecoders.append("VideoToolbox")
106120 codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (iOS)" })
107121 } else if(Qt.platform.os == "android") {
108 codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android 5.0 MediaCodec (H.264)" })
122 defaultDecoders.push("MediaCodec")
123 codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android(>=4.1) MediaCodec (H.264)" })
109124 } else if (Qt.platform.os == "linux") {
125 defaultDecoders.push("VAAPI")
126 defaultDecoders.push("CUDA")
110127 codecMode.append({ name: "VAAPI", hardware: true, zcopy: true, description: "VA-API (Linux) " })
111128 codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux)"})
112129 }
113 for (var i = 0; i < codecMode.count; ++i) {
130 defaultDecoders.push("FFmpeg")
131 if (PlayerConfig.decoderPriorityNames.length > 1) {
132 listView.currentIndex = 0;
133 d.selectedItem = listView.currentItem
134 listView.currentItem.state = "selected"
135 return
136 }
137 for (var i = 1; i < codecMode.count; ++i) {
114138 if (codecMode.get(i).name === PlayerConfig.decoderPriorityNames[0]) {
115139 listView.currentIndex = i;
116140 d.selectedItem = listView.currentItem
33 Name=\"org.qtav.qmlplayer\"
44 ProcessorArchitecture=\"$$lower($$VCPROJ_ARCH)\"
55 Publisher=\"CN=930FB10A-C50C-4801-84BA-255229D27D44\"
6 Version=\"1.12.0.0\" />
6 Version=\"1.13.0.0\" />
77 <mp:PhoneIdentity
88 PhoneProductId=\"e556656c-d8e8-49f9-8148-26506e94575a\"
99 PhonePublisherId=\"00000000-0000-0000-0000-000000000000\" />
99 <Identity
1010 Name=\"org.qtav.qmlplayer\"
1111 Publisher=\"CN=930FB10A-C50C-4801-84BA-255229D27D44\"
12 Version=\"1.12.0.0\"
12 Version=\"1.13.0.0\"
1313 ProcessorArchitecture=\"$$lower($$VCPROJ_ARCH)\" /> <!--replaced by qmake-->
1414 <mp:PhoneIdentity
1515 PhoneProductId=\"e556656c-d8e8-49f9-8148-26506e94575a\"
33 Name=\"org.qtav.qmlplayer\"
44 ProcessorArchitecture=\"$$lower($$VCPROJ_ARCH)\"
55 Publisher=\"CN=930FB10A-C50C-4801-84BA-255229D27D44\"
6 Version=\"1.12.0.0\" />
6 Version=\"1.13.0.0\" />
77 <Properties>
88 <DisplayName>QtAV Video Player</DisplayName>
99 <PublisherDisplayName>Lucas Wang</PublisherDisplayName>
00 /******************************************************************************
11 QtAV Player Demo: this file is part of QtAV examples
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
303303 setForceFrameRate(settings.value(QString::fromLatin1("force_fps"), 0.0).toReal());
304304 settings.beginGroup(QString::fromLatin1("decoder"));
305305 settings.beginGroup(QString::fromLatin1("video"));
306 QString decs_default(QString::fromLatin1("FFmpeg"));
306 QString decs_default(QString::fromLatin1("HW FFmpeg")); // HW is ignored
307307 setDecoderPriorityNames(settings.value(QString::fromLatin1("priority"), decs_default).toString().split(QString::fromLatin1(" "), QString::SkipEmptyParts));
308308 setZeroCopy(settings.value(QString::fromLatin1("zeroCopy"), true).toBool());
309309 settings.endGroup(); //video
2020 static QLibrary xlib;
2121 #endif //Q_OS_LINUX
2222 #if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
23 #include <Availability.h>
24 # if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
2325 //http://www.cocoachina.com/macdev/cocoa/2010/0201/453.html
2426 #include <CoreServices/CoreServices.h>
27 # else
28 /* MAC OSX 10.8+ has deprecated the UpdateSystemActivity stuff in favor of a new API.. */
29 # include <IOKit/pwr_mgt/IOPMLib.h>
30 # endif
2531 #endif //Q_OS_MAC
2632 #ifdef Q_OS_WIN
2733 #include <QAbstractEventDispatcher>
148154 }
149155 #endif //Q_OS_LINUX
150156 ssTimerId = 0;
157 osxIOPMAssertionId = 0U; // mac >=10.8 only
151158 retrieveState();
152159 }
153160
157164 #ifdef Q_OS_LINUX
158165 if (xlib.isLoaded())
159166 xlib.unload();
167 #elif defined(Q_OS_MAC) && !defined(Q_OS_IOS)
168 # if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
169 if (osxIOPMAssertionId) {
170 IOPMAssertionRelease((IOPMAssertionID)osxIOPMAssertionId);
171 osxIOPMAssertionId = 0U;
172 }
173 # endif
160174 #endif
161175 }
162176
309323 if (e->timerId() != ssTimerId)
310324 return;
311325 #if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
326 # if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
312327 UpdateSystemActivity(OverallAct);
328 # else // OSX >= 10.8, use new API
329 IOPMAssertionID assertionId = osxIOPMAssertionId;
330 IOReturn r = IOPMAssertionDeclareUserActivity(CFSTR("QtAVScreenSaver"),kIOPMUserActiveLocal,&assertionId);
331 if (r == kIOReturnSuccess) {
332 osxIOPMAssertionId = assertionId;
333 }
334 # endif
313335 return;
314336 #endif //Q_OS_MAC
315337 #ifdef Q_OS_LINUX
3232 int allowExposures;
3333 #endif //Q_OS_LINUX
3434 int ssTimerId; //for mac
35 quint32 osxIOPMAssertionId; // for mac OSX >= 10.8
3536 };
3637
3738 #endif // SCREENSAVER_H
3434 #include <QtCore/QStandardPaths>
3535 #endif
3636 #include <QtDebug>
37 #include <QMutex>
3738
3839 #ifdef Q_OS_WINRT
3940 #include <wrl.h>
9798 return qInstallMsgHandler(MsgHandlerWrapper::handler);
9899 }
99100 #endif
101
102 QMutex loggerMutex;
100103 void Logger(QtMsgType type, const QMessageLogContext &, const QString& qmsg)
101104 {
105 // QFile is not thread-safe
106 QMutexLocker locker(&loggerMutex);
107
102108 const QByteArray msgArray = qmsg.toUtf8();
103109 const char* msg = msgArray.constData();
104110 switch (type) {
296296 }
297297 }
298298 break;
299
300 case QEvent::MouseButtonDblClick: {
301 QMouseEvent *me = static_cast<QMouseEvent*>(event);
302 Qt::MouseButton mbt = me->button();
303 QWidget *mpWindow = static_cast<QWidget*>(player->parent());
304 if (mbt == Qt::LeftButton) {
305 if (Qt::WindowFullScreen ==mpWindow->windowState()){
306 mpWindow->setWindowState(mpWindow->windowState() ^ Qt::WindowFullScreen);
307 }else{
308 mpWindow->showFullScreen();
309 }
310 }
311 break;
312 }
313
299314 case QEvent::GraphicsSceneContextMenu: {
300315 QGraphicsSceneContextMenuEvent *e = static_cast<QGraphicsSceneContextMenuEvent*>(event);
301316 showMenu(e->screenPos());
345360 }
346361 return false;
347362 }
363
348364 if (event->type() == QEvent::MouseButtonPress) {
349365 QMouseEvent *me = static_cast<QMouseEvent*>(event);
350366 Qt::MouseButton mbt = me->button();
110110 , mpSubtitle(0)
111111 , m_preview(0)
112112 , m_shader(NULL)
113 , m_glsl(NULL)
113114 {
114115 #if defined(Q_OS_MACX) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
115116 QApplication::setStyle(QStyleFactory::create("Fusion"));
548549 connect(mpTimeSlider, SIGNAL(onLeave()), SLOT(onTimeSliderLeave()));
549550 connect(mpTimeSlider, SIGNAL(onHover(int,int)), SLOT(onTimeSliderHover(int,int)));
550551 connect(&Config::instance(), SIGNAL(userShaderEnabledChanged()), SLOT(onUserShaderChanged()));
552 connect(&Config::instance(), SIGNAL(intermediateFBOChanged()), SLOT(onUserShaderChanged()));
551553 connect(&Config::instance(), SIGNAL(fragHeaderChanged()), SLOT(onUserShaderChanged()));
552554 connect(&Config::instance(), SIGNAL(fragSampleChanged()), SLOT(onUserShaderChanged()));
553555 connect(&Config::instance(), SIGNAL(fragPostProcessChanged()), SLOT(onUserShaderChanged()));
13241326 m_preview->preview();
13251327 const int w = Config::instance().previewWidth();
13261328 const int h = Config::instance().previewHeight();
1327 m_preview->setWindowFlags(m_preview->windowFlags() |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
1329 m_preview->setWindowFlags(Qt::Tool |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
13281330 m_preview->resize(w, h);
13291331 m_preview->move(gpos - QPoint(w/2, h));
13301332 m_preview->show();
13321334
13331335 void MainWindow::onTimeSliderLeave()
13341336 {
1335 if (m_preview && m_preview->isVisible())
1336 m_preview->hide();
1337 /*if (m_preview && m_preview->isVisible())
1338 m_preview->hide();*/
1339 if (!m_preview)
1340 {
1341 return;
1342 }
1343 if (m_preview->isVisible())
1344 {
1345 m_preview->close();
1346 }
1347 delete m_preview;
1348 m_preview = NULL;
13371349 }
13381350
13391351 void MainWindow::handleError(const AVError &e)
15321544 return;
15331545 #ifndef QT_NO_OPENGL
15341546 if (Config::instance().userShaderEnabled()) {
1547 if (Config::instance().intermediateFBO()) {
1548 if (!m_glsl)
1549 m_glsl = new GLSLFilter(this);
1550 m_glsl->installTo(mpRenderer);
1551 } else {
1552 if (m_glsl)
1553 m_glsl->uninstall();
1554 }
15351555 if (!m_shader)
15361556 m_shader = new DynamicShaderObject(this);
15371557 m_shader->setHeader(Config::instance().fragHeader());
3838 class SubtitleFilter;
3939 class VideoPreviewWidget;
4040 class DynamicShaderObject;
41 class GLSLFilter;
4142 }
4243 QT_BEGIN_NAMESPACE
4344 class QMenu;
212213 QtAV::SubtitleFilter *mpSubtitle;
213214 QtAV::VideoPreviewWidget *m_preview;
214215 QtAV::DynamicShaderObject *m_shader;
216 QtAV::GLSLFilter *m_glsl;
215217 };
216218
217219
1818 <key>CFBundleName</key>
1919 <string>QtAV Player</string>
2020 <key>CFBundleShortVersionString</key>
21 <string>1.12</string>
21 <string>1.13</string>
2222 <key>CFBundleVersion</key>
23 <string>1.12.0</string>
23 <string>1.13.0</string>
2424 <key>LSRequiresIPhoneOS</key>
2525 <true/>
2626 <key>UIFileSharingEnabled</key>
3030 RC_ICONS = $$PROJECTROOT/src/QtAV.ico
3131 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
3232 QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org"
33 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
33 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
3434 QMAKE_TARGET_PRODUCT = "QtAV $$1"
3535 export(RC_ICONS)
3636 export(QMAKE_TARGET_COMPANY)
0 #include "VUMeterFilter.h"
1 #include <QtAV/AudioFrame.h>
2 #include <cmath>
3
4 VUMeterFilter::VUMeterFilter(QObject *parent)
5 : AudioFilter(parent)
6 , mLeft(0)
7 , mRight(0)
8 {
9 }
10
11 void VUMeterFilter::process(Statistics *statistics, AudioFrame *frame)
12 {
13 if (!frame)
14 return;
15 const AudioFormat& af = frame->format();
16 int step = frame->channelCount();
17 const quint8* data[2];
18 data[0] = frame->constBits(0);
19 if (frame->planeCount() > 0) {
20 step = 1;
21 data[1] = frame->constBits(1);
22 } else {
23 data[1] = data[0] + step*af.sampleSize();
24 }
25 const int s = frame->samplesPerChannel();
26 const int r = 1;
27 float level[2];
28 if (af.isFloat()) {
29 float max[2];
30 max[0] = max[1] = 0;
31 const float *p0 = (float*)data[0];
32 const float *p1 = (float*)data[1];
33 for (int i = 0; i < s; i+=step) {
34 max[0] = qMax(max[0], qAbs(p0[i]));
35 max[1] = qMax(max[1], qAbs(p1[i]));
36 }
37 level[0] = 20.0f * log10(max[0]);
38 level[1] = 20.0f * log10(max[1]);
39 } else if (!af.isUnsigned()) {
40 const int b = af.sampleSize();
41 if (b == 2) {
42 qint16 max[2];
43 max[0] = max[1] = 0;
44 const qint16 *p0 = (qint16*)data[0];
45 const qint16 *p1 = (qint16*)data[1];
46 for (int i = 0; i < s; i+=step) {
47 max[0] = qMax(max[0], qAbs(p0[i]));
48 max[1] = qMax(max[1], qAbs(p1[i]));
49 }
50 level[0] = 20.0f * log10((double)max[0]/(double)INT16_MAX);
51 level[1] = 20.0f * log10((double)max[1]/(double)INT16_MAX);
52 }
53 }
54 if (!qFuzzyCompare(level[0], mLeft)) {
55 mLeft = level[0];
56 Q_EMIT leftLevelChanged(mLeft);
57 }
58 if (!qFuzzyCompare(level[1], mRight)) {
59 mRight = level[1];
60 Q_EMIT rightLevelChanged(mRight);
61 }
62 //qDebug("db: %d --- %d", mLeft, mRight);
63 }
0 #ifndef VUMeterFilter_H
1 #define VUMeterFilter_H
2 #include <QtAV/Filter.h>
3
4 using namespace QtAV;
5 class VUMeterFilter : public AudioFilter
6 {
7 Q_OBJECT
8 Q_PROPERTY(int leftLevel READ leftLevel WRITE setLeftLevel NOTIFY leftLevelChanged)
9 Q_PROPERTY(int rightLevel READ rightLevel WRITE setRightLevel NOTIFY rightLevelChanged)
10 public:
11 VUMeterFilter(QObject* parent = NULL);
12 int leftLevel() const {return mLeft;}
13 void setLeftLevel(int value) {mLeft = value;}
14 int rightLevel() const {return mRight;}
15 void setRightLevel(int value) {mRight = value;}
16 Q_SIGNALS:
17 void leftLevelChanged(int value);
18 void rightLevelChanged(int value);
19 protected:
20 void process(Statistics* statistics, AudioFrame* frame = 0) Q_DECL_OVERRIDE;
21 private:
22 int mLeft;
23 int mRight;
24 };
25
26 #endif // VUMeterFilter_H
0 #include "VUMeterFilter.h"
1
2 #include <QtGui/QGuiApplication>
3 #include <QQuickItem>
4 #include <QtQml/QQmlEngine>
5 #include <QtQml/QQmlContext>
6 #include <QtQml/QQmlApplicationEngine>
7 int main(int argc, char** argv)
8 {
9 qmlRegisterType<VUMeterFilter>("com.qtav.vumeter", 1, 0, "VUMeterFilter");
10 QGuiApplication app(argc, argv);
11
12 QQmlApplicationEngine engine;
13 engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
14
15 return app.exec();
16 }
0 import QtQuick 2.6
1 import QtQuick.Window 2.2
2 import QtQuick.Dialogs 1.0
3 import QtAV 1.7
4 import com.qtav.vumeter 1.0
5 Window {
6 visible: true
7 width: 640
8 height: 480
9 title: qsTr("QtAV VU Meter Filter. Press key 'O' to open a file")
10
11 VUMeterFilter {
12 id: vu
13 }
14
15 AudioFilter {
16 id: afilter
17 type: AudioFilter.UserFilter
18 userFilter: vu
19 }
20
21 Video {
22 id: video
23 autoPlay: true
24 anchors.fill: parent
25 audioFilters: [afilter]
26 Text {
27 anchors.fill: parent
28 color: "white"
29 text: "dB left=" + vu.leftLevel + " right=" + vu.rightLevel
30 }
31 }
32
33 Item {
34 anchors.fill: parent
35 focus: true
36 Keys.onPressed: {
37 switch (event.key) {
38 case Qt.Key_O:
39 fileDialog.open()
40 break
41 }
42 }
43 }
44 FileDialog {
45 id: fileDialog
46 onAccepted: video.source = fileUrl
47 }
48 }
0 <RCC>
1 <qresource prefix="/">
2 <file>main.qml</file>
3 </qresource>
4 </RCC>
0 TEMPLATE = app
1 CONFIG -= app_bundle
2
3 QT += av qml quick
4 CONFIG += c++11
5
6 HEADERS = VUMeterFilter.h
7 SOURCES = main.cpp VUMeterFilter.cpp
8
9 RESOURCES += qml.qrc
10
11 # Additional import path used to resolve QML modules in Qt Creator's code model
12 QML_IMPORT_PATH =
13
14 # Additional import path used to resolve QML modules just for Qt Quick Designer
15 QML_DESIGNER_IMPORT_PATH =
16
17 # The following define makes your compiler emit warnings if you use
18 # any feature of Qt which as been marked deprecated (the exact warnings
19 # depend on your compiler). Please consult the documentation of the
20 # deprecated API in order to know how to port your code away from it.
21 DEFINES += QT_DEPRECATED_WARNINGS
22
23 # You can also make your code fail to compile if you use deprecated APIs.
24 # In order to do so, uncomment the following line.
25 # You can also select to disable deprecated APIs only up to a certain version of Qt.
26 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
27
28 # Default rules for deployment.
29 qnx: target.path = /tmp/$${TARGET}/bin
30 else: unix:!android: target.path = /opt/$${TARGET}/bin
31 !isEmpty(target.path): INSTALLS += target
0 # Copyright (c) 2017, Riverbank Computing Limited
1 # All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are met:
5 #
6 # 1. Redistributions of source code must retain the above copyright notice,
7 # this list of conditions and the following disclaimer.
8 #
9 # 2. Redistributions in binary form must reproduce the above copyright notice,
10 # this list of conditions and the following disclaimer in the documentation
11 # and/or other materials provided with the distribution.
12 #
13 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 # POSSIBILITY OF SUCH DAMAGE.
24
25 # This is v2.3 of this boilerplate.
26
27
28 from distutils import sysconfig
29 import glob
30 import os
31 import optparse
32 import sys
33
34
35 ###############################################################################
36 # You shouldn't need to modify anything above this line.
37 ###############################################################################
38
39
40 class QtAVConfiguration(object):
41 """ This class encapsulates all the module specific information needed by
42 the rest of this script to implement a configure.py script for modules that
43 build on top of PyQt. Functions implemented by the rest of this script
44 that begin with an underscore are considered internal and shouldn't be
45 called from here.
46 """
47
48 # The name of the module as it would be used in an import statement.
49 name = 'QtAV'
50
51 # Set if support for C++ exceptions can be disabled.
52 no_exceptions = True
53
54 # The name (without the .pyi extension) of the name of the PEP 484 stub
55 # file to be generated. If it is None or an empty string then a stub file
56 # is not generated.
57 pep484_stub_file = 'QtAV'
58
59 @staticmethod
60 def get_sip_file(target_configuration):
61 """ Return the name of the module's .sip file. target_configuration is
62 the target configuration.
63 """
64
65 return 'sip/QtAV/QtAVmod.sip'
66
67 @staticmethod
68 def get_sip_installs(target_configuration):
69 """ Return a tuple of the installation directory of the module's .sip
70 files and a sequence of the names of each of the .sip files relative to
71 the directory containing this configuration script. None is returned
72 if the module's .sip files are not to be installed.
73 target_configuration is the target configuration.
74 """
75
76 if target_configuration.qtav_sip_dir == '':
77 return None
78
79 path = os.path.join(target_configuration.qtav_sip_dir, 'QtAV')
80 files = glob.glob('sip/QtAV/*.sip')
81
82 return path, files
83
84 @staticmethod
85 def get_qmake_configuration(target_configuration):
86 """ Return a dict of qmake configuration values for CONFIG, DEFINES,
87 INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either
88 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding
89 version of Qt. target_configuration is the target configuration.
90 """
91 libs = r"-L%s/lib " % (target_configuration.qtav_base_dir)
92 if sys.platform == "win32":
93 libs += "-lQtAV1 -lQtAVWidgets1"
94 else:
95 libs += "-lQtAV -lQtAVWidgets"
96
97 return {'QT': 'widgets opengl',
98 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"),
99 'LIBS': libs}
100
101 @staticmethod
102 def get_mac_wrapped_library_file(target_configuration):
103 """ Return the full pathname of the file that implements the library
104 being wrapped by the module as it would be called on OS/X so that the
105 module will reference it explicitly without DYLD_LIBRARY_PATH being
106 set. If it is None or an empty string then the default is used.
107 target_configuration is the target configuration.
108 """
109
110 return None
111
112
113 class QtAVWidgetsConfiguration(object):
114 """ This class encapsulates all the module specific information needed by
115 the rest of this script to implement a configure.py script for modules that
116 build on top of PyQt. Functions implemented by the rest of this script
117 that begin with an underscore are considered internal and shouldn't be
118 called from here.
119 """
120
121 # The name of the module as it would be used in an import statement.
122 name = 'QtAVWidgets'
123
124 # Set if support for C++ exceptions can be disabled.
125 no_exceptions = True
126
127 # The name (without the .pyi extension) of the name of the PEP 484 stub
128 # file to be generated. If it is None or an empty string then a stub file
129 # is not generated.
130 pep484_stub_file = 'QtAVWidgets'
131
132 @staticmethod
133 def get_sip_file(target_configuration):
134 """ Return the name of the module's .sip file. target_configuration is
135 the target configuration.
136 """
137
138 return 'sip/QtAVWidgets/QtAVWidgetsmod.sip'
139
140 @staticmethod
141 def get_sip_installs(target_configuration):
142 """ Return a tuple of the installation directory of the module's .sip
143 files and a sequence of the names of each of the .sip files relative to
144 the directory containing this configuration script. None is returned
145 if the module's .sip files are not to be installed.
146 target_configuration is the target configuration.
147 """
148
149 if target_configuration.qtav_sip_dir == '':
150 return None
151
152 path = os.path.join(target_configuration.qtav_sip_dir, 'QtAVWidgets')
153 files = glob.glob('sip/QtAVWidgets/*.sip')
154
155 return path, files
156
157 @staticmethod
158 def get_qmake_configuration(target_configuration):
159 """ Return a dict of qmake configuration values for CONFIG, DEFINES,
160 INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either
161 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding
162 version of Qt. target_configuration is the target configuration.
163 """
164 libs = r"-L%s/lib " % (target_configuration.qtav_base_dir)
165 if sys.platform == "win32":
166 libs += "-lQtAV1 -lQtAVWidgets1"
167 else:
168 libs += "-lQtAV -lQtAVWidgets"
169
170 return {'QT': 'widgets opengl',
171 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"),
172 'LIBS': libs}
173
174 @staticmethod
175 def get_mac_wrapped_library_file(target_configuration):
176 """ Return the full pathname of the file that implements the library
177 being wrapped by the module as it would be called on OS/X so that the
178 module will reference it explicitly without DYLD_LIBRARY_PATH being
179 set. If it is None or an empty string then the default is used.
180 target_configuration is the target configuration.
181 """
182
183 return None
184
185
186 class PackageConfiguration(object):
187 """ This class encapsulates all the package specific information needed by
188 the rest of this script to implement a configure.py script for modules that
189 build on top of PyQt. Functions implemented by the rest of this script
190 that begin with an underscore are considered internal and shouldn't be
191 called from here.
192 """
193
194 # The descriptive name of the module. This is used in help text and error
195 # messages.
196 descriptive_name = "PyQtAV"
197
198 # The version of the module as a string. Set it to None if you don't
199 # provide version information.
200 version = '1.12.0'
201
202 # The sequence of module configurations that make up the package.
203 modules = (QtAVConfiguration(), QtAVWidgetsConfiguration())
204
205 # Set if a configuration script is provided that handles versions of PyQt4
206 # prior to v4.10 (i.e. versions where the pyqtconfig.py module is
207 # available). If provided the script must be called configure-old.py and
208 # be in the same directory as this script.
209 legacy_configuration_script = False
210
211 # The minimum version of SIP that is required. This should be a
212 # dot-separated string of two or three integers (e.g. '1.0', '4.10.3'). If
213 # it is None or an empty string then the version is not checked.
214 minimum_sip_version = '4.19.1'
215
216 # Set if the module supports redefining 'protected' as 'public'.
217 protected_is_public_is_supported = True
218
219 # Set if the module supports PyQt4.
220 pyqt4_is_supported = False
221
222 # Set if the module supports PyQt5.
223 pyqt5_is_supported = True
224
225 # Set if the PyQt5 support is the default. It is ignored unless both
226 # 'pyqt4_is_supported' and 'pyqt5_is_supported' are set.
227 pyqt5_is_default = True
228
229 # The name (without the .api extension) of the name of the QScintilla API
230 # file to be generated. If it is None or an empty string then an API file
231 # is not generated.
232 qscintilla_api_file = 'PyQtAV'
233
234 # The email address that will be included when an error in the script is
235 # detected. Leave it blank if you don't want to include an address.
236 support_email_address = 'support@riverbankcomputing.com'
237
238 # Set if the user can provide a configuration file. It is normally only
239 # used if cross-compilation is supported.
240 user_configuration_file_is_supported = True
241
242 # Set if the user is allowed to pass PyQt sip flags on the command line.
243 # It is normally only used if cross-compilation is supported. It is
244 # ignored unless at least one of 'pyqt4_is_supported' or
245 # 'pyqt5_is_supported' is set.
246 user_pyqt_sip_flags_is_supported = True
247
248 @staticmethod
249 def init_target_configuration(target_configuration):
250 """ Perform any module specific initialisation of the target
251 target configuration. Typically this is the initialisation of module
252 specific attributes. To avoid name clashes attributes should be given
253 a module specific prefix. target_configuration is the target
254 configuration.
255 """
256
257 target_configuration.qtav_version = None
258 target_configuration.qtav_base_dir = None
259 target_configuration.qtav_sip_dir = None
260
261 @staticmethod
262 def init_optparser(optparser, target_configuration):
263 """ Perform any module specific initialisation of the command line
264 option parser. To avoid name clashes destination attributes should be
265 given a module specific prefix. optparser is the option parser.
266 target_configuration is the target configuration.
267 """
268
269 optparser.add_option('--qtav-version', dest='qtav_version',
270 type='string', metavar="VERSION",
271 help="the QtAV version number (eg. 1.12.0)")
272
273 optparser.add_option('--qtav-base-dir', '-v',
274 dest='qtav_base_dir', type='string', default=None,
275 action='callback', callback=optparser_store_abspath_dir,
276 metavar="DIR",
277 help="the base directory that contains QtAV installation ")
278
279 optparser.add_option("--no-sip-files", action="store_true",
280 default=False, dest="qtav_no_sip_files",
281 help="disable the installation of the .sip files "
282 "[default: enabled]")
283
284 @staticmethod
285 def apply_options(target_configuration, options):
286 """ Apply the module specific command line options to the target
287 configuration. target_configuration is the target configuration.
288 options are the parsed options.
289 """
290
291 # Historically Digia have been incapable of remembering to update the
292 # version number in a new release of add-on libraries so we allow the
293 # user to specify it while defaulting to the Qt version.
294 qtav_version_str = options.qtav_version
295 if qtav_version_str is None:
296 qtav_version_str = target_configuration.qt_version_str
297
298 target_configuration.qtav_version = version_from_string(
299 qtav_version_str)
300
301 if target_configuration.qtav_version is None:
302 error("%s is not a valid QtAV version." % qtav_version_str)
303
304 if target_configuration.qtav_version >= 0x020000 and target_configuration.pyqt_package != 'PyQt5':
305 error("QtAV v%s requires PyQt5." % options.qtav_version)
306
307 if options.qtav_base_dir is not None:
308 target_configuration.qtav_base_dir = options.qtav_base_dir
309 else:
310 error("--qtav-base-dir must be spcified")
311
312 if options.qtav_no_sip_files:
313 target_configuration.qtav_sip_dir = ''
314 else:
315 target_configuration.qtav_sip_dir = os.path.join(target_configuration.qtav_base_dir, 'share', 'sip')
316
317 @staticmethod
318 def check_package(target_configuration):
319 """ Perform any package specific checks now that the target
320 configuration is complete. target_configuration is the target
321 configuration.
322 """
323
324 # Nothing to do.
325
326 @staticmethod
327 def inform_user(target_configuration):
328 """ Inform the user about module specific configuration information.
329 target_configuration is the target configuration.
330 """
331
332 major = (target_configuration.qtav_version >> 16) & 0xff
333 minor = (target_configuration.qtav_version >> 8) & 0xff
334 patch = target_configuration.qtav_version & 0xff
335 version = '%d.%d.%d' % (major, minor, patch)
336
337 inform("QtAV %s is being used." % version)
338
339 if target_configuration.qtav_sip_dir != '':
340 inform(
341 "The QtAV .sip files will be installed in %s." %
342 target_configuration.qtav_sip_dir)
343
344 @staticmethod
345 def pre_code_generation(target_config):
346 """ Perform any module specific initialisation prior to generating the
347 code. target_config is the target configuration.
348 """
349
350 # Nothing to do.
351
352 @staticmethod
353 def get_sip_flags(target_configuration):
354 """ Return the list of module-specific flags to pass to SIP.
355 target_configuration is the target configuration.
356 """
357
358 major = (target_configuration.qtav_version >> 16) & 0xff
359 minor = (target_configuration.qtav_version >> 8) & 0xff
360 patch = target_configuration.qtav_version & 0xff
361 version_tag = 'QtAV_%d_%d_%d' % (major, minor, patch)
362
363 return ['-t', version_tag]
364
365
366 ###############################################################################
367 # You shouldn't need to modify anything below this line.
368 ###############################################################################
369
370
371 def error(msg):
372 """ Display an error message and terminate. msg is the text of the error
373 message.
374 """
375
376 sys.stderr.write(_format("Error: " + msg) + "\n")
377 sys.exit(1)
378
379
380 def inform(msg):
381 """ Display an information message. msg is the text of the error message.
382 """
383
384 sys.stdout.write(_format(msg) + "\n")
385
386
387 def quote(path):
388 """ Return a path with quotes added if it contains spaces. path is the
389 path.
390 """
391
392 if ' ' in path:
393 path = '"%s"' % path
394
395 return path
396
397
398 def optparser_store_abspath(option, opt_str, value, parser):
399 """ An optparser callback that saves an option as an absolute pathname. """
400
401 setattr(parser.values, option.dest, os.path.abspath(value))
402
403
404 def optparser_store_abspath_dir(option, opt_str, value, parser):
405 """ An optparser callback that saves an option as the absolute pathname
406 of an existing directory.
407 """
408
409 if not os.path.isdir(value):
410 raise optparse.OptionValueError("'%s' is not a directory" % value)
411
412 setattr(parser.values, option.dest, os.path.abspath(value))
413
414
415 def optparser_store_abspath_exe(option, opt_str, value, parser):
416 """ An optparser callback that saves an option as the absolute pathname
417 of an existing executable.
418 """
419
420 if not os.access(value, os.X_OK):
421 raise optparse.OptionValueError("'%s' is not an executable" % value)
422
423 setattr(parser.values, option.dest, os.path.abspath(value))
424
425
426 def read_define(filename, define):
427 """ Read the value of a #define from a file. filename is the name of the
428 file. define is the name of the #define. None is returned if there was no
429 such #define.
430 """
431
432 f = open(filename)
433
434 for l in f:
435 wl = l.split()
436 if len(wl) >= 3 and wl[0] == "#define" and wl[1] == define:
437 # Take account of embedded spaces.
438 value = ' '.join(wl[2:])[1:-1]
439 break
440 else:
441 value = None
442
443 f.close()
444
445 return value
446
447
448 def version_from_string(version_str):
449 """ Convert a version string of the form m, m.n or m.n.o to an encoded
450 version number (or None if it was an invalid format). version_str is the
451 version string.
452 """
453
454 parts = version_str.split('.')
455 if not isinstance(parts, list):
456 return None
457
458 if len(parts) == 1:
459 parts.append('0')
460
461 if len(parts) == 2:
462 parts.append('0')
463
464 if len(parts) != 3:
465 return None
466
467 version = 0
468
469 for part in parts:
470 try:
471 v = int(part)
472 except ValueError:
473 return None
474
475 version = (version << 8) + v
476
477 return version
478
479
480 def _format(msg, left_margin=0, right_margin=78):
481 """ Format a message by inserting line breaks at appropriate places. msg
482 is the text of the message. left_margin is the position of the left
483 margin. right_margin is the position of the right margin. Returns the
484 formatted message.
485 """
486
487 curs = left_margin
488 fmsg = " " * left_margin
489
490 for w in msg.split():
491 l = len(w)
492 if curs != left_margin and curs + l > right_margin:
493 fmsg = fmsg + "\n" + (" " * left_margin)
494 curs = left_margin
495
496 if curs > left_margin:
497 fmsg = fmsg + " "
498 curs = curs + 1
499
500 fmsg = fmsg + w
501 curs = curs + l
502
503 return fmsg
504
505
506 class _ConfigurationFileParser:
507 """ A parser for configuration files. """
508
509 def __init__(self, config_file):
510 """ Read and parse a configuration file. """
511
512 self._config = {}
513 self._extrapolating = []
514
515 cfg = open(config_file)
516 line_nr = 0
517 last_name = None
518
519 section = ''
520 section_config = {}
521 self._config[section] = section_config
522
523 for l in cfg:
524 line_nr += 1
525
526 # Strip comments.
527 l = l.split('#')[0]
528
529 # See if this might be part of a multi-line.
530 multiline = (last_name is not None and len(l) != 0 and l[0] == ' ')
531
532 l = l.strip()
533
534 if l == '':
535 last_name = None
536 continue
537
538 # See if this is a new section.
539 if l[0] == '[' and l[-1] == ']':
540 section = l[1:-1].strip()
541 if section == '':
542 error(
543 "%s:%d: Empty section name." % (
544 config_file, line_nr))
545
546 if section in self._config:
547 error(
548 "%s:%d: Section '%s' defined more than once." % (
549 config_file, line_nr, section))
550
551 section_config = {}
552 self._config[section] = section_config
553
554 last_name = None
555 continue
556
557 parts = l.split('=', 1)
558 if len(parts) == 2:
559 name = parts[0].strip()
560 value = parts[1].strip()
561 elif multiline:
562 name = last_name
563 value = section_config[last_name]
564 value += ' ' + l
565 else:
566 name = value = ''
567
568 if name == '' or value == '':
569 error("%s:%d: Invalid line." % (config_file, line_nr))
570
571 section_config[name] = value
572 last_name = name
573
574 cfg.close()
575
576 def sections(self):
577 """ Return the list of sections, excluding the default one. """
578
579 return [s for s in self._config.keys() if s != '']
580
581 def preset(self, name, value):
582 """ Add a preset value to the configuration. """
583
584 self._config[''][name] = value
585
586 def get(self, section, name, default=None):
587 """ Get a configuration value while extrapolating. """
588
589 # Get the name from the section, or the default section.
590 value = self._config[section].get(name)
591 if value is None:
592 value = self._config[''].get(name)
593 if value is None:
594 if default is None:
595 error(
596 "Configuration file references non-existent name "
597 "'%s'." % name)
598
599 return default
600
601 # Handle any extrapolations.
602 parts = value.split('%(', 1)
603 while len(parts) == 2:
604 prefix, tail = parts
605
606 parts = tail.split(')', 1)
607 if len(parts) != 2:
608 error(
609 "Configuration file contains unterminated "
610 "extrapolated name '%s'." % tail)
611
612 xtra_name, suffix = parts
613
614 if xtra_name in self._extrapolating:
615 error(
616 "Configuration file contains a recursive reference to "
617 "'%s'." % xtra_name)
618
619 self._extrapolating.append(xtra_name)
620 xtra_value = self.get(section, xtra_name)
621 self._extrapolating.pop()
622
623 value = prefix + xtra_value + suffix
624
625 parts = value.split('%(', 1)
626
627 return value
628
629 def getboolean(self, section, name, default):
630 """ Get a boolean configuration value while extrapolating. """
631
632 value = self.get(section, name, default)
633
634 # In case the default was returned.
635 if isinstance(value, bool):
636 return value
637
638 if value in ('True', 'true', '1'):
639 return True
640
641 if value in ('False', 'false', '0'):
642 return False
643
644 error(
645 "Configuration file contains invalid boolean value for "
646 "'%s'." % name)
647
648 def getlist(self, section, name, default):
649 """ Get a list configuration value while extrapolating. """
650
651 value = self.get(section, name, default)
652
653 # In case the default was returned.
654 if isinstance(value, list):
655 return value
656
657 return value.split()
658
659
660 class _HostPythonConfiguration:
661 """ A container for the host Python configuration. """
662
663 def __init__(self):
664 """ Initialise the configuration. """
665
666 self.platform = sys.platform
667 self.version = sys.hexversion >> 8
668
669 self.inc_dir = sysconfig.get_python_inc()
670 self.venv_inc_dir = sysconfig.get_python_inc(prefix=sys.prefix)
671 self.module_dir = sysconfig.get_python_lib(plat_specific=1)
672 self.debug = hasattr(sys, 'gettotalrefcount')
673
674 if sys.platform == 'win32':
675 try:
676 # Python v3.3 and later.
677 base_prefix = sys.base_prefix
678
679 except AttributeError:
680 try:
681 # virtualenv for Python v2.
682 base_prefix = sys.real_prefix
683
684 except AttributeError:
685 # We can't detect the base prefix in Python v3 prior to
686 # v3.3.
687 base_prefix = sys.prefix
688
689 self.data_dir = sys.prefix
690 self.lib_dir = base_prefix + '\\libs'
691 else:
692 self.data_dir = sys.prefix + '/share'
693 self.lib_dir = sys.prefix + '/lib'
694
695
696 class _TargetQtConfiguration:
697 """ A container for the target Qt configuration. """
698
699 def __init__(self, qmake):
700 """ Initialise the configuration. qmake is the full pathname of the
701 qmake executable that will provide the configuration.
702 """
703
704 pipe = os.popen(quote(qmake) + ' -query')
705
706 for l in pipe:
707 l = l.strip()
708
709 tokens = l.split(':', 1)
710 if isinstance(tokens, list):
711 if len(tokens) != 2:
712 error("Unexpected output from qmake: '%s'\n" % l)
713
714 name, value = tokens
715 else:
716 name = tokens
717 value = None
718
719 name = name.replace('/', '_')
720
721 setattr(self, name, value)
722
723 pipe.close()
724
725
726 class _TargetConfiguration:
727 """ A container for the target configuration. """
728
729 def __init__(self, pkg_config):
730 """ Initialise the configuration with default values. pkg_config is
731 the package configuration.
732 """
733
734 # Values based on the host Python configuration.
735 py_config = _HostPythonConfiguration()
736 self.py_debug = py_config.debug
737 self.py_platform = py_config.platform
738 self.py_version = py_config.version
739 self.py_module_dir = py_config.module_dir
740 self.py_inc_dir = py_config.inc_dir
741 self.py_venv_inc_dir = py_config.venv_inc_dir
742 self.py_pylib_dir = py_config.lib_dir
743 self.py_sip_dir = os.path.join(py_config.data_dir, 'sip')
744 self.sip_inc_dir = py_config.venv_inc_dir
745
746 # Remaining values.
747 self.debug = False
748 self.pyqt_sip_flags = None
749 self.pyqt_version_str = ''
750 self.qmake = self._find_exe('qmake')
751 self.qmake_spec = ''
752 self.qt_version = 0
753 self.qt_version_str = ''
754 self.sip = self._find_exe('sip5', 'sip')
755 self.sip_version = None
756 self.sip_version_str = None
757 self.sysroot = ''
758 self.stubs_dir = ''
759
760 self.prot_is_public = (self.py_platform.startswith('linux') or self.py_platform == 'darwin')
761
762 if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
763 pyqt = 'PyQt5' if pkg_config.pyqt5_is_default else 'PyQt4'
764 elif pkg_config.pyqt5_is_supported and not pkg_config.pyqt4_is_supported:
765 pyqt = 'PyQt5'
766 elif not pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
767 pyqt = 'PyQt4'
768 else:
769 pyqt = None
770
771 if pyqt is not None:
772 self.module_dir = os.path.join(py_config.module_dir, pyqt)
773 self.pyqt_sip_dir = os.path.join(self.py_sip_dir, pyqt)
774 else:
775 self.module_dir = py_config.module_dir
776 self.pyqt_sip_dir = None
777
778 self.pyqt_package = pyqt
779
780 pkg_config.init_target_configuration(self)
781
782 def update_from_configuration_file(self, config_file):
783 """ Update the configuration with values from a file. config_file
784 is the name of the configuration file.
785 """
786
787 inform("Reading configuration from %s..." % config_file)
788
789 parser = _ConfigurationFileParser(config_file)
790
791 # Populate some presets from the command line.
792 parser.preset('py_major', str(self.py_version >> 16))
793 parser.preset('py_minor', str((self.py_version >> 8) & 0xff))
794 parser.preset('sysroot', self.sysroot)
795
796 if self.pyqt_package is None:
797 section = ''
798 else:
799 # At the moment we only need to distinguish between PyQt4 and
800 # PyQt5. If that changes we may need a --target-pyqt-version
801 # command line option.
802 pyqt_version = 0x050000 if self.pyqt_package == 'PyQt5' else 0x040000
803
804 # Find the section corresponding to the version of PyQt.
805 section = None
806 latest_section = -1
807
808 for name in parser.sections():
809 parts = name.split()
810 if len(parts) != 2 or parts[0] != 'PyQt':
811 continue
812
813 section_pyqt_version = version_from_string(parts[1])
814 if section_pyqt_version is None:
815 continue
816
817 # Major versions must match.
818 if section_pyqt_version >> 16 != pyqt_version >> 16:
819 continue
820
821 # It must be no later that the version of PyQt.
822 if section_pyqt_version > pyqt_version:
823 continue
824
825 # Save it if it is the latest so far.
826 if section_pyqt_version > latest_section:
827 section = name
828 latest_section = section_pyqt_version
829
830 if section is None:
831 error(
832 "%s does not define a section that covers PyQt "
833 "v%s." % (config_file, self.pyqt_version_str))
834
835 self.py_platform = parser.get(section, 'py_platform', self.py_platform)
836 self.py_inc_dir = parser.get(section, 'py_inc_dir', self.py_inc_dir)
837 self.py_venv_inc_dir = self.py_inc_dir
838 self.py_pylib_dir = parser.get(section, 'py_pylib_dir',
839 self.py_pylib_dir)
840
841 self.sip_inc_dir = self.py_venv_inc_dir
842
843 self.module_dir = parser.get(section, 'module_dir', self.module_dir)
844
845 if self.pyqt_package is not None:
846 self.py_sip_dir = parser.get(section, 'py_sip_dir',
847 self.py_sip_dir)
848
849 # Construct the SIP flags.
850 flags = []
851
852 flags.append('-t')
853 flags.append(self._get_platform_tag())
854
855 if self.pyqt_package == 'PyQt5':
856 if self.qt_version < 0x050000:
857 error("PyQt5 requires Qt v5.0 or later.")
858
859 if self.qt_version > 0x060000:
860 self.qt_version = 0x060000
861 else:
862 if self.qt_version > 0x050000:
863 self.qt_version = 0x050000
864
865 major = (self.qt_version >> 16) & 0xff
866 minor = (self.qt_version >> 8) & 0xff
867 patch = self.qt_version & 0xff
868
869 flags.append('-t')
870 flags.append('Qt_%d_%d_%d' % (major, minor, patch))
871
872 for feat in parser.getlist(section, 'pyqt_disabled_features', []):
873 flags.append('-x')
874 flags.append(feat)
875
876 self.pyqt_sip_flags = ' '.join(flags)
877
878 def _get_platform_tag(self):
879 """ Return the tag for the target platform. """
880
881 # This replicates the logic in PyQt's configure scripts.
882 if self.py_platform == 'win32':
883 plattag = 'WS_WIN'
884 elif self.py_platform == 'darwin':
885 plattag = 'WS_MACX'
886 else:
887 plattag = 'WS_X11'
888
889 return plattag
890
891 def introspect_pyqt(self, pkg_config):
892 """ Introspect PyQt to determine the sip flags required. pkg_config
893 is the package configuration.
894 """
895
896 if self.pyqt_package == 'PyQt5':
897 try:
898 from PyQt5 import QtCore
899 except ImportError:
900 error(
901 "Unable to import PyQt5.QtCore. Make sure PyQt5 is "
902 "installed.")
903 else:
904 try:
905 from PyQt4 import QtCore
906 except ImportError:
907 error(
908 "Unable to import PyQt4.QtCore. Make sure PyQt4 is "
909 "installed.")
910
911 self.pyqt_version_str = QtCore.PYQT_VERSION_STR
912 self.qt_version_str = QtCore.qVersion()
913
914 # See if we have a PyQt that embeds its configuration.
915 try:
916 pyqt_config = QtCore.PYQT_CONFIGURATION
917 except AttributeError:
918 pyqt_config = None
919
920 if pyqt_config is None:
921 if pkg_config.legacy_configuration_script:
922 # Fallback to the old configuration script.
923 config_script = sys.argv[0].replace('configure', 'configure-old')
924 args = [sys.executable, config_script] + sys.argv[1:]
925
926 try:
927 os.execv(sys.executable, args)
928 except OSError:
929 pass
930
931 error("Unable to execute '%s'" % config_script)
932
933 error("PyQt v4.10 or later is required.")
934
935 self.pyqt_sip_flags = pyqt_config['sip_flags']
936
937 def apply_sysroot(self):
938 """ Apply sysroot where necessary. """
939
940 if self.sysroot != '':
941 self.py_inc_dir = self._apply_sysroot(self.py_inc_dir)
942 self.py_venv_inc_dir = self._apply_sysroot(self.py_venv_inc_dir)
943 self.py_pylib_dir = self._apply_sysroot(self.py_pylib_dir)
944 self.py_sip_dir = self._apply_sysroot(self.py_sip_dir)
945 self.module_dir = self._apply_sysroot(self.module_dir)
946 self.sip_inc_dir = self._apply_sysroot(self.sip_inc_dir)
947
948 def _apply_sysroot(self, dir_name):
949 """ Replace any leading sys.prefix of a directory name with sysroot.
950 """
951
952 if dir_name.startswith(sys.prefix):
953 dir_name = self.sysroot + dir_name[len(sys.prefix):]
954
955 return dir_name
956
957 def get_qt_configuration(self, opts):
958 """ Get the Qt configuration that can be extracted from qmake. opts
959 are the command line options.
960 """
961
962 # Query qmake.
963 qt_config = _TargetQtConfiguration(self.qmake)
964
965 self.qt_version_str = getattr(qt_config, 'QT_VERSION', '')
966 self.qt_version = version_from_string(self.qt_version_str)
967 if self.qt_version is None:
968 error("Unable to determine the version of Qt.")
969
970 # On Windows for Qt versions prior to v5.9.0 we need to be explicit
971 # about the qmake spec.
972 if self.qt_version < 0x050900 and self.py_platform == 'win32':
973 if self.py_version >= 0x030500:
974 self.qmake_spec = 'win32-msvc2015'
975 elif self.py_version >= 0x030300:
976 self.qmake_spec = 'win32-msvc2010'
977 elif self.py_version >= 0x020600:
978 self.qmake_spec = 'win32-msvc2008'
979 elif self.py_version >= 0x020400:
980 self.qmake_spec = 'win32-msvc.net'
981 else:
982 self.qmake_spec = 'win32-msvc'
983 else:
984 # Otherwise use the default.
985 self.qmake_spec = ''
986
987 # The binary MacOS/X Qt installer used to default to XCode. If so then
988 # use macx-clang (Qt v5) or macx-g++ (Qt v4).
989 if sys.platform == 'darwin':
990 try:
991 # Qt v5.
992 if qt_config.QMAKE_SPEC == 'macx-xcode':
993 # This will exist (and we can't check anyway).
994 self.qmake_spec = 'macx-clang'
995 else:
996 # No need to explicitly name the default.
997 self.qmake_spec = ''
998 except AttributeError:
999 # Qt v4.
1000 self.qmake_spec = 'macx-g++'
1001
1002 self.api_dir = os.path.join(qt_config.QT_INSTALL_DATA, 'qsci')
1003 self.qt_inc_dir = qt_config.QT_INSTALL_HEADERS
1004 self.qt_lib_dir = qt_config.QT_INSTALL_LIBS
1005
1006 if self.sysroot == '':
1007 self.sysroot = getattr(qt_config, 'QT_SYSROOT', '')
1008
1009 def apply_pre_options(self, opts):
1010 """ Apply options from the command line that influence subsequent
1011 configuration. opts are the command line options.
1012 """
1013
1014 # On Windows the interpreter must be a debug build if a debug version
1015 # is to be built and vice versa.
1016 if sys.platform == 'win32':
1017 if opts.debug:
1018 if not self.py_debug:
1019 error(
1020 "A debug version of Python must be used when "
1021 "--debug is specified.")
1022 elif self.py_debug:
1023 error(
1024 "--debug must be specified when a debug version of "
1025 "Python is used.")
1026
1027 self.debug = opts.debug
1028
1029 # Get the system root.
1030 if opts.sysroot is not None:
1031 self.sysroot = opts.sysroot
1032
1033 # Determine how to run qmake.
1034 if opts.qmake is not None:
1035 self.qmake = opts.qmake
1036
1037 # On Windows add the directory that probably contains the Qt DLLs
1038 # to PATH.
1039 if sys.platform == 'win32':
1040 path = os.environ['PATH']
1041 path = os.path.dirname(self.qmake) + ';' + path
1042 os.environ['PATH'] = path
1043
1044 if self.qmake is None:
1045 error(
1046 "Use the --qmake argument to explicitly specify a working "
1047 "Qt qmake.")
1048
1049 if opts.qmakespec is not None:
1050 self.qmake_spec = opts.qmakespec
1051
1052 if self.pyqt_package is not None:
1053 try:
1054 self.pyqt_package = opts.pyqt_package
1055 except AttributeError:
1056 # Multiple PyQt versions are not supported.
1057 pass
1058
1059 self.module_dir = os.path.join(self.py_module_dir,
1060 self.pyqt_package)
1061
1062 def apply_post_options(self, opts, pkg_config):
1063 """ Apply options from the command line that override the previous
1064 configuration. opts are the command line options. pkg_config is the
1065 package configuration.
1066 """
1067
1068 if self.pyqt_package is not None:
1069 if pkg_config.user_pyqt_sip_flags_is_supported:
1070 if opts.pyqt_sip_flags is not None:
1071 self.pyqt_sip_flags = opts.pyqt_sip_flags
1072
1073 if opts.pyqt_sip_dir is not None:
1074 self.pyqt_sip_dir = opts.pyqt_sip_dir
1075 else:
1076 self.pyqt_sip_dir = os.path.join(self.py_sip_dir,
1077 self.pyqt_package)
1078
1079 if _has_stubs(pkg_config):
1080 if opts.stubsdir is not None:
1081 self.stubs_dir = opts.stubsdir
1082
1083 if opts.no_stubs:
1084 self.stubs_dir = ''
1085 elif self.stubs_dir == '':
1086 self.stubs_dir = self.module_dir
1087
1088 if pkg_config.qscintilla_api_file:
1089 if opts.apidir is not None:
1090 self.api_dir = opts.apidir
1091
1092 if opts.no_qsci_api:
1093 self.api_dir = ''
1094
1095 if opts.destdir is not None:
1096 self.module_dir = opts.destdir
1097
1098 if pkg_config.protected_is_public_is_supported:
1099 if opts.prot_is_public is not None:
1100 self.prot_is_public = opts.prot_is_public
1101 else:
1102 self.prot_is_public = False
1103
1104 if opts.sip_inc_dir is not None:
1105 self.sip_inc_dir = opts.sip_inc_dir
1106
1107 if opts.sip is not None:
1108 self.sip = opts.sip
1109
1110 pkg_config.apply_options(self, opts)
1111
1112 @staticmethod
1113 def _find_exe(*exes):
1114 """ Find an executable, ie. the first on the path. """
1115
1116 path_dirs = os.environ.get('PATH', '').split(os.pathsep)
1117
1118 for exe in exes:
1119 # Strip any surrounding quotes.
1120 if exe.startswith('"') and exe.endswith('"'):
1121 exe = exe[1:-1]
1122
1123 if sys.platform == 'win32':
1124 exe = exe + '.exe'
1125
1126 for d in path_dirs:
1127 exe_path = os.path.join(d, exe)
1128
1129 if os.access(exe_path, os.X_OK):
1130 return exe_path
1131
1132 return None
1133
1134
1135 def _create_optparser(target_config, pkg_config):
1136 """ Create the parser for the command line. target_config is the target
1137 configuration containing default values. pkg_config is the package
1138 configuration.
1139 """
1140
1141 pkg_name = pkg_config.descriptive_name
1142
1143 p = optparse.OptionParser(usage="python %prog [options]",
1144 version=pkg_config.version)
1145
1146 p.add_option('--spec', dest='qmakespec', default=None, action='store',
1147 metavar="SPEC",
1148 help="pass -spec SPEC to qmake")
1149
1150 if _has_stubs(pkg_config):
1151 p.add_option('--stubsdir', dest='stubsdir', type='string',
1152 default=None, action='callback',
1153 callback=optparser_store_abspath, metavar="DIR",
1154 help="the PEP 484 stubs will be installed in DIR [default: "
1155 "with the module]")
1156 p.add_option('--no-stubs', dest='no_stubs', default=False,
1157 action='store_true',
1158 help="disable the installation of the PEP 484 stubs "
1159 "[default: enabled]")
1160
1161 if pkg_config.qscintilla_api_file:
1162 p.add_option('--apidir', '-a', dest='apidir', type='string',
1163 default=None, action='callback',
1164 callback=optparser_store_abspath, metavar="DIR",
1165 help="the QScintilla API file will be installed in DIR "
1166 "[default: QT_INSTALL_DATA/qsci]")
1167 p.add_option('--no-qsci-api', dest='no_qsci_api', default=False,
1168 action='store_true',
1169 help="disable the installation of the QScintilla API file "
1170 "[default: enabled]")
1171
1172 if pkg_config.user_configuration_file_is_supported:
1173 p.add_option('--configuration', dest='config_file', type='string',
1174 default=None, action='callback',
1175 callback=optparser_store_abspath, metavar="FILE",
1176 help="FILE defines the target configuration")
1177
1178 p.add_option('--destdir', '-d', dest='destdir', type='string',
1179 default=None, action='callback', callback=optparser_store_abspath,
1180 metavar="DIR",
1181 help="install %s in DIR [default: %s]" %
1182 (pkg_name, target_config.module_dir))
1183
1184 if pkg_config.protected_is_public_is_supported:
1185 p.add_option('--protected-is-public', dest='prot_is_public',
1186 default=None, action='store_true',
1187 help="enable building with 'protected' redefined as 'public' "
1188 "[default: %s]" % target_config.prot_is_public)
1189 p.add_option('--protected-not-public', dest='prot_is_public',
1190 action='store_false',
1191 help="disable building with 'protected' redefined as 'public'")
1192
1193 if target_config.pyqt_package is not None:
1194 pyqt = target_config.pyqt_package
1195
1196 if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
1197 p.add_option('--pyqt', dest='pyqt_package', type='choice',
1198 choices=['PyQt4', 'PyQt5'], default=pyqt,
1199 action='store', metavar="PyQtn",
1200 help="configure for PyQt4 or PyQt5 [default: %s]" % pyqt)
1201
1202 if pkg_config.user_pyqt_sip_flags_is_supported:
1203 p.add_option('--pyqt-sip-flags', dest='pyqt_sip_flags',
1204 default=None, action='store', metavar="FLAGS",
1205 help="the sip flags used to build PyQt [default: query PyQt]")
1206
1207 p.add_option('--qmake', '-q', dest='qmake', type='string', default=None,
1208 action='callback', callback=optparser_store_abspath_exe,
1209 metavar="FILE",
1210 help="the pathname of qmake is FILE [default: %s]" % (
1211 target_config.qmake or "search PATH"))
1212
1213 p.add_option('--sip', dest='sip', type='string', default=None,
1214 action='callback', callback=optparser_store_abspath_exe,
1215 metavar="FILE",
1216 help="the pathname of sip is FILE [default: "
1217 "%s]" % (target_config.sip or "None"))
1218 p.add_option('--sip-incdir', dest='sip_inc_dir', type='string',
1219 default=None, action='callback',
1220 callback=optparser_store_abspath_dir, metavar="DIR",
1221 help="the directory containing the sip.h header file file is DIR "
1222 "[default: %s]" % target_config.sip_inc_dir)
1223
1224 if target_config.pyqt_package is not None:
1225 p.add_option('--pyqt-sipdir', dest='pyqt_sip_dir', type='string',
1226 default=None, action='callback',
1227 callback=optparser_store_abspath_dir, metavar="DIR",
1228 help="the directory containing the PyQt .sip files is DIR "
1229 "[default: %s]" % target_config.pyqt_sip_dir)
1230
1231 p.add_option('--concatenate', '-c', dest='concat', default=False,
1232 action='store_true',
1233 help="concatenate the C++ source files")
1234 p.add_option('--concatenate-split', '-j', dest='split', type='int',
1235 default=1, metavar="N",
1236 help="split the concatenated C++ source files into N pieces "
1237 "[default: 1]")
1238 p.add_option('--static', '-k', dest='static', default=False,
1239 action='store_true',
1240 help="build a static %s" % pkg_name)
1241 p.add_option("--sysroot", dest='sysroot', type='string', action='callback',
1242 callback=optparser_store_abspath_dir, metavar="DIR",
1243 help="DIR is the target system root directory")
1244 p.add_option('--no-docstrings', dest='no_docstrings', default=False,
1245 action='store_true',
1246 help="disable the generation of docstrings")
1247 p.add_option('--trace', '-r', dest='tracing', default=False,
1248 action='store_true',
1249 help="build %s with tracing enabled" % pkg_name)
1250 p.add_option('--debug', '-u', default=False, action='store_true',
1251 help="build %s with debugging symbols" % pkg_name)
1252 p.add_option('--verbose', '-w', dest='verbose', default=False,
1253 action='store_true',
1254 help="enable verbose output during configuration")
1255
1256 pkg_config.init_optparser(p, target_config)
1257
1258 return p
1259
1260
1261 def _has_stubs(pkg_config):
1262 """ See if a stub file for any of the modules will be generated.
1263 pkg_config is the package configuration.
1264 """
1265
1266 for module_config in pkg_config.modules:
1267 if module_config.pep484_stub_file:
1268 return True
1269
1270 return False
1271
1272
1273 def _inform_user(target_config, pkg_config):
1274 """ Tell the user the values that are going to be used. target_config is
1275 the target configuration. pkg_config is the package configuration.
1276 """
1277
1278 pkg_name = pkg_config.descriptive_name
1279
1280 inform("Configuring %s %s..." % (pkg_name, pkg_config.version))
1281
1282 pkg_config.inform_user(target_config)
1283
1284 inform("%s will be installed in %s." %
1285 (pkg_name, target_config.module_dir))
1286
1287 if target_config.debug:
1288 inform("A debug version of %s will be built." % pkg_name)
1289
1290 if target_config.py_debug:
1291 inform("A debug build of Python is being used.")
1292
1293 if target_config.pyqt_version_str != '':
1294 inform("PyQt %s is being used." % target_config.pyqt_version_str)
1295 else:
1296 inform("%s is being used." % target_config.pyqt_package)
1297
1298 if target_config.qt_version_str != '':
1299 inform("Qt %s is being used." % target_config.qt_version_str)
1300
1301 if target_config.sysroot != '':
1302 inform("The system root directory is %s." % target_config.sysroot)
1303
1304 inform("sip %s is being used." % target_config.sip_version_str)
1305 inform("The sip executable is %s." % target_config.sip)
1306
1307 if target_config.prot_is_public:
1308 inform("%s is being built with 'protected' redefined as 'public'." %
1309 pkg_name)
1310
1311 if target_config.stubs_dir != '':
1312 inform("The PEP 484 stubs will be installed in %s." %
1313 target_config.stubs_dir)
1314
1315 if pkg_config.qscintilla_api_file and target_config.api_dir != '':
1316 inform("The QScintilla API file will be installed in %s." %
1317 os.path.join(target_config.api_dir, 'api', 'python'))
1318
1319
1320 def _generate_code(target_config, opts, pkg_config, module_config):
1321 """ Generate the code for the module. target_config is the target
1322 configuration. opts are the command line options. pkg_config is the
1323 package configuration. module_config is the module configuration.
1324 """
1325
1326 inform(
1327 "Generating the C++ source for the %s module..." %
1328 module_config.name)
1329
1330 # Generate the code in a module-specific sub-directory.
1331 try:
1332 os.mkdir(module_config.name)
1333 except:
1334 pass
1335
1336 # Build the SIP command line.
1337 argv = [quote(target_config.sip)]
1338
1339 # Tell SIP if this is a debug build of Python (SIP v4.19.1 and later).
1340 if target_config.sip_version >= 0x041301 and target_config.py_debug:
1341 argv.append('-D')
1342
1343 # Add the module-specific flags.
1344 argv.extend(pkg_config.get_sip_flags(target_config))
1345
1346 if target_config.pyqt_package is not None:
1347 # Get the flags used for the main PyQt module.
1348 argv.extend(target_config.pyqt_sip_flags.split())
1349
1350 # Add the backstop version.
1351 argv.append('-B')
1352 argv.append('Qt_6_0_0' if target_config.pyqt_package == 'PyQt5'
1353 else 'Qt_5_0_0')
1354
1355 # Add PyQt's .sip files to the search path.
1356 argv.append('-I')
1357 argv.append(quote(target_config.pyqt_sip_dir))
1358
1359 if target_config.stubs_dir != '':
1360 # Generate the stub file.
1361 argv.append('-y')
1362 argv.append(quote(module_config.pep484_stub_file + '.pyi'))
1363
1364 if pkg_config.qscintilla_api_file and target_config.api_dir != '':
1365 # Generate the API file.
1366 argv.append('-a')
1367 argv.append(quote(module_config.name + '.api'))
1368
1369 if target_config.prot_is_public:
1370 argv.append('-P');
1371
1372 if not opts.no_docstrings:
1373 argv.append('-o');
1374
1375 if opts.concat:
1376 argv.append('-j')
1377 argv.append(str(opts.split))
1378
1379 if opts.tracing:
1380 argv.append('-r')
1381
1382 argv.append('-c')
1383 argv.append(os.path.abspath(module_config.name))
1384
1385 # This assumes that, for multi-module packages, all modules's .sip files
1386 # will be rooted in a common root directory.
1387 sip_file = module_config.get_sip_file(target_config)
1388
1389 head, tail = os.path.split(sip_file)
1390 while head:
1391 head, tail = os.path.split(head)
1392
1393 if tail != sip_file:
1394 argv.append('-I')
1395 argv.append(quote(tail))
1396
1397 argv.append(sip_file)
1398
1399 check_file = os.path.join(module_config.name,
1400 'sipAPI%s.h' % module_config.name)
1401 _remove_file(check_file)
1402
1403 _run_command(' '.join(argv), opts.verbose)
1404
1405 if not os.access(check_file, os.F_OK):
1406 error("Unable to create the C++ code.")
1407
1408 # Generate the .pro file.
1409 _generate_pro(target_config, opts, module_config)
1410
1411
1412 def _get_qt_qmake_config(qmake_config, qt_version):
1413 """ Return a dict of qmake configuration values for a specific Qt version.
1414 """
1415
1416 qt_qmake_config = {}
1417
1418 for name, value in qmake_config.items():
1419 name_parts = name.split(':')
1420 if len(name_parts) == 2 and name_parts[0] == qt_version:
1421 qt_qmake_config[name_parts[1]] = value
1422
1423 return qt_qmake_config
1424
1425
1426 def _write_qt_qmake_config(qt_qmake_config, pro):
1427 """ Write the qmake configuration values to a .pro file. """
1428
1429 for name in ('QT', 'CONFIG', 'DEFINES', 'INCLUDEPATH', 'LIBS'):
1430 value = qt_qmake_config.get(name)
1431 if value:
1432 pro.write(' %s += %s\n' % (name, value))
1433
1434
1435 def _generate_pro(target_config, opts, module_config):
1436 """ Generate the .pro file for the module. target_config is the target
1437 configuration. opts are the command line options. module_config is the
1438 module configuration.
1439 """
1440
1441 inform("Generating the .pro file for the %s module..." % module_config.name)
1442
1443 # Without the 'no_check_exist' magic the target.files must exist when qmake
1444 # is run otherwise the install and uninstall targets are not generated.
1445
1446 qmake_config = module_config.get_qmake_configuration(target_config)
1447
1448 pro = open(os.path.join(module_config.name, module_config.name + '.pro'),
1449 'w')
1450
1451 pro.write('TEMPLATE = lib\n')
1452
1453 qt = qmake_config.get('QT')
1454 if qt:
1455 pro.write('QT += %s\n' % qt)
1456
1457 pro.write('CONFIG += %s\n' % ('debug' if target_config.debug else 'release'))
1458 pro.write('CONFIG += %s\n' % ('staticlib' if opts.static else 'plugin plugin_bundle'))
1459
1460 config = qmake_config.get('CONFIG')
1461 if config:
1462 pro.write('CONFIG += %s\n' % config)
1463
1464 # Work around QTBUG-39300.
1465 pro.write('CONFIG -= android_install\n')
1466
1467 qt5_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt5')
1468 qt4_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt4')
1469
1470 if qt5_qmake_config or qt4_qmake_config:
1471 pro.write('''
1472 greaterThan(QT_MAJOR_VERSION, 4) {
1473 ''')
1474
1475 if qt5_qmake_config:
1476 _write_qt_qmake_config(qt5_qmake_config, pro)
1477
1478 if qt4_qmake_config:
1479 pro.write('} else {\n')
1480 _write_qt_qmake_config(qt4_qmake_config, pro)
1481
1482 pro.write('}\n')
1483
1484 mname = module_config.name
1485
1486 pro.write('TARGET = %s\n' % mname)
1487
1488 if not opts.static:
1489 pro.write('''
1490 win32 {
1491 PY_MODULE = %s.pyd
1492 PY_MODULE_SRC = $(DESTDIR_TARGET)
1493
1494 LIBS += -L%s
1495 } else {
1496 PY_MODULE = %s.so
1497
1498 macx {
1499 PY_MODULE_SRC = $(TARGET).plugin/Contents/MacOS/$(TARGET)
1500
1501 QMAKE_LFLAGS += "-undefined dynamic_lookup"
1502
1503 equals(QT_MAJOR_VERSION, 5) {
1504 equals(QT_MINOR_VERSION, 5) {
1505 QMAKE_RPATHDIR += $$[QT_INSTALL_LIBS]
1506 }
1507 }
1508 } else {
1509 PY_MODULE_SRC = $(TARGET)
1510 }
1511 }
1512
1513 QMAKE_POST_LINK = $(COPY_FILE) $$PY_MODULE_SRC $$PY_MODULE
1514
1515 target.CONFIG = no_check_exist
1516 target.files = $$PY_MODULE
1517 ''' % (mname, quote(target_config.py_pylib_dir), mname))
1518
1519 pro.write('''
1520 target.path = %s
1521 INSTALLS += target
1522 ''' % quote(target_config.module_dir))
1523
1524 sip_installs = module_config.get_sip_installs(target_config)
1525 if sip_installs is not None:
1526 path, files = sip_installs
1527
1528 pro.write('''
1529 sip.path = %s
1530 sip.files =''' % quote(path))
1531
1532 for f in files:
1533 pro.write(' \\\n ../%s' % f)
1534
1535 pro.write('''
1536 INSTALLS += sip
1537 ''')
1538
1539 pro.write('\n')
1540
1541 # These optimisations could apply to other platforms.
1542 if module_config.no_exceptions:
1543 if target_config.py_platform.startswith('linux') or target_config.py_platform == 'darwin':
1544 pro.write('QMAKE_CXXFLAGS += -fno-exceptions\n')
1545
1546 if target_config.py_platform.startswith('linux') and not opts.static:
1547 if target_config.py_version >= 0x030000:
1548 entry_point = 'PyInit_%s' % mname
1549 else:
1550 entry_point = 'init%s' % mname
1551
1552 exp = open(os.path.join(mname, mname + '.exp'), 'wt')
1553 exp.write('{ global: %s; local: *; };' % entry_point)
1554 exp.close()
1555
1556 pro.write('QMAKE_LFLAGS += -Wl,--version-script=%s.exp\n' % mname)
1557
1558 if target_config.prot_is_public:
1559 pro.write('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public\n')
1560
1561 defines = qmake_config.get('DEFINES')
1562 if defines:
1563 pro.write('DEFINES += %s\n' % defines)
1564
1565 includepath = qmake_config.get('INCLUDEPATH')
1566 if includepath:
1567 pro.write('INCLUDEPATH += %s\n' % includepath)
1568
1569 # Make sure the SIP include directory is searched before the Python include
1570 # directory if they are different.
1571 pro.write('INCLUDEPATH += %s\n' % quote(target_config.sip_inc_dir))
1572 if target_config.py_inc_dir != target_config.sip_inc_dir:
1573 pro.write('INCLUDEPATH += %s\n' % quote(target_config.py_inc_dir))
1574
1575 libs = qmake_config.get('LIBS')
1576 if libs:
1577 pro.write('LIBS += %s\n' % libs)
1578
1579 if not opts.static:
1580 dylib = module_config.get_mac_wrapped_library_file(target_config)
1581
1582 if dylib:
1583 pro.write('''
1584 macx {
1585 QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\\\n\\\\t)$$quote(install_name_tool -change %s %s $$PY_MODULE)
1586 }
1587 ''' % (os.path.basename(dylib), dylib))
1588
1589 pro.write('\n')
1590 pro.write('HEADERS = sipAPI%s.h\n' % mname)
1591
1592 pro.write('SOURCES =')
1593 for s in os.listdir(module_config.name):
1594 if s.endswith('.cpp'):
1595 pro.write(' \\\n %s' % s)
1596 pro.write('\n')
1597
1598 pro.close()
1599
1600
1601 def _run_qmake(target_config, verbose, pro_name):
1602 """ Run qmake against a .pro file. target_config is the target
1603 configuration. verbose is set if the output is to be displayed. pro_name
1604 is the name of the .pro file.
1605 """
1606
1607 inform("Generating the Makefiles...")
1608
1609 # qmake doesn't behave consistently if it is not run from the directory
1610 # containing the .pro file - so make sure it is.
1611 pro_dir, pro_file = os.path.split(pro_name)
1612 if pro_dir != '':
1613 cwd = os.getcwd()
1614 os.chdir(pro_dir)
1615 else:
1616 cwd = None
1617
1618 mf = 'Makefile'
1619
1620 _remove_file(mf)
1621
1622 args = [quote(target_config.qmake)]
1623
1624 # Make sure all Makefiles are generated now in case qmake has been
1625 # configured with environment variables.
1626 args.append('-recursive')
1627
1628 if target_config.qmake_spec != '':
1629 args.append('-spec')
1630 args.append(target_config.qmake_spec)
1631
1632 args.append(pro_file)
1633
1634 _run_command(' '.join(args), verbose)
1635
1636 if not os.access(mf, os.F_OK):
1637 error(
1638 "%s failed to create a Makefile from %s." %
1639 (target_config.qmake, pro_name))
1640
1641 # Restore the current directory.
1642 if cwd is not None:
1643 os.chdir(cwd)
1644
1645
1646 def _run_command(cmd, verbose):
1647 """ Run a command and display the output if requested. cmd is the command
1648 to run. verbose is set if the output is to be displayed.
1649 """
1650
1651 if verbose:
1652 sys.stdout.write(cmd + "\n")
1653
1654 fout = _get_command_output(cmd)
1655
1656 # Read stdout and stderr until there is no more output.
1657 lout = fout.readline()
1658 while lout:
1659 if verbose:
1660 if sys.hexversion >= 0x03000000:
1661 sys.stdout.write(str(lout, encoding=sys.stdout.encoding))
1662 else:
1663 sys.stdout.write(lout)
1664
1665 lout = fout.readline()
1666
1667 fout.close()
1668
1669 try:
1670 os.wait()
1671 except:
1672 pass
1673
1674
1675 def _get_command_output(cmd):
1676 """ Return a pipe from which a command's output can be read. cmd is the
1677 command.
1678 """
1679
1680 try:
1681 import subprocess
1682 except ImportError:
1683 _, sout = os.popen4(cmd)
1684
1685 return sout
1686
1687 p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
1688 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1689
1690 return p.stdout
1691
1692
1693 def _remove_file(fname):
1694 """ Remove a file which may or may not exist. fname is the name of the
1695 file.
1696 """
1697
1698 try:
1699 os.remove(fname)
1700 except OSError:
1701 pass
1702
1703
1704 def _check_sip(target_config, pkg_config):
1705 """ Check that the version of sip is good enough. target_config is the
1706 target configuration. pkg_config is the package configuration.
1707 """
1708
1709 if target_config.sip is None:
1710 error(
1711 "Make sure you have a working sip on your PATH or use the "
1712 "--sip argument to explicitly specify a working sip.")
1713
1714 pipe = os.popen(' '.join([quote(target_config.sip), '-V']))
1715
1716 for l in pipe:
1717 version_str = l.strip()
1718 break
1719 else:
1720 error("'%s -V' did not generate any output." % target_config.sip)
1721
1722 pipe.close()
1723
1724 if '.dev' in version_str or 'snapshot' in version_str:
1725 version = 0
1726 else:
1727 version = version_from_string(version_str)
1728 if version is None:
1729 error(
1730 "'%s -V' generated unexpected output: '%s'." % (
1731 target_config.sip, version_str))
1732
1733 min_sip_version = pkg_config.minimum_sip_version
1734 if min_sip_version:
1735 min_version = version_from_string(min_sip_version)
1736 if version < min_version:
1737 error(
1738 "This version of %s requires sip %s or later." %
1739 (pkg_config.descriptive_name, min_sip_version))
1740
1741 target_config.sip_version = version
1742 target_config.sip_version_str = version_str
1743
1744
1745 def _main(argv, pkg_config):
1746 """ Create the configured package. argv is the list of command line
1747 arguments. pkg_config is the package configuration.
1748 """
1749
1750 # Create the default target configuration.
1751 target_config = _TargetConfiguration(pkg_config)
1752
1753 # Parse the command line.
1754 p = _create_optparser(target_config, pkg_config)
1755 opts, args = p.parse_args()
1756
1757 if args:
1758 p.print_help()
1759 sys.exit(2)
1760
1761 target_config.apply_pre_options(opts)
1762
1763 # Query qmake for the basic configuration information.
1764 target_config.get_qt_configuration(opts)
1765
1766 # Update the target configuration.
1767 if pkg_config.user_configuration_file_is_supported:
1768 config_file = opts.config_file
1769 else:
1770 config_file = None
1771
1772 if config_file is not None:
1773 target_config.update_from_configuration_file(config_file)
1774 else:
1775 target_config.apply_sysroot()
1776
1777 target_config.apply_post_options(opts, pkg_config)
1778
1779 if target_config.pyqt_package is not None:
1780 if target_config.pyqt_sip_flags is None:
1781 target_config.introspect_pyqt(pkg_config)
1782
1783 # Check SIP is new enough.
1784 _check_sip(target_config, pkg_config)
1785
1786 # Perform any package specific checks now that all other information has
1787 # been captured.
1788 pkg_config.check_package(target_config)
1789
1790 # Tell the user what's been found.
1791 _inform_user(target_config, pkg_config)
1792
1793 # Allow for module specific hacks.
1794 pkg_config.pre_code_generation(target_config)
1795
1796 # Generate the code.
1797 for module_config in pkg_config.modules:
1798 _generate_code(target_config, opts, pkg_config, module_config)
1799
1800 # Concatenate any .api files.
1801 if pkg_config.qscintilla_api_file and target_config.api_dir != '':
1802 inform("Generating the QScintilla API file...")
1803 f = open(pkg_config.qscintilla_api_file + '.api', 'w')
1804
1805 for module_config in pkg_config.modules:
1806 api = open(module_config.name + '.api')
1807
1808 for l in api:
1809 if target_config.pyqt_package is not None:
1810 l = target_config.pyqt_package + '.' + l
1811
1812 f.write(l)
1813
1814 api.close()
1815 os.remove(module_config.name + '.api')
1816
1817 f.close()
1818
1819 # Generate the top-level .pro file.
1820 inform("Generating the top-level .pro file...")
1821
1822 pro_name = pkg_config.descriptive_name + '.pro'
1823 pro = open(pro_name, 'w')
1824
1825 pro.write('''TEMPLATE = subdirs
1826 CONFIG += ordered nostrip
1827 SUBDIRS = %s
1828 ''' % ' '.join([module.name for module in pkg_config.modules]))
1829
1830 if target_config.stubs_dir != '':
1831 stubs = [module.pep484_stub_file + '.pyi' for module in pkg_config.modules if module.pep484_stub_file]
1832
1833 if stubs:
1834 pro.write('''
1835 pep484_stubs.path = %s
1836 pep484_stubs.files = %s
1837 INSTALLS += pep484_stubs
1838 ''' % (target_config.stubs_dir, ' '.join(stubs)))
1839
1840 if pkg_config.qscintilla_api_file and target_config.api_dir != '':
1841 pro.write('''
1842 api.path = %s/api/python
1843 api.files = %s.api
1844 INSTALLS += api
1845 ''' % (target_config.api_dir, pkg_config.qscintilla_api_file))
1846
1847 pro.close()
1848
1849 # Generate the Makefile.
1850 _run_qmake(target_config, opts.verbose, pro_name)
1851
1852
1853 ###############################################################################
1854 # The script starts here.
1855 ###############################################################################
1856
1857 if __name__ == '__main__':
1858 # Assume the product is a package containing multiple modules. If it isn't
1859 # then create a dummy package containing the single module.
1860 try:
1861 pkg_config_type = PackageConfiguration
1862 except NameError:
1863 pkg_config_type = type('PackageConfiguration', (object, ), {})
1864
1865 if not hasattr(pkg_config_type, 'modules'):
1866 mod_config_type = ModuleConfiguration
1867
1868 # Extract the package-specific attributes and methods.
1869 pkg_config_type.descriptive_name = mod_config_type.descriptive_name
1870 pkg_config_type.legacy_configuration_script = mod_config_type.legacy_configuration_script
1871 pkg_config_type.minimum_sip_version = mod_config_type.minimum_sip_version
1872 pkg_config_type.protected_is_public_is_supported = mod_config_type.protected_is_public_is_supported
1873 pkg_config_type.pyqt4_is_supported = mod_config_type.pyqt4_is_supported
1874 pkg_config_type.pyqt5_is_supported = mod_config_type.pyqt5_is_supported
1875 pkg_config_type.pyqt5_is_default = mod_config_type.pyqt5_is_default
1876 pkg_config_type.qscintilla_api_file = mod_config_type.qscintilla_api_file
1877 pkg_config_type.support_email_address = mod_config_type.support_email_address
1878 pkg_config_type.user_configuration_file_is_supported = mod_config_type.user_configuration_file_is_supported
1879 pkg_config_type.user_pyqt_sip_flags_is_supported = mod_config_type.user_pyqt_sip_flags_is_supported
1880 pkg_config_type.version = mod_config_type.version
1881
1882 pkg_config_type.init_target_configuration = staticmethod(
1883 mod_config_type.init_target_configuration)
1884 pkg_config_type.init_optparser = staticmethod(
1885 mod_config_type.init_optparser)
1886 pkg_config_type.apply_options = staticmethod(
1887 mod_config_type.apply_options)
1888 pkg_config_type.inform_user = staticmethod(
1889 mod_config_type.inform_user)
1890 pkg_config_type.pre_code_generation = staticmethod(
1891 mod_config_type.pre_code_generation)
1892 pkg_config_type.get_sip_flags = staticmethod(
1893 mod_config_type.get_sip_flags)
1894
1895 # Note the name change.
1896 pkg_config_type.check_package = staticmethod(
1897 mod_config_type.check_module)
1898
1899 pkg_config_type.modules = [mod_config_type()]
1900
1901 pkg_config = pkg_config_type()
1902
1903 try:
1904 _main(sys.argv, pkg_config)
1905 except SystemExit:
1906 raise
1907 except:
1908 if pkg_config.support_email_address:
1909 sys.stderr.write(
1910 """An internal error occured. Please report all the output from the program,
1911 including the following traceback, to %s.
1912 """ % pkg_config.support_email_address)
1913
1914 raise
0 import sys
1
2 from PyQt5 import QtCore, QtGui, QtWidgets
3 import QtAV, QtAVWidgets
4
5
6 class PlayerWindow(QtWidgets.QWidget):
7 def __init__(self, parent = None):
8 QtWidgets.QWidget.__init__(self, parent)
9
10 self.m_unit = 1000.0
11 self.setWindowTitle("QtAV simple player example")
12 self.m_player = QtAV.AVPlayer(self)
13 vl = QtWidgets.QVBoxLayout()
14 self.setLayout(vl)
15 self.m_vo = QtAV.VideoOutput(self)
16
17 if self.m_vo.widget() is None:
18 QtWidgets.QMessageBox.warning(0, "QtAV error", "Can not create video renderer")
19 return
20
21 self.m_player.setRenderer(self.m_vo)
22 vl.addWidget(self.m_vo.widget())
23 self.m_slider = QtWidgets.QSlider()
24 self.m_slider.setOrientation(QtCore.Qt.Horizontal)
25 self.m_slider.sliderMoved[int].connect(self.seekBySliderVal)
26 self.m_slider.sliderPressed.connect(self.seekBySlider)
27 self.m_player.positionChanged.connect(self.updateSliderVal)
28 self.m_player.started.connect(self.updateSlider)
29 self.m_player.notifyIntervalChanged.connect(self.updateSliderUnit)
30
31 vl.addWidget(self.m_slider)
32 hb = QtWidgets.QHBoxLayout()
33 vl.addLayout(hb)
34 self.m_openBtn = QtWidgets.QPushButton("Open")
35 self.m_playBtn = QtWidgets.QPushButton("Play/Pause")
36 self.m_stopBtn = QtWidgets.QPushButton("Stop")
37 hb.addWidget(self.m_openBtn)
38 hb.addWidget(self.m_playBtn)
39 hb.addWidget(self.m_stopBtn)
40 self.m_openBtn.clicked.connect(self.openMedia)
41 self.m_playBtn.clicked.connect(self.playPause)
42 self.m_stopBtn.clicked.connect(self.m_player.stop)
43
44 def openMedia(self):
45 fname = QtWidgets.QFileDialog.getOpenFileName(None, "Open a video")[0]
46 if not fname:
47 return
48 self.m_player.play(fname)
49
50 def seekBySliderVal(self, value):
51 if not self.m_player.isPlaying():
52 return
53 self.m_player.seek(int(value * self.m_unit))
54
55 def seekBySlider(self):
56 self.seekBySliderVal(self.m_slider.value())
57
58 def playPause(self):
59 if not self.m_player.isPlaying():
60 self.m_player.play()
61 return
62 self.m_player.pause(not self.m_player.isPaused())
63
64 def updateSliderVal(self, value):
65 self.m_slider.setRange(0, int(self.m_player.duration() / self.m_unit))
66 self.m_slider.setValue(int(value / self.m_unit))
67
68 def updateSlider(self):
69 self.updateSliderVal(self.m_player.position())
70
71 def updateSliderUnit(self):
72 self.m_unit = float(self.m_player.notifyInterval())
73 self.updateSlider()
74
75
76 if __name__ == "__main__":
77 QtAVWidgets.registerRenderers()
78 app = QtWidgets.QApplication(sys.argv)
79 player = PlayerWindow()
80 player.show()
81 player.resize(800, 600)
82 app.exec_()
0 namespace QtAV {
1
2 class AVError
3 {
4 %TypeHeaderCode
5 #include <QtAV/AVError.h>
6 %End
7
8 public:
9 enum ErrorCode {
10 NoError,
11
12 //open/read/seek network stream error. value must be less then ResourceError because of correct_error_by_ffmpeg
13 NetworkError, // all above and before NoError are NetworkError
14
15 OpenTimedout,
16 OpenError,
17 ParseStreamTimedOut,
18 FindStreamInfoTimedout = ParseStreamTimedOut,
19 ParseStreamError,
20 FindStreamInfoError = ParseStreamError,
21 StreamNotFound, //a,v,s?
22 ReadTimedout,
23 ReadError,
24 SeekError,
25 ResourceError, // all above and before NetworkError are ResourceError
26
27 OpenCodecError,
28 CloseCodecError,
29 AudioCodecNotFound,
30 VideoCodecNotFound,
31 SubtitleCodecNotFound,
32 CodecError, // all above and before NoError are CodecError
33
34 FormatError, // all above and before CodecError are FormatError
35
36 // decrypt error. Not implemented
37 AccessDenied, // all above and before NetworkError are AccessDenied
38
39 UnknowError
40 };
41
42 AVError();
43 AVError(ErrorCode code, int ffmpegError = 0);
44 AVError(ErrorCode code, const QString& detail, int ffmpegError = 0);
45 AVError(const AVError& other);
46
47 //AVError &operator=(const AVError &other);
48 bool operator==(const AVError &other) const;
49 bool operator!=(const AVError &other) const;
50
51 void setError(ErrorCode ec);
52 ErrorCode error() const;
53 QString string() const;
54
55 int ffmpegErrorCode() const;
56 QString ffmpegErrorString() const;
57 };
58
59 };
0 namespace QtAV
1 {
2
3 class AVOutput
4 {
5 %TypeHeaderCode
6 #include <QtAV/AVOutput.h>
7 %End
8
9 public:
10 explicit AVOutput();
11 virtual ~AVOutput();
12
13 private:
14 AVOutput(const AVOutput& o);
15 };
16
17 };
0 namespace QtAV
1 {
2
3 class AVPlayer : public QObject
4 {
5 %TypeHeaderCode
6 #include <QtAV/AVPlayer.h>
7 %End
8
9 public:
10 enum State {
11 StoppedState,
12 PlayingState,
13 PausedState
14 };
15
16 static const QStringList& supportedProtocols();
17
18 explicit AVPlayer(QObject *parent /TransferThis/ = 0);
19 ~AVPlayer();
20
21 // AVClock* masterClock();
22 void setFile(const QString& path);
23 QString file() const;
24 void setIODevice(QIODevice* device);
25 // void setInput(MediaIO* in);
26 // MediaIO* input() const;
27
28 bool isLoaded() const;
29 void setAsyncLoad(bool value = true);
30 bool isAsyncLoad() const;
31 void setAutoLoad(bool value = true); // NOT implemented
32 bool isAutoLoad() const; // NOT implemented
33
34 MediaStatus mediaStatus() const;
35 bool relativeTimeMode() const;
36
37 qint64 absoluteMediaStartPosition() const;
38 qreal durationF() const;
39 qint64 duration() const;
40 qint64 mediaStartPosition() const;
41 qint64 mediaStopPosition() const;
42 qreal mediaStartPositionF() const;
43 qint64 startPosition() const;
44 qint64 stopPosition() const;
45 qint64 position() const;
46 int repeat() const;
47 int currentRepeat() const;
48 bool setExternalAudio(const QString& file);
49 QString externalAudio() const;
50 const QVariantList& externalAudioTracks() const;
51 const QVariantList& internalAudioTracks() const;
52 const QVariantList& internalVideoTracks() const;
53 bool setAudioStream(const QString& file, int n = 0);
54 bool setAudioStream(int n);
55 bool setVideoStream(int n);
56 const QVariantList& internalSubtitleTracks() const;
57 bool setSubtitleStream(int n);
58 int currentAudioStream() const;
59 int currentVideoStream() const;
60 int currentSubtitleStream() const;
61 int audioStreamCount() const;
62 int videoStreamCount() const;
63 int subtitleStreamCount() const;
64 // VideoCapture *videoCapture() const;
65 void play(const QString& path);
66 bool isPlaying() const;
67 bool isPaused() const;
68 State state() const;
69 void setState(State value);
70
71 void addVideoRenderer(VideoRenderer *renderer);
72 void removeVideoRenderer(VideoRenderer *renderer);
73 void clearVideoRenderers();
74 void setRenderer(VideoRenderer* renderer);
75 VideoRenderer* renderer();
76 QList<QtAV::VideoRenderer*> videoOutputs();
77 // AudioOutput* audio();
78 void setSpeed(qreal speed);
79 qreal speed() const;
80
81 void setInterruptTimeout(qint64 ms);
82 qint64 interruptTimeout() const;
83 void setInterruptOnTimeout(bool value);
84 bool isInterruptOnTimeout() const;
85 void setFrameRate(qreal value);
86 qreal forcedFrameRate() const;
87 // const Statistics& statistics() const;
88 // bool installFilter(AudioFilter* filter, int index = 0x7FFFFFFF);
89 // bool installFilter(VideoFilter* filter, int index = 0x7FFFFFFF);
90 // bool uninstallFilter(AudioFilter* filter);
91 // bool uninstallFilter(VideoFilter* filter);
92 // QList<Filter*> audioFilters() const;
93 // QList<Filter*> videoFilters() const;
94 // void setPriority(const QVector<QtAV::VideoDecoderId>& ids);
95 void setVideoDecoderPriority(const QStringList& names);
96 QStringList videoDecoderPriority() const;
97 //void setPriority(const QVector<AudioOutputId>& ids);
98 int brightness() const;
99 int contrast() const;
100 int hue() const; //not implemented
101 int saturation() const;
102 unsigned int chapters() const;
103 void setOptionsForFormat(const QVariantHash &dict);
104 QVariantHash optionsForFormat() const;
105 void setOptionsForAudioCodec(const QVariantHash &dict);
106 QVariantHash optionsForAudioCodec() const;
107 void setOptionsForVideoCodec(const QVariantHash& dict);
108 QVariantHash optionsForVideoCodec() const;
109
110 // MediaEndAction mediaEndAction() const;
111 // void setMediaEndAction(MediaEndAction value);
112
113
114
115 public Q_SLOTS:
116 bool load();
117
118 void togglePause();
119 void pause(bool p = true);
120 void play();
121 void stop();
122 void stepForward();
123 void stepBackward();
124
125 void setRelativeTimeMode(bool value);
126 void setRepeat(int max);
127 void setStartPosition(qint64 pos);
128 // void setStopPosition(qint64 pos = std::numeric_limits<qint64>::max());
129 // void setTimeRange(qint64 start, qint64 stop = std::numeric_limits<qint64>::max());
130
131 bool isSeekable() const;
132 void setPosition(qint64 position);
133 void seek(qreal r); // r: [0, 1]
134 void seek(qint64 pos); //ms. same as setPosition(pos)
135 void seekForward();
136 void seekBackward();
137 void seekNextChapter();
138 void seekPreviousChapter();
139 // void setSeekType(SeekType type);
140 // SeekType seekType() const;
141
142 qreal bufferProgress() const;
143 qreal bufferSpeed() const;
144 qint64 buffered() const;
145 // void setBufferMode(BufferMode mode);
146 // BufferMode bufferMode() const;
147 void setBufferValue(qint64 value);
148 int bufferValue() const;
149
150 void setNotifyInterval(int msec);
151 int notifyInterval() const;
152 void updateClock(qint64 msecs); //update AVClock's external clock
153 void setBrightness(int val);
154 void setContrast(int val);
155 void setHue(int val); //not implemented
156 void setSaturation(int val);
157
158 Q_SIGNALS:
159 void bufferProgressChanged(qreal);
160 void relativeTimeModeChanged();
161 void autoLoadChanged();
162 void asyncLoadChanged();
163 void muteChanged();
164 void sourceChanged();
165 void loaded(); // == mediaStatusChanged(QtAV::LoadedMedia)
166 void mediaStatusChanged(QtAV::MediaStatus status);
167 // void mediaEndActionChanged(QtAV::MediaEndAction action);
168 void durationChanged(qint64);
169 // void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax
170 void paused(bool p);
171 void started();
172 void stopped();
173 void stoppedAt(qint64 position);
174 void stateChanged(QtAV::AVPlayer::State state);
175 void speedChanged(qreal speed);
176 void repeatChanged(int r);
177 void currentRepeatChanged(int r);
178 void startPositionChanged(qint64 position);
179 void stopPositionChanged(qint64 position);
180 void seekableChanged();
181 void seekFinished(qint64 position);
182 void positionChanged(qint64 position);
183 void interruptTimeoutChanged();
184 void interruptOnTimeoutChanged();
185 void notifyIntervalChanged();
186 void brightnessChanged(int val);
187 void contrastChanged(int val);
188 void hueChanged(int val);
189 void saturationChanged(int val);
190 void chaptersChanged(unsigned int val);
191 void subtitleStreamChanged(int value);
192 void internalAudioTracksChanged(const QVariantList& tracks);
193 void internalVideoTracksChanged(const QVariantList& tracks);
194 void externalAudioTracksChanged(const QVariantList& tracks);
195 void internalSubtitleTracksChanged(const QVariantList& tracks);
196 void internalSubtitleHeaderRead(const QByteArray& codec, const QByteArray& data);
197 // void internalSubtitlePacketRead(int track, const QtAV::Packet& packet);
198
199 protected:
200 virtual void timerEvent(QTimerEvent *);
201 };
202
203 };
0 namespace QtAV
1 {
2
3 class Frame
4 {
5 %TypeHeaderCode
6 #include <QtAV/Frame.h>
7 %End
8
9 public:
10 Frame(const Frame& other);
11 virtual ~Frame() = 0;
12
13 // NOTE: We don't expose the assignment operators.
14 //Frame& operator =(const Frame &other);
15
16 int planeCount() const;
17 virtual int channelCount() const;
18
19 //int bytesPerLine(int plane = 0) const;
20 //QByteArray frameData() const;
21 //int dataAlignment() const;
22 //uchar* frameDataPtr(int* size = NULL) const;
23 //QByteArray data(int plane = 0) const;
24 //uchar* bits(int plane = 0);
25 //const uchar *bits(int plane = 0) const;
26 //const uchar* constBits(int plane = 0) const;
27 //void setBits(uchar *b, int plane = 0);
28 //void setBits(const QVector<uchar*>& b);
29 //void setBits(quint8 *slice[]);
30
31 //void setBytesPerLine(int lineSize, int plane = 0);
32 //void setBytesPerLine(const QVector<int>& lineSize);
33 //void setBytesPerLine(int stride[]);
34
35 QVariantMap availableMetaData() const;
36 QVariant metaData(const QString& key) const;
37 void setMetaData(const QString &key, const QVariant &value);
38 void setTimestamp(qreal ts);
39 qreal timestamp() const;
40 void swap(Frame &other);
41 };
42
43 };
0 namespace QtAV
1 {
2
3 class OpenGLRendererBase : public QtAV::VideoRenderer
4 {
5 %TypeHeaderCode
6 #include <QtAV/OpenGLRendererBase.h>
7 %End
8
9 public:
10 virtual ~OpenGLRendererBase();
11 bool isSupported(VideoFormat::PixelFormat pixfmt) const;
12 OpenGLVideo* opengl() const;
13
14 protected:
15 virtual bool receiveFrame(const VideoFrame& frame);
16 virtual void drawBackground();
17 virtual void drawFrame();
18 void onInitializeGL();
19 void onPaintGL();
20 void onResizeGL(int w, int h);
21 void onResizeEvent(int w, int h);
22 void onShowEvent();
23
24 private:
25 OpenGLRendererBase();
26 };
27
28 };
0 namespace QtAV
1 {
2
3 class OpenGLVideo : public QObject
4 {
5 %TypeHeaderCode
6 #include <QtAV/OpenGLVideo.h>
7 %End
8
9 public:
10 enum MeshType {
11 RectMesh,
12 SphereMesh
13 };
14
15 static bool isSupported(VideoFormat::PixelFormat pixfmt);
16 OpenGLVideo();
17 void setOpenGLContext(QOpenGLContext *ctx);
18 QOpenGLContext* openGLContext();
19 void setCurrentFrame(const VideoFrame& frame);
20 void fill(const QColor& color);
21 void render(const QRectF& target = QRectF(), const QRectF& roi = QRectF(), const QMatrix4x4& transform = QMatrix4x4());
22 void setProjectionMatrixToRect(const QRectF& v);
23 void setViewport(const QRectF& r);
24
25 void setBrightness(qreal value);
26 void setContrast(qreal value);
27 void setHue(qreal value);
28 void setSaturation(qreal value);
29
30 // TODO: Add after we expose VideoShader
31 //void setUserShader(VideoShader* shader);
32 //VideoShader* userShader() const;
33
34 void setMeshType(MeshType value);
35 MeshType meshType() const;
36 signals:
37 void beforeRendering();
38 void afterRendering();
39 };
40
41 };
0 namespace QtAV
1 {
2
3 class QPainterRenderer : public QtAV::VideoRenderer /Abstract/
4 {
5 %TypeHeaderCode
6 #include <QtAV/QPainterRenderer.h>
7 %End
8
9 public:
10 QPainterRenderer();
11 bool isSupported(VideoFormat::PixelFormat pixfmt) const;
12 protected:
13 bool preparePixmap(const VideoFrame& frame);
14 void drawBackground();
15 void drawFrame();
16 };
17
18 };
0 %Module(name=QtAV, keyword_arguments="Optional")
1
2 %Import QtGui/QtGuimod.sip
3 %Import QtWidgets/QtWidgetsmod.sip
4 %Import QtOpenGL/QtOpenGLmod.sip
5
6 %Timeline {QtAV_1_11_0 QtAV_1_12_0}
7
8 %Feature FULL_API
9
10 %Copying
11 QtAV: Multimedia framework based on Qt and FFmpeg
12 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
13
14 This file is part of QtAV (from 2015)
15
16 This library is free software; you can redistribute it and/or
17 modify it under the terms of the GNU Lesser General Public
18 License as published by the Free Software Foundation; either
19 version 2.1 of the License, or (at your option) any later version.
20
21 This library is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 Lesser General Public License for more details.
25
26 You should have received a copy of the GNU Lesser General Public
27 License along with this library; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 %End
30
31 %HideNamespace QtAV
32
33 %Include global.sip
34 %Include VideoOutput.sip
35 %Include AVError.sip
36 %Include AVPlayer.sip
37
38 %If (FULL_API)
39 %Include VideoFormat.sip
40 %Include Frame.sip
41 %Include VideoFrame.sip
42 %Include QPainterRenderer.sip
43 %Include AVOutput.sip
44 %Include VideoRenderer.sip
45 %Include OpenGLRendererBase.sip
46 %Include OpenGLVideo.sip
47 %End
0 namespace QtAV
1 {
2
3 class VideoFormat
4 {
5 %TypeHeaderCode
6 #include <QtAV/VideoFormat.h>
7 %End
8
9 public:
10 enum PixelFormat {
11 Format_Invalid = -1,
12 Format_ARGB32, // AARRGGBB or 00RRGGBB, check hasAlpha is required
13 Format_BGRA32, //BBGGRRAA
14 Format_ABGR32, // QImage.RGBA8888 le
15 Format_RGBA32, // QImage. no
16 Format_RGB32, // 0xAARRGGBB native endian. same as QImage::Format_ARGB32. be: ARGB32, le: BGRA32
17 Format_BGR32, // 0xAABBGGRR native endian
18 Format_RGB24,
19 Format_BGR24,
20 Format_RGB565,
21 Format_BGR565,
22 Format_RGB555,
23 Format_BGR555,
24
25 //http://www.fourcc.org/yuv.php
26 Format_AYUV444,
27 Format_YUV444P,
28 Format_YUV422P,
29 Format_YUV420P,
30 Format_YUV411P,
31 Format_YUV410P,
32 Format_YV12,
33 Format_UYVY, //422
34 Format_VYUY, //not in ffmpeg. OMX_COLOR_FormatCrYCbY
35 Format_YUYV, //422, aka yuy2
36 Format_YVYU, //422
37 Format_NV12,
38 Format_NV21,
39 Format_IMC1,
40 Format_IMC2,
41 Format_IMC3, //same as IMC1, swap U V
42 Format_IMC4, //same as IMC2, swap U V
43 Format_Y8, //GREY. single 8 bit Y plane
44 Format_Y16, //single 16 bit Y plane. LE
45
46 Format_Jpeg, //yuvj
47
48 //Format_CameraRaw,
49 //Format_AdobeDng,
50
51 Format_YUV420P9LE,
52 Format_YUV422P9LE,
53 Format_YUV444P9LE,
54 Format_YUV420P10LE,
55 Format_YUV422P10LE,
56 Format_YUV444P10LE,
57 Format_YUV420P12LE,
58 Format_YUV422P12LE,
59 Format_YUV444P12LE,
60 Format_YUV420P14LE,
61 Format_YUV422P14LE,
62 Format_YUV444P14LE,
63 Format_YUV420P16LE,
64 Format_YUV422P16LE,
65 Format_YUV444P16LE,
66 Format_YUV420P9BE,
67 Format_YUV422P9BE,
68 Format_YUV444P9BE,
69 Format_YUV420P10BE,
70 Format_YUV422P10BE,
71 Format_YUV444P10BE,
72 Format_YUV420P12BE,
73 Format_YUV422P12BE,
74 Format_YUV444P12BE,
75 Format_YUV420P14BE,
76 Format_YUV422P14BE,
77 Format_YUV444P14BE,
78 Format_YUV420P16BE,
79 Format_YUV422P16BE,
80 Format_YUV444P16BE,
81
82 Format_RGB48, // native endian
83 Format_RGB48LE,
84 Format_RGB48BE,
85 Format_BGR48,
86 Format_BGR48LE,
87 Format_BGR48BE,
88 Format_RGBA64, //native endian
89 Format_RGBA64LE,
90 Format_RGBA64BE,
91 Format_BGRA64, //native endian
92 Format_BGRA64LE,
93 Format_BGRA64BE,
94
95 Format_VYU, // for rgb422_apple texture, the layout is like rgb24: (v, y, u, )
96 Format_XYZ12,
97 Format_XYZ12LE,
98 Format_XYZ12BE,
99 Format_User
100 };
101
102 static PixelFormat pixelFormatFromImageFormat(QImage::Format format);
103 static QImage::Format imageFormatFromPixelFormat(PixelFormat format);
104 static PixelFormat pixelFormatFromFFmpeg(int ff); //AVPixelFormat
105 static int pixelFormatToFFmpeg(PixelFormat fmt);
106 static QVector<int> pixelFormatsFFmpeg();
107
108 VideoFormat(PixelFormat format = Format_Invalid);
109 VideoFormat(int formatFF /Constrained/);
110 VideoFormat(QImage::Format fmt /Constrained/);
111 VideoFormat(const QString& name);
112 VideoFormat(const VideoFormat &other /Constrained/);
113 ~VideoFormat();
114
115 // NOTE: We don't expose the assignment operators.
116 //VideoFormat& operator=(const VideoFormat &other);
117 //VideoFormat& operator=(VideoFormat::PixelFormat pixfmt);
118 //VideoFormat& operator=(QImage::Format qpixfmt);
119 //VideoFormat& operator=(int ffpixfmt);
120 bool operator==(const VideoFormat &other) const;
121 bool operator==(VideoFormat::PixelFormat pixfmt /Constrained/) const;
122 bool operator==(QImage::Format qpixfmt /Constrained/) const;
123 bool operator==(int ffpixfmt /Constrained/) const;
124 bool operator!=(const VideoFormat &other) const;
125 bool operator!=(VideoFormat::PixelFormat pixfmt /Constrained/) const;
126 bool operator!=(QImage::Format qpixfmt /Constrained/) const;
127 bool operator!=(int ffpixfmt /Constrained/) const;
128
129 bool isValid() const;
130
131 PixelFormat pixelFormat() const;
132 int pixelFormatFFmpeg() const;
133 QImage::Format imageFormat() const;
134 QString name() const;
135
136 void setPixelFormat(PixelFormat format);
137 void setPixelFormatFFmpeg(int format);
138
139 int channels() const;
140 int channels(int plane) const;
141 int planeCount() const;
142 int bitsPerPixel() const;
143 /// nv12: 16 for uv plane
144 int bitsPerPixel(int plane) const;
145 /// bgr24 is 24 not 32
146 int bitsPerPixelPadded() const;
147 int bytesPerPixel() const;
148 int bytesPerPixel(int plane) const;
149 int bitsPerComponent() const;
150
151 int bytesPerLine(int width, int plane) const;
152 int chromaWidth(int lumaWidth) const;
153 int chromaHeight(int lumaHeight) const;
154 int width(int lumaWidth, int plane) const;
155 int height(int lumaHeight, int plane) const;
156 qreal normalizedWidth(int plane) const;
157 qreal normalizedHeight(int plane) const;
158 bool isBigEndian() const;
159 bool hasPalette() const;
160 bool isPseudoPaletted() const;
161 bool isBitStream() const;
162 bool isHWAccelerated() const;
163 bool isPlanar() const;
164 bool isRGB() const;
165 bool isXYZ() const;
166 bool hasAlpha() const;
167
168 static bool isPlanar(PixelFormat pixfmt);
169 static bool isRGB(PixelFormat pixfmt);
170 static bool hasAlpha(PixelFormat pixfmt);
171 };
172
173 //QDebug operator<<(QDebug debug, const VideoFormat &fmt);
174 //QDebug operator<<(QDebug debug, VideoFormat::PixelFormat pixFmt);
175
176 };
0 namespace QtAV
1 {
2
3 class VideoFrame : public QtAV::Frame
4 {
5 %TypeHeaderCode
6 #include <QtAV/VideoFrame.h>
7 %End
8
9 public:
10 //static VideoFrame fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized = true, bool swapUV = false);
11 //static void copyPlane(quint8 *dst, size_t dst_stride, const quint8 *src, size_t src_stride, unsigned byteWidth, unsigned height);
12
13 public:
14
15 VideoFrame();
16 VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray(), int alignment = 1);
17 VideoFrame(const QImage& image);
18 VideoFrame(const VideoFrame &other);
19 ~VideoFrame();
20
21 // NOTE: We don't expose the assignment operators.
22 //VideoFrame &operator=(const VideoFrame &other);
23
24 int channelCount() const;
25 VideoFrame clone() const;
26 VideoFormat format() const;
27 VideoFormat::PixelFormat pixelFormat() const;
28 QImage::Format imageFormat() const;
29 int pixelFormatFFmpeg() const;
30
31 bool isValid() const;
32 operator bool() const;
33
34 QSize size() const;
35 int width() const;
36 int height() const;
37 int effectiveBytesPerLine(int plane) const;
38 int planeWidth(int plane) const;
39 int planeHeight(int plane) const;
40 float displayAspectRatio() const;
41 void setDisplayAspectRatio(float displayAspectRatio);
42
43 // TODO: After we expose ColorSpace and ColorRange exposed.
44 //ColorSpace colorSpace() const;
45 //void setColorSpace(ColorSpace value);
46 //ColorRange colorRange() const;
47 //void setColorRange(ColorRange value);
48
49 QImage toImage(QImage::Format fmt = QImage::Format_ARGB32, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const;
50 VideoFrame to(VideoFormat::PixelFormat pixfmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const;
51 VideoFrame to(const VideoFormat& fmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const;
52
53 // TODO
54 //bool to(VideoFormat::PixelFormat pixfmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const;
55 //bool to(const VideoFormat& fmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const;
56
57 //void* map(SurfaceType type, void* handle, int plane = 0);
58 //void* map(SurfaceType type, void* handle, const VideoFormat& fmt, int plane = 0);
59
60 void unmap(void* handle);
61
62 //void* createInteropHandle(void* handle, SurfaceType type, int plane);
63 };
64
65
66 class VideoFrameConverter
67 {
68 public:
69 VideoFrameConverter();
70 ~VideoFrameConverter();
71 void setEq(int brightness, int contrast, int saturation);
72 VideoFrame convert(const VideoFrame& frame, const VideoFormat& fmt /Constrained/) const;
73 VideoFrame convert(const VideoFrame& frame, VideoFormat::PixelFormat fmt /Constrained/) const;
74 VideoFrame convert(const VideoFrame& frame, QImage::Format fmt /Constrained/) const;
75 VideoFrame convert(const VideoFrame& frame, int fffmt /Constrained/) const;
76 };
77
78 };
0 namespace QtAV
1 {
2
3 class VideoOutput : public QObject, public QtAV::VideoRenderer
4 {
5 %TypeHeaderCode
6 #include <QtAV/VideoOutput.h>
7 %End
8
9 public:
10 VideoOutput(QWidget* parent /TransferThis/ = 0);
11 VideoOutput(VideoRendererId rendererId, QObject *parent /TransferThis/ = 0);
12 ~VideoOutput();
13
14 VideoRendererId id() const;
15
16 virtual QWidget* widget() final;
17 };
18
19 };
0 namespace QtAV
1 {
2
3 typedef int VideoRendererId /NoTypeName/;
4
5 class VideoRenderer : public QtAV::AVOutput /Abstract/
6 {
7 %TypeHeaderCode
8 #include <QtAV/VideoRenderer.h>
9 %End
10
11 public:
12 enum OutAspectRatioMode {
13 RendererAspectRatio //Use renderer's aspect ratio, i.e. stretch to fit the renderer rect
14 , VideoAspectRatio //Use video's aspect ratio and align center in renderer.
15 , CustomAspectRation //Use the ratio set by setOutAspectRatio(qreal). Mode will be set to this if that function is called
16 //, AspectRatio4_3, AspectRatio16_9
17 };
18
19 VideoRenderer();
20 virtual ~VideoRenderer();
21 virtual VideoRendererId id() const = 0;
22
23 private:
24 VideoRenderer(const VideoRenderer& r);
25 };
26
27 };
0 %ModuleCode
1 #include <QtAV/QtAV_Global.h>
2 %End
3
4 namespace QtAV {
5
6 unsigned Version();
7 %MethodCode
8 ::QtAV_Version();
9 %End
10
11 QString Version_String();
12 %MethodCode
13 ::QtAV_Version_String();
14 %End
15
16 QString Version_String_Long();
17 %MethodCode
18 ::QtAV_Version_String_Long();
19 %End
20
21 enum LogLevel {
22 LogOff,
23 LogDebug, // log all
24 LogWarning, // log warning, critical, fatal
25 LogCritical, // log critical, fatal
26 LogFatal, // log fatal
27 LogAll
28 };
29 QString aboutFFmpeg_PlainText();
30 QString aboutFFmpeg_HTML();
31 QString aboutQtAV_PlainText();
32 QString aboutQtAV_HTML();
33 void setLogLevel(LogLevel value);
34 LogLevel logLevel();
35 //void setFFmpegLogHandler(void(*)(void *, int, const char *, va_list));
36 void setFFmpegLogLevel(const QByteArray& level);
37
38 QString avformatOptions();
39 QString avcodecOptions();
40
41 enum MediaStatus
42 {
43 UnknownMediaStatus,
44 NoMedia,
45 LoadingMedia, // when source is set
46 LoadedMedia, // if auto load and source is set. player is stopped state
47 StalledMedia, // insufficient buffering or other interruptions (timeout, user interrupt)
48 BufferingMedia, // NOT IMPLEMENTED
49 BufferedMedia, // when playing //NOT IMPLEMENTED
50 EndOfMedia, // Playback has reached the end of the current media. The player is in the StoppedState.
51 InvalidMedia // what if loop > 0 or stopPosition() is not mediaStopPosition()?
52 };
53
54 enum BufferMode {
55 BufferTime,
56 BufferBytes,
57 BufferPackets
58 };
59
60 enum MediaEndActionFlag {
61 MediaEndAction_Default,
62 MediaEndAction_KeepDisplay,
63 MediaEndAction_Pause
64 };
65
66 //Q_DECLARE_FLAGS(MediaEndAction, MediaEndActionFlag)
67
68 enum SeekUnit {
69 SeekByTime, // only this is supported now
70 SeekByByte,
71 SeekByFrame
72 };
73 enum SeekType {
74 AccurateSeek, // slow
75 KeyFrameSeek, // fast
76 AnyFrameSeek
77 };
78
79
80 enum ColorSpace {
81 ColorSpace_Unknown,
82 ColorSpace_RGB,
83 ColorSpace_GBR, // for planar gbr format(e.g. video from x264) used in glsl
84 ColorSpace_BT601,
85 ColorSpace_BT709,
86 ColorSpace_XYZ
87 };
88
89
90 enum ColorRange {
91 ColorRange_Unknown,
92 ColorRange_Limited, // TV, MPEG
93 ColorRange_Full // PC, JPEG
94 };
95
96
97 enum SurfaceType {
98 HostMemorySurface,
99 GLTextureSurface,
100 SourceSurface,
101 UserSurface = 0xffff
102 };
103
104 };
0 namespace QtAV
1 {
2
3 class GLWidgetRenderer2 : public QGLWidget, public QtAV::OpenGLRendererBase
4 {
5 %TypeHeaderCode
6 #include <QtAVWidgets/GLWidgetRenderer2.h>
7 %End
8
9 public:
10 void setBrightness(qreal brightness);
11 qreal brightness();
12 void setContrast(qreal contrast);
13 qreal contrast();
14 void setHue(qreal hue);
15 qreal hue();
16 void setSaturation(qreal saturation);
17 qreal saturation();
18 void setBackgroundColor(QColor color);
19 QColor backgroundColor();
20
21 QRectF regionOfInterest();
22 void setRegionOfInterest(QRectF region);
23 qreal sourceAspectRatio();
24 qreal outAspectRatio();
25 void setOutAspectRatio(qreal ratio);
26 OutAspectRatioMode outAspectRatioMode();
27 void setOutAspectRatioMode(OutAspectRatioMode mode);
28
29 int orientation();
30 void setOrientation(int orientation);
31 QRect videoRect();
32 QSize videoFrameSize();
33
34 public:
35 GLWidgetRenderer2(QWidget* parent /TransferThis/ = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0);
36 virtual VideoRendererId id();
37 virtual QWidget* widget();
38
39 signals:
40 void sourceAspectRatioChanged(qreal value) final;
41 void regionOfInterestChanged();
42 void outAspectRatioChanged();
43 void outAspectRatioModeChanged();
44 void brightnessChanged(qreal value);
45 void contrastChanged(qreal);
46 void hueChanged(qreal);
47 void saturationChanged(qreal);
48 void orientationChanged();
49 void videoRectChanged();
50 void videoFrameSizeChanged();
51 void backgroundColorChanged();
52
53 protected:
54 virtual void initializeGL();
55 virtual void paintGL();
56 virtual void resizeGL(int w, int h);
57 virtual void resizeEvent(QResizeEvent *); // not virtual in QGLWidget (Qt<5.5)
58 virtual void showEvent(QShowEvent *);
59
60 };
61 typedef GLWidgetRenderer2 VideoRendererGLWidget2;
62
63 };
0 namespace QtAV
1 {
2
3 class GraphicsItemRenderer : public QGraphicsObject, public QtAV::QPainterRenderer
4 {
5 %TypeHeaderCode
6 #include <QtAVWidgets/GraphicsItemRenderer.h>
7 %End
8
9 public:
10 void setBrightness(qreal brightness);
11 qreal brightness();
12 void setContrast(qreal contrast);
13 qreal contrast();
14 void setHue(qreal hue);
15 qreal hue();
16 void setSaturation(qreal saturation);
17 qreal saturation();
18 void setBackgroundColor(QColor color);
19 QColor backgroundColor();
20
21 QRectF regionOfInterest();
22 void setRegionOfInterest(QRectF region);
23 qreal sourceAspectRatio();
24 qreal outAspectRatio();
25 void setOutAspectRatio(qreal ratio);
26 OutAspectRatioMode outAspectRatioMode();
27 void setOutAspectRatioMode(OutAspectRatioMode mode);
28
29 int orientation();
30 void setOrientation(int orientation);
31 QRect videoRect();
32 QSize videoFrameSize();
33
34 public:
35 GraphicsItemRenderer(QGraphicsItem* parent /TransferThis/ = 0);
36 virtual VideoRendererId id();
37
38 bool isSupported(VideoFormat::PixelFormat pixfmt);
39
40 QRectF boundingRect() const;
41 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
42 QGraphicsItem* graphicsItem();
43
44 bool isOpenGL() const;
45 void setOpenGL(bool o);
46
47 OpenGLVideo* opengl() const;
48
49 signals:
50 void sourceAspectRatioChanged(qreal value) final;
51 void regionOfInterestChanged();
52 void outAspectRatioChanged();
53 void outAspectRatioModeChanged();
54 void brightnessChanged(qreal value);
55 void contrastChanged(qreal);
56 void hueChanged(qreal);
57 void saturationChanged(qreal);
58 void backgroundColorChanged();
59 void orientationChanged();
60 void videoRectChanged();
61 void videoFrameSizeChanged();
62 void openGLChanged();
63
64 protected:
65 bool receiveFrame(const VideoFrame& frame);
66 void drawBackground();
67 void drawFrame();
68 bool event(QEvent *event);
69 };
70 typedef GraphicsItemRenderer VideoRendererGraphicsItem;
71
72 };
0 namespace QtAV
1 {
2
3 class OpenGLWidgetRenderer : public QOpenGLWidget, public QtAV::OpenGLRendererBase
4 {
5 %TypeHeaderCode
6 #include <QtAVWidgets/OpenGLWidgetRenderer.h>
7 %End
8
9 public:
10 void setBrightness(qreal brightness);
11 qreal brightness();
12 void setContrast(qreal contrast);
13 qreal contrast();
14 void setHue(qreal hue);
15 qreal hue();
16 void setSaturation(qreal saturation);
17 qreal saturation();
18 void setBackgroundColor(QColor color);
19 QColor backgroundColor();
20
21 QRectF regionOfInterest();
22 void setRegionOfInterest(QRectF region);
23 qreal sourceAspectRatio();
24 qreal outAspectRatio();
25 void setOutAspectRatio(qreal ratio);
26 OutAspectRatioMode outAspectRatioMode();
27 void setOutAspectRatioMode(OutAspectRatioMode mode);
28
29 int orientation();
30 void setOrientation(int orientation);
31 QRect videoRect();
32 QSize videoFrameSize();
33
34 public:
35 explicit OpenGLWidgetRenderer(QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0);
36 virtual VideoRendererId id();
37 virtual QWidget* widget();
38
39 signals:
40 void sourceAspectRatioChanged(qreal value) final;
41 void regionOfInterestChanged();
42 void outAspectRatioChanged();
43 void outAspectRatioModeChanged();
44 void brightnessChanged(qreal value);
45 void contrastChanged(qreal);
46 void hueChanged(qreal);
47 void saturationChanged(qreal);
48 void orientationChanged();
49 void videoRectChanged();
50 void videoFrameSizeChanged();
51 void backgroundColorChanged();
52
53 protected:
54 virtual void initializeGL();
55 virtual void paintGL();
56 virtual void resizeGL(int w, int h);
57 virtual void resizeEvent(QResizeEvent *); // not virtual in QGLWidget (Qt<5.5)
58 virtual void showEvent(QShowEvent *);
59
60 };
61 typedef OpenGLWidgetRenderer VideoRendererOpenGLWidget;
62
63 };
0 %Module(name=QtAVWidgets, keyword_arguments="Optional")
1
2 %Import QtGui/QtGuimod.sip
3 %Import QtWidgets/QtWidgetsmod.sip
4 %Import QtOpenGL/QtOpenGLmod.sip
5 %Import QtAV/QtAVmod.sip
6
7 %Timeline {QtAVWidgets_1_11_0 QtAVWidgets_1_12_0}
8
9 %Feature FULL_QTWIDGETS_API
10
11 %Copying
12 QtAV: Multimedia framework based on Qt and FFmpeg
13 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
14
15 This file is part of QtAV (from 2015)
16
17 This library is free software; you can redistribute it and/or
18 modify it under the terms of the GNU Lesser General Public
19 License as published by the Free Software Foundation; either
20 version 2.1 of the License, or (at your option) any later version.
21
22 This library is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 Lesser General Public License for more details.
26
27 You should have received a copy of the GNU Lesser General Public
28 License along with this library; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 %End
31
32 %HideNamespace QtAV
33
34 %Include global.sip
35
36 %If (FULL_QTWIDGETS_API)
37 %Include GLWidgetRenderer2.sip
38 %Include GraphicsItemRenderer.sip
39 // NOTE: We don't expose QOpenGLWidget because it is present in Qt >= 5.4.0
40 %Include OpenGLWidgetRenderer.sip
41 %Include VideoPreviewWidget.sip
42 %Include WidgetRenderer.sip
43 %End
0 namespace QtAV
1 {
2
3 class VideoPreviewWidget : public QWidget
4 {
5 %TypeHeaderCode
6 #include <QtAVWidgets/VideoPreviewWidget.h>
7 %End
8
9 public:
10 explicit VideoPreviewWidget(QWidget *parent /TransferThis/ = 0);
11 void setTimestamp(qint64 msec);
12 qint64 timestamp() const;
13 void preview();
14 void setFile(const QString& file);
15 QString file() const;
16 void setKeepAspectRatio(bool value = true);
17 bool isKeepAspectRatio() const;
18 bool isAutoDisplayFrame() const;
19 void setAutoDisplayFrame(bool b=true);
20 public slots:
21 void displayFrame(const QtAV::VideoFrame& frame);
22 void displayNoFrame();
23 signals:
24 void timestampChanged();
25 void fileChanged();
26 void gotError(const QString &);
27 void gotAbort(const QString &);
28 void gotFrame(const QtAV::VideoFrame & frame);
29 protected:
30 virtual void resizeEvent(QResizeEvent *);
31 };
32
33 };
0 namespace QtAV
1 {
2
3 class WidgetRenderer : public QWidget, public QtAV::QPainterRenderer
4 {
5 %TypeHeaderCode
6 #include <QtAVWidgets/WidgetRenderer.h>
7 %End
8
9 public:
10 QColor backgroundColor();
11 void setBackgroundColor(QColor color);
12 QRectF regionOfInterest();
13 void setRegionOfInterest(QRectF region);
14 qreal sourceAspectRatio();
15 qreal outAspectRatio();
16 void setOutAspectRatio(qreal ratio);
17 OutAspectRatioMode outAspectRatioMode();
18 void setOutAspectRatioMode(OutAspectRatioMode mode);
19 int orientation();
20 void setOrientation(int orientation);
21 QRect videoRect();
22 QSize videoFrameSize();
23
24 public:
25 WidgetRenderer(QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0);
26 virtual VideoRendererId id();
27 virtual QWidget* widget();
28
29 signals:
30 void sourceAspectRatioChanged(qreal value) final;
31 void regionOfInterestChanged();
32 void outAspectRatioChanged();
33 void outAspectRatioModeChanged();
34 void brightnessChanged(qreal value);
35 void contrastChanged(qreal);
36 void hueChanged(qreal);
37 void saturationChanged(qreal);
38 void backgroundColorChanged();
39 void orientationChanged();
40 void videoRectChanged();
41 void videoFrameSizeChanged();
42 void imageReady();
43
44 protected:
45 bool receiveFrame(const VideoFrame& frame);
46 void resizeEvent(QResizeEvent *);
47 void paintEvent(QPaintEvent *);
48 bool onSetOrientation(int value);
49 };
50 typedef WidgetRenderer VideoRenderWidget;
51
52 };
0 %ModuleCode
1 #include <QtAVWidgets/global.h>
2 %End
3
4
5 namespace QtAV {
6
7 // Use custom code to allow QtAVWidgets.registerRenderers() instead of
8 // QtAVWidgets.Widgets.registerRenderers()
9 void registerRenderers();
10 %MethodCode
11 QtAV::Widgets::registerRenderers();
12 %End
13
14 VideoRendererId VideoRendererId_Widget;
15 VideoRendererId VideoRendererId_GraphicsItem;
16 VideoRendererId VideoRendererId_GLWidget;
17 VideoRendererId VideoRendererId_GDI;
18 VideoRendererId VideoRendererId_Direct2D;
19 VideoRendererId VideoRendererId_XV;
20 VideoRendererId VideoRendererId_X11;
21 VideoRendererId VideoRendererId_GLWidget2;
22 VideoRendererId VideoRendererId_OpenGLWidget;
23
24 void about();
25 void aboutFFmpeg();
26 void aboutQtAV();
27
28 };
3131 # add HEADERS for moc
3232 if(WIN32)
3333 set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc)
34 configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
34 configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
3535 endif()
3636 add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE})
3737 target_link_libraries(${MODULE}
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33 theoribeiro <theo@fictix.com.br>
44
55 * This file is part of QtAV (from 2013)
155155 Q_EMIT sourceChanged();
156156 if (!source)
157157 return;
158 ((QmlAVPlayer*)source)->player()->addVideoRenderer(this);
158 AVPlayer* p = qobject_cast<AVPlayer*>(source);
159 if (!p) {
160 QmlAVPlayer* qp = qobject_cast<QmlAVPlayer*>(source);
161 if (!qp) {
162 qWarning("source MUST be of type AVPlayer or QmlAVPlayer");
163 return;
164 }
165 p = qp->player();
166 }
167 p->addVideoRenderer(this);
159168 }
160169
161170 QQuickItemRenderer::FillMode QQuickItemRenderer::fillMode() const
193202 {
194203 if (videoFrameSize().isEmpty())
195204 return QPointF();
196
205 DPTR_D(const QQuickItemRenderer);
197206 // Just normalize and use that function
198207 // m_nativeSize is transposed in some orientations
199 if (orientation()%180 == 0)
208 if (d.rotation()%180 == 0)
200209 return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height()));
201210 else
202211 return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width()));
212221 {
213222 qreal dx = point.x();
214223 qreal dy = point.y();
215 if (orientation()%180 == 0) {
224 DPTR_D(const QQuickItemRenderer);
225 if (d.rotation()%180 == 0) {
216226 dx *= contentRect().width();
217227 dy *= contentRect().height();
218228 } else {
220230 dy *= contentRect().width();
221231 }
222232
223 switch (orientation()) {
233 switch (d.rotation()) {
224234 case 0:
225235 default:
226236 return contentRect().topLeft() + QPointF(dx, dy);
242252 QPointF QQuickItemRenderer::mapPointToSource(const QPointF &point) const
243253 {
244254 QPointF norm = mapPointToSourceNormalized(point);
245 if (orientation()%180 == 0)
255 if (d_func().rotation()%180 == 0)
246256 return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height());
247257 else
248258 return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width());
262272 // Normalize the item source point
263273 qreal nx = (point.x() - contentRect().x()) / contentRect().width();
264274 qreal ny = (point.y() - contentRect().y()) / contentRect().height();
265 switch (orientation()) {
275 switch (d_func().rotation()) {
266276 case 0:
267277 default:
268278 return QPointF(nx, ny);
339349 if (d.frame_changed)
340350 sgvn->setCurrentFrame(d.video_frame);
341351 d.frame_changed = false;
342 sgvn->setTexturedRectGeometry(d.out_rect, normalizedROI(), d.orientation);
352 sgvn->setTexturedRectGeometry(d.out_rect, normalizedROI(), d.rotation());
343353 return;
344354 }
345355 if (!d.frame_changed) {
356366 if (d.texture)
357367 delete d.texture;
358368
359 if (d.orientation == 0) {
369 if (d.rotation() == 0) {
360370 d.texture = window()->createTextureFromImage(d.image);
361 } else if (d.orientation == 180) {
371 } else if (d.rotation() == 180) {
362372 d.texture = window()->createTextureFromImage(d.image.mirrored(true, true));
363373 }
364374 static_cast<QSGSimpleTextureNode*>(d.node)->setTexture(d.texture);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33 theoribeiro <theo@fictix.com.br>
44
55 * This file is part of QtAV (from 2013)
5959 bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE;
6060
6161 QObject *source() const;
62 /*!
63 * \brief setSource
64 * \param source nullptr, an object of type AVPlayer or QmlAVPlayer
65 */
6266 void setSource(QObject *source);
6367
6468 FillMode fillMode() const;
6262 Q_ENUMS(Status)
6363 Q_ENUMS(Error)
6464 Q_ENUMS(ChannelLayout)
65 Q_ENUMS(BufferMode)
6566 // not supported by QtMultimedia
6667 Q_ENUMS(PositionValue)
68 Q_ENUMS(MediaEndAction)
6769 Q_PROPERTY(int startPosition READ startPosition WRITE setStartPosition NOTIFY startPositionChanged)
6870 Q_PROPERTY(int stopPosition READ stopPosition WRITE setStopPosition NOTIFY stopPositionChanged)
6971 Q_PROPERTY(bool fastSeek READ isFastSeek WRITE setFastSeek NOTIFY fastSeekChanged)
7981 Q_PROPERTY(int audioTrack READ audioTrack WRITE setAudioTrack NOTIFY audioTrackChanged)
8082 Q_PROPERTY(int videoTrack READ videoTrack WRITE setVideoTrack NOTIFY videoTrackChanged)
8183 Q_PROPERTY(int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged)
84 Q_PROPERTY(BufferMode bufferMode READ bufferMode WRITE setBufferMode NOTIFY bufferModeChanged)
85 Q_PROPERTY(qreal frameRate READ frameRate WRITE setFrameRate NOTIFY frameRateChanged)
8286 Q_PROPERTY(QUrl externalAudio READ externalAudio WRITE setExternalAudio NOTIFY externalAudioChanged)
8387 Q_PROPERTY(QVariantList internalAudioTracks READ internalAudioTracks NOTIFY internalAudioTracksChanged)
8488 Q_PROPERTY(QVariantList internalVideoTracks READ internalVideoTracks NOTIFY internalVideoTracksChanged)
8690 Q_PROPERTY(QVariantList internalSubtitleTracks READ internalSubtitleTracks NOTIFY internalSubtitleTracksChanged)
8791 // internal subtitle, e.g. mkv embedded subtitles
8892 Q_PROPERTY(int internalSubtitleTrack READ internalSubtitleTrack WRITE setInternalSubtitleTrack NOTIFY internalSubtitleTrackChanged)
93 Q_PROPERTY(MediaEndAction mediaEndAction READ mediaEndAction WRITE setMediaEndAction NOTIFY mediaEndActionChanged)
8994
9095 Q_PROPERTY(QQmlListProperty<QuickAudioFilter> audioFilters READ audioFilters)
9196 Q_PROPERTY(QQmlListProperty<QuickVideoFilter> videoFilters READ videoFilters)
128133 Mono,
129134 Stereo
130135 };
136 enum BufferMode
137 {
138 BufferTime = QtAV::BufferTime,
139 BufferBytes = QtAV::BufferBytes,
140 BufferPackets = QtAV::BufferPackets
141 };
142 enum MediaEndAction {
143 MediaEndAction_Default, /// stop playback (if loop end) and clear video renderer
144 MediaEndAction_KeepDisplay = 1, /// stop playback but video renderer keeps the last frame
145 MediaEndAction_Pause = 1 << 1 /// pause playback. Currently AVPlayer repeat mode will not work if this flag is set
146 };
131147
132148 explicit QmlAVPlayer(QObject *parent = 0);
133149 //derived
224240 void setAudioTrack(int value);
225241 QVariantList internalAudioTracks() const;
226242 /*!
227 /*!
228243 * \brief videoTrack
229244 * The video stream number in current media.
230245 * Value can be: 0, 1, 2.... 0 means the 1st video stream in current media
235250
236251 int bufferSize() const;
237252 void setBufferSize(int value);
253
254 BufferMode bufferMode() const;
255 void setBufferMode(BufferMode value);
256
257 qreal frameRate() const;
258 void setFrameRate(qreal value);
259
260 MediaEndAction mediaEndAction() const;
261 void setMediaEndAction(MediaEndAction value);
238262 /*!
239263 * \brief externalAudio
240264 * If externalAudio url is valid, player will use audioTrack of external audio as audio source.
306330 void internalSubtitleTrackChanged();
307331 void internalSubtitleTracksChanged();
308332 void bufferSizeChanged();
333 void bufferModeChanged();
334 void frameRateChanged();
335 void mediaEndActionChanged();
309336
310337 void errorChanged();
311338 void error(Error error, const QString &errorString);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
6565 OpenGLVideo* opengl() const Q_DECL_OVERRIDE;
6666
6767 QObject *source() const;
68 /*!
69 * \brief setSource
70 * \param source nullptr, an object of type AVPlayer or QmlAVPlayer
71 */
6872 void setSource(QObject *source);
6973
7074 FillMode fillMode() const;
421421 if (mpPlayer) {
422422 mpPlayer->setBufferValue(value);
423423 Q_EMIT bufferSizeChanged();
424 }
425 }
426
427 QmlAVPlayer::BufferMode QmlAVPlayer::bufferMode() const
428 {
429 return static_cast<BufferMode>(mpPlayer->bufferMode());
430 }
431
432 void QmlAVPlayer::setBufferMode(QmlAVPlayer::BufferMode value)
433 {
434 QtAV::BufferMode mode = static_cast<QtAV::BufferMode>(value);
435 if(mpPlayer->bufferMode() == mode)
436 return;
437 if(mpPlayer) {
438 mpPlayer->setBufferMode(mode);
439 Q_EMIT bufferModeChanged();
440 }
441 }
442
443 qreal QmlAVPlayer::frameRate() const
444 {
445 return mpPlayer->forcedFrameRate();
446 }
447
448 void QmlAVPlayer::setFrameRate(qreal value)
449 {
450 if(mpPlayer) {
451 mpPlayer->setFrameRate(value);
452 Q_EMIT frameRateChanged();
453 }
454 }
455
456 QmlAVPlayer::MediaEndAction QmlAVPlayer::mediaEndAction() const
457 {
458 return static_cast<MediaEndAction>(int(mpPlayer->mediaEndAction()));
459 }
460
461 void QmlAVPlayer::setMediaEndAction(QmlAVPlayer::MediaEndAction value)
462 {
463 QtAV::MediaEndAction action = static_cast<QtAV::MediaEndAction>(value);
464 if (mpPlayer->mediaEndAction() == action)
465 return;
466 if (mpPlayer) {
467 mpPlayer->setMediaEndAction(action);
468 Q_EMIT mediaEndActionChanged();
424469 }
425470 }
426471
7676 void setupAspectRatio() { //TODO: call when out_rect, renderer_size, orientation changed
7777 matrix.setToIdentity();
7878 matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1);
79 if (orientation)
80 matrix.rotate(orientation, 0, 0, 1); // Z axis
79 if (rotation())
80 matrix.rotate(rotation(), 0, 0, 1); // Z axis
8181 // FIXME: why x/y is mirrored?
82 if (orientation%180)
82 if (rotation()%180)
8383 matrix.scale(-1, 1);
8484 else
8585 matrix.scale(1, -1);
150150 DPTR_D(QuickFBORenderer);
151151 if (d.source == source)
152152 return;
153
154 AVPlayer* p0 = 0;
155 p0 = qobject_cast<AVPlayer*>(d.source);
156 if (!p0) {
157 QmlAVPlayer* qp0 = qobject_cast<QmlAVPlayer*>(d.source);
158 if (qp0)
159 p0 = qp0->player();
160 }
161 if(p0)
162 p0->removeVideoRenderer(this);
163
153164 d.source = source;
154165 Q_EMIT sourceChanged();
155166 if (!source)
156167 return;
157 ((QmlAVPlayer*)source)->player()->addVideoRenderer(this);
168 AVPlayer* p = qobject_cast<AVPlayer*>(source);
169 if (!p) {
170 QmlAVPlayer* qp = qobject_cast<QmlAVPlayer*>(source);
171 if (!qp) {
172 qWarning("source MUST be of type AVPlayer or QmlAVPlayer");
173 return;
174 }
175 p = qp->player();
176 }
177 p->addVideoRenderer(this);
158178 }
159179
160180 QuickFBORenderer::FillMode QuickFBORenderer::fillMode() const
189209
190210 // Just normalize and use that function
191211 // m_nativeSize is transposed in some orientations
192 if (orientation()%180 == 0)
212 if (d_func().rotation()%180 == 0)
193213 return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height()));
194214 else
195215 return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width()));
205225 {
206226 qreal dx = point.x();
207227 qreal dy = point.y();
208 if (orientation()%180 == 0) {
228 if (d_func().rotation()%180 == 0) {
209229 dx *= contentRect().width();
210230 dy *= contentRect().height();
211231 } else {
213233 dy *= contentRect().width();
214234 }
215235
216 switch (orientation()) {
236 switch (d_func().rotation()) {
217237 case 0:
218238 default:
219239 return contentRect().topLeft() + QPointF(dx, dy);
235255 QPointF QuickFBORenderer::mapPointToSource(const QPointF &point) const
236256 {
237257 QPointF norm = mapPointToSourceNormalized(point);
238 if (orientation()%180 == 0)
258 if (d_func().rotation()%180 == 0)
239259 return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height());
240260 else
241261 return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width());
255275 // Normalize the item source point
256276 qreal nx = (point.x() - contentRect().x()) / contentRect().width();
257277 qreal ny = (point.y() - contentRect().y()) / contentRect().height();
258 switch (orientation()) {
278 switch (d_func().rotation()) {
259279 case 0:
260280 default:
261281 return QPointF(nx, ny);
0 /******************************************************************************
1 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
3
4 * This file is part of QtAV (from 2016)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ******************************************************************************/
20
021 #include "QmlAV/QuickFilter.h"
122 #include "QtAV/private/Filter_p.h"
223 #include "QtAV/LibAVFilter.h"
1031 {
1132 public:
1233 QuickVideoFilterPrivate() : type(QuickVideoFilter::AVFilter)
34 , user_filter(NULL)
1335 , avfilter(new LibAVFilterVideo())
1436 , glslfilter(new GLSLFilter())
1537 {
1840
1941 QuickVideoFilter::FilterType type;
2042 VideoFilter *filter;
21 QScopedPointer<VideoFilter> user_filter;
43 VideoFilter *user_filter; // life time is managed by qml
2244 QScopedPointer<LibAVFilterVideo> avfilter;
2345 QScopedPointer<GLSLFilter> glslfilter;
2446 };
5476 else if (value == AVFilter)
5577 d.filter = d.avfilter.data();
5678 else
57 d.filter = d.user_filter.data();
79 d.filter = d.user_filter;
5880 Q_EMIT typeChanged();
5981 }
6082
7597
7698 VideoFilter* QuickVideoFilter::userFilter() const
7799 {
78 return d_func().user_filter.data();
100 return d_func().user_filter;
79101 }
80102
81103 void QuickVideoFilter::setUserFilter(VideoFilter *f)
82104 {
83105 DPTR_D(QuickVideoFilter);
84 if (d.user_filter.data() == f)
85 return;
86 d.user_filter.reset(f);
106 if (d.user_filter == f)
107 return;
108 d.user_filter = f;
109 if (d.type == UserFilter)
110 d.filter = d.user_filter;
87111 Q_EMIT userFilterChanged();
88112 }
89113
114138 public:
115139 QuickAudioFilterPrivate() : AudioFilterPrivate()
116140 , type(QuickAudioFilter::AVFilter)
141 , user_filter(NULL)
117142 , avfilter(new LibAVFilterAudio())
118143 {
119144 filter = avfilter.data();
121146
122147 QuickAudioFilter::FilterType type;
123148 AudioFilter *filter;
124 QScopedPointer<AudioFilter> user_filter;
149 AudioFilter* user_filter; // life time is managed by qml
125150 QScopedPointer<LibAVFilterAudio> avfilter;
126151 };
127152
146171 if (value == AVFilter)
147172 d.filter = d.avfilter.data();
148173 else
149 d.filter = d.user_filter.data();
174 d.filter = d.user_filter;
150175 Q_EMIT typeChanged();
151176 }
152177
167192
168193 AudioFilter *QuickAudioFilter::userFilter() const
169194 {
170 return d_func().user_filter.data();
195 return d_func().user_filter;
171196 }
172197
173198 void QuickAudioFilter::setUserFilter(AudioFilter *f)
174199 {
175200 DPTR_D(QuickAudioFilter);
176 if (d.user_filter.data() == f)
177 return;
178 d.user_filter.reset(f);
201 if (d.user_filter == f)
202 return;
203 d.user_filter = f;
204 if (d.type == UserFilter)
205 d.filter = d.user_filter;
179206 Q_EMIT userFilterChanged();
180207 }
181208
2727 {
2828 connect(&m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged()));
2929 connect(&m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame)));
30 connect(&m_extractor, SIGNAL(error()), SLOT(displayNoFrame()));
30 connect(&m_extractor, SIGNAL(error(const QString &)), SLOT(displayNoFrame()));
31 connect(&m_extractor, SIGNAL(aborted(const QString &)), SLOT(displayNoFrame()));
3132 connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame()));
3233 }
3334
8888 property alias internalAudioTracks: player.internalAudioTracks
8989 property alias externalAudioTracks: player.externalAudioTracks
9090 property alias internalVideoTracks: player.internalVideoTracks
91 property alias internalSubtitleTracks: player.internalSubtitleTracks
92 property alias internalSubtitleTrack: player.internalSubtitleTrack
9193 /*** Properties of VideoOutput ***/
9294 /*!
9395 \qmlproperty enumeration Video::fillMode
1212 preparePaths($$OUT_PWD/../out)
1313 #https://github.com/wang-bin/QtAV/issues/368#issuecomment-73246253
1414 #http://qt-project.org/forums/viewthread/38438
15 # mkspecs/features/qml_plugin.prf
16 URI = QtAV #uri used in QtAVQmlPlugin::registerTypes(uri)
17 qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI # not sure what moc does
18 #DESTDIR = $$BUILD_DIR/bin/QtAV
15 # mkspecs/features/qml_plugin.prf mkspecs/features/qml_module.prf
16 TARGETPATH = QtAV
17 URI = $$replace(TARGETPATH, "/", ".")
18 qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI
19
20 static: CONFIG += builtin_resources
21
22 builtin_resources {
23 RESOURCES += libQmlAV.qrc
24 }
25
1926 qtav_qml.files = qmldir Video.qml plugins.qmltypes
2027 !static { #static lib copy error before ranlib. copy only in sdk_install
2128 plugin.files = $$DESTDIR/$$qtSharedLib($$NAME)
6269 EXTRA_COPY_FILES = $$qtav_qml.files
6370
6471 QMAKE_WRITE_DEFAULT_RC = 1
65 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
72 QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com"
6673 QMAKE_TARGET_DESCRIPTION = "QtAV QML module. QtAV Multimedia framework. http://qtav.org"
67 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
74 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
6875 QMAKE_TARGET_PRODUCT = "QtAV QML"
6976
7077 SOURCES += \
0 <RCC>
1 <qresource prefix="/qt-project.org/imports/QtAV">
2 <file>qmldir</file>
3 <file>Video.qml</file>
4 </qresource>
5 </RCC>
3131 #include "QmlAV/QuickFBORenderer.h"
3232 #endif
3333
34 inline void initResources() {
35 #ifdef BUILD_QMLAV_STATIC
36 Q_INIT_RESOURCE(libQmlAV);
37 #endif
38 }
39
3440 namespace QtAV {
3541
3642 class QtAVQmlPlugin : public QQmlExtensionPlugin
3844 Q_OBJECT
3945 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
4046 public:
47 QtAVQmlPlugin() {
48 initResources();
49 }
4150 void registerTypes(const char *uri)
4251 {
4352 Q_ASSERT(QLatin1String(uri) == QLatin1String("QtAV"));
187187 }
188188 }
189189 Enum {
190 name: "MediaEndAction"
191 values: {
192 "MediaEndAction_Default": 0,
193 "MediaEndAction_KeepDisplay": 1,
194 "MediaEndAction_Pause": 2
195 }
196 }
197 Enum {
190198 name: "ChannelLayout"
191199 values: {
192200 "ChannelLayoutAuto": 0,
207215 Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true }
208216 Property { name: "autoPlay"; type: "bool" }
209217 Property { name: "autoLoad"; type: "bool" }
218 Property { name: "mediaEndAction"; type: "MediaEndAction" }
210219 Property { name: "playbackRate"; type: "double" }
211220 Property { name: "source"; type: "QUrl" }
212221 Property { name: "loops"; type: "int" }
00 <?xml version="1.0" encoding="UTF-8"?>
11 <Installer>
22 <Name>QtAV</Name>
3 <Version>1.12.0</Version>
3 <Version>1.13.0</Version>
44 <Title>QtAV Multimedia framework</Title>
55 <Publisher>WangBin wbsecg1@gmail.com</Publisher>
66 <ProductUrl>http://qtav.org</ProductUrl>
11 <Package>
22 <DisplayName>QtAV</DisplayName>
33 <Description>Install QtAV multimedia library</Description>
4 <Version>1.12.0-0</Version>
5 <ReleaseDate>2017-06-20</ReleaseDate>
4 <Version>1.13.0-0</Version>
5 <ReleaseDate>2019-07-11</ReleaseDate>
66 <Name>com.qtav.product</Name>
77 <Licenses>
88 <License name="LGPL v2.1" file="lgpl-2.1.txt" />
2222 copy /y bin\*portaudio*.dll %QTDIR%\bin
2323 xcopy /syi include %QTDIR%\include
2424 copy /y lib\*Qt*AV*.lib %QTDIR%\lib
25 copy /y lib\QtAV1.lib %QTDIR%\lib\Qt5AV.lib
26 copy /y lib\QtAVWidgets1.lib %QTDIR%\lib\Qt5AVWidgets.lib
2527 copy /y lib\*Qt*AV*.a %QTDIR%\lib
26 xcopy /syi qml\QtAV %QTDIR%\qml\QtAV
28 copy /y lib\libQtAV1.a %QTDIR%\lib\libQt5AV.a
29 copy /y lib\libQtAVWidgets1.a %QTDIR%\lib\libQt5AVWidgets.a
30 xcopy /syi bin\QtAV %QTDIR%\qml\QtAV
2731 xcopy /syi mkspecs %QTDIR%\mkspecs
2832 @echo QtAV SDK is installed
2933 goto END
11 <Package>
22 <DisplayName>Development files</DisplayName>
33 <Description>Install QtAV headers and lib.</Description>
4 <Version>1.12.0-0</Version>
5 <ReleaseDate>2017-06-20</ReleaseDate>
4 <Version>1.13.0-0</Version>
5 <ReleaseDate>2019-07-11</ReleaseDate>
66 <Name>com.qtav.product.dev</Name>
77 <Default>script</Default>
88 <Script>installscript.qs</Script>
11 <Package>
22 <DisplayName>Examples</DisplayName>
33 <Description>Install QtAV examples.</Description>
4 <Version>1.12.0-0</Version>
5 <ReleaseDate>2017-06-20</ReleaseDate>
4 <Version>1.13.0-0</Version>
5 <ReleaseDate>2019-07-11</ReleaseDate>
66 <Name>com.qtav.product.examples</Name>
77 <Default>script</Default>
88 <Script>installscript.qs</Script>
11 <Package>
22 <DisplayName>Player</DisplayName>
33 <Description>Default player.</Description>
4 <Version>1.12.0-0</Version>
5 <ReleaseDate>2017-06-20</ReleaseDate>
4 <Version>1.13.0-0</Version>
5 <ReleaseDate>2019-07-11</ReleaseDate>
66 <Name>com.qtav.product.player</Name>
77 <Default>true</Default>
88 <ForcedInstallation>true</ForcedInstallation>
11 <Package>
22 <DisplayName>Runtime library</DisplayName>
33 <Description>Install QtAV runtime library.</Description>
4 <Version>1.12.0-0</Version>
5 <ReleaseDate>2017-06-20</ReleaseDate>
4 <Version>1.13.0-0</Version>
5 <ReleaseDate>2019-07-11</ReleaseDate>
66 <Name>com.qtav.product.runtime</Name>
77 <Translations>
88 <Translation>zh_CN.qm</Translation>
210210 newSeekRequest(new stepBackwardTask(this, pre_pts));
211211 }
212212
213 void AVDemuxThread::seek(qint64 pos, SeekType type)
214 {
215 end = false;
216 // queue maybe blocked by put()
217 if (audio_thread) {
218 audio_thread->packetQueue()->clear();
219 }
220 if (video_thread) {
221 video_thread->packetQueue()->clear();
222 }
213 void AVDemuxThread::seek(qint64 external_pos, qint64 pos, SeekType type)
214 {
223215 class SeekTask : public QRunnable {
224216 public:
225 SeekTask(AVDemuxThread *dt, qint64 t, SeekType st)
217 SeekTask(AVDemuxThread *dt, qint64 external_pos, qint64 t, SeekType st)
226218 : demux_thread(dt)
227219 , type(st)
228220 , position(t)
221 , external_pos(external_pos)
229222 {}
230223 void run() {
224 // queue maybe blocked by put()
225 if (demux_thread->audio_thread) {
226 demux_thread->audio_thread->packetQueue()->clear();
227 }
228 if (demux_thread->video_thread) {
229 demux_thread->video_thread->packetQueue()->clear();
230 }
231231 if (demux_thread->video_thread)
232232 demux_thread->video_thread->setDropFrameOnSeek(true);
233 demux_thread->seekInternal(position, type);
233 demux_thread->seekInternal(position, type, external_pos);
234234 }
235235 private:
236236 AVDemuxThread *demux_thread;
237237 SeekType type;
238238 qint64 position;
239 qint64 external_pos;
239240 };
240 newSeekRequest(new SeekTask(this, pos, type));
241 }
242
243 void AVDemuxThread::seekInternal(qint64 pos, SeekType type)
241
242 end = false;
243 // queue maybe blocked by put()
244 // These must be here or seeking while paused will not update the video frame
245 if (audio_thread) {
246 audio_thread->packetQueue()->clear();
247 }
248 if (video_thread) {
249 video_thread->packetQueue()->clear();
250 }
251 newSeekRequest(new SeekTask(this, external_pos, pos, type));
252 }
253
254 void AVDemuxThread::seekInternal(qint64 pos, SeekType type, qint64 external_pos)
244255 {
245256 AVThread* av[] = { audio_thread, video_thread};
246257 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);
263274 Q_ASSERT(sync_id != 0);
264275 qDebug("demuxer sync id: %d/%d", sync_id, t->clock()->syncId());
265276 t->packetQueue()->clear();
277 if (external_pos != std::numeric_limits < qint64 >::min() )
278 t->clock()->updateExternalClock(qMax(qint64(0), external_pos));
279 t->clock()->updateValue(double(pos)/1000.0);
266280 t->requestSeek();
267281 // TODO: the first frame (key frame) will not be decoded correctly if flush() is called.
268282 //PacketBuffer *pb = t->packetQueue();
4545 AVThread* videoThread();
4646 void stepForward(); // show next video frame and pause
4747 void stepBackward();
48 void seek(qint64 pos, SeekType type); //ms
48 void seek(qint64 external_pos, qint64 pos, SeekType type); //ms
4949 //AVDemuxer* demuxer
5050 bool isPaused() const;
5151 bool isEnd() const;
8383 void setAVThread(AVThread *&pOld, AVThread* pNew);
8484 void newSeekRequest(QRunnable *r);
8585 void processNextSeekTask();
86 void seekInternal(qint64 pos, SeekType type); //must call in AVDemuxThread
86 void seekInternal(qint64 pos, SeekType type, qint64 external_pos = std::numeric_limits < qint64 >::min()); //must call in AVDemuxThread
8787 void pauseInternal(bool value);
8888
8989 bool paused;
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
325325 class AVInitializer {
326326 public:
327327 AVInitializer() {
328 #if !AVCODEC_STATIC_REGISTER
328329 avcodec_register_all();
330 #endif
329331 #if QTAV_HAVE(AVDEVICE)
330332 avdevice_register_all();
331333 #endif
334 #if !AVFORMAT_STATIC_REGISTER
332335 av_register_all();
336 #endif
333337 avformat_network_init();
334338 }
335339 ~AVInitializer() {
351355 static QStringList exts;
352356 static QStringList fmts;
353357 if (exts.isEmpty() && fmts.isEmpty()) {
358 QStringList e, f;
359 #if AVFORMAT_STATIC_REGISTER
360 const AVInputFormat *i = NULL;
361 void* it = NULL;
362 while ((i = av_demuxer_iterate(&it))) {
363 #else
364 AVInputFormat *i = NULL;
354365 av_register_all(); // MUST register all input/output formats
355 AVInputFormat *i = NULL;
356 QStringList e, f;
357366 while ((i = av_iformat_next(i))) {
367 #endif
358368 if (i->extensions)
359369 e << QString::fromLatin1(i->extensions).split(QLatin1Char(','), QString::SkipEmptyParts);
360370 if (i->name)
399409 #if QTAV_HAVE(AVDEVICE)
400410 protocols << QStringLiteral("avdevice");
401411 #endif
412 #if !AVFORMAT_STATIC_REGISTER
402413 av_register_all(); // MUST register all input/output formats
414 #endif
403415 void* opq = 0;
404416 const char* protocol = avio_enum_protocols(&opq, 0);
405417 while (protocol) {
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
4242 , started(false)
4343 , eof(false)
4444 , media_changed(true)
45 , open(false)
4546 , format_ctx(0)
4647 , format(0)
4748 , io(0)
4950 , aenc(0)
5051 , venc(0)
5152 {
53 #if !AVFORMAT_STATIC_REGISTER
5254 av_register_all();
55 #endif
5356 }
5457 ~Private() {
5558 //delete interrupt_hanlder;
7275 bool started;
7376 bool eof;
7477 bool media_changed;
78 bool open;
7579 AVFormatContext *format_ctx;
7680 //copy the info, not parse the file when constructed, then need member vars
7781 QString file;
121125 c->time_base = s->time_base;
122126 /* Some formats want stream headers to be separate. */
123127 if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
124 c->flags |= CODEC_FLAG_GLOBAL_HEADER;
128 c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
125129 // expose avctx to encoder and set properties in encoder?
126130 // list codecs for a given format in ui
127131 return s;
142146 c->height = venc->height();
143147 /// MUST set after encoder is open to ensure format is valid and the same
144148 c->pix_fmt = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(venc->pixelFormat());
149
150 // Set avg_frame_rate based on encoder frame_rate
151 s->avg_frame_rate = av_d2q(venc->frameRate(), venc->frameRate()*1001.0+2);
152
145153 video_streams.push_back(s->id);
146154 }
147155 }
156164 c->channel_layout = aenc->audioFormat().channelLayoutFFmpeg();
157165 c->channels = aenc->audioFormat().channels();
158166 c->bits_per_raw_sample = aenc->audioFormat().bytesPerSample()*8; // need??
167
168 AVCodecContext *avctx = (AVCodecContext *) aenc->codecContext();
169 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56,5,100)
170 c->initial_padding = avctx->initial_padding;
171 #else
172 c->delay = avctx->delay;
173 #endif
174 if (avctx->extradata_size) {
175 c->extradata = avctx->extradata;
176 c->extradata_size = avctx->extradata_size;
177 }
178
159179 audio_streams.push_back(s->id);
160180 }
161181 }
167187 static QStringList exts;
168188 static QStringList fmts;
169189 if (exts.isEmpty() && fmts.isEmpty()) {
190 QStringList e, f;
191 #if AVFORMAT_STATIC_REGISTER
192 const AVOutputFormat *o = NULL;
193 void* it = NULL;
194 while ((o = av_muxer_iterate(&it))) {
195 #else
170196 av_register_all(); // MUST register all input/output formats
171197 AVOutputFormat *o = NULL;
172 QStringList e, f;
173198 while ((o = av_oformat_next(o))) {
199 #endif
174200 if (o->extensions)
175201 e << QString::fromLatin1(o->extensions).split(QLatin1Char(','), QString::SkipEmptyParts);
176202 if (o->name)
220246 #if QTAV_HAVE(AVDEVICE)
221247 protocols << QStringLiteral("avdevice");
222248 #endif
249 #if !AVFORMAT_STATIC_REGISTER
223250 av_register_all(); // MUST register all input/output formats
251 #endif
224252 void* opq = 0;
225253 const char* protocol = avio_enum_protocols(&opq, 1);
226254 while (protocol) {
322350 d->format_forced.clear();
323351 }
324352 d->io->setProperty("device", QVariant::fromValue(device)); //open outside?
353
354 if (device->isWritable()) {
355 d->io->setAccessMode(MediaIO::Write);
356 }
357
325358 return d->media_changed;
326359 }
327360
391424 // d->format_ctx->start_time_realtime
392425 AV_ENSURE_OK(avformat_write_header(d->format_ctx, &d->dict), false);
393426 d->started = false;
427 d->open = true;
394428
395429 return true;
396430 }
399433 {
400434 if (!isOpen())
401435 return true;
436 d->open = false;
402437 av_write_trailer(d->format_ctx);
403438 // close AVCodecContext* in encoder
404439 // custom io will call avio_close in ~MediaIO()
420455
421456 bool AVMuxer::isOpen() const
422457 {
423 return d->format_ctx;
458 return d->open;
424459 }
425460
426461 bool AVMuxer::writeAudio(const QtAV::Packet& packet)
0 /******************************************************************************
0 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
22 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
4444 #include "QtAV/private/AVCompat.h"
4545 #include "utils/internal.h"
4646 #include "utils/Logger.h"
47 extern "C" {
48 #include <libavutil/mathematics.h>
49 }
4750
4851 #define EOF_ISSUE_SOLVED 0
4952 namespace QtAV {
679682 d->video_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::VideoStream);
680683 Q_EMIT internalVideoTracksChanged(d->video_tracks);
681684 Q_EMIT durationChanged(duration());
685 Q_EMIT chaptersChanged(chapters());
682686 // setup parameters from loaded media
683687 d->media_start_pts = d->demuxer.startTime();
684688 // TODO: what about other proctols? some vob duration() == 0
714718 d->vdec = 0;
715719 }
716720 d->demuxer.unload();
721 Q_EMIT chaptersChanged(0);
717722 Q_EMIT durationChanged(0LL); // for ui, slider is invalid. use stopped instead, and remove this signal here?
718723 // ??
719724 d->audio_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::AudioStream);
856861 if (relativeTimeMode())
857862 pos_pts += absoluteMediaStartPosition();
858863 d->seeking = true;
859 masterClock()->updateValue(double(pos_pts)/1000.0); //what is duration == 0
860 masterClock()->updateExternalClock(pos_pts); //in msec. ignore usec part using t/1000
861 d->read_thread->seek(pos_pts, seekType());
864 d->read_thread->seek(position,pos_pts, seekType());
862865
863866 Q_EMIT positionChanged(position); //emit relative position
864867 }
12301233 d->vthread->start();
12311234 }
12321235
1233 d->read_thread->setMediaEndAction(mediaEndAction());
1234 d->read_thread->start();
1235
12361236 if (d->demuxer.audioCodecContext() && d->athread)
12371237 d->athread->waitForStarted();
12381238 if (d->demuxer.videoCodecContext() && d->vthread)
12391239 d->vthread->waitForStarted();
1240
1241 d->read_thread->setMediaEndAction(mediaEndAction());
1242 d->read_thread->start();
1243
12401244 /// demux thread not started, seek tasks will be cleared
12411245 d->read_thread->waitForStarted();
12421246 if (d->timer_id < 0) {
13841388 }
13851389 }
13861390
1391 void AVPlayer::seekChapter(int incr)
1392 {
1393 if (!chapters())
1394 return;
1395
1396 qint64 pos = masterClock()->value() * AV_TIME_BASE;
1397 int i = 0;
1398
1399 AVFormatContext *ic = d->demuxer.formatContext();
1400
1401 AVRational av_time_base_q;
1402 av_time_base_q.num = 1;
1403 av_time_base_q.den = AV_TIME_BASE;
1404
1405 /* find the current chapter */
1406 for (i = 0; i < chapters(); ++i) {
1407 AVChapter *ch = ic->chapters[i];
1408 if (av_compare_ts(pos, av_time_base_q, ch->start, ch->time_base) < 0) {
1409 --i;
1410 break;
1411 }
1412 }
1413
1414 i += incr;
1415 //i = FFMAX(i, 0);
1416 if (i <= 0)
1417 i = 0;
1418 if (i >= chapters())
1419 return;
1420
1421 //av_log(NULL, AV_LOG_VERBOSE, "Seeking to chapter %d.\n", i);
1422 qDebug() << QString::fromLatin1("Seeking to chapter : ") << QString::number(i);
1423 setPosition(av_rescale_q(ic->chapters[i]->start, ic->chapters[i]->time_base,
1424 av_time_base_q) / 1000);
1425 }
1426
13871427 void AVPlayer::stop()
13881428 {
13891429 // check d->timer_id, <0 return?
15311571 seek(position() - kSeekMS);
15321572 }
15331573
1574 void AVPlayer::seekNextChapter()
1575 {
1576 if (chapters() <= 1)
1577 return;
1578 seekChapter(1);
1579 }
1580
1581 void AVPlayer::seekPreviousChapter()
1582 {
1583 if (chapters() <= 1)
1584 return;
1585 seekChapter(-1);
1586 }
1587
15341588 void AVPlayer::setSeekType(SeekType type)
15351589 {
15361590 d->seek_type = type;
16341688 return d->saturation;
16351689 }
16361690
1691 unsigned int AVPlayer::chapters() const
1692 {
1693 return d->demuxer.formatContext()->nb_chapters;
1694 }
1695
16371696 void AVPlayer::setSaturation(int val)
16381697 {
16391698 if (d->saturation == val)
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
2727 #include "QtAV/MediaIO.h"
2828 #include "QtAV/VideoCapture.h"
2929 #include "QtAV/private/AVCompat.h"
30 #if AV_MODULE_CHECK(LIBAVFORMAT, 55, 18, 0, 39, 100)
31 extern "C" {
32 #include <libavutil/display.h>
33 }
34 #endif
3035 #include "utils/Logger.h"
3136
3237 namespace QtAV {
324329 statistics.video_only.pix_fmt = QLatin1String(av_get_pix_fmt_name(avctx->pix_fmt));
325330 statistics.video_only.height = avctx->height;
326331 statistics.video_only.width = avctx->width;
332 statistics.video_only.rotate = 0;
333 #if AV_MODULE_CHECK(LIBAVFORMAT, 55, 18, 0, 39, 100)
334 quint8 *sd = av_stream_get_side_data(demuxer.formatContext()->streams[s], AV_PKT_DATA_DISPLAYMATRIX, NULL);
335 if (sd) {
336 double r = av_display_rotation_get((qint32*)sd);
337 if (!qIsNaN(r))
338 statistics.video_only.rotate = ((int)r + 360) % 360;
339 }
340 #endif
327341 }
328342 // notify statistics change after audio/video thread is set
329343 bool AVPlayer::Private::setupAudioThread(AVPlayer *player)
413427 }
414428 }
415429 }
430
431 // we set the thre state before the thread start,
432 // as it maybe clear after by AVDemuxThread starting
433 athread->resetState();
416434 athread->setDecoder(adec);
417435 setAVOutput(ao, ao, athread);
418436 updateBufferValue(athread->packetQueue());
444462 QVariantMap t;
445463 t[QStringLiteral("id")] = info.size();
446464 t[QStringLiteral("file")] = demuxer->fileName();
465 t[QStringLiteral("stream_index")] = QVariant(s);
466
447467 AVStream *stream = demuxer->formatContext()->streams[s];
448468 AVCodecContext *ctx = stream->codec;
449469 if (ctx) {
588608 }
589609 QObject::connect(vthread, SIGNAL(finished()), player, SLOT(tryClearVideoRenderers()), Qt::DirectConnection);
590610 }
611
612 // we set the thre state before the thread start
613 // as it maybe clear after by AVDemuxThread starting
614 vthread->resetState();
591615 vthread->setDecoder(vdec);
592616
593617 vthread->setBrightness(brightness);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
363363 void AVThread::waitAndCheck(ulong value, qreal pts)
364364 {
365365 DPTR_D(AVThread);
366 if (value <= 0)
366 if (value <= 0 || pts < 0)
367367 return;
368368 value += d.wait_err;
369369 d.wait_timer.restart();
381381 us = qMin(us, ulong((double)(qMax<qreal>(0, pts - d.clock->value()))*1000000.0));
382382 //qDebug("us: %lu/%lu, pts: %f, clock: %f", us, ms-et.elapsed(), pts, d.clock->value());
383383 processNextTask();
384 us = qMin<ulong>(us, (ms-d.wait_timer.elapsed())*1000);
384 const qint64 left = qint64(ms) - d.wait_timer.elapsed();
385 if (left <= 0) {
386 us = 0;
387 break;
388 }
389 us = qMin<ulong>(us, left*1000);
385390 }
386391 if (us > 0)
387392 usleep(us);
8080 qreal decodeFrameRate() const; //move to statistics?
8181 void setDropFrameOnSeek(bool value);
8282
83 void resetState();
8384 public slots:
8485 virtual void stop();
8586 /*change pause state. the pause/continue action will do in the next loop*/
99100 void onFinished();
100101 protected:
101102 AVThread(AVThreadPrivate& d, QObject *parent = 0);
102 void resetState();
103103 /*
104104 * If the pause state is true setted by pause(true), then block the thread and wait for pause state changed, i.e. pause(false)
105105 * and return true. Otherwise, return false immediatly.
285285 // uninstall encoder filters first then encoders can be closed safely
286286 if (sourcePlayer()) {
287287 sourcePlayer()->uninstallFilter(d->afilter);
288 disconnect(sourcePlayer(), SIGNAL(stopped()), d->afilter, SLOT(finish()));
288289 sourcePlayer()->uninstallFilter(d->vfilter);
290 disconnect(sourcePlayer(), SIGNAL(stopped()), d->vfilter, SLOT(finish()));
289291 }
290292 if (d->afilter)
291293 d->afilter->finish(); //FIXME: thread of sync mode
2929 #endif
3030
3131 namespace QtAV {
32
33 const qint64 kHz = 1000000LL;
3432
3533 typedef struct {
3634 AVSampleFormat avfmt;
132132
133133 AudioFrame AudioFrame::clone() const
134134 {
135 Q_D(const AudioFrame);
135 return mid(0, -1);
136 }
137
138 AudioFrame AudioFrame::mid(int pos, int len) const
139 {
140 Q_D(const AudioFrame);
141
136142 if (d->format.sampleFormatFFmpeg() == AV_SAMPLE_FMT_NONE
137 || d->format.channels() <= 0)
143 || d->format.channels() <= 0) {
138144 return AudioFrame();
139 if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0)
145 }
146
147 if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0 || len == 0) {
140148 return AudioFrame(format());
141 QByteArray buf(bytesPerLine()*planeCount(), 0);
149 }
150
151 int bufSize = bytesPerLine();
152 int posBytes = 0;
153 if (pos > 0) {
154 posBytes = pos * d->format.bytesPerSample();
155 bufSize -= posBytes;
156 } else {
157 pos = 0;
158 }
159
160 int lenBytes = len * d->format.bytesPerSample();
161 if (len > 0 && lenBytes < bufSize) {
162 bufSize = lenBytes;
163 } else {
164 lenBytes = bufSize;
165 }
166
167 QByteArray buf(bufSize * planeCount(), 0);
142168 char *dst = buf.data(); //must before buf is shared, otherwise data will be detached.
169
143170 for (int i = 0; i < planeCount(); ++i) {
144 const int plane_size = bytesPerLine(i);
145 memcpy(dst, constBits(i), plane_size);
146 dst += plane_size;
147 }
171 memcpy(dst, constBits(i) + posBytes, lenBytes);
172 dst += lenBytes;
173 }
174
148175 AudioFrame f(d->format, buf);
149 f.setSamplesPerChannel(samplesPerChannel());
150 f.setTimestamp(timestamp());
176 f.setSamplesPerChannel(bufSize / d->format.bytesPerSample());
177 f.setTimestamp(d->timestamp + (qreal) d->format.durationForBytes(posBytes) / AudioFormat::kHz);
151178 // meta data?
152179 return f;
180 }
181
182 void AudioFrame::prepend(AudioFrame &other)
183 {
184 Q_D(AudioFrame);
185
186 if (d->format != other.format()) {
187 qWarning() << "To prepend a frame it must have the same audio format";
188 return;
189 }
190
191 d->data.prepend(other.data());
192 d->samples_per_ch += other.samplesPerChannel();
193 d->timestamp = other.timestamp();
194
195 for (int i = 0; i < planeCount(); i++) {
196 d->line_sizes[i] += other.bytesPerLine(i);
197 }
153198 }
154199
155200 AudioFormat AudioFrame::format() const
7777 //No decoder or output. No audio output is ok, just display picture
7878 if (!d.dec || !d.dec->isAvailable() || !d.outputSet)
7979 return;
80 resetState();
80 // resetState(); // we can't reset the thread state from here
8181 Q_ASSERT(d.clock != 0);
8282 d.init();
8383 Packet pkt;
1111 include_directories(${QTDIR}/include) #TODO: remove. use external/include
1212 get_filename_component(QTDIR "${QTDIR}" ABSOLUTE)
1313
14 list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/external/include)
15 list(APPEND EXTRA_LIBS ${CMAKE_LIBRARY_PATH_FLAG}${CMAKE_SOURCE_DIR}/external/lib)
14 list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/external/include)
15 list(APPEND EXTRA_LIBS ${CMAKE_LIBRARY_PATH_FLAG}${QTAV_SOURCE_DIR}/external/lib)
1616 if(APPLE)
1717 if(IOS)
1818 #set_xcode_property(myioslib IPHONEOS_DEPLOYMENT_TARGET "8.0")
2121 list(APPEND EXTRA_LIBS -L/usr/local/lib)
2222 endif()
2323 endif()
24 if(EXISTS ${CMAKE_SOURCE_DIR}/contrib/capi/capi.h)
24 if(EXISTS ${QTAV_SOURCE_DIR}/contrib/capi/capi.h)
2525 set(HAVE_CAPI 1)
26 list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/capi) # TODO: only files use capi.h
26 list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/contrib/capi) # TODO: only files use capi.h
2727 list(APPEND EXTRA_DEFS -DQTAV_HAVE_CAPI=1)
2828 endif()
2929
323323 if(WIN32 OR WindowsStore OR WindowsPhone)
324324 check_include_files(XAudio2.h HAVE_XAUDIO2_H)
325325 if(NOT HAVE_XAUDIO2_H)
326 list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/dxsdk)
326 list(APPEND EXTRA_INCLUDE ${QTAV_SOURCE_DIR}/contrib/dxsdk)
327327 endif()
328328 message("Qt5Gui_EGL_INCLUDE_DIRS: ${Qt5Gui_EGL_INCLUDE_DIRS}")
329329 list(APPEND HEADERS
472472 set_source_files_properties(${RESOURCES_SOURCES} PROPERTIES GENERATED ON)
473473 if(WIN32)
474474 set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc)
475 configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
475 configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
476476 endif()
477477 # add HEADERS for moc
478478 add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE})
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
5656 QByteArray Frame::frameData() const
5757 {
5858 return d_func()->data;
59 }
60
61 int Frame::dataAlignment() const
62 {
63 return d_func()->data_align;
5964 }
6065
6166 QByteArray Frame::data(int plane) const
352352 d->vframes.put(frame);
353353 Q_EMIT frameRead(frame);
354354 //qDebug("frame got @%.3f, queue enough: %d", frame.timestamp(), vframes.isEnough());
355 if (d->vframes.isEnough())
355 if (d->vframes.isFull())
356356 break;
357357 } else {
358358 qDebug("dec error, continue to decoder");
00 /******************************************************************************
11 ImageConverter: Base class for image resizing & color model convertion
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
4444
4545 QByteArray ImageConverter::outData() const
4646 {
47 return d_func().data_out;
47 DPTR_D(const ImageConverter);
48 return d.data_out;
4849 }
4950
5051 bool ImageConverter::check() const
214215 const int nb_planes = qMax(av_pix_fmt_count_planes(d.fmt_out), 0);
215216 d.bits.resize(nb_planes);
216217 d.pitchs.resize(nb_planes);
217 // alignment is 16. sws in ffmpeg is 16, libav10 is 8
218 const int kAlign = 16;
218 // alignment is 16. sws in ffmpeg is 16, libav10 is 8. if not aligned sws will print warnings and go slow code paths
219 const int kAlign = DataAlignment;
219220 AV_ENSURE(av_image_fill_linesizes((int*)d.pitchs.constData(), d.fmt_out, kAlign > 7 ? FFALIGN(d.w_out, 8) : d.w_out), false);
220221 for (int i = 0; i < d.pitchs.size(); ++i)
221222 d.pitchs[i] = FFALIGN(d.pitchs[i], kAlign);
223224 if (s < 0)
224225 return false;
225226 d.data_out.resize(s + kAlign-1);
226 const int offset = (kAlign - ((uintptr_t)d.data_out.constData() & (kAlign-1))) & (kAlign-1);
227 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);
227 d.out_offset = (kAlign - ((uintptr_t)d.data_out.constData() & (kAlign-1))) & (kAlign-1);
228 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);
228229 // TODO: special formats
229230 //if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
230231 // avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt);
00 /******************************************************************************
11 ImageConverter: Base class for image resizing & color model convertion
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
3434 {
3535 DPTR_DECLARE_PRIVATE(ImageConverter)
3636 public:
37 enum { DataAlignment = 16 };
38
3739 ImageConverter();
3840 virtual ~ImageConverter();
3941
42 // the real data starts with DataAlignment (16bit) aligned address
4043 QByteArray outData() const;
4144 // return false if i/o format not supported, or size is not valid.
4245 // TODO: use isSupported(i/o format);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
4242 , contrast(0)
4343 , saturation(0)
4444 , update_data(true)
45 , out_offset(0)
4546 {
4647 bits.reserve(8);
4748 pitchs.reserve(8);
5657 ColorRange range_in, range_out;
5758 int brightness, contrast, saturation;
5859 bool update_data;
60 int out_offset;
5961 QByteArray data_out;
6062 QVector<quint8*> bits;
6163 QVector<int> pitchs;
0 /******************************************************************************
0 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
22 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
8181 Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged)
8282 Q_PROPERTY(QtAV::MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged)
8383 Q_PROPERTY(QtAV::MediaEndAction mediaEndAction READ mediaEndAction WRITE setMediaEndAction NOTIFY mediaEndActionChanged)
84 Q_PROPERTY(unsigned int chapters READ chapters NOTIFY chaptersChanged)
8485 Q_ENUMS(State)
8586 public:
8687 /*!
198199 /*!
199200 * \brief externalAudioTracks
200201 * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support.
201 * [ {id: 0, file: abc.dts, language: eng, title: xyz}, ...]
202 * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...]
202203 * id: used for setAudioStream(id)
204 * stream_index: the actual muxer stream index -- not used by API to this class but possibly
205 * useful for interop with external libs that require absolute stream_index
203206 * \sa externalAudioTracksChanged
204207 */
205208 const QVariantList& externalAudioTracks() const;
209 /*!
210 * \brief internalAudioTracks
211 * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support.
212 * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...]
213 * id: used for setAudioStream(id)
214 * stream_index: the actual muxer stream index -- not used by API to this class but possibly
215 * useful for interop with external libs that require absolute stream_index
216 */
206217 const QVariantList& internalAudioTracks() const;
218 /*!
219 * \brief internalVideoTracks
220 * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support.
221 * [ {id: 0, stream_index: 2, file: abc.dts, language: eng, title: xyz}, ...]
222 * id: used for setAudioStream(id)
223 * stream_index: the actual muxer stream index -- not used by API to this class but possibly
224 * useful for interop with external libs that require absolute stream_index
225 */
207226 const QVariantList& internalVideoTracks() const;
208227 /*!
209228 * \brief setAudioStream
355374 int contrast() const;
356375 int hue() const; //not implemented
357376 int saturation() const;
377 unsigned int chapters() const;
358378 /*!
359379 * \sa AVDemuxer::setOptions()
360380 * example:
470490 void seek(qint64 pos); //ms. same as setPosition(pos)
471491 void seekForward();
472492 void seekBackward();
493 void seekNextChapter();
494 void seekPreviousChapter();
473495 void setSeekType(SeekType type);
474496 SeekType seekType() const;
475497
560582 void contrastChanged(int val);
561583 void hueChanged(int val);
562584 void saturationChanged(int val);
585 void chaptersChanged(unsigned int val);
563586 void subtitleStreamChanged(int value);
564587 /*!
565588 * \brief internalAudioTracksChanged
592615 void updateMediaStatus(QtAV::MediaStatus status);
593616 void onSeekFinished(qint64 value);
594617 void tryClearVideoRenderers();
618 void seekChapter(int incr);
595619 protected:
596620 // TODO: set position check timer interval
597621 virtual void timerEvent(QTimerEvent *);
6363 */
6464 const AudioFormat& audioFormat() const;
6565 void setAudioFormat(const AudioFormat& format);
66
67 int frameSize() const;
6668 Q_SIGNALS:
6769 void audioFormatChanged();
6870 public:
6767 ChannelLayout_Stereo,
6868 ChannelLayout_Unsupported //ok. now it's not complete
6969 };
70
71 static const qint64 kHz = 1000000LL;
72
7073 //typedef qint64 ChannelLayout; //currently use latest FFmpeg's
7174 // TODO: constexpr
7275 friend int RawSampleSize(SampleFormat fmt) { return fmt & ((1<<(kSize+1)) - 1); }
5656 * then you can use clone().
5757 */
5858 AudioFrame clone() const;
59 AudioFrame mid(int pos, int len = -1) const;
60 void prepend(AudioFrame &other);
5961 AudioFormat format() const;
6062 void setSamplesPerChannel(int samples);
6163 // may change after resampling
8181 void frameEncoded(const QtAV::Packet& packet);
8282 void startTimeChanged(qint64 value);
8383 // internal use only
84 void requestToEncode(const AudioFrame& frame);
84 void requestToEncode(const QtAV::AudioFrame& frame);
8585 protected Q_SLOTS:
8686 void encode(const QtAV::AudioFrame& frame = AudioFrame());
8787 protected:
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
6060 */
6161 int bytesPerLine(int plane = 0) const;
6262 // the whole frame data. may be empty unless clone() or allocate is called
63 // real data starts with dataAlignment() aligned address
6364 QByteArray frameData() const;
65 int dataAlignment() const;
66 uchar* frameDataPtr(int* size = NULL) const {
67 const int a = dataAlignment();
68 uchar* p = (uchar*)frameData().constData();
69 const int offset = (a - ((quintptr)p & (a-1))) & (a-1);
70 if (size)
71 *size = frameData().size() - offset;
72 return p+offset;
73 }
6474 // deep copy 1 plane data
6575 QByteArray data(int plane = 0) const;
6676 uchar* bits(int plane = 0);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
114114 * shader manager and material will be reset
115115 */
116116 void resetGL();
117 void updateViewport();
117118 };
118119
119120
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
167167 #ifndef QByteArrayLiteral
168168 #define QByteArrayLiteral(str) QByteArray(str, sizeof(str) - 1)
169169 #endif
170 /*
171 * msvc sucks! can not deal with (defined(QTAV_HAVE_##FEATURE) && QTAV_HAVE_##FEATURE)
172 */
170
173171 // TODO: internal use. move to a private header
174 #define QTAV_HAVE(FEATURE) (defined QTAV_HAVE_##FEATURE && QTAV_HAVE_##FEATURE)
172 #define QTAV_HAVE(FEATURE) (QTAV_HAVE_##FEATURE+0)
175173
176174 #ifndef Q_DECL_OVERRIDE
177175 #define Q_DECL_OVERRIDE
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
105105 */
106106 int gop_size;
107107 QString pix_fmt;
108 int rotate;
108109 /// return current absolute time (seconds since epcho
109110 qint64 frameDisplayed(qreal pts); // used to compute currentDisplayFPS()
110111 private:
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
5151 VideoFrame();
5252 //must set planes and linesize manually if data is empty
5353 // must set planes and linesize manually
54 VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray());
54 // alignment: data ptr alignment
55 VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray(), int alignment = 1);
5556 VideoFrame(const QImage& image);
5657 VideoFrame(const VideoFrame &other);
5758 ~VideoFrame();
6666 void setPosition(qint64 value);
6767 qint64 position() const;
6868
69 virtual bool event(QEvent *e);
7069 Q_SIGNALS:
7170 void frameExtracted(const QtAV::VideoFrame& frame); // parameter: VideoFrame, bool changed?
7271 void sourceChanged();
7372 void asyncChanged();
74 void error(); // clear preview image in a slot
73 void error(const QString &errorMessage); ///< emitted with a helpful error message -- connect to this to show empty image in preview widget
74 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
7575 void autoExtractChanged();
7676 /*!
7777 * \brief positionChanged
7979 */
8080 void positionChanged();
8181 void precisionChanged();
82
83 void aboutToExtract(qint64 pos);
8482
8583 public Q_SLOTS:
8684 /*!
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
22 solve the version problem and diffirent api in FFmpeg and libav
3 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
3 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
44
55 * This file is part of QtAV
66
5858 #include <libavutil/parseutils.h>
5959 #include <libavutil/pixdesc.h>
6060 #include <libavutil/avstring.h>
61 #include <libavfilter/version.h>
62
63 #define AVCODEC_STATIC_REGISTER FFMPEG_MODULE_CHECK(LIBAVCODEC, 58, 10, 100)
64 #define AVFORMAT_STATIC_REGISTER FFMPEG_MODULE_CHECK(LIBAVFORMAT, 58, 9, 100)
6165
6266 #if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 51, 73, 101)
6367 #include <libavutil/channel_layout.h>
7882 #endif //QTAV_HAVE(AVRESAMPLE)
7983
8084 #if QTAV_HAVE(AVFILTER)
85 #if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(3,8,0)
8186 #include <libavfilter/avfiltergraph.h> /*code is here for old version*/
87 #else
8288 #include <libavfilter/avfilter.h>
89 #endif
8390 #include <libavfilter/buffersink.h>
8491 #include <libavfilter/buffersrc.h>
8592 #endif //QTAV_HAVE(AVFILTER)
455462 } } while(0)
456463
457464 #endif //QTAV_COMPAT_H
465
466 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,33,0)
467 #define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER
468 #endif
469
470 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,56,100)
471 #define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE
472 #endif
473
474 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56,56,100)
475 #define AV_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE
476 #endif
7070 public:
7171 AudioEncoderPrivate()
7272 : AVEncoderPrivate()
73 , frame_size(0)
7374 {
7475 bit_rate = 64000;
7576 }
7879
7980 AudioResampler *resampler;
8081 AudioFormat format, format_used;
82
83 int frame_size; // used if avctx->frame_size == 0
8184 };
8285
8386 class Q_AV_PRIVATE_EXPORT VideoEncoderPrivate : public AVEncoderPrivate
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
3535 public:
3636 FramePrivate()
3737 : timestamp(0)
38 , data_align(1)
3839 {}
3940 virtual ~FramePrivate() {}
4041
4344 QVariantMap metadata;
4445 QByteArray data;
4546 qreal timestamp;
47 int data_align;
4648 };
4749
4850 } //namespace QtAV
00 /******************************************************************************
11 QtAV: Media play library based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
2727 #include <QtCore/QRect>
2828 #include <QtAV/VideoFrame.h>
2929 #include <QtGui/QColor>
30
30 #include "QtAV/Statistics.h"
3131 /*TODO:
3232 * Region of Interest(ROI)
3333 * use matrix to compute out rect, mapped point etc
5353 , out_aspect_ratio_mode(VideoRenderer::VideoAspectRatio)
5454 , out_aspect_ratio(0)
5555 , quality(VideoRenderer::QualityBest)
56 , orientation(0)
5756 , preferred_format(VideoFormat::Format_RGB32)
5857 , force_preferred(false)
5958 , brightness(0)
6160 , hue(0)
6261 , saturation(0)
6362 , bg_color(0, 0, 0)
63 , orientation(0)
6464 {
6565 //conv.setInFormat(PIX_FMT_YUV420P);
6666 //conv.setOutFormat(PIX_FMT_BGR32); //TODO: why not RGB32?
7878 return out_rect0 != out_rect;
7979 }
8080 // dar: displayed aspect ratio in video renderer orientation
81 const qreal dar = (orientation % 180) ? 1.0/outAspectRatio : outAspectRatio;
81 int rotate = orientation;
82 if (statistics) {
83 rotate += int(statistics->video_only.rotate);
84 }
85 const qreal dar = (rotate % 180) ? 1.0/outAspectRatio : outAspectRatio;
8286 //qDebug("out rect: %f %dx%d ==>", out_aspect_ratio, out_rect.width(), out_rect.height());
8387 if (rendererAspectRatio >= dar) { //equals to original video aspect ratio here, also equals to out ratio
8488 //renderer is too wide, use renderer's height, horizonal align center
96100 return out_rect0 != out_rect;
97101 }
98102 virtual void setupQuality() {}
103 int rotation() const {
104 if (!statistics)
105 return orientation;
106 return statistics->video_only.rotate + orientation;
107 }
99108
100109 //draw background when necessary, for example, renderer is resized. Then set to false
101110 bool update_background;
113122 //out_rect: the displayed video frame out_rect in the renderer
114123 QRect out_rect; //TODO: out_out_rect
115124 QRectF roi;
116 int orientation;
117125
118126 VideoFrame video_frame;
119127 VideoFormat::PixelFormat preferred_format;
121129
122130 qreal brightness, contrast, hue, saturation;
123131 QColor bg_color;
132 private:
133 int orientation;
134 friend class VideoRenderer;
124135 };
125136
126137 } //namespace QtAV
2222 #define QTAV_VERSION_H
2323
2424 #define QTAV_MAJOR 1 //((QTAV_VERSION&0xff0000)>>16)
25 #define QTAV_MINOR 12 //((QTAV_VERSION&0xff00)>>8)
25 #define QTAV_MINOR 13 //((QTAV_VERSION&0xff00)>>8)
2626 #define QTAV_PATCH 0 //(QTAV_VERSION&0xff)
2727
2828
1818 BEGIN
1919 BLOCK "000004b0"
2020 BEGIN
21 VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
21 VALUE "CompanyName", "wbsecg1@gmail.com"
2222 VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org"
2323 VALUE "FileVersion", QTAV_VERSION_STR ".0"
24 VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
24 VALUE "LegalCopyright", "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
2525 VALUE "InternalName", "QtAV"
2626 VALUE "OriginalFilename", "QtAV.dll"
2727 VALUE "ProductName", "QtAV"
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2019 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
189189 {
190190 static QString about = QString::fromLatin1("<img src='qrc:/QtAV.svg'><h3>QtAV " QTAV_VERSION_STR_LONG "</h3>\n"
191191 "<p>%1</p><p>%2</p><p>%3 </p>"
192 "<p>Copyright (C) 2012-2016 Wang Bin (aka. Lucas Wang) <a href='mailto:wbsecg1@gmail.com'>wbsecg1@gmail.com</a></p>\n"
192 "<p>Copyright (C) 2012-2019 Wang Bin (aka. Lucas Wang) <a href='mailto:wbsecg1@gmail.com'>wbsecg1@gmail.com</a></p>\n"
193193 "<p>%4: <a href='http://qtav.org/donate.html'>http://qtav.org/donate.html</a></p>\n"
194194 "<p>%5: <a href='https://github.com/wang-bin/QtAV'>https://github.com/wang-bin/QtAV</a></p>\n"
195195 "<p>%6: <a href='http://qtav.org'>http://qtav.org</a></p>"
196196 ).arg(QObject::tr("Multimedia framework base on Qt and FFmpeg.\n"))
197197 .arg(QObject::tr("Distributed under the terms of LGPLv2.1 or later.\n"))
198 .arg(QObject::tr("Shanghai University->S3 Graphics->Deepin->PPTV, Shanghai, China"))
198 .arg(QObject::tr("Shanghai, China"))
199199 .arg(QObject::tr("Donate"))
200200 .arg(QObject::tr("Source"))
201201 .arg(QObject::tr("Home page"));
274274 void* obj = const_cast<void*>(reinterpret_cast<const void*>(avformat_get_class()));
275275 opts = Internal::optionsToString((void*)&obj);
276276 opts.append(ushort('\n'));
277 av_register_all();
277 #if AVFORMAT_STATIC_REGISTER
278 const AVInputFormat *i = NULL;
279 void* it = NULL;
280 while ((i = av_demuxer_iterate(&it))) {
281 #else
278282 AVInputFormat *i = NULL;
283 av_register_all(); // MUST register all input/output formats
279284 while ((i = av_iformat_next(i))) {
285 #endif
280286 QString opt(Internal::optionsToString((void*)&i->priv_class).trimmed());
281287 if (opt.isEmpty())
282288 continue;
284290 .arg(QLatin1String(i->name))
285291 .arg(opt));
286292 }
293 #if AVFORMAT_STATIC_REGISTER
294 const AVOutputFormat *o = NULL;
295 it = NULL;
296 while ((o = av_muxer_iterate(&it))) {
297 #else
298 av_register_all(); // MUST register all input/output formats
287299 AVOutputFormat *o = NULL;
288300 while ((o = av_oformat_next(o))) {
301 #endif
289302 QString opt(Internal::optionsToString((void*)&o->priv_class).trimmed());
290303 if (opt.isEmpty())
291304 continue;
304317 void* obj = const_cast<void*>(reinterpret_cast<const void*>(avcodec_get_class()));
305318 opts = Internal::optionsToString((void*)&obj);
306319 opts.append(ushort('\n'));
320 const AVCodec* c = NULL;
321 #if AVCODEC_STATIC_REGISTER
322 void* it = NULL;
323 while ((c = av_codec_iterate(&it))) {
324 #else
307325 avcodec_register_all();
308 AVCodec* c = NULL;
309 while ((c=av_codec_next(c))) {
326 while ((c = av_codec_next(c))) {
327 #endif
310328 QString opt(Internal::optionsToString((void*)&c->priv_class).trimmed());
311329 if (opt.isEmpty())
312330 continue;
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
5555 , coded_width(0)
5656 , coded_height(0)
5757 , gop_size(0)
58 , rotate(0)
5859 , d(new Private())
5960 {
6061 }
6566 , coded_width(v.coded_width)
6667 , coded_height(v.coded_height)
6768 , gop_size(v.gop_size)
69 , rotate(v.rotate)
6870 , d(v.d)
6971 {
7072 }
7678 coded_width = v.coded_width;
7779 coded_height = v.coded_height;
7880 gop_size = v.gop_size;
81 rotate = v.rotate;
7982 d = v.d;
8083 return *this;
8184 }
00 /******************************************************************************
11 VideoCapture.cpp: description
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
8383 QMetaObject::invokeMethod(cap, "failed");
8484 return;
8585 }
86 if (file.write(frame.frameData()) <= 0) {
86 int sz = 0;
87 const char* data = (const char*)frame.frameDataPtr(&sz);
88 if (file.write(data, sz) <= 0) {
8789 qWarning("VideoCapture is failed to write captured frame with original format");
8890 QMetaObject::invokeMethod(cap, "failed");
8991 file.close();
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
153153 {
154154 }
155155
156 VideoFrame::VideoFrame(int width, int height, const VideoFormat &format, const QByteArray& data)
156 VideoFrame::VideoFrame(int width, int height, const VideoFormat &format, const QByteArray& data, int alignment)
157157 : Frame(new VideoFramePrivate(width, height, format))
158158 {
159159 Q_D(VideoFrame);
160160 d->data = data;
161 d->data_align = alignment;
161162 }
162163
163164 VideoFrame::VideoFrame(const QImage& image)
352353 VideoFrame f(to(VideoFormat(VideoFormat::pixelFormatFromImageFormat(fmt)), dstSize, roi));
353354 if (!f)
354355 return QImage();
355 QImage image((const uchar*)f.frameData().constData(), f.width(), f.height(), f.bytesPerLine(0), fmt);
356 QImage image(f.frameDataPtr(), f.width(), f.height(), f.bytesPerLine(0), fmt);
356357 return image.copy();
357358 }
358359
394395 qWarning() << "VideoFrame::to error: " << format() << "=>" << fmt;
395396 return VideoFrame();
396397 }
397 VideoFrame f(w, h, fmt, conv.outData());
398 VideoFrame f(w, h, fmt, conv.outData(), ImageConverter::DataAlignment);
398399 f.setBits(conv.outPlanes());
399400 f.setBytesPerLine(conv.outLineSizes());
400401 if (fmt.isRGB()) {
2525 #include <QtCore/QScopedPointer>
2626 #include <QtCore/QStringList>
2727 #include <QtCore/QThread>
28 #include <QtCore/QMutex>
29 #include <QtCore/QMutexLocker>
2830 #include "QtAV/VideoCapture.h"
2931 #include "QtAV/VideoDecoder.h"
3032 #include "QtAV/AVDemuxer.h"
3234 #include "utils/BlockingQueue.h"
3335 #include "utils/Logger.h"
3436
35 // TODO: event and signal do not work
36 #define ASYNC_SIGNAL 0
37 #define ASYNC_EVENT 0
38 #define ASYNC_TASK 1
3937 namespace QtAV {
4038
4139 class ExtractThread : public QThread {
4240 public:
4341 ExtractThread(QObject *parent = 0)
4442 : QThread(parent)
43 , timeout_ms(50UL)
4544 , stop(false)
4645 {
47 tasks.setCapacity(1); // avoid too frequent
46 // avoid too frequent -- we only care about the latest request
47 // whether it be for a frame or to stop thread or whatever else.
48 tasks.setCapacity(1);
4849 }
4950 ~ExtractThread() {
5051 waitStop();
5657 wait();
5758 }
5859
60 unsigned long timeout_ms;
61
5962 void addTask(QRunnable* t) {
60 if (tasks.size() >= tasks.capacity()) {
61 QRunnable *task = tasks.take(); //clear only for seek task
62 if (task->autoDelete())
63 // Note that while a simpler solution would have been to not use
64 // a custom 'Task' mechanism but rather to use signals
65 // or QEvent posting to this thread -- the approach here has a very
66 // advantageous property. Namely, duplicate repeated requests for
67 // a frame end up getting dropped and only the latest request gets
68 // serviced. This interoperates well with eg VideoPreviewWidget
69 // which really only cares about the latest frame requested by the user.
70 while (tasks.size() >= tasks.capacity() && tasks.capacity() > 0) {
71 // Race condition existed here -- to avoid it we need to take()
72 // with a timeout (to make sure it doesn't hang) and check return value.
73 // This is because extractor thread is also calling .take() at the same time,
74 // and in rare cases the above expression in the while loop evaluates to true
75 // while by the time the below line executes the underlying queue may become empty.
76 // This led to very occasional hangs. Hence this .take() call was modified to include
77 // a timeout.
78 QRunnable *task = tasks.take(timeout_ms); //clear for seek & stop task
79 if (task && task->autoDelete())
6380 delete task;
6481 }
65 tasks.put(t);
82 if (!tasks.put(t,timeout_ms)) {
83 qWarning("ExtractThread::addTask -- added a task to an already-full queue! FIXME!");
84 }
6685 }
6786 void scheduleStop() {
6887 class StopTask : public QRunnable {
6988 public:
7089 StopTask(ExtractThread* t) : thread(t) {}
71 void run() { thread->stop = true;}
90 void run() { thread->stop = true; }
7291 private:
7392 ExtractThread *thread;
7493 };
7796
7897 protected:
7998 virtual void run() {
80 #if ASYNC_TASK
8199 while (!stop) {
82100 QRunnable *task = tasks.take();
83 if (!task)
84 return;
85 task->run();
86 if (task->autoDelete())
87 delete task;
88 }
89 #else
90 exec();
91 #endif //ASYNC_TASK
101 if (task) {
102 task->run();
103 if (task->autoDelete())
104 delete task;
105 }
106 }
107 qDebug("ExtractThread exiting...");
92108 }
93109 public:
94110 volatile bool stop;
102118 {
103119 public:
104120 VideoFrameExtractorPrivate()
105 : extracted(false)
106 , abort_seek(false)
121 : abort_seek(false)
107122 , async(true)
108123 , has_video(true)
109124 , auto_extract(true)
134149 #if QTAV_HAVE(VDA)
135150 // << QStringLiteral("VDA") // only 1 app can use VDA at a given time
136151 #endif //QTAV_HAVE(VDA)
137 << QStringLiteral("FFmpeg");
152 #if QTAV_HAVE(VIDEOTOOLBOX)
153 // << QStringLiteral("VideoToolbox")
154 #endif //QTAV_HAVE(VIDEOTOOLBOX)
155 << QStringLiteral("FFmpeg");
138156 }
139157 ~VideoFrameExtractorPrivate() {
140158 // stop first before demuxer and decoder close to avoid running new seek task after demuxer is closed.
167185 else
168186 precision = kDefaultPrecision;
169187 }
188 demuxer.setStreamIndex(AVDemuxer::VideoStream, 0);
170189 foreach (const QString& c, codecs) {
171190 VideoDecoder *vd = VideoDecoder::create(c.toUtf8().constData());
172191 if (!vd)
173192 continue;
174193 decoder.reset(vd);
175 decoder->setCodecContext(demuxer.videoCodecContext());
176 if (!decoder->open()) {
194 AVCodecContext *cctx = demuxer.videoCodecContext();
195 if (cctx) decoder->setCodecContext(demuxer.videoCodecContext());
196 if (!cctx || !decoder->open()) {
177197 decoder.reset(0);
178198 continue;
179199 }
188208 }
189209
190210 // return the key frame position
191 bool extractInPrecision(qint64 value, int range) {
211 bool extractInPrecision(qint64 value, int range, QString & err, bool & aborted) {
192212 abort_seek = false;
213 err = "";
214 aborted = false;
193215 frame = VideoFrame();
194216 if (value < demuxer.startTime())
195217 value += demuxer.startTime();
201223 bool warn_out_of_range = true;
202224 while (!demuxer.atEnd()) {
203225 if (abort_seek) {
226 err = "abort seek before read";
204227 qDebug("VideoFrameExtractor abort seek before read");
228 aborted = true;
205229 return false;
206230 }
207231 if (!demuxer.readFrame())
235259 }
236260 if (!pkt.isValid()) {
237261 qWarning("VideoFrameExtractor failed to get a packet at %lld", value);
262 err = QString().sprintf("failed to get a packet at %lld",value);
238263 return false;
239264 }
240265 decoder->flush(); //must flush otherwise old frames will be decoded at the beginning
244269 while (k < 2 && !frame.isValid()) {
245270 if (abort_seek) {
246271 qDebug("VideoFrameExtractor abort seek before decoding key frames");
272 err = "abort seek before decoding key frames";
273 aborted = true;
247274 return false;
248275 }
249276 //qWarning("invalid key frame!!!!! undecoded: %d", decoder->undecodedSize());
267294 while (!demuxer.atEnd()) {
268295 if (abort_seek) {
269296 qDebug("VideoFrameExtractor abort seek after key frame before read");
297 err = "abort seek after key frame before read";
298 aborted = true;
270299 return false;
271300 }
272301 if (!demuxer.readFrame())
297326 if (!decoder->decode(pkt)) {
298327 qWarning("!!!!!!!!!decode failed!!!!");
299328 frame = VideoFrame();
329 err = "decode failed";
300330 return false;
301331 }
302332 // store the last decoded frame because next frame may be out of range
319349 if (diff > range && t > pts) {
320350 qWarning("out pts out of range. diff=%lld, range=%d", diff, range);
321351 frame = VideoFrame();
352 err = QString().sprintf("out pts out of range. diff=%lld, range=%d", diff, range);
322353 return false;
323354 }
324355 }
325356 ++seek_count;
326357 // now we get the final frame
327358 if (demuxer.atEnd())
328 releaseResourceInternal();
359 releaseResourceInternal(false);
329360 return true;
330361 }
331 void releaseResourceInternal() {
362 void releaseResourceInternal(bool releaseFrame = true) {
363 if (releaseFrame) frame = VideoFrame();
332364 seek_count = 0;
333365 // close codec context first.
334366 decoder.reset(0);
347379 thread.addTask(new Cleaner(this));
348380 }
349381
350 bool extracted;
351382 volatile bool abort_seek;
352383 bool async;
353384 bool has_video;
355386 bool auto_extract;
356387 bool auto_precision;
357388 int seek_count;
358 qint64 position;
359 int precision;
360 QString source;
389 qint64 position; ///< only read/written by this->thread(), never read by extractor thread so no lock necessary
390 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
391 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
361392 AVDemuxer demuxer;
362393 QScopedPointer<VideoDecoder> decoder;
363 VideoFrame frame;
394 VideoFrame frame; ///< important: we only allow the extract thread to modify this value
364395 QStringList codecs;
365396 ExtractThread thread;
366397 static QVariantHash dec_opt_framedrop, dec_opt_normal;
373404 QObject(parent)
374405 {
375406 DPTR_D(VideoFrameExtractor);
376 moveToThread(&d.thread);
377407 d.thread.start();
378 connect(this, SIGNAL(aboutToExtract(qint64)), SLOT(extractInternal(qint64)));
379408 }
380409
381410 void VideoFrameExtractor::setSource(const QString url)
386415 d.source = url;
387416 d.has_video = true;
388417 Q_EMIT sourceChanged();
389 d.frame = VideoFrame();
390418 d.safeReleaseResource();
391419 }
392420
431459 if (qAbs(value - d.position) < precision()) {
432460 return;
433461 }
434 d.frame = VideoFrame();
435 d.extracted = false;
436462 d.position = value;
437463 Q_EMIT positionChanged();
438464 if (!autoExtract())
453479 d.auto_precision = value < 0;
454480 // explain why value (p0) is used but not the actual decoded position (p)
455481 // it's key frame finding rule
456 if (value >= 0)
482 if (value >= 0) {
457483 d.precision = value;
484 }
458485 Q_EMIT precisionChanged();
459486 }
460487
461488 int VideoFrameExtractor::precision() const
462489 {
463490 return d_func().precision;
464 }
465
466 bool VideoFrameExtractor::event(QEvent *e)
467 {
468 //qDebug("event: %d", e->type());
469 if (e->type() != QEvent::User)
470 return QObject::event(e);
471 extractInternal(position()); // FIXME: wrong position
472 return true;
473491 }
474492
475493 void VideoFrameExtractor::extract()
479497 extractInternal(position());
480498 return;
481499 }
482 #if ASYNC_SIGNAL
483 else {
484 Q_EMIT aboutToExtract(position());
485 return;
486 }
487 #endif
488 #if ASYNC_TASK
489500 class ExtractTask : public QRunnable {
490501 public:
491502 ExtractTask(VideoFrameExtractor *e, qint64 t)
499510 VideoFrameExtractor *extractor;
500511 qint64 position;
501512 };
513 // We want to abort the previous extract() since we are
514 // only interested in the latest request.
515 // So, abort_seek is a 'previous frame extract abort mechanism'
516 // -- this flag is repeatedly checked by extractInPrecision()
517 // (called by extractInternal()) and if true, method returns early.
518 // Note if seek/decode is aborted, aborted() signal will be emitted.
502519 d.abort_seek = true;
503520 d.thread.addTask(new ExtractTask(this, position()));
504 return;
505 #endif
506 #if ASYNC_EVENT
507 qApp->postEvent(this, new QEvent(QEvent::User));
508 #endif //ASYNC_EVENT
509521 }
510522
511523 void VideoFrameExtractor::extractInternal(qint64 pos)
513525 DPTR_D(VideoFrameExtractor);
514526 int precision_old = precision();
515527 if (!d.checkAndOpen()) {
516 Q_EMIT error();
528 Q_EMIT error("Cannot open file");
517529 //qWarning("can not open decoder....");
518 return; // error handling
530 return;
519531 }
520532 if (precision_old != precision()) {
521533 Q_EMIT precisionChanged();
522534 }
523 d.extracted = d.extractInPrecision(pos, precision());
524 if (!d.extracted) {
525 Q_EMIT error();
535 bool extractOk = false, isAborted = true;
536 QString err;
537 extractOk = d.extractInPrecision(pos, precision(), err, isAborted);
538 if (!extractOk) {
539 if (isAborted)
540 Q_EMIT aborted(QString().sprintf("Abort at position %lld: %s",pos,err.toLatin1().constData()));
541 else
542 Q_EMIT error(QString().sprintf("Cannot extract frame at position %lld: %s",pos,err.toLatin1().constData()));
526543 return;
527544 }
528545 Q_EMIT frameExtracted(d.frame);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
242242 DPTR_D(VideoThread);
243243 if (!d.dec || !d.dec->isAvailable() || !d.outputSet)
244244 return;
245 resetState();
245 // resetState(); // we can't reset the thread state from here
246246 if (d.capture->autoSave()) {
247247 d.capture->setCaptureName(QFileInfo(d.statistics->url).completeBaseName());
248248 }
319319 if (!pkt.isValid()) {
320320 // may be we should check other information. invalid packet can come from
321321 wait_key_frame = true;
322 qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size());
322 qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size());
323323 d.dec->flush(); //d.dec instead of dec because d.dec maybe changed in processNextTask() but dec is not
324324 d.render_pts0 = pkt.pts;
325325 sync_id = pkt.position;
487487 dec->setOptions(*dec_opt);
488488 if (!dec->decode(pkt)) {
489489 d.pts_history.push_back(d.pts_history.back());
490 qWarning("Decode video failed. undecoded: %d/%d", dec->undecodedSize(), pkt.data.size());
490 //qWarning("Decode video failed. undecoded: %d/%d", dec->undecodedSize(), pkt.data.size());
491491 if (pkt.isEOF()) {
492492 Q_EMIT eofDecoded();
493493 qDebug("video decode eof done. d.render_pts0: %.3f", d.render_pts0);
522522 continue;
523523 }
524524 pkt_data = pkt.data.constData();
525 if (frame.timestamp() <= 0)
525 if (frame.timestamp() < 0)
526526 frame.setTimestamp(pkt.pts); // pkt.pts is wrong. >= real timestamp
527527 const qreal pts = frame.timestamp();
528528 d.pts_history.push_back(pts);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
4646 AVDecoder::AVDecoder(AVDecoderPrivate &d)
4747 :DPTR_INIT(&d)
4848 {
49 #if !AVCODEC_STATIC_REGISTER
4950 avcodec_register_all(); // avcodec_find_decoder will always be used
51 #endif
5052 }
5153
5254 AVDecoder::~AVDecoder()
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
4242 static QStringList codecs;
4343 if (!codecs.isEmpty())
4444 return codecs;
45 const AVCodec* c = NULL;
46 #if AVCODEC_STATIC_REGISTER
47 void* it = NULL;
48 while ((c = av_codec_iterate(&it))) {
49 #else
4550 avcodec_register_all();
46 AVCodec* c = NULL;
47 while ((c=av_codec_next(c))) {
51 while ((c = av_codec_next(c))) {
52 #endif
4853 if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_AUDIO)
4954 continue;
5055 codecs.append(QString::fromLatin1(c->name));
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
6262 : AudioDecoderPrivate()
6363 , frame(av_frame_alloc())
6464 {
65 #if !AVCODEC_STATIC_REGISTER
6566 avcodec_register_all();
67 #endif
6668 }
6769 ~AudioDecoderFFmpegPrivate() {
6870 if (frame) {
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
4444 static QStringList codecs;
4545 if (!codecs.isEmpty())
4646 return codecs;
47 const AVCodec* c = NULL;
48 #if AVCODEC_STATIC_REGISTER
49 void* it = NULL;
50 while ((c = av_codec_iterate(&it))) {
51 #else
4752 avcodec_register_all();
48 AVCodec* c = NULL;
49 while ((c=av_codec_next(c))) {
53 while ((c = av_codec_next(c))) {
54 #endif
5055 if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_AUDIO)
5156 continue;
5257 codecs.append(QString::fromLatin1(c->name));
7984 return d_func().format_used;
8085 }
8186
87 int AudioEncoder::frameSize() const
88 {
89 return d_func().frame_size;
90 }
8291 } //namespace QtAV
5252 public:
5353 AudioEncoderFFmpegPrivate()
5454 : AudioEncoderPrivate()
55 , frame_size(0)
5655 {
5756 avcodec_register_all();
5857 // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264).
6160 bool open() Q_DECL_OVERRIDE;
6261 bool close() Q_DECL_OVERRIDE;
6362
64 int frame_size; // used if avctx->frame_size == 0
6563 QByteArray buffer;
6664 };
6765
152150 } else {
153151 buffer_size = frame_size*format_used.bytesPerSample()*format_used.channels()*2+200;
154152 }
155 if (buffer_size < FF_MIN_BUFFER_SIZE)
156 buffer_size = FF_MIN_BUFFER_SIZE;
153 if (buffer_size < AV_INPUT_BUFFER_MIN_SIZE)
154 buffer_size = AV_INPUT_BUFFER_MIN_SIZE;
157155 buffer.resize(buffer_size);
158156 return true;
159157 }
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2016)
55
6060 InteropResource* InteropResource::create(InteropType type)
6161 {
6262 if (type == InteropAuto) {
63 #ifdef Q_OS_MACX
63 type = InteropCVOpenGLES;
64 #if defined(Q_OS_MACX)
6465 type = InteropIOSurface;
65 #else
66 type = InteropCVPixelBuffer;
67 type = InteropCVOpenGLES;
66 #endif
67 #if defined(__builtin_available)
68 if (__builtin_available(iOS 11, macOS 10.6, *))
69 type = InteropIOSurface;
6870 #endif
6971 }
7072 switch (type) {
7173 case InteropCVPixelBuffer: return CreateInteropCVPixelbuffer();
72 #ifdef Q_OS_MACX
74 #if defined(Q_OS_MACX) || defined(__IPHONE_11_0)
7375 case InteropIOSurface: return CreateInteropIOSurface();
7476 //case InteropCVOpenGL: return CreateInteropCVOpenGL();
7577 #else
+0
-116
src/codec/video/SurfaceInteropIOSurface.cpp less more
0 /******************************************************************************
1 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
3
4 * This file is part of QtAV (from 2016)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ******************************************************************************/
20
21 #include "SurfaceInteropCV.h"
22 #include <IOSurface/IOSurface.h>
23 #include "QtAV/VideoFrame.h"
24 #include "opengl/OpenGLHelper.h"
25
26 namespace QtAV {
27 namespace cv {
28 VideoFormat::PixelFormat format_from_cv(int cv);
29
30 // https://www.opengl.org/registry/specs/APPLE/rgb_422.txt
31 // https://www.opengl.org/registry/specs/APPLE/ycbcr_422.txt uyvy: UNSIGNED_SHORT_8_8_REV_APPLE, yuy2: GL_UNSIGNED_SHORT_8_8_APPLE
32 // check extension GL_APPLE_rgb_422 and rectangle?
33 class InteropResourceIOSurface Q_DECL_FINAL : public InteropResource
34 {
35 public:
36 bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE;
37 bool mapToTexture2D() const Q_DECL_OVERRIDE { return false;}
38 bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE;
39 GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE
40 {
41 Q_UNUSED(fmt);
42 Q_UNUSED(plane);
43 Q_UNUSED(planeWidth);
44 Q_UNUSED(planeHeight);
45 GLuint tex = 0;
46 DYGL(glGenTextures(1, &tex));
47 return tex;
48 }
49 };
50
51 InteropResource* CreateInteropIOSurface()
52 {
53 return new InteropResourceIOSurface();
54 }
55
56 bool InteropResourceIOSurface::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt)
57 {
58 switch (cvfmt) {
59 case '2vuy':
60 case 'yuvs': {
61 *outFmt = VideoFormat::Format_VYU;
62 if (strides[0] <= 0)
63 strides[0] = 4*width; //RGB layout: BRGX
64 else
65 strides[0] *= 2;
66 }
67 break;
68 default:
69 return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt);
70 }
71 return true;
72 }
73
74 bool InteropResourceIOSurface::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane)
75 {
76 Q_UNUSED(w);
77 Q_UNUSED(h);
78 const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf);
79 GLint iformat;
80 GLenum format, dtype;
81 getParametersGL(pixfmt, &iformat, &format, &dtype, plane);
82 switch (pixfmt) {
83 case '2vuy':
84 case 'yuvs':
85 iformat = GL_RGB8; // ES2 requires internal format and format are the same. OSX can use internal format GL_RGB or sized GL_RGB8
86 format = GL_RGB_422_APPLE;
87 dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE;
88 break;
89 // macOS: GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV
90 // GL_YCBCR_422_APPLE: convert to rgb texture internally (bt601). only supports OSX
91 // GL_RGB_422_APPLE: raw yuv422 texture
92 case 'BGRA':
93 iformat = GL_RGBA8;
94 format = GL_BGRA;
95 dtype = GL_UNSIGNED_INT_8_8_8_8_REV;
96 break;
97 default:
98 break;
99 }
100 const GLenum target = GL_TEXTURE_RECTANGLE;
101 DYGL(glBindTexture(target, *tex));
102 const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane);
103 const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane);
104 //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype);
105
106 const IOSurfaceRef surface = CVPixelBufferGetIOSurface(buf);
107 CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, planeW, planeH, format, dtype, surface, plane);
108 if (err != kCGLNoError) {
109 qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err));
110 }
111 DYGL(glBindTexture(target, 0));
112 return true;
113 }
114 } // namespace cv
115 } // namespace QtAV
0 /******************************************************************************
1 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
3
4 * This file is part of QtAV (from 2016)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ******************************************************************************/
20 #define IOS_USE_PRIVATE 0 // private symbols are forbidden by app store
21 #include "SurfaceInteropCV.h"
22 #ifdef Q_OS_IOS
23 #import <OpenGLES/EAGL.h>
24 #include <CoreVideo/CVOpenGLESTextureCache.h>
25 # ifdef __IPHONE_11_0 // always defined in new sdk
26 # import <OpenGLES/EAGLIOSurface.h>
27 # endif //__IPHONE_11_0
28 # if COREVIDEO_SUPPORTS_IOSURFACE && IOS_USE_PRIVATE
29 // 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
30 @interface EAGLContext()
31 - (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
32 @end
33 # endif //COREVIDEO_SUPPORTS_IOSURFACE
34 #else
35 #include <IOSurface/IOSurface.h>
36 #endif //Q_OS_IOS
37
38 #include "QtAV/VideoFrame.h"
39 #include "opengl/OpenGLHelper.h"
40
41 namespace QtAV {
42 namespace cv {
43 VideoFormat::PixelFormat format_from_cv(int cv);
44
45 // https://www.opengl.org/registry/specs/APPLE/rgb_422.txt
46 // https://www.opengl.org/registry/specs/APPLE/ycbcr_422.txt uyvy: UNSIGNED_SHORT_8_8_REV_APPLE, yuy2: GL_UNSIGNED_SHORT_8_8_APPLE
47 // check extension GL_APPLE_rgb_422 and rectangle?
48 class InteropResourceIOSurface Q_DECL_FINAL : public InteropResource
49 {
50 public:
51 bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE;
52 bool mapToTexture2D() const Q_DECL_OVERRIDE { return false;}
53 bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE;
54 GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE
55 {
56 Q_UNUSED(fmt);
57 Q_UNUSED(plane);
58 Q_UNUSED(planeWidth);
59 Q_UNUSED(planeHeight);
60 GLuint tex = 0;
61 DYGL(glGenTextures(1, &tex));
62 return tex;
63 }
64 };
65
66 InteropResource* CreateInteropIOSurface()
67 {
68 return new InteropResourceIOSurface();
69 }
70
71 bool InteropResourceIOSurface::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt)
72 {
73 switch (cvfmt) {
74 case '2vuy':
75 case 'yuvs': {
76 *outFmt = VideoFormat::Format_VYU;
77 if (strides[0] <= 0)
78 strides[0] = 4*width; //RGB layout: BRGX
79 else
80 strides[0] *= 2;
81 }
82 break;
83 default:
84 return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt);
85 }
86 return true;
87 }
88
89 bool InteropResourceIOSurface::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane)
90 {
91 Q_UNUSED(w);
92 Q_UNUSED(h);
93 #if COREVIDEO_SUPPORTS_IOSURFACE // IOSurface header is not included otherwise
94 const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf);
95 GLint iformat;
96 GLenum format, dtype;
97 getParametersGL(pixfmt, &iformat, &format, &dtype, plane);
98 switch (pixfmt) {
99 case '2vuy':
100 case 'yuvs':
101 // ES2 requires internal format and format are the same. desktop can use internal format GL_RGB or sized GL_RGB8
102 #ifdef Q_OS_IOS
103 iformat = GL_RGB_422_APPLE;
104 #else
105 iformat = GL_RGB8;
106 #endif
107 format = GL_RGB_422_APPLE;
108 dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE;
109 break;
110 // macOS: GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV
111 // GL_YCBCR_422_APPLE: convert to rgb texture internally (bt601). only supports OSX
112 // GL_RGB_422_APPLE: raw yuv422 texture
113 case 'BGRA':
114 #ifdef Q_OS_IOS
115 iformat = GL_RGBA;
116 format = GL_RGBA;
117 #else
118 iformat = GL_RGBA8;
119 format = GL_BGRA;
120 dtype = GL_UNSIGNED_INT_8_8_8_8_REV;
121 #endif
122 break;
123 default:
124 break;
125 }
126 const GLenum target = GL_TEXTURE_RECTANGLE;
127 DYGL(glBindTexture(target, *tex));
128 const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane);
129 const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane);
130 //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype);
131
132 const IOSurfaceRef surface = CVPixelBufferGetIOSurface(buf); // available in ios11 sdk, ios4 runtime
133 #ifdef Q_OS_IOS
134 BOOL ok = false;
135 # ifdef __IPHONE_11_0
136 # if __has_builtin(__builtin_available) // xcode9: runtime check @available is required, or -Wno-unguarded-availability-new
137 if (@available(iOS 11, *)) //symbol is available in iOS5+, but crashes at runtime
138 # else
139 if (false)
140 # endif // __has_builtin(__builtin_available)
141 ok = [[EAGLContext currentContext] texImageIOSurface:surface target:target internalFormat:iformat width:planeW height:planeH format:format type:dtype plane:plane];
142 else // fallback to old private api if runtime version < 11
143 # endif //__IPHONE_11_0
144 {
145 #if IOS_USE_PRIVATE
146 ok = [[EAGLContext currentContext] texImageIOSurface:surface target:target internalFormat:iformat width:planeW height:planeH format:format type:dtype plane:plane invert:NO];
147 #endif //IOS_USE_PRIVATE
148 }
149 if (!ok) {
150 qWarning("error creating IOSurface texture at plane %d", plane);
151 }
152 #else
153 CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, planeW, planeH, format, dtype, surface, plane);
154 if (err != kCGLNoError) {
155 qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err));
156 }
157 #endif // Q_OS_IOS
158 DYGL(glBindTexture(target, 0));
159 #endif //COREVIDEO_SUPPORTS_IOSURFACE
160 return true;
161 }
162 } // namespace cv
163 } // namespace QtAV
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
9292 static QStringList codecs;
9393 if (!codecs.isEmpty())
9494 return codecs;
95 const AVCodec* c = NULL;
96 #if AVCODEC_STATIC_REGISTER
97 void* it = NULL;
98 while ((c = av_codec_iterate(&it))) {
99 #else
95100 avcodec_register_all();
96 AVCodec* c = NULL;
97 while ((c=av_codec_next(c))) {
101 while ((c = av_codec_next(c))) {
102 #endif
98103 if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_VIDEO)
99104 continue;
100105 codecs.append(QString::fromLatin1(c->name));
00 /******************************************************************************
11 QtAV: Media play library based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2016)
55
3333
3434 static bool check_ffmpeg_hevc_dxva2()
3535 {
36 #if !AVCODEC_STATIC_REGISTER
3637 avcodec_register_all();
38 #endif
3739 AVHWAccel *hwa = av_hwaccel_next(0);
3840 while (hwa) {
3941 if (strncmp("hevc_dxva2", hwa->name, 10) == 0)
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
146146 .arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav"))
147147 .arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch);
148148 }
149 virtual VideoFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL;
150149
151150 // TODO: av_opt_set in setter
152151 void setSkipLoopFilter(DiscardType value);
282281 return VideoDecoderId_FFmpeg;
283282 }
284283
285 VideoFrame VideoDecoderFFmpeg::frame()
286 {
287 DPTR_D(VideoDecoderFFmpeg);
288 if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx)
289 return VideoFrame();
290 // it's safe if width, height, pixfmt will not change, only data change
291 VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
292 frame.setDisplayAspectRatio(d.getDAR(d.frame));
293 frame.setBits(d.frame->data);
294 frame.setBytesPerLine(d.frame->linesize);
295 // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
296 frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
297 frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
298 d.updateColorDetails(&frame);
299 if (frame.format().hasPalette()) {
300 frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4));
301 }
302 return frame;
303 }
304
305284 void VideoDecoderFFmpeg::setSkipLoopFilter(DiscardType value)
306285 {
307286 DPTR_D(VideoDecoderFFmpeg);
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
131131 //qDebug("pic_type=%c", av_get_picture_type_char(d.frame->pict_type));
132132 d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size());
133133 if (ret < 0) {
134 qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret));
134 //qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret));
135135 return false;
136136 }
137137 if (!got_frame_ptr) {
147147 return true;
148148 }
149149
150 VideoFrame VideoDecoderFFmpegBase::frame()
151 {
152 DPTR_D(VideoDecoderFFmpegBase);
153 if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx)
154 return VideoFrame();
155 // it's safe if width, height, pixfmt will not change, only data change
156 VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
157 frame.setDisplayAspectRatio(d.getDAR(d.frame));
158 frame.setBits(d.frame->data);
159 frame.setBytesPerLine(d.frame->linesize);
160 // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
161 frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
162 frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
163 d.updateColorDetails(&frame);
164 if (frame.format().hasPalette()) {
165 frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4));
166 }
167 return frame;
168 }
150169 } //namespace QtAV
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
3434 DPTR_DECLARE_PRIVATE(VideoDecoderFFmpegBase)
3535 public:
3636 virtual bool decode(const Packet& packet) Q_DECL_OVERRIDE;
37 virtual VideoFrame frame() Q_DECL_OVERRIDE;
3738 protected:
3839 VideoDecoderFFmpegBase(VideoDecoderFFmpegBasePrivate &d);
3940 private:
4950 , width(0)
5051 , height(0)
5152 {
53 #if !AVCODEC_STATIC_REGISTER
5254 avcodec_register_all();
55 #endif
5356 frame = av_frame_alloc();
5457 }
5558 virtual ~VideoDecoderFFmpegBasePrivate() {
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
154154
155155 AVPixelFormat VideoDecoderFFmpegHWPrivate::getFormat(struct AVCodecContext *avctx, const AVPixelFormat *pi_fmt)
156156 {
157 #ifdef AV_HWACCEL_FLAG_ALLOW_SOFTWARE
158 avctx->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_SOFTWARE;
159 #endif
157160 bool can_hwaccel = false;
158161 for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) {
159162 const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]);
191194 end:
192195 qWarning("hardware acceleration is not available" );
193196 /* Fallback to default behaviour */
197 #if QTAV_HAVE(AVBUFREF)
198 avctx->get_buffer2 = avcodec_default_get_buffer2;
199 #endif
194200 return avcodec_default_get_format(avctx, pi_fmt);
195201 }
196202
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2013)
55
6262 codec_ctx->reget_buffer = reget_buffer;
6363 #endif //QTAV_HAVE(AVBUFREF)
6464 }
65
66 virtual bool open() Q_DECL_OVERRIDE { return prepare();}
67 virtual void close() Q_DECL_OVERRIDE {restore();}
6568 // return hwaccel_context or null
6669 virtual void* setup(AVCodecContext* avctx) = 0;
6770
00 /******************************************************************************
11 QtAV: Media play library based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2016)
55
1818 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1919 ******************************************************************************/
2020
21 #include "VideoDecoderFFmpegBase.h"
21 #include "VideoDecoderFFmpegHW.h"
22 #include "VideoDecoderFFmpegHW_p.h"
2223 #if FFMPEG_MODULE_CHECK(LIBAVCODEC, 57, 28, 100)
2324 #define QTAV_HAVE_MEDIACODEC 1
2425 #endif
2829 #include <QtAndroidExtras>
2930 extern "C" {
3031 #include <libavcodec/jni.h>
32 #include <libavcodec/mediacodec.h>
3133 }
34
35 // 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.
36 #ifdef MEDIACODEC_TEXTURE
37 #include "QtAV/SurfaceInterop.h"
38 #include "opengl/OpenGLHelper.h"
39 #include "MediaCodecTextureStandalone.h"
40 #endif
3241
3342 namespace QtAV {
3443 class VideoDecoderMediaCodecPrivate;
35 class VideoDecoderMediaCodec : public VideoDecoderFFmpegBase
44 class VideoDecoderMediaCodec Q_DECL_FINAL : public VideoDecoderFFmpegHW
3645 {
3746 DPTR_DECLARE_PRIVATE(VideoDecoderMediaCodec)
3847 public:
3948 VideoDecoderMediaCodec();
49
4050 VideoDecoderId id() const Q_DECL_OVERRIDE;
4151 QString description() const Q_DECL_OVERRIDE;
4252 VideoFrame frame() Q_DECL_OVERRIDE;
53
54 void flush() Q_DECL_OVERRIDE { // workaroudn for EAGAIN error in avcodec_receive_frame
55 if (copyMode() == ZeroCopy) {
56
57 } else {
58 VideoDecoderFFmpegHW::flush();
59 }
60 }
4361 };
4462
4563 extern VideoDecoderId VideoDecoderId_MediaCodec;
4664 FACTORY_REGISTER(VideoDecoder, MediaCodec, "MediaCodec")
4765
48 class VideoDecoderMediaCodecPrivate Q_DECL_FINAL : public VideoDecoderFFmpegBasePrivate
66 class VideoDecoderMediaCodecPrivate Q_DECL_FINAL : public VideoDecoderFFmpegHWPrivate
4967 {
5068 public:
69 ~VideoDecoderMediaCodecPrivate() {
70 #ifdef MEDIACODEC_TEXTURE
71 if (pool_)
72 api_->texture_pool_release(&pool_);
73 #endif
74 }
75 void close() Q_DECL_OVERRIDE {
76 restore();
77 #ifdef MEDIACODEC_TEXTURE
78 if (codec_ctx)
79 av_mediacodec_default_free(codec_ctx);
80 #endif
81 }
82 bool enableFrameRef() const Q_DECL_OVERRIDE { return true;}
83
84 void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE {
85 if (copy_mode != VideoDecoderFFmpegHW::ZeroCopy)
86 return nullptr;
87 #ifdef MEDIACODEC_TEXTURE
88 api_->set_jvm(QAndroidJniEnvironment::javaVM());
89 if (!pool_)
90 pool_ = api_->texture_pool_create();
91 av_mediacodec_default_free(avctx);
92 AVMediaCodecContext *mc = av_mediacodec_alloc_context();
93 AV_ENSURE(av_mediacodec_default_init(avctx, mc, api_->texture_pool_ensure_surface(pool_)), nullptr);
94 #endif
95 return avctx->hwaccel_context; // set in av_mediacodec_default_init
96 }
97
98 bool getBuffer(void **, uint8_t **data) {return true;}
99 void releaseBuffer(void *, uint8_t *) {}
100 AVPixelFormat vaPixelFormat() const {
101 if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy)
102 return AV_PIX_FMT_MEDIACODEC;
103 return AV_PIX_FMT_NONE;
104 }
105 #ifdef MEDIACODEC_TEXTURE
106 const MdkMediaCodecTextureAPI* api_ = mdk_mediacodec_get_api("qtav.nonfree.mediacodec");
107 MdkMediaCodecTextureAPI::TexturePool *pool_ = nullptr;
108 #endif
51109 };
52110
53111 VideoDecoderMediaCodec::VideoDecoderMediaCodec()
54 : VideoDecoderFFmpegBase(*new VideoDecoderMediaCodecPrivate())
112 : VideoDecoderFFmpegHW(*new VideoDecoderMediaCodecPrivate())
55113 {
56 setCodecName("h264_mediacodec");
114 setProperty("hwaccel", "mediacodec");
115 #ifdef MEDIACODEC_TEXTURE
116 setProperty("copyMode", "ZeroCopy");
117 #endif
57118 av_jni_set_java_vm(QAndroidJniEnvironment::javaVM(), NULL);
58119 }
59120
67128 return QStringLiteral("MediaCodec");
68129 }
69130
131 static void av_mediacodec_render_buffer(void *buf)
132 {
133 av_mediacodec_release_buffer((AVMediaCodecBuffer*)buf, 1);
134 }
135
136 static void av_mediacodec_buffer_unref(void* buf)
137 {
138 av_buffer_unref((AVBufferRef**)&buf);
139 }
140
70141 VideoFrame VideoDecoderMediaCodec::frame()
71142 {
72143 DPTR_D(VideoDecoderMediaCodec);
73144 if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx)
74145 return VideoFrame();
75146 // it's safe if width, height, pixfmt will not change, only data change
76 VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
147 if (copyMode() != ZeroCopy) {
148 VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
149 frame.setDisplayAspectRatio(d.getDAR(d.frame));
150 frame.setBits(d.frame->data);
151 frame.setBytesPerLine(d.frame->linesize);
152 // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
153 frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
154 frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
155 d.updateColorDetails(&frame);
156 return frame;
157 }
158 // print width height
159 VideoFrame frame(d.frame->width, d.frame->height, VideoFormat::Format_RGB32);
160 frame.setBytesPerLine(d.frame->width*4);
77161 frame.setDisplayAspectRatio(d.getDAR(d.frame));
78 frame.setBits(d.frame->data);
79 frame.setBytesPerLine(d.frame->linesize);
80 // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
81 frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
82 frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
83 d.updateColorDetails(&frame);
162 frame.setTimestamp(d.frame->pkt_pts/1000.0);
163 #ifdef MEDIACODEC_TEXTURE
164 class MediaCodecTextureInterop : public VideoSurfaceInterop
165 {
166 const MdkMediaCodecTextureAPI* api_ = nullptr;
167 MdkMediaCodecTextureAPI::Texture *tex_ = nullptr;
168 public:
169 MediaCodecTextureInterop(const MdkMediaCodecTextureAPI *api, MdkMediaCodecTextureAPI::Texture *mt) : api_(api), tex_(mt) {}
170 ~MediaCodecTextureInterop() {
171 api_->texture_release(&tex_);
172 }
173
174 void* map(SurfaceType, const VideoFormat &, void *handle, int plane) {
175 Q_UNUSED(plane);
176 GLuint* t = reinterpret_cast<GLuint*>(handle);
177 *t = api_->texture_to_gl(tex_, nullptr, nullptr);
178 return t;
179 }
180 };
181 assert(d.frame->buf[0] && d.frame->data[3] && "No AVMediaCodecBuffer or ref in AVFrame");
182 AVBufferRef* bufref = av_buffer_ref(d.frame->buf[0]);
183 AVMediaCodecBuffer *mcbuf = (AVMediaCodecBuffer*)d.frame->data[3];
184 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);
185
186 MediaCodecTextureInterop *interop = new MediaCodecTextureInterop(d.api_, mt);
187 frame.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr((interop))));
188 #endif
84189 return frame;
85190 }
86191 } //namespace QtAV
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
147147 VideoDecoderVideoToolbox::VideoDecoderVideoToolbox()
148148 : VideoDecoderFFmpegHW(*new VideoDecoderVideoToolboxPrivate())
149149 {
150 #if 1//!AV_MODULE_CHECK(LIBAVCODEC, 57, 30, 1, 89, 100) // ffmpeg3.3
150151 setProperty("threads", 1); // to avoid crash at av_videotoolbox_alloc_context/av_videotoolbox_default_free. I have no idea how the are called
152 #endif
151153 // dynamic properties about static property details. used by UI
152154 setProperty("detail_format", tr("Output pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV.\nOSX < 10.7 only supports UYVY, BGRA and YUV420p"));
153155 setProperty("detail_interop"
173175 VideoFrame VideoDecoderVideoToolbox::frame()
174176 {
175177 DPTR_D(VideoDecoderVideoToolbox);
178 if (!d.codec_ctx->hwaccel_context) {
179 return VideoDecoderFFmpegBase::frame();
180 }
176181 CVPixelBufferRef cv_buffer = (CVPixelBufferRef)d.frame->data[3];
177182 if (!cv_buffer) {
178183 qDebug("Frame buffer is empty.");
285290
286291 bool VideoDecoderVideoToolboxPrivate::getBuffer(void **opaque, uint8_t **data)
287292 {
293 qDebug("vt getbuffer");
288294 *data = (uint8_t *)1; // dummy. it's AVFrame.data[0], must be non null required by ffmpeg
289295 Q_UNUSED(opaque);
290296 return true;
313319 break;
314320 }
315321 switch (codec_ctx->codec_id) {
322 case AV_CODEC_ID_HEVC:
316323 case AV_CODEC_ID_H264:
317324 case AV_CODEC_ID_H263:
318325 case AV_CODEC_ID_MPEG4:
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
4545 static QStringList codecs;
4646 if (!codecs.isEmpty())
4747 return codecs;
48 const AVCodec* c = NULL;
49 #if AVCODEC_STATIC_REGISTER
50 void* it = NULL;
51 while ((c = av_codec_iterate(&it))) {
52 #else
4853 avcodec_register_all();
49 AVCodec* c = NULL;
50 while ((c=av_codec_next(c))) {
54 while ((c = av_codec_next(c))) {
55 #endif
5156 if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_VIDEO)
5257 continue;
5358 codecs.append(QString::fromLatin1(c->name));
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
8989 : VideoEncoderPrivate()
9090 , nb_encoded(0)
9191 {
92 #if !AVCODEC_STATIC_REGISTER
9293 avcodec_register_all();
94 #endif
9395 // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264).
9496 avctx = avcodec_alloc_context3(NULL);
9597 }
244246 applyOptionsForContext();
245247 AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false);
246248 // from mpv ao_lavc
247 const int buffer_size = qMax<int>(qMax<int>(width*height*6+200, FF_MIN_BUFFER_SIZE), sizeof(AVPicture));//??
249 const int buffer_size = qMax<int>(qMax<int>(width*height*6+200, AV_INPUT_BUFFER_MIN_SIZE), sizeof(AVPicture));//??
248250 buffer.resize(buffer_size);
249251 return true;
250252 }
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
109109 { 0x37, 192}, // Kepler Generation (SM 3.7) GK21x class
110110 { 0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class
111111 { 0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class //enc: h264 yuv444p, hevc (libavcodec/nvenc.c)
112 { 0x53, 128}, // Maxwell Generation (SM 5.3) GM20x class
113 { 0x60, 64 }, // Pascal Generation (SM 6.0) GP100 class
114 { 0x61, 128}, // Pascal Generation (SM 6.1) GP10x class
115 { 0x62, 128}, // Pascal Generation (SM 6.2) GP10x class
116 { 0x70, 64 }, // Volta Generation (SM 7.0) GV100 class
112117 { -1, -1 }
113118 };
114119 int index = 0;
119124 ++index;
120125 }
121126 // If we don't find the values, we default use the previous one to run properly
122 printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[7].Cores);
127 printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[index-1].Cores);
123128 return nGpuArchCoresPerSM[index - 1].Cores;
124129 }
125130
0 /******************************************************************************
1 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
3
4 * This file is part of QtAV (from 2017)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ******************************************************************************/
20
21 #include "DXVAHDVP.h"
22 #define DX_LOG_COMPONENT "D3D9VP"
23 #include "utils/DirectXHelper.h"
24 #include "utils/Logger.h"
25 #include "directx/DXVAHDVP.h"
26
27 namespace QtAV {
28 namespace dx {
29
30 DXVAHDVP::DXVAHDVP(ComPtr<IDirect3DDevice9> dev)
31 : m_dev(NULL)
32 , m_w(0)
33 , m_h(0)
34 , m_cs(ColorSpace_BT709)
35 , m_range(ColorRange_Limited)
36 , fDXVAHD_CreateDevice(NULL)
37 {
38 DX_ENSURE(dev.As(&m_dev));
39 fDXVAHD_CreateDevice = (PDXVAHD_CreateDevice)GetProcAddress(GetModuleHandle(TEXT("dxva2.dll")), "DXVAHD_CreateDevice");
40 }
41
42 void DXVAHDVP::setOutput(IDirect3DSurface9 *surface)
43 {
44 m_out = surface;
45 }
46
47 void DXVAHDVP::setSourceRect(const QRect &r)
48 {
49 m_srcRect = r;
50 }
51
52 void DXVAHDVP::setColorSpace(ColorSpace value)
53 {
54 m_cs = value;
55 }
56
57 void DXVAHDVP::setColorRange(ColorRange value)
58 {
59 m_range = value;
60 }
61
62 bool DXVAHDVP::process(IDirect3DSurface9 *surface)
63 {
64 if (!surface || !m_out)
65 return false;
66 D3DSURFACE_DESC desc;
67 surface->GetDesc(&desc);
68 if (!ensureResource(desc.Width, desc.Height, desc.Format))
69 return false;
70
71 if (!m_srcRect.isEmpty()) {
72 const RECT sr = {m_srcRect.x(), m_srcRect.y(), m_srcRect.width(), m_srcRect.height()};
73 DXVAHD_STREAM_STATE_SOURCE_RECT_DATA r;
74 r.Enable = 1;
75 r.SourceRect = sr;
76 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_SOURCE_RECT, sizeof(r), &r), false);
77 }
78 #if 0
79 DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3df = {m_fmt};
80 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_D3DFORMAT, sizeof(d3df), &d3df), false);
81 DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA ff = {DXVAHD_FRAME_FORMAT_PROGRESSIVE};
82 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_FRAME_FORMAT, sizeof(ff), &ff), false);
83 DXVAHD_STREAM_STATE_DESTINATION_RECT_DATA dstr = { true, {0, 0, desc.Width, desc.Height} };
84 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_DESTINATION_RECT, sizeof(dstr), &dstr), false);
85 DXVAHD_BLT_STATE_TARGET_RECT_DATA tgtr = {true, {0, 0, desc.Width, desc.Height} };
86 DX_ENSURE(m_vp->SetVideoProcessBltState(DXVAHD_BLT_STATE_TARGET_RECT, sizeof(tgtr), &tgtr), false);
87 for (int i = 0; i < 7; ++i) {
88 DXVAHD_STREAM_STATE_FILTER_DATA flt = {false, DXVAHD_FILTER(i)};
89 DXVAHD_STREAM_STATE state = static_cast<DXVAHD_STREAM_STATE>(DXVAHD_STREAM_STATE_FILTER_BRIGHTNESS + i);
90 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, state, sizeof(flt), &flt), false);
91 }
92 #endif
93
94 DXVAHD_STREAM_STATE_OUTPUT_RATE_DATA rate;
95 ZeroMemory(&rate, sizeof(rate));
96 rate.OutputRate = DXVAHD_OUTPUT_RATE_NORMAL;
97 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_OUTPUT_RATE, sizeof(rate), &rate), false);
98
99 DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA ics;
100 ZeroMemory(&ics, sizeof(ics));
101 ics.Type = 0; // 0: video, 1: graphics
102 ics.RGB_Range = 0; // full
103 ics.YCbCr_Matrix = m_cs == ColorSpace_BT601 ? 0 : 1; //0: bt601, 1: bt709
104 ics.YCbCr_xvYCC = m_range == ColorRange_Full ? 1 : 0;
105 DX_ENSURE(m_vp->SetVideoProcessStreamState(0/*stream index*/, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE, sizeof(ics), &ics), false);
106
107 DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA cs;
108 ZeroMemory(&cs, sizeof(cs));
109 cs.Usage = 0; // output usage. 0: for playback (default), 1: video processing (e.g. video editor)
110 cs.RGB_Range = 1; //
111 cs.YCbCr_Matrix = 1; //0: bt601, 1: bt709
112 cs.YCbCr_xvYCC = 1;
113 DX_ENSURE(m_vp->SetVideoProcessBltState(DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE, sizeof(cs), &cs), false);
114
115 DXVAHD_STREAM_DATA stream;
116 ZeroMemory(&stream, sizeof(stream));
117 stream.Enable = true;
118 stream.OutputIndex = 0;
119 stream.InputFrameOrField = 0;
120 stream.pInputSurface = surface;
121 DX_ENSURE(m_vp->VideoProcessBltHD(m_out.Get(), 0, 1, &stream), false);
122 return true;
123 }
124
125 bool DXVAHDVP::ensureResource(UINT width, UINT height, D3DFORMAT format)
126 {
127 const bool dirty = width != m_w || height != m_h || m_fmt != format;
128 if (dirty || !m_viddev) {
129 if (!fDXVAHD_CreateDevice)
130 return false;
131 DXVAHD_RATIONAL fps = {0, 0};
132 DXVAHD_CONTENT_DESC desc;
133 desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
134 desc.InputFrameRate = fps;
135 desc.InputWidth = width;
136 desc.InputHeight = height;
137 desc.OutputFrameRate = fps;
138 desc.OutputWidth = width;
139 desc.OutputHeight = height;
140 DX_ENSURE(fDXVAHD_CreateDevice(m_dev.Get(), &desc, DXVAHD_DEVICE_USAGE_PLAYBACK_NORMAL, NULL, &m_viddev), false);
141 }
142
143 // TODO: check when format is changed, or record supported formats
144 DXVAHD_VPDEVCAPS caps;
145 DX_ENSURE(m_viddev->GetVideoProcessorDeviceCaps(&caps), false);
146 QScopedPointer<DXVAHD_VPCAPS, QScopedPointerArrayDeleter<DXVAHD_VPCAPS>> pVPCaps(new (std::nothrow) DXVAHD_VPCAPS[caps.VideoProcessorCount]);
147 DX_ENSURE(m_viddev->GetVideoProcessorCaps(caps.VideoProcessorCount, pVPCaps.data()), false);
148 QScopedPointer<D3DFORMAT, QScopedPointerArrayDeleter<D3DFORMAT>> fmts(new (std::nothrow) D3DFORMAT[caps.InputFormatCount]);
149 DX_ENSURE(m_viddev->GetVideoProcessorInputFormats(caps.InputFormatCount, fmts.data()), false);
150 bool fmt_found = false;
151 for (UINT i = 0; i < caps.InputFormatCount; ++i) {
152 if (fmts.data()[i] == format) {
153 fmt_found = true;
154 break;
155 }
156 }
157 if (!fmt_found) {
158 qDebug("input format is not supported by DXVAHD");
159 return false;
160 }
161 if (dirty || !m_vp)
162 DX_ENSURE(m_viddev->CreateVideoProcessor(&pVPCaps.data()[0].VPGuid, &m_vp), false);
163 m_w = width;
164 m_h = height;
165 m_fmt = format;
166 return true;
167 }
168 } //namespace dx
169 } //namespace QtAV
0 /******************************************************************************
1 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
3
4 * This file is part of QtAV (from 2017)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ******************************************************************************/
20
21 #ifndef QTAV_D3D9VPP_H
22 #define QTAV_D3D9VPP_H
23 #include <QtCore/QRect>
24 #include "directx/dxcompat.h"
25 #include <d3d9.h>
26 #include <dxvahd.h>
27 #include <wrl/client.h>
28 #include <QtAV/QtAV_Global.h>
29 using namespace Microsoft::WRL;
30 // https://msdn.microsoft.com/en-us/library/windows/desktop/ee663581(v=vs.85).aspx
31
32 namespace QtAV {
33 namespace dx {
34
35 class DXVAHDVP
36 {
37 public:
38 // brightness, contrast, hue, saturation, rotation, source/dest rect
39 DXVAHDVP(ComPtr<IDirect3DDevice9> dev);
40 void setOutput(IDirect3DSurface9* surface);
41 void setSourceRect(const QRect& r);
42 // input color space and range
43 void setColorSpace(ColorSpace value);
44 void setColorRange(ColorRange value);
45 bool process(IDirect3DSurface9 *surface);
46 private:
47 bool ensureResource(UINT width, UINT height, D3DFORMAT format);
48
49 ComPtr<IDirect3DDevice9Ex> m_dev;
50 ComPtr<IDXVAHD_Device> m_viddev;
51 ComPtr<IDXVAHD_VideoProcessor> m_vp;
52 ComPtr<IDirect3DSurface9> m_out;
53 UINT m_w, m_h; //enumerator
54 ColorSpace m_cs;
55 ColorRange m_range;
56 QRect m_srcRect;
57 PDXVAHD_CreateDevice fDXVAHD_CreateDevice;
58 D3DFORMAT m_fmt;
59 };
60 } //namespace dx
61 } //namespace QtAV
62 #endif //QTAV_D3D9VPP_H
5050 EGLInteropResource()
5151 : egl(new EGL())
5252 , vp(0)
53 , boundTex(0)
5354 {}
5455 ~EGLInteropResource();
5556 VideoFormat::PixelFormat format(DXGI_FORMAT) const Q_DECL_OVERRIDE { return VideoFormat::Format_RGB32;}
6263 EGL* egl;
6364 dx::D3D11VP *vp;
6465 ComPtr<ID3D11Texture2D> d3dtex;
66 GLuint boundTex;
6567 };
6668
6769 InteropResource* CreateInteropEGL() { return new EGLInteropResource();}
163165 vp->setSourceRect(QRect(0, 0, w, h));
164166 if (!vp->process(surface.Get(), index))
165167 return false;
168 if (boundTex == tex)
169 return true;
166170 DYGL(glBindTexture(GL_TEXTURE_2D, tex));
167 eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER);
171 if (boundTex)
172 EGL_WARN(eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER));
173 EGL_WARN(eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER));
168174 DYGL(glBindTexture(GL_TEXTURE_2D, 0));
175 boundTex = tex;
169176 return true;
170177 }
171178 } //namespace d3d11
3333 class AudioEncodeFilterPrivate Q_DECL_FINAL : public AudioFilterPrivate
3434 {
3535 public:
36 AudioEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0) {}
36 AudioEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0), leftOverAudio() {}
3737 ~AudioEncodeFilterPrivate() {
3838 if (enc) {
3939 enc->close();
4646 bool async;
4747 QAtomicInt finishing;
4848 QThread enc_thread;
49 AudioFrame leftOverAudio;
4950 };
5051
5152 AudioEncodeFilter::AudioEncodeFilter(QObject *parent)
5253 : AudioFilter(*new AudioEncodeFilterPrivate(), parent)
5354 {
55 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
5456 connect(this, SIGNAL(requestToEncode(QtAV::AudioFrame)), this, SLOT(encode(QtAV::AudioFrame)));
57 #else
58 connect(this, &AudioEncodeFilter::requestToEncode, this, &AudioEncodeFilter::encode);
59 #endif
5560 connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit()));
5661 }
5762
169174 AudioFrame f(frame);
170175 if (f.format() != d.enc->audioFormat())
171176 f = f.to(d.enc->audioFormat());
172 if (!d.enc->encode(f)) {
173 if (f.timestamp() == std::numeric_limits<qreal>::max()) {
174 Q_EMIT finished();
175 d.finishing = 0;
176 }
177 return;
178 }
179 if (!d.enc->encoded().isValid())
180 return;
181 Q_EMIT frameEncoded(d.enc->encoded());
177
178 if (d.leftOverAudio.isValid()) {
179 f.prepend(d.leftOverAudio);
180 d.leftOverAudio = AudioFrame();
181 }
182
183 int frameSizeEncoder = d.enc->frameSize() ? d.enc->frameSize() : f.samplesPerChannel();
184 int frameSize = f.samplesPerChannel();
185
186 QList<AudioFrame> audioFrames;
187 for (int i = 0; i < frameSize; i += frameSizeEncoder) {
188 if (frameSize - i >= frameSizeEncoder) {
189 audioFrames.append(f.mid(i, frameSizeEncoder));
190 } else {
191 d.leftOverAudio = f.mid(i);
192 }
193 }
194
195 for (int i = 0; i < audioFrames.length(); i++) {
196 if (!d.enc->encode(audioFrames.at(i))) {
197 if (f.timestamp() == std::numeric_limits<qreal>::max()) {
198 Q_EMIT finished();
199 d.finishing = 0;
200 }
201 return;
202 }
203 if (!d.enc->encoded().isValid())
204 return;
205 Q_EMIT frameEncoded(d.enc->encoded());
206 }
182207 }
183208
184209
203228 VideoEncodeFilter::VideoEncodeFilter(QObject *parent)
204229 : VideoFilter(*new VideoEncodeFilterPrivate(), parent)
205230 {
206 connect(this, SIGNAL(requestToEncode(QtAV::VideoFrame)), this, SLOT(encode(QtAV::VideoFrame)));
231 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
232 connect(this, SIGNAL(requestToEncode(QtAV::VideoFrame)), this, SLOT(encode(QtAV::VideoFrame)));
233 #else
234 connect(this, &VideoEncodeFilter::requestToEncode, this, &VideoEncodeFilter::encode);
235 #endif
207236 connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit()));
208237 }
209238
291320 return;
292321 // encode delayed frames can pass an invalid frame
293322 if (!d.enc->isOpen() && frame.isValid()) {
294 d.enc->setWidth(frame.width());
295 d.enc->setHeight(frame.height());
323 if (d.enc->width() == 0) {
324 d.enc->setWidth(frame.width());
325 }
326 if (d.enc->height() == 0) {
327 d.enc->setHeight(frame.height());
328 }
296329 if (!d.enc->open()) { // TODO: error()
297330 qWarning("Failed to open video encoder");
298331 return;
309342 d.finishing = 0;
310343 return;
311344 }
312 if (d.enc->width() != frame.width() || d.enc->height() != frame.height()) {
313 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());
314 return;
315 }
316345 if (frame.timestamp()*1000.0 < startTime())
317346 return;
318347 // TODO: async
319348 VideoFrame f(frame);
320 if (f.pixelFormat() != d.enc->pixelFormat())
321 f = f.to(d.enc->pixelFormat());
349 if (f.pixelFormat() != d.enc->pixelFormat() || d.enc->width() != f.width() || d.enc->height() != f.height())
350 f = f.to(d.enc->pixelFormat(), QSize(d.enc->width(), d.enc->height()));
322351 if (!d.enc->encode(f)) {
323352 if (f.timestamp() == std::numeric_limits<qreal>::max()) {
324353 Q_EMIT finished();
121121 {
122122 DPTR_D(FilterManager);
123123 QList<Filter*>& fs = d.afilter_player_map[player];
124 bool ret = false;
124125 if (fs.removeAll(filter) > 0) {
125 if (fs.isEmpty())
126 d.afilter_player_map.remove(player);
127 return true;
128 }
129 return false;
126 ret = true;
127 }
128 if (fs.isEmpty())
129 d.afilter_player_map.remove(player);
130 return ret;
130131 }
131132
132133 bool FilterManager::unregisterVideoFilter(Filter *filter, AVPlayer *player)
133134 {
134135 DPTR_D(FilterManager);
135136 QList<Filter*>& fs = d.vfilter_player_map[player];
137 bool ret = false;
136138 if (fs.removeAll(filter) > 0) {
137 if (fs.isEmpty())
138 d.vfilter_player_map.remove(player);
139 return true;
140 }
141 return false;
139 ret = true;
140 }
141 if (fs.isEmpty())
142 d.vfilter_player_map.remove(player);
143 return ret;
142144 }
143145
144146 bool FilterManager::unregisterFilter(Filter *filter, AVOutput *output)
145147 {
146148 DPTR_D(FilterManager);
147149 QList<Filter*>& fs = d.filter_out_map[output];
148 return fs.removeAll(filter) > 0;
150 bool ret = fs.removeAll(filter) > 0;
151 if (fs.isEmpty()) d.filter_out_map.remove(output);
152 return ret;
149153 }
150154
151155 bool FilterManager::uninstallFilter(Filter *filter)
152156 {
153157 DPTR_D(FilterManager);
154 QMap<AVPlayer*, QList<Filter*> >::iterator it = d.vfilter_player_map.begin();
155 while (it != d.vfilter_player_map.end()) {
158 QMap<AVPlayer*, QList<Filter*> > map1(d.vfilter_player_map); // NB: copy it for iteration because called code may modify map -- which caused crashes
159 QMap<AVPlayer*, QList<Filter*> >::iterator it = map1.begin();
160 while (it != map1.end()) {
156161 if (uninstallVideoFilter(filter, it.key()))
157162 return true;
158163 ++it;
159164 }
160 it = d.afilter_player_map.begin();
161 while (it != d.afilter_player_map.end()) {
165 QMap<AVPlayer *, QList<Filter *> > map2(d.afilter_player_map); // copy to avoid crashes when called-code modifies map
166 it = map2.begin();
167 while (it != map2.end()) {
162168 if (uninstallAudioFilter(filter, it.key()))
163169 return true;
164170 ++it;
165171 }
166 QMap<AVOutput*, QList<Filter*> >::iterator it2 = d.filter_out_map.begin();
167 while (it2 != d.filter_out_map.end()) {
172 QMap<AVOutput*, QList<Filter*> > map3(d.filter_out_map); // copy to avoid crashes
173 QMap<AVOutput*, QList<Filter*> >::iterator it2 = map3.begin();
174 while (it2 != map3.end()) {
168175 if (uninstallFilter(filter, it2.key()))
169176 return true;
170177 ++it2;
119119 // pixel_aspect==sar, pixel_aspect is more compatible
120120 QString buffersrc_args = args;
121121 qDebug("buffersrc_args=%s", buffersrc_args.toUtf8().constData());
122 AVFilter *buffersrc = avfilter_get_by_name(video ? "buffer" : "abuffer");
122 #if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(7,0,0)
123 const
124 #endif
125 AVFilter *buffersrc = avfilter_get_by_name(video ? "buffer" : "abuffer");
123126 Q_ASSERT(buffersrc);
124127 AV_ENSURE_OK(avfilter_graph_create_filter(&in_filter_ctx,
125128 buffersrc,
127130 filter_graph)
128131 , false);
129132 /* buffer video sink: to terminate the filter chain. */
133 #if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(7,0,0)
134 const
135 #endif
130136 AVFilter *buffersink = avfilter_get_by_name(video ? "buffersink" : "abuffersink");
131137 Q_ASSERT(buffersink);
132138 AV_ENSURE_OK(avfilter_graph_create_filter(&out_filter_ctx, buffersink, "out",
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2015)
55
2323 #include "QtAV/private/mkid.h"
2424 #include "QtAV/private/factory.h"
2525 #include <QtCore/QFile>
26 #include <qpa/qplatformnativeinterface.h>
2726 #include <QtGui/QGuiApplication>
2827 #include <QtAndroidExtras>
2928 #include "utils/Logger.h"
29 #include "jmi/jmi.h"
3030
3131 // TODO: how to get filename and find subtitles?
3232 //http://stackoverflow.com/questions/5657411/android-getting-a-file-uri-from-a-content-uri
4545 QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);}
4646 const QStringList& protocols() const Q_DECL_OVERRIDE
4747 {
48 static QStringList p = QStringList() << QStringLiteral("content"); // "file:" is supported too but we use QFile
48 static QStringList p = QStringList() << QStringLiteral("content") << QStringLiteral("android.resource"); // "file:" is supported too but we use QFile
4949 return p;
5050 }
5151 virtual bool isSeekable() const Q_DECL_OVERRIDE;
6262 void onUrlChanged() Q_DECL_OVERRIDE;
6363
6464 private:
65 QAndroidJniObject app_ctx;
6665 QFile qt_file;
6766 // if use Java.io.InputStream, record pos
6867 };
7372 AndroidIO::AndroidIO()
7473 : MediaIO()
7574 {
76 QPlatformNativeInterface *interface = QGuiApplication::platformNativeInterface();
77 jobject activity = (jobject)interface->nativeResourceForIntegration("QtActivity");
78 app_ctx = QAndroidJniObject(activity).callObjectMethod("getApplicationContext","()Landroid/content/Context;");
75 jmi::javaVM(QAndroidJniEnvironment::javaVM()); // nativeResourceForIntegration("javaVM")
7976 }
8077
8178 bool AndroidIO::isSeekable() const
105102 void AndroidIO::onUrlChanged()
106103 {
107104 qt_file.close();
108 QAndroidJniObject content_resolver = app_ctx.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
109 if (!content_resolver.isValid()) {
110 qWarning("getContentResolver error");
105 if (url().isEmpty())
106 return;
107 struct Application final: jmi::ClassTag { static std::string name() {return "android/app/Application";}};
108 jmi::JObject<Application> app_ctx(jmi::android::application());
109
110 struct ContentResolver final: jmi::ClassTag { static std::string name() { return "android/content/ContentResolver";}};
111 struct GetContentResolver final: jmi::MethodTag { static const char* name() {return "getContentResolver";}};
112 jmi::JObject<ContentResolver> cr = app_ctx.call<jmi::JObject<ContentResolver>, GetContentResolver>();
113 if (!cr.error().empty()) {
114 qWarning("getContentResolver error: %s", cr.error().data());
111115 return;
112116 }
113 QAndroidJniObject s = QAndroidJniObject::fromString(url());
114 QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", s.object<jstring>());
115 //input_stream = content_resolver.callObjectMethod("openInputStream", "(Landroid.net.Uri;)Ljava.io.InputStream;", uri.object<jobject>()); // TODO: why error?
116 //qDebug() << "onUrlChanged InputStream: " << input_stream.toString();
117 s = QAndroidJniObject::fromString(QStringLiteral("r"));
118 QAndroidJniObject pfd = content_resolver.callObjectMethod("openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", uri.object<jobject>(), s.object<jstring>());
119 if (!pfd.isValid()) {
120 qWarning("openFileDescriptor error");
117 struct Uri final: jmi::ClassTag { static std::string name() { return "android/net/Uri";}};
118 struct Parse final: jmi::MethodTag { static const char* name() {return "parse";}};
119 jmi::JObject<Uri> uri = jmi::JObject<Uri>::callStatic<jmi::JObject<Uri>, Parse>(url().toUtf8().constData()); // move?
120 // openInputStream?
121 struct ParcelFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/os/ParcelFileDescriptor";}};
122 // AssetFileDescriptor supported schemes: content, android.resource, file
123 // ParcelFileDescriptor supported schemes: content, file
124 #if 1
125 struct AssetFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/content/res/AssetFileDescriptor";}};
126 struct OpenAssetFileDescriptor final: jmi::MethodTag { static const char* name() {return "openAssetFileDescriptor";}};
127 jmi::JObject<AssetFileDescriptor> afd = cr.call<jmi::JObject<AssetFileDescriptor>, OpenAssetFileDescriptor>(std::move(uri), "r"); // TODO: rw
128 if (!afd.error().empty()) {
129 qWarning("openAssetFileDescriptor error: %s", afd.error().data());
121130 return;
122131 }
123 int fd = pfd.callMethod<int>("detachFd", "()I");
132 struct GetParcelFileDescriptor final: jmi::MethodTag { static const char* name() {return "getParcelFileDescriptor";}};
133 jmi::JObject<ParcelFileDescriptor> pfd = afd.call<jmi::JObject<ParcelFileDescriptor>, GetParcelFileDescriptor>();
134 #else
135 struct OpenFileDescriptor final: jmi::MethodTag { static const char* name() {return "openFileDescriptor";}};
136 jmi::JObject<ParcelFileDescriptor> pfd = cr.call<jmi::JObject<ParcelFileDescriptor>, OpenFileDescriptor>(std::move(uri), "r");
137 #endif
138 if (!pfd.error().empty()) {
139 qWarning("get ParcelFileDescriptor error: %s", pfd.error().data());
140 return;
141 }
142 struct DetachFd final: jmi::MethodTag { static const char* name() {return "detachFd";}};
143 int fd = pfd.call<int,DetachFd>();
124144 qt_file.open(fd, QIODevice::ReadOnly);
125145 }
126146 } //namespace QtAV
5050
5151 !rc_file {
5252 RC_ICONS = QtAV.ico
53 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
53 QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com"
5454 QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org"
55 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
55 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
5656 QMAKE_TARGET_PRODUCT = "QtAV"
5757 } else:win32 {
5858 RC_FILE = QtAV.rc
117117 DEFINES += __STDC_CONSTANT_MACROS
118118 android {
119119 CONFIG *= config_opensl
120 !no_gui_private:qtHaveModule(androidextras) { #qt5.2 has QAndroidJniObject
121 QT *= androidextras gui-private #QPlatformNativeInterface get "QtActivity"
120 SOURCES *= jmi/jmi.cpp
121 qtHaveModule(androidextras) { #qt5.2 has QAndroidJniObject
122 QT *= androidextras #QPlatformNativeInterface get "QtActivity"
122123 SOURCES *= io/AndroidIO.cpp
123124 SOURCES *= codec/video/VideoDecoderMediaCodec.cpp
125 exists($$[QT_INSTALL_HEADERS]/MediaCodecTextureStandalone.h) {
126 DEFINES *= MEDIACODEC_TEXTURE
127 LIBS *= -lqtav-mediacodec
128 }
124129 }
125130 }
126131 config_x11 {
151156 ios {
152157 LIBS += -framework AVFoundation
153158 } else {
154 LIBS += -framework QTKit
155159 # assume avdevice targets to the same version as Qt and always >= 10.6
156160 !isEqual(QMAKE_MACOSX_DEPLOYMENT_TARGET, 10.6): LIBS += -framework AVFoundation
157161 }
161165 config_avfilter {
162166 DEFINES += QTAV_HAVE_AVFILTER=1
163167 LIBS += -lavfilter
168 mac:!ios:static_ffmpeg: LIBS += -framework AppKit
164169 }
165170 config_ipp {
166171 DEFINES += QTAV_HAVE_IPP=1
181186 CONFIG *= config_openal
182187 SOURCES += output/audio/AudioOutputAudioToolbox.cpp
183188 LIBS += -framework AudioToolbox
189 LIBS += -Wl,-unexported_symbols_list,$$PWD/unexport.list
190 } else:!win32 {
191 #LIBS += -Wl,--exclude-libs,ALL
184192 }
185193 win32: {
186194 HEADERS += output/audio/xaudio2_compat.h
320328 }
321329 mac {
322330 HEADERS *= codec/video/SurfaceInteropCV.h
323 SOURCES *= codec/video/SurfaceInteropCV.cpp
331 SOURCES *= codec/video/SurfaceInteropCV.cpp \
332 codec/video/SurfaceInteropIOSurface.mm
324333 ios {
325334 OBJECTIVE_SOURCES *= codec/video/SurfaceInteropCVOpenGLES.mm
326335 } else {
327 CONFIG += config_vda
328 SOURCES *= codec/video/SurfaceInteropIOSurface.cpp
336 #CONFIG += config_vda
329337 #SOURCES *= codec/video/SurfaceInteropCVOpenGL.cpp
330 LIBS += -framework IOSurface
331338 }
332339 LIBS += -framework CoreVideo -framework CoreFoundation
333340 }
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
172172 gr->updateGeometry(geometry);
173173 }
174174
175 OpenGLVideo::OpenGLVideo() {}
175 OpenGLVideo::OpenGLVideo() {
176 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
177 // TODO: system resolution change
178 connect(QGuiApplication::instance(), SIGNAL(primaryScreenChanged(QScreen*)), this, SLOT(updateViewport()));
179 #endif
180 }
176181
177182 bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt)
178183 {
205210 d.material->setSaturation(s);
206211 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
207212 d.manager = ctx->findChild<ShaderManager*>(QStringLiteral("__qtav_shader_manager"));
208 QSizeF surfaceSize = ctx->surface()->size();
209 #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
210 surfaceSize *= ctx->screen()->devicePixelRatio();
211 #else
212 surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's
213 #endif
214 #else
215 QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height());
216 #endif
217 setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize));
213 #endif
214 updateViewport();
218215 if (d.manager)
219216 return;
220217 // TODO: what if ctx is delete?
348345 DPTR_D(OpenGLVideo);
349346 Q_ASSERT(d.manager);
350347 Q_EMIT beforeRendering();
351 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)
352348 const qint64 mt = d.material->type();
353349 if (d.material_type != mt) {
354350 qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt);
359355 VideoShader *shader = d.user_shader;
360356 if (!shader)
361357 shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code)
358 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)
362359 shader->update(d.material);
363360 shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix);
364361 // uniform end. attribute begin
386383 d_func().resetGL();
387384 }
388385
386 void OpenGLVideo::updateViewport()
387 {
388 DPTR_D(OpenGLVideo);
389 if (!d.ctx)
390 return;
391 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
392 QSizeF surfaceSize = d.ctx->surface()->size();
393 #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
394 surfaceSize *= d.ctx->screen()->devicePixelRatio();
395 #else
396 surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's
397 #endif
398 #else
399 QSizeF surfaceSize = QSizeF(d.ctx->device()->width(), d.ctx->device()->height());
400 #endif
401 setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize));
402 }
389403 } //namespace QtAV
220220 //}
221221 // buffers_free.ref();
222222 }
223 DWORD status;
224 stream_buf->GetStatus(&status);
223 //DWORD status;
224 //stream_buf->GetStatus(&status);
225225 //qDebug("status: %lu", status);
226 return;
227 if (status & DSBSTATUS_LOOPING) {
226 //return;
227 //if (status & DSBSTATUS_LOOPING) {
228228 // sound will loop even if buffer is finished
229 DX_ENSURE(stream_buf->Stop());
229 //DX_ENSURE(stream_buf->Stop());
230230 // reset positions to ensure the notification positions and played buffer matches
231 DX_ENSURE(stream_buf->SetCurrentPosition(0));
232 write_offset = 0;
233 }
231 //DX_ENSURE(stream_buf->SetCurrentPosition(0));
232 //write_offset = 0;
233 //}
234234 }
235235
236236 bool AudioOutputDSound::write(const QByteArray &data)
2626 #include <SLES/OpenSLES_Android.h>
2727 #include <SLES/OpenSLES_AndroidConfiguration.h>
2828 #include <sys/system_properties.h>
29 #include <cmath>
2930 #endif
3031 #include "QtAV/private/mkid.h"
3132 #include "QtAV/private/factory.h"
323324 // Volume interface
324325 //SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false);
325326
326 sem.release(buffer_count - sem.available());
327 /*
328 * DO NOT call sem.release(buffer_count - sem.available()) because playInitialData() will enqueue buffers and then sem.release() is called in callback.
329 * Otherwise, Enqueue() the 1st(or more) real buffer may report SL_RESULT_BUFFER_INSUFFICIENT and noise will be played.
330 * Can not Enqueue() internally here because buffers are managed in AudioOutput to ensure 0-copy
331 */
327332 return true;
328333 }
329334
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
4848 {
4949 matrix.setToIdentity();
5050 matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1);
51 if (orientation)
52 matrix.rotate(orientation, 0, 0, 1); // Z axis
51 if (rotation())
52 matrix.rotate(rotation(), 0, 0, 1); // Z axis
5353 }
5454
5555 OpenGLRendererBase::OpenGLRendererBase(OpenGLRendererBasePrivate &d)
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
9999 if (d.pixmap.isNull())
100100 return;
101101 QRect roi = realROI();
102 if (orientation() == 0) {
102 if (d.rotation() == 0) {
103103 //assume that the image data is already scaled to out_size(NOT renderer size!)
104104 if (roi.size() == d.out_rect.size()) {
105105 d.painter->drawPixmap(d.out_rect.topLeft(), d.pixmap, roi);
115115 d.painter->save();
116116 d.painter->translate(rendererWidth()/2, rendererHeight()/2);
117117 // TODO: why rotate then scale gives wrong result?
118 if (orientation() % 180)
118 if (d.rotation() % 180)
119119 d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererHeight(), (qreal)d.out_rect.height()/(qreal)rendererWidth());
120120 else
121121 d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererWidth(), (qreal)d.out_rect.height()/(qreal)rendererHeight());
122 d.painter->rotate(orientation());
122 d.painter->rotate(d.rotation());
123123 d.painter->translate(-rendererWidth()/2, -rendererHeight()/2);
124124 d.painter->drawPixmap(QRect(0, 0, rendererWidth(), rendererHeight()), d.pixmap, roi);
125125 d.painter->restore();
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
3939 #if defined(Q_OS_DARWIN)
4040 avwidgets.setFileName(QStringLiteral("QtAVWidgets.framework/QtAVWidgets")); //no dylib check
4141 #elif defined(Q_OS_WIN)
42 avwidgets.setFileName(QStringLiteral("QtAVWidgets").append(QString::number(QTAV_VERSION_MAJOR(QtAV_Version()))));
42 avwidgets.setFileName(QStringLiteral("QtAVWidgets")
43 # ifndef QT_NO_DEBUG
44 .append("d")
45 # endif
46 .append(QString::number(QTAV_VERSION_MAJOR(QtAV_Version()))));
4347 #else
4448 avwidgets.setFileNameAndVersion(QStringLiteral("QtAVWidgets"), QTAV_VERSION_MAJOR(QtAV_Version()));
4549 #endif
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
287287
288288 void VideoRenderer::setOrientation(int value)
289289 {
290 DPTR_D(VideoRenderer);
290291 // currently only supports a multiple of 90
291292 value = (value + 360) % 360;
292293 if (value % 90)
293294 return;
294 DPTR_D(VideoRenderer);
295295 if (d.orientation == value)
296296 return;
297297 int old = orientation();
311311
312312 int VideoRenderer::orientation() const
313313 {
314 return d_func().orientation;
314 DPTR_D(const VideoRenderer);
315 return d.orientation;
315316 }
316317
317318 // only qpainter and opengl based renderers support orientation.
504505 */
505506 if (d.video_frame.isValid()) {
506507 drawFrame();
508 //qDebug("render elapsed: %lld", et.elapsed());
507509 if (d.statistics) {
508510 d.statistics->video_only.frameDisplayed(d.video_frame.timestamp());
509511 d.statistics->video.current_time = QTime(0, 0, 0).addMSecs(int(d.video_frame.timestamp() * 1000.0));
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV (from 2014)
55
7979 QStringList ffmpeg_supported_sub_extensions_by_codec()
8080 {
8181 QStringList exts;
82 AVCodec *c = av_codec_next(NULL);
83 while (c) {
84 if (c->type != AVMEDIA_TYPE_SUBTITLE) {
85 c = av_codec_next(c);
82 const AVCodec* c = NULL;
83 #if AVCODEC_STATIC_REGISTER
84 void* it = NULL;
85 while ((c = av_codec_iterate(&it))) {
86 #else
87 avcodec_register_all();
88 while ((c = av_codec_next(c))) {
89 #endif
90 if (c->type != AVMEDIA_TYPE_SUBTITLE)
8691 continue;
87 }
8892 qDebug("sub codec: %s", c->name);
89 AVInputFormat *i = av_iformat_next(NULL);
90 while (i) {
93 #if AVFORMAT_STATIC_REGISTER
94 const AVInputFormat *i = NULL;
95 void* it2 = NULL;
96 while ((i = av_demuxer_iterate(&it2))) {
97 #else
98 av_register_all(); // MUST register all input/output formats
99 AVInputFormat *i = NULL;
100 while ((i = av_iformat_next(i))) {
101 #endif
91102 if (!strcmp(i->name, c->name)) {
92103 qDebug("found iformat");
93104 if (i->extensions) {
98109 }
99110 break;
100111 }
101 i = av_iformat_next(i);
102112 }
103113 if (!i) {
104114 //qDebug("codec name '%s' is not found in AVInputFormat, just append codec name", c->name);
105115 //exts.append(c->name);
106116 }
107 c = av_codec_next(c);
108117 }
109118 return exts;
110119 }
112121 QStringList ffmpeg_supported_sub_extensions()
113122 {
114123 QStringList exts;
124 #if AVFORMAT_STATIC_REGISTER
125 const AVInputFormat *i = NULL;
126 void* it = NULL;
127 while ((i = av_demuxer_iterate(&it))) {
128 #else
129 av_register_all(); // MUST register all input/output formats
115130 AVInputFormat *i = NULL;
116131 while ((i = av_iformat_next(i))) {
132 #endif
117133 // strstr parameters can not be null
118134 if (i->long_name && strstr(i->long_name, "subtitle")) {
119135 if (i->extensions) {
126142 // AVCodecDescriptor.name and AVCodec.name may be different. avcodec_get_name() use AVCodecDescriptor if possible
127143 QStringList codecs;
128144 const AVCodec* c = NULL;
145 #if AVCODEC_STATIC_REGISTER
146 it = NULL;
147 while ((c = av_codec_iterate(&it))) {
148 #else
149 avcodec_register_all();
129150 while ((c = av_codec_next(c))) {
151 #endif
130152 if (c->type == AVMEDIA_TYPE_SUBTITLE)
131153 codecs.append(QString::fromLatin1(c->name));
132154 }
248270 codec_ctx->time_base.den = 1000;
249271 if (!data.isEmpty()) {
250272 av_free(codec_ctx->extradata);
251 codec_ctx->extradata = (uint8_t*)av_mallocz(data.size() + FF_INPUT_BUFFER_PADDING_SIZE);
273 codec_ctx->extradata = (uint8_t*)av_mallocz(data.size() + AV_INPUT_BUFFER_PADDING_SIZE);
252274 if (!codec_ctx->extradata)
253275 return false;
254276 codec_ctx->extradata_size = data.size();
0 _av*
1 _ff*
2 _sw*
3 _uchardet_*
4 _ass_*
5 _lzma_*
6 _al*
7 _rgb*
4747 */
4848 void setThreshold(int min); //wake up and enqueue
4949
50 void put(const T& t);
51 T take();
50 /*! \brief put
51 * put t into the queue. Will block if blockFull is set to true (and optionally can specify a wait timeout in ms).
52 * \param t the item to copy into the queue
53 * \param wait_timeout_ms this parameter is used if blockFull == true (the default).
54 * If the queue is full, optionally wait for the queue to not be full a maximum of wait_timeout_ms milliseconds,
55 * and return false if timeout expires. ULONG_MAX means wait indefinitely.
56 * \return true if item was successfully placed in the queue and the queue was not full.
57 * false is returned if the queue is (still) full. The item is still placed into a full queue!
58 * Note that even a 'full' queue will accept new items and t WILL be placed in the queue regardless of return value.
59 */
60 bool put(const T& t, unsigned long wait_timeout_ms = ULONG_MAX);
61 /*! \brief take
62 * Dequeue 1 item from queue, optionally blocking.
63 * \param wait_timeout_ms this parameter is used if blockEmpty == true (the default).
64 * If the queue is empty, optionally wait for the queue to not be empty a maximum of wait_timeout_ms milliseconds.
65 * ULONG_MAX means wait indefinitely.
66 * \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.
67 * \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.
68 */
69 T take(unsigned long wait_timeout_ms = ULONG_MAX, bool *isValid = 0);
5270 void setBlocking(bool block); //will wake if false. called when no more data can enqueue
5371 void blockEmpty(bool block);
5472 void blockFull(bool block);
132150 }
133151
134152 template <typename T, template <typename> class Container>
135 void BlockingQueue<T, Container>::put(const T& t)
136 {
153 bool BlockingQueue<T, Container>::put(const T& t, unsigned long timeout_ms)
154 {
155 bool ret = true;
137156 QWriteLocker locker(&lock);
138157 Q_UNUSED(locker);
139158 if (checkFull()) {
159 ret = false;
140160 //qDebug("queue full"); //too frequent
141161 if (full_callback) {
142162 full_callback->call();
143163 }
144164 if (block_full)
145 cond_full.wait(&lock);
165 ret = cond_full.wait(&lock, timeout_ms);
166 // uncomment here to reject placing items into a full queue -- update API docs if you do this.
167 // if (!ret) return false;
146168 }
147169 queue.enqueue(t);
148170 onPut(t); // emit bufferProgressChanged here if buffering
152174 } else {
153175 //qDebug("buffering: %d/%d~%d", queue.size(), thres, cap);
154176 }
155 }
156
157 template <typename T, template <typename> class Container>
158 T BlockingQueue<T, Container>::take()
159 {
177 return ret;
178 }
179
180 template <typename T, template <typename> class Container>
181 T BlockingQueue<T, Container>::take(unsigned long timeout_ms, bool *isValid)
182 {
183 if (isValid) *isValid = false;
160184 QWriteLocker locker(&lock);
161185 Q_UNUSED(locker);
162186 if (checkEmpty()) {//TODO:always block?
165189 empty_callback->call();
166190 }
167191 if (block_empty)
168 cond_empty.wait(&lock); //block when empty only
192 cond_empty.wait(&lock,timeout_ms); //block when empty only
169193 }
170194 if (checkEmpty()) {
171195 //qWarning("Queue is still empty");
175199 return T();
176200 }
177201 T t(queue.dequeue());
202 if (isValid) *isValid = true;
178203 cond_full.wakeOne();
179204 onTake(t); // emit start buffering here if empty
180205 return t;
8383 D3DPRESENT_PARAMETERS d3dpp;
8484 InitParameters(&d3dpp);
8585 // D3DCREATE_MULTITHREADED is required by gl interop. https://www.opengl.org/registry/specs/NV/DX_interop.txt
86 // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING in cuda samples
87 DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_MIXED_VERTEXPROCESSING;
86 // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING is required by cuda in cuD3D9CtxCreate()
87 DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING;
8888 IDirect3DDevice9Ex *d3d9dev = NULL;
8989 // mpv:
9090 /* Direct3D needs a HWND to create a device, even without using ::Present
4242 set(HEADERS ${SDK_HEADERS})
4343 if(WIN32)
4444 set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc)
45 configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
45 configure_file(${QTAV_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE})
4646 endif()
4747 # add HEADERS for moc
4848 add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE})
00 /******************************************************************************
11 QtAV: Multimedia framework based on Qt and FFmpeg
2 Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
2 Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
33
44 * This file is part of QtAV
55
2121 #include "QtAVWidgets/GraphicsItemRenderer.h"
2222 #include "QtAV/private/QPainterRenderer_p.h"
2323 #include "QtAV/FilterContext.h"
24 #define QTAV_HAVE_OPENGL (!defined QT_NO_OPENGL && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) || defined(QT_OPENGL_LIB)))
24 #if !defined QT_NO_OPENGL && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) || defined(QT_OPENGL_LIB))
25 #define QTAV_HAVE_OPENGL 1
26 #else
27 #define QTAV_HAVE_OPENGL 0
28 #endif
2529 #if QTAV_HAVE(OPENGL)
2630 #include "QtAV/OpenGLVideo.h"
2731 #else
3337 #include <QEvent>
3438 #include <QKeyEvent>
3539 #include <QGraphicsSceneEvent>
40 #include <QtCore/QCoreApplication>
3641 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
3742 #include <QtGui/QSurface>
3843 #endif
5055 void setupAspectRatio() {
5156 matrix.setToIdentity();
5257 matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1);
53 if (orientation)
54 matrix.rotate(orientation, 0, 0, 1); // Z axis
58 if (rotation())
59 matrix.rotate(rotation(), 0, 0, 1); // Z axis
5560 }
5661 // return true if opengl is enabled and context is ready. may called by non-rendering thread
5762 bool checkGL() {
125130 {
126131 preparePixmap(frame);
127132 }
128 scene()->update(sceneBoundingRect()); //TODO: thread?
133 // moved to event
134 // scene()->update(sceneBoundingRect()); //TODO: thread?
135 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
129136 //update(); //does not cause an immediate paint. my not redraw.
130137 return true;
131138 }
172179 } else {
173180 qWarning("FilterContext not available!");
174181 }
182 // save painter state, switch to native opengl painting
183 painter->save();
184 painter->beginNativePainting();
185
175186 handlePaintEvent();
187
188 // end native painting, restore state
189 painter->endNativePainting();
190 painter->restore();
191
176192 d.painter = 0; //painter may be not available outside this function
177193 if (ctx)
178194 ctx->painter = 0;
289305 #if CONFIG_GRAPHICSWIDGET
290306 bool GraphicsItemRenderer::event(QEvent *event)
291307 {
292 setFocus(); //WHY: Force focus
293 QEvent::Type type = event->type();
294 qDebug("GraphicsItemRenderer event type = %d", type);
295 if (type == QEvent::KeyPress) {
296 qDebug("KeyPress Event. key=%d", static_cast<QKeyEvent*>(event)->key());
297 }
308 if (e->type() == QEvent::User) {
309 scene()->update(sceneBoundingRect());
310 }
311 else {
312 setFocus(); //WHY: Force focus
313 QEvent::Type type = event->type();
314 qDebug("GraphicsItemRenderer event type = %d", type);
315 if (type == QEvent::KeyPress) {
316 qDebug("KeyPress Event. key=%d", static_cast<QKeyEvent*>(event)->key());
317 }
318 }
298319 return true;
299320 }
300321 #else
322 bool GraphicsItemRenderer::event(QEvent *event)
323 {
324 if (event->type() != QEvent::User)
325 return GraphicsWidget::event(event);
326 scene()->update(sceneBoundingRect());
327 return true;
328 }
301329 /*simply passes event to QGraphicsWidget::event(). you should not have to
302330 *reimplement sceneEvent() in a subclass of QGraphicsWidget.
303331 */
103103 #if CONFIG_GRAPHICSWIDGET
104104 bool event(QEvent *event) Q_DECL_OVERRIDE;
105105 #else
106 bool event(QEvent *event) Q_DECL_OVERRIDE;
106107 //bool sceneEvent(QEvent *event) Q_DECL_OVERRIDE;
107108 #endif //CONFIG_GRAPHICSWIDGET
108109
4444 void setFile(const QString& file);
4545 QString file() const;
4646 // default is false
47 void setKeepAspectRatio(bool value = true);
48 bool isKeepAspectRatio() const;
47 void setKeepAspectRatio(bool value = true) { m_keep_ar = value; }
48 bool isKeepAspectRatio() const { return m_keep_ar; }
49 /// AutoDisplayFrame -- default is true. if true, new frames from underlying extractor will update display widget automatically.
50 bool isAutoDisplayFrame() const { return m_auto_display; }
51 /// If false, new frames (or frame errors) won't automatically update widget
52 /// (caller must ensure to call displayFrame()/displayFrame(frame) for this if false).
53 /// set to false only if you want to do your own frame caching magic with preview frames.
54 void setAutoDisplayFrame(bool b=true);
55 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
56 void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame
57 void displayNoFrame();
4958 Q_SIGNALS:
5059 void timestampChanged();
5160 void fileChanged();
61 /// emitted on real decode error -- in that case displayNoFrame() will be automatically called
62 void gotError(const QString &);
63 /// usually emitted when a new request for a frame came in and current request was aborted. displayNoFrame() will be automatically called
64 void gotAbort(const QString &);
65 /// useful if calling code is interested in keeping stats on good versus bad frame counts,
66 /// or if it wants to cache preview frames. Keeping counts helps caller decide if
67 /// preview is working reliably or not for the designated file.
68 /// parameter frame will always have: frame.isValid() == true, and will be
69 /// already-scaled and in the right format to fit in the preview widget
70 void gotFrame(const QtAV::VideoFrame & frame);
5271 protected:
5372 virtual void resizeEvent(QResizeEvent *);
54 private Q_SLOTS:
55 void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame
56 void displayNoFrame();
5773
5874 private:
59 bool m_keep_ar;
75 bool m_keep_ar, m_auto_display;
6076 QString m_file;
6177 VideoFrameExtractor *m_extractor;
6278 VideoOutput *m_out;
2828 VideoPreviewWidget::VideoPreviewWidget(QWidget *parent)
2929 : QWidget(parent)
3030 , m_keep_ar(false)
31 , m_auto_display(false) // set to false initially to trigger connections in setAutoDisplayFrame() below -- will default to true
3132 , m_extractor(new VideoFrameExtractor(this))
3233 , m_out(new VideoOutput(VideoRendererId_Widget, this))
3334 // FIXME: opengl may crash, so use software renderer here
3637 Q_ASSERT_X(m_out->widget(), "VideoPreviewWidget()", "widget based renderer is not found");
3738 m_out->widget()->setParent(this);
3839 connect(m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged()));
39 connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame)));
40 connect(m_extractor, SIGNAL(error()), SLOT(displayNoFrame()));
41 connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame()));
40 connect(m_extractor, SIGNAL(error(const QString &)), this, SIGNAL(gotError(const QString &)));
41 connect(m_extractor, SIGNAL(aborted(const QString &)), this, SIGNAL(gotAbort(const QString &)));
4242 m_extractor->setAutoExtract(false);
43 m_auto_display = false;
44 setAutoDisplayFrame(true); // set up frame-related connections, defaulting autoDisplayFrame to true
45 }
46
47 void VideoPreviewWidget::setAutoDisplayFrame(bool b)
48 {
49 if (!!b == !!m_auto_display) return; // avoid reduntant connect/disconnect calls
50 if (b) {
51 connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame)));
52 connect(m_extractor, SIGNAL(error(const QString &)), SLOT(displayNoFrame()));
53 connect(m_extractor, SIGNAL(aborted(const QString &)), SLOT(displayNoFrame()));
54 connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame()));
55 } else {
56 disconnect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), this, SLOT(displayFrame(QtAV::VideoFrame)));
57 disconnect(m_extractor, SIGNAL(error(const QString &)), this, SLOT(displayNoFrame()));
58 disconnect(m_extractor, SIGNAL(aborted(const QString &)), this, SLOT(displayNoFrame()));
59 disconnect(this, SIGNAL(fileChanged()), this, SLOT(displayNoFrame()));
60 }
4361 }
4462
4563 void VideoPreviewWidget::resizeEvent(QResizeEvent *e)
82100 if (diff > m_extractor->precision()) {
83101 //qWarning("timestamp difference (%d/%lld) is too large! ignore", diff);
84102 }
85 if (m_out->isSupported(frame.format().pixelFormat())) {
86 m_out->receive(frame);
103 if (!frame.isValid()) {
104 displayNoFrame();
87105 return;
88106 }
89107 QSize s = m_out->widget()->rect().size();
92110 fs.scale(s, Qt::KeepAspectRatio);
93111 s = fs;
94112 }
95 VideoFrame f(frame.to(VideoFormat::Format_RGB32, s));
96 if (!f.isValid())
113 // make sure frame is the same size as the output widget size, and of the desired format
114 // if not, convert & scale
115 VideoFrame f(frame.pixelFormat() == m_out->preferredPixelFormat() && frame.size() == s
116 ? frame
117 : frame.to(m_out->preferredPixelFormat(), s)
118 );
119 if (!f.isValid()) {
97120 return;
121 }
98122 m_out->receive(f);
123 Q_EMIT gotFrame(f);
99124 }
100125
101126 void VideoPreviewWidget::displayNoFrame()
2323 #include <QBoxLayout>
2424 #include <QMessageBox>
2525 #include <QPushButton>
26 #include <QTableWidget>
26 #include <QTabWidget>
2727 #include <QTextBrowser>
2828
2929 #include "QtAVWidgets/WidgetRenderer.h"
2323
2424 !rc_file {
2525 RC_ICONS = $$PROJECTROOT/src/QtAV.ico
26 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com"
26 QMAKE_TARGET_COMPANY = "wbsecg1@gmail.com"
2727 QMAKE_TARGET_DESCRIPTION = "QtAVWidgets module. QtAV Multimedia framework. http://qtav.org"
28 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com"
28 QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2019 WangBin, wbsecg1@gmail.com"
2929 QMAKE_TARGET_PRODUCT = "QtAV Widgets"
3030 } else:win32 {
3131 RC_FILE = QtAVWidgets.rc