QtAV-1.12.0/000077500000000000000000000000001312235004300123775ustar00rootroot00000000000000QtAV-1.12.0/.gitignore000066400000000000000000000011061312235004300143650ustar00rootroot00000000000000CVS .#* # Hidden files are ignored by default with exceptions .* !/.gitignore !/.gitmodules !/.qmake.conf !/.travis.yml # .install files in debian/ are generated and ignored debian/*.install obj TestResults *.pbxuser *.perspectivev3 *.old *.log *.out *.cache *.deb *.git.old* Makefile* *.*pro*.user* *.[oa] *.so* *.dll *.lib *.exp *.exe *.out #vc files *.def *.dep *.idb *.layout *.manifest *.ncb *.obj *.pdb *.suo *.user *.tlh *.tli #intel compiler *.ilk #dirs /bin* /lib* build Debug Release *.Debug *.Release *.fuse* #qt *.prl *moc_* *.moc *qrc_res.cpp #cache *.swp QtAV-1.12.0/.gitmodules000066400000000000000000000004621312235004300145560ustar00rootroot00000000000000[submodule "contrib/capi"] path = contrib/capi url = https://github.com/wang-bin/capi.git [submodule "tools/build_ffmpeg"] path = tools/build_ffmpeg url = https://github.com/wang-bin/build_ffmpeg.git [submodule "contrib/uchardet"] path = contrib/uchardet url = https://github.com/BYVoid/uchardet.git QtAV-1.12.0/.qmake.conf000066400000000000000000000007561312235004300144320ustar00rootroot00000000000000QTAV_MAJOR_VERSION = 1 QTAV_MINOR_VERSION = 12 QTAV_PATCH_VERSION = 0 QTAV_VERSION = $${QTAV_MAJOR_VERSION}.$${QTAV_MINOR_VERSION}.$${QTAV_PATCH_VERSION} #MODULE_VERSION = $$QTAV_VERSION # set runpath instead of rpath for gcc for elf targets. Qt>=5.5 CONFIG *= enable_new_dtags # OSX10.6 is not supported in Qt5.4 macx:isEqual(QT_MAJOR_VERSION,5):greaterThan(QT_MINOR_VERSION, 3): CONFIG *= c++11 android: CONFIG*=c++11 QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.8 QMAKE_IOS_DEPLOYMENT_TARGET = 6.0 QtAV-1.12.0/.travis.yml000066400000000000000000000017331312235004300145140ustar00rootroot00000000000000dist: trusty #TODO: mingw cross build language: cpp cache: apt matrix: include: - os: osx compiler: clang env: - QT=brew - AV=ffmpeg-brew - os: linux compiler: gcc env: - QT=4.8.6 - AV=ffmpeg-1.0.10 - CC=gcc - os: linux compiler: clang env: - QT=5.0.2 - AV=ffmpeg-1.2.10 - os: linux compiler: clang env: - QT=5.1.1 - AV=ffmpeg-2.0.6 - os: linux compiler: clang env: - QT=5.2.1 - AV=ffmpeg-2.2.11 branches: only: - master - prelease - ci/travis - /^travis.*$/ - dev git: submodules: true install: . tools/ci/$TRAVIS_OS_NAME/install.sh script: - ./tools/ci/$TRAVIS_OS_NAME/script.sh after_success: ./tools/ci/$TRAVIS_OS_NAME/after_success.sh notifications: irc: channels: - "chat.freenode.net#qtav" email: recipients: - wbsecg1@gmail.com on_success: change on_failure: always QtAV-1.12.0/CMakeLists.txt000066400000000000000000000076121312235004300151450ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) set(QTAV_MAJOR 1) set(QTAV_MINOR 11) set(QTAV_PATCH 0) set(PROJECT_VERSION ${QTAV_MAJOR}.${QTAV_MINOR}.${QTAV_PATCH}) set(SO_VERSION ${QTAV_MAJOR}) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(POLICY CMP0063) # visibility. since 3.3 cmake_policy(SET CMP0063 NEW) endif() set(CMAKE_CXX_VISIBILITY_PRESET hidden) #use with -fdata-sections -ffunction-sections to reduce target size set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) option(BUILD_EXAMPLES "Build examples" ON) option(BUILD_PLAYERS "Build players" ON) option(BUILD_TESTS "Build tests" ON) option(BUILD_QT5OPENGL "Build with Qt5 OpenGL module" ON) list(APPEND CMAKE_FIND_ROOT_PATH ${QTDIR}) find_package(Qt5Core REQUIRED) find_package(Qt5Gui REQUIRED) get_target_property(qmake Qt5::qmake LOCATION) execute_process( COMMAND ${qmake} -query QT_INSTALL_HEADERS OUTPUT_VARIABLE QT_INSTALL_HEADERS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${qmake} -query QT_INSTALL_LIBS OUTPUT_VARIABLE QT_INSTALL_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${qmake} -query QT_INSTALL_BINS OUTPUT_VARIABLE QT_INSTALL_BINS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${qmake} -query QT_INSTALL_QML OUTPUT_VARIABLE QT_INSTALL_QML OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${qmake} -query QT_INSTALL_PREFIX OUTPUT_VARIABLE QT_INSTALL_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QT5_CFLAGS}") set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(POLICY CMP0063) # visibility. since 3.3 cmake_policy(SET CMP0063 NEW) endif() set(CMAKE_CXX_VISIBILITY_PRESET hidden) #use with -fdata-sections -ffunction-sections to reduce target size set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Find includes in corresponding build directories set(CMAKE_INCLUDE_CURRENT_DIR ON) # for .moc # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) if(NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(QTAV_INSTALL_HEADERS ${CMAKE_INSTALL_PREFIX}/include) set(QTAV_INSTALL_LIBS ${CMAKE_INSTALL_PREFIX}/lib) set(QTAV_INSTALL_BINS ${CMAKE_INSTALL_PREFIX}/bin) set(QTAV_INSTALL_QML ${CMAKE_INSTALL_PREFIX}/qml) else() set(CMAKE_INSTALL_PREFIX ${QT_INSTALL_PREFIX} CACHE PATH "default install path" FORCE) set(QTAV_INSTALL_HEADERS ${QT_INSTALL_HEADERS}) set(QTAV_INSTALL_LIBS ${QT_INSTALL_LIBS}) set(QTAV_INSTALL_BINS ${QT_INSTALL_BINS}) set(QTAV_INSTALL_QML ${QT_INSTALL_QML}) endif() message(STATUS "Qt version: ${Qt5Core_VERSION_STRING}") message(STATUS "Qt prefix: ${QT_INSTALL_PREFIX}") message(STATUS "QtAV headers prefix: ${QTAV_INSTALL_HEADERS}") message(STATUS "QtAV libs prefix: ${QTAV_INSTALL_LIBS}") message(STATUS "QtAV bins prefix: ${QTAV_INSTALL_BINS}") message(STATUS "QtAV qml prefix: ${QTAV_INSTALL_QML}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) #set(CMAKE_DEBUG_POSTFIX d) install(FILES cmake/FindQtAV.cmake DESTINATION ${QTAV_INSTALL_LIBS}/cmake ) if(CMAKE_GENERATOR MATCHES "Visual Studio.*") set(UCRT_INCLUDE $ENV{UniversalCRTSDKDir}Include\\$ENV{UCRTVersion}\\ucrt) set(VC_ARCH $ENV{Platform}) if(NOT VC_ARCH) set(VC_ARCH x86) endif() set(UCRT_LIB $ENV{UniversalCRTSDKDir}Lib\\$ENV{UCRTVersion}\\ucrt\\${VC_ARCH}) if(EXISTS ${UCRT_INCLUDE}) message("VS project. ucrt: ${UCRT_LIB}") include_directories(${UCRT_INCLUDE}) link_libraries(${CMAKE_LIBRARY_PATH_FLAG}${UCRT_LIB}) endif() endif() add_subdirectory(src) find_package(Qt5Widgets) if(Qt5Widgets_FOUND) add_subdirectory(widgets) endif() find_package(Qt5 COMPONENTS Qml Quick) if(Qt5Qml_FOUND AND Qt5Quick_FOUND) add_subdirectory(qml) endif() if(BUILD_EXAMPLES OR BUILD_PLAYERS) add_subdirectory(examples) endif() QtAV-1.12.0/Changelog000066400000000000000000000437611312235004300142240ustar00rootroot00000000000000version 1.12.0 2017-06-20 Changelog - CMake support - Fix iOS build - Fix AVPacket memory leak - Geometry & GeometryRenderer class is public - PAL8, XYZ color format support - Fix OpenGL background color - Fix external clock error after pause - VA-API encoder support (ffmpeg>=3) - Other improvements, fixes, and untested features version 1.11.0 2016-06-19 Changelog - Dynamic load QtAVWidgets in VideoOutput. `QtAV::Widgets::registerRenderers()` and `QT += avwidgets` is not requred now. - D3D11 decoder support. Not stable on Windows phone. - DXVA refactor and bug fix. - VA-API: fix memory leak when seeking - OpenGL: * Fix PBO binding * Custom shader support (WIP) * Edit and apply custom shader when playing. Based on Qt property system. * Optimize uniform update * GLSL filter support * Add `OpenGLVideo.beforeRendering/afterRendering` - CUDA: * Direct copy is default for windows. Now Rendering CUDA decoded frames is faster than any other players * 0-copy is default for linux. * Use new API (4.0) - AVPlayer: * Add `AVPlayer.stoppedAt` signal * add `setTimeRange()` * setStart/StopPosition can be called at any time with any value * Ensure all threads are running when `started` is emitted * Support preload using `load()`. Now no reload in `play()` - Fix build with QT_NAMESPACE - Fix swscale color range - Fix linux install destinations - Add AVTranscoder.async, encoders are in individual threads - Add FrameReader class - Use FFmpeg 3.0 API - QML: * Support filters, including sw filters from libavfilter, and GLSL filter * Add `MediaPlayer/Video.audioBackends` property * Support autoLoad * Add `VideoOutput.mapTo/FromXXX` between source frame and item * Video EQ in VideoOutput/Video * Add `MediaPlayer.startPosition/stopPotion` - QMLPlayer: Open video and subtitle at the same time. History view. Zoom. Video EQ version 1.10.0 2016-03-01 Changelog - Improve seek speed, fix drop too much frames after seek - VideoFrameExtractor: seek faster if called many times. Speed up progress bar preview - Support media files from android assets. Use protocol 'assets:' - Apply video decoder options when playing - Better iOS support - VideoToolbox: * Check profile to avoid crash * Support RGB output * Use `rgb422_apple` texture * Support iOS 0-copy rendering using OpenGLES * More interop types for OSX and iOS - OpenGL * Use 16bit depth textures for >8bit videos (e.g. 10bit) if possible * Correct input/output color range. This fixes QtAV video different from other players * Fix high opengl and core profile rendering * Better EGL detection - VO: * Simplify signal emitting * Support background color, simplify background painting * VideoOutput delete crash, resize issues * Improve performance of QPainter based renderers - AudioOutput: * remove setBufferSize, add a better API setBufferSamples * Add flush(), clear(). Not implemented for backends. * Fix OpenAL playback for some audio formats * Dynamic load OpenAL by default except iOS * Add AudioToolbox support for OSX/iOS and becomes the default backend * PulseAudio: workaround noise for multiple instance - Subtitle: * Support ass font files in android assets * Fix srt decoding - QML: * Add QtMultimedia compatible APIs VideoOutput.contentRect/sourceRect - Simplify SurfaceType - Fix strange behaviors near eof: seek, stepForward, exit etc. - Fix playback block if audio duration is shorter than video - VideoFrame constructed from QImage takes image's ownership - Add VideoFrame.colorRange, AudioFrame.duration - encoder: support start time - Add AVPlayer.bufferSpeed, seekFinished(qint64) - Fix abort when loading - Examples: * Fix videos in videowall not synchronized * QMLPlayer has better touch screen experience * QMLPlayer supports win10 store (But Qt is not well supported) * Drag & Drop support on OSX * URL open dialog for QMLPlayer version 1.9.0 2015-12-19 Changelog: - No audio thread if audio stream < 0. null AudioOutput now still create audio thread. - VideoOutput: fix lock error - Filter: add X11 text rendering for X11Renderer/XVRenderer. - LibAVFilter supports HW frames(not recommended) - VideoFrame copy and init issue, bpp compute error - Avoid using some stl APIs that breaks ABI - Add x11 renderer - XV: support NV12 and packed yuv formats - Fix factory initialization for static build(iOS for example) - Fix ffmpeg log level - Remove AVPicture which is deprecated - SWS 16 alignment, output to user defined buffer - Always define the registered id so we can use everywhere - Better iOS support: build, run, deploy(prl) - use libuchardet now, use system one if possible - Fix android build on windows - XAudio2: fix crash. `double` sample format is not supported - Fix clock value if speed changed - VideoRenderer.updateUi - VideoCapture: fix HW frame save - New logo ![](http://qtav.org/blog/images/QtAV1.9.0-logo.png "Logo") - Android: support protocol 'content'. Now can play from Gallery. - MediaIO is not writable by default - WinRT (Windows Store) support: * Link to XAudio2 * File open and protocol - VA-API: * Add hevc, vp9. Not tested * Support more profiles * Improve nvidia support * Interop with egl support(dma, tfp). libva 0.38 and Qt>=5.5 are required. * Check resolution support before open * Add thread count option (new ffmpeg >2.8 does not support) * Prefer /dev/dri/renderD128 for drm * Correctly release X11/drm resource * Interop with egl support(dma, tfp). libva 0.38 and Qt>=5.5 are required. - OpenGL: * OpenGL ES3 optimization * use new OpenGL and GLSL APIs if possible - CI: * Add windows. Build with latest Qt release. Using msbuild, nmake and mingw32-make - API: * AVPlayer.stepForward(), stepBackword(). playNextFrame() is deprecated * AVPlayer.state * AVPlayer.videoDecoderPriority with name list * VideoRenderer.frameSize (C++), VideoOutput.frameSize * XXXDecoder/Encoder::supportedCodecs, AV(De)Muxer.supportedFormats/ Extensions * QML: more metaData keys: startTime, sampleFormat, channelLayout, pixelFormat, videoFrames - QMLPlayer/Player: * fix OpenGLWidgetRenderer crash * Handle Home/Back button for mobile * Preview item is movable * ppa: rename /usr/bin/player to /usr/bin/Player version 1.8.0 2015-09-01 Changelog: - XAudio2 support - VideoToolbox hardware decoder(OSX) support. It can decode h264, h263, mpeg1, mpeg2, mpeg4. Better than VDA - CUDA 0-copy support - Dynamic video decoder switch when playing (try it in QMLPlayer) - Embedded ass subtitles support - ass subtitle font setup - No OpenGL build fix for linux - Fix qpainter rendering if vbo is used - Fix OpenGL rendering for some rgb formats - DXVA 0-copy improvement, intel clearvideo workaround etc. - AO: add null backend - HW: fix profile check - Ref counted frames from ff decoders. This fix opengl crash when uploading textures - Fix audio resampler parameters not set correctly - VS2015 support version 1.7.0 2015-07-09 Changelog: - DXVA GPU zero-copy support - DXVA copy back improvement - VAAPI zero-copy improvement - Fix opengl wrong display if switch decoder between VDA and FFmpeg - Video encoding support. Try examples/simpletranscode. Audio is not supported now - External audio track support - Pulseaudio improvement, fix crashes and hang - OpenGL: disable VBO for ANGLE to workaround qpainter filters have no effect. Less GL calls. Fix OpenGLWindowRenderer can not update correctly - Fix capture video when a zero-copy HW decoder is used. - Fix wrong audio data is played at the beginning of playback - Enable XV shm - Improve a/v sync - Build android on windows - Fix msvc debug build - Use OpenSL ES as for android. It works correctly now. No OpenAL required any more. Add volume control for OpenSL ES. - Fix crashes - Add ubuntu PPA - CI: appveyor for windows - AVInput=>MediaIO. You can use it to support custom output when encoding video - Add seekFinished, durationChanged, QML MediaPlayer.videoCapture, Subtitle delay etc - QMLPlayer: swipe to seek version 1.6.0 2015-04-23 Changlog: - VDA: use NV12 as default format. Support 0 copy. Better performance than any other playrs - CUDA: fix wrong bitstream filters - OpenGL: * PBO support (enable by enviroment var QTAV_PBO=1) * rectangle texture support * packed yuv formats support - Support buffer progress, buffer value control - Support frame rate setup (AVPlayer::setFrameRate()) - Support RAW streams, such as h264, yuv - Audio filter support - Better libavfilter support. Now support libav avfilter, support audio filters from libavfilter - Add pulseaudio support - Detect volume change by per-app volume control - Simplify audio output API - Volume and mute control by audio backend API - Use float audio sample if possible - Fix iOS static link error - Fix error handling, EOF detect, Qt4.7 build etc. - Deploy: add qml, fix lib name, deploy sdk from installer, mingw deployment on linux. - player/QMLPlayer: * decoder detail * more options: preview config, buffer, fps, timeout * unregister context menu(win32) if uninstalled, ifw2.0 * QMLPlayer file associate for android version 1.5.0 2015-02-13 Changelog: - Add QtAVWidgets module contains widget based renderers, video preview widget - QtAV module only depends on QtCore and QtGui - Improve audio output API - Simplify deployment on OSX. Deploy sdk from release packages without build. - Improve seekable detection - Fix noise audio if volume increased - Fix noise and wrong playback speed for wmv videos - Preview is more accurate - Do not copy AVPacket but use reference count. - Faster audio track switch - Reopen audio output only if audio format changed - VideoFrame: new API for converting to another format or QImage - OpenGL VAO, VBO support. - QML * Fix some crashes in QQuickItemRenderer * Add FBO item renderer and use the same code path as other opengl renderers * timeout property - player and QMLPlayer example * player: real time preview on progress bar * Fast seek and display if seek shortcut is pressed and hold version 1.4.2 2014-12-27 Changelog: - Async load - Faster seek and preview. Thanks wm4(mpv developer) - Better a/v sync solution. Now can play high fps video better than ANY other players. - DXVA: enable HEVC decoder if libavcodec runtime supports. LAVFilters ffmpeg supports it. - Cedarv improvements, nv12 output support, neon code from libvdpau-sunxi - Multiple OpenAL instances support - AVInput class to extend media sources. Support play from qrc resouces. - Display the first frame after seek - Better clock error correction - Fix play frame by frame issues - Fix QML video shake - More details for error signals - Apply user options in the next playback - Dynamic load libass (submodule 'capi' is required, https://github.com/wang-bin/capi) - VA-API: fix blurred bottom line issue. works on libav >= 9 now. - Progress is start at 0 by default even if 1st pts is not 0. - Fix capture issues: capture current displayed frame, easier api - RGB48 support. OpenGL only supports RGB48LE. - Link to static ffmpeg support - Fix open error if input url/path contains special charactors version 1.4.1 2014-11-06 Changelog: - More QtMultimedia compatible QML playback API - Debian package support. Now you can build on ubuntu>=14.04 using debuild - Subtitle: libass renderering support. Dynamically change FFmpeg parser and libass - Log level control support without changing any Qt logging functions (qDebug, qWarning etc.) - Video orientation support in renderers - Bug fix: video frame with alpha value. video stream start time >0. OpenAL can not close correctly... - QML only: video preview. - players: config UI in QMLPlayer. Video preview on progress bar. version 1.4.0 2014-09-10 Changelog: - Improve OpenGL: more pixel formats, supports QML(not stable), dynamic gl support etc. - VAAPI: GLX direct rendering support. check 4k. - DXVA: more decoding surfaces to improve performance - VDA (OSX hardware decoding) support - Android support. Easy to deploy - Subtitle support, including QML - Improve OpenAL. Works fine on most platforms including android - FFmpeg libavfilter support - Audio output api change - deploy script for ifw, OSX - Bug fix - WIP: dsound, opensl, audio engine, audio filters etc. version 1.3.3 2014-04-20 - CUDA support for windows and linux. (The first player/library support CUDA decoding on linux?) - Renderering 16-bit YUV (for example Hi10P) for both OpenGL and OpenGL ES2. maybe it's the first player/library supports rendering 10-bit using OpenGL ES2. VLC, XBMC and mplayer can't Supported format: 9, 10, 12, 14, 16 bit, planar, little/big endian. - Support Sailfish OS. Works perfectly. - Fix mosaics for some videos since QtAV1.3.1 - Better seeking - New VideoOutput class with QObject features support - Improve cedarv hardware decoding. - Audio channel api for QML - FFmpeg build script supports maemo5, meego, sailfish, android, mingw gcc, msvc - Fix build error for Qt 5.0 - player: * fix wrong video display size when fullscreen mode changed or toolbar hidden version 1.3.2 2014-03-16 - OpenGL improvement and better performance(works on iOS and Android): * enable GLSL if supported * use GLSL to render YUV frames * GLSL based video equalizer - XV bug fix and improvement. Supports NV12. Supports video equalizer. - Play from QIODevice support - Capture YUV frames support. - Fix wrong duration in OSD - CUDA decoder working in progress - player: * no gui blocking while opening a stream * add config dialog - Regressions: * OpenGL can not display H10p version 1.3.1 2014-01-27 - Initial OpenAL support. - Fix hardware decoding crashes: seeking, packet loss(e.g. bad network) - Allwinner's CedarX hardware decoding support. Thanks Miroslav Bendik. - FFmpeg log and error handling. New class: AVError - AVDictionary support for decoder and demuxer. API: AVPlayer::setOptionsForXXX() - Fix crash on app exit - Fix Hi10bits crash - Build: * fix XXX_EXPORT macro confliction. * configurable qmake project etc. - QMLPlayer: better UI - player: * wheel to zoom in/out. * apply decoder without restart. * exit on main window close. * move EventHandler to player. - Installer: ubuntu support. - Screen saver for OSX, X11. Improve windows version 1.3.0 2013-12-19 - Selectable audio track - Multiple rendering target for 1 video support - Hardware decoding support(unstable): DXVA2 for windows, VA-API for x11(bad performance now) - Region of Interest(ROI) support, i.e. crop. - Filter support(Experimental) - Video eq: brightness, contrast, saturation - Repeat support - Mang api changes - QML: compatible with many QtMultiMedia APIs - build/install: check arch, fix wrong rpath and launch error on Mac, add installer - player: playlist, history, better online tv, etc. version 1.2.4 2013-09-08 - QML support(thanks theoribeiro ). Can replace some of qtmultimedia qml apps, e.g. qmlvideofx - Fix build for FFmpeg >= 0.9, libav >= 0.8 - Channel can be choosed by user: original, stereo, left, right, centre(mono) - OSD for GL ES2 renderer - player: statistics viewer, disable screensaver on widows when playing, more online tv channels version 1.2.3 2013-08-01 - FFmpeg/libav avresample support - OpenGL ES2 support. Can build against Qt5 GLES2 version now - XV support for Qt5 - more audio format support, e.g. sample format, channels. - Fix H.264 decoding error for the beginning frames. - Fix audio decoding error if packet should be decoded multiple times. WMA audio. - Save video captures to system Picture directory version 1.2.2 2013-07-03 - Audio resampler using swresample - Play speed control - Audio format - Play good with audio that noise before(mp3, ...) - init and deinit FFmpeg only when needed - Component id accessed by symbol - mms support version 1.2.1 2013-06-19 - Configurable video quality - Correct aspect ratio. Any value is supported - Fix some media playing problem - Fix crash if no audio - Fix d2d crash by setting multi-thread type - Fix WidgetRenderer can not update problem - Fix mem leak if renderer changed - player: volume control, display correct online tv name, correct display time version 1.2.0 2013-05-27 - Better gui for the player example - Dynamically change renderer engine - Filter framework works. Filters on data and renderer support - XV rendering support - Fix some qmake project bugs - Other features, bug fix, code refine version 1.1.11 2013-03-15 - OpenGL support. Only rendering now. - OSD support. - New shortcuts: Ctrl+O=>open, O=>switch OSD, Q/ESC=>quit - Command line option: -vo gl/d2d/gdi/qt, -h|--help - Fix wrong aspect ratio when resizing - Better qmake project - Building debian package support: make deb - Bug fix version 1.1.10 2013-03-04 - Direct2D support. Auto check before compile - GDI+ renderer improved - Custom renderer template - Aspect ratio switch support - Config test support for both Qt4 and Qt5 - Better build system - Auto update rc file - Some documents version 1.1.9 2013-02-22 - Add network interrupt callback. Thanks Vito vcovito - QtAV and FFmpeg runtime version check - Add factory model to manager the components. Currently used in ImageConverter - MSVC support - Seek bug fix: some times blocked - qmake: no additional arguments required(if source dir is writable) - Add 2 graphics item renderer examples - GDI+ renderer support - Start to use script to create new classes version 1.1.8 2013-02-06 - LGPL version 2.1 - better Qt5 support - better synchronisation and seeking - crash fixed when getting stream info - crash fixed when replaying a music - drag & drop, simple internal gui and internal event filter - videowall works better version 1.1.7 2013-01-30 - scale image without qt support! - buggy ipp support. - thread safe capture - Fix first frame not correctly decoded! - new API - tests and examples: clockcontrol, videowall version 1.1.6 2013-01-23 - fix video blocking and crash when seeking - add tests: network, shared renderer - update frames when seeking in a paused state - avoid seeking too frequently - capture sync/async - playing music support - Auto select a suitable clock version 1.1.5 2013-01-18 - playing single stream support. i.e. playing video without audio or playing music - plannar audio support - output can be shared with multiple player - g++ compile error fix - external clock - optimizations and bug fix QtAV-1.12.0/QtAV.pro000066400000000000000000000057701312235004300137450ustar00rootroot00000000000000include(root.pri) TEMPLATE = subdirs CONFIG -= ordered SUBDIRS = libqtav tools libqtav.file = src/libQtAV.pro !no-widgets { SUBDIRS += libqtavwidgets libqtavwidgets.file = widgets/libQtAVWidgets.pro libqtavwidgets.depends = libqtav examples.depends += libqtavwidgets #TODO: enable widgets based examples } greaterThan(QT_MAJOR_VERSION, 4) { # qtHaveModule does not exist in Qt5.0 isEqual(QT_MINOR_VERSION, 0)|qtHaveModule(quick) { SUBDIRS += libqmlav libqmlav.file = qml/libQmlAV.pro libqmlav.depends += libqtav examples.depends += libqmlav } } !no-examples { SUBDIRS += examples examples.depends += libqtav } !cross_compile:!no-tests { SUBDIRS += tests tests.depends += libqtav libqtavwidgets } OTHER_FILES += README.md TODO.txt Changelog OTHER_FILES += templates/vo.h templates/vo.cpp templates/COPYRIGHT.h templates/mkclass.sh OTHER_FILES += \ templates/base.h templates/base.cpp templates/base_p.h \ templates/derived.h templates/derived.cpp templates/derived_p.h \ templates/final.h templates/final.cpp #OTHER_FILES += config.test/mktest.sh EssentialDepends = avutil avcodec avformat swscale winrt: CONFIG *= no-avdevice no-openal no-portaudio no-dsound no-gdiplus OptionalDepends = swresample avresample !no-avfilter: OptionalDepends *= avfilter !no-avdevice: OptionalDepends *= avdevice # QtOpenGL module. In Qt5 we can disable it and still have opengl support contains(QT_CONFIG, opengl):!no-gl:!no-widgets { greaterThan(QT_MAJOR_VERSION, 4):qtHaveModule(opengl):!config_gl { GL=config_gl done_config_gl cache(CONFIG, add, GL) } else { OptionalDepends *= gl } } ## sse2 sse4_1 may be defined in Qt5 qmodule.pri but is not included. Qt4 defines sse and sse2 #configure.prf always use simulator !iphoneos:!no-sse4_1:!sse4_1: OptionalDepends *= sse4_1 # no-xxx can set in $$PWD/user.conf !no-openal:!mac:!ios: OptionalDepends *= openal #FIXME: ios openal header not found in qtCompileTest but fine if manually make !no-libass: OptionalDepends *= libass !no-uchardet: OptionalDepends *= uchardet win32:macx:!android:!winrt:!no-portaudio: OptionalDepends *= portaudio win32 { OptionalDepends *= dx !no-xaudio2: OptionalDepends *= xaudio2 !no-direct2d:!no-widgets: OptionalDepends *= direct2d !no-dxva: OptionalDepends *= dxva !no-d3d11va: OptionalDepends *= d3d11va !no-dsound: OptionalDepends *= dsound !no-gdiplus:!no-widgets: OptionalDepends *= gdiplus } unix:!mac { !android { !no-pulseaudio: OptionalDepends *= pulseaudio !no-x11:!no-widgets: OptionalDepends *= x11 !no-xv:!no-widgets: OptionalDepends *= xv !no-vaapi: OptionalDepends *= vaapi } !no-cedarv: OptionalDepends *= libcedarv } mac|ios { !no-videotoolbox: OptionalDepends *= videotoolbox } runConfigTests() !config_avresample:!config_swresample { error("libavresample or libswresample is required. Setup your environment correctly then delete $$BUILD_DIR/.qmake.conf and run qmake again") } PACKAGE_VERSION = $$QTAV_VERSION PACKAGE_NAME= QtAV include(pack.pri) #packageSet($$QTAV_VERSION, QtAV) QtAV-1.12.0/README.md000066400000000000000000000134711312235004300136640ustar00rootroot00000000000000# [QtAV](http://www.qtav.org) [![Build Status](https://travis-ci.org/wang-bin/QtAV.svg)](https://travis-ci.org/wang-bin/QtAV) [![Appveyor](https://ci.appveyor.com/api/projects/status/github/wang-bin/qtav?svg=true&passingText=windows%20-%20OK)](https://ci.appveyor.com/project/wang-bin/qtav) [![Join the chat at https://gitter.im/QtAV/Lobby](https://badges.gitter.im/QtAV/Lobby.svg)](https://gitter.im/QtAV/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) QtAV is a multimedia playback library based on Qt and FFmpeg. It can help you to write a player with less effort than ever before. QtAV has been added to FFmpeg projects page [http://ffmpeg.org/projects.html](http://ffmpeg.org/projects.html) **QtAV is free software licensed under the term of LGPL v2.1. The player example is licensed under GPL v3. If you use QtAV or its constituent libraries, you must adhere to the terms of the license in question.** #### [Home page](http://www.qtav.org) ### Features QtAV can meet your most demands - Hardware decoding suppprt: DXVA2, VAAPI, VDA/VideoToolbox, CedarX, CUDA(the 1st player support CUDA on linux?) - OpenGL and ES2 support for almost all formats including Hi10P videos (The 1st player/library support 10bit in ES2? VLC, XBMC, mplayer does not support now) - Real time preview - Video capture in rgb and yuv format - OSD and custom filters - Filters in libavfilter, for example stero3d, blur - Subtitle track select. Dynamic change FFmpeg and libass engine - Play frame by frame - Playback speed control - Variant streams: locale file, http, rtsp etc. and your custom streams - Audio channel, tracks and external audio tracks - Dynamically change render engine when playing. - Dynamically change video decoder - Multiple video outputs for 1 player - Video eq(software and OpenGL): brightness, contrast, saturation, hue - QML support. Most playback APIs are compatible with QtMultimedia module - Compatiblity: QtAV can be built with both Qt4 and Qt5, FFmpeg(>=1.0) and [Libav](http://libav.org) (>=9.0). Latest FFmpeg release is recommended. ### Extensible Framework Some components in QtAV are designed to be extensible. For example, you can write your decoder, audio output for particular platform. [Here is a very good example to add cedar hardware accelerated decoder for A13-OLinuXino](https://github.com/mireq/QtAV/commit/d7b428c1dae66b2a85b7a6bfa7b253980b5b963c) # For Developers #### Requirements [![Qt](http://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Qt_logo.svg/64px-Qt_logo.svg.png "Qt4.8 or Qt5")](http://www.qt.io) [![FFmpeg](http://ffmpeg.org/ffmpeg-logo.png "(>=1.0)Latest version is recommanded")](http://ffmpeg.org) [![Libav](http://libav.org/libav-logo-text.png ">=9.0")](http://libav.org) ![OpenAL](http://upload.wikimedia.org/wikipedia/zh/2/28/OpenAL_logo.png "OpenAL or OpenAL soft") **The required development files to build QtAV can be found in sourceforge page: [depends](https://sourceforge.net/projects/qtav/files/depends)** #### Build See the wiki [Build QtAV](https://github.com/wang-bin/QtAV/wiki/Build-QtAV) and [QtAV Build Configurations](https://github.com/wang-bin/QtAV/wiki/QtAV-Build-Configurations) #### How To Write a Player Write a media player using QtAV is quite easy. GLWidgetRenderer2 renderer; renderer.show(); AVPlayer player; player.setRenderer(&renderer); player.play("test.avi"); For more detail to using QtAV, see the wiki [Use QtAV In Your Project](https://github.com/wang-bin/QtAV/wiki/Use-QtAV-In-Your-Projects) or examples. QtAV can also be used in **Qml** import QtQuick 2.0 import QtAV 1.6 Item { Video { id: video source: "test.mp4" } MouseArea { anchors.fill: parent onClicked: video.play() } } ### How To Contribute - [Fork](https://github.com/wang-bin/QtAV/fork) QtAV project on github and make a branch. Commit in that branch, and push, then create a pull request to be reviewed and merged. - [Create an issue](https://github.com/wang-bin/QtAV/issues/new) if you have any problem when using QtAV or you find a bug, etc. - What you can do: translation, write document, wiki, find or fix bugs, give your idea for this project etc. #### Contributors - Wang Bin(Lucas Wang): creator, maintainer - Gianluigi Tiesi(sherpya): avdevice input support - Stefan Ladage: QIODevice support. Wiki about build QtAV for iOS. Let OpenAL work on OSX and iOS - Miroslav Bendik: Cedarv support. Better qmlvideofx appearance - theoribeiro: initial QML support - Vito Covito: interrupt callback - Alexander, Marius Wachtler, Petar Koretić, Sandro Cavazzoni(skaman), Dimitri E. Prado, karlox ... For End Users ------------- #### Player Commandline Options Run `player -h` #### Default Shortcuts - Double click: fullscreen switch - Ctrl+O: open a file - Space: pause/continue - F: fullscreen on/off - T: stays on top on/off - N/B: show the next/previous frame. Continue the playing by pressing "Space" - O: OSD - P: replay - Q/ESC: quit - S: stop - A: switch aspect ratio - R: rotate 90 - M: mute on/off - Up / Down: volume + / - - Ctrl+Up/Down: speed + / - - -> / <-: seek forward / backward - Crtl+Wheel: zoom in/out - Drag and drop a media file to player Screenshots ---------- Use QtAV in QML with OpenGL shaders(example is from qtmultimedia. But qtmultimedia is replaced by QtAV) ![Alt text](https://sourceforge.net/p/qtav/screenshot/QtAV-QML-Shader.jpg "QtAV QML Shaders") ![Alt text](http://www.qtav.org/screenshots/player-OSX.jpg "player on OSX") ![QMLPlayer](http://www.qtav.org/screenshots/QMLPlayer-preview-ubuntu.jpg "QMLPlayer") ![Alt text](http://www.qtav.org/screenshots/videowall.png "video wall") *** ### [Donate 捐赠](http://www.qtav.org/donate.html) > Copyright © Wang Bin wbsecg1@gmail.com > Shanghai University->S3 Graphics->Deepin, Shanghai, China > 2013-01-21 QtAV-1.12.0/appveyor.yml000066400000000000000000000034761312235004300150010ustar00rootroot00000000000000branches: only: - master - prelease - ci/appveyor - appveyor environment: matrix: - arch: x86 cc: VS2015 qt: 5.9 toolchain_version: 14 mode: release QTDIR: C:\Qt\5.9\msvc2015 - arch: x64 cc: VS2013 qt: 5.6 toolchain_version: 12 mode: debug QTDIR: C:\Qt\5.6\msvc2013_64 - arch: 32 cc: MinGW qt: 5.7 toolchain_version: 530 mode: debug QTDIR: C:\Qt\5.7\mingw53_32 - arch: 32 cc: MinGW qt: 5.4 toolchain_version: 491 mode: release QTDIR: C:\Qt\5.4\mingw491_32 - arch: 32 cc: MinGW qt: 5.3 toolchain_version: 482 mode: debug QTDIR: C:\Qt\5.3\mingw482_32 matrix: fast_finish: false cache: - AV init: - set vcarch=%arch% - if "%arch%" == "x64" set vcarch=amd64 - if not %cc%==MinGW call "C:\Program Files (x86)\Microsoft Visual Studio %toolchain_version%.0\VC\vcvarsall.bat" %vcarch% - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS% - echo PROCESSOR_IDENTIFIER=%PROCESSOR_IDENTIFIER% - echo QTDIR=%QTDIR% install: - git submodule update --init - mkdir build - cd build - ..\tools\ci\win\install_dep.bat before_build: - set PATH=%QTDIR%\bin;%PATH% - if %cc%==MinGW set PATH=C:\Qt\Tools\mingw%toolchain_version%_32\bin;%PATH% - echo PATH=%PATH% build_script: - ..\tools\ci\win\build.bat after_build: - ..\tools\ci\win\deploy_win.bat test: off artifacts: - path: 'QtAV*.7z' # relative to repo root deploy: provider: FTP protocol: sftp host: frs.sourceforge.net username: novesky,qtav password: secure: 2mQg4oxdX8S7rMJz2TCGGg== folder: /home/frs/project/q/qt/qtav/ci # why always relative path even if starts with / ? I have to make a link home/frs/project/q/qt/qtav/ci => /home/frs/project/q/qt/qtav/ci active_mode: false on: mode: release cc: VS2015 QtAV-1.12.0/cmake/000077500000000000000000000000001312235004300134575ustar00rootroot00000000000000QtAV-1.12.0/cmake/FindQtAV.cmake000066400000000000000000000036451312235004300161050ustar00rootroot00000000000000# - Try to find the QtAV library # # Once done this will define # # QTAV_FOUND - system has libqtav # QTAV_INCLUDE_DIRS - the libqtav include directory # QTAV_LIBRARIES - Link these to use libqtav find_package(Qt5 QUIET REQUIRED NO_MODULE COMPONENTS Core) get_target_property(qmake Qt5::qmake LOCATION) execute_process( COMMAND ${qmake} -query QT_INSTALL_HEADERS OUTPUT_VARIABLE QT_INSTALL_HEADERS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${qmake} -query QT_INSTALL_LIBS OUTPUT_VARIABLE QT_INSTALL_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE ) find_path(QTAV_INCLUDE_DIR NAMES QtAV.h HINTS ${QT_INSTALL_HEADERS} PATH_SUFFIXES QtAV ) find_library(QTAV_LIBRARY NAMES QtAV QtAV1 HINTS ${QT_INSTALL_LIBS} ) find_path(QTAVWIDGETS_INCLUDE_DIR NAMES QtAVWidgets.h HINTS ${QT_INSTALL_HEADERS} PATH_SUFFIXES QtAVWidgets ) find_library(QTAVWIDGETS_LIBRARY NAMES QtAVWidgets QtAVWidgets1 HINTS ${QT_INSTALL_LIBS} ) set(QTAV_INCLUDE_DIRS ${QTAV_INCLUDE_DIR} ${QTAV_INCLUDE_DIR}/..) set(QTAV_LIBRARIES ${QTAV_LIBRARY}) if(NOT QTAVWIDGETS_INCLUDE_DIR MATCHES "QTAVWIDGETS_INCLUDE_DIR-NOTFOUND") set(QTAVWIDGETS_INCLUDE_DIRS ${QTAVWIDGETS_INCLUDE_DIR} ${QTAVWIDGETS_INCLUDE_DIR}/.. ${QTAV_INCLUDE_DIRS}) endif() if(NOT QTAV_LIBRARIES MATCHES "QTAV_LIBRARIES-NOTFOUND") set(QTAVWIDGETS_LIBRARIES ${QTAVWIDGETS_LIBRARY} ${QTAV_LIBRARY}) endif() find_package(PackageHandleStandardArgs REQUIRED) find_package_handle_standard_args(QtAV REQUIRED_VARS QTAV_LIBRARIES QTAV_INCLUDE_DIRS) mark_as_advanced(QTAV_INCLUDE_DIRS QTAV_LIBRARIES QTAVWIDGETS_INCLUDE_DIRS QTAVWIDGETS_LIBRARIES) message("QtAV_FOUND = ${QTAV_FOUND}") message("QTAV_INCLUDE_DIRS = ${QTAV_INCLUDE_DIRS}") message("QTAV_LIBRARIES = ${QTAV_LIBRARIES}") message("QTAVWIDGETS_INCLUDE_DIRS = ${QTAVWIDGETS_INCLUDE_DIRS}") message("QTAVWIDGETS_LIBRARIES = ${QTAVWIDGETS_LIBRARIES}") QtAV-1.12.0/cmake/QtAV.rc.in000066400000000000000000000021011312235004300152170ustar00rootroot00000000000000#include "winver.h" #include "@CMAKE_SOURCE_DIR@/src/QtAV/version.h" IDI_ICON1 ICON DISCARDABLE "@CMAKE_SOURCE_DIR@/src/QtAV.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 PRODUCTVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEFLAGSMASK 0x3fL FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000004b0" BEGIN VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin->PPTV | wbsecg1@gmail.com" VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org" VALUE "FileVersion", QTAV_VERSION_STR ".0" VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" VALUE "InternalName", "@MODULE@" VALUE "ProductName", "@MODULE@" VALUE "ProductVersion", QTAV_VERSION_STR ".0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0, 1200 END END QtAV-1.12.0/common.pri000066400000000000000000000420601312235004300144050ustar00rootroot00000000000000# qmake common template pri file # Copyright (C) 2011-2016 Wang Bin # Shanghai, China. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # isEmpty(COMMON_PRI_INCLUDED): { #begin COMMON_PRI_INCLUDED mac:contains(QT_CONFIG, qt_framework):!mac_dylib: CONFIG += mac_framework isEmpty(QMAKE_EXTENSION_SHLIB) { unix { mac|ios: QMAKE_EXTENSION_SHLIB = dylib else: QMAKE_EXTENSION_SHLIB = so #why android is empty? } else:win* { QMAKE_EXTENSION_SHLIB = dll } } CONFIG += profile #profiling, -pg is not supported for msvc debug:!ios:!android:!*msvc*:profile { QMAKE_CXXFLAGS_DEBUG += -pg QMAKE_LFLAGS_DEBUG += -pg QMAKE_CXXFLAGS_DEBUG = $$unique(QMAKE_CXXFLAGS_DEBUG) QMAKE_LFLAGS_DEBUG = $$unique(QMAKE_LFLAGS_DEBUG) } #$$[TARGET_PLATFORM] #$$[QT_ARCH] #windows symbian windowsce arm _OS = _ARCH = _EXTRA = unix { _OS = _unix android { _OS = _android } else:ios { _OS = _ios } else:macx { _OS = _osx } else:*maemo* { _OS = _maemo *maemo5*:_OS = _maemo5 *maemo6*:_OS = _maemo6 } else:*meego* { _OS = _meego !isEmpty(MEEGO_EDITION): _OS = _$$MEEGO_EDITION } else:*linux* { _OS = _linux } # QMAKE_RPATHDIR will be ignored if QMAKE_LFLAGS_RPATH is not defined. e.g. qt4.8 unsupported/macx-clang-libc++ isEmpty(QMAKE_LFLAGS_RPATH): QMAKE_LFLAGS_RPATH=-Wl,-rpath, } else:wince* { _OS = _wince } else:winrt { _OS = _winrt } else:win32 { #true for wince _OS = _win } #*arm*: _ARCH = $${_ARCH}_arm contains(QT_ARCH, arm.*) { _ARCH = $${_ARCH}_$${QT_ARCH} } *64|contains(QMAKE_TARGET.arch, x86_64): _ARCH = $${_ARCH}_x64 *llvm*: _EXTRA = _llvm #*msvc*: win32-msvc* { #Don't warn about sprintf, fopen etc being 'unsafe' DEFINES += _CRT_SECURE_NO_WARNINGS } #################################functions######################################### defineTest(qtAtLeast) { #e.g. qtAtLeast(4), qtAtLeast(5, 2), qtAtLeast(5, 4, 2) lessThan(QT_MAJOR_VERSION, $$1):return(false) isEmpty(2):return(true) greaterThan(QT_MAJOR_VERSION, $$1):return(true) lessThan(QT_MINOR_VERSION, $$2):return(false) isEmpty(3):return(true) greaterThan(QT_MINOR_VERSION, $$2):return(true) lessThan(QT_PATCH_VERSION, $$3):return(false) return(true) } defineTest(qtRunQuitly) { #win32 always call windows command contains(QMAKE_HOST.os,Windows) { system("$$1 2>&1 >nul")|return(false) #system always call win32 cmd } else { system("$$1 2>&1 >/dev/null")|return(false) } return(true) } defineReplace(platformTargetSuffix) { ios:CONFIG(iphonesimulator, iphonesimulator|iphoneos): \ suffix = _iphonesimulator else: \ suffix = CONFIG(debug, debug|release) { !debug_and_release|build_pass { mac: return($${suffix}_debug) win32: return($${suffix}d) } } return($$suffix) } #Acts like qtLibraryTarget. From qtcreator.pri defineReplace(qtLibName) { #TEMPLATE += fakelib #LIB_FULLNAME = $$qtLibraryTarget($$1) #TEMPLATE -= fakelib unset(RET) RET = $$1 #qt5.4.2 add qt5LibraryTarget to fix qtLibraryTarget break qtAtLeast(5, 4) { mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { QMAKE_FRAMEWORK_BUNDLE_NAME = $$RET export(QMAKE_FRAMEWORK_BUNDLE_NAME) } else { # insert the major version of Qt in the library name # unless it's a framework build isEqual(QT_MAJOR_VERSION, 5):isEqual(QT_MINOR_VERSION,4):lessThan(QT_PATCH_VERSION, 2):RET ~= s,^Qt,Qt$$QT_MAJOR_VERSION, } } RET = $$RET$$platformTargetSuffix() !win32: return($$RET) isEmpty(2): VERSION_EXT = $$VERSION else: VERSION_EXT = $$2 !isEmpty(VERSION_EXT) { VERSION_EXT = $$section(VERSION_EXT, ., 0, 0) #isEqual(VERSION_EXT, 0):unset(VERSION_EXT) } RET = $${RET}$${VERSION_EXT} unset(VERSION_EXT) return($$RET) } #fakelib defineReplace(qtStaticLib) { # static lib does not have major version flag at the end unset(LIB_FULLNAME) TEMPLATE += fakelib LIB_FULLNAME = $$qtLibraryTarget($$1) TEMPLATE -= fakelib LIB_FULLNAME = $${QMAKE_PREFIX_STATICLIB}$$member(LIB_FULLNAME, 0).$${QMAKE_EXTENSION_STATICLIB} return($$LIB_FULLNAME) } defineReplace(qtSharedLib) { unset(LIB_FULLNAME) LIB_FULLNAME = $$qtLibName($$1, $$2) LIB_FULLNAME = $${QMAKE_PREFIX_SHLIB}$$member(LIB_FULLNAME, 0).$${QMAKE_EXTENSION_SHLIB} #default_post.prf return($$LIB_FULLNAME) } defineReplace(qtLongName) { unset(LONG_NAME) LONG_NAME = $$1$${_OS}_$$join(TARGET_ARCH,+)$${_EXTRA} return($$LONG_NAME) } defineTest(empty_file) { isEmpty(1): error("empty_file(name) requires one argument") #"type NUL >filename" can create an empty file in windows, see http://stackoverflow.com/questions/210201/how-to-create-empty-text-file-from-a-batch-file # 'echo. >file' or 'echo >file' will insert a new line, so use stderr win32:isEmpty(QMAKE_SH) { system("echo. 2> $$1")|return(false) } else { #if sh is after win's echo, then "echo >$$1" fails because win's echo is used system("sh -c echo 2> $$1")|return(false) } } config_simd { #TODO: QMAKE_CFLAGS_XXX, QT_CPU_FEATURES *g++*|*qcc*: QMAKE_CFLAGS_NEON = -mfpu=neon win32-icc { QMAKE_CFLAGS_SSE2 = -arch:SSE2 QMAKE_CFLAGS_SSE4_1 = -arch:SSE4.1 } else:*-icc { #mac, linux QMAKE_CFLAGS_SSE2 = -xSSE2 QMAKE_CFLAGS_SSE4_1 = -xSSE4.1 } else:*msvc* { # all x64 processors supports sse2. unknown option for vc #!isEqual(QT_ARCH, x86_64)|!x86_64 { QMAKE_CFLAGS_SSE2 = -arch:SSE2 QMAKE_CFLAGS_SSE4_1 = -arch:SSE2 #} } else { QMAKE_CFLAGS_SSE2 = -msse2 QMAKE_CFLAGS_SSE4_1 = -msse4.1 } #mac: simd will load qt_build_config and the result is soname will prefixed with QT_INSTALL_LIBS and link flag will append soname after QMAKE_LFLAGS_SONAME defineTest(addSimdCompiler) { #from qt5 simd.prf name = $$1 upname = $$upper($$name) headers_var = $${upname}_HEADERS sources_var = $${upname}_SOURCES asm_var = $${upname}_ASM # config_$$1 is defined by config.tests (tests/arch) CONFIG($$1)|CONFIG(config_$$1) { cflags = $$eval(QMAKE_CFLAGS_$${upname}) contains(QT_CPU_FEATURES, $$name) { # Default compiler settings include this feature, so just add to SOURCES SOURCES += $$eval($$sources_var) export(SOURCES) } else { #qt4 always need eval() if var is not const # We need special compiler flags eval($${name}_compiler.commands = $$QMAKE_CXX -c $(CXXFLAGS) $$cflags $(INCPATH) ${QMAKE_FILE_IN}) msvc: eval($${name}_compiler.commands += -Fo${QMAKE_FILE_OUT}) else: eval($${name}_compiler.commands += -o ${QMAKE_FILE_OUT}) eval($${name}_compiler.dependency_type = TYPE_C) eval($${name}_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}) eval($${name}_compiler.input = $$sources_var) eval($${name}_compiler.variable_out = OBJECTS) eval($${name}_compiler.name = compiling[$${name}] ${QMAKE_FILE_IN}) silent: eval($${name}_compiler.commands = @echo compiling[$${name}] ${QMAKE_FILE_IN} && $$eval($${name}_compiler.commands)) QMAKE_EXTRA_COMPILERS += $${name}_compiler export($${name}_compiler.commands) export($${name}_compiler.dependency_type) export($${name}_compiler.output) export($${name}_compiler.input) export($${name}_compiler.variable_out) export($${name}_compiler.name) } # We always need an assembler (need to run the C compiler and without precompiled headers) msvc { # Don't know how to run MSVC's assembler... !isEmpty($$asm_var): error("Sorry, not implemented: assembling $$upname for MSVC.") } else: false { # This is just for the IDE SOURCES += $$eval($$asm_var) export(SOURCES) } else { eval($${name}_assembler.commands = $$QMAKE_CC -c $(CFLAGS)) !contains(QT_CPU_FEATURES, $${name}): eval($${name}_assembler.commands += $$cflags) clang:no_clang_integrated_as: eval($${name}_assembler.commands += -fno-integrated-as) eval($${name}_assembler.commands += $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}) eval($${name}_assembler.dependency_type = TYPE_C) eval($${name}_assembler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}) eval($${name}_assembler.input = $$asm_var) eval($${name}_assembler.variable_out = OBJECTS) eval($${name}_assembler.name = assembling[$${name}] ${QMAKE_FILE_IN}) silent: eval($${name}_assembler.commands = @echo assembling[$${name}] ${QMAKE_FILE_IN} && $$eval($${name}_assembler.commands)) QMAKE_EXTRA_COMPILERS += $${name}_assembler export($${name}_assembler.commands) export($${name}_assembler.dependency_type) export($${name}_assembler.output) export($${name}_assembler.input) export($${name}_assembler.variable_out) export($${name}_assembler.name) } HEADERS += $$eval($$headers_var) export(HEADERS) export(QMAKE_EXTRA_COMPILERS) } } addSimdCompiler(sse2) addSimdCompiler(sse3) addSimdCompiler(ssse3) addSimdCompiler(sse4_1) addSimdCompiler(sse4_2) addSimdCompiler(avx) addSimdCompiler(avx2) addSimdCompiler(neon) addSimdCompiler(mips_dsp) addSimdCompiler(mips_dspr2) } #config_simd ##TODO: add defineReplace(getValue): parameter is varname lessThan(QT_MAJOR_VERSION, 5) { defineTest(log){ system(echo $$system_quote($$1)) } defineTest(mkpath) { qtRunQuitly("$$QMAKE_MKDIR $$system_path($$1)")|return(false) return(true) } defineTest(write_file) { #log("write_file($$1, $$2, $$3)") !isEmpty(4): error("write_file(name, [content var, [append]]) requires one to three arguments.") ##TODO: 1.how to replace old value ##getting the ref value requires a function whose parameter has var name and return a string. join() is the only function ## var name is $$2. ## echo a string with "\n" will fail, so we can not use join #val = $$join($$2, $$escape_expand(\\n))$$escape_expand(\\n) isEmpty(3)|!isEqual(3, append) { #system("$$QMAKE_DEL_FILE $$1") #for win commad "del", path format used in qmake such as D:/myfile is not supported, "/" will be treated as an otpion for "del" empty_file($$1) } for(val, $$2) { system("echo $$system_quote($$val) >> \"$$1\"")|return(false) } return(true) } #defineTest(cache) { # !isEmpty(4): error("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments.") #} defineReplace(clean_path) { win32:1 ~= s|\\\\|/|g contains(1, ^/.*):pfx = / else:pfx = segs = $$split(1, /) out = for(seg, segs) { equals(seg, ..):out = $$member(out, 0, -2) else:!equals(seg, .):out += $$seg } return($$join(out, /, $$pfx)) } #make sure BUILD_DIR and SOURCE_ROOT is already defined. otherwise return the input path #only operate on string, seperator is always "/" defineReplace(shadowed) { isEmpty(SOURCE_ROOT)|isEmpty(BUILD_DIR):return($$1) 1 ~= s,$$SOURCE_ROOT,,g shadow_dir = $$BUILD_DIR/$$1 shadow_dir ~= s,//,/,g return($$shadow_dir) } defineReplace(shell_path) { # QMAKE_DIR_SEP: \ for win cmd and / for sh 1 ~= s,\\\\,$$QMAKE_DIR_SEP,g 1 ~= s,//,$$QMAKE_DIR_SEP,g return($$1) } defineReplace(shell_quote_win) { # Chars that should be quoted (TM). # - control chars & space # - the windows shell meta chars "&()<>^| # - the potential separators ,;= #TODO: how to deal with "^", "|"? every char are separated by "|"? #how to avoid replacing "^" again for the second time isEmpty(1):error("shell_quote(arg) requires one argument.") special_chars = & \( \) < > for(c, special_chars) { 1 ~= s,$$c,^$$c,g } #for qmake \\ #1 ~= s,\\),^\),g #1 ~= s,\\(,^\(,g return($$1) } defineReplace(shell_quote_unix) { # - unix shell: 0-32 \'"$`<>|;&(){}*?#!~[] #TODO: how to deal with "#" "|" and "^"? #how to avoid replacing "^" again for the second time # \$ is eol special_chars = & \( \) < > \\ \' \" ` ; \{ \} * ? ! ~ \[ \] for(c, special_chars) { 1 ~= s,$$c,\\$$c,g } return($$1) } ##TODO: see qmake/library/ioutils.cpp defineReplace(shell_quote) { contains(QMAKE_HOST.os,Windows):isEmpty(QMAKE_SH):return($$shell_quote_win($$1)) return($$shell_quote_unix($$1)) } ##TODO: see qmake/library/ioutils.cpp defineReplace(system_quote) { isEmpty(1):error("system_quote(arg) requires one argument.") contains(QMAKE_HOST.os,Windows): return($$shell_quote_win($$1)) return($$shell_quote_unix($$1)) } defineReplace(system_path) { contains(QMAKE_HOST.os,Windows) { 1 ~= s,/,\\,g #qmake \\=>put \\=>real \? } else { 1 ~= s,\\\\,/,g ##why is \\\\. real \=>we read \\=>qmake \\\\? } return($$1) } } #lessThan(QT_MAJOR_VERSION, 5) # set default rpath and add user defined rpaths defineTest(set_rpath) { !unix: return(true) RPATHDIR = $$ARGS #see doc LIBS += -L/usr/local/lib # $$[QT_INSTALL_LIBS] and $$DESTDIR and pro dir will be auto added to QMAKE_RPATHDIR if QMAKE_RPATHDIR is not empty # Current (sub)project dir is auto added to the first value as prefix. e.g. QMAKE_RPATHDIR = .. ==> -Wl,-rpath,ROOT/.. # Executable dir search: ld -z origin, g++ -Wl,-R,'$ORIGIN', in makefile -Wl,-R,'$$ORIGIN' # Working dir search: "." # mac: install_name @rpath will search paths set in rpath link flags # QMAKE_RPATHDIR: lflags maybe wrong, paths are modified #!cross_compile: RPATHDIR *= $$PROJECT_LIBDIR macx|ios { RPATHDIR *= @loader_path/../Frameworks @executable_path/../Frameworks QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ } else { RPATHDIR *= \$\$ORIGIN \$\$ORIGIN/lib . /usr/local/lib $$[QT_INSTALL_LIBS] # $$PROJECT_LIBDIR only for host == target. But QMAKE_TARGET.arch is only available on windows. QT_ARCH is bad, e.g. QT_ARCH=i386 while QMAKE_HOST.arch=i686 # https://bugreports.qt-project.org/browse/QTBUG-30263 QMAKE_LFLAGS *= -Wl,-z,origin #\'-Wl,-rpath,$$join(RPATHDIR, ":")\' } for(R,RPATHDIR) { QMAKE_LFLAGS *= \'$${QMAKE_LFLAGS_RPATH}$$R\' } # QMAKE_RPATHDIR *= $$RPATHDIR #export(QMAKE_RPATHDIR) export(QMAKE_LFLAGS_SONAME) export(QMAKE_LFLAGS) return(true) } #argument 1 is default dir if not defined defineTest(getBuildRoot) { !isEmpty(2): unset(BUILD_DIR) isEmpty(BUILD_DIR) { BUILD_DIR=$$(BUILD_DIR) isEmpty(BUILD_DIR) { #build_cache = $$PROJECTROOT/.build.cache #use root project's cache for subdir projects #!exists($$build_cache):build_cache = $$PWD/.build.cache #common.pri is in the root dir of a sub project #exists($$build_cache):include($$build_cache) isEmpty(BUILD_DIR) { BUILD_DIR=$$[BUILD_DIR] isEmpty(BUILD_DIR) { !isEmpty(1) { BUILD_DIR=$$1 } else { BUILD_DIR = $$OUT_PWD warning(BUILD_DIR not specified, using $$BUILD_DIR) } } } } } export(BUILD_DIR) #message(BUILD_DIR=$$BUILD_DIR) return(true) } ##############################paths#################################### #for Qt2, Qt3 which does not have QT_VERSION. Qt4: $$[QT_VERSION] defineTest(preparePaths) { getBuildRoot($$1, $$2) MOC_DIR = $$BUILD_DIR/.moc/$${QT_VERSION}/$$TARGET RCC_DIR = $$BUILD_DIR/.rcc/$${QT_VERSION}/$$TARGET UI_DIR = $$BUILD_DIR/.ui/$${QT_VERSION}/$$TARGET #obj is platform dependent OBJECTS_DIR = $$qtLongName($$BUILD_DIR/.obj/$$TARGET) #before target name changed isEqual(TEMPLATE, app) { DESTDIR = $$BUILD_DIR/bin # TARGET = $$qtLongName($$TARGET) EXE_EXT = win32: EXE_EXT = .exe CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP):!mac_framework: QMAKE_POST_LINK = -$$QMAKE_STRIP $$DESTDIR/$${TARGET}$${EXE_EXT} #.exe in win } else: DESTDIR = $$qtLongName($$BUILD_DIR/lib) !build_pass { message(target: $$DESTDIR/$$TARGET) !isEmpty(PROJECTROOT) { TRANSLATIONS *= i18n/$${TARGET}_zh_CN.ts export(TRANSLATIONS) } } #export vars outside this function export(MOC_DIR) export(RCC_DIR) export(UI_DIR) export(OBJECTS_DIR) export(DESTDIR) #export(TARGET) return(true) } COMMON_PRI_INCLUDED = 1 } #end COMMON_PRI_INCLUDED QtAV-1.12.0/config.tests/000077500000000000000000000000001312235004300150055ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avcodec/000077500000000000000000000000001312235004300164115ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avcodec/avcodec.pro000066400000000000000000000004251312235004300205400ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavcodec include(../paths.pri) QtAV-1.12.0/config.tests/avcodec/main.cpp000066400000000000000000000021201312235004300200340ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/avdevice/000077500000000000000000000000001312235004300165735ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avdevice/avdevice.pro000066400000000000000000000004261312235004300211050ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavdevice include(../paths.pri) QtAV-1.12.0/config.tests/avdevice/main.cpp000066400000000000000000000021221312235004300202200ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/avfilter/000077500000000000000000000000001312235004300166215ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avfilter/avfilter.pro000066400000000000000000000004261312235004300211610ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavfilter include(../paths.pri) QtAV-1.12.0/config.tests/avfilter/main.cpp000066400000000000000000000021561312235004300202550ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2014 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { // avfilter_register_all(); return 0; } QtAV-1.12.0/config.tests/avformat/000077500000000000000000000000001312235004300166245ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avformat/avformat.pro000066400000000000000000000004261312235004300211670ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavformat include(../paths.pri) QtAV-1.12.0/config.tests/avformat/main.cpp000066400000000000000000000021221312235004300202510ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/avresample/000077500000000000000000000000001312235004300171445ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avresample/avresample.pro000066400000000000000000000004301312235004300220220ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavresample include(../paths.pri) QtAV-1.12.0/config.tests/avresample/main.cpp000066400000000000000000000021261312235004300205750ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/avutil/000077500000000000000000000000001312235004300163115ustar00rootroot00000000000000QtAV-1.12.0/config.tests/avutil/avutil.pro000066400000000000000000000004241312235004300203370ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lavutil include(../paths.pri) QtAV-1.12.0/config.tests/avutil/main.cpp000066400000000000000000000021161312235004300177410ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/d3d11va/000077500000000000000000000000001312235004300161505ustar00rootroot00000000000000QtAV-1.12.0/config.tests/d3d11va/d3d11va.pro000066400000000000000000000001531312235004300200340ustar00rootroot00000000000000CONFIG -= qt CONFIG += console CONFIG += c++11 SOURCES += main.cpp include(../paths.pri) LIBS += -lavcodec QtAV-1.12.0/config.tests/d3d11va/main.cpp000066400000000000000000000024441312235004300176040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../dxcompat.h" #include extern "C" { #include } #include //ComPtr is used in QtAV int main() { av_d3d11va_alloc_context(); D3D11_VIDEO_PROCESSOR_STREAM s; //used by vp return 0; } QtAV-1.12.0/config.tests/direct2d/000077500000000000000000000000001312235004300165055ustar00rootroot00000000000000QtAV-1.12.0/config.tests/direct2d/direct2d.pro000066400000000000000000000001221312235004300207220ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp #dynamic load #LIBS += -ld2d1 QtAV-1.12.0/config.tests/direct2d/main.cpp000066400000000000000000000021561312235004300201410ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../dxcompat.h" #include int main() { return 0; } QtAV-1.12.0/config.tests/dsound/000077500000000000000000000000001312235004300163015ustar00rootroot00000000000000QtAV-1.12.0/config.tests/dsound/dsound.pro000066400000000000000000000001471312235004300203210ustar00rootroot00000000000000CONFIG -= qt app_bundle CONFIG += console SOURCES += main.cpp LIBS += -ldsound include(../paths.pri) QtAV-1.12.0/config.tests/dsound/main.cpp000066400000000000000000000021041312235004300177260ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/dx/000077500000000000000000000000001312235004300154205ustar00rootroot00000000000000QtAV-1.12.0/config.tests/dx/dx.pro000066400000000000000000000001711312235004300165540ustar00rootroot00000000000000CONFIG -= qt app_bundle CONFIG += console CONFIG *= c++11 CONFIG += config_dx SOURCES += main.cpp include(../paths.pri) QtAV-1.12.0/config.tests/dx/main.cpp000066400000000000000000000023761312235004300170600ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../dxcompat.h" #include // standalone check? #include #include //ComPtr is used in QtAV int main() { D3D11_VIDEO_PROCESSOR_STREAM s; //used by vp return 0; } QtAV-1.12.0/config.tests/dxcompat.h000066400000000000000000000002251312235004300167740ustar00rootroot00000000000000#include "../src/directx/dxcompat.h" //#include //to include _mingw.h // check __MINGW64_VERSION_MAJOR? 5.x has complete headers we need QtAV-1.12.0/config.tests/dxva/000077500000000000000000000000001312235004300157475ustar00rootroot00000000000000QtAV-1.12.0/config.tests/dxva/dxva.pro000066400000000000000000000001121312235004300174250ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp include(../paths.pri) QtAV-1.12.0/config.tests/dxva/main.cpp000066400000000000000000000023231312235004300173770ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../dxcompat.h" extern "C" { #include //will include d3d9.h, dxva2api.h } #include #include int main() { return 0; } QtAV-1.12.0/config.tests/gdiplus/000077500000000000000000000000001312235004300164545ustar00rootroot00000000000000QtAV-1.12.0/config.tests/gdiplus/gdiplus.pro000066400000000000000000000001171312235004300206440ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp LIBS += -lgdiplus -lGdi32 QtAV-1.12.0/config.tests/gdiplus/main.cpp000066400000000000000000000021371312235004300201070ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include //vc #include int main() { return 0; } QtAV-1.12.0/config.tests/gentest.sh000077500000000000000000000011631312235004300170160ustar00rootroot00000000000000SCRIPT_DIR=${0%/*} help_post(){ echo "This will create a test for $1. You may change the default value: \"#include <$1.h>\" in $1/main.cpp and \"LIBS += -l$1\" in $1/$1.pro" } help(){ cecho red "Usage: $0 name" help_post name exit 0 } [ $# -lt 1 ] && help NAME=$1 help_post $NAME mkdir -p $NAME cat >$NAME/${NAME}.pro < $NAME/main.cpp cat >> $NAME/main.cpp < void test() { } EOF echo "test for $NAME created" QtAV-1.12.0/config.tests/gl/000077500000000000000000000000001312235004300154075ustar00rootroot00000000000000QtAV-1.12.0/config.tests/gl/gl.pro000066400000000000000000000001111312235004300165240ustar00rootroot00000000000000CONFIG += qt release QT += opengl CONFIG += console SOURCES += main.cpp QtAV-1.12.0/config.tests/gl/main.cpp000066400000000000000000000021151312235004300170360ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/libass/000077500000000000000000000000001312235004300162625ustar00rootroot00000000000000QtAV-1.12.0/config.tests/libass/libass.pro000066400000000000000000000002021312235004300202530ustar00rootroot00000000000000include(../paths.pri) SOURCES += main.cpp exists(../../contrib/capi/capi.pri) { CONFIG = staticlib } else { LIBS += -lass } QtAV-1.12.0/config.tests/libass/main.cpp000066400000000000000000000021421312235004300177110ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2014 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ extern "C" { #include } void test() { ass_library_init(); } QtAV-1.12.0/config.tests/libcedarv/000077500000000000000000000000001312235004300167405ustar00rootroot00000000000000QtAV-1.12.0/config.tests/libcedarv/libcedardrv.pro000066400000000000000000000001051312235004300217370ustar00rootroot00000000000000SOURCES += main.cpp LIBS += -lvecore -lcedarv include(../paths.pri) QtAV-1.12.0/config.tests/libcedarv/main.cpp000066400000000000000000000021211312235004300203640ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/openal/000077500000000000000000000000001312235004300162635ustar00rootroot00000000000000QtAV-1.12.0/config.tests/openal/main.cpp000066400000000000000000000023541312235004300177170ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #if defined(HEADER_OPENAL_PREFIX) #include #include #else #include #include #endif int main() { alcGetCurrentContext(); alGetError(); return 0; } QtAV-1.12.0/config.tests/openal/openal.pro000066400000000000000000000004361312235004300202660ustar00rootroot00000000000000include(../paths.pri) SOURCES += main.cpp mac: DEFINES += HEADER_OPENAL_PREFIX exists(../../contrib/capi/capi.pri) { CONFIG = staticlib } else { win32: LIBS += -lOpenAL32 unix:!mac:!blackberry: LIBS += -lopenal blackberry: LIBS += -lOpenAL mac: LIBS += -framework OpenAL } QtAV-1.12.0/config.tests/paths.pri000066400000000000000000000010251312235004300166360ustar00rootroot00000000000000TEMPLATE = lib # can not create exe for some platforms (winrt, ios). If check header only, staticlib is fine # not static lib because sometimes we need to check link flags. if qt is static build, this chek may fail. we don't test link for static build because it's impossible to add all dependencies to link flags INCLUDEPATH += $$[QT_INSTALL_HEADERS] LIBS += -L$$[QT_INSTALL_LIBS] CONFIG -= qt app_bundle lib_bundle CONFIG += console *msvc*: INCLUDEPATH *= $$PWD/../src/compat/msvc !config_dx: INCLUDEPATH *= $$PWD/../contrib/dxsdk QtAV-1.12.0/config.tests/portaudio/000077500000000000000000000000001312235004300170135ustar00rootroot00000000000000QtAV-1.12.0/config.tests/portaudio/main.cpp000066400000000000000000000000611312235004300204400ustar00rootroot00000000000000#include int main() { return 0; }QtAV-1.12.0/config.tests/portaudio/portaudio.pro000066400000000000000000000003651312235004300215470ustar00rootroot00000000000000CONFIG -= qt CONFIG += console *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lportaudio include(../paths.pri) QtAV-1.12.0/config.tests/pulseaudio/000077500000000000000000000000001312235004300171575ustar00rootroot00000000000000QtAV-1.12.0/config.tests/pulseaudio/main.cpp000066400000000000000000000000741312235004300206100ustar00rootroot00000000000000#include int main() { return 0; } QtAV-1.12.0/config.tests/pulseaudio/pulseaudio.pro000066400000000000000000000001051312235004300220470ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp LIBS += -lpulse QtAV-1.12.0/config.tests/sse4_1/000077500000000000000000000000001312235004300161035ustar00rootroot00000000000000QtAV-1.12.0/config.tests/sse4_1/sse4_1.cpp000066400000000000000000000042051312235004300177060ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the config.tests of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include int main(int, char**) { __m128 a = _mm_setzero_ps(); _mm_ceil_ps(a); __m128i result = _mm_mullo_epi32(_mm_set1_epi32(42), _mm_set1_epi32(64)); (void)result; return 0; } QtAV-1.12.0/config.tests/sse4_1/sse4_1.pro000066400000000000000000000023141312235004300177230ustar00rootroot00000000000000SOURCES = sse4_1.cpp #SSE4_1_SOURCES = sse4_1.cpp CONFIG -= qt dylib release debug_and_release CONFIG += debug console sse4_1 # for Qt4. we can only detect sse2 in Qt4 #qt5 only has gcc, qcc, vc, linux icc. win32-icc { QMAKE_CFLAGS_SSE4_1 = -arch:SSE4.1 } else:*-icc { #mac, linux QMAKE_CFLAGS_SSE4_1 = -xSSE4.1 } else:*msvc* { QMAKE_CFLAGS_SSE4_1 = -arch:SSE2 } else { QMAKE_CFLAGS_SSE4_1 = -msse4.1 } sse4_1 { HEADERS += $$SSE4_1_HEADERS sse4_1_compiler.commands = $$QMAKE_CXX -c $(CXXFLAGS) !contains(QT_CPU_FEATURES, sse4_1):sse4_1_compiler.commands += $$QMAKE_CFLAGS_SSE4_1 sse4_1_compiler.commands += $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} sse4_1_compiler.dependency_type = TYPE_C sse4_1_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} sse4_1_compiler.input = SSE4_1_SOURCES sse4_1_compiler.variable_out = OBJECTS sse4_1_compiler.name = compiling[sse4_1] ${QMAKE_FILE_IN} silent:sse4_1_compiler.commands = @echo compiling[sse4_1] ${QMAKE_FILE_IN} && $$sse4_1_compiler.commands QMAKE_EXTRA_COMPILERS += sse4_1_compiler } isEmpty(QMAKE_CFLAGS_SSE4_1):error("This compiler does not support SSE4.1") else:QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_SSE4_1 QtAV-1.12.0/config.tests/swresample/000077500000000000000000000000001312235004300171675ustar00rootroot00000000000000QtAV-1.12.0/config.tests/swresample/main.cpp000066400000000000000000000021261312235004300206200ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/swresample/swresample.pro000066400000000000000000000004301312235004300220700ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lswresample include(../paths.pri) QtAV-1.12.0/config.tests/swscale/000077500000000000000000000000001312235004300164465ustar00rootroot00000000000000QtAV-1.12.0/config.tests/swscale/main.cpp000066400000000000000000000021201312235004300200710ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include int main() { return 0; } QtAV-1.12.0/config.tests/swscale/swscale.pro000066400000000000000000000004251312235004300206320ustar00rootroot00000000000000CONFIG -= qt CONFIG += console DEFINES += __STDC_CONSTANT_MACROS *msvc* { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO QMAKE_LFLAGS += /SAFESEH:NO INCLUDEPATH += ../../src/compat/msvc } SOURCES += main.cpp LIBS += -lswscale include(../paths.pri) QtAV-1.12.0/config.tests/uchardet/000077500000000000000000000000001312235004300166045ustar00rootroot00000000000000QtAV-1.12.0/config.tests/uchardet/main.cpp000066400000000000000000000021271312235004300202360ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include void test() { uchardet_new(); } QtAV-1.12.0/config.tests/uchardet/uchardet.pro000066400000000000000000000001251312235004300211230ustar00rootroot00000000000000include(../paths.pri) TARGET = uchardet_test SOURCES += main.cpp LIBS += -luchardet QtAV-1.12.0/config.tests/vaapi/000077500000000000000000000000001312235004300161055ustar00rootroot00000000000000QtAV-1.12.0/config.tests/vaapi/main.cpp000066400000000000000000000021741312235004300175410ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013-2014 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include int main() { vaErrorStr(0); return 0; } QtAV-1.12.0/config.tests/vaapi/vaapi.pro000066400000000000000000000001301312235004300177210ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp LIBS += -lva include(../paths.pri) QtAV-1.12.0/config.tests/videotoolbox/000077500000000000000000000000001312235004300175225ustar00rootroot00000000000000QtAV-1.12.0/config.tests/videotoolbox/main.cpp000066400000000000000000000022201312235004300211460ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ extern "C" { #include } int main() { av_videotoolbox_alloc_context(); return 0; } QtAV-1.12.0/config.tests/videotoolbox/videotoolbox.pro000066400000000000000000000003041312235004300227560ustar00rootroot00000000000000DEFINES += __STDC_CONSTANT_MACROS SOURCES += main.cpp LIBS += -lavcodec -lavutil -framework CoreVideo -framework CoreFoundation -framework CoreMedia -framework VideoToolbox include(../paths.pri) QtAV-1.12.0/config.tests/x11/000077500000000000000000000000001312235004300154165ustar00rootroot00000000000000QtAV-1.12.0/config.tests/x11/main.cpp000066400000000000000000000021621312235004300170470ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include void test() { XPutImage(NULL, 0, NULL, NULL, 0, 0, 0, 0, 0, 0); } QtAV-1.12.0/config.tests/x11/x11.pro000066400000000000000000000001131312235004300165440ustar00rootroot00000000000000include(../paths.pri) TARGET = x11_test SOURCES += main.cpp LIBS += -lX11 QtAV-1.12.0/config.tests/xaudio2/000077500000000000000000000000001312235004300163605ustar00rootroot00000000000000QtAV-1.12.0/config.tests/xaudio2/main.cpp000066400000000000000000000023711312235004300200130ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../dxcompat.h" #ifdef __GNUC__ // macros used by XAudio 2.7 (June 2010 SDK) #ifndef __in #define __in #endif #ifndef __out #define __out #endif #endif #include int main() { return 0; } QtAV-1.12.0/config.tests/xaudio2/xaudio2.pro000066400000000000000000000001261312235004300204540ustar00rootroot00000000000000CONFIG -= qt app_bundle CONFIG += console SOURCES += main.cpp include(../paths.pri) QtAV-1.12.0/config.tests/xv/000077500000000000000000000000001312235004300154425ustar00rootroot00000000000000QtAV-1.12.0/config.tests/xv/main.cpp000066400000000000000000000021751312235004300170770ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include int main() { return 0; } QtAV-1.12.0/config.tests/xv/xv.pro000066400000000000000000000001021312235004300166120ustar00rootroot00000000000000CONFIG -= qt CONFIG += console SOURCES += main.cpp LIBS += -lXv QtAV-1.12.0/configure.pri000066400000000000000000000135061312235004300151010ustar00rootroot00000000000000#Designed by Wang Bin(Lucas Wang). 2013 ### ONLY FOR Qt4. common.pri must be included before it so that write_file() can be used####### ### .qmake.cache MUST be created before it! ####ASSUME compile tests and .qmake.cache is in project out root dir # Ensure that a cache is present. If none was found on startup, this will create # one in the build directory of the project which loads this feature. #cache() QMAKE_CONFIG_LOG = $$dirname(_QMAKE_CACHE_QT4_)/config.log QMAKE_CONFIG_TESTS_DIR = $$_PRO_FILE_PWD_/config.tests lessThan(QT_MAJOR_VERSION, 5) { defineTest(cache) { !isEmpty(4): error("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments.") !exists($$_QMAKE_CACHE_QT4_) { log("Info: creating cache file $$_QMAKE_CACHE_QT4_") write_file($$_QMAKE_CACHE_QT4_)|return(false) } isEmpty(1):return(true) !isEmpty(2):isEqual(2, set):mode_set=1 isEmpty(3) { isEmpty(mode_set):error("cache(): modes other than 'set' require a source variable.") srcvar = $$1 srcval = $$join($$srcvar, $$escape_expand(\\t)) ##TODO: how to use white space? } else { srcvar = $$3 dstvar = $$1 #We need the ref value, so the function's parameter must be a var name. join() is! srcval = $$join($$srcvar, $$escape_expand(\\t)) ##TODO: how to use white space? isEqual(2, add) { varstr = "$$1 += $$srcval" ##TODO: how to use white space? } else:isEqual(2, sub) { varstr = "$$1 -= $$srcval" } else:isEqual(2, set) { ##TODO: erase existing VAR in cache varstr = "$$1 = $$srcval" } else { error("cache(): invalid flag $$2.") } } #log("varstr: $$varstr") ##TODO: remove existing lines contain $$srcvar #because write_file() will write 1 line for each value(separated by space), so the value must be closed with "", then it's 1 value, not list #erase the existing var and value pair win32 {#:isEmpty(QMAKE_SH) { #windows sucks. can not access the cache } else { #use sed for unix or msys #convert '/' to '\/' for sed srcval ~= s,/,\\/,g srcval ~= s,\\+,\\\\+,g #for sed regexp. '+' in qmake is '\+' ? system("sed -i -r '/.*$${dstvar}.*$${srcval}.*/d' $$_QMAKE_CACHE_QT4_ >/dev/null") } write_file($$_QMAKE_CACHE_QT4_, varstr, append) } } #Qt4 QMAKE_MAKE = $$(MAKE) !isEmpty(QMAKE_MAKE) { # We were called recursively. Use the right make, as MAKEFLAGS may be set as well. } equals(MAKEFILE_GENERATOR, UNIX) { QMAKE_MAKE = make } else:equals(MAKEFILE_GENERATOR, MINGW) { !equals(QMAKE_HOST.os, Windows): \ QMAKE_MAKE = make else: \ QMAKE_MAKE = mingw32-make } else:if(equals(MAKEFILE_GENERATOR, MSVC.NET)|equals(MAKEFILE_GENERATOR, MSBUILD)) { QMAKE_MAKE = nmake } else { # error: the reset qmake will not work and displays nothing in qtc warning("Configure tests are not supported with the $$MAKEFILE_GENERATOR Makefile generator.") } defineTest(qtRunLoggedCommand) { msg = "+ $$1" write_file($$QMAKE_CONFIG_LOG, msg, append)#|error("write file failed") #for debug system("$$1 >> \"$$QMAKE_CONFIG_LOG\" 2>&1")|return(false) return(true) } # Try to build the test project in $$QMAKE_CONFIG_TESTS_DIR/$$1 # ($$_PRO_FILE_PWD_/config.tests/$$1 by default). # # If the test passes, config_$$1 will be added to CONFIG. # The result is automatically cached. Use of cached results # can be suppressed by passing CONFIG+=recheck to qmake. # # Returns: true iff the test passes defineTest(qtCompileTest) { positive = config_$$1 done = done_config_$$1 $$done:!recheck { $$positive:return(true) return(false) } log("Checking for $${1}... ") msg = "executing config test $$1" write_file($$QMAKE_CONFIG_LOG, msg, append) test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$1 test_out_dir = $$shadowed($$test_dir) #system always call win32 cmd in windows, so we need "cd /d" to ensure success cd to a different partition contains(QMAKE_HOST.os,Windows):test_cmd_base = "cd /d $$system_quote($$system_path($$test_out_dir)) &&" else: test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&" # On WinRT we need to change the entry point as we cannot create windows applications winrt { qmake_configs += " \"QMAKE_LFLAGS+=/ENTRY:main\"" } # Disable qmake features which are typically counterproductive for tests qmake_configs = "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\"" iphoneos: qmake_configs += "\"CONFIG+=iphoneos\"" iphonesimulator: qmake_configs += "\"CONFIG+=iphonesimulator\"" # Clean up after previous run exists($$test_out_dir/Makefile):qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE distclean") mkpath($$test_out_dir)#|error("Aborting.") #mkpath currently return false, do not know why SPEC = !isEmpty(QMAKESPEC): SPEC = "-spec $$QMAKESPEC" qtRunLoggedCommand("$$test_cmd_base $$system_quote($$system_path($$QMAKE_QMAKE)) $$SPEC $$qmake_configs $$system_path($$test_dir)") { qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE") { log("yes$$escape_expand(\\n)") msg = "test $$1 succeeded" write_file($$QMAKE_CONFIG_LOG, msg, append) !$$positive { CONFIG += $$positive cache(CONFIG, add, positive) } !$$done { CONFIG += $$done cache(CONFIG, add, done) } export(CONFIG) return(true) } } log("no$$escape_expand(\\n)") msg = "test $$1 FAILED" write_file($$QMAKE_CONFIG_LOG, msg, append) $$positive { CONFIG -= $$positive cache(CONFIG, sub, positive) } !$$done { CONFIG += $$done cache(CONFIG, add, done) } export(CONFIG) return(false) } QtAV-1.12.0/deploy.pri000066400000000000000000000043431312235004300144130ustar00rootroot00000000000000isEmpty(PROJECTROOT): PROJECTROOT = $$PWD INSTALL_PREFIX = /usr/local share.files = $$PROJECTROOT/qtc_packaging/common/changelog \ $$PROJECTROOT/qtc_packaging/common/copyright \ $$PROJECTROOT/qtc_packaging/common/README share.path = /usr/share/doc/$${TARGET} isEqual(TEMPLATE, app) { unix:!symbian { !isEmpty(MEEGO_VERSION_MAJOR) { DEFINES += CACHE_APPDIR INSTALL_PREFIX = /opt/$${TARGET} desktopfile.files = $$PROJECTROOT/qtc_packaging/debian_harmattan/$${TARGET}.desktop desktopfile.path = /usr/share/applications icon.files = $$PROJECTROOT/qtc_packaging/debian_harmattan/$${TARGET}.png icon.path = /usr/share/icons/hicolor/80x80/apps #debian.files = $$PROJECTROOT/qtc_packaging/harmattan/control } else:maemo5 { INSTALL_PREFIX = /opt/$${TARGET} desktopfile.files = $$PROJECTROOT/qtc_packaging/debian_fremantle/$${TARGET}.desktop desktopfile.path = /usr/share/applications/hildon icon.files = $$PROJECTROOT/qtc_packaging/debian_fremantle/$${TARGET}.png icon.path = /usr/share/icons/hicolor/64x64/apps #debian.files = $$PROJECTROOT/qtc_packaging/fremantle/control } else { desktopfile.files = $$PROJECTROOT/qtc_packaging/debian_generic/$${TARGET}.desktop desktopfile.path = /usr/share/applications icon.files = $$PROJECTROOT/qtc_packaging/debian_generic/$${TARGET}.png icon.path = /usr/share/icons/hicolor/64x64/apps #debian.files = $$PROJECTROOT/qtc_packaging/generic/control } INSTALLS += desktopfile icon #debian.path = /DEBIAN isEmpty(target.path): target.path = $${INSTALL_PREFIX}/bin } } else { unix:!symbian { sdkheaders.files = $$SDK_HEADERS sdkheaders_private.files = $$SDK_PRIVATE_HEADERS isEmpty(SDK_HEADERS) { sdkheaders.files = $$HEADERS } sdkheaders.path = $$[QT_INSTALL_HEADERS]/$$MODULE_INCNAME sdkheaders_private.path = $$[QT_INSTALL_HEADERS]/$$MODULE_INCNAME/$$MODULE_VERSION/$$MODULE_INCNAME/private !plugin: target.path = $$[QT_INSTALL_LIBS] INSTALLS += sdkheaders sdkheaders_private } } INSTALLS *= target share for(bin, BIN_INSTALLS) { eval($${bin}.path = $${INSTALL_PREFIX}/bin) message("adding $$bin to bin install targets...") INSTALLS += $$bin } QtAV-1.12.0/doc/000077500000000000000000000000001312235004300131445ustar00rootroot00000000000000QtAV-1.12.0/doc/BuildQtAV-zh_CN.md000066400000000000000000000103671312235004300162670ustar00rootroot00000000000000## 0. 依赖 FFmpeg (>=1.0) 或 Libav (>=9.0). 强烈建议使用最新的 FFmpeg release版本,我主要用这个. 使用libav可能会导致dxva, vaapi, vda 和 libavfilter 不能使用。 编译好的FFmpeg可以这里下载 [QtAV sourceforge page](https://sourceforge.net/projects/qtav/files/depends/FFmpeg) 或者windows版[Zeranoe](http://ffmpeg.zeranoe.com/builds) 或者自己编译[Build FFmpeg](https://github.com/wang-bin/QtAV/wiki/Build-FFmpeg) 其他依赖 #### Windows PortAudio 或 OpenAL(建议). #### OSX, iOS 无. 调用系统 OpenAL #### Android OpenAL(OpenSL为后端). 当前 OpenSL 不能正常工作. #### Ubuntu OpenAL. 要启用所有功能需要安装 XVideo 和 VAAPI 的开发包. sudo apt-get install libopenal-dev libva-dev libxv-dev 运行时可能要安装vaapi driver让vaapi工作 sudo apt-get install libva-intel-vaapi-driver ## 1. Setup the environment 首先你 *必须* 让编译器能找到 FFmpeg 的头文件和库文件., 否则在 qmake 时会出错。如果你已经把那些文件放到该放的位置了,可以忽略这步。 #### 把头文件和库放到 Qt 头文件和库的目录 这是让编译器找到 ffmpeg 和其他依赖库最简单的方法 #### 使用环境变量 vc 编译器会在环境变量 __*INCLUDE*__ 制定的那些目录搜索头文件, __*LIB*__ 制定的目录搜索库文件,因此如果你使用命令行编译的话可以这样设置环境 set INCLUDE=ffmpeg_path\include;portaudio_path\include;%INCLUDE% set LIB=ffmpeg_path\lib;portaudio_path\lib;%LIB% GCC 会在环境变量 __*CPATH*__ 搜索头文件, __*LIBRARY_PATH*__ 里搜索库文件。因此你可以设置这些变量来包含 FFmpeg 和 PortAudio 相关的路径。 unix shell环境下的 gcc (也包括环境中有sh.exe的mingw环境): export CPATH=ffmpeg_path/include:portaudio_path/include:$CPATH export LIBRARY_PATH=ffmpeg_path/include:portaudio_path/lib:$LIBRARY_PATH 由于包含 libQtAV.pri 的工程不会添加 FFmpeg 等相关的链接参数,所以链接器可能会从 $LD_LIBRARY_PATH 中去找 QtAV 库的依赖库: export LD_LIBRARY_PATH=ffmpeg_path/lib:portaudio_path/lib:$LD_LIBRARY_PATH windows 无sh.exe的环境下的 gcc set CPATH=ffmpeg_path\include;portaudio_path\include;%CPATH% set LIBRARY_PATH=ffmpeg_path\lib;portaudio_path\lib;%LIBRARY_PATH% 如果使用 QtCreator 进行编译, 打开左边的 '工程' 页面,添加或追加相应的环境变量就行 ![QtCreator Settings](http://wang-bin.github.io/qtav.org/images/qtc-set.jpg "QtCreator Settings") ## 2. qmake 对于大多数系统, 只要 qmake make 强烈建议不要在源码目录编译,而是使用如下的方法 cd your_build_dir qmake QtAV_source_dir/QtAV.pro make qmake 在第一次运行的时候会检测所依赖的库, 你要保证这些库能被找到。 然后 qmake 会在编译目录生成一个 cache 文件 _.qmake.cache_ . cache 文件包含了检测结果,比如 portaudio 是否支持。 如果你想重新检测, 则可以删除 _**.qmake.cache**_ 再运行 qmake, 也可以直接给 qmake 加个额外参数 qmake QtAV_source_dir/QtAV.pro CONFIG+=recheck _WARNING_: If you are in windows mingw with sh.exe environment, you may need run qmake twice. I have not find out the reason! ## 3. Make 使用 make, jom, nmake 或者 QtCreator 进行编译. ## Windows 下的编译 你必须在qmake前配置好环境,如最开始所说的。 #### Visual Studio 我没有在 QtAV 里放任何 vs 的工程文件,因为这些工程很容易由 qmake 生成 打开命令行 qmake -r -tp vc QtAV.pro 然后 sln 和 vcxproj(vcproj) 文件会创建. 用 Visual Studio 打开 QtAV.sln 进行编译. 另外你也可以使用 Qt vs plugin 来导入qmake 工程(未测试) #### QtCreator 里使用 MSVC QtCreator 会检测 VC 编译器,编译过程和 gcc 的差不多,很简单。 #### VC 命令行下编译 我从 VS2012 Update1 中提取了 VC 编译器和 windows sdk. 可以从这里下载 http://qtbuild.googlecode.com/files/vs2012-x86.7z 这个编译环境很精简但是开发C++的功能很完整,至少能用它成功编译 Qt。QtAV-1.12.0/doc/BuildQtAV.md000066400000000000000000000135511312235004300152660ustar00rootroot00000000000000***Uninstall QtAV SDK before building to avoid header files confliction. Run sdk_uninstall.bat/sh under your build dir*** Shadow build is recommended (required for mac build). ## 0. Prerequisites - Get QtAV source code use git git clone https://github.com/wang-bin/QtAV.git git submodule update --init - FFmpeg>=1.0/Libav>=9.0. The latest release is recommended. You can download latest [prebuilt FFmpeg](https://sourceforge.net/projects/qtav/files/depends), or [build yourself](https://github.com/wang-bin/build_ffmpeg/wiki) - Libass headers is required if you need ass subtitle rendering support. For windows, download http://sourceforge.net/projects/qtav/files/depends/QtAV-depends-windows-x86%2Bx64.7z/download and using `bin, include, lib` in the package is enough to build. Other requirements are: - ***Windows*** OpenAL(Optional). OpenAL is not required since QtAV1.8.0. XAudio2 is always used. XAudio2 supports XP~windows 10 and Windows Store apps. Windows 8 and later natively supports XAudio2. For Windows 7 and older, you have to install [the driver from DirectX](http://sourceforge.net/projects/qtav/files/depends/DXSDK2010_XAudio2_redist.7z/download) to run. - ***OS X, iOS*** None. AudioToolbox and System OpenAL is used - ***Android*** On Windows you must put `mingw32-make.exe` in one of `%PATH%` to avoid qmake error. - ***Ubuntu*** OpenAL(recommended) or PulseAudio. To enable all supported features, you must install libass, XVideo and VA-API dev packages. sudo apt-get install libopenal-dev libpulse-dev libva-dev libxv-dev libass-dev libegl1-mesa-dev You may have to [install VA-API drivers](https://github.com/wang-bin/QtAV/wiki/Hardware-Accelerated-Decoding#va-api) to make VA-API available at runtime ## 1. Setup the environment You **MUST** let your compiler know where FFmpeg headers and libraries are. Otherwise you will get an error when running qmake. If they are already be where they should be, for example you install from apt on ubunt, just skip this step. Choose one of the following methods. #### (Recommended) Put FFmpeg headers and libs into Qt directories It's the simplest and best way. Qt include and lib dir are always searched in QtAV. It should work for all platforms, including android, iOS, WinRT and meego etc. #### (NOT Recommended)Use Environment Vars - VC: `INCLUDE` and `LIB` command line: set INCLUDE=ffmpeg_path\include;openal_path\include;%INCLUDE% set LIB=ffmpeg_path\lib;openal_path\lib;%LIB% - gcc/clang: `CPATH` and `LIBRARY_PATH` unix shell environment(including mingw with sh.exe) command line export CPATH=ffmpeg_path/include:openal_path/include:$CPATH export LIBRARY_PATH=ffmpeg_path/lib:openal_path/lib:$LIBRARY_PATH windows cmd.exe environment without UNIX Shell command line set CPATH=ffmpeg_path\include;openal_path\include;%CPATH% set LIBRARY_PATH=ffmpeg_path\lib;openal_path\lib;%LIBRARY_PATH% - If build in QtCreator, open 'Projects' page and add/append the environment/values. ![QtCreator Settings](http://wang-bin.github.io/qtav.org/images/qtc-set.jpg "QtCreator Settings") ## 2. Build in QtCreator or command line - Command line build For most platforms, just run mkdir your_build_dir cd your_build_dir qmake QtAV_source_dir/QtAV.pro make -j4 It's strongly recommended not to build in source dir(especially OSX). qmake will check the required libraries to make sure they can be found by compiler. Then qmake will create a cache file `.qmake.cache` in your build dir. Cache file stores the dependencies check results, for example, whether openal is available. If you want to recheck, you can either delete `.qmake.cache` and run qmake again, or run qmake QtAV_source_dir/QtAV.pro -r "CONFIG+=recheck" _WARNING_: If you are in windows mingw with sh.exe environment, you may need run qmake twice. I have not found out the reason behind this phenomenon. -------------------------------- #### Visual Studio/MSBuild I don't put any vs project file in QtAV, because it's easy to create by qmake. Open cmd qmake -r -tp vc QtAV.pro Then sln and vcxproj(vcproj) files will be created. Run `msbuild /m` to build the projects. You can also open QtAV.sln in your Visual Studio to Compile it. Another solution is using Qt vs plugin. It will help you to load qmake projects(not tested). #### QtCreator With MSVC QtCreator will detect VC compiler if it is installed. So it's easy to build in QtCreator #### Build for Android on Windows You may get qmake error `libavutil is required, but compiler can not find it`. That's because `mingw32-make.exe` can not be found in the config test step. An workaround is put your `mingw32-make.exe` to one of `%PATH%` dirs #### Build Debian Packages run debuild -us -uc in QtAV source tree #### Link to Static FFmpeg and OpenAL QtAV >=1.4.2 supports linking to static ffmpeg and openal libs. It's disabled by default. To enable it, add CONFIG += static_ffmpeg static_openal in $QtAV/.qmake.conf for Qt5 or $QtAV_BUILD_DIR/.qmake.cache #### Ubuntu 12.04 Support If QtAV, FFmpeg and OpenAL are built on newer OS, some symbols will not be found on 12.04. For example, clock_gettime is in both librt and glibc2.17, we must force the linker link against librt because 12.04 glibc does not have that symbol. add CONFIG += glibc_compat to .qmake.conf or .qmake.cache #### CI - [travis ci for linux and OSX](https://travis-ci.org/wang-bin/QtAV) - [appveyor for mingw and vs2013(msbuild+nmake)](https://ci.appveyor.com/project/wang-bin/qtav) You can read the build log to see how they work. ### Build Error - qmake error `Checking for avutil... no` Make sure ffmpeg headers and libs can be found by compiler. Read config.log in build dir for details. QtAV-1.12.0/doc/CompileFFmpegAndPortAudioWithMSVC.md000066400000000000000000000041431312235004300217440ustar00rootroot00000000000000## FFmpeg Build Script I write a script to simplify the build. The script is in QtAV/scripts/build_ffmpeg.sh. It supports mingw gcc, msvc(2013), android, maemo5, maemo6(meego), sailfish os etc. Just put the script in ffmpeg source tree and run ./build_ffmpeg.sh To cross build for android, maemo etc, you have to set some vars, for example NDK_ROOT for android. The vars can be set in config-android.sh, config-maemo5.sh etc. #### Install make install prefix=some_dir ref: http://ffmpeg.org/platform.html#Microsoft-Visual-C_002b_002b-or-Intel-C_002b_002b-Compiler-for-Windows ## Build with VC Open vs prompt, go to msys' bin dir and run sh --login -i Then your are ready to use bash environment and with VC environment. Then go to ffmpeg source dir and run './build_ffmpeg.sh vc' if get error about gawk, just ignore `make -ki` ## Additional Tools for Windows Your msys may lack of some command line tools that ffmpeg build system needs, such as pr, od, install, pkg-config, nasm. You can download one of build-ffmpeg-win-additinal-tools_msys2/gnuwin32.7z here: https://sourceforge.net/projects/qtav/files/depends/ extract and append it's bin dir to PATH. then you can build ffmpeg with gcc or vc. If you build with vc and not vs2013, you have to download [c99wrap](https://github.com/libav/c99-to-c89/) ## Use MinGW libraries in VC If we build FFmpeg using mingw gcc, the `*,lib` are also created and installed in /usr/local/bin. So VC linker can use those. `*,lib` are generated by vc toolchain's `lib.exe` and `link.exe` if they are found. Otherwise `dlltool` is used. You may need additional link flag `/SAFESEH:NO` when linking to libs created by dlltool to avoid link error. If gcc does not create `*.lib`, for example, portaudio, then you can do it yourself. dlltool -m i386 -d libportaudio-2.dll.def -l portaudio.lib -D libportaudio-2.dll the def file can be found in lib/.lib for portaudio ## Build PortAudio cmake is recommended. It's not difficult. ## Build QtAV https://github.com/wang-bin/QtAV/wiki/Build-QtAV [prebuilt FFmpeg+portuaido](https://sourceforge.net/projects/qtav/files/depends)QtAV-1.12.0/doc/MultiplePlayersSynchronization.md000066400000000000000000000006671312235004300217540ustar00rootroot00000000000000## Hypothesis The players' playing states are not changed when syncing, e.g. speed, play or pause state. ## KEY Sending and Recieving the sync requests takes time. The delay must be computed ## Solution ### Players in 1 Process Use AVClock. It's easy. ### Players in Network Pass 2 value. One is aboslute time when sending the sync request. Another is the play time of the video. This method also works for players in 1 processQtAV-1.12.0/doc/NativePaintingInQWidget-zh_CN.md000066400000000000000000000000001312235004300211470ustar00rootroot00000000000000QtAV-1.12.0/doc/NativePaintingInQWidget.md000066400000000000000000000022571312235004300201700ustar00rootroot00000000000000Sometimes, we may want paint in QWidget without QPainter, for example, using DirectX api. This document will tell you how to reach it. ###0. setAttribute(Qt::WA_PaintOnScreen, true) Document says that if you want to use native painting, the attribute should be true. Otherwise you will see the fuck flicker. ###1. Let paint engine be 0 Thus QPainter is disabled. Otherwise you will see the fuck flicker again. This page is helpful: http://lists.trolltech.com/qt4-preview-feedback/2005-04/thread00609-0.html It seems that let paintEngine() return 0 is from here. ###2. Reimplement `paintEvent` Oberviously, you should do something here. You can begin the painting in paintEvent ###3. Reimplement `showEvent` This is important if you change the `WindowStaysOnTopHint` flag. As far as i know, some paint engines use the resource depend on the window. If window changes, resource must be recreated again. For example, render target in direct2d, device context in gdi+. If the window flag `WindowStaysOnTopHint` changes, the widget will hide then show again, maybe that's the reason. If you forget this step, then the widget will never update again after the `WindowStaysOnTopHint` changes. QtAV-1.12.0/doc/QtAVBuildConfigurations.md000066400000000000000000000042331312235004300201760ustar00rootroot00000000000000Since QtAV 1.3.1, you can configure how to build QtAV in a easier way. When running qmake, qmake will do some tests to check the building environment and the features can be enabled. The default behavior is to check all the features in both `$$EssentialDepends` and `$$OptionalDepends`(see QtAV.pro) and to build all parts in source tree including examples, tests. Now you can simply select what you want to check and build using a *user.conf* in QtAV source directory. Disable Features Manually ========================= Currently features in `$$EssentialDepends` will always be checked: avutil, avcodec, avformat, swscale, swresample, avresample, gl. Features in `$$OptionalDepends` will also be checked by default. Features in `$$OptionalDepends` can be disabled by adding a CONFIG value in *QtAV_SRC_DIR/user.conf*(create it if not exists). For example, to disable vaapi, just add: CONFIG += no-vaapi All options available are: no-openal, no-portaudio, no-direct2d, no-gdiplus, no-dxva, no-xv, no-vaapi, no-cedarv. Disable config.tests ==================== config.tests will check what features QtAV can support. It may takes a few seconds. Some you may skip these checking to save time. You can add CONFIG += no_config_tests in *QtAV_SRC/user.conf* to disable it then qmake will be faster. But you then must enable the features you want manually in *user.conf*. All features available are `$$OptionalDepends`. For example, to enable *avresample*, add CONFIG += config_avresample in *user.conf*. Link Against Static FFmpeg ========================== It is supported since 1.4.2. You can add `CONFIG += static_ffmpeg` in QTAV_BUILD_DIR/.qmake.cache. Additional link flags will be added. Only ffmpeg build with QtAV/script/build_ffmpeg.sh is tested. Old GLibc Compatibility ====================== QtAV built from a newer linux system like ubuntu 14.04 may not work on old system like ubuntu 12.04 because of some symbols is missing. For example, `clock_get_time` is in glibc>=2.17 and librt. An convenience option is added since 1.4.2. Just add `CONFIG += glibc_compat` in QTAV_BUILD_DIR/.qmake.cache TODO ==== more flexible configuration, such as build path, blablabla.QtAV-1.12.0/doc/UseQtAVinYourProjects-zh_CN.md000066400000000000000000000042721312235004300207020ustar00rootroot00000000000000QtAV 1.3.4 之后可以很方便地使之作为一个Qt模块,就像其他Qt模块那样。首先要编译QtAV,编译好后在编译目录会生成安装为Qt模块的脚本 `sdk_install.sh` 和 `sdk_uninstall.sh`,windows为`sdk_install.bat` 和 `sdk_uninstall.bat`,运行`sdk_install.sh` 或`sdk_install.bat`进行安装。安装后要使用QtAV只要在你的qmake工程里加上 CONFIG += av 就行。对于Qt5,也可以使用 QT += av 在 C++ 文件中,加入 #include 自 QtAV 1.5 基于 QWidget 的渲染器被移到了新模块 QtAVWidgets, 若需要使用这些渲染器比如 OpenGLWidgetRenderer,在工程文件加入 QT += avwidgets C++ 代码中加入 #include #include 请确保在使用渲染器前调用`QtAV::Widgets::registerRenderers()` ## QtAV < 1.3.4 在你的项目中包含 QtAV 非常容易. 因为 QtAV 的qmake工程是精心设计的. (可以参考: https://github.com/wang-bin/LibProjWizard4QtCreator) 你可以参考 QtAV 里的例子来了解如何使用 QtAV, 或者也可以使用以下步骤 ###1. 新建一个 subdirs 类型的工程myproject及一个直接调用QtAV的player子工程 myproject/myproject.pro TEMPLATE = subdirs SUBDIRS += libQtAV myplayer myplayer.depends += libQtAV libQtAV.file = QtAV/QtAV.pro include(QtAV/root.pri) ###2. 把 QtAV 放到myproject 可以在 myproject/ 目录下使用 `git clone git@github.com:wang-bin/QtAV.git`, 或者复制 QtAV 代码到 myproject/. 建议使用git,这样方便获取最新代码. 现在的目录变为 > myproject/myproject.pro > myproject/myplayer/myplayer.pro > myproject/QtAV/QtAV.pro > myproject/QtAV/src/libQtAV.pro > myproject/QtAV/src/libQtAV.pri ###3. 在player工程中包含 libQtAV.pri 在 myproject/myplayer/myplayer.pro, add include(../QtAV/src/libQtAV.pri) ###4. 生成 Makefile qmake or qmake -r ###5. make player 会生成在编译目录下的bin目录. 在windows下, QtAV 的 dll文件也会在那里生成 注意:windows 下如果用`qmake`命令(命令行下。QtCreator 默认使用`qmake -r`),则可能需要运行`qmake`两次,否则 make 可能失败QtAV-1.12.0/doc/UseQtAVinYourProjects.md000066400000000000000000000032111312235004300176730ustar00rootroot00000000000000 For QtAV version >= 1.3.4, QtAV can be installed as Qt5 modules easily. Integrating QtAV in your project is very easy. - Install QtAV SDK * Install without building QtAV https://github.com/wang-bin/QtAV/wiki/Deploy-SDK-Without-Building-QtAV * Or use the latest code and build yourself. Then go to building directory, `sdk_install.sh` or `sdk_install.bat`. OSX is a little different because the shared library id must be modified but sdk_install.sh just simply copy files. you have to run QtAV/tools/sdk_osx.sh. Assume your Qt is installed in `$HOME/Qt5.3`, then run `qtav_src_dir/sdk_osx.sh qtav_build_dir/lib_mac_x86_64/QtAV*.framework ~/Qt5.3/5.3/clang_64/lib` - qmake Project To use QtAV, just add the following line in your Qt4 project CONFIG += avwidgets or add the following line in your Qt5 project QT += avwidgets (In Qt5, if QtWidgets module and QtAV widget based renderers are not required by your project, you can simply add `QT += av`) - C++ Code add #include #include Make sure `QtAV::Widgets::registerRenderers()` is called before creating a renderer. ### Try the Example In the latest code, `examples/simpleplayer_sdk` is a complete example to show how to use QtAV SDK to write an multimedia app. You can use `simpleplayer_sdk.pro` as a template. ### Link Error Because qt automatically rename the module if it's name contains `Qt`, so you may get `cannot find -lQt5AVWidgets`. As a temporary workaround, please manually rename `libQtAVWidgets.so` to `libQt5AVWidgets.so` (windows is Qt5AV.lib or Qt5AV.a) in `$QTDIR/lib`. `-lQt5AV` error is the same. It should be fixed in 1.9.0QtAV-1.12.0/doc/UsingPipe.md000066400000000000000000000010111312235004300153620ustar00rootroot00000000000000FFmpeg supports playing media streams from pipe. Pipe is a protocol in FFmpeg. To use it, you just need to replace the file name with 'pipe:'. for example cat hello.avi |player pipe: It also supports streams from file number pipe:number 'number' can be 0, 1, 2, .... 0 is stdin, 1 is stdout and 2 is stderr. If 'number' is empty, it means stdin. Using pipe in AVPlayer class is almost the same. Just use "pipe:N" as file name. #### Named Pipe player \\.\pipe\name FFmpeg is powerful, do you think so?QtAV-1.12.0/examples/000077500000000000000000000000001312235004300142155ustar00rootroot00000000000000QtAV-1.12.0/examples/CMakeLists.txt000066400000000000000000000132631312235004300167620ustar00rootroot00000000000000find_package(Qt5Sql REQUIRED) if(WIN32) set(EXE_TYPE WIN32) elseif(APPLE) #set(EXE_TYPE MACOSX_BUNDLE) endif() set(COMMON_SRC common/common.cpp common/Config.cpp common/qoptions.cpp ) set(COMMON_HEADERS common/common.h common/Config.h common/qoptions.h common/ScreenSaver.h common/common_export.h ) if(NOT IOS) list(APPEND COMMON_SRC common/ScreenSaver.cpp) if(APPLE) list(APPEND COMMON_LIBS "-framework CoreServices") endif() endif() SET(COMMON_QRC common/theme/theme.qrc) SOURCE_GROUP("Resources" FILES ${COMMON_QRC}) qt5_add_resources(COMMON_RES ${COMMON_QRC}) set_source_files_properties(${COMMON_RES} PROPERTIES GENERATED ON) # add HEADERS for moc add_library(common STATIC ${COMMON_SRC} ${COMMON_RES} ${COMMON_HEADERS}) target_link_libraries(common LINK_PUBLIC Qt5::Core Qt5::Gui ${COMMON_LIBS} LINK_PRIVATE Qt5::Sql ) target_compile_definitions(common PUBLIC -DBUILD_COMMON_STATIC) target_include_directories(common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set(PLAYER_SRC player/main.cpp player/MainWindow.cpp player/ClickableMenu.cpp player/StatisticsView.cpp player/Slider.cpp player/TVView.cpp player/EventFilter.cpp player/config/ConfigDialog.cpp player/config/ConfigPageBase.cpp player/config/CaptureConfigPage.cpp player/config/VideoEQConfigPage.cpp player/config/DecoderConfigPage.cpp player/config/MiscPage.cpp player/filters/OSD.cpp player/filters/OSDFilter.cpp player/playlist/PlayListModel.cpp player/playlist/PlayListItem.cpp player/playlist/PlayListDelegate.cpp player/playlist/PlayList.cpp player/config/PropertyEditor.cpp player/config/AVFormatConfigPage.cpp player/config/AVFilterConfigPage.cpp player/config/ShaderPage.cpp player/filters/AVFilterSubtitle.cpp ) set(PLAYER_HEADERS player/MainWindow.h player/ClickableMenu.h player/StatisticsView.h player/Slider.h player/TVView.h player/EventFilter.h player/config/ConfigDialog.h player/config/ConfigPageBase.h player/config/CaptureConfigPage.h player/config/VideoEQConfigPage.h player/config/DecoderConfigPage.h player/config/MiscPage.h player/filters/OSD.h player/filters/OSDFilter.h player/playlist/PlayListModel.h player/playlist/PlayListItem.h player/playlist/PlayListDelegate.h player/playlist/PlayList.h player/config/PropertyEditor.h player/config/AVFormatConfigPage.h player/config/AVFilterConfigPage.h player/config/ShaderPage.h player/filters/AVFilterSubtitle.h ) SET(PLAYER_QRC player/res/player.qrc) SOURCE_GROUP("Resources" FILES ${PLAYER_QRC}) qt5_add_resources(PLAYER_RES ${PLAYER_QRC}) set_source_files_properties(${PLAYER_RES} PROPERTIES GENERATED ON) if(Qt5Quick_FOUND) set(QMLPLAYER_QRC QMLPlayer/qmlplayer.qrc) qt5_add_resources(QMLPLAYER_RES ${QMLPLAYER_QRC}) set_source_files_properties(${QMLPLAYER_RES} PROPERTIES GENERATED ON) set(MODULE QMLPlayer) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_executable(QMLPlayer ${EXE_TYPE} QMLPlayer/main.cpp QMLPlayer/qtquick2applicationviewer/qtquick2applicationviewer.cpp QMLPlayer/qtquick2applicationviewer/qtquick2applicationviewer.h ${QMLPLAYER_RES} ${RC_FILE} ) target_include_directories(QMLPlayer PRIVATE QMLPlayer/qtquick2applicationviewer) target_link_libraries(QMLPlayer Qt5::Quick common) install(TARGETS QMLPlayer RUNTIME DESTINATION ${QTAV_INSTALL_BINS} ) endif() if(Qt5Widgets_FOUND) set(MODULE Player) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_executable(Player ${EXE_TYPE} ${PLAYER_SRC} ${PLAYER_HEADERS} ${PLAYER_RES} ${RC_FILE}) target_link_libraries(Player QtAVWidgets common) install(TARGETS Player RUNTIME DESTINATION ${QTAV_INSTALL_BINS} ) if(BUILD_EXAMPLES) SET(FILTERS_QRC filters/res.qrc) SOURCE_GROUP("Resources" FILES ${FILTERS_QRC}) qt5_add_resources(FILTERS_RES ${FILTERS_QRC}) set_source_files_properties(${FILTERS_RES} PROPERTIES GENERATED ON) add_executable(filters filters/main.cpp filters/SimpleFilter.cpp ${FILTERS_RES}) target_link_libraries(filters QtAVWidgets) add_executable(glslfilter glslfilter/main.cpp) target_link_libraries(glslfilter QtAVWidgets) add_executable(shader shader/main.cpp) target_link_libraries(shader QtAVWidgets) add_executable(sharedoutput sharedoutput/main.cpp sharedoutput/widget.cpp) target_link_libraries(sharedoutput QtAVWidgets) add_executable(simpleplayer simpleplayer/main.cpp simpleplayer/playerwindow.cpp) target_link_libraries(simpleplayer QtAVWidgets) add_executable(videocapture videocapture/main.cpp videocapture/playerwindow.cpp) target_link_libraries(videocapture QtAVWidgets) add_executable(videographicsitem videographicsitem/main.cpp videographicsitem/videoplayer.cpp) target_link_libraries(videographicsitem QtAVWidgets) add_executable(videogroup videogroup/main.cpp videogroup/videogroup.cpp) target_link_libraries(videogroup QtAVWidgets) add_executable(videowall videowall/main.cpp videowall/VideoWall.cpp) target_link_libraries(videowall QtAVWidgets) add_executable(simpletranscode simpletranscode/main.cpp) target_link_libraries(simpletranscode QtAV) endif() endif() if(BUILD_EXAMPLES) add_executable(audiopipeline audiopipeline/main.cpp) target_link_libraries(audiopipeline QtAV) add_executable(framereader framereader/main.cpp) target_link_libraries(framereader QtAV) add_executable(window window/main.cpp) target_link_libraries(window QtAV) endif() QtAV-1.12.0/examples/QMLPlayer/000077500000000000000000000000001312235004300160235ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/ControlPanel.qml000066400000000000000000000001001312235004300211250ustar00rootroot00000000000000import QtQuick 2.0 Rectangle { width: 100 height: 62 } QtAV-1.12.0/examples/QMLPlayer/QMLPlayer.pro000066400000000000000000000041231312235004300203530ustar00rootroot00000000000000!static:VERSION = $$QTAV_VERSION # vc: will create exp and lib, result in static build error QT += sql android { QT += androidextras } *maemo*: DEFINES += Q_OS_MAEMO # Add more folders to ship with the application, here folder_01.source = qml/QMLPlayer folder_01.target = qml #will copy to target path #DEPLOYMENTFOLDERS = folder_01 # Additional import path used to resolve QML modules in Creator's code model QML_IMPORT_PATH = # If your application uses the Qt Mobility libraries, uncomment the following # lines and add the respective components to the MOBILITY variable. # CONFIG += mobility # MOBILITY += RESOURCES += \ qmlplayer.qrc TRANSLATIONS = i18n/QMLPlayer_zh_CN.ts i18n/QMLPlayer.ts # The .cpp file which was generated for your project. Feel free to hack it. SOURCES += main.cpp lupdate_only{ SOURCES = qml/QMLPlayer/*.qml qml/QMLPlayer/*.js } # Installation path target.path = $$[QT_INSTALL_BINS] desktopfile.files = $$PWD/../../qtc_packaging/debian_generic/QMLPlayer.desktop desktopfile.path = /usr/share/applications # Please do not modify the following two lines. Required for deployment. include(qtquick2applicationviewer/qtquick2applicationviewer.pri) qtcAddDeployment() #!*msvc*: QMAKE_LFLAGS += -u __link_hack isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/../.. STATICLINK=1 include($${PROJECTROOT}/examples/common/libcommon.pri) preparePaths($$OUT_PWD/../../out) mac: RC_FILE = $$PROJECTROOT/src/QtAV.icns genRC($$TARGET) ios { RCC_DIR = #in qt_preprocess.mk, rule name is relative path while dependency name is absolute path MOC_DIR = QMAKE_INFO_PLIST = ios/Info.plist } DISTFILES += \ android/src/org/qtav/qmlplayer/QMLPlayerActivity.java \ android/gradle/wrapper/gradle-wrapper.jar \ android/AndroidManifest.xml \ android/res/values/libs.xml \ android/build.gradle \ android/gradle/wrapper/gradle-wrapper.properties \ android/gradlew \ android/gradlew.bat ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android #ubuntu14.04 use qt5.2, remove incompatible code in qmlplayer !qtAtLeast(5, 3):unix: system("sed -i '/\/\/IF_QT53/,/\/\/ENDIF_QT53/d' qml/QMLPlayer/main.qml") QtAV-1.12.0/examples/QMLPlayer/QMLPlayer_harmattan.desktop000066400000000000000000000003601312235004300232620ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Name=QMLPlayer Exec=/usr/bin/QMLPlayer Icon=/usr/share/icons/hicolor/80x80/apps/QMLPlayer80.png X-Window-Icon= X-HildonDesk-ShowInToolbar=true X-Osso-Type=application/x-executable QtAV-1.12.0/examples/QMLPlayer/QMLPlayer_sdk.pro000066400000000000000000000110571312235004300212200ustar00rootroot00000000000000TARGET = QMLPlayer !static:VERSION = $$QTAV_VERSION QT += av svg qml quick sql android { QT += androidextras } *maemo*: DEFINES += Q_OS_MAEMO # Add more folders to ship with the application, here folder_01.source = qml/QMLPlayer folder_01.target = qml #will copy to target path #DEPLOYMENTFOLDERS = folder_01 # Additional import path used to resolve QML modules in Creator's code model QML_IMPORT_PATH = # If your application uses the Qt Mobility libraries, uncomment the following # lines and add the respective components to the MOBILITY variable. # CONFIG += mobility # MOBILITY += RESOURCES += qmlplayer.qrc TRANSLATIONS = i18n/QMLPlayer_zh_CN.ts i18n/QMLPlayer.ts # The .cpp file which was generated for your project. Feel free to hack it. SOURCES += main.cpp lupdate_only{ SOURCES = qml/QMLPlayer/*.qml qml/QMLPlayer/*.js } # Installation path target.path = $$[QT_INSTALL_BINS] desktopfile.files = $$PWD/../../qtc_packaging/debian_generic/QMLPlayer.desktop desktopfile.path = /usr/share/applications # Please do not modify the following two lines. Required for deployment. include(qtquick2applicationviewer/qtquick2applicationviewer.pri) qtcAddDeployment() COMMON = $$PWD/../common INCLUDEPATH *= $$COMMON $$COMMON/.. RESOURCES += $$COMMON/theme/theme.qrc isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/../.. fonts.files = fonts fonts.path = fonts QMAKE_BUNDLE_DATA += fonts mac: RC_FILE = $$PROJECTROOT/src/QtAV.icns QMAKE_INFO_PLIST = $$COMMON/Info.plist ios: QMAKE_INFO_PLIST = ios/Info.plist videos.files = videos videos.path = / #QMAKE_BUNDLE_DATA += videos defineTest(genRC) { RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia playback framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV $$1" export(RC_ICONS) export(QMAKE_TARGET_COMPANY) export(QMAKE_TARGET_DESCRIPTION) export(QMAKE_TARGET_COPYRIGHT) export(QMAKE_TARGET_PRODUCT) return(true) } genRC($$TARGET) #SystemParametersInfo !winrt:*msvc*: LIBS += -lUser32 DEFINES += BUILD_QOPT_LIB HEADERS *= \ $$COMMON/common.h \ $$COMMON/Config.h \ $$COMMON/qoptions.h \ $$COMMON/ScreenSaver.h \ $$COMMON/common_export.h SOURCES *= \ $$COMMON/common.cpp \ $$COMMON/Config.cpp \ $$COMMON/qoptions.cpp !macx: SOURCES += $$COMMON/ScreenSaver.cpp macx:!ios { #SOURCE is ok OBJECTIVE_SOURCES += $$COMMON/ScreenSaver.cpp LIBS += -framework CoreServices #-framework ScreenSaver } SOURCES *= main.cpp android { DISTFILES += \ android/src/org/qtav/qmlplayer/QMLPlayerActivity.java \ android/gradle/wrapper/gradle-wrapper.jar \ android/AndroidManifest.xml \ android/res/values/libs.xml \ android/build.gradle \ android/gradle/wrapper/gradle-wrapper.properties \ android/gradlew \ android/gradlew.bat ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android #user can put fonts in android/assets/fonts } winrt|wince { # vs project: qmake -tp vc "CONFIG+=windeployqt" QT *= opengl #qtav is build with opengl module CONFIG *= windeployqt WINDEPLOYQT_OPTIONS = -qmldir $$shell_quote($$system_path($$_PRO_FILE_PWD_/qml/QMLPlayer)) DISTFILES *= # will be listed in "Distribution Files" in vs but not deployed. not sure what's the usage # If a file does not exist, it will not be added to vs project # If not using vs, edit AppxManifest.map for Qt5.5 depend_dll.files = \ $$[QT_INSTALL_BINS]/QtAV$${QTAV_MAJOR_VERSION}.dll \ $$[QT_INSTALL_BINS]/avcodec-*.dll \ $$[QT_INSTALL_BINS]/avformat-*.dll \ $$[QT_INSTALL_BINS]/avutil-*.dll \ $$[QT_INSTALL_BINS]/avfilter-*.dll \ $$[QT_INSTALL_BINS]/swresample-*.dll \ $$[QT_INSTALL_BINS]/swscale-*.dll exists($$[QT_INSTALL_BINS]/avresample-*.dll): depend_dll.files += $$[QT_INSTALL_BINS]/avresample-*.dll exists($$[QT_INSTALL_BINS]/ass.dll): depend_dll.files += $$[QT_INSTALL_BINS]/ass.dll #depend_dll.path = $$OUT_PWD DEPLOYMENT = depend_dll fonts #vs2015update1 error about multiple qt5core.dll(in both build dir and qtbin dir), we can remove them in `Deployment Files` # WINRT_MANIFEST file: "=>\" VCLibsSuffix = winphone { VCLibsSuffix = .Phone WINRT_MANIFEST = winrt/WinPhone8.Package.appxmanifest } else:*-msvc2015 { #isEqual(VCPROJ_ARCH, ARM): VCLibsSuffix = .Phone WINRT_MANIFEST = winrt/WinRT10.Package.appxmanifest } else { WINRT_MANIFEST = winrt/WinRT8.Package.appxmanifest } OTHER_FILES *= winrt/* WINRT_ASSETS_PATH = winrt/assets #for qmake generated manifest } QtAV-1.12.0/examples/QMLPlayer/android/000077500000000000000000000000001312235004300174435ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/AndroidManifest.xml000066400000000000000000000111021312235004300232270ustar00rootroot00000000000000 QtAV-1.12.0/examples/QMLPlayer/android/build.gradle000066400000000000000000000027361312235004300217320ustar00rootroot00000000000000buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.1.0' } } allprojects { repositories { jcenter() } } apply plugin: 'com.android.application' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } android { /******************************************************* * The following variables: * - androidBuildToolsVersion, * - androidCompileSdkVersion * - qt5AndroidDir - holds the path to qt android files * needed to build any Qt application * on Android. * * are defined in gradle.properties file. This file is * updated by QtCreator and androiddeployqt tools. * Changing them manually might break the compilation! *******************************************************/ compileSdkVersion androidCompileSdkVersion.toInteger() buildToolsVersion androidBuildToolsVersion sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] res.srcDirs = [qt5AndroidDir + '/res', 'res'] resources.srcDirs = ['src'] renderscript.srcDirs = ['src'] assets.srcDirs = ['assets'] jniLibs.srcDirs = ['libs'] } } lintOptions { abortOnError false } } QtAV-1.12.0/examples/QMLPlayer/android/gradle/000077500000000000000000000000001312235004300207015ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/gradle/wrapper/000077500000000000000000000000001312235004300223615ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/gradle/wrapper/gradle-wrapper.jar000066400000000000000000001413501312235004300257770ustar00rootroot00000000000000PK  ;B META-INF/PK  ;BӸNhMETA-INF/MANIFEST.MFMLK-. K-*ϳR03-IM+I, dZ)%b"i5520460BS##Sm PK  ;Borg/PK  ;B org/gradle/PK  ;Borg/gradle/wrapper/PK  ;Bhdf#org/gradle/wrapper/Download$1.class}M 0h5Z+v/ ׆p!.O2ĝCR QC7SĮMp8P/bXDJɰ+DDS(U(WA dQ$ʶۖ /y!om/XaXn GlM`^0l8P줊 ιZ=CaN"dSx2gPIK"՘#aTG~ i\?W#;#.C)` 0dDmwԇ!im'RvwɌfc$BU)9H8C~FÚs5,/UzDKsҋ bɉ!Kkr|ZظB6crC8łS OSrfpm\)X %7Jk iL&/".UȦ~PK  ;BXs"org/gradle/wrapper/IDownload.classE 0  ^b AP^26J;t>;ɗ|{z~+%5O&WΔ(a_4[gR#!XbQVg={}1AYCX'R5c/J$S@pP\mKulPK  ;BHE*P 3org/gradle/wrapper/ExclusiveFileAccessManager.classVKpU=//2 "@`ȇ@ I'i{z=KJ0*KK˅.)7nrrcyݓ/w?{ޝ/xQT,Z#%mrDN8ĩ0JxgtYAhҬ/rcz0!%C0a I#ݍMNΩjPxcf@I2j:B>Y99+ d,h2V )(55Uu V/ KSz4C14JFj^)jGl5i#ՇFi-mVM_ ԯnť^w9O[f:gۚēaȈuU3>sNK;t`ʶ0CdXA OӤju#0A_9jGLYEm&tVm6De V (xNڪk yDk͹IQOI;P|voF3S|wu{ijM4iH/{j緊!uGKVVkcoZODJO/,nrv:ߡ m4VP[آ -ldAtW] FpnJAR4 YyՋSQ^ %/5RN출)_4!;W|qQ[SZotDbYvtQyE+ A~3w]ج@ >*5I(kQU%*bUsW*-/WĪr~%.XH`ӂ|HqhW.j:ٝPn1fɩ'q* FܗajVSoi8D+(ˮ6u#Jz P̰q0@OF Sp@TL׵u Ew#mp 01CiJO$b.H#oCs\P?&PUSto eMڷxEDž&bOʻV1t9ehۮB :!1> NHDxad@E_|RFDZa2QJ02Qc#70v_rV@#|ߊFI»_y-_8h!VɖΨ;nL!_(#n.:fX?o/X(C|GIx}ߠK]G#y9])4oNgDʩ $f&/CPK  ;Bc`-org/gradle/wrapper/WrapperConfiguration.classn@g8N7Ρ--@[QU6@QB[I*ؑSDC!fN, ?y~:q_R- _FǷNAm?'He+Kp1rũ 9pd vmJ_SvV:X W7KGtBRt-VFgJ8璎  jNg<|R WBZFrng}zlj2t᳼w4ĵF 7o:l֬JqI +&%MO(lDk?COxpU;W<" k:E-+C?P7<|ߵ?53ɣ2~܄|6}f iK[b[M=i4Ot!ZBإ.N],OȟGkBZA=9^&Ћ5cG#%i4cB̶1- !ЅnZu&BDyq7p%Feܣ SK:aL=-ݴpw0?n#>zc 8C_;Aq57"+pŹAJ Xz^;PK  ;B+Nm q&org/gradle/wrapper/PathAssembler.classV{W=cKeu]`'^Xv>gd5&0?UVGi -# ԇUoV#DFh${F ;Hl+d-Խ#XNbN{־'1,%_R$[TU*“ɁTE~b*h&W75쯢}(m{`ml;olb3cv.DMQ=A:Dx=ڸv[Dk<墅v4j:%j~((BɸNOBO Uqx2v$6.fCwѹٔs.긋@pG,crQ̏{X]<'Һ'*8!* Nlл;I9:u|=kdmhӵxzHeEgݘ,h#f0OXziik񤋾 |O5 #{wNmZ+HyP"q=Ə{{k:pō U|ҕ7}Lf?]G_s1 ؿ,tzt2PnÔUN,u=+]]X#CP\t}8>gq>f$ۈ&Df*6YHW&| ;Zx[oB:OJ])Oҕpa35 k:zR.APK  ;B(/ org/gradle/wrapper/Install.classX |uO{j5t!-X%n,0.IȈ <Ҏݝ,HĎ 4m6Tu61mMZ7M4m#iqצ6fV{H1F?x}|6ǂ?[|[?-Eg2  }CׂAz_AkiF Rl{~PF@? 5ҿi%{CT迂R54#eE Џd¤0$L7H'? J r- PR*^]\R  vr]CU,W jWxm8oV\7 M4*n[Mv;t~;ef(5HdoɁЛwy']Asv))׿S}>4p`HLgZGLKLvږʹLl-abi=3DԢLkMkcҢ1㜥%1ЗYI>ݝJn1m(v`$ҧcqVgq=ڤP(F@οH^ƷƲ5aQiЇ1:aQgk#ewfk6RLrb18n隭)鶢bu{c˜L[mM C{35mR( 1V%"&*S\<ͤ$`<` [*"Saeeɩ:3 zRjR4MضR/1imYԸe$]QÂj+Xu b/ά,trE{rۋ2(I۴wޫYV=\x|ޘ0 N[Rc,&_DnOi[ѭJԎK,e{GZ ͔5KM}l!@'9˔ I1'dEϒ8ٝusb V:i-A8,s?zx\K 8aZC`\FZ{AA35Ҩ%­"F%vi(gZbbtEv3|z¶pl~Y3i,t-.0^ r o^X;,Q]"t ~S#Q :҇J& v7uC*)}SG^S_}*U]ܬV__zԻT~7B|RyE!`:aq=ˡίTM Mᨩ ^`kF"%mSb=?ć fx+Oivl:9oE.L|GDU>ȣL'+Dw ,pI!>Lneiӂ^г*{TWj1#1$Ր`F?7=;oW*T5TQ*}YLYQ݆V e3iuUIORIϨ|ߍ \t"_$ǹk*8.r&1 l-*IdTCB,By^SQ;~\'I?E_?] 0#q vE\Y80 r@^tF(56-~:o$'өyD"IXV\b_e1 Z Ĩ~-s.z]s@t9p;zAhb_HY^XF? 'ҝ;Ě]0ku^) L%[ק\ʫF4u^ MK)eFo!ك. ()W@^-a& @TXTϕEN(Ŗ]Ӣbɤd!Ͱg tW0]M)yP3ܳGB{,eҶͅUULW QZڼH(;rKwto{n KV>`j|۲W"iAG?y-ms<"㑿(G:0qp)MEs~ޘo‡TMᴖ2-?Ǒ9IJ>eg>C? >Pܟ3B9.y_ux=T%)֭*r H%:#v oMTGD^ N4:MrǸRN9cR<5ϑ6K˻hWt0YSE9 y[r^/ m S+djUȳץgM4da<!~] ?@CU F>z1,ӇoɾH f[;0]yZBuOUbxәՋ=/њhq4ZH}(u6>Btz)hrj5Q@'c8=A̓)^^A=Ok#sn_]jOSƷ҆K/ѭ%t\NkNpI^$n T؏=TA~~~h]z@c$*ڝQ4j^IV=wIr]o{y#- sqiS 5FsY(2ޓ+] */9ID~*,`lqg֫Of[k\s>E>sx$P@N 0 Я@|p { ̗rҖ *.Gl|;=8>Owf͵.z 0|8I\.Je`pX?rjEV8 ]f`E ԉN O2Bʫ3>.Lɀ3 ^ N b :Ht.\Zk2 N p+p;t's9!D> JW`l{03 ۞@y{1kNjR&^v+#{ ã ͖IO`N P/[Pʹ裡Sf`GV7({Vӯ*} ~9 ͊؀/9"6`NjN_GN;4+&}%~j4s\W CB-$~7CI'\85LZ8~W:jY:y}#B\.J#77 i_GHlId%HGRf'>PK  ;BG -org/gradle/wrapper/BootstrapMainStarter.classVY[V=²eT%Y@fӔ(MB I0KE#"KT tK }K_W}h;Wꇻ̝9gޑ緗2'$/aZFfdbN<(Ã%,+d4aUšulH$w|xLJ2>>C-W ͞ GSf hk[gn$ ͔Ehh9iebKM,RgvΦ͒ [lf R@ox31]52miFfܕhflFٸG2 9gf ]ji\.niF {`vlm%^⇳ h)xh_bI9,3ضRvՑeGeJkv;10=O؞FN/ŶfjH0!S5sq[sZV/9ج 0/A0V8 p];-9kl##aG] O*0)aO@wQԭZT/2:{EAhUeOSh>ڥJH8T>/!+.mq_(_)x~~8:[Q-bXe0=et0NeYa!B3pw%%qZlk[y!ߗ*x9ϧEo X"mPÑ>?^Ye2NU.պ-afʇUP}y~Z̠zGJ=Suub*= ma])cT~EFm>5J-N35g"jQ֌} a^(ԊT:$DzÑ^"e76jɪ6=^/iQ:yC4r]N4o.F@w Z4F#D/k>N3=NɊ$7а|ORb3\_R#4ǐbŀR1oHMIO+>$g/N*}7%ÜsaU "><0DHsnҿ/j-IV#EҮ-ҍD$䅾QD͋'hKڏQQ]׆[j:/(_' \->F8@ aMZ!Nu†"ctl%{9Ӕ,:ĹPK  ;Bf[A (org/gradle/wrapper/WrapperExecutor.classWi`e~&t7iJ7I/RXJKM6ᦴ\dLvɔ2;E*(z[&@(}}(p8{CFU32!< ?Ĝ<('wA<y8}$ !|2O  c}ZB<ϊ0zQ0/i_ Z_7Bݽё};zGDvk{Hؖܨ`QZޮ󺂕%:$z|ڙ)?)e22[I8kYݲ =`k_6C}XWH:0;d-^լ ټ]帺%3)m!\43ILkcRics]Ml̐~iѠz6,_R+*<}z2OaT2Stlq5"43k!koS`QypZfTx]Ba}I= Fo94kYһSp"/BF-C)-\`̼,ƻx. [k)G9j%nJ4Mk[$.ߦTQX]RCDm>)4atAyz3Qw{cf'uۏNI*hmy&)ȉU-W[$JH+uMl&i$4kWA# W)T0QAqj)l9M>/Y-gh"e}wi|S~eNEPV4"__^K-e2-ۋmUEߡJ,gߕ!{>p>czy6E£hLym92̘vT|oge NHK *~N2UU*F0b;T<*lUح')|E֩t?*^W+)_ʡzMfeV+tLBe),ѽivw*Lq"?ޑ*ii|"x SEVH'jgHЂy+=̺Rݾ 8=oo׭RnWZcz2DpШ*xf碩B "Ugts[=樖ImnL9=Hzҧ=T3L{t(3|3Mz/'7 kTW*k?v1*UJ-f4Ǟ^wzRMр3T$}4/G3oxl-2.p8,wԲKJ Upq^3L+o_+6>rek>W\n;G-b'hF,so)VK1.H9>2 o˸@p3'I6#9/=%A!ۜgcZ KҬ~m^$J^KʒyH+ɐ (6%l+ra$Ǭ'@;&LJ/ڕ+sgB cAzȷ^\M\'|ՈeyD[+&wu>%r+砌ϡj<E`5X:aGN p5ǥ.4GD U<6НX>H8WCŢGDo:TslyOmsMfZ3U'G w͢S)G$vXGJ m1vgt˜id|fb+gprNsNwjwLgo!4m8z*̢{iZypt})GX Ѐ p֓fM%y3ť滑pnt;8On2EYrNqf(ULPo2l qi#X 'AC ba3h;ZYvomL>_xV0AVLs7sJgt# ڏb:Y=sӪsSJf&:*UWү([P$sZ8(}s8yr~,r5Ebbgd[m0K(b3E\MrBDl㪡m cq{L bL"mmLg[}/&7b ^R(@)Wqf/"m3 12ct}?.xltJZ6r*,XP˝PW9$KEX񣨉nv8. wys3W݂zʐV7*(ټߏqFS).ćT1 >qϏ3 >!$di?Ti|֏d9?> xH6IHKCBȥU5$Vibh<L;fXXˆsRD&Zq GsfZh&Ey]R5D1hri'*qMb\ ])1ʠ):E#2i2iˆ28וY:U d 2svy!Vf,nfWee3Jt\Sxo, o- Ju8͕^ E1T)­{^Б#SM9w5ܗj)~՝BXҔE AD`s;[`ےP:Mw {*VL82qT>f{+ ɵl Vs Da_'A'<l!6Tl+NZ޺<fkE+5C[XC[yc}h;S,Aq kہtYn@3nEYM(R)09JwtȳJΛΧᲥy;;/'+hho;TtI10j&7=e,jCK'K,EfShA2v%v+ ~:?Z}[]yDnG=}sqɭhK}`dUxfl̮>j`$\PD2a]hb(-4w&m#x]x*渐_~E3'5' @g rD \G9Iu[ɾwvٻy,1^zqe+{W iO0k+6f&p'C/IK c:MyOG #1&m=W@@~h N/r\1 k6f ,RTN9ZvY Ӻ#ĐՁQ6y݅! ,c\GPK  ;BO6 "org/gradle/wrapper/Install$1.classVwUfҤ lYTImJ  PJW@T#΄ɤnq_ׂ  꽙Kwޛ?5X:tsM/7}߇~~<,9̍M?7:CHCg˃0aB+a#U inf ?F8xwld8>><ó!ͨ5fԓ;tC = i݊Yuzim2{lcȔNʝ=UZ`qԲ dRڑ63hzuSw6 J)NTlߺW@in`~*ʮjPaPLS-JI,-v W cp9pez˔2jM 1sdTIr@̂S` b[#`#ɼ5&S,$4sD62;Ut Ss6n0eeҶ-M'Bb"ŶvLΫ6p3n&HgJoUWMU4%BS$peؖ֍UWT(K 1oxU,{ xx}x_6̧pklԇ3*>*>>|s|K|k|÷*;ΩwQ:YҐPP$-{~OLeƱhgØWFȮ..UT9XR2akd@|V3 EOv۷X=@3Α'yVB:rGvmP[UۤS<"W dw,W$[YtXs6ڟ#;4gb2Ej|qc2Vq9-]#)G1+M?aAIm Jc\*JsPV<36zk%+?i7K. M!Ǎy-9Y|io3ۦKho= +_P Pߐ 4XGiԊ2x_^y " W+/{Vp/dBZ5;^ D;l7ᕓh/,5\F; dPYRDROGdeQ, `0gUce |Y(MGq$ ]RZ"M%8YV &Cɦ[-4%4*vܾ͟6:̕aDoͱcG7U8WmxJ|Cʝ~@iW\GJ=׭E3w-m/tuJ8TKث ~ޔq:T0HτV&]c0i{l_[<#GY&~*XGZ[{Do [dS?]x&$ꆼK,h>V[nTdsr%T3c23%G1T*Ƣ&q.{*zED{(E=?`rj$*\2c1ldu}o{$ҏ{rZnOHy>'s݂NY-PK  ;BI}#8z !org/gradle/wrapper/Download.classVi{~G4 ۲qq{!m rB P+v*hKҵ-fK7%MKڤYJt/ S$}/ m;%衖;s2{~CDCXÒaYC^y\PQСÂ=eU\ц:TYWt|W𢎯!3ox 2aT.#"%hW5|O5@5G:~U'*TFʲ3Y0]W W\OO;vI8^^]r- gMvDOϞzfv*>7y|n%`ۤmiyf,Zd Z'=N]AY*m͘E 6}޼hLki,9yki†mqP~2o /Ws6L ̅ΚyuUDa״,-9f .9LE8cGKV6sLm-ʎ fKBFrUZMUSc᧩#\ƎnJ sVwt}rVU)eO)lq*atM{ǰޅ ̆jSlzH MZ՞ Z`:Nws /iD MIC ケ[u6:~ %N [iĸAL\ѪYuf쒓ȑʠilvE) DpgfGxsbA'dqd<&{'!$W&(C7}Oq솼f^D<>{)]7m)L ~] w+=DWfhwNj>AC? quj8wm>?84ca|!Zmbr?{ivz*xH\k`ޓU;-wF*x:FS*q㣛|rjHYxkd>R`g"O gHo %WDU"ef;ݢl֒>MYQ c>3G1|YE)" ]2PnZ[) wy }VkFf᭖濩Xأgb{8&퓏p_>X]ZҙP:ҙp<Ψq5цәBox̣`>6$DU_8'Ϙ:9k} 8 Yi}Iy07uC@F>PK  ;BU HPQO#gradle-wrapper-classpath.propertiesSO)IUHIM,R(Up,(R00Q002222Vpv Q0204*(JM.)M/JLIM**+MPK :B$xbuild-receipt.propertiesmOn0 +Yrd{В-@EׂGA$z*IȻ#uuw<~U kELޕU($L\ Lzv$|iQ\c.<*ptI`HcZ|ET`/LŠz:R8P\? J*]ƴa"k74EI25&Ej[xP , '[|?Ac^PK :Borg/gradle/cli/PK :B`1org/gradle/cli/AbstractCommandLineConverter.classUmOA~^_8ˋXBR@E !I*iHxJW?o/%2N(3;733_ Qd`U(ָε <(EsHɴLJgV]gitd4q&{Fdm [70 ;cj6UΓSvt0}9|7ITVe\ C:]9VOBCL]X T 扠jʉjuaqPCA>ۡc!ʯhcv[ aNǦCP ArQbpq +d<q tWאX1reZu6]{ob|S):6\/\5ǫ'8R׍[/G8"Gd0LVǰ4Dy~>4u 9 8]Ώ!|7Rߠ} [CL!w 轔AEk$!ɸUFJFq ]ܦ=p%AMSOwF2DrF>\ Vu!)l."=q@(Pk3HRE,piWu.|9F0/?fxCDlnmMJ]k'iu#0BWՔ!f,By@wZ͕t!BI]#HI9|g|{ -|PK :B <org/gradle/cli/CommandLineParser$MissingOptionArgState.classkO`6d ^ed D%Ɖ&3gj|!~?pլ+^djrk;ØΤt'k #@wYR2Mx;y(<6; Q2 i[AA-lq*ud>8[t< =ߎ6 5>e1"0{f])efAC(2A Ұ;.ᲆuLAa4-fX:}fa 0RY ;¨~+ZM]x A, Jr$) iOH3U`?'5B (yao2gl209K:BBطO\]Qp}mIyF) ?mc8߆ne.⤤! C0SVW41k!jCTξ 8-0 pfCC ;?L]/+yAS?2& ae\%}$PK :B =org/gradle/cli/CommandLineParser$OptionStringComparator.classTAOAf;eam"TE+( ,"ibBlCLʒenN<؋H41IF7۵ yooy/>(⑁0ԢcVGǜZL*McAGor`!fwW2 U-G>kHر)2\ukQ{ϐ< , <˩W܃C#-|_nzu][52 { ]|eW*3pGMs}q$L[8u,wFf6V+*vzD\/ {kpξΈj#ʩ )Ȁ\/%d0e4(,jkTr^M>ԭ;ͩC $L!t,3<ɱ"|:1:N&04C2o Zb1tS^S7D0 4WrhJX $KӂhOWO"y8?E߂ x(?A\kfN{8o~n|Ca}Gc@Nc(jJ -Fw7BIFJ-Au"?Llr6z~ov,IfIʍCK)&+dHN]t .Yp?Oa:9\#;F1L]H c?PK :BFK1org/gradle/cli/CommandLineArgumentException.classJ1O3ZmłbwڪJADA\}: L"\\>,nrO yx}p 2aEE9炧=OQzTqvK9fU v#tiˀFCb!ė*BE{2ȅ 9`)K,Ihh\x &J>p1YITةQcBW4e"1[\r fz:iw /~EǒUwueԘUi}RUnvxO؞nڷYaTņikPK :BlZ5=org/gradle/cli/CommandLineParser$KnownOptionParserState.classX[|WdY&M h;$ˆFCJ%mH@bf`3K[b۴zE-*fCH)h5j??|}3Mv&}wn؎ɴŐщc!'㼘Xy<0\   T 2/+ _ 2v2|o[0'j$i |IH<_0qq+xE«'XiװA6̤@ m{MS{R@ϲ%Rz,2b=f& S?َn7%zRk %H<B"r\%}@ -WNhkb}ݚA#ijn& Kf&c;f8 c5Lo*J/|yCΘC+AֺQ3㣺}XMIϊk!6xZ1 j7wW4)Vn;MW`_SW*a-/RN`^~K:V-c]Rwh΂Mz!tJgTy!+Pa4l(^W5RҞ3<, "_1!屷{A[qt򲃗_rѠ]"vv)wb?C _p^mk >n;pba8"вN2\^ڸiT媮v\W5Sx;Uą=Isc?{MUOdi:a$eeDK8! J 8"HxSV?h8 P Hv͙)ڊ0cDfBgs/9[J{`މ#Vk-sf<: mshpl4genYDZ} ʂh}l`E.v%S\z&4y:5beL9ouqWq zNSt+0]ƽ8 Q ʲ ]mimWͰ'ဏ5S z Z7 FQt/$$:Քg]!i}96M*߭I|?$PK :B|7org/gradle/cli/CommandLineParser$OptionComparator.classUmOP~Q:oSD6A("8,.`2C2Yҵ?_b`ʆ4V7I3P9߇J{o #ϰ]?ͳdYS45 XRsSWY<qug* Vz Tqھ\`vhu{ cM15T<ǰjj`#H6b?ԫڮᧈ}Lڸ .2kWm&@Q43ZQa*˰ *véoL[9N-#~ϰ)ba-d2 ?+⹌"^PW^-fdȢ%-c=XB^2GeF =.TaSţۂJWӽ  j.5w X[&)`PsN ;ť ]/ao4<}u-G)\!1u'bwW"Ym ,A)ޱ3$>LjM8GgGSH'h Ȳ_~ԥ9ʥ;dHqc䛥$tӽ& "55}zN!txlKaș/MKQHJk,뜳Lh&0G8>~ :[NPST eH'IЏl~P V0PK :Bသyn?org/gradle/cli/CommandLineParser$UnknownOptionParserState.classUKOQӖ@"C D AK;G3d:UŅ$<nM7nwϝNJ.ܜs9~I&v%څk6CX߃CRdSi?Bff_ǾiU ą_+%% Q,Wgɋ,)vODD7W 1 sRys,H*xtNUj/q,R"Z#JN>dT 8UWRJk$}Ʋ`;Gzg9bhs i_g0eI~"qB Y*/GB,?PK :BI &org/gradle/cli/CommandLineOption.classV[WW1bTKT0$bl *\PV42d⥗o/}]u>е'C3$3i}H9>{>+_.c?fUс.|nc9@ |Eஊe{qO}.g쁜k%B E1|!C%* Uq \U;PjaWЕ7JeݩB݉˥\ޱriOh]C;}Y^2ewkl"#O&Z T^AZ[)XkkzȌآ%.3FP[aL. Fp.(Oa+]TB7b"'9X\mC`vGZܢehY5X3]ʕlh\4ruy ^ /g5=a:4#ֹp淼3=ppk͵Uzɳ<e|x7}޽uw#kz`3Ϥo@m@+~7FmQ7}fE!CMRdc(m5K\b _J|ق\\BjvAL}='$O x^n7^]e4;*`bH Ti(RQiA*V*xG:cTDh8㉆9{-o份sf[_'Hϯ<ǭewc2X[W٨+Q36S(}D̉'5Rvf)pw["G(JDJEHs-(U '+;XcLQ[ W8 X懽u8"n"uD+( d~ƎuDPDL3Ke~Adl,篗q~~GЏ^ O8I^ 1f{ NBI?Nk g8;sW2gpܭ AvxA>`<).YFdNWfF-Lha ' Rr[ :y/2[^fι}FU\bC͗Lk3d|-+ \ztv#Mn`R6Gɤ;Iy0К;l`6OV5Ƣ"R*w8m?8org/gradle/cli/CommandLineParser$OptionParserState.classR]KA=YvMMw)F)n_$"h@ <6IfVf'%OO}*U XHp=\g#M.6='dCڈ*"a0V'k&a?ljԊ+.]JN * GӯmQtiȯӸP< Ci-va,;- $1 O ̦$|87"Ga!E|Jq )m`+cBg,-^Fil=oP{P.Cg sm4Yptd^pc)T莎7l*S *b#VrGl4vM8S4N*h gLXSIñWyarէ!:yk;\˒> Kg=Awުضj׮;!f5*؏:3Ԡ\ {pEIRhQ`K'b\kfF:׿\mڮ:s!Ken8]Bfюyv}YlY`wֱ 6,/9(H@ޣUk*^~!{\T&?O k(K7B״ؙsL<GpTǟ߅UJ<6aEa=⒊8+>PQEpœcaJoKΛg4[\bIqI?0a A nhO)DMbJHINǡ:YMt5Ms]uoy&0YY0㾀 ;N;Q.rj9]r KA )fGs~6x>?V,2L7k QP,]8g|7Tj^5 B1J1v*8Ŷ+v^Gu .(u >F tfPdUt]GBP4BbIRcfgK7?x_"o l/$z ݇X<^ Ń- Q0 c.P P}%{x&#v-!A8~Q-z+{"%B^/) SkJ_gPK :BA5l| :org/gradle/cli/ProjectPropertiesCommandLineConverter.classKO@D|?Pâu#Q+$C;1m  JW&.(1D,9vo/[@yl汕G)v }FHWkwLS!]nY7ZK:̿cJDZRysV;H+-)nkS#cruLXgh|BjFYDΏ%L%񎅎*_?ֈ:("<ڄbJՍ ؊tf^*K ߵ XUVi01k p8wZ8T0g?PaΛm=C Ss | 1\Zq-}C_JEˉjE+ w'PK :B1 PForg/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classS]oA=|lEg+ZꋁĄ Mifwi+^|gQβ`01w{sg @ S $ uTu<ԱːlHGMXto֑x9w{6E#I/ڎ/_TtO:Vp`Gx-׳L[}[qg/:wtb瘟reNHM9I ]̎Xޱb'IMɀni󵺚E_ ]i9<+%8G|MJuݱ/nnܮJ2%{)He`:1<1c6İ#j1Gm[ UasR]I>7ʢ0MD;v&Y>ޓ!MkRW\&ߘ,3! !dbͭwHlM̘ !ɾ#~`?C43bU&ra, \u2Ϩ捨bg$?Cr)pV"Hc<-R"?!EJJ䦀ay ! +a^A~,2+PK :BV"&&org/gradle/cli/CommandLineParser.classZ{`Tՙ}ܙ @A($!!!G( jD &s&3q٪U>JbϮUkխnkݶsds9^~;,!DKjM5hx7+~Z̿؆5Fa?i.G ᄠLDJTSOǍJ@4 'L5cL 45Sj"(3 J&t}JS4%^T4U )US:V,ФZG,ԱZ>V&FZɗФN:6*6פAǹjq4e)= bJ3Us.+Ide.UAid(kV.(w&tRۄ~%ve.9UMA9Gpn9O6|ʥ]H&lmقY[RD3fL%lo3mksܶv4g ݹ.O쏻IJv̔ExI.LTGmdD[Z{SVҝ5D0ә9'SԫߌG`՛HZl:u^̛NLK7 eg1hX-uFfj0I}7寶132X%kK65[ks%GX!y+k ig)G\D3=4YϨVtnfw̢-x4VPZSKKK:%$zإf2ӓ(8ClsdJN j욝^-$O `Dtʄe59Tpr Gy ΚE@.+a73hTP_D(h&ҲKMš$Z|ɉHtBV1 x}@6=\%[xn1DL$#V2O?V1{=3494["*k 1A#-F![TRmVL+։:BO~3Is2Ӥg+-Ǩ ~`A' OB /}f*_ |{QsKMD,f8p$gQ{(qVt]$hKɰN"8I&w-xŵL& ΢'==mWXTfܮ\r`Εj癓YTDdXR,UVMael!{띉d+$X,Q ܁; n-n9[S)2]txQvd{Ts/q3472rAD\ęIɕ\%R[5= o ςQfh+}6x1dĈfH0;K I lPhn9d]lhyŚ! }B^]ΐ!F|ܐk:ViuN._RL)L$ ^)zJqFcB[n I>a'7Cn[ \gmxgx:ysIҲx>e,Ɵ9&wr|:u{H6 æ(T dg$dR&wYϒ*_9 G䐁W䋆|G1䰊-B{a)0/>9GBzR/jxU8,.j )y`@NiNNkjOVOR*N~re$ꇉ_D?9*MM< VyH+gtM:r~`;3kjj~s[=B WxT;3A3fd g:ܐ=mCfge;EEw ;첞DVgp|#%auO.RՍ DvzʎCk 5mǩhury8D 7%J8TOa0}36਷%U(*8{9VV|T1-=D_o ]cU$JGQ!lCeW]`VQ*b఼Fa oqay,8ms| +1a?Ƃ@80j\K|0>p)+IHja-/Un:jM;>l@n;JR/&<9FxfwPjiʛHFK+ c"^>3U9è3RT+:MʐrYӃXIZԬ,,7؉au)U7~ 'KΑq샥 XYԉ'P2F`t'g_a ŕU_wЃG"z?b7~|\ĥ r6 [v"%{O_9!9kQ~ nq[*ww,P?b6|q?&ӨRN3 .6SaQng5HCNzoG9@REɯ`qYiPh7Oi~MO&?R[.*2r(:S+.=m]Rv>ce4)U5ʟGKst7~;ς5a#3k2Mra|A|99=x|E)dzrx^Eُo]xIU"12|35q#G%UNO1$3(G4gr(e6MH?eNk2 uљSAI\^ķ!y ayߡX.e">e^!td>Y=.\]W?ɥo8 x.~-u"5{zpWnW~=v-9KTRvRuJ1KK&k6y l;Գxg sbX RQVY^&_o謯-o;7?}UѓNS}7W~WcchREv;´hiC6M}L_}&\?L /a%~Akm;eQW~xq#t&csQRRzz6LqbՇ[yLawW*T{?J{#{m5ߣd&Cn ~걹ϞK&'mxLB2lY_笰ʞ0}+w\N% wnFqAWO)ܹ~8?5?ZWo`I]@PK :B`ť)3org/gradle/cli/CommandLineParser$AfterOptions.classO@ǿ׍ut  @ 1Q$[j|/!J/e|1Mzsyk@KTLjHc$]Ŵ$f5SQT1ϐ-,ђͺkeV|ȽU[YbHݰdw0$sҰشG|!`ɭr=[1鿴 [/|[ۮf"93H ~*[F+u{/Mm0$WuL2"Pqa%ҤO5lt!w_D[qŚ+T+j2Vqb |A+[^պgEfAVd Mb,x-w4%c\Uq-E\g9R2LetwaD:BÇn&ڊnG\V{-뀏@b!PI=Pd)4ft E7 C~z#A+W֠u6rGǀ@XFq f /a9P ,'NUZAёSC̰>Xh(3$)$};YT+RU3y$UMZ,|4!?`;2|0p!6)aM@3~PK :BQb3org/gradle/cli/CommandLineParser$OptionString.classSNA=,+]˗(U-e)'#  ZlwD qGbw+Bs;s=̷?(a/Ρ1T(©J*a*Q(LYns n)~!pn\ }ۭ3x\rl&:35DūY %۵o8U}[Q2n"Pu- h\Yyͱ̪cnMxsG$nw˼A}nBSaóSnC›m$-"å*bGDeV5{]g捌БtuwiBI#ScW)ػ04l,k*KX|۲M%mF^5mM}Ep~@J՜aʪFy[3tӑrrlSӳd1]PmE˩sV\iCѲ GVA[SLpq"ZNadm[=ϺtQBlEOx -+yhMiY]^z9"T3D@TlP=\Wru7UrB5ʚOVJPउeҬy1Hi]w='-j]^&:izk/I4u ]ys6F}DQ:H3x6Ǜ-ܗc+ce MD<þa>à{ bfynS[DA(8ħ2>Cn  e|y(.1!Be'*Kk]e7}U+aE{޴6S%wHSiUMO=aߞuF^8!Y.j[i4,!2m.=35=WwbN@v"Rdi ?9h4  {j(Fn"cO~Զ8np^9a.fNd'92MX Ԧ@BHXвXBpٮ!g9jy@[Ӌ`A՛qZ-A~$G O T`UTAcɠjCq?Q-$9Jpa"u:w]B%tMa#pW%ݩ~rt~]?D'aq"|bSt}pshtWi6@omLaiM'-\ E8[knfir9xs{G D68q"𦛈%Icѧ)*%̻%,`υy 8r_PK :B ,org/gradle/cli/ParsedCommandLineOption.classS[OA-"\A(-ʊP1!iĤoC;Ylw- &JD}G.I|9|93|`+)dPPQL!B0冊I,-,&q[~器+䲤⾊ {lqWAW-+ b^ f~,Cz2$J^YA4_T[Wjls6)5fn2Gsy;+c&k_2U`V]Rm4=a[T.ipoSP0/DND=t=Z+jpS0? KOrbjrjd0ajWKp &N&7YָE4Wl/xY92,BS*jx<Iy2=4yt ZcݦVrjxR]#Xя :naLŊXUc ˁ[40ӕk+!ܿ{ `^Rku{2؉]gڃ7en5,SKfΫUu3ˋtqp咾/Wgۥv~t0N7Nɤ\z d/W$Dl27{c_"ɯR Cc#HQ Q,E9!; F8/cg^#_"/P>"k5iDpg }-T!a#˾!#ғ<#}W$K#&;t m݂VM#ds˰"2Y"F6O7" PK :B$R<Y)org/gradle/cli/CommandLineConverter.class]K0Of5ZVP -gm m*Y:m^QbB).yO'?_p 6ps>'B"p`K1޳$OR⻵dJb#=|Cl8B X 2 kP Ʃ?(RY#vjMcb;'IJ8e+3,< _yF<58ٰQUk]5wFΖ5>qa^Ex#w)1'm QU1f1H9<\6}S&bQ!l!4}`R$YuHsW-֠pæ0I1G3wtK=.>!Iz&KIIkZPK :BI2org/gradle/cli/CommandLineParser$ParserState.classSn@}q4 @A$MSn(HHQ6rEO\āvZ&R%{fvg޼/pXvvUϱcnAȾd!dىaHrԱ臼RFxhg(SXļX :=lu#廾savHȡUۄH|{""IXw'Bn/VۍSB._d5˙`#-O17BBv 0 J Pm *CC7˙0ނYI`6Ҕ& ~M]3ͬ4ŌRȢ|2)|֘Ț)X|,q@B◝ٙ?߾⹈VʹDDAI6kEH/Ex,8CF+" )yST9[䄱蹔+YTYOjfM36bk Sڸ`yg+/L$U k}I2t_[̢)A2H R,d5Vi87{lyJj*#ah1UK1C;W"W "=/ZcڞW`nUї3l{r,tIvַwlbfy)vxQ8\"|e!P9q1jRUa3#+|hTBs%,COl0Lt䥾)%]M%nrǖvk^XbΚrj|ZKaBmUOL΍]TOTM1|>r]EgcDudjPNgXZA^R8!ΫC_Hg6Ԭux\$`FՉޟ6^.LͻXר~yiԅσ˷{||熋7 }+ .tul l ^6.uqD2[tM?W4˃??"MSy:;w=&4xl;r Yl :]G p8J4O v8F~#pl.hG͹vVm!\#:ChW7InG&:S+DiQx\X$PK :BGEgradle-cli-classpath.propertiesSO)IUHIM,R(Up,(R00Q002221Vpv Q0204*(JM.)**+MPK  ;B AMETA-INF/PK  ;BӸNh)META-INF/MANIFEST.MFPK  ;BAorg/PK  ;B Aorg/gradle/PK  ;BAorg/gradle/wrapper/PK  ;Bhdf#+org/gradle/wrapper/Download$1.classPK  ;B ΀pDorg/gradle/wrapper/Download$SystemPropertiesProxyAuthenticator.classPK  ;BXs"org/gradle/wrapper/IDownload.classPK  ;BHE*P 3qorg/gradle/wrapper/ExclusiveFileAccessManager.classPK  ;Bc`- org/gradle/wrapper/WrapperConfiguration.classPK  ;B! 0rorg/gradle/wrapper/SystemPropertiesHandler.classPK  ;B+Nm q&org/gradle/wrapper/PathAssembler.classPK  ;B(/ org/gradle/wrapper/Install.classPK  ;BG -4*org/gradle/wrapper/BootstrapMainStarter.classPK  ;Bf[A (A/org/gradle/wrapper/WrapperExecutor.classPK  ;B{w`~% *9org/gradle/wrapper/GradleWrapperMain.classPK  ;BO6 "5Dorg/gradle/wrapper/Install$1.classPK  ;BV8Jorg/gradle/wrapper/PathAssembler$LocalDistribution.classPK  ;BI}#8z !Lorg/gradle/wrapper/Download.classPK  ;BU HPQO# Tgradle-wrapper-classpath.propertiesPK :B$xTbuild-receipt.propertiesPK :BAUorg/gradle/cli/PK :B`1Uorg/gradle/cli/AbstractCommandLineConverter.classPK :B2_e(Yorg/gradle/cli/CommandLineParser$1.classPK :B <Yorg/gradle/cli/CommandLineParser$MissingOptionArgState.classPK :B =\org/gradle/cli/CommandLineParser$OptionStringComparator.classPK :BFK1_org/gradle/cli/CommandLineArgumentException.classPK :BlZ5=kaorg/gradle/cli/CommandLineParser$KnownOptionParserState.classPK :B|7Jiorg/gradle/cli/CommandLineParser$OptionComparator.classPK :Bသyn?rlorg/gradle/cli/CommandLineParser$UnknownOptionParserState.classPK :BI &oorg/gradle/cli/CommandLineOption.classPK :B>8torg/gradle/cli/CommandLineParser$OptionParserState.classPK :B&vorg/gradle/cli/ParsedCommandLine.classPK :BA5l| : ~org/gradle/cli/ProjectPropertiesCommandLineConverter.classPK :B1 PForg/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classPK :BV"&&org/gradle/cli/CommandLineParser.classPK :B`ť)3org/gradle/cli/CommandLineParser$AfterOptions.classPK :BQb3yorg/gradle/cli/CommandLineParser$OptionString.classPK :BE#! ;aorg/gradle/cli/AbstractPropertiesCommandLineConverter.classPK :B ,korg/gradle/cli/ParsedCommandLineOption.classPK :B7Ɋ=qorg/gradle/cli/CommandLineParser$OptionAwareParserState.classPK :B$R<Y)eorg/gradle/cli/CommandLineConverter.classPK :B@ͤ1| <org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classPK :B;|9+org/gradle/cli/SystemPropertiesCommandLineConverter.classPK :BI2org/gradle/cli/CommandLineParser$ParserState.classPK :B67< ;Qorg/gradle/cli/CommandLineParser$AfterFirstSubCommand.classPK :BGEgradle-cli-classpath.propertiesPK//hjQtAV-1.12.0/examples/QMLPlayer/android/gradle/wrapper/gradle-wrapper.properties000066400000000000000000000003461312235004300274160ustar00rootroot00000000000000#Wed Apr 10 15:27:10 PDT 2013 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip QtAV-1.12.0/examples/QMLPlayer/android/gradlew000077500000000000000000000117301312235004300210200ustar00rootroot00000000000000#!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" QtAV-1.12.0/examples/QMLPlayer/android/gradlew.bat000066400000000000000000000044121312235004300215610ustar00rootroot00000000000000@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega QtAV-1.12.0/examples/QMLPlayer/android/res/000077500000000000000000000000001312235004300202345ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-hdpi/000077500000000000000000000000001312235004300227375ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-hdpi/icon.png000066400000000000000000000046211312235004300244000ustar00rootroot00000000000000PNG  IHDRHHUGbKGD pHYs B(xtIME - F, IDATxmPTwwA`L$jTXĦ2F^,htԷ!3qVƎ;NL&iNL&m5JIGҨH{a{]شxs=<9< P@Ezˆ$p`;=XhTY/!D0Lw\@":jsZa&"ˀySH;{y[?\@$~ $ vJ*DLr(F =Z̜o@zpZ>a:`p ;boU FɤTJVl֐c`7ՄUbD u(y?&&kS>E^DKtSik/Dgz9$d;a ((FQ'+JQ*vA-nLc'9rW_}21]; iJIIC8Yf3,7TW23LƇg%!1cD!#k;!pL1thDE]tMLd%.(xm$Fu*BѶ%'x!Y^M(M?DٖJ I./,f1o6ԦlDxq;`k 8әiNݲpWmu5p>CNrWxE}H҃KCf(FY'|l.9[bZgjΉg 8y )Ĉѭ[;{6'Ul)B'N`|LfH%US}ds \6RR2wL,d'jp!lmN]'U >L0YO ɛԓF>'UڏZ>U8,[B4_[ߴޔM'(gFXE+x+`I*pxVgE`()5M۹ g8F=1{u6~ۛ˭rF􊽬4=zVF% MG!k/&ٜ//sE9\CdGcw#FYQ\8aBaB[mT*o.<`N=$_s^y'KmygfPQ;pj;jɬ˔w58J&bnk'.!SQB]veE-w H-~&SyrP{d.\>t#J-.zX-_͗A&heݕudenST-" ߝ*aH؁9u8beJtNnD1TA0 79͉C|2b2(QWI7EԴո$?'Yǀj>zh=lf pT}RNszӝWn&ӳ+#< HC$S"%d Hu􊽴ڹg:5"7?Fl{ly6afֿ"x|A -X1#5:!t zX;'I"!׺$ A1HPto D+yrrI o6ۋj@.>{_34t3tODMT RNg|Oͨ&^f' R 4\GDC,eH\@P@7XM#г;IENDB`QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-ldpi/000077500000000000000000000000001312235004300227435ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-ldpi/icon.png000066400000000000000000000024051312235004300244020ustar00rootroot00000000000000PNG  IHDR$$bKGD pHYs B(xtIME /*IDATXoHg?ɪԍm"vL:Q}1]Ah+!R2Zݠ lƺtLZ[+ :&V(?%&1Ff {{KN_ډEC-$"TOQUnRI** c߰SMv-H|'dO A D"06njmg#8CQ[  P'~+"4%dNmfYhm~B Aw b|C\,B Ul<$v5ra2.EIH=dϳ.^/;v4V@ Kj'5lşsZI% (RI$TRc2a UPd,! IGŸ)|㵊}+CU'bNJ 4D"TQ +^OƞTl|PoYj*WzES/i'= "E #!(aۗׄ'LrSY| 2 萚MݟmIR̘q4J$prVw5≧lI:tQukj6Lkm<4u <T,&(pFH!e]n^3+X?wϦD 3e=zp *%i*[]U qF ɧ:w3߃,duS&dFumI$Zi冚}Rnlj H!S=MWE"g'@-]-4yaƵh9i)-;ٶ^G' Tzh:]]5MyL,>|J;w85zc)_ {\'/rd J@^RzSXPguyA v7[9duud daKQaC6Z5EQ]BNxM#-ΛhyH6TWl=u9F%X̴m иKsY;pn5^MzeC@# ddlH~ ;/ߏli{H /0 ;c}'23`FIENDB`QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-mdpi/000077500000000000000000000000001312235004300227445ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/res/drawable-mdpi/icon.png000066400000000000000000000033611312235004300244050ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYs B(xtIME jbI~IDAThmlUϳ[t) Fn"00AF4`  C 2Bx /с &/K7Ag]η{sx WZG2@2 HQ'U@9ns2AeO+ ]O(p0 a kGQ%>2Xm,v:GmXoYQPDh~Dbs#@p6Yf14FŌB+ižcƁތ[ m@l /QCSד\^J>k2L$:` 3=uV&$F%gfEEk]F2x#4dj:$oafa-f~$>|1f#sL:Kq09'ٿe$`Wp/ C Kѡ0 t&;6A F.-,Q\t)CmCp 8Y/+&$TD`UҪV O$9Hc$ql~bj fR#-,H92pWZҟ䑇 WP$&N ͐{h$fz4C.q,`#w= D"Z=;~6RDHƂq#Lhwȅ M}djBeE f0YL3H$&M S|X EjH!am L2b4xAJbcI 6)̵f49 ᜔n);LL>a)a YdqWUCUP.F0J*RUH]L'(QЉ\SHa k0!Z힪H_a&2~|AǂE|yulmD@D#i\,mƀ3gԗ%>8t*l'12X{8pH:'&/Jama3b) sn !&0zWaoݎebX,X0a j bBDul!#`>󩢊rТ \^\>Fvd2z[ր6O%RJ#Xb;tr.w 5q1ePP!a%~Sz1`):|K =0*F 7&Y,ޕJCyI'a;U⒲;6S&g/yr[@#n>&kn\U.¡8\9ƜâwxU9 lꋫI=JYKYH݊W62m9+|h;sQ4̉Cnb.}ơnK`ifE0ݱApr T(K F2$͐ƠAd]M'_WwRV nCXGf 1[BTmOlk]ވT28T uM50tqZ%n& B_N@wލϑ(|# H`(E.$yE(CeA3!)fx  ưW}p ؏oP \cR/S &r@tIIENDB`QtAV-1.12.0/examples/QMLPlayer/android/res/values/000077500000000000000000000000001312235004300215335ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/res/values/libs.xml000066400000000000000000000012251312235004300232060ustar00rootroot00000000000000 https://download.qt-project.org/ministro/android/qt5/qt-5.4 QtAV-1.12.0/examples/QMLPlayer/android/src/000077500000000000000000000000001312235004300202325ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/src/org/000077500000000000000000000000001312235004300210215ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/src/org/qtav/000077500000000000000000000000001312235004300217745ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/src/org/qtav/qmlplayer/000077500000000000000000000000001312235004300240025ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/android/src/org/qtav/qmlplayer/QMLPlayerActivity.java000066400000000000000000000024501312235004300301710ustar00rootroot00000000000000package org.qtav.qmlplayer; import org.qtproject.qt5.android.bindings.QtActivity; import android.content.Context; import android.content.Intent; import android.app.PendingIntent; import android.util.Log; import android.os.Bundle; import android.view.WindowManager; import android.content.pm.ActivityInfo; public class QMLPlayerActivity extends QtActivity { private final static String TAG = "QMLPlayer"; private static String m_request_url; private static QMLPlayerActivity m_instance; public QMLPlayerActivity() { m_instance = this; } public static String getUrl() { return m_instance.m_request_url; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); Intent intent = getIntent(); String action = intent.getAction(); Log.i("QMLPlayerActivity", "Action: " + action); Log.i("QMLPlayerActivity", "Data: " + intent.getDataString()); if (intent.ACTION_VIEW.equals(action)) { m_request_url = intent.getDataString(); } setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } QtAV-1.12.0/examples/QMLPlayer/i18n/000077500000000000000000000000001312235004300166025ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/i18n/QMLPlayer.ts000066400000000000000000000531741312235004300207720ustar00rootroot00000000000000 About About based on QtAV is a cross platform, high performance multimedia framework Home page Install QtAV for Windows desktop/store, macOS, Linux and Android Double click show/hide control bar Click right area show config panel Open a subtitle press open button to select a subtitle, or a video + a subtitle a URL press and hold open button Shortcut Mute Fullscreen Up/Down Volume Left/Right Swipe Seek backward/forward Space Pause/Resume Quit Rotate Aspect ratio Donate AudioPage Audio Channel layout Audio track External Click here to select a file Stereo Mono Left Right Open an external audio track ConfigPanel Media info Video codec Effect Subtitle Audio Misc About EffectPage Effect Brightness Contrast Hue Saturation Reset MediaInfoPage Media Information Duration: Size: Title Album Comment Year Date Author Publisher Genre Track Track count Video Resolution Bit rate Codec Pixel format Frames Frame rate Audio Sample format Sample rate Channels MiscPage Misc Reset all Preview Press on the preview item to seek Requirement Restart to apply Developer only Language PlayListPanel History SubtitlePage Subtitle Delay ms Engine Supported formats Bottom margin Font Outline Font file Force Clear Fonts dir Embedded Subtitles External Subtitle Enabled Auto load Choose a font file Choose a fonts dir VideoCodec Video Codec Takes effect on the next play hardware decoding software decoding Zero Copy support Zero copy main Subtitle Open a URL QtAV-1.12.0/examples/QMLPlayer/i18n/QMLPlayer_zh_CN.qm000066400000000000000000000143571312235004300220420ustar00rootroot00000000000000pX XpZgZe[ % {[ \cZl+f< g5 zwYT *s3<6sc@. E_#sP% w(Wxd Lb NHˤˤ RE؃ fEuW  Mȿ(b<59$ Ib[ %g;wWRWM0M$ϗ,N^]ax@ X s 0r ɴ- @ d<  b KI }>  / 8 >P I\ ) %Y  3 Fn G 8  3  SP  3o?:N+pS iTre7i]QsNAboutAboutkO Aspect ratioAbout pQSS:WClick right areaAboutcP`DonateAboutSQ Double clickAboutQh\O FullscreenAboutN;u Home pageAboutXN:Windows hLb/UF^, macOS, Linux T Android[QtAV@Install QtAV for Windows desktop/store, macOS, Linux and AndroidAbout]/S Left/RightAboutYMuteAboutbS_OpenAbout fP\/`bY  Pause/ResumeAbout&QtAV f/^sS0`'vYZOShFg?QtAV is a cross platform, high performance multimedia frameworkAboutQQuitAbout eˏl90^RotateAbout__Seek backward/forwardAbout_cw.ShortcutAboutzzh<SpaceAboutc%RSwipeAboutN /N Up/DownAboutVolumeAboutURLa URLAbout[W^U a subtitleAboutWNbased onAbout c bS_c press and hold open buttonAbout.pQbS_c bNN*[W^U,b bNN*ƘT[W^U?press open button to select a subtitle, or a video + a subtitleAbout f>y:nhshow config panelAboutf>y:/cR6hshow/hide control barAboutAudio AudioPageh Audio track AudioPageXSChannel layout AudioPagepQkdYN beNClick here to select a file AudioPageYcExternal AudioPage]XSLeft AudioPageSUXS(N-Y.)Mono AudioPage bS_YchOpen an external audio track AudioPageSXSRight AudioPagezOSXStereo AudioPageQsNAbout ConfigPanelAudio ConfigPaneleHgEffect ConfigPanelZOSO`o Media info ConfigPanelgByMisc ConfigPanel[W^USubtitle ConfigPanelƘx Video codec ConfigPanelN^ Brightness EffectPage[k^Contrast EffectPageeHgEffect EffectPagerHue EffectPagenReset EffectPageqT^ Saturation EffectPageNAlbum MediaInfoPageAudio MediaInfoPageO\Author MediaInfoPagexsBit rate MediaInfoPageXSChannels MediaInfoPagexCodec MediaInfoPagelComment MediaInfoPageegDate MediaInfoPagee: Duration:  MediaInfoPage^'s Frame rate MediaInfoPage^'epFrames MediaInfoPageOSGenre MediaInfoPageZOSO`oMedia Information MediaInfoPageP} h<_ Pixel format MediaInfoPageQrH Publisher MediaInfoPageRs Resolution MediaInfoPageh7h<_ Sample format MediaInfoPageh7s Sample rate MediaInfoPageY'\:Size:  MediaInfoPagehTitle MediaInfoPagehTrack MediaInfoPagehep Track count MediaInfoPageƘVideo MediaInfoPage^tNYear MediaInfoPage_Sрu(Developer onlyMiscPageLanguageMiscPagegByMiscMiscPagepQhFSۈLl!Press on the preview item to seekMiscPagePreviewMiscPage_YgaN RequirementMiscPagenb@g  Reset allMiscPageoNT/TueHRestart to applyMiscPagede>SSHistory PlayListPanel PreviewPageRR} Auto load SubtitlePage^y Bottom margin SubtitlePage bNN*[WOSeNChoose a font file SubtitlePage bNN*[WOSv_UChoose a fonts dir SubtitlePagendClear SubtitlePage^eDelay SubtitlePageQ]L[W^UEmbedded Subtitles SubtitlePageT/u(Enabled SubtitlePage_dEngine SubtitlePageYc[W^UExternal Subtitle SubtitlePage[WOSFont SubtitlePage[WOSeN Font file SubtitlePage[WOSv_U Fonts dir SubtitlePage_:R6Force SubtitlePagen^Outline SubtitlePage[W^USubtitle SubtitlePagee/ch<_Supported formats SubtitlePagekyms SubtitlePage N k!de>ueHTakes effect on the next play VideoCodecƘx Video Codec VideoCodec 0 be/cZero Copy support VideoCodec0 b Zero copy VideoCodecxlhardware decoding VideoCodecosoftware decoding VideoCodec bS_URL Open a URLmain[W^USubtitlemainQtAV-1.12.0/examples/QMLPlayer/i18n/QMLPlayer_zh_CN.ts000066400000000000000000000525041312235004300220470ustar00rootroot00000000000000 About About 关于 based on 基于 QtAV is a cross platform, high performance multimedia framework QtAV 是跨平台、高性能的多媒体框架 Home page 主页 Install QtAV for Windows desktop/store, OSX, Linux and Android 给Windows 桌面/商店, OSX, Linux和Android安装QtAV Install QtAV for Windows desktop/store, macOS, Linux and Android 为Windows 桌面/商店, macOS, Linux 和 Android安装QtAV Double click 双击 show/hide control bar 显示/隐藏控制栏 Click right area 点击右边区域 show config panel 显示设置栏 Open 打开 a subtitle 字幕 press open button to select a subtitle, or a video + a subtitle 点击打开按钮选择一个字幕,或选择一个视频和字幕 press and hold open button 长按打开按钮 a URL URL Shortcut 快捷键 Mute 静音 Fullscreen 全屏 Up/Down 上/下 Volume 音量 Left/Right 左/右 Seek backward/forward 快退快进 Swipe 挥动 Space 空格 Pause/Resume 暂停/恢复 Quit 退出 Rotate 旋转90度 Aspect ratio 比例 Donate 捐赠 AudioPage Audio 音频 Channel layout 声道 Audio track 音轨 External 外挂 Click here to select a file 点击此处以选择文件 Stereo 立体声 Mono 单声道(中央) Left 左声道 Right 右声道 Open an external audio track 打开外挂音轨 ConfigPanel Media info 预览 媒体信息 Video codec 视频解码 Effect 效果 Subtitle 字幕 Audio 音频 Misc 杂项 About 关于 EffectPage Effect 效果 Brightness 亮度 Contrast 对比度 Hue 色调 Saturation 饱和度 Reset 重置 MediaInfoPage Media Information 媒体信息 Duration: 时长: Size: 大小: Title 标题 Album 专辑 Comment 注释 Year 年份 Date 日期 Author 作者 Publisher 出版 Genre 体裁 Track 音轨 Track count 音轨数 Video 视频 Resolution 分辨率 Codec 编码 Pixel format 像素格式 Frame rate 帧率 Bit rate 码率 Frames 帧数 Audio 音频 Sample format 采样格式 Sample rate 采样率 Channels 声道 MiscPage Misc 杂项 Reset all 重置所有 Preview 预览 Press on the preview item to seek 点击预览框可进行跳转 Requirement 必备条件 Restart to apply 软件重启后生效 Developer only 开发者用 Language 语言 PlayListPanel History 播放历史 PreviewPage SubtitlePage Subtitle 字幕 Font file 字体文件 Force 强制 Clear 清除 Fonts dir 字体目录 Embedded Subtitles 内嵌字幕 External Subtitle 外挂字幕 Enabled 启用 Auto load 自动加载 Delay 延时 ms 毫秒 Choose a font file 选择一个字体文件 Choose a fonts dir 选择一个字体目录 Supported formats 支持格式 Font 字体 Outline 轮廓 Bottom margin 底部距离 Engine 引擎 VideoCodec Video Codec 视频解码 hardware decoding 硬解 software decoding 软解 Zero Copy support 0 拷贝支持 Zero copy 0 拷贝 Takes effect on the next play 下次播放生效 main Subtitle 字幕 Open a URL 打开URL QtAV-1.12.0/examples/QMLPlayer/ios/000077500000000000000000000000001312235004300166155ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/ios/Info.plist000066400000000000000000000136071312235004300205740ustar00rootroot00000000000000 CFBundleIconFile @ICON@ CFBundlePackageType APPL CFBundleGetInfoString Created by Wang Bin CFBundleSignature ???? CFBundleExecutable QMLPlayer CFBundleIdentifier org.qtav.qmlplayer CFBundleDisplayName ${PRODUCT_NAME} CFBundleName ${PRODUCT_NAME} CFBundleShortVersionString 1.12 CFBundleVersion 1.12.0 LSRequiresIPhoneOS UIFileSharingEnabled UILaunchStoryboardName LaunchScreen UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight NOTE This file was generated by Qt/QMake. CFBundleDocumentTypes CFBundleTypeExtensions AAC AC3 AIFF M4A MKA MP3 OGG PCM VAW WAV WAW WMA aac ac3 aiff m4a mka mp3 ogg pcm vaw wav waw wma CFBundleTypeIconFile document.icns CFBundleTypeName Audio file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions * * 3GP 3IV 3gp 3iv ASF AVI CPK DAT DIVX DV FLAC FLI FLV H264 I263 M2TS M4V MKV MOV MP2 MP4 MPEG MPG MPG2 MPG4 NSV NUT NUV OGG OGM QT RM RMVB VCD VFW VOB WEBM WMV asf avi cpk dat divx dv flac fli flv h264 i263 m2ts m4v mkv mov mp2 mp4 mpeg mpg mpg2 mpg4 mts nsv nut nuv ogg ogm qt rm rmvb vcd vfw vob webm wmv f4v ts CFBundleTypeIconFile document.icns CFBundleTypeName Movie file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions AQT ASS JSS RT SMI SRT SSA SUB TXT UTF aqt ass jss rt smi srt ssa sub txt utf CFBundleTypeIconFile document.icns CFBundleTypeName Subtitles file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLName Streaming Protocol CFBundleURLSchemes mms mmst http httpproxy rtp rtsp ftp udp smb QtAV-1.12.0/examples/QMLPlayer/main.cpp000066400000000000000000000167411312235004300174640ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_ANDROID #include #endif #include "qtquick2applicationviewer.h" #include "../common/ScreenSaver.h" #include "../common/common.h" int main(int argc, char *argv[]) { //call qtav api result in crash at startup QOptions options(get_common_options()); options.add(QLatin1String("QMLPlayer options")) ("scale", 1.0, QLatin1String("scale of graphics context. 0: auto")) ; options.parse(argc, argv); Config::setName(QString::fromLatin1("QMLPlayer")); do_common_options_before_qapp(options); QGuiApplication app(argc, argv); app.setApplicationName(QStringLiteral("QMLPlayer")); app.setApplicationDisplayName(QStringLiteral("QtAV QMLPlayer")); QDir::setCurrent(qApp->applicationDirPath()); qDebug() << "event dispatcher:" << QCoreApplication::eventDispatcher(); ; do_common_options(options); qDebug() << "arguments======= " << app.arguments(); qDebug() << "current dir: " << QDir::currentPath(); set_opengl_backend(options.option(QStringLiteral("gl")).value().toString(), app.arguments().first()); load_qm(QStringList() << QStringLiteral("QMLPlayer"), options.value(QStringLiteral("language")).toString()); QtQuick2ApplicationViewer viewer; QString binDir = qApp->applicationDirPath(); if (binDir.endsWith(QLatin1String(".app/Contents/MacOS"))) { binDir.remove(QLatin1String(".app/Contents/MacOS")); binDir = binDir.left(binDir.lastIndexOf(QLatin1String("/"))); } QQmlEngine *engine = viewer.engine(); if (!engine->importPathList().contains(binDir)) engine->addImportPath(binDir); qDebug() << engine->importPathList(); engine->rootContext()->setContextProperty(QStringLiteral("PlayerConfig"), &Config::instance()); qDebug(">>>>>>>>devicePixelRatio: %f", qApp->devicePixelRatio()); QScreen *sc = app.primaryScreen(); qDebug() << "dpi phy: " << sc->physicalDotsPerInch() << ", logical: " << sc->logicalDotsPerInch() << ", dpr: " << sc->devicePixelRatio() << "; vis rect:" << sc->virtualGeometry(); // define a global var for js and qml engine->rootContext()->setContextProperty(QStringLiteral("screenPixelDensity"), sc->physicalDotsPerInch()*sc->devicePixelRatio()); qreal r = sc->physicalDotsPerInch()/sc->logicalDotsPerInch(); const qreal kR = #if defined(Q_OS_ANDROID) 2.0; #elif defined(Q_OS_WINRT) 1.2; #else 1.0; #endif if (std::isinf(r) || std::isnan(r)) r = kR; float sr = options.value(QStringLiteral("scale")).toFloat(); #if defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) sr = r; #if defined(Q_OS_WINPHONE) sr = kR; #endif if (sr > 2.0) sr = 2.0; //FIXME #endif if (qFuzzyIsNull(sr)) sr = r; engine->rootContext()->setContextProperty(QStringLiteral("scaleRatio"), sr); qDebug() << "touch devices: " << QTouchDevice::devices(); engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), false); #ifdef Q_OS_WINPHONE engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), true); #endif foreach (const QTouchDevice* dev, QTouchDevice::devices()) { if (dev->type() == QTouchDevice::TouchScreen) { engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), true); break; } } QString qml = QStringLiteral("qml/QMLPlayer/main.qml"); if (QFile(qApp->applicationDirPath() + QLatin1String("/") + qml).exists()) qml.prepend(qApp->applicationDirPath() + QLatin1String("/")); else qml.prepend(QLatin1String("qrc:///")); viewer.setMainQmlFile(qml); viewer.show(); QOption op = options.option(QStringLiteral("width")); if (op.isSet()) viewer.setWidth(op.value().toInt()); op = options.option(QStringLiteral("height")); if (op.isSet()) viewer.setHeight(op.value().toInt()); op = options.option(QStringLiteral("x")); if (op.isSet()) viewer.setX(op.value().toInt()); op = options.option(QStringLiteral("y")); if (op.isSet()) viewer.setY(op.value().toInt()); if (options.value(QStringLiteral("fullscreen")).toBool()) viewer.showFullScreen(); viewer.setTitle(QStringLiteral("QMLPlayer based on QtAV. wbsecg1@gmail.com")); /* * find root item, then root.init(argv). so we can deal with argv in qml */ #if 1 QString json = app.arguments().join(QStringLiteral("\",\"")); json.prepend(QLatin1String("[\"")).append(QLatin1String("\"]")); json.replace(QLatin1String("\\"), QLatin1String("/")); //FIXME QMetaObject::invokeMethod(viewer.rootObject(), "init", Q_ARG(QVariant, json)); //#else QObject *player = viewer.rootObject()->findChild(QStringLiteral("player")); AppEventFilter *ae = new AppEventFilter(player, player); qApp->installEventFilter(ae); QString file; #ifdef Q_OS_ANDROID file = QAndroidJniObject::callStaticObjectMethod("org.qtav.qmlplayer.QMLPlayerActivity" , "getUrl" , "()Ljava/lang/String;") .toString(); #endif if (app.arguments().size() > 1) { file = options.value(QStringLiteral("file")).toString(); if (file.isEmpty()) { if (argc > 1 && !app.arguments().last().startsWith(QLatin1Char('-')) && !app.arguments().at(argc-2).startsWith(QLatin1Char('-'))) file = app.arguments().last(); } } qDebug() << "file: " << file; if (player && !file.isEmpty()) { if (!file.startsWith(QLatin1String("file:")) && QFile(file).exists()) file.prepend(QLatin1String("file:")); //qml use url and will add qrc: if no scheme file.replace(QLatin1String("\\"), QLatin1String("/")); //qurl //QMetaObject::invokeMethod(player, "play", Q_ARG(QUrl, QUrl(file))); player->setProperty("source", QUrl(file)); } #endif QObject::connect(&Config::instance(), SIGNAL(changed()), &Config::instance(), SLOT(save())); QObject::connect(viewer.rootObject(), SIGNAL(requestFullScreen()), &viewer, SLOT(showFullScreen())); QObject::connect(viewer.rootObject(), SIGNAL(requestNormalSize()), &viewer, SLOT(showNormal())); ScreenSaver::instance().disable(); //restore in dtor return app.exec(); } QtAV-1.12.0/examples/QMLPlayer/qml/000077500000000000000000000000001312235004300166145ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/000077500000000000000000000000001312235004300204225ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/About.qml000066400000000000000000000060131312235004300222070ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Page { title: qsTr("About") height: Math.min(maxHeight, scroll.contentHeight) Flickable { id: scroll contentHeight: titleHeight + about.contentHeight anchors.fill: content Text { id: about color: "white" linkColor: "orange" wrapMode: Text.Wrap anchors.fill: parent anchors.margins: Utils.kMargin //horizontalAlignment: Text.AlignHCenter font.pixelSize: Utils.scaled(15) onContentHeightChanged: parent.contentHeight = contentHeight + 2*anchors.margins onLinkActivated: Qt.openUrlExternally(link) text: "

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

" + "

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

" + "

Distributed under the terms of LGPLv2.1 or later.

" + "

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

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

" + "

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

" + "\n

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

" + qsTr("Click right area") + ": " + qsTr("show config panel") + "

" + qsTr("Open") + " " + qsTr("a subtitle") + ": " + qsTr("press open button to select a subtitle, or a video + a subtitle") + "

" + qsTr("Open") + " " + qsTr("a URL") + ": " + qsTr("press and hold open button") + "

" + (Qt.platform.os === "android" || Qt.platform.os === "ios" || Qt.platform.os === "winphone" || Qt.platform.os === "blackberry" ? " " : "

" + qsTr("Shortcut") + ":

" + "
M: " + qsTr("Mute") + " . F: " + qsTr("Fullscreen") + " .
" + qsTr("Up/Down") + ": " + qsTr("Volume") + "+/- . " + qsTr("Left/Right") + "/" + qsTr("Swipe") +": " + qsTr("Seek backward/forward") + " .
" + qsTr("Space") + ": " + qsTr("Pause/Resume") + " . Q: " + qsTr("Quit") + " .
" + qsTr("Rotate") + "A: " + qsTr("Aspect ratio") + "
") } Button { id: donateBtn text: qsTr("Donate") bgColor: "#990000ff" anchors.top: about.bottom anchors.topMargin: -Utils.scaled(50) anchors.right: parent.right anchors.rightMargin: Utils.kMargin width: Utils.scaled(60) height: Utils.scaled(40) onClicked: Qt.openUrlExternally("http://www.qtav.org/donate.html") } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/AudioPage.qml000066400000000000000000000105421312235004300227750ustar00rootroot00000000000000import QtQuick 2.0 import QtQuick.Dialogs 1.0 import QtAV 1.6 import "utils.js" as Utils Page { id: root title: qsTr("Audio") signal channelChanged(int channel) signal externalAudioChanged(string file) signal audioTrackChanged(int track) property var internalAudioTracks : "unkown" property var externalAudioTracks : "unkown" property alias isExternal: externalCheck.checked height: Math.min(maxHeight, scroll.contentHeight) Flickable { id: scroll anchors.fill: content contentHeight: titleHeight + channelLabel.height + (channels.visible ? channels.contentHeight : 0) + Utils.kItemHeight + trackLabel.height + tracksMenu.contentHeight + Utils.kSpacing*5 Column { anchors.fill: parent spacing: Utils.kSpacing Text { id: channelLabel color: "white" text: qsTr("Channel layout") font.pixelSize: Utils.kFontSize width: parent.width visible: Qt.platform.os !== "winphone" && Qt.platform.os !== "winrt" } Menu { visible: channelLabel.visible id: channels width: parent.width itemWidth: parent.width model: ListModel { id: channelModel } onClicked: { root.channelChanged(model.get(index).value) } } Text { id: trackLabel color: "white" text: qsTr("Audio track") font.pixelSize: Utils.kFontSize width: parent.width } Menu { id: tracksMenu width: parent.width itemWidth: parent.width model: ListModel { id: tracksModel } onClicked: { console.log("audio track clikced: " + tracksModel.get(index).value) root.audioTrackChanged(tracksModel.get(index).value) } } Row { width: parent.width height: Utils.kItemHeight Button { id: externalCheck checked: false checkable: true text: qsTr("External") width: Utils.scaled(60) height: Utils.kItemHeight onCheckedChanged: { if (checked) root.externalAudioChanged(file.text) else root.externalAudioChanged("") updateTracksMenu() } } Text { id: file color: "orange" font.pixelSize: Utils.kFontSize width: parent.width - externalCheck.width height: parent.height wrapMode: Text.Wrap verticalAlignment: Text.AlignVCenter text: qsTr("Click here to select a file") MouseArea { anchors.fill: parent onClicked: fileDialog.open() } } } } } //Flickable Component.onCompleted: { channelModel.append({name: qsTr("Stereo"), value: MediaPlayer.Stereo }) channelModel.append({name: qsTr("Mono"), value: MediaPlayer.Mono }) channelModel.append({name: qsTr("Left"), value: MediaPlayer.Left }) channelModel.append({name: qsTr("Right"), value: MediaPlayer.Right }) } onInternalAudioTracksChanged: updateTracksMenu() onExternalAudioTracksChanged: updateTracksMenu() function updateTracksMenu() { tracksModel.clear() var c = internalAudioTracks if (isExternal) { c = externalAudioTracks } for (var i = 0; i < c.length; ++i) { var t = c[i] var label = "#" + t.id if (t.codec) label += " '" + t.codec + "'" if (t.language) label += " (" + t.language + ")" if (t.title) label += ": " + t.title tracksModel.append({name: label, value: t.id}) } } FileDialog { id: fileDialog title: qsTr("Open an external audio track") onAccepted: { file.text = fileUrl if (externalCheck.checked) root.externalAudioChanged(fileUrl.toString()) } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/Button.qml000066400000000000000000000060611312235004300224130ustar00rootroot00000000000000/****************************************************************************** Copyright (C) 2013-2016 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ import QtQuick 2.0 Rectangle { id: root property string text property url icon property alias iconChecked: iconChecked.source property bool checkable: false property bool checked: false property color bgColor: "#555555" property color bgColorSelected: "#ee6666dd" property color textColor: "white" property bool hovered: false //mouseArea.containsMouse readonly property alias pressed: mouseArea.pressed signal clicked() signal pressAndHold() opacity: 0.7 color: checked ? bgColorSelected : mouseArea.pressed ? Qt.darker(bgColor) : bgColor border.color: Qt.lighter(color) Text { id: text anchors.fill: parent text: root.text font.pixelSize: 0.5 * parent.height color: textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } Image { source: icon anchors.fill: parent visible: !checked } Image { id: iconChecked anchors.fill: parent visible: checked } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true onClicked: { if (root.checkable) root.checked = !root.checked root.clicked() } onHoveredChanged: { //console.log("button.hover mouseX: " + mouseX) if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff return hovered = mouseArea.containsMouse } onPressAndHold: root.pressAndHold() } states: [ State { name: "brighter" when: hovered // only the first true State is applied, so put scale and opacity together PropertyChanges { target: root; opacity: 1.0; scale: mouseArea.pressed ? 1.06 : 1.0 } } ] transitions: [ Transition { from: "*"; to: "*" PropertyAnimation { properties: "opacity,scale" easing.type: Easing.OutQuart duration: 300 } } ] } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/ConfigPanel.qml000066400000000000000000000040131312235004300233200ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Rectangle { id: root layer.enabled: true property string selected: "" property string selectedUrl property int selectedX: 0 property int selectedY: 0 color: "#80101010" signal clicked Menu { id: menu anchors.fill: parent anchors.topMargin: Utils.scaled(40) itemWidth: root.width - Utils.scaled(2) spacing: Utils.scaled(1) model: ListModel { id: contentModel // ListElement property value can not be script before Qt5.4. translate here(QT_TR_NOOP) and used in other qml fails } onClicked: { selectedX = x selectedY = y selectedUrl = model.get(index).url root.clicked() } } Component.onCompleted: { contentModel.append({name: qsTr("Media info"), url: "MediaInfoPage.qml" }) contentModel.append({name: qsTr("Video codec"), url: "VideoCodec.qml" }) contentModel.append({name: qsTr("Effect"), url: "EffectPage.qml" }) contentModel.append({name: qsTr("Subtitle"), url: "SubtitlePage.qml" }) contentModel.append({name: qsTr("Audio"), url: "AudioPage.qml" }) contentModel.append({name: qsTr("Misc"), url: "MiscPage.qml" }) contentModel.append({name: qsTr("About"), url: "About.qml" }) } states: [ State { name: "show" PropertyChanges { target: root opacity: 0.9 anchors.rightMargin: 0 } }, State { name: "hide" PropertyChanges { target: root opacity: 0 anchors.rightMargin: -root.width } } ] transitions: [ Transition { from: "*"; to: "*" PropertyAnimation { properties: "opacity,anchors.rightMargin" easing.type: Easing.OutQuart duration: 500 } } ] } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/ControlPanel.qml000066400000000000000000000415741312235004300235500ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils import QtQuick.Window 2.1 import QtAV 1.4 // TODO: Control.qml Rectangle { id: root function scaled(x) { console.log("Screen " + screenPixelDensity + "; r: " + Screen.pixelDensity/Screen.logicalPixelDensity + "; size: " + Screen.width + "x" + Screen.height); console.log("screen density logical: " + Screen.logicalPixelDensity + " pixel: " + Screen.pixelDensity + "; " + x + ">>>" +x*Screen.pixelDensity/Screen.logicalPixelDensity); return x*Screen.pixelDensity/Screen.logicalPixelDensity; } color: "black" opacity: 0.9 radius: Utils.scaled(10) height: Utils.scaled(80) property string playState: "stop" property string mediaSource property int duration: 0 property real volume: 1 property bool mute: false property bool hiding: false property int previewHeight: preview.height signal seek(int ms) signal seekForward(int ms) signal seekBackward(int ms) signal stop signal play signal togglePause signal showInfo signal showHelp signal openFile signal openUrl function setPlayingProgress(value) { playState = "play" progress.value = value //now.text = Utils.msec2string(value*duraion) } function setStopState() { playState = "stop" aniShow() playBtn.checked = false progress.value = 0 duration = 0 //now.text = Utils.msec2string(0) video.file = "" } function setPlayingState() { video.file = mediaSource // why need this? playState = "pause" playBtn.checked = true //life.text = Utils.msec2string(duraion) aniShow() //for pause change hideIfTimedout() } function setPauseState() { aniShow() playBtn.checked = false } function toggleFullScreen() { fullScreenBtn.checked = !fullScreenBtn.checked } gradient: Gradient { GradientStop { position: 0.0; color: "#88445566" } GradientStop { position: 0.618; color: "#cc1a2b3a" } GradientStop { position: 1.0; color: "#ee000000" } } MouseArea { anchors.fill: parent hoverEnabled: true // onEntered, onExited onHoveredChanged: { //var m = mapToItem(root, mouse.x, mouse.y) // TODO: why root.contains(m) always true? if (containsMouse) { if (timer.running) //timer may ran a few seconds(<3) ago timer.stop(); root.aniShow() } else { //if (player.playbackState !== MediaPlayer.StoppedState) //if (playState !== "stop") // timer.start() } } onPressed: { if (timer.running) //timer may ran a few seconds(<3) ago timer.stop(); root.aniShow() } } ProgressBar { id: progress anchors { top: parent.top topMargin: Utils.scaled(8) left: parent.left leftMargin: Utils.scaled(20) right: parent.right rightMargin: Utils.scaled(20) } height: Utils.scaled(8) onValueChangedByUi: { /*if (player.playbackState != MediaPlayer.StoppedState) { player.seek(player.duration * value) }*/ if (playState != "stop") { seek(duration * value) } } onEnter: { //if (player.playbackState == MediaPlayer.StoppedState) // return if (playState == "stop") return preview.visible = true preview.state = dpos.y > 0 ? "out" : "out_" preview.state = "in" preview.anchors.leftMargin = pos.x - preview.width/2 } onLeave: { //if (player.playbackState == MediaPlayer.StoppedState) // return if (playState == "stop") return if (pos.y > -previewText.height && pos.y <= height/2) return preview.state = dpos.y > 0 ? "out" : "out_" } onHoverAt: { //if (player.playbackState == MediaPlayer.StoppedState) // return if (playState == "stop") return; showPreview(value) } } Rectangle { id: preview layer.enabled: true //opacity: 0.8 anchors.left: progress.left anchors.bottom: progress.top width: PlayerConfig.previewEnabled ? Utils.scaled(180) : previewText.contentWidth + 2*Utils.kSpacing height: PlayerConfig.previewEnabled ? Utils.scaled(120) : previewText.contentHeight + 2*Utils.kSpacing color: "black" state: "out" property real timestamp property alias video: video VideoPreview { id: video opengl: true visible: PlayerConfig.previewEnabled fillMode: VideoOutput.Stretch anchors.top: parent.top width: parent.width height: parent.height * 4/5 file: mediaSource } Text { id: previewText width: parent.width anchors.bottom: parent.bottom anchors.top: PlayerConfig.previewEnabled ? video.bottom : parent.top text: "" color: "white" font.pixelSize: Utils.scaled(12) font.bold: true horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } MouseArea { anchors.fill: parent propagateComposedEvents: true property int gPos: 0 property int moved: 0 onClicked: { if (moved) //why press+move+release is click? return if (preview.opacity === 0 || root.opacity === 0) { mouse.accepted = false return } mouse.accepted = true seek(preview.timestamp) } onPressed: { gPos = mapToItem(progress, mouseX, 0).x moved = 0 } onMouseXChanged: { if (!pressed) { mouse.accepted = false return } mouse.accepted = true var x1 = mapToItem(progress, mouseX, 0).x var dx = x1 - gPos gPos = x1 moved += dx showPreview(preview.timestamp/duration+dx/progress.width) } } states: [ State { name: "in" PropertyChanges { target: preview anchors.bottomMargin: 2 opacity: 1.0//0.9 } }, State { name: "out" PropertyChanges { target: preview anchors.bottomMargin: preview.height opacity: 0 } }, State { name: "out_" PropertyChanges { target: preview anchors.bottomMargin: -(preview.height + progress.height) opacity: 0 } } ] transitions: [ Transition { to: "in" NumberAnimation { properties: "opacity,anchors.bottomMargin" duration: 200 easing.type: Easing.OutCubic } }, Transition { to: "out" NumberAnimation { properties: "opacity,anchors.bottomMargin" duration: 200 easing.type: Easing.InCubic } }, Transition { to: "out_" NumberAnimation { properties: "opacity,anchors.bottomMargin" duration: 200 easing.type: Easing.InCubic } }//, //Transition { from: "out"; to: "out_" }, //Transition { from: "out_"; to: "out" } ] } Item { layer.enabled: true property int volBarPos: volBtn.x + volBtn.width/2 + x anchors { top: progress.bottom bottom: parent.bottom left: parent.left right: parent.right margins: Utils.scaled(8) } Text { id: now text: Utils.msec2string(progress.value*duration) anchors { top: parent.top topMargin: Utils.scaled(2) left: parent.left } color: "white" font { pixelSize: Utils.scaled(12) //or point size? } } Text { id: life text: Utils.msec2string(duration) anchors { top: parent.top topMargin: Utils.scaled(2) right: parent.right } color: "white" font { pixelSize: Utils.scaled(12) } } Button { id: playBtn anchors.centerIn: parent checkable: true bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(50) height: Utils.scaled(50) icon: Utils.resurl("theme/default/play.svg") iconChecked: Utils.resurl("theme/default/pause.svg") onClicked: { if (mediaSource == "") return if (playState == "stop") { play() } else { togglePause() } /* if (player.playbackState == MediaPlayer.StoppedState) { player.play() } else { player.togglePause() } */ } } /* Button { id: backwardBtn anchors.right: stopBtn.left anchors.verticalCenter: playBtn.verticalCenter bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(35) height: Utils.scaled(35) icon: Utils.resurl("theme/default/backward.svg") onClicked: { //player.seek(player.position-10000) seekBackward(10000) } }*/ Button { id: stopBtn anchors.verticalCenter: playBtn.verticalCenter anchors.right: playBtn.left bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(35) height: Utils.scaled(35) icon: Utils.resurl("theme/default/stop.svg") onClicked: { //player.stop() stop() } } /* Button { id: forwardBtn anchors.left: playBtn.right anchors.verticalCenter: playBtn.verticalCenter bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(35) height: Utils.scaled(35) icon: Utils.resurl("theme/default/forward.svg") onClicked: { //player.seek(player.position+10000) seekForward(10000) } }*/ Button { id: fullScreenBtn anchors.left: parent.left anchors.leftMargin: Utils.scaled(50) anchors.verticalCenter: parent.verticalCenter checkable: true checked: false bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(30) height: Utils.scaled(30) icon: Utils.resurl("theme/default/fullscreen.svg") iconChecked: Utils.resurl("theme/default/fullscreen.svg") visible: true onCheckedChanged: { if (checked) requestFullScreen() else requestNormalSize() } } Button { id: volBtn anchors.left: fullScreenBtn.right anchors.verticalCenter: parent.verticalCenter checkable: true checked: false bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(30) height: Utils.scaled(30) icon: Utils.resurl("theme/default/volume.svg") iconChecked: Utils.resurl("theme/default/mute.svg") onClicked: { if (!isTouchScreen) { root.mute = checked return } volBar.visible = !volBar.visible checked = root.mute } onPressAndHold: { //for qt5.6 mobile if (!isTouchScreen) return root.mute = !root.mute checked = root.mute } onHoveredChanged: { volBar.anchors.bottom = parent.top if (isTouchScreen) volBar.anchors.bottomMargin = 0 //ensure grip does not cover volBtn else volBar.anchors.bottomMargin = -(y + 2)//height/2) volBar.x = parent.volBarPos - volBar.width/2 } } Row { anchors.right: parent.right anchors.rightMargin: Utils.scaled(50) anchors.verticalCenter: parent.verticalCenter /* Button { id: infoBtn bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(30) height: Utils.scaled(30) icon: Utils.resurl("theme/default/info.svg") visible: true onClicked: showInfo() }*/ Button { id: openFileBtn bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(30) height: Utils.scaled(30) icon: Utils.resurl("theme/default/open.svg") onClicked: openFile() onPressAndHold: openUrl() } /* Button { id: helpBtn bgColor: "transparent" bgColorSelected: "transparent" width: Utils.scaled(30) height: Utils.scaled(30) icon: Utils.resurl("theme/default/help.svg") onClicked: showHelp() }*/ } } Slider { //volume id: volBar width:Utils.scaled(60) height: Utils.scaled(140) visible: hovered || volBtn.hovered opacity: 0.9 value: volume > 1 ? 0.5 - (volume - 1)/4 : 1 - volume/2 orientation: Qt.Vertical onValueChanged: { if (value < 0.5) volume = 1 + 4*(0.5-value) else volume = 2*(1-value) } Text { color: "white" id: voltext // Math.floor(10*volume)/10 //why display 1.100000001? text: Math.floor(volume) + "." + Math.floor((volume - Math.floor(volume))*10) } } Timer { id: timer interval: 3000 onTriggered: { root.aniHide() //root.visible = false //no mouse event } } function hideIfTimedout() { timer.start() } PropertyAnimation { id: anim target: root properties: "opacity" function reverse() { duration = 1500 to = 0 from = root.opacity } function reset() { duration = 200 from = root.opacity to = 0.9 } } function aniShow() { hiding = false anim.stop() anim.reset() anim.start() } function aniHide() { hiding = true anim.stop() anim.reverse() anim.start() } function toggleVisible() { if (hiding) aniShow() else aniHide() } function showPreview(value) { if (value < 0) return preview.visible = true preview.state = "in" if (PlayerConfig.previewEnabled && preview.video.file) { preview.video.timestamp = value*duration } var v = value * progress.width preview.anchors.leftMargin = v - preview.width/2 preview.timestamp = value*duration previewText.text = Utils.msec2string(preview.timestamp) //console.log("hover: "+value + " duration: " + player.duration) } function hidePreview() { preview.state = "out_" } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/DelegateItem.qml000066400000000000000000000022071312235004300234670ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Rectangle { id: root width: Math.max(Utils.kItemWidth, itemText.contentWidth+8) height: itemText.contentWidth property color selectedColor: "#66ddaadd" property alias text: itemText.text color: "#99000000" signal clicked Text { id: itemText color: "white" anchors.fill: parent anchors.margins: 4 font.pixelSize: Utils.kFontSize anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } MouseArea { anchors.fill: parent onClicked: { root.state = "selected" root.clicked() } } states: [ State { name: "selected" PropertyChanges { target: delegateItem color: selectedColor } } ] transitions: [ Transition { from: "*"; to: "*" ColorAnimation { properties: "color" easing.type: Easing.OutQuart duration: 500 } } ] } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/EffectPage.qml000066400000000000000000000075331312235004300231360ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Page { id: root title: qsTr("Effect") property alias brightness : brightnessControl.value property alias contrast : contrastControl.value property alias hue: hueControl.value property alias saturation: saturationControl.value height: Math.min(maxHeight, scroll.contentHeight) Flickable { id: scroll anchors.fill: content contentHeight: titleHeight + brightnessControl.height + brightnessControl.height + hueControl.height + saturationControl.height + Utils.kItemHeight + Utils.kSpacing*6 Column { anchors.fill: parent spacing: Utils.kSpacing Item { width: parent.width height: Utils.scaled(40) Text { color: "white" text: qsTr("Brightness") font.pixelSize: Utils.kFontSize anchors.left: parent.left height: parent.height verticalAlignment: Text.AlignVCenter } Slider { id: brightnessControl lineColor: "gray" anchors.right: parent.right width: parent.width*3/4 height: parent.height value: 0 min: -1 max: 1 orientation: Qt.Horizontal } } Item { width: parent.width height: Utils.scaled(40) Text { color: "white" text: qsTr("Contrast") font.pixelSize: Utils.kFontSize anchors.left: parent.left height: parent.height verticalAlignment: Text.AlignVCenter } Slider { id: contrastControl lineColor: "#ccddeeff" anchors.right: parent.right width: parent.width*3/4 height: parent.height value: 0 min: -1 max: 1 orientation: Qt.Horizontal } } Item { width: parent.width height: Utils.scaled(40) Text { color: "white" text: qsTr("Hue") font.pixelSize: Utils.kFontSize anchors.left: parent.left height: parent.height verticalAlignment: Text.AlignVCenter } Slider { id: hueControl lineColor: "#eebb8899" anchors.right: parent.right width: parent.width*3/4 height: parent.height value: 0 min: -1 max: 1 orientation: Qt.Horizontal } } Item { width: parent.width height: Utils.scaled(40) Text { color: "white" text: qsTr("Saturation") font.pixelSize: Utils.kFontSize anchors.left: parent.left height: parent.height verticalAlignment: Text.AlignVCenter } Slider { id: saturationControl lineColor: "#ee88ccaa" anchors.right: parent.right width: parent.width*3/4 height: parent.height value: 0 min: -1 max: 1 orientation: Qt.Horizontal } } Button { text: qsTr("Reset") bgColor: "#990000ff" width: parent.width/4 height: Utils.kItemHeight onClicked: { brightness = 0 contrast = 0 hue = 0 saturation = 0 } } } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/MediaInfoPage.qml000066400000000000000000000072611312235004300235730ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Page { id: root title: qsTr("Media Information") height: Math.min(maxHeight, scroll.contentHeight) property var information: "unknow" //let it be defined Flickable { id: scroll anchors.fill: content Text { id: info wrapMode: Text.Wrap color: "white" font.pixelSize: Utils.kFontSize anchors.fill: parent onContentSizeChanged: { scroll.contentHeight = contentHeight + Utils.scaled(32) } } } onInformationChanged: { console.log(information) var metaData = information.metaData var text = "

" + information.source + "

" if (typeof metaData.duration != "undefined") { text += "

" + qsTr("Duration: ") if (metaData.startTime > 0) text += metaData.startTime + "+" text += metaData.duration + " ms

" } if (typeof metaData.size != "undefined") text += "

" + qsTr("Size: ") + Math.round(metaData.size/1024/1024*100)/100 + " M

" if (typeof metaData.title != "undefined") text += "

" + qsTr("Title") + ": " +metaData.title + "

" if (typeof metaData.albumTitle != "undefined") text += "

" + qsTr("Album") + ": " + Utils.htmlEscaped(metaData.albumTitle) + "

" if (typeof metaData.title != "undefined") text += "

" + qsTr("Comment") + ": " + Utils.htmlEscaped(metaData.comment) + "

" if (typeof metaData.year != "undefined") text += "

" + qsTr("Year") + ": " + metaData.year + "

" if (typeof metaData.date != "undefined") text += "

" + qsTr("Date") + ": " + metaData.date + "

" if (typeof metaData.author != "undefined") text += "

" + qsTr("Author") + ": " + Utils.htmlEscaped(metaData.author) + "

" if (typeof metaData.publisher != "undefined") text += "

" + qsTr("Publisher") + ": " + Utils.htmlEscaped(metaData.publisher) + "

" if (typeof metaData.genre != "undefined") text += "

" + qsTr("Genre") + ": " + metaData.genre + "

" if (typeof metaData.trackNumber != "undefined") text += "

" + qsTr("Track") + ": " + metaData.trackNumber + "

" if (typeof metaData.trackCount != "undefined") text += "

" + qsTr("Track count") + ": " + metaData.trackCount + "

" if (information.hasVideo) { text += "

" + qsTr("Video") + "

" + "

" + qsTr("Resolution") + ": " + metaData.resolution.width + "x" + + metaData.resolution.height + "

" + qsTr("Bit rate") + ": " + metaData.videoBitRate + "

" + qsTr("Codec") + ": " + metaData.videoCodec + "

" + qsTr("Pixel format") + ": " + metaData.pixelFormat + "

" + qsTr("Frames") + ": " + metaData.videoFrames + ", " + qsTr("Frame rate") + ": " + Math.round(100*metaData.videoFrameRate)/100 + "

" } if (information.hasAudio) { text += "

" + qsTr("Audio") + "

" + "

" + qsTr("Codec") + ": " + metaData.audioCodec + "

" + qsTr("Sample format") + ": " + metaData.sampleFormat + "

" + qsTr("Sample rate") + ": " + metaData.sampleRate + "

" + qsTr("Channels") + ": " + metaData.channelCount + " (" + metaData.channelLayout + ")" + "

" + qsTr("Bit rate") + ": " + metaData.audioBitRate + "

" } info.text = text } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/Menu.qml000066400000000000000000000036041312235004300220440ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Item { id: root property alias model: listView.model property int currentIndex: -1 //listView.currentIndex //wrong value property int itemWidth: Utils.kItemWidth property int itemHeight: Utils.kItemHeight property alias header: listView.header property alias listOrientation: listView.orientation property alias spacing: listView.spacing property alias contentWidth: listView.contentWidth property alias contentHeight: listView.contentHeight signal clicked(int index, int x, int y) height: model.count * itemHeight QtObject { id: d property Item selectedItem property string detail: "" } Component { id: itemDelegate DelegateItem { id: delegateItem text: qsTr(name) anchors.horizontalCenter: parent.horizontalCenter width: itemWidth height: itemHeight onClicked: { root.clicked(index, delegateItem.x, delegateItem.y) if (d.selectedItem == delegateItem) return if (d.selectedItem) d.selectedItem.state = "baseState" d.selectedItem = delegateItem } } } ListView { id: listView anchors.fill: parent focus: true currentIndex: -1 delegate: itemDelegate } onCurrentIndexChanged: { if (d.selectedItem) d.selectedItem.state = "baseState" listView.currentIndex = currentIndex d.selectedItem = listView.currentItem if (d.selectedItem) d.selectedItem.state = "selected" } Component.onCompleted: { listView.currentIndex = currentIndex d.selectedItem = listView.currentItem if (d.selectedItem) d.selectedItem.state = "selected" } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/MiscPage.qml000066400000000000000000000214241312235004300226300ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Page { title: qsTr("Misc") height: Math.min(maxHeight, scroll.contentHeight) Flickable { id: scroll anchors.fill: content contentHeight: titleHeight + (7+glSet.visible*4)*Utils.kItemHeight + detail.contentHeight + 5*Utils.kSpacing Column { anchors.fill: parent spacing: Utils.kSpacing Button { text: qsTr("Reset all") bgColor: "#ff0000" width: parent.width height: Utils.kItemHeight onClicked: PlayerConfig.reset() } Row { width: parent.width height: Utils.kItemHeight Button { text: qsTr("Preview") checkable: true checked: PlayerConfig.previewEnabled width: parent.width/3 height: Utils.kItemHeight onCheckedChanged: { console.log("check changed " + checked ) PlayerConfig.previewEnabled = checked } } Text { id: detail font.pixelSize: Utils.kFontSize color: "white" width: parent.width*2/3 height: parent.height horizontalAlignment: Text.AlignHCenter text: qsTr("Press on the preview item to seek") } } Row { width: parent.width height: Utils.kItemHeight visible: Qt.platform.os === "linux" Button { text: "EGL" checkable: true checked: PlayerConfig.EGL width: parent.width/3 height: Utils.kItemHeight onCheckedChanged: PlayerConfig.EGL = checked } Text { font.pixelSize: Utils.kFontSize color: "white" width: parent.width*2/3 height: parent.height horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: qsTr("Requirement") + ": Qt>=5.5+XCB" } } Row { id: glSet width: parent.width height: 5*Utils.kItemHeight spacing: Utils.kSpacing visible: Qt.platform.os === "windows" || Qt.platform.os === "wince" Text { width: parent.width/3 height: parent.height color: "white" font.pixelSize: Utils.kFontSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: glSet.visible text: "OpenGL\n(" + qsTr("Restart to apply") + ")" } Menu { id: glMenu width: parent.width/3 height: parent.height itemWidth: width model: ListModel { id: glModel ListElement { name: "Auto" } ListElement { name: "Desktop" } ListElement { name: "OpenGLES" } ListElement { name: "Software" } } onClicked: { var gl = glModel.get(index).name PlayerConfig.openGLType = gl if (gl === "OpenGLES") { angle.sourceComponent = angleMenuComponent } else { angle.sourceComponent = undefined } } function updateUi() { currentIndex = -1 for (var i = 0; i < glModel.count; ++i) { console.log("gl: " + glModel.get(i).name) if (PlayerConfig.openGLType === i) { currentIndex = i break } } } Component.onCompleted: updateUi() } Loader { id: angle width: parent.width/3 height: parent.height } Component { id: angleMenuComponent Menu { anchors.fill: parent itemWidth: width model: ListModel { id: angleModel ListElement { name: "Auto"; detail: "D3D9 > D3D11" } ListElement { name: "D3D9" } ListElement { name: "D3D11" } ListElement { name: "WARP" } } onClicked: PlayerConfig.ANGLEPlatform = angleModel.get(index).name function updateUi() { currentIndex = -1 if (Qt.platform.os !== "windows" && Qt.platform.os !== "wince") return for (var i = 0; i < angleModel.count; ++i) { console.log("d3d: " + angleModel.get(i).name) if (PlayerConfig.ANGLEPlatform === angleModel.get(i).name) { currentIndex = i break } } } Component.onCompleted: updateUi() } } } Row { width: parent.width height: 3*Utils.kItemHeight spacing: Utils.kSpacing Text { font.pixelSize: Utils.kFontSize color: "white" width: parent.width/3 height: parent.height horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WrapAnywhere text: "Log level\n" + qsTr("Developer only") } Menu { id: logMenu width: parent.width/2 height: parent.height itemWidth: width model: ListModel { id: logModel ListElement { name: "off" } ListElement { name: "warning" } ListElement { name: "debug" } ListElement { name: "all" } } onClicked: PlayerConfig.logLevel = logModel.get(index).name function updateUi() { currentIndex = -1 for (var i = 0; i < logModel.count; ++i) { if (PlayerConfig.logLevel === logModel.get(i).name) { currentIndex = i break } } } Component.onCompleted: updateUi() } } Row { width: parent.width height: 2*Utils.kItemHeight spacing: Utils.kSpacing Text { font.pixelSize: Utils.kFontSize color: "white" width: parent.width/3 height: parent.height horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WrapAnywhere text: qsTr("Language") + "\n" + qsTr("Restart to apply") } Menu { id: langMenu width: parent.width/2 height: parent.height itemWidth: width model: ListModel { id: langModel ListElement { name: "system" } ListElement { name: "en_US" } ListElement { name: "zh_CN" } } onClicked: PlayerConfig.language = langModel.get(index).name function updateUi() { currentIndex = -1 for (var i = 0; i < langModel.count; ++i) { if (PlayerConfig.language === langModel.get(i).name) { currentIndex = i break } } } Component.onCompleted: updateUi() } } } Component.onCompleted: { if (Qt.platform.os !== "windows" && Qt.platform.os !== "wince") return if (PlayerConfig.openGLType === 2) { angle.sourceComponent = angleMenuComponent } } Connections { target: PlayerConfig onLogLevelChanged: logMenu.updateUi() onLanguageChanged: langMenu.updateUi() onOpenGLTypeChanged: { if (glSet.visible) glMenu.updateUi() } } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/Page.qml000066400000000000000000000034611312235004300220150ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Rectangle { id: root layer.enabled: true color: "#aa1a2b3a" focus: true property alias title: title.text property alias content: content property real titleHeight: title.height + 2*Utils.kMargin property real maxHeight: Utils.scaled(300) Text { id: title anchors.top: parent.top width: parent.width height: Utils.scaled(40) color: "white" font.pixelSize: Utils.scaled(20) font.bold: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } Canvas { anchors.fill: parent onPaint: { var ctx = getContext('2d') var g = ctx.createLinearGradient(0, 0, parent.width, 0) g.addColorStop(0, "#00ffffff") g.addColorStop(0.3, "#f0ffffff") g.addColorStop(0.7, "#f0ffffff") g.addColorStop(1, "#00ffffff") ctx.fillStyle = g ctx.fillRect(0, title.height, parent.width, 1) } } MouseArea { // avoid mouse events propagated to parents anchors.fill: parent hoverEnabled: true propagateComposedEvents: false } Item { id: content anchors { top: title.bottom bottom: parent.bottom left: parent.left right: parent.right } anchors.margins: Utils.kMargin } // TODO: why must put here otherwise can't clicked? Button { anchors.top: parent.top anchors.right: parent.right width: Utils.scaled(20) height: Utils.scaled(20) bgColor: "transparent" bgColorSelected: "transparent" icon: Utils.resurl("theme/default/close.svg") onClicked: root.visible = false } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/PlayListPanel.qml000066400000000000000000000117701312235004300236640ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Rectangle { id: root color: "#aa204010" layer.enabled: true readonly property int kMaxItems: 128 border { color: "white" width: 1 } readonly property int kMargin: Utils.scaled(20) signal play(url source, int start) function addHistory(url, duration) { console.log("add history: " + url) var info = {url:url, file: Utils.fileName(url), start: 0, duration: duration} historyModel.insert(0, info) historyView.currentIndex = 0 for (var i = historyModel.count - 1; i > 0; --i) { if (historyModel.get(i).url === url) historyModel.remove(i, 1) } if (historyModel.count > kMaxItems) { historyModel.remove(kMaxItems, 1) } PlayerConfig.removeHistory(url) PlayerConfig.addHistory(info) } MouseArea { anchors.fill: parent hoverEnabled: !isTouchScreen onEntered: { if (mouseY < closeBtn.height) return if (root.state !== "show") root.state = "show" } onExited: { if (isTouchScreen) return if (root.state === "show") root.state = "ready" } } Column { anchors.fill: parent anchors.margins: Utils.scaled(6) anchors.rightMargin: kMargin spacing: Utils.kSpacing Text { id: title anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: Utils.kFontSize color: "orange" font.bold: true text: qsTr("History") horizontalAlignment: Text.AlignHCenter } ListView { id: historyView highlightMoveDuration: 100 highlight: Rectangle { color: "#88ddaadd"; radius: 1 } flickableDirection: Flickable.VerticalFlick width: parent.width height: parent.height - title.height focus: true model: ListModel { id: historyModel} delegate: Component { Item { width: parent.width height: info.height Column { id: info width: parent.width Text { width: parent.width elide: Text.ElideMiddle font.pixelSize: Utils.kFontSize color: "white" text: file } Text { font.pixelSize: Utils.kFontSize*2/3 color: "white" text: Utils.msec2string(start) + "/" + Utils.msec2string(duration) } } MouseArea { anchors.fill: parent onClicked: historyView.currentIndex = index onDoubleClicked: { console.log("click: " + url + " file: " + file) root.play(Qt.resolvedUrl(url), start) } } } } Component.onCompleted: { for (var i = 0; i < PlayerConfig.history.length; ++i) { var info = PlayerConfig.history[i]; historyModel.insert(0, {url:info.url, file: Utils.fileName(info.url), start: info.start, duration: info.duration}) } historyView.currentIndex = 0 } } } Button { id: closeBtn anchors.top: parent.top anchors.right: parent.right width: kMargin height: kMargin bgColor: "transparent" bgColorSelected: "transparent" icon: Utils.resurl("theme/default/close.svg") onClicked: { if (root.state === "ready") root.state = "hide" else root.state = "ready" } } states: [ State { name: "show" PropertyChanges { target: root opacity: 0.9 anchors.leftMargin: 0 } }, State { name: "hide" PropertyChanges { target: root opacity: 0.6 anchors.leftMargin: -root.width } }, State { name: "ready" PropertyChanges { target: root opacity: 0.9 anchors.leftMargin: -root.width + kMargin } } ] transitions: [ Transition { from: "*"; to: "*" PropertyAnimation { properties: "opacity,anchors.leftMargin" easing.type: Easing.OutQuart duration: 500 } } ] } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/ProgressBar.qml000066400000000000000000000074161312235004300233760ustar00rootroot00000000000000/****************************************************************************** Copyright (C) 2013-2015 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ import QtQuick 2.0 import "utils.js" as Utils Rectangle { id: root color: "#44eeeeee" radius: Utils.scaled(5) property alias value: grip.value property color lineColor: "#880000ee" property color gripColor: "white" property real gripSize: Utils.scaled(8) property real gripTolerance: Utils.scaled(3.0) property real increment: 0.1 property bool showGrip: true property bool tracking: true signal valueChangedByUi signal hoverAt(real value) // dx, dy: only the direction. dx>0 means enter from left or leave to left signal enter(point pos, point dpos) signal leave(point pos, point dpos) Rectangle { anchors { left: parent.left verticalCenter: parent.verticalCenter } radius: parent.radius width: grip.x + grip.radius height: parent.height color: displayedColor(root.lineColor) } MouseArea { anchors.fill: parent hoverEnabled: true onClicked: { if (parent.width) { parent.value = mouse.x / parent.width valueChangedByUi(parent.value) } } onMouseXChanged: { hoverAt(mouseX/parent.width) //time and preview } onEntered: { enter(Qt.point(mouseX, mouseY), Qt.point(0, mouseY > height/2 ? 1 : -1)) hoverAt(mouseX/parent.width) } onExited: { leave(Qt.point(mouseX, mouseY), Qt.point(0, mouseY > height/2 ? 1 : -1)) } } Rectangle { id: grip property real value: 0 x: (value * parent.width - width/2) anchors.verticalCenter: parent.verticalCenter width: root.gripTolerance * root.gripSize height: parent.height radius: width/2 color: "transparent" MouseArea { id: mouseArea enabled: root.enabled anchors.fill: parent drag { target: grip axis: Drag.XAxis minimumX: -parent.width/2 maximumX: root.width - parent.width/2 } onPositionChanged: { if (drag.active) updatePosition() } onReleased: { updatePosition() } function updatePosition() { value = (grip.x + grip.width/2) / grip.parent.width valueChangedByUi(value) } } Rectangle { anchors.centerIn: parent width: root.gripSize height: parent.height radius: width/2 color: root.showGrip ? root.gripColor : "transparent" } } function displayedColor(c) { var tint = Qt.rgba(c.r, c.g, c.b, 0.25) return enabled ? c : Qt.tint(c, tint) } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/Slider.qml000066400000000000000000000112711312235004300223610ustar00rootroot00000000000000/****************************************************************************** Copyright (C) 2013-2016 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ import QtQuick 2.0 import "utils.js" as Utils Item { id: root property alias value: grip.value property color fillColor: "white" property color lineColor: "blue" property real lineWidth: Utils.scaled(6) property color gripColor: "white" property real gripSize: Utils.scaled(12) property real gripTolerance: Utils.scaled(3.0) property int orientation: Qt.Horizontal property bool hovered: false //mouseArea.containsMouse || gripMouseArea.containsMouse property real max: 1 property real min: 0 Rectangle { anchors.centerIn: parent height: orientation === Qt.Horizontal ? lineWidth : parent.height width: orientation === Qt.Horizontal ? parent.width : lineWidth color: lineColor } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true onHoveredChanged: { //console.log("slider.hover mouseX: " + mouseX) if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff return hovered = mouseArea.containsMouse } onClicked: { var newValue = min + (mouse.x / parent.width)*(max-min) if (orientation === Qt.Horizontal) { newValue = min + (mouse.x / parent.width)*(max-min) } else { newValue = min + (mouse.y / parent.height)*(max-min) } var increment = 1.0/width if (Math.abs(newValue - parent.value) > parent.increment*(max-min)) { if (newValue > parent.value) parent.value = Math.min(max, parent.value + parent.increment*(max-min)) else parent.value = Math.max(min, parent.value - parent.increment*(max-min)) } } } Item { id: grip property real value: 0.5 x: orientation === Qt.Horizontal ? ((value-min)/(max-min) * parent.width - width/2) : (parent.width - width)/2 y: orientation === Qt.Horizontal ? (parent.height - height)/2 : ((value-min)/(max-min) * parent.height - height/2) width: root.gripTolerance * root.gripSize height: width readonly property real radius: width/2 MouseArea { id: gripMouseArea anchors.fill: parent hoverEnabled: true onHoveredChanged: { //console.log("slider.grip.hover mouseX: " + mouseX) if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff return hovered = gripMouseArea.containsMouse } drag { target: grip axis: orientation === Qt.Horizontal ? Drag.XAxis : Drag.YAxis minimumX: orientation === Qt.Horizontal ? -parent.radius : 0 maximumX: orientation === Qt.Horizontal ? root.width - parent.radius : 0 minimumY: orientation === Qt.Horizontal ? 0 : -parent.radius maximumY: orientation === Qt.Horizontal ? 0 : root.height - parent.radius } onPositionChanged: { if (drag.active) updatePosition() } onReleased: updatePosition() function updatePosition() { if (orientation === Qt.Horizontal) value = min + ((grip.x + grip.radius) / grip.parent.width)*(max-min) else value = min + ((grip.y + grip.radius) / grip.parent.height)*(max-min) } } Rectangle { anchors.centerIn: parent width: root.gripSize height: width radius: width/2 color: root.gripColor } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/SubtitlePage.qml000066400000000000000000000324371312235004300235360ustar00rootroot00000000000000import QtQuick 2.0 import QtQuick.Dialogs 1.1 import "utils.js" as Utils Page { id: root title: qsTr("Subtitle") signal subtitleTrackChanged(int track) property var supportedFormats: ["ass" , "ssa"] property var internalSubtitleTracks : "unkown" height: Math.min(maxHeight, scroll.contentHeight) Flickable { id: scroll anchors.fill: content contentHeight: titleHeight + tracksMenu.height + 5*Utils.kItemHeight + engine.contentHeight + Utils.kSpacing*4 Column { anchors.fill: parent spacing: Utils.kSpacing Row { width: parent.width height: Utils.kItemHeight spacing: Utils.kSpacing Text { id: delayLabel color: "white" font.pixelSize: Utils.kFontSize text: qsTr("Delay") + " (" + qsTr("ms") + ")" width: contentWidth height: Utils.kItemHeight } TextInput { color: "orange" font.pixelSize: Utils.kFontSize height: Utils.kItemHeight text: PlayerConfig.subtitleDelay onTextChanged: { var v = parseFloat(text) if (isNaN(v)) return PlayerConfig.subtitleDelay = Math.round(v*1000)/1000 } } } Row { width: parent.width height: 2*Utils.kItemHeight // engine.contentHeight spacing: Utils.kSpacing Text { color: "white" text: qsTr("Engine") font.pixelSize: Utils.kFontSize width: contentWidth height: parent.height } Menu { id: engine width: parent.width - Utils.kItemWidth height: parent.height itemWidth: width currentIndex: PlayerConfig.subtitleEngines[0] === "FFmpeg" ? 0 : 1 model: ListModel { ListElement { name: "FFmpeg" } ListElement { name: "LibASS" } } onClicked: { PlayerConfig.subtitleEngines = [ model.get(index).name, model.get((index+1)%model.count).name ] var e = model.get(index).name if (e === "FFmpeg") engineDetail.sourceComponent = ffmpeg else engineDetail.sourceComponent = libass } } } Row { Text { color: "white" text: qsTr("Supported formats") + ": " font.pixelSize: Utils.kFontSize } Text { id: formats color: "orange" font.pixelSize: Utils.kFontSize text: supportedFormats.join(",") wrapMode: Text.Wrap } } Item { width: parent.width height: 2*Utils.kItemHeight Loader { id: engineDetail anchors.fill: parent } Component { id: ffmpeg Row { Text { color: "white" text: qsTr("Bottom margin") font.pixelSize: Utils.kFontSize } TextInput { color: "orange" font.pixelSize: Utils.kFontSize width: Utils.scaled(40) height: parent.height validator: IntValidator{bottom: 0;} text: PlayerConfig.subtitleBottomMargin onTextChanged: PlayerConfig.subtitleBottomMargin = parseInt(text) } Button { text: qsTr("Font") width: Utils.scaled(60) height: Utils.kItemHeight onClicked: fontDialog.open() } Rectangle { color: PlayerConfig.subtitleColor width: Utils.kItemHeight height: Utils.kItemHeight MouseArea { anchors.fill: parent onPressed: colorDialog.open() } } Button { text: qsTr("Outline") checkable: true checked: PlayerConfig.subtitleOutline width: Utils.scaled(60) height: Utils.kItemHeight onCheckedChanged: PlayerConfig.subtitleOutline = checked } Rectangle { color: PlayerConfig.subtitleOutlineColor width: Utils.kItemHeight height: Utils.kItemHeight MouseArea { anchors.fill: parent onPressed: outlineColorDialog.open() } } Text { id: stylePreview text: "Q" font: PlayerConfig.subtitleFont style: PlayerConfig.subtitleOutline ? Text.Outline : Text.Normal styleColor: PlayerConfig.subtitleOutlineColor color: PlayerConfig.subtitleColor } } } Component { id: libass Column { Item { width: parent.width height: Utils.kItemHeight Text { id: fontFile color: "orange" elide: Text.ElideMiddle font.pixelSize: Utils.kFontSize anchors.left: parent.left anchors.right: openFontFile.left height: parent.height text: PlayerConfig.assFontFile } Button { id: openFontFile text: qsTr("Font file") width: Utils.scaled(80) anchors.right: forceFontFile.left height: parent.height onClicked: fontFileDialog.open() } Button { id: forceFontFile text: qsTr("Force") width: Utils.scaled(50) anchors.right: clearFontFile.left height: parent.height checkable: true checked: PlayerConfig.assFontFileForced onCheckedChanged: PlayerConfig.assFontFileForced = checked } Button { id: clearFontFile text: qsTr("Clear") width: Utils.scaled(50) anchors.right: parent.right height: parent.height onClicked: PlayerConfig.assFontFile = "" } } Item { visible: Qt.platform.os !== "winphone" && Qt.platform.os !== "winrt" width: parent.width height: Utils.kItemHeight Text { id: fontsDir color: "orange" elide: Text.ElideMiddle font.pixelSize: Utils.kFontSize text: PlayerConfig.assFontsDir anchors.left: parent.left anchors.right: openFontsDir.left height: parent.height } Button { id: openFontsDir text: qsTr("Fonts dir") width: Utils.scaled(80) anchors.right: clearFontsDir.left height: parent.height onClicked: fontsDirDialog.open() } Button { id: clearFontsDir text: qsTr("Clear") width: Utils.scaled(50) anchors.right: parent.right height: parent.height onClicked: PlayerConfig.assFontsDir = "" } } } } } Item { width: parent.width height: tracksMenu.height Text { id: intSubLabel color: "white" text: qsTr("Embedded Subtitles") + ": " + internalSubtitleTracks.length font.pixelSize: Utils.kFontSize font.bold: true wrapMode: Text.WrapAnywhere width: Utils.scaled(110) height: parent.height visible: height > 0 } Menu { id: tracksMenu anchors.left: intSubLabel.right anchors.right: parent.right itemWidth: width model: ListModel { id: tracksModel } onClicked: { console.log("subtitle track clikced: " + tracksModel.get(index).value) root.subtitleTrackChanged(tracksModel.get(index).value) } } } Row { width: parent.width height: 2*Utils.kItemHeight spacing: Utils.kSpacing Text { id: extSubLabel color: "white" text: qsTr("External Subtitle") wrapMode: Text.WrapAnywhere font.pixelSize: Utils.kFontSize font.bold: true width: Utils.scaled(110) } Column { width:parent.width - extSubLabel.width height: parent.height Row { width: parent.width height: Utils.kItemHeight Button { text: qsTr("Enabled") checkable: true checked: PlayerConfig.subtitleEnabled width: parent.width/2 height: Utils.kItemHeight onCheckedChanged: PlayerConfig.subtitleEnabled = checked } Button { id: autoLoad text: qsTr("Auto load") checkable: true checked: enabled && PlayerConfig.subtitleAutoLoad visible: Qt.platform.os !== "winrt" width: parent.width/2 height: Utils.kItemHeight onCheckedChanged: PlayerConfig.subtitleAutoLoad = checked } } } } } } //Flickable onInternalSubtitleTracksChanged: updateTracksMenu() function updateTracksMenu() { tracksModel.clear() tracksMenu.height = Math.min(internalSubtitleTracks.length, 3)*tracksMenu.itemHeight for (var i = 0; i < internalSubtitleTracks.length; ++i) { var t = internalSubtitleTracks[i] var label = "#" + t.id if (t.codec) label += " '" + t.codec + "'" if (t.language) label += " (" + t.language + ")" if (t.title) label += ": " + t.title tracksModel.append({name: label, value: t.id}) } } FileDialog { id: fontFileDialog title: qsTr("Choose a font file") folder: PlayerConfig.assFontFile onAccepted: PlayerConfig.assFontFile = fileUrl } FileDialog { id: fontsDirDialog title: qsTr("Choose a fonts dir") selectFolder: true folder: PlayerConfig.assFontsDir onAccepted: PlayerConfig.assFontsDir = folder } FontDialog { id: fontDialog font: PlayerConfig.subtitleFont onAccepted: PlayerConfig.subtitleFont = font } ColorDialog { id: colorDialog color: PlayerConfig.subtitleColor onAccepted: PlayerConfig.subtitleColor = color } ColorDialog { id: outlineColorDialog color: PlayerConfig.subtitleOutlineColor onAccepted: PlayerConfig.subtitleOutlineColor = color } Component.onCompleted: { if (PlayerConfig.subtitleEngines[0] === "FFmpeg") engineDetail.sourceComponent = ffmpeg else engineDetail.sourceComponent = libass } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/VideoCodec.qml000066400000000000000000000120661312235004300231460ustar00rootroot00000000000000import QtQuick 2.0 import "utils.js" as Utils Page { id: root title: qsTr("Video Codec") height: titleHeight + detail.height + listView.height + copyMode.height + Utils.kSpacing*4 signal zeroCopyChanged(bool value) QtObject { id: d property Item selectedItem property string detail: qsTr("Takes effect on the next play") } Column { anchors.fill: content spacing: Utils.kSpacing Text { id: detail text: d.detail color: "white" height: contentHeight + 1.6*Utils.kItemHeight width: parent.width font.pixelSize: Utils.kFontSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } ListView { id: listView contentWidth: parent.width - Utils.scaled(20) height: Utils.kItemHeight anchors { //topMargin: Utils.kMargin horizontalCenter: parent.horizontalCenter } onContentWidthChanged: { anchors.leftMargin = Math.max(10, (parent.width - contentWidth)/2) anchors.rightMargin = anchors.leftMargin width = parent.width - 2*anchors.leftMargin } orientation: ListView.Horizontal spacing: Utils.scaled(6) focus: true delegate: contentDelegate model: codecMode } ListModel { id: codecMode ListElement { name: "FFmpeg"; hardware: false; zcopy: false; description: "FFmpeg/Libav" } } Component { id: contentDelegate DelegateItem { id: delegateItem text: name //width: Utils.kItemWidth height: Utils.kItemHeight color: "#aa000000" selectedColor: "#aa0000cc" border.color: "white" onClicked: { if (d.selectedItem == delegateItem) return if (d.selectedItem) d.selectedItem.state = "baseState" d.selectedItem = delegateItem } onStateChanged: { if (state != "selected") return d.detail = description + " " + (hardware ? qsTr("hardware decoding") : qsTr("software decoding")) if (name === "FFmpeg") { copyMode.visible = false } else { copyMode.visible = zcopy d.detail += "\n" + qsTr("Zero Copy support") + ":" + zcopy } PlayerConfig.decoderPriorityNames = [ name ] } } } Button { id: copyMode text: qsTr("Zero copy") checked: PlayerConfig.zeroCopy checkable: true width: parent.width height: Utils.kItemHeight onCheckedChanged: PlayerConfig.zeroCopy = checked } } Component.onCompleted: { if (Qt.platform.os == "windows") { codecMode.append({ name: "DXVA", hardware: true, zcopy: true, description: "DirectX Video Acceleration (Windows)\nUse OpenGLES(ANGLE) + D3D to support 0-copy" }) codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration\n0-copy is supported under OpenGLES(ANGLE)" }) codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux).\nH264 10bit support."}) } else if (Qt.platform.os == "winrt" || Qt.platform.os == "winphone") { codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration" }) } else if (Qt.platform.os == "osx") { codecMode.append({ name: "VDA", hardware: true, zcopy: true, description: "VDA (OSX)" }) codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (OSX)" }) } else if (Qt.platform.os == "ios") { codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (iOS)" }) } else if(Qt.platform.os == "android") { codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android 5.0 MediaCodec (H.264)" }) } else if (Qt.platform.os == "linux") { codecMode.append({ name: "VAAPI", hardware: true, zcopy: true, description: "VA-API (Linux) " }) codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux)"}) } for (var i = 0; i < codecMode.count; ++i) { if (codecMode.get(i).name === PlayerConfig.decoderPriorityNames[0]) { listView.currentIndex = i; d.selectedItem = listView.currentItem listView.currentItem.state = "selected" break } } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/main.qml000066400000000000000000000544311312235004300220700ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ //if qt<5.3, remove lines: sed '/\/\/IF_QT53/,/\/\/ENDIF_QT53/d' import QtQuick 2.0 //IF_QT53 import QtQuick.Dialogs 1.2 /* //ENDIF_QT53 import QtQuick.Dialogs 1.1 /* */ //import QtMultimedia 5.0 import QtAV 1.7 import QtQuick.Window 2.1 import "utils.js" as Utils Rectangle { id: root layer.enabled: false //VideoOutput2 can not update correctly if layer.enable is true. default is false objectName: "root" width: Utils.scaled(800) height: Utils.scaled(450) color: "black" signal requestFullScreen signal requestNormalSize function init(argv) { console.log("init>>>>>screen density logical: " + Screen.logicalPixelDensity + " pixel: " + Screen.pixelDensity); } VideoFilter { id: negate type: VideoFilter.GLSLFilter shader: Shader { postProcess: "gl_FragColor.rgb = vec3(1.0-gl_FragColor.r, 1.0-gl_FragColor.g, 1.0-gl_FragColor.b);" } } VideoFilter { id: hflip type: VideoFilter.GLSLFilter shader: Shader { sample: "vec4 sample2d(sampler2D tex, vec2 pos, int p) { return texture(tex, vec2(1.0-pos.x, pos.y));}" } } VideoOutput2 { id: videoOut opengl: true fillMode: VideoOutput.PreserveAspectFit anchors.fill: parent source: player orientation: 0 property real zoom: 1 //filters: [negate, hflip] SubtitleItem { id: subtitleItem fillMode: videoOut.fillMode rotation: -videoOut.orientation source: subtitle anchors.fill: parent } Text { id: subtitleLabel rotation: -videoOut.orientation horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignBottom font: PlayerConfig.subtitleFont style: PlayerConfig.subtitleOutline ? Text.Outline : Text.Normal styleColor: PlayerConfig.subtitleOutlineColor color: PlayerConfig.subtitleColor anchors.fill: parent anchors.bottomMargin: PlayerConfig.subtitleBottomMargin } } MediaPlayer { id: player objectName: "player" //loops: MediaPlayer.Infinite //autoLoad: true autoPlay: true videoCodecPriority: PlayerConfig.decoderPriorityNames onPositionChanged: control.setPlayingProgress(position/duration) videoCapture { autoSave: true onSaved: { msg.info("capture saved at: " + path) } } onSourceChanged: { videoOut.zoom = 1 videoOut.regionOfInterest = Qt.rect(0, 0, 0, 0) msg.info("url: " + source) } onDurationChanged: control.duration = duration onPlaying: { control.mediaSource = player.source control.setPlayingState() if (!pageLoader.item) return if (pageLoader.item.information) { pageLoader.item.information = { source: player.source, hasAudio: player.hasAudio, hasVideo: player.hasVideo, metaData: player.metaData } } } onSeekFinished: { console.log("seek finished " + Utils.msec2string(position)) } onInternalAudioTracksChanged: { if (typeof(pageLoader.item.internalAudioTracks) != "undefined") pageLoader.item.internalAudioTracks = player.internalAudioTracks } onExternalAudioTracksChanged: { if (typeof(pageLoader.item.externalAudioTracks) != "undefined") pageLoader.item.externalAudioTracks = player.externalAudioTracks } onInternalSubtitleTracksChanged: { if (typeof(pageLoader.item.internalSubtitleTracks) != "undefined") pageLoader.item.internalSubtitleTracks = player.internalSubtitleTracks } onStopped: control.setStopState() onPaused: control.setPauseState() onError: { if (error != MediaPlayer.NoError) { msg.error(errorString) } } muted: control.mute // TODO: control from system volume: control.volume onVolumeChanged: { //why need this? control.volume = player.volume is not enough? if (Math.abs(control.volume - volume) >= 0.01) { control.volume = volume } } onStatusChanged: { if (status == MediaPlayer.Loading) msg.info("Loading " + source) else if (status == MediaPlayer.Buffering) msg.info("Buffering") else if (status == MediaPlayer.Buffered) msg.info("Buffered") else if (status == MediaPlayer.EndOfMedia) msg.info("End") else if (status == MediaPlayer.InvalidMedia) msg.info("Invalid") } onBufferProgressChanged: { msg.info("Buffering " + Math.floor(bufferProgress*100) + "%...") } // onSeekFinished: msg.info("Seek finished: " + Utils.msec2string(position)) } Subtitle { id: subtitle player: player enabled: PlayerConfig.subtitleEnabled autoLoad: PlayerConfig.subtitleAutoLoad engines: PlayerConfig.subtitleEngines delay: PlayerConfig.subtitleDelay fontFile: PlayerConfig.assFontFile fontFileForced: PlayerConfig.assFontFileForced fontsDir: PlayerConfig.assFontsDir onContentChanged: { //already enabled if (!canRender || !subtitleItem.visible) subtitleLabel.text = text } onLoaded: { msg.info(qsTr("Subtitle") + ": " + path.substring(path.lastIndexOf("/") + 1)) console.log(msg.text) } onSupportedSuffixesChanged: { if (!pageLoader.item) return pageLoader.item.supportedFormats = supportedSuffixes } onEngineChanged: { // assume a engine canRender is only used as a renderer subtitleItem.visible = canRender subtitleLabel.visible = !canRender } onEnabledChanged: { subtitleItem.visible = enabled subtitleLabel.visible = enabled } } MultiPointTouchArea { //mouseEnabled: true //not available on qt5.2(ubuntu14.04) anchors.fill: parent onGestureStarted: { if (player.playbackState == MediaPlayer.StoppedState) return var p = gesture.touchPoints[0] var dx = p.x - p.previousX var dy = p.y - p.previousY var t = dy/dx var ml = Math.abs(dx) + Math.abs(dy) var ML = Math.abs(p.x - p.startX) + Math.abs(p.y - p.startY) //console.log("dx: " + dx + " dy: " + dy + " ml: " + ml + " ML: " + ML) if (ml < 2.0 || 5*ml < ML) return if (t > -1 && t < 1) { player.fastSeek = true if (dx > 0) { player.seekForward() } else { player.seekBackward() } } } MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: control.opacity > 0 || cursor_timer.running ? Qt.ArrowCursor : Qt.BlankCursor onWheel: { var deg = wheel.angleDelta.y/8 var dp = wheel.pixelDelta var p = Qt.point(mouseX, mouseY) //root.mapToItem(videoOut, Qt.point(mouseX, mouseY)) var fp = videoOut.mapPointToSource(p) if (fp.x < 0) fp.x = 0; if (fp.y < 0) fp.y = 0; if (fp.x > videoOut.videoFrameSize.width) fp.x = videoOut.videoFrameSize.width if (fp.y > videoOut.videoFrameSize.height) fp.y = videoOut.videoFrameSize.height videoOut.zoom *= (1.0 + deg*3.14/180.0); if (videoOut.zoom < 1.0) videoOut.zoom = 1.0 var x0 = fp.x - fp.x/videoOut.zoom; var y0 = fp.y - fp.y/videoOut.zoom; // in fact, it must insected with video frame rect. opengl save us videoOut.regionOfInterest.x = x0 videoOut.regionOfInterest.y = y0 videoOut.regionOfInterest.width = videoOut.videoFrameSize.width/videoOut.zoom videoOut.regionOfInterest.height = videoOut.videoFrameSize.height/videoOut.zoom } onDoubleClicked: { control.toggleVisible() } onClicked: { if (playList.state === "show") return if (playList.state === "hide") playList.state = "ready" else playList.state = "hide" } onMouseXChanged: { cursor_timer.start() if (mouseX > root.width || mouseX < 0 || mouseY > root.height || mouseY < 0) return; if (mouseX < Utils.scaled(20)) { if (playList.state === "hide") { playList.state = "ready" } } if (root.width - mouseX < configPanel.width) { //qt5.6 mouseX is very large if mouse released //console.log("configPanel show: root width: " + root.width + " mouseX: " + mouseX + "panel width: " + configPanel.width) configPanel.state = "show" } else { configPanel.state = "hide" //console.log("configPanel hide: root width: " + root.width + " mouseX: " + mouseX + "panel width: " + configPanel.width) } if (player.playbackState == MediaPlayer.StoppedState || !player.hasVideo) return; if (mouseY < control.y - control.previewHeight) { control.hidePreview() // TODO: check previw hovered too } else { if (pressed) { control.showPreview(mouseX/parent.width) } } } Timer { id: cursor_timer interval: 2000 } } } Text { id: msg objectName: "msg" horizontalAlignment: Text.AlignHCenter font.pixelSize: Utils.scaled(20) style: Text.Outline styleColor: "green" color: "white" anchors.top: root.top width: root.width height: root.height / 4 onTextChanged: { msg_timer.stop() visible = true msg_timer.start() } Timer { id: msg_timer interval: 2000 onTriggered: msg.visible = false } function error(txt) { styleColor = "red" text = txt } function info(txt) { styleColor = "green" text = txt } } Item { anchors.fill: parent focus: true Keys.onPressed: { switch (event.key) { case Qt.Key_M: control.mute = !control.mute break case Qt.Key_Right: player.fastSeek = event.isAutoRepeat player.seek(player.position + 10000) break case Qt.Key_Left: player.fastSeek = event.isAutoRepeat player.seek(player.position - 10000) break case Qt.Key_Up: control.volume = Math.min(2, control.volume+0.05) break case Qt.Key_Down: control.volume = Math.max(0, control.volume-0.05) break case Qt.Key_Space: if (player.playbackState == MediaPlayer.PlayingState) { player.pause() } else if (player.playbackState == MediaPlayer.PausedState){ player.play() } break case Qt.Key_Plus: player.playbackRate += 0.1; console.log("player.playbackRate: " + player.playbackRate); break; case Qt.Key_Minus: player.playbackRate = Math.max(0.1, player.playbackRate - 0.1); break; case Qt.Key_F: control.toggleFullScreen() break case Qt.Key_R: videoOut.orientation += 90 break; case Qt.Key_T: videoOut.orientation -= 90 break; case Qt.Key_C: player.videoCapture.capture() break case Qt.Key_A: if (videoOut.fillMode === VideoOutput.Stretch) { videoOut.fillMode = VideoOutput.PreserveAspectFit } else if (videoOut.fillMode === VideoOutput.PreserveAspectFit) { videoOut.fillMode = VideoOutput.PreserveAspectCrop } else { videoOut.fillMode = VideoOutput.Stretch } break case Qt.Key_O: fileDialog.open() break; case Qt.Key_N: player.stepForward() break case Qt.Key_B: player.stepBackward() break; //case Qt.Key_Back: case Qt.Key_Q: Qt.quit() break } } } DropArea { anchors.fill: root onEntered: { if (!drag.hasUrls) return; console.log(drag.urls) player.source = drag.urls[0] } } Item { id: configPage anchors.right: configPanel.left anchors.rightMargin: -configPanel.anchors.rightMargin*Utils.scaled(20)/configPanel.width //anchors.bottom: control.top y: Math.max(0, Math.min(configPanel.selectedY, root.height - pageLoader.height - control.height)) width: parent.width < 4*configPanel.width ? parent.width - configPanel.width : parent.width/2 + configPanel.width -16 // height: maxHeight readonly property real maxHeight: control.y //- Math.max(0, configPanel.selectedY) Loader { id: pageLoader anchors.right: parent.right width: parent.width focus: true onLoaded: { if (!item) return item.maxHeight = configPage.maxHeight if (item.information) { item.information = { source: player.source, hasAudio: player.hasAudio, hasVideo: player.hasVideo, metaData: player.metaData } } if (item.hasOwnProperty("internalAudioTracks")) item.internalAudioTracks = player.internalAudioTracks if (typeof(item.externalAudioTracks) != "undefined") item.externalAudioTracks = player.externalAudioTracks if ("internalSubtitleTracks" in item) item.internalSubtitleTracks = player.internalSubtitleTracks } } Connections { target: pageLoader.item onVisibleChanged: { if (!pageLoader.item.visible) pageLoader.source = "" } onChannelChanged: player.channelLayout = channel onExternalAudioChanged: player.externalAudio = file onAudioTrackChanged: player.audioTrack = track onSubtitleTrackChanged: player.internalSubtitleTrack = track onBrightnessChanged: videoOut.brightness = target.brightness onContrastChanged: videoOut.contrast = target.contrast onHueChanged: videoOut.hue = target.hue onSaturationChanged: { console.log("saturation: " + target.saturation) videoOut.saturation = target.saturation } } } ConfigPanel { id: configPanel anchors { top: parent.top right: parent.right bottom: control.top } width: Utils.scaled(100) onClicked: { pageLoader.source = selectedUrl if (pageLoader.item) pageLoader.item.visible = true } onSelectedUrlChanged: pageLoader.source = selectedUrl } PlayListPanel { id: playList visible: Qt.platform.os !== "winrt" anchors { top: parent.top left: parent.left bottom: control.top } width: Math.min(parent.width, Utils.scaled(480)) - Utils.scaled(20) Connections { target: player // onStatusChanged: too late to call status is wrong value onDurationChanged: { if (player.duration <= 0) return var url = player.source.toString() if (url.startsWith("winrt:@")) { url = url.substring(url.indexOf(":", 7) + 1); } console.log("duration changed: " + url) playList.addHistory(url, player.duration) } } onPlay: { player.source = source if (start > 0) player.seek(start) } } ControlPanel { id: control anchors { left: parent.left bottom: parent.bottom right: parent.right margins: Utils.scaled(12) } mediaSource: player.source duration: player.duration onSeek: { player.fastSeek = false player.seek(ms) } onSeekForward: { player.fastSeek = false player.seek(player.position + ms) } onSeekBackward: { player.fastSeek = false player.seek(player.position - ms) } onPlay: player.play() onStop: player.stop() onTogglePause: { if (player.playbackState == MediaPlayer.PlayingState) { player.pause() } else { player.play() } } volume: player.volume onOpenFile: fileDialog.open() //IF_QT53 onOpenUrl: urlDialog.open() //ENDIF_QT53 onShowInfo: pageLoader.source = "MediaInfoPage.qml" onShowHelp: pageLoader.source = "About.qml" } //IF_QT53 Dialog { id: urlDialog standardButtons: StandardButton.Open | StandardButton.Cancel title: qsTr("Open a URL") Rectangle { color: "black" anchors.top: parent.top height: Utils.kItemHeight width: parent.width TextInput { id: urlEdit color: "orange" font.pixelSize: Utils.kFontSize anchors.fill: parent } } onAccepted: player.source = urlEdit.displayText } //ENDIF_QT53 FileDialog { id: fileDialog title: "Please choose a media file" selectMultiple: true folder: PlayerConfig.lastFile onAccepted: { var sub, av for (var i = 0; i < fileUrls.length; ++i) { var s = fileUrls[i].toString() if (s.endsWith(".srt") || s.endsWith(".ass") || s.endsWith(".ssa") || s.endsWith(".sub") || s.endsWith(".idx") //vob || s.endsWith(".mpl2") || s.endsWith(".smi") || s.endsWith(".sami") || s.endsWith(".sup") || s.endsWith(".txt")) sub = fileUrls[i] else av = fileUrls[i] } if (sub) { subtitle.autoLoad = false subtitle.file = sub } else { subtitle.autoLoad = PlayerConfig.subtitleAutoLoad subtitle.file = "" } if (av) { player.source = av PlayerConfig.lastFile = av } } } Connections { target: Qt.application onStateChanged: { //since 5.1 if (Qt.platform.os === "winrt" || Qt.platform.os === "winphone") //paused by system return // winrt is handled by system switch (Qt.application.state) { case Qt.ApplicationSuspended: case Qt.ApplicationHidden: player.pause() break default: break } } } Connections { target: PlayerConfig onZeroCopyChanged: { var opt = player.videoCodecOptions if (PlayerConfig.zeroCopy) { opt["copyMode"] = "ZeroCopy" } else { opt["copyMode"] = "OptimizedCopy" //FIXME: CUDA } player.videoCodecOptions = opt } } } QtAV-1.12.0/examples/QMLPlayer/qml/QMLPlayer/utils.js000066400000000000000000000036721312235004300221300ustar00rootroot00000000000000 var kItemWidth = scaled(60) var kItemHeight = scaled(30) var kMargin = scaled(8) var kFontSize = scaled(16) var kSpacing = scaled(4) // "/xxx" will be resolved as qrc:///xxx. while "xxx" is "qrc:///QMLDIR/xxx var resprefix = Qt.resolvedUrl(" ").substring(0, 4) == "qrc:" ? "/" : "" function resurl(s) { //why called twice if in qrc? return resprefix + s } String.prototype.startsWith = function(s) { return this.indexOf(s) === 0; }; String.prototype.endsWith = function(suffix) { return this.indexOf(suffix, this.length - suffix.length) !== -1; }; function fileName(path) { return path.substring(path.lastIndexOf("/") + 1) } function msec2string(t) { t = Math.floor(t/1000) var ss = t%60 t = (t-ss)/60 var mm = t%60 var hh = (t-mm)/60 if (ss < 10) ss = "0" + ss if (mm < 10) mm = "0" + mm if (hh < 10) hh = "0" + hh return hh + ":" + mm +":" + ss } function scaled(x) { //console.log("scaleRatio: " + scaleRatio + "; " + x + ">>>" + x*scaleRatio); return x * scaleRatio; } function htmlEscaped(s) { if (!s) { return ''; } var escaped = ''; var namedHtml = { '38': '&', '60': '<', '62': '>', '34': '"', '160': ' ', '162': '¢', '163': '£', '164': '¤', '169': '©', '174': '®', }; var wasNewLine = 0; for (var i = 0, il = s.length; i < il; ++i) { var c = s.charCodeAt(i); var es = namedHtml[c]; if (typeof es !== 'undefined') { wasNewLine = 0; escaped += es; } else { if (c === 13 || c === 10) { if (wasNewLine == 0) escaped += '
'; wasNewLine++; } else { wasNewLine = 0; escaped += String.fromCharCode(c); } } } return escaped; } QtAV-1.12.0/examples/QMLPlayer/qmlplayer.qrc000066400000000000000000000017201312235004300205400ustar00rootroot00000000000000 qml/QMLPlayer/main.qml qml/QMLPlayer/Button.qml qml/QMLPlayer/ProgressBar.qml qml/QMLPlayer/Slider.qml qml/QMLPlayer/utils.js qml/QMLPlayer/ControlPanel.qml qml/QMLPlayer/VideoCodec.qml qml/QMLPlayer/ConfigPanel.qml qml/QMLPlayer/Page.qml qml/QMLPlayer/SubtitlePage.qml qml/QMLPlayer/AudioPage.qml qml/QMLPlayer/DelegateItem.qml qml/QMLPlayer/Menu.qml qml/QMLPlayer/MediaInfoPage.qml qml/QMLPlayer/About.qml i18n/QMLPlayer_zh_CN.qm qml/QMLPlayer/MiscPage.qml qml/QMLPlayer/PlayListPanel.qml qml/QMLPlayer/EffectPage.qml QtAV-1.12.0/examples/QMLPlayer/qtquick2applicationviewer/000077500000000000000000000000001312235004300232345ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/qtquick2applicationviewer/qtquick2applicationviewer.cpp000066400000000000000000000064601312235004300311570ustar00rootroot00000000000000// checksum 0xc01f version 0x90005 /* This file was generated by the Qt Quick 2 Application wizard of Qt Creator. QtQuick2ApplicationViewer is a convenience class containing mobile device specific code such as screen orientation handling. Also QML paths and debugging are handled here. It is recommended not to modify this file, since newer versions of Qt Creator may offer an updated version of it. */ #include "qtquick2applicationviewer.h" #include #include #include class QtQuick2ApplicationViewerPrivate { QString mainQmlFile; friend class QtQuick2ApplicationViewer; static QString adjustPath(const QString &path); }; QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path) { if (path.startsWith(QLatin1String("qrc:"))) return path; #if defined(Q_OS_IOS) if (!QDir::isAbsolutePath(path)) return QString::fromLatin1("%1/%2") .arg(QCoreApplication::applicationDirPath(), path); #elif defined(Q_OS_MAC) if (!QDir::isAbsolutePath(path)) return QString::fromLatin1("%1/../Resources/%2") .arg(QCoreApplication::applicationDirPath(), path); #elif defined(Q_OS_BLACKBERRY) if (!QDir::isAbsolutePath(path)) return QString::fromLatin1("app/native/%1").arg(path); #elif !defined(Q_OS_ANDROID) QString pathInInstallDir = QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path); if (QFileInfo(pathInInstallDir).exists()) return pathInInstallDir; pathInInstallDir = QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path); if (QFileInfo(pathInInstallDir).exists()) return pathInInstallDir; #elif defined(Q_OS_ANDROID_NO_SDK) return QLatin1String("/data/user/qt/") + path; #endif return path; } QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent) : QQuickView(parent) , d(new QtQuick2ApplicationViewerPrivate()) { connect(engine(), SIGNAL(quit()), SLOT(close())); setResizeMode(QQuickView::SizeRootObjectToView); } QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer() { delete d; } void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file) { if (file.startsWith(QLatin1String("qrc:"))) { d->mainQmlFile = file; setSource(QUrl(d->mainQmlFile)); return; } d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file); #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) QUrl qmlUrl(QUrl(QLatin1String("assets:/")+d->mainQmlFile)); #else QUrl qmlUrl(QUrl::fromLocalFile(d->mainQmlFile)); #endif if (d->mainQmlFile.startsWith(QLatin1String("qrc:/"))) { qmlUrl = QUrl(d->mainQmlFile); } setSource(qmlUrl); } void QtQuick2ApplicationViewer::addImportPath(const QString &path) { engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path)); } void QtQuick2ApplicationViewer::showExpanded() { if (QGuiApplication::platformName() == QLatin1String("qnx") || QGuiApplication::platformName() == QLatin1String("eglfs")) { showFullScreen(); } else { show(); } return; #if defined(Q_OS_QNX) //|| defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_MAEMO) showFullScreen(); #else show(); #endif } QtAV-1.12.0/examples/QMLPlayer/qtquick2applicationviewer/qtquick2applicationviewer.h000066400000000000000000000016241312235004300306210ustar00rootroot00000000000000// checksum 0xfde6 version 0x90005 /* This file was generated by the Qt Quick 2 Application wizard of Qt Creator. QtQuick2ApplicationViewer is a convenience class containing mobile device specific code such as screen orientation handling. Also QML paths and debugging are handled here. It is recommended not to modify this file, since newer versions of Qt Creator may offer an updated version of it. */ #ifndef QTQUICK2APPLICATIONVIEWER_H #define QTQUICK2APPLICATIONVIEWER_H #include class QtQuick2ApplicationViewer : public QQuickView { Q_OBJECT public: explicit QtQuick2ApplicationViewer(QWindow *parent = 0); virtual ~QtQuick2ApplicationViewer(); void setMainQmlFile(const QString &file); void addImportPath(const QString &path); void showExpanded(); private: class QtQuick2ApplicationViewerPrivate *d; }; #endif // QTQUICK2APPLICATIONVIEWER_H QtAV-1.12.0/examples/QMLPlayer/qtquick2applicationviewer/qtquick2applicationviewer.pri000066400000000000000000000143401312235004300311630ustar00rootroot00000000000000# checksum 0x7b0d version 0x90005 # This file was generated by the Qt Quick 2 Application wizard of Qt Creator. # The code below adds the QtQuick2ApplicationViewer to the project and handles # the activation of QML debugging. # It is recommended not to modify this file, since newer versions of Qt Creator # may offer an updated version of it. QT += qml quick SOURCES += $$PWD/qtquick2applicationviewer.cpp HEADERS += $$PWD/qtquick2applicationviewer.h INCLUDEPATH += $$PWD # This file was generated by an application wizard of Qt Creator. # The code below handles deployment to Android and Maemo, aswell as copying # of the application data to shadow build directories on desktop. # It is recommended not to modify this file, since newer versions of Qt Creator # may offer an updated version of it. defineTest(qtcAddDeployment) { for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} greaterThan(QT_MAJOR_VERSION, 4) { itemsources = $${item}.files } else { itemsources = $${item}.sources } $$itemsources = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath= $$eval($${deploymentfolder}.target) export($$itemsources) export($$itempath) DEPLOYMENT += $$item } MAINPROFILEPWD = $$PWD android-no-sdk { for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} itemfiles = $${item}.files $$itemfiles = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target) export($$itemfiles) export($$itempath) INSTALLS += $$item } target.path = /data/user/qt export(target.path) INSTALLS += target } else:android { for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} itemfiles = $${item}.files $$itemfiles = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath = /assets/$$eval($${deploymentfolder}.target) export($$itemfiles) export($$itempath) INSTALLS += $$item } x86 { target.path = /libs/x86 } else: armeabi-v7a { target.path = /libs/armeabi-v7a } else { target.path = /libs/armeabi } export(target.path) INSTALLS += target } else:win32 { copyCommand = for(deploymentfolder, DEPLOYMENTFOLDERS) { source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) source = $$replace(source, /, \\) sourcePathSegments = $$split(source, \\) target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) target = $$replace(target, /, \\) target ~= s,\\\\\\.?\\\\,\\, !isEqual(source,$$target) { !isEmpty(copyCommand):copyCommand += && isEqual(QMAKE_DIR_SEP, \\) { copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" } else { source = $$replace(source, \\\\, /) target = $$OUT_PWD/$$eval($${deploymentfolder}.target) target = $$replace(target, \\\\, /) copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" } } } !isEmpty(copyCommand) { copyCommand = @echo Copying application data... && $$copyCommand copydeploymentfolders.commands = $$copyCommand first.depends = $(first) copydeploymentfolders export(first.depends) export(copydeploymentfolders.commands) QMAKE_EXTRA_TARGETS += first copydeploymentfolders } } else:unix { maemo5 { desktopfile.files = $${TARGET}.desktop desktopfile.path = /usr/share/applications/hildon icon.files = $${TARGET}64.png icon.path = /usr/share/icons/hicolor/64x64/apps } else:!isEmpty(MEEGO_VERSION_MAJOR) { desktopfile.files = $${TARGET}_harmattan.desktop desktopfile.path = /usr/share/applications icon.files = $${TARGET}80.png icon.path = /usr/share/icons/hicolor/80x80/apps } else { # Assumed to be a Desktop Unix copyCommand = for(deploymentfolder, DEPLOYMENTFOLDERS) { source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) source = $$replace(source, \\\\, /) macx { target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) } else { target = $$OUT_PWD/$$eval($${deploymentfolder}.target) } target = $$replace(target, \\\\, /) sourcePathSegments = $$split(source, /) targetFullPath = $$target/$$last(sourcePathSegments) targetFullPath ~= s,/\\.?/,/, !isEqual(source,$$targetFullPath) { !isEmpty(copyCommand):copyCommand += && copyCommand += $(MKDIR) \"$$target\" copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" } } !isEmpty(copyCommand) { copyCommand = @echo Copying application data... && $$copyCommand copydeploymentfolders.commands = $$copyCommand first.depends = $(first) copydeploymentfolders export(first.depends) export(copydeploymentfolders.commands) QMAKE_EXTRA_TARGETS += first copydeploymentfolders } } !isEmpty(target.path) { installPrefix = $${target.path} } else { installPrefix = /opt/$${TARGET} } for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} itemfiles = $${item}.files $$itemfiles = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) export($$itemfiles) export($$itempath) INSTALLS += $$item } !isEmpty(desktopfile.path) { export(icon.files) export(icon.path) export(desktopfile.files) export(desktopfile.path) INSTALLS += icon desktopfile } isEmpty(target.path) { target.path = $${installPrefix}/bin export(target.path) } INSTALLS += target } export (ICON) export (INSTALLS) export (DEPLOYMENT) export (LIBS) export (QMAKE_EXTRA_TARGETS) } QtAV-1.12.0/examples/QMLPlayer/winrt/000077500000000000000000000000001312235004300171665ustar00rootroot00000000000000QtAV-1.12.0/examples/QMLPlayer/winrt/WinPhone8.Package.appxmanifest000066400000000000000000000116121312235004300247610ustar00rootroot00000000000000 QtAV Video Player Lucas Wang assets/logo_store.png 6.3.1 6.3.1 QtAV QMLPlayer assets/logo_store.png .3gp .3gp2 .3gpp .amv .asf .avi .divx .drc .dv .f4v .flv .h264 .m2v .m2t .m2ts .mkv .mov .mp2 .mp3 .mp4 .mpeg .mpg .mpg2 .mpg4 .mts .mtv .mxf .mxg .nsv .nut .nuv .ogg .ogm .ogv .ogx .qt .rm .rmvb .ts .vob .webm .wm .wmv .wtv .y4m .aac .ac3 .flac .wma .wav .m4a .m4v QtAV-1.12.0/examples/QMLPlayer/winrt/WinRT10.Package.appxmanifest000066400000000000000000000124651312235004300243150ustar00rootroot00000000000000 QtAV Video Player Lucas Wang assets/logo_store.png QtAV QMLPlayer QtAV media player assets/logo_store.png .3gp .3gp2 .3gpp .amv .asf .avi .divx .drc .dv .f4v .flv .h264 .m2v .m2t .m2ts .mkv .mov .mp2 .mp3 .mp4 .mpeg .mpg .mpg2 .mpg4 .mts .mtv .mxf .mxg .nsv .nut .nuv .ogg .ogm .ogv .ogx .qt .rm .rmvb .ts .vob .webm .wm .wmv .wtv .y4m .aac .ac3 .flac .wma .wav .m4a .m4v QtAV-1.12.0/examples/QMLPlayer/winrt/WinRT8.Package.appxmanifest000066400000000000000000000114571312235004300242440ustar00rootroot00000000000000 QtAV Video Player Lucas Wang assets/logo_store.png 6.3.0 6.3.0 QtAV QMLPlayer assets/logo_store.png .3gp .3gp2 .3gpp .amv .asf .avi .divx .drc .dv .f4v .flv .h264 .m2v .m2t .m2ts .mkv .mov .mp2 .mp3 .mp4 .mpeg .mpg .mpg2 .mpg4 .mts .mtv .mxf .mxg .nsv .nut .nuv .ogg .ogm .ogv .ogx .qt .rm .rmvb .ts .vob .webm .wm .wmv .wtv .y4m .aac .ac3 .flac .wma .wav .m4a .m4v QtAV-1.12.0/examples/audiopipeline/000077500000000000000000000000001312235004300170445ustar00rootroot00000000000000QtAV-1.12.0/examples/audiopipeline/audiopipeline.pro000066400000000000000000000001651312235004300224170ustar00rootroot00000000000000PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) include(src.pri) QtAV-1.12.0/examples/audiopipeline/main.cpp000066400000000000000000000101611312235004300204730ustar00rootroot00000000000000/****************************************************************************** audiopipeline: this file is part of QtAV examples Copyright (C) 2015-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << QLatin1String("usage: ") << a.applicationFilePath().split(QLatin1String("/")).last().append(QLatin1String(" url")); if (a.arguments().size() < 2) return 0; QScopedPointer ao(new AudioOutput()); AVDemuxer demuxer; demuxer.setMedia(a.arguments().last()); if (!demuxer.load()) { qWarning() << "Failed to load file " << demuxer.fileName(); return 1; } QScopedPointer dec(AudioDecoder::create()); // delete by user dec->setCodecContext(demuxer.audioCodecContext()); //dec->prepare(); if (!dec->open()) qFatal("open decoder error"); int astream = demuxer.audioStream(); Packet pkt; while (!demuxer.atEnd()) { if (!pkt.isValid()) { // continue to decode previous undecoded data if (!demuxer.readFrame() || demuxer.stream() != astream) continue; pkt = demuxer.packet(); } if (!dec->decode(pkt)) { pkt = Packet(); // set invalid to read from demuxer continue; } // decode the rest data in the next loop. read from demuxer if no data remains pkt.data = QByteArray::fromRawData(pkt.data.constData() + pkt.data.size() - dec->undecodedSize(), dec->undecodedSize()); AudioFrame frame(dec->frame()); // why is faster to call frame() for hwdec? no frame() is very slow for VDA if (!frame) continue; //frame.setAudioResampler(dec->resampler()); // if not set, always create a resampler in AudioFrame.to() AudioFormat af(frame.format()); if (ao->isOpen()) { af = ao->audioFormat(); } else { ao->setAudioFormat(af); dec->resampler()->setOutAudioFormat(ao->audioFormat()); // if decoded format is not supported by audio renderer, change decoder output format if (af != ao->audioFormat()) dec->resampler()->prepare(); // now af is supported by audio renderer. it's safe to open if (!ao->open()) qFatal("Open audio output error"); #if 0 // always resample ONCE due to QtAV bug // the first format unsupported frame still need to be converted to a supported format if (!ao->isSupported(frame.format())) frame = frame.to(af); #endif qDebug() << "Input: " << frame.format(); qDebug() << "Output: " << af; } printf("playing: %.3f...\r", frame.timestamp()); fflush(0); // always resample ONCE. otherwise data are all 0x0. QtAV bug frame = frame.to(af); QByteArray data(frame.data()); // plane data. currently only packet sample formats are supported. while (!data.isEmpty()) { ao->play(QByteArray::fromRawData(data.constData(), qMin(data.size(), ao->bufferSize()))); data.remove(0, qMin(data.size(), ao->bufferSize())); } } // dec, ao will be closed in dtor. demuxer will call unload in dtor return 0; } QtAV-1.12.0/examples/audiopipeline/src.pri000066400000000000000000000000701312235004300203440ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle SOURCES += main.cpp QtAV-1.12.0/examples/common/000077500000000000000000000000001312235004300155055ustar00rootroot00000000000000QtAV-1.12.0/examples/common/Config.cpp000066400000000000000000001011661312235004300174230ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "Config.h" #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif #include #include #include #include "common.h" class Config::Data { public: Data() { is_loading = false; if (!Data::name.isEmpty()) file = appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini"); else file = appDataDir() + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini"); if (!QDir(appDataDir()).exists()) { if (!QDir().mkpath(appDataDir())) { qWarning() << "Failed to create appDataDir: " << appDataDir(); } } moveOldCfg(); } void moveOldCfg() { // for old config data migration QString dir_old = qApp->applicationDirPath() + QString::fromLatin1("/data"); if (!QDir(dir_old).exists()) { dir_old = QDir::homePath() + QString::fromLatin1("/.QtAV"); } if (QDir(dir_old).exists()) { if (!QFile(file).exists()) { QString old = dir_old + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini"); if (QFile(old).exists()) { QFile::copy(old, file); QFile::remove(old); } old = dir_old + QString::fromLatin1("/playlist.qds"); if (QFile(old).exists()) { if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/playlist.qds"))) qWarning("error to move old playlist data"); QFile::remove(old); } old = dir_old + QString::fromLatin1("/history.qds"); if (QFile(old).exists()) { if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/history.qds"))) qWarning("error to move old history data"); QFile::remove(old); } } } } void save() { if (is_loading) return; qDebug() << "sync config to " << file; QSettings settings(file, QSettings::IniFormat); // TODO: why crash on mac qt5.4 if call on aboutToQuit() settings.setValue(QString::fromLatin1("log"), log); settings.setValue(QString::fromLatin1("language"), lang); settings.setValue(QString::fromLatin1("last_file"), last_file); settings.setValue(QString::fromLatin1("timeout"), timeout); settings.setValue(QString::fromLatin1("abort_timeout"), abort_timeout); settings.setValue(QString::fromLatin1("force_fps"), force_fps); settings.beginGroup(QString::fromLatin1("decoder")); settings.beginGroup(QString::fromLatin1("video")); settings.setValue(QString::fromLatin1("priority"), video_decoders.join(QString::fromLatin1(" "))); settings.endGroup(); settings.endGroup(); settings.beginGroup(QString::fromLatin1("capture")); settings.setValue(QString::fromLatin1("zeroCopy"), zero_copy); settings.setValue(QString::fromLatin1("dir"), capture_dir); settings.setValue(QString::fromLatin1("format"), capture_fmt); settings.setValue(QString::fromLatin1("quality"), capture_quality); settings.endGroup(); settings.beginGroup(QString::fromLatin1("subtitle")); settings.setValue(QString::fromLatin1("enabled"), subtitle_enabled); settings.setValue(QString::fromLatin1("autoLoad"), subtitle_autoload); settings.setValue(QString::fromLatin1("engines"), subtitle_engines); settings.setValue(QString::fromLatin1("delay"), subtitle_delay); settings.setValue(QString::fromLatin1("font"), subtitle_font); settings.setValue(QString::fromLatin1("color"), subtitle_color); settings.setValue(QString::fromLatin1("outline_color"), subtitle_outline_color); settings.setValue(QString::fromLatin1("outline"), subtitle_outline); settings.setValue(QString::fromLatin1("bottom margin"), subtilte_bottom_margin); settings.beginGroup(QString::fromLatin1("ass")); settings.setValue(QString::fromLatin1("font_file"), ass_font_file); settings.setValue(QString::fromLatin1("force_font_file"), ass_force_font_file); settings.setValue(QString::fromLatin1("fonts_dir"), ass_fonts_dir); settings.endGroup(); settings.endGroup(); settings.beginGroup(QString::fromLatin1("preview")); settings.setValue(QString::fromLatin1("enabled"), preview_enabled); settings.setValue(QString::fromLatin1("width"), preview_w); settings.setValue(QString::fromLatin1("height"), preview_h); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avformat")); settings.setValue(QString::fromLatin1("enable"), avformat_on); settings.setValue(QString::fromLatin1("avioflags"), direct ? QString::fromLatin1("direct") : QString::fromLatin1("0")); settings.setValue(QString::fromLatin1("probesize"), probe_size); settings.setValue(QString::fromLatin1("analyzeduration"), analyze_duration); settings.setValue(QString::fromLatin1("extra"), avformat_extra); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avfilterVideo")); settings.setValue(QString::fromLatin1("enable"), avfilterVideo_on); settings.setValue(QString::fromLatin1("options"), avfilterVideo); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avfilterAudio")); settings.setValue(QString::fromLatin1("enable"), avfilterAudio_on); settings.setValue(QString::fromLatin1("options"), avfilterAudio); settings.endGroup(); settings.beginGroup(QString::fromLatin1("opengl")); settings.setValue(QString::fromLatin1("egl"), egl); const char* glname = Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).valueToKey(opengl); settings.setValue(QString::fromLatin1("type"), QString::fromLatin1(glname)); settings.setValue(QString::fromLatin1("angle_platform"), angle_dx); settings.endGroup(); settings.beginGroup(QString::fromLatin1("shader")); settings.setValue(QString::fromLatin1("enable"), user_shader); settings.setValue(QString::fromLatin1("fbo"), fbo); settings.setValue(QString::fromLatin1("fragHeader"), frag_header); settings.setValue(QString::fromLatin1("fragSample"), frag_sample); settings.setValue(QString::fromLatin1("fragPostProcess"), frag_pp); settings.endGroup(); settings.beginGroup(QString::fromLatin1("buffer")); settings.setValue(QString::fromLatin1("value"), buffer_value); settings.endGroup(); qDebug() << "sync end"; } QString file; bool is_loading; qreal force_fps; QStringList video_decoders; bool zero_copy; QString last_file; QString capture_dir; QString capture_fmt; int capture_quality; bool avformat_on; bool direct; unsigned int probe_size; int analyze_duration; QString avformat_extra; bool avfilterVideo_on; QString avfilterVideo; bool avfilterAudio_on; QString avfilterAudio; QStringList subtitle_engines; bool subtitle_autoload; bool subtitle_enabled; QFont subtitle_font; QColor subtitle_color, subtitle_outline_color; bool subtitle_outline; int subtilte_bottom_margin; qreal subtitle_delay; bool ass_force_font_file; QString ass_font_file; QString ass_fonts_dir; bool preview_enabled; int preview_w, preview_h; bool egl; Config::OpenGLType opengl; QString angle_dx; bool abort_timeout; qreal timeout; int buffer_value; QString log; QString lang; QVariantList history; bool user_shader; bool fbo; QString frag_header; QString frag_sample; QString frag_pp; static QString name; }; QString Config::Data::name; Config& Config::instance() { static Config cfg; return cfg; } void Config::setName(const QString &name) { Config::Data::name = name; } QString Config::getName() { return Config::Data::name; } QString Config::defaultConfigFile() { return appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini");; } Config::Config(QObject *parent) : QObject(parent) , mpData(new Data()) { // DO NOT call save() in dtor because it's a singleton and may be deleted later than qApp, QFont is not valid connect(qApp, SIGNAL(aboutToQuit()), SLOT(save())); //FIXME: what if qapp not ready reload(); } Config::~Config() { delete mpData; } QString Config::defaultDir() { return appDataDir(); } bool Config::reset() { QFile cf(mpData->file); if (!cf.remove()) { qWarning() << "Failed to remove config file: " << cf.errorString(); return false; } reload(); save(); return true; } void Config::reload() { QSqlDatabase db(QSqlDatabase::database()); if (!db.isOpen()) { db = QSqlDatabase::addDatabase(QString::fromUtf8("QSQLITE")); db.setDatabaseName(appDataDir().append(QString("/%1.db").arg(mpData->name))); if (!db.open()) qWarning("error open db"); db.exec("CREATE TABLE IF NOT EXISTS history (url TEXT primary key, start BIGINT, duration BIGINT)"); } QSqlQuery query(db.exec(QString::fromUtf8("SELECT * FROM history"))); while (query.next()) { QVariantMap var; var[QString::fromUtf8("url")] = query.value(0).toString(); var[QString::fromUtf8("start")] = query.value(1).toLongLong(); var[QString::fromUtf8("duration")] = query.value(2).toLongLong(); mpData->history.append(var); } mpData->is_loading = true; QSettings settings(mpData->file, QSettings::IniFormat); setLogLevel(settings.value(QString::fromLatin1("log"), QString()).toString()); setLanguage(settings.value(QString::fromLatin1("language"), #if QT_VERSION == QT_VERSION_CHECK(5, 6, 0) && defined(Q_OS_WINPHONE) //qt bug QString::fromLatin1("en_US") #else QString::fromLatin1("system") #endif ).toString()); setLastFile(settings.value(QString::fromLatin1("last_file"), QString()).toString()); setTimeout(settings.value(QString::fromLatin1("timeout"), 30.0).toReal()); setAbortOnTimeout(settings.value(QString::fromLatin1("abort_timeout"), true).toBool()); setForceFrameRate(settings.value(QString::fromLatin1("force_fps"), 0.0).toReal()); settings.beginGroup(QString::fromLatin1("decoder")); settings.beginGroup(QString::fromLatin1("video")); QString decs_default(QString::fromLatin1("FFmpeg")); setDecoderPriorityNames(settings.value(QString::fromLatin1("priority"), decs_default).toString().split(QString::fromLatin1(" "), QString::SkipEmptyParts)); setZeroCopy(settings.value(QString::fromLatin1("zeroCopy"), true).toBool()); settings.endGroup(); //video settings.endGroup(); //decoder settings.beginGroup(QString::fromLatin1("capture")); setCaptureDir(settings.value(QString::fromLatin1("dir"), QString()).toString()); if (captureDir().isEmpty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) setCaptureDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); #else setCaptureDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); #endif } setCaptureFormat(settings.value(QString::fromLatin1("format"), QString::fromLatin1("png")).toString()); setCaptureQuality(settings.value(QString::fromLatin1("quality"), 100).toInt()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("subtitle")); setSubtitleAutoLoad(settings.value(QString::fromLatin1("autoLoad"), true).toBool()); setSubtitleEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool()); setSubtitleEngines(settings.value(QString::fromLatin1("engines"), QStringList() << QString::fromLatin1("FFmpeg") << QString::fromLatin1("LibASS")).toStringList()); setSubtitleDelay(settings.value(QString::fromLatin1("delay"), 0.0).toInt()); QFont f; f.setPointSize(20); f.setBold(true); setSubtitleFont(settings.value(QString::fromLatin1("font"), f).value()); setSubtitleColor(settings.value(QString::fromLatin1("color"), QColor("white")).value()); setSubtitleOutlineColor(settings.value(QString::fromLatin1("outline_color"), QColor("blue")).value()); setSubtitleOutline(settings.value(QString::fromLatin1("outline"), true).toBool()); setSubtitleBottomMargin(settings.value(QString::fromLatin1("bottom margin"), 8).toInt()); settings.beginGroup(QString::fromLatin1("ass")); setAssFontFile(settings.value(QString::fromLatin1("font_file"), QString()).toString()); setAssFontFileForced(settings.value(QString::fromLatin1("force_font_file"), false).toBool()); setAssFontsDir(settings.value(QString::fromLatin1("fonts_dir"), QString()).toString()); settings.endGroup(); settings.endGroup(); settings.beginGroup(QString::fromLatin1("preview")); setPreviewEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool()); setPreviewWidth(settings.value(QString::fromLatin1("width"), 160).toInt()); setPreviewHeight(settings.value(QString::fromLatin1("height"), 90).toInt()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avformat")); setAvformatOptionsEnabled(settings.value(QString::fromLatin1("enable"), false).toBool()); reduceBuffering(settings.value(QString::fromLatin1("avioflags"), 0).toString() == QLatin1String("direct")); probeSize(settings.value(QString::fromLatin1("probesize"), 5000000).toUInt()); analyzeDuration(settings.value(QString::fromLatin1("analyzeduration"), 5000000).toInt()); avformatExtra(settings.value(QString::fromLatin1("extra"), QString()).toString()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avfilterVideo")); avfilterVideoEnable(settings.value(QString::fromLatin1("enable"), true).toBool()); avfilterVideoOptions(settings.value(QString::fromLatin1("options"), QString()).toString()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("avfilterAudio")); avfilterAudioEnable(settings.value(QString::fromLatin1("enable"), true).toBool()); avfilterAudioOptions(settings.value(QString::fromLatin1("options"), QString()).toString()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("opengl")); setEGL(settings.value(QString::fromLatin1("egl"), false).toBool()); const QString glname = settings.value(QString::fromLatin1("type"), QString::fromLatin1("OpenGLES")).toString(); setOpenGLType((Config::OpenGLType)Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).keysToValue(glname.toLatin1().constData())); // d3d11 bad performance (gltexsubimage2d) setANGLEPlatform(settings.value(QString::fromLatin1("angle_platform"), QString::fromLatin1("d3d9")).toString()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("shader")); setUserShaderEnabled(settings.value(QString::fromLatin1("enable"), false).toBool()); setIntermediateFBO(settings.value(QString::fromLatin1("fbo"), false).toBool()); setFragHeader(settings.value(QString::fromLatin1("fragHeader"), QString()).toString()); setFragSample(settings.value(QString::fromLatin1("fragSample"), QString::fromLatin1("// horizontal mirror effect\n" "vec4 sample2d(sampler2D tex, vec2 pos, int p) {\n" " return texture(tex, vec2(1.0-pos.x, pos.y));\n" "}")).toString()); setFragPostProcess(settings.value(QString::fromLatin1("fragPostProcess"), QString::fromLatin1("//negate color effect\n" "gl_FragColor.rgb = vec3(1.0-gl_FragColor.r, 1.0-gl_FragColor.g, 1.0-gl_FragColor.b);")).toString()); settings.endGroup(); settings.beginGroup(QString::fromLatin1("buffer")); setBufferValue(settings.value(QString::fromLatin1("value"), -1).toInt()); settings.endGroup(); mpData->is_loading = false; } qreal Config::forceFrameRate() const { return mpData->force_fps; } Config& Config::setForceFrameRate(qreal value) { if (mpData->force_fps == value) return *this; mpData->force_fps = value; Q_EMIT forceFrameRateChanged(); Q_EMIT changed(); return *this; } QStringList Config::decoderPriorityNames() const { return mpData->video_decoders; } Config& Config::setDecoderPriorityNames(const QStringList &value) { if (mpData->video_decoders == value) { qDebug("decoderPriority not changed"); return *this; } mpData->video_decoders = value; Q_EMIT decoderPriorityNamesChanged(); Q_EMIT changed(); mpData->save(); return *this; } bool Config::zeroCopy() const { return mpData->zero_copy; } Config& Config::setZeroCopy(bool value) { if (mpData->zero_copy == value) return *this; mpData->zero_copy = value; Q_EMIT zeroCopyChanged(); Q_EMIT changed(); mpData->save(); return *this; } QString Config::captureDir() const { return mpData->capture_dir; } Config& Config::setCaptureDir(const QString& dir) { if (mpData->capture_dir == dir) return *this; mpData->capture_dir = dir; Q_EMIT captureDirChanged(dir); Q_EMIT changed(); return *this; } QString Config::captureFormat() const { return mpData->capture_fmt; } Config& Config::setCaptureFormat(const QString& format) { if (mpData->capture_fmt == format) return *this; mpData->capture_fmt = format; Q_EMIT captureFormatChanged(format); Q_EMIT changed(); return *this; } // only works for non-yuv capture int Config::captureQuality() const { return mpData->capture_quality; } Config& Config::setCaptureQuality(int quality) { if (mpData->capture_quality == quality) return *this; mpData->capture_quality = quality; Q_EMIT captureQualityChanged(quality); Q_EMIT changed(); return *this; } QStringList Config::subtitleEngines() const { return mpData->subtitle_engines; } Config& Config::setSubtitleEngines(const QStringList &value) { if (mpData->subtitle_engines == value) return *this; mpData->subtitle_engines = value; Q_EMIT subtitleEnginesChanged(); Q_EMIT changed(); return *this; } bool Config::subtitleAutoLoad() const { return mpData->subtitle_autoload; } Config& Config::setSubtitleAutoLoad(bool value) { if (mpData->subtitle_autoload == value) return *this; mpData->subtitle_autoload = value; Q_EMIT subtitleAutoLoadChanged(); Q_EMIT changed(); return *this; } bool Config::subtitleEnabled() const { return mpData->subtitle_enabled; } Config& Config::setSubtitleEnabled(bool value) { if (mpData->subtitle_enabled == value) return *this; mpData->subtitle_enabled = value; Q_EMIT subtitleEnabledChanged(); Q_EMIT changed(); return *this; } QFont Config::subtitleFont() const { return mpData->subtitle_font; } Config& Config::setSubtitleFont(const QFont& value) { if (mpData->subtitle_font == value) return *this; mpData->subtitle_font = value; Q_EMIT subtitleFontChanged(); Q_EMIT changed(); return *this; } bool Config::subtitleOutline() const { return mpData->subtitle_outline; } Config& Config::setSubtitleOutline(bool value) { if (mpData->subtitle_outline == value) return *this; mpData->subtitle_outline = value; Q_EMIT subtitleOutlineChanged(); Q_EMIT changed(); return *this; } QColor Config::subtitleColor() const { return mpData->subtitle_color; } Config& Config::setSubtitleColor(const QColor& value) { if (mpData->subtitle_color == value) return *this; mpData->subtitle_color = value; Q_EMIT subtitleColorChanged(); Q_EMIT changed(); return *this; } QColor Config::subtitleOutlineColor() const { return mpData->subtitle_outline_color; } Config& Config::setSubtitleOutlineColor(const QColor& value) { if (mpData->subtitle_outline_color == value) return *this; mpData->subtitle_outline_color = value; Q_EMIT subtitleOutlineColorChanged(); Q_EMIT changed(); return *this; } int Config::subtitleBottomMargin() const { return mpData->subtilte_bottom_margin; } Config& Config::setSubtitleBottomMargin(int value) { if (mpData->subtilte_bottom_margin == value) return *this; mpData->subtilte_bottom_margin = value; Q_EMIT subtitleBottomMarginChanged(); Q_EMIT changed(); return *this; } qreal Config::subtitleDelay() const { return mpData->subtitle_delay; } Config& Config::setSubtitleDelay(qreal value) { if (mpData->subtitle_delay == value) return *this; mpData->subtitle_delay = value; Q_EMIT subtitleDelayChanged(); Q_EMIT changed(); return *this; } QString Config::assFontFile() const { return mpData->ass_font_file; } Config& Config::setAssFontFile(const QString &value) { if (mpData->ass_font_file == value) return *this; mpData->ass_font_file = value; Q_EMIT assFontFileChanged(); Q_EMIT changed(); return *this; } QString Config::assFontsDir() const { return mpData->ass_fonts_dir; } Config& Config::setAssFontsDir(const QString &value) { if (mpData->ass_fonts_dir == value) return *this; mpData->ass_fonts_dir = value; Q_EMIT assFontsDirChanged(); Q_EMIT changed(); return *this; } bool Config::isAssFontFileForced() const { return mpData->ass_force_font_file; } Config& Config::setAssFontFileForced(bool value) { if (mpData->ass_force_font_file == value) return *this; mpData->ass_force_font_file = value; Q_EMIT assFontFileForcedChanged(); Q_EMIT changed(); return *this; } bool Config::previewEnabled() const { return mpData->preview_enabled; } Config& Config::setPreviewEnabled(bool value) { if (mpData->preview_enabled == value) return *this; mpData->preview_enabled = value; Q_EMIT previewEnabledChanged(); Q_EMIT changed(); return *this; } int Config::previewWidth() const { return mpData->preview_w; } Config& Config::setPreviewWidth(int value) { if (mpData->preview_w == value) return *this; mpData->preview_w = value; Q_EMIT previewWidthChanged(); Q_EMIT changed(); return *this; } int Config::previewHeight() const { return mpData->preview_h; } Config& Config::setPreviewHeight(int value) { if (mpData->preview_h == value) return *this; mpData->preview_h = value; Q_EMIT previewHeightChanged(); Q_EMIT changed(); return *this; } QVariantHash Config::avformatOptions() const { QVariantHash vh; if (!mpData->avformat_extra.isEmpty()) { QStringList s(mpData->avformat_extra.split(QString::fromLatin1(" "))); for (int i = 0; i < s.size(); ++i) { int eq = s[i].indexOf(QLatin1String("=")); if (eq < 0) { continue; } else { vh[s[i].mid(0, eq)] = s[i].mid(eq+1); } } } if (mpData->probe_size > 0) { vh[QString::fromLatin1("probesize")] = mpData->probe_size; } if (mpData->analyze_duration) { vh[QString::fromLatin1("analyzeduration")] = mpData->analyze_duration; } if (mpData->direct) { vh[QString::fromLatin1("avioflags")] = QString::fromLatin1("direct"); }; return vh; } bool Config::avformatOptionsEnabled() const { return mpData->avformat_on; } Config& Config::setAvformatOptionsEnabled(bool value) { if (mpData->avformat_on == value) return *this; mpData->avformat_on = value; Q_EMIT avformatOptionsEnabledChanged(); Q_EMIT changed(); return *this; } unsigned int Config::probeSize() const { return mpData->probe_size; } Config& Config::probeSize(unsigned int ps) { mpData->probe_size = ps; return *this; } int Config::analyzeDuration() const { return mpData->analyze_duration; } Config& Config::analyzeDuration(int ad) { mpData->analyze_duration = ad; return *this; } bool Config::reduceBuffering() const { return mpData->direct; } Config& Config::reduceBuffering(bool y) { mpData->direct = y; return *this; } QString Config::avformatExtra() const { return mpData->avformat_extra; } Config& Config::avformatExtra(const QString &text) { mpData->avformat_extra = text; return *this; } QString Config::avfilterVideoOptions() const { return mpData->avfilterVideo; } Config& Config::avfilterVideoOptions(const QString& options) { if (mpData->avfilterVideo == options) return *this; mpData->avfilterVideo = options; Q_EMIT avfilterVideoChanged(); Q_EMIT changed(); return *this; } bool Config::avfilterVideoEnable() const { return mpData->avfilterVideo_on; } Config& Config::avfilterVideoEnable(bool e) { if (mpData->avfilterVideo_on == e) return *this; mpData->avfilterVideo_on = e; Q_EMIT avfilterVideoChanged(); Q_EMIT changed(); return *this; } QString Config::avfilterAudioOptions() const { return mpData->avfilterAudio; } Config& Config::avfilterAudioOptions(const QString& options) { if (mpData->avfilterAudio == options) return *this; mpData->avfilterAudio = options; Q_EMIT avfilterAudioChanged(); Q_EMIT changed(); return *this; } bool Config::avfilterAudioEnable() const { return mpData->avfilterAudio_on; } Config& Config::avfilterAudioEnable(bool e) { if (mpData->avfilterAudio_on == e) return *this; mpData->avfilterAudio_on = e; Q_EMIT avfilterAudioChanged(); Q_EMIT changed(); return *this; } bool Config::isEGL() const { return mpData->egl; } Config& Config::setEGL(bool value) { if (mpData->egl == value) return *this; mpData->egl = value; Q_EMIT EGLChanged(); Q_EMIT changed(); return *this; } Config::OpenGLType Config::openGLType() const { return mpData->opengl; } Config& Config::setOpenGLType(OpenGLType value) { if (mpData->opengl == value) return *this; mpData->opengl = value; Q_EMIT openGLTypeChanged(); Q_EMIT changed(); return *this; } QString Config::getANGLEPlatform() const { return mpData->angle_dx; } Config& Config::setANGLEPlatform(const QString& value) { if (mpData->angle_dx == value) return *this; mpData->angle_dx = value; Q_EMIT ANGLEPlatformChanged(); Q_EMIT changed(); return *this; } bool Config::userShaderEnabled() const { return mpData->user_shader; } Config& Config::setUserShaderEnabled(bool value) { if (mpData->user_shader == value) return *this; mpData->user_shader = value; Q_EMIT userShaderEnabledChanged(); Q_EMIT changed(); return *this; } bool Config::intermediateFBO() const { return mpData->fbo; } Config& Config::setIntermediateFBO(bool value) { if (mpData->fbo == value) return *this; mpData->fbo = value; Q_EMIT intermediateFBOChanged(); Q_EMIT changed(); return *this; } QString Config::fragHeader() const { return mpData->frag_header; } Config& Config::setFragHeader(const QString &text) { if (mpData->frag_header == text) return *this; mpData->frag_header = text; Q_EMIT fragHeaderChanged(); Q_EMIT changed(); return *this; } QString Config::fragSample() const { return mpData->frag_sample; } Config& Config::setFragSample(const QString &text) { if (mpData->frag_sample == text) return *this; mpData->frag_sample = text; Q_EMIT fragSampleChanged(); Q_EMIT changed(); return *this; } QString Config::fragPostProcess() const { return mpData->frag_pp; } Config& Config::setFragPostProcess(const QString &text) { if (mpData->frag_pp == text) return *this; mpData->frag_pp = text; Q_EMIT fragPostProcessChanged(); Q_EMIT changed(); return *this; } int Config::bufferValue() const { return mpData->buffer_value; } Config& Config::setBufferValue(int value) { if (mpData->buffer_value == value) return *this; mpData->buffer_value = value; Q_EMIT bufferValueChanged(); Q_EMIT changed(); return *this; } qreal Config::timeout() const { return mpData->timeout; } Config& Config::setTimeout(qreal value) { if (mpData->timeout == value) return *this; mpData->timeout = value; Q_EMIT timeoutChanged(); Q_EMIT changed(); return *this; } QString Config::logLevel() const { return mpData->log; } Config& Config::setLogLevel(const QString& value) { if (mpData->log == value.toLower()) return *this; mpData->log = value.toLower(); Q_EMIT logLevelChanged(); Q_EMIT changed(); return *this; } QString Config::language() const { return mpData->lang; } Config& Config::setLanguage(const QString& value) { if (mpData->lang == value) return *this; mpData->lang = value; Q_EMIT languageChanged(); Q_EMIT changed(); return *this; } QVariantList Config::history() const { return mpData->history; } void Config::addHistory(const QVariantMap &value) { mpData->history.prepend(value); Q_EMIT historyChanged(); QSqlDatabase db = QSqlDatabase::database(); QSqlQuery query(db); if (!query.prepare(QString::fromUtf8("INSERT INTO history (url, start, duration) " "VALUES (:url, :start, :duration)"))) { qWarning("error prepare sql query"); } query.bindValue(QString::fromUtf8(":url"), value.value("url").toString()); query.bindValue(QString::fromUtf8(":start"), value.value("start").toLongLong()); query.bindValue(QString::fromUtf8(":duration"), value.value("duration").toLongLong()); if (!query.exec()) qWarning("failed to add history: %d", db.isOpen()); } void Config::removeHistory(const QString &url) { QVariantList::Iterator it = mpData->history.begin(); bool change = false; while (it != mpData->history.end()) { if (it->toMap().value("url") != url) { ++it; continue; } it = mpData->history.erase(it); change = true; } if (!change) return; Q_EMIT historyChanged(); QSqlDatabase db = QSqlDatabase::database(); QSqlQuery query(db); query.prepare(QString::fromUtf8("DELETE FROM history WHERE url = :url")); query.bindValue(QString::fromUtf8(":url"), url); if (!query.exec()) qWarning("failed to remove history"); } void Config::clearHistory() { if (mpData->history.isEmpty()) return; mpData->history.clear(); Q_EMIT historyChanged(); QSqlDatabase db = QSqlDatabase::database(); QSqlQuery query(db); query.prepare(QString::fromUtf8("DELETE FROM history")); // 'TRUNCATE table history' is faster if (!query.exec()) qWarning("failed to clear history"); } bool Config::abortOnTimeout() const { return mpData->abort_timeout; } Config& Config::setAbortOnTimeout(bool value) { if (mpData->abort_timeout == value) return *this; mpData->abort_timeout = value; Q_EMIT abortOnTimeoutChanged(); Q_EMIT changed(); return *this; } QString Config::lastFile() const { return mpData->last_file; } Config& Config::setLastFile(const QString &value) { if (mpData->last_file == value) return *this; mpData->last_file = value; Q_EMIT lastFileChanged(); Q_EMIT changed(); return *this; } void Config::save() { mpData->save(); } QtAV-1.12.0/examples/common/Config.h000066400000000000000000000307041312235004300170670ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYER_CONFIG_H #define PLAYER_CONFIG_H #include "common_export.h" #include #include #include #include #include //TODO: use hash to simplify api /* * MVC model. signals from Config notify ui update. signals from ui does not change Config unless ui changes applyed by XXXPage.apply() * signals from ui will emit Config::xxxChanged() with the value in ui. ui cancel the change also emit it with the value stores in Config. * apply() will change the value in Config */ class COMMON_EXPORT Config : public QObject { Q_OBJECT Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged) // last file opened by file dialog Q_PROPERTY(QString lastFile READ lastFile WRITE setLastFile NOTIFY lastFileChanged) Q_PROPERTY(qreal forceFrameRate READ forceFrameRate WRITE setForceFrameRate NOTIFY forceFrameRateChanged) Q_PROPERTY(QStringList decoderPriorityNames READ decoderPriorityNames WRITE setDecoderPriorityNames NOTIFY decoderPriorityNamesChanged) Q_PROPERTY(bool zeroCopy READ zeroCopy WRITE setZeroCopy NOTIFY zeroCopyChanged) Q_PROPERTY(QString captureDir READ captureDir WRITE setCaptureDir NOTIFY captureDirChanged) Q_PROPERTY(QString captureFormat READ captureFormat WRITE setCaptureFormat NOTIFY captureFormatChanged) Q_PROPERTY(int captureQuality READ captureQuality WRITE setCaptureQuality NOTIFY captureQualityChanged) Q_PROPERTY(QStringList subtitleEngines READ subtitleEngines WRITE setSubtitleEngines NOTIFY subtitleEnginesChanged) Q_PROPERTY(bool subtitleAutoLoad READ subtitleAutoLoad WRITE setSubtitleAutoLoad NOTIFY subtitleAutoLoadChanged) Q_PROPERTY(bool subtitleEnabled READ subtitleEnabled WRITE setSubtitleEnabled NOTIFY subtitleEnabledChanged) Q_PROPERTY(QFont subtitleFont READ subtitleFont WRITE setSubtitleFont NOTIFY subtitleFontChanged) Q_PROPERTY(QColor subtitleColor READ subtitleColor WRITE setSubtitleColor NOTIFY subtitleColorChanged) Q_PROPERTY(QColor subtitleOutlineColor READ subtitleOutlineColor WRITE setSubtitleOutlineColor NOTIFY subtitleOutlineColorChanged) Q_PROPERTY(bool subtitleOutline READ subtitleOutline WRITE setSubtitleOutline NOTIFY subtitleOutlineChanged) Q_PROPERTY(int subtitleBottomMargin READ subtitleBottomMargin WRITE setSubtitleBottomMargin NOTIFY subtitleBottomMarginChanged) Q_PROPERTY(qreal subtitleDelay READ subtitleDelay WRITE setSubtitleDelay NOTIFY subtitleDelayChanged) // font properties for libass engine Q_PROPERTY(QString assFontFile READ assFontFile WRITE setAssFontFile NOTIFY assFontFileChanged) Q_PROPERTY(QString assFontsDir READ assFontsDir WRITE setAssFontsDir NOTIFY assFontsDirChanged) Q_PROPERTY(bool assFontFileForced READ isAssFontFileForced WRITE setAssFontFileForced NOTIFY assFontFileForcedChanged) Q_PROPERTY(bool previewEnabled READ previewEnabled WRITE setPreviewEnabled NOTIFY previewEnabledChanged) Q_PROPERTY(int previewWidth READ previewWidth WRITE setPreviewWidth NOTIFY previewWidthChanged) Q_PROPERTY(int previewHeight READ previewHeight WRITE setPreviewHeight NOTIFY previewHeightChanged) Q_PROPERTY(bool EGL READ isEGL WRITE setEGL NOTIFY EGLChanged) Q_PROPERTY(OpenGLType openGLType READ openGLType WRITE setOpenGLType NOTIFY openGLTypeChanged) Q_PROPERTY(QString ANGLEPlatform READ getANGLEPlatform WRITE setANGLEPlatform NOTIFY ANGLEPlatformChanged) Q_PROPERTY(bool avformatOptionsEnabled READ avformatOptionsEnabled WRITE setAvformatOptionsEnabled NOTIFY avformatOptionsEnabledChanged) Q_PROPERTY(qreal timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged) Q_PROPERTY(int bufferValue READ bufferValue WRITE setBufferValue NOTIFY bufferValueChanged) Q_PROPERTY(QString logLevel READ logLevel WRITE setLogLevel NOTIFY logLevelChanged) Q_ENUMS(OpenGLType) Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged) Q_PROPERTY(bool userShaderEnabled READ userShaderEnabled WRITE setUserShaderEnabled NOTIFY userShaderEnabledChanged) Q_PROPERTY(bool intermediateFBO READ intermediateFBO WRITE setIntermediateFBO NOTIFY intermediateFBOChanged) Q_PROPERTY(QString fragHeader READ fragHeader WRITE setFragHeader NOTIFY fragHeaderChanged) Q_PROPERTY(QString fragSample READ fragSample WRITE setFragSample NOTIFY fragSampleChanged) Q_PROPERTY(QString fragPostProcess READ fragPostProcess WRITE setFragPostProcess NOTIFY fragPostProcessChanged) public: enum OpenGLType { // currently only for windows Auto, Desktop, OpenGLES, Software }; static Config& instance(); static void setName(const QString& name); // config file base name static QString getName(); /*! * \brief defaultConfigFile * Config file name is $appname.ini. Must call Config::setName() first */ static QString defaultConfigFile(); static QString defaultDir(); Q_INVOKABLE bool reset(); void reload(); //void loadFromFile(const QString& file); QString lastFile() const; Config& setLastFile(const QString& value); qreal forceFrameRate() const; Config& setForceFrameRate(qreal value); // in priority order. the same order as displayed in ui QStringList decoderPriorityNames() const; Config& setDecoderPriorityNames(const QStringList& names); bool zeroCopy() const; Config& setZeroCopy(bool value); QString captureDir() const; Config& setCaptureDir(const QString& dir); /*! * \brief captureFormat * can be "yuv" to capture yuv image without convertion. the suffix is the yuv format, e.g. "yuv420p", "nv12" * or can be "jpg", "png" * \return */ QString captureFormat() const; Config& setCaptureFormat(const QString& format); // only works for non-yuv capture. value: -1~100, -1: default int captureQuality() const; Config& setCaptureQuality(int quality); QStringList subtitleEngines() const; Config& setSubtitleEngines(const QStringList& value); bool subtitleAutoLoad() const; Config& setSubtitleAutoLoad(bool value); bool subtitleEnabled() const; Config& setSubtitleEnabled(bool value); QFont subtitleFont() const; Config& setSubtitleFont(const QFont& value); bool subtitleOutline() const; Config& setSubtitleOutline(bool value); QColor subtitleColor() const; Config& setSubtitleColor(const QColor& value); QColor subtitleOutlineColor() const; Config& setSubtitleOutlineColor(const QColor& value); int subtitleBottomMargin() const; Config& setSubtitleBottomMargin(int value); qreal subtitleDelay() const; Config& setSubtitleDelay(qreal value); QString assFontFile() const; Config& setAssFontFile(const QString& value); QString assFontsDir() const; Config& setAssFontsDir(const QString& value); bool isAssFontFileForced() const; Config& setAssFontFileForced(bool value); bool previewEnabled() const; Config& setPreviewEnabled(bool value); int previewWidth() const; Config& setPreviewWidth(int value); int previewHeight() const; Config& setPreviewHeight(int value); QVariantHash avformatOptions() const; bool avformatOptionsEnabled() const; Config& setAvformatOptionsEnabled(bool value); int analyzeDuration() const; Config& analyzeDuration(int ad); unsigned int probeSize() const; Config& probeSize(unsigned int ps); bool reduceBuffering() const; Config& reduceBuffering(bool y); QString avformatExtra() const; Config& avformatExtra(const QString& text); QString avfilterVideoOptions() const; Config& avfilterVideoOptions(const QString& options); bool avfilterVideoEnable() const; Config& avfilterVideoEnable(bool e); QString avfilterAudioOptions() const; Config& avfilterAudioOptions(const QString& options); bool avfilterAudioEnable() const; Config& avfilterAudioEnable(bool e); // currently only for xcb bool isEGL() const; Config& setEGL(bool value); // can be "Desktop", "OpenGLES", "Software" OpenGLType openGLType() const; Config& setOpenGLType(OpenGLType value); QString getANGLEPlatform() const; Config& setANGLEPlatform(const QString &value); // ms >0. default 30000ms qreal timeout() const; Config& setTimeout(qreal value); bool abortOnTimeout() const; Config& setAbortOnTimeout(bool value); // <0: auto int bufferValue() const; Config& setBufferValue(int value); // can be: "", "off", "debug", "warning", "critical", "fatal", "all" QString logLevel() const; Config& setLogLevel(const QString& value); QString language() const; Config& setLanguage(const QString& value); Q_INVOKABLE QVariant operator ()(const QString& key) const; Q_INVOKABLE Config& operator ()(const QString& key, const QVariant& value); /// history will not be clear in reset() QVariantList history() const; // {url: urlString, start: ms, duration: ms} Q_INVOKABLE void addHistory(const QVariantMap& value); Q_INVOKABLE void removeHistory(const QString& url); Q_INVOKABLE void clearHistory(); Config& setUserShaderEnabled(bool value); bool userShaderEnabled() const; Config& setIntermediateFBO(bool value); bool intermediateFBO() const; Config& setFragHeader(const QString& text); QString fragHeader() const; Config& setFragSample(const QString& text); QString fragSample() const; Config& setFragPostProcess(const QString& text); QString fragPostProcess() const; public: Q_SIGNAL void changed(); Q_SIGNAL void userShaderEnabledChanged(); Q_SIGNAL void intermediateFBOChanged(); Q_SIGNAL void fragHeaderChanged(); Q_SIGNAL void fragSampleChanged(); Q_SIGNAL void fragPostProcessChanged(); Q_SIGNAL void lastFileChanged(); //keyword 'signals' maybe protected. we need call the signals in other classes. Q_SIGNAL is empty Q_SIGNAL void forceFrameRateChanged(); Q_SIGNAL void decodingThreadsChanged(int n); Q_SIGNAL void decoderPriorityNamesChanged(); Q_SIGNAL void registeredDecodersChanged(const QVector& r); Q_SIGNAL void zeroCopyChanged(); Q_SIGNAL void captureDirChanged(const QString& dir); Q_SIGNAL void captureFormatChanged(const QString& fmt); Q_SIGNAL void captureQualityChanged(int quality); Q_SIGNAL void avfilterVideoChanged(); Q_SIGNAL void avfilterAudioChanged(); Q_SIGNAL void subtitleEnabledChanged(); Q_SIGNAL void subtitleAutoLoadChanged(); Q_SIGNAL void subtitleEnginesChanged(); Q_SIGNAL void subtitleFontChanged(); Q_SIGNAL void subtitleColorChanged(); Q_SIGNAL void subtitleOutlineChanged(); Q_SIGNAL void subtitleOutlineColorChanged(); Q_SIGNAL void subtitleBottomMarginChanged(); Q_SIGNAL void subtitleDelayChanged(); Q_SIGNAL void assFontFileChanged(); Q_SIGNAL void assFontsDirChanged(); Q_SIGNAL void assFontFileForcedChanged(); Q_SIGNAL void previewEnabledChanged(); Q_SIGNAL void previewWidthChanged(); Q_SIGNAL void previewHeightChanged(); Q_SIGNAL void EGLChanged(); Q_SIGNAL void openGLTypeChanged(); Q_SIGNAL void ANGLEPlatformChanged(); Q_SIGNAL void avformatOptionsEnabledChanged(); Q_SIGNAL void bufferValueChanged(); Q_SIGNAL void timeoutChanged(); Q_SIGNAL void abortOnTimeoutChanged(); Q_SIGNAL void logLevelChanged(); Q_SIGNAL void languageChanged(); Q_SIGNAL void historyChanged(); protected: explicit Config(QObject *parent = 0); ~Config(); public Q_SLOTS: void save(); private: class Data; Data *mpData; }; #endif // PLAYER_CONFIG_H QtAV-1.12.0/examples/common/Info.plist000066400000000000000000000131251312235004300174570ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleDocumentTypes CFBundleTypeExtensions AAC AC3 AIFF M4A MKA MP3 OGG PCM VAW WAV WAW WMA aac ac3 aiff m4a mka mp3 ogg pcm vaw wav waw wma CFBundleTypeIconFile document.icns CFBundleTypeName Audio file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions * * 3GP 3IV 3gp 3iv ASF AVI CPK DAT DIVX DV FLAC FLI FLV H264 I263 M2TS M4V MKV MOV MP2 MP4 MPEG MPG MPG2 MPG4 NSV NUT NUV OGG OGM QT RM RMVB VCD VFW VOB WEBM WMV asf avi cpk dat divx dv flac fli flv h264 i263 m2ts m4v mkv mov mp2 mp4 mpeg mpg mpg2 mpg4 mts nsv nut nuv ogg ogm qt rm rmvb vcd vfw vob webm wmv f4v ts CFBundleTypeIconFile document.icns CFBundleTypeName Movie file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions AQT ASS JSS RT SMI SRT SSA SUB TXT UTF aqt ass jss rt smi srt ssa sub txt utf CFBundleTypeIconFile document.icns CFBundleTypeName Subtitles file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleExecutable @EXECUTABLE@ CFBundleIconFile QtAV.icns CFBundleIdentifier com.qtav.@EXECUTABLE@ CFBundleInfoDictionaryVersion 6.0 CFBundleName QtAV @EXECUTABLE@ CFBundleDisplayName QtAV @EXECUTABLE@ LSMinimumSystemVersionByArchitecture x86_64 10.6.0 CFBundlePackageType APPL CFBundleShortVersionString @SHORT_VERSION@ CFBundleVersion @FULL_VERSION@ NSHighResolutionCapable UIFileSharingEnabled CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLName Streaming Protocol CFBundleURLSchemes mms mmst http httpproxy rtp rtsp ftp udp smb QtAV-1.12.0/examples/common/ScreenSaver.cpp000066400000000000000000000244011312235004300204320ustar00rootroot00000000000000#include "ScreenSaver.h" #include #include #ifdef Q_OS_LINUX //#include #ifndef Success #define Success 0 #endif struct _XDisplay; typedef struct _XDisplay Display; typedef Display* (*fXOpenDisplay)(const char*/* display_name */); typedef int (*fXCloseDisplay)(Display*/* display */); typedef int (*fXSetScreenSaver)(Display*, int /* timeout */, int /* interval */,int /* prefer_blanking */, int /* allow_exposures */); typedef int (*fXGetScreenSaver)(Display*, int* /* timeout_return */, int* /* interval_return */, int* /* prefer_blanking_return */, int* /* allow_exposures_return */); typedef int (*fXResetScreenSaver)(Display*/* display */); static fXOpenDisplay XOpenDisplay = 0; static fXCloseDisplay XCloseDisplay = 0; static fXSetScreenSaver XSetScreenSaver = 0; static fXGetScreenSaver XGetScreenSaver = 0; static fXResetScreenSaver XResetScreenSaver = 0; static QLibrary xlib; #endif //Q_OS_LINUX #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) //http://www.cocoachina.com/macdev/cocoa/2010/0201/453.html #include #endif //Q_OS_MAC #ifdef Q_OS_WIN #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif //mingw gcc4.4 EXECUTION_STATE #ifdef __MINGW32__ #ifndef _WIN32_WINDOWS #define _WIN32_WINDOWS 0x0410 #endif //_WIN32_WINDOWS #endif //__MINGW32__ #include #define USE_NATIVE_EVENT 0 #if USE_NATIVE_EVENT class ScreenSaverEventFilter #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) : public QAbstractNativeEventFilter #endif { public: //screensaver is global static ScreenSaverEventFilter& instance() { static ScreenSaverEventFilter sSSEF; return sSSEF; } void enable(bool yes = true) { if (!yes) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) mLastEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter); #else QAbstractEventDispatcher::instance()->installNativeEventFilter(this); #endif } else { if (!QAbstractEventDispatcher::instance()) return; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) mLastEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(mLastEventFilter); #else QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); #endif } } void disable(bool yes = true) { enable(!yes); } bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) { Q_UNUSED(eventType); MSG* msg = static_cast(message); //qDebug("ScreenSaverEventFilter: %p", msg->message); if (WM_DEVICECHANGE == msg->message) { qDebug("~~~~~~~~~~device event"); /*if (msg->wParam == DBT_DEVICEREMOVECOMPLETE) { qDebug("Remove device"); }*/ } if (msg->message == WM_SYSCOMMAND && ((msg->wParam & 0xFFF0) == SC_SCREENSAVE || (msg->wParam & 0xFFF0) == SC_MONITORPOWER) ) { //qDebug("WM_SYSCOMMAND SC_SCREENSAVE SC_MONITORPOWER"); if (result) { //*result = 0; //why crash? } return true; } return false; } private: ScreenSaverEventFilter() {} ~ScreenSaverEventFilter() {} #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) static QAbstractEventDispatcher::EventFilter mLastEventFilter; static bool eventFilter(void* message) { return ScreenSaverEventFilter::instance().nativeEventFilter("windows_MSG", message, 0); } #endif }; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QAbstractEventDispatcher::EventFilter ScreenSaverEventFilter::mLastEventFilter = 0; #endif #endif //USE_NATIVE_EVENT #endif //Q_OS_WIN ScreenSaver& ScreenSaver::instance() { static ScreenSaver sSS; return sSS; } ScreenSaver::ScreenSaver() { state_saved = false; modified = false; #ifdef Q_OS_LINUX timeout = 0; interval = 0; preferBlanking = 0; allowExposures = 0; if (qgetenv("DISPLAY").isEmpty()) { isX11 = false; } else { xlib.setFileName(QString::fromLatin1("libX11.so")); isX11 = xlib.load(); // meego only has libX11.so.6, libX11.so.6.x.x if (!isX11) { xlib.setFileName(QString::fromLatin1("libX11.so.6")); isX11 = xlib.load(); } if (!isX11) { qDebug("open X11 so failed: %s", xlib.errorString().toUtf8().constData()); } else { XOpenDisplay = (fXOpenDisplay)xlib.resolve("XOpenDisplay"); XCloseDisplay = (fXCloseDisplay)xlib.resolve("XCloseDisplay"); XSetScreenSaver = (fXSetScreenSaver)xlib.resolve("XSetScreenSaver"); XGetScreenSaver = (fXGetScreenSaver)xlib.resolve("XGetScreenSaver"); XResetScreenSaver = (fXResetScreenSaver)xlib.resolve("XResetScreenSaver"); } isX11 = XOpenDisplay && XCloseDisplay && XSetScreenSaver && XGetScreenSaver && XResetScreenSaver; } #endif //Q_OS_LINUX ssTimerId = 0; retrieveState(); } ScreenSaver::~ScreenSaver() { restoreState(); #ifdef Q_OS_LINUX if (xlib.isLoaded()) xlib.unload(); #endif } //http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947%28v=vs.85%29.aspx //http://msdn.microsoft.com/en-us/library/windows/desktop/aa373208%28v=vs.85%29.aspx /* TODO: * SystemParametersInfo will change system wild settings. An application level solution is better. Use native event * SPI_SETSCREENSAVETIMEOUT? * SPI_SETLOWPOWERTIMEOUT, SPI_SETPOWEROFFTIMEOUT for 32bit */ bool ScreenSaver::enable(bool yes) { bool rv = false; #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) #if USE_NATIVE_EVENT ScreenSaverEventFilter::instance().enable(yes); modified = true; rv = true; return true; #else /* int val; //SPI_SETLOWPOWERTIMEOUT, SPI_SETPOWEROFFTIMEOUT. SPI_SETSCREENSAVETIMEOUT if ( SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &val, 0)) { SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, val, NULL, 0); } */ //http://msdn.microsoft.com/en-us/library/aa373208%28VS.85%29.aspx static EXECUTION_STATE sLastState = 0; if (!yes) { //Calling SetThreadExecutionState without ES_CONTINUOUS simply resets the idle timer; to keep the display or system in the working state, the thread must call SetThreadExecutionState periodically //ES_CONTINUOUS: Informs the system that the state being set should remain in effect until the next call that uses ES_CONTINUOUS and one of the other state flags is cleared. sLastState = SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_CONTINUOUS); } else { if (sLastState) sLastState = SetThreadExecutionState(sLastState|ES_CONTINUOUS); } rv = sLastState != 0; modified = true; #endif //USE_NATIVE_EVENT #endif //defined(Q_OS_WIN) && !defined(Q_OS_WINRT) #ifdef Q_OS_LINUX if (isX11) { Display *display = XOpenDisplay(0); // -1: restore default. 0: disable int ret = 0; if (yes) ret = XSetScreenSaver(display, -1, interval, preferBlanking, allowExposures); else ret = XSetScreenSaver(display, 0, interval, preferBlanking/*DontPreferBlanking*/, allowExposures); //TODO: why XSetScreenSaver return 1? now use XResetScreenSaver to workaround ret = XResetScreenSaver(display); XCloseDisplay(display); rv = ret==Success; qDebug("ScreenSaver::enable %d, ret %d timeout origin: %d", yes, ret, timeout); } modified = true; if (!yes) { if (ssTimerId <= 0) { ssTimerId = startTimer(1000 * 60); } } else { if (ssTimerId) killTimer(ssTimerId); } rv = true; modified = true; #endif //Q_OS_LINUX #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) if (!yes) { if (ssTimerId <= 0) { ssTimerId = startTimer(1000 * 60); } } else { if (ssTimerId) killTimer(ssTimerId); } rv = true; modified = true; #endif //Q_OS_MAC if (!rv) { qWarning("Failed to enable screen saver (%d)", yes); } else { qDebug("Succeed to enable screen saver (%d)", yes); } return rv; } void ScreenSaver::enable() { enable(true); } void ScreenSaver::disable() { enable(false); } bool ScreenSaver::retrieveState() { bool rv = false; qDebug("ScreenSaver::retrieveState"); if (!state_saved) { #ifdef Q_OS_LINUX if (isX11) { Display *display = XOpenDisplay(0); XGetScreenSaver(display, &timeout, &interval, &preferBlanking, &allowExposures); XCloseDisplay(display); qDebug("ScreenSaver::retrieveState timeout: %d, interval: %d, preferBlanking:%d, allowExposures:%d", timeout, interval, preferBlanking, allowExposures); state_saved = true; rv = true; } #endif //Q_OS_LINUX } else { qDebug("ScreenSaver::retrieveState: state already saved previously, doing nothing"); } return rv; } bool ScreenSaver::restoreState() { bool rv = false; if (!modified) { qDebug("ScreenSaver::restoreState: state did not change, doing nothing"); return true; } if (state_saved) { #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) #if USE_NATIVE_EVENT ScreenSaverEventFilter::instance().enable(); rv = true; #else SetThreadExecutionState(ES_CONTINUOUS); #endif //USE_NATIVE_EVENT #endif //defined(Q_OS_WIN) && !defined(Q_OS_WINRT) #ifdef Q_OS_LINUX if (isX11) { Display *display = XOpenDisplay(0); // -1: restore default. 0: disable XSetScreenSaver(display, timeout, interval, preferBlanking, allowExposures); XCloseDisplay(display); rv = true; } #endif //Q_OS_LINUX } else { qWarning("ScreenSaver::restoreState: no data, doing nothing"); } return rv; } void ScreenSaver::timerEvent(QTimerEvent *e) { if (e->timerId() != ssTimerId) return; #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) UpdateSystemActivity(OverallAct); return; #endif //Q_OS_MAC #ifdef Q_OS_LINUX if (!isX11) return; Display *display = XOpenDisplay(0); XResetScreenSaver(display); XCloseDisplay(display); #endif //Q_OS_LINUX } QtAV-1.12.0/examples/common/ScreenSaver.h000066400000000000000000000015111312235004300200740ustar00rootroot00000000000000#ifndef SCREENSAVER_H #define SCREENSAVER_H #include "common_export.h" #include // TODO: read QtSystemInfo.ScreenSaver class COMMON_EXPORT ScreenSaver : QObject { Q_OBJECT public: static ScreenSaver& instance(); ScreenSaver(); ~ScreenSaver(); // enable: just restore the previous settings. settings changed during the object life will ignored bool enable(bool yes); public slots: void enable(); void disable(); protected: virtual void timerEvent(QTimerEvent *); private: //return false if already called bool retrieveState(); bool restoreState(); bool state_saved, modified; #ifdef Q_OS_LINUX bool isX11; int timeout; int interval; int preferBlanking; int allowExposures; #endif //Q_OS_LINUX int ssTimerId; //for mac }; #endif // SCREENSAVER_H QtAV-1.12.0/examples/common/common.cpp000066400000000000000000000325461312235004300175130ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "common.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif #include #ifdef Q_OS_WINRT #include #include #include #include #include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::ApplicationModel::Activation; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Storage; using namespace ABI::Windows::Storage::Pickers; #define COM_LOG_COMPONENT "WinRT" #define COM_ENSURE(f, ...) COM_CHECK(f, return __VA_ARGS__;) #define COM_WARN(f) COM_CHECK(f) #define COM_CHECK(f, ...) \ do { \ HRESULT hr = f; \ if (FAILED(hr)) { \ qWarning() << QString::fromLatin1(COM_LOG_COMPONENT " error@%1. " #f ": (0x%2) %3").arg(__LINE__).arg(hr, 0, 16).arg(qt_error_string(hr)); \ __VA_ARGS__ \ } \ } while (0) QString UrlFromFileArgs(IInspectable *args) { ComPtr fileArgs; COM_ENSURE(args->QueryInterface(fileArgs.GetAddressOf()), QString()); ComPtr> files; COM_ENSURE(fileArgs->get_Files(&files), QString()); ComPtr item; COM_ENSURE(files->GetAt(0, &item), QString()); HString path; COM_ENSURE(item->get_Path(path.GetAddressOf()), QString()); quint32 pathLen; const wchar_t *pathStr = path.GetRawBuffer(&pathLen); const QString filePath = QString::fromWCharArray(pathStr, pathLen); qDebug() << "file path: " << filePath; item->AddRef(); //ensure we can access it later. TODO: how to release? return QString::fromLatin1("winrt:@%1:%2").arg((qint64)(qptrdiff)item.Get()).arg(filePath); } #endif Q_GLOBAL_STATIC(QFile, fileLogger) #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) class QMessageLogContext {}; typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); QtMsgHandler qInstallMessageHandler(QtMessageHandler h) { static QtMessageHandler hh; hh = h; struct MsgHandlerWrapper { static void handler(QtMsgType type, const char *msg) { static QMessageLogContext ctx; hh(type, ctx, QString::fromUtf8(msg)); } }; return qInstallMsgHandler(MsgHandlerWrapper::handler); } #endif void Logger(QtMsgType type, const QMessageLogContext &, const QString& qmsg) { const QByteArray msgArray = qmsg.toUtf8(); const char* msg = msgArray.constData(); switch (type) { case QtDebugMsg: printf("Debug: %s\n", msg); fileLogger()->write(QByteArray("Debug: ")); break; case QtWarningMsg: printf("Warning: %s\n", msg); fileLogger()->write(QByteArray("Warning: ")); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s\n", msg); fileLogger()->write(QByteArray("Critical: ")); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s\n", msg); fileLogger()->write(QByteArray("Fatal: ")); abort(); } fflush(0); fileLogger()->write(msgArray); fileLogger()->write(QByteArray("\n")); //fileLogger()->flush(); // crash in qt5.7 } QOptions get_common_options() { static QOptions ops = QOptions().addDescription(QString::fromLatin1("Options for QtAV players")) .add(QString::fromLatin1("common options")) ("help,h", QLatin1String("print this")) ("ao", QString(), QLatin1String("audio output. Can be ordered combination of available backends (-ao help). Leave empty to use the default setting. Set 'null' to disable audio.")) ("-egl", QLatin1String("Use EGL. Only works for Qt>=5.5+XCB")) ("-gl", QLatin1String("OpenGL backend for Qt>=5.4(windows). can be 'desktop', 'opengles' and 'software'")) ("x", 0, QString()) ("y", 0, QLatin1String("y")) ("-width", 800, QLatin1String("width of player")) ("height", 450, QLatin1String("height of player")) ("fullscreen", QLatin1String("fullscreen")) ("decoder", QLatin1String("FFmpeg"), QLatin1String("use a given decoder")) ("decoders,-vd", QLatin1String("cuda;vaapi;vda;dxva;cedarv;ffmpeg"), QLatin1String("decoder name list in priority order separated by ';'")) ("file,f", QString(), QLatin1String("file or url to play")) ("language", QString(), QLatin1String("language on UI. can be 'system' and locale name e.g. zh_CN")) ("log", QString(), QLatin1String("log level. can be 'off', 'fatal', 'critical', 'warning', 'debug', 'all'")) ("logfile" #if defined(Q_OS_IOS) , appDataDir().append(QString::fromLatin1("/log-%1.txt")) #elif defined(Q_OS_WINRT) || defined(Q_OS_ANDROID) , QString() #else , QString::fromLatin1("log-%1.txt") #endif , QString::fromLatin1("log to file. Set empty to disable log file (-logfile '')")) ; return ops; } void do_common_options_before_qapp(const QOptions& options) { #ifdef Q_OS_LINUX QSettings cfg(Config::defaultConfigFile(), QSettings::IniFormat); const bool set_egl = cfg.value("opengl/egl").toBool(); //https://bugreports.qt.io/browse/QTBUG-49529 // it's too late if qApp is created. but why ANGLE is not? if (options.value(QString::fromLatin1("egl")).toBool() || set_egl) { //FIXME: Config is constructed too early because it requires qApp // only apply to current run. no config change qputenv("QT_XCB_GL_INTEGRATION", "xcb_egl"); } else { qputenv("QT_XCB_GL_INTEGRATION", "xcb_glx"); } qDebug() << "QT_XCB_GL_INTEGRATION: " << qgetenv("QT_XCB_GL_INTEGRATION"); #endif //Q_OS_LINUX } void do_common_options(const QOptions &options, const QString& appName) { if (options.value(QString::fromLatin1("help")).toBool()) { options.print(); exit(0); } // has no effect if qInstallMessageHandler() called //qSetMessagePattern("%{function} @%{line}: %{message}"); #if !defined(Q_OS_WINRT) && !defined(Q_OS_ANDROID) QString app(appName); if (app.isEmpty() && qApp) app = qApp->applicationName(); QString logfile(options.option(QString::fromLatin1("logfile")).value().toString().arg(app)); if (!logfile.isEmpty()) { if (QDir(logfile).isRelative()) { QString log_path(QString::fromLatin1("%1/%2").arg(qApp->applicationDirPath()).arg(logfile)); QFile f(log_path); if (!f.open(QIODevice::WriteOnly)) { log_path = QString::fromLatin1("%1/%2").arg(appDataDir()).arg(logfile); qDebug() << "executable dir is not writable. log to " << log_path; } logfile = log_path; } qDebug() << "set log file: " << logfile; fileLogger()->setFileName(logfile); if (fileLogger()->open(QIODevice::WriteOnly)) { qInstallMessageHandler(Logger); } else { qWarning() << "Failed to open log file '" << fileLogger()->fileName() << "': " << fileLogger()->errorString(); } } #endif QByteArray level(options.value(QString::fromLatin1("log")).toByteArray()); if (level.isEmpty()) level = Config::instance().logLevel().toLatin1(); if (!level.isEmpty()) qputenv("QTAV_LOG", level); } void load_qm(const QStringList &names, const QString& lang) { QString l(Config::instance().language()); if (!lang.isEmpty()) l = lang; if (l.toLower() == QLatin1String("system")) l = QLocale::system().name(); QStringList qms(names); qms << QLatin1String("QtAV") << QLatin1String("qt"); foreach(QString qm, qms) { QTranslator *ts = new QTranslator(qApp); QString path = qApp->applicationDirPath() + QLatin1String("/i18n/") + qm + QLatin1String("_") + l; //qDebug() << "loading qm: " << path; if (ts->load(path)) { qApp->installTranslator(ts); } else { path = QString::fromUtf8(":/i18n/%1_%2").arg(qm).arg(l); //qDebug() << "loading qm: " << path; if (ts->load(path)) qApp->installTranslator(ts); else delete ts; } } QTranslator qtts; if (qtts.load(QLatin1String("qt_") + QLocale::system().name())) qApp->installTranslator(&qtts); } void set_opengl_backend(const QString& glopt, const QString &appname) { QString gl = appname.toLower().replace(QLatin1String("\\"), QLatin1String("/")); int idx = gl.lastIndexOf(QLatin1String("/")); if (idx >= 0) gl = gl.mid(idx + 1); idx = gl.lastIndexOf(QLatin1String(".")); if (idx > 0) gl = gl.left(idx); if (gl.indexOf(QLatin1String("-desktop")) > 0) gl = QLatin1String("desktop"); else if (gl.indexOf(QLatin1String("-es")) > 0 || gl.indexOf(QLatin1String("-angle")) > 0) gl = gl.mid(gl.indexOf(QLatin1String("-es")) + 1); else if (gl.indexOf(QLatin1String("-sw")) > 0 || gl.indexOf(QLatin1String("-software")) > 0) gl = QLatin1String("software"); else gl = glopt.toLower(); if (gl.isEmpty()) { switch (Config::instance().openGLType()) { case Config::Desktop: gl = QLatin1String("desktop"); break; case Config::OpenGLES: gl = QLatin1String("es"); break; case Config::Software: gl = QLatin1String("software"); break; default: break; } } if (gl == QLatin1String("es") || gl == QLatin1String("angle") || gl == QLatin1String("opengles")) { gl = QLatin1String("es_"); gl.append(Config::instance().getANGLEPlatform().toLower()); } #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) if (gl.startsWith(QLatin1String("es"))) { qApp->setAttribute(Qt::AA_UseOpenGLES); #ifdef QT_OPENGL_DYNAMIC qputenv("QT_OPENGL", "angle"); #endif #ifdef Q_OS_WIN if (gl.endsWith(QLatin1String("d3d11"))) qputenv("QT_ANGLE_PLATFORM", "d3d11"); else if (gl.endsWith(QLatin1String("d3d9"))) qputenv("QT_ANGLE_PLATFORM", "d3d9"); else if (gl.endsWith(QLatin1String("warp"))) qputenv("QT_ANGLE_PLATFORM", "warp"); #endif } else if (gl == QLatin1String("desktop")) { qApp->setAttribute(Qt::AA_UseDesktopOpenGL); } else if (gl == QLatin1String("software")) { qApp->setAttribute(Qt::AA_UseSoftwareOpenGL); } #endif } QString appDataDir() { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) return QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) return QStandardPaths::writableLocation(QStandardPaths::DataLocation); #else return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); #endif //5.4.0 #endif // 5.0.0 } AppEventFilter::AppEventFilter(QObject *player, QObject *parent) : QObject(parent) , m_player(player) {} bool AppEventFilter::eventFilter(QObject *obj, QEvent *ev) { //qDebug() << __FUNCTION__ << " watcher: " << obj << ev; if (obj != qApp) return false; if (ev->type() == QEvent::WinEventAct) { // winrt file open/pick. since qt5.6.1 qDebug("QEvent::WinEventAct"); #ifdef Q_OS_WINRT class QActivationEvent : public QEvent { public: void* args() const {return d;} //IInspectable* }; QActivationEvent *ae = static_cast(ev); const QString url(UrlFromFileArgs((IInspectable*)ae->args())); if (!url.isEmpty()) { qDebug() << "winrt url: " << url; if (m_player) QMetaObject::invokeMethod(m_player, "play", Q_ARG(QUrl, QUrl(url))); } return true; #endif } if (ev->type() != QEvent::FileOpen) return false; QFileOpenEvent *foe = static_cast(ev); if (m_player) QMetaObject::invokeMethod(m_player, "play", Q_ARG(QUrl, QUrl(foe->url()))); return true; } static void initResources() { Q_INIT_RESOURCE(theme); } namespace { struct ResourceLoader { public: ResourceLoader() { initResources(); } } qrc; } QtAV-1.12.0/examples/common/common.h000066400000000000000000000040751312235004300171540ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef COMMON_H #define COMMON_H #include #include #include #include "qoptions.h" #include "Config.h" #include "ScreenSaver.h" QOptions COMMON_EXPORT get_common_options(); void COMMON_EXPORT do_common_options_before_qapp(const QOptions& options); /// help, log file, ffmpeg log level void COMMON_EXPORT do_common_options(const QOptions& options, const QString& appName = QString()); void COMMON_EXPORT load_qm(const QStringList& names, const QString &lang = QLatin1String("system")); // if appname ends with 'desktop', 'es', 'angle', software', 'sw', set by appname. otherwise set by command line option glopt, or Config file // TODO: move to do_common_options_before_qapp void COMMON_EXPORT set_opengl_backend(const QString& glopt = QString::fromLatin1("auto"), const QString& appname = QString()); QString COMMON_EXPORT appDataDir(); class COMMON_EXPORT AppEventFilter : public QObject { public: AppEventFilter(QObject *player = 0, QObject* parent = 0); QUrl url() const; virtual bool eventFilter(QObject *obj, QEvent *ev); private: QObject *m_player; }; #endif // COMMON_H QtAV-1.12.0/examples/common/common.pro000066400000000000000000000025521312235004300175230ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2014-01-03T11:07:23 # #------------------------------------------------- # Qt4 need QDesktopServices QT = core gui sql TARGET = common TEMPLATE = lib DEFINES += BUILD_QOPT_LIB CONFIG *= common-buildlib staticlib staticlib: DEFINES += BUILD_COMMON_STATIC #var with '_' can not pass to pri? PROJECTROOT = $$PWD/../.. !include(libcommon.pri): error("could not find libcommon.pri") preparePaths($$OUT_PWD/../../out) INCLUDEPATH += $$PROJECTROOT/src # android apk hack android { QT += svg LIBS += -L$$qtLongName($$BUILD_DIR/lib) isEqual(QT_MAJOR_VERSION, 5):isEqual(QT_MINOR_VERSION, 4):lessThan(QT_PATCH_VERSION, 2) { LIBS += -lQt5AV } else { LIBS += -lQtAV #QML app does not link to libQtAV but we need it. why no QmlAV plugin if remove this? } } else { #include($$PROJECTROOT/libQtAV.pri) } RESOURCES += \ theme/theme.qrc #QMAKE_LFLAGS += -u _link_hack #SystemParametersInfo !winrt:*msvc*: LIBS += -lUser32 HEADERS = common.h \ Config.h \ qoptions.h \ ScreenSaver.h \ common_export.h SOURCES = common.cpp \ Config.cpp \ qoptions.cpp !macx: SOURCES += ScreenSaver.cpp macx:!ios { #SOURCE is ok OBJECTIVE_SOURCES += ScreenSaver.cpp LIBS += -framework CoreServices #-framework ScreenSaver } # don't install. was set in libcommon.pri INSTALLS = QtAV-1.12.0/examples/common/common_export.h000066400000000000000000000006131312235004300205470ustar00rootroot00000000000000#ifndef COMMON_EXPORT_H #define COMMON_EXPORT_H #include #ifdef BUILD_COMMON_STATIC #define COMMON_EXPORT #else #if defined(BUILD_COMMON_LIB) # undef COMMON_EXPORT # define COMMON_EXPORT Q_DECL_EXPORT #else # undef COMMON_EXPORT # define COMMON_EXPORT //Q_DECL_IMPORT //only for vc? link to static lib error #endif #endif //BUILD_COMMON_STATIC #endif // COMMON_EXPORT_H QtAV-1.12.0/examples/common/libcommon.pri000066400000000000000000000120651312235004300202040ustar00rootroot00000000000000# qmake library building template pri file # Copyright (C) 2011-2015 Wang Bin # Shanghai, China. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # ############################## HOW TO ################################## # Suppose the library name is XX # Usually what you need to change are: staticlink, LIB_VERSION, NAME and DLLDESTDIR. # And rename xx-buildlib and LIBXX_PRI_INCLUDED # the contents of libXX.pro is: # TEMPLATE = lib # QT -= gui # CONFIG *= xx-buildlib # STATICLINK = 1 #optional. default is 0, i.e. dynamically link # PROJECTROOT = $$PWD/.. # include(libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # ... # the content of other pro using this library is: # TEMPLATE = app # PROJECTROOT = $$PWD/.. # STATICLINK = 1 #or 0 # include(dir_of_XX/libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # NAME = common !isEmpty(LIB$$upper($$NAME)_PRI_INCLUDED): { error("lib$${NAME}.pri already included") unset(NAME) } eval(LIB$$upper($$NAME)_PRI_INCLUDED = 1) LIB_VERSION = $$QTAV_VERSION #0.x.y may be wrong for dll # If user haven't supplied STATICLINK, then auto-detect isEmpty(STATICLINK) { static|contains(CONFIG, staticlib) { STATICLINK = 1 } else { STATICLINK = 0 } # Override for ios. Dynamic link is only supported # in iOS 8.1. ios:STATICLINK = 1 } TEMPLATE += fakelib PROJECT_TARGETNAME = $$qtLibraryTarget($$NAME) TEMPLATE -= fakelib isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/.. include($${PROJECTROOT}/common.pri) preparePaths($$OUT_PWD/../out) CONFIG += depend_includepath #? PROJECT_SRCPATH = $$PWD PROJECT_LIBDIR = $$qtLongName($$BUILD_DIR/lib) INCLUDEPATH *= $$PROJECT_SRCPATH $$PROJECT_SRCPATH/.. $$PROJECT_SRCPATH/$$NAME DEPENDPATH *= $$PROJECT_SRCPATH #QMAKE_LFLAGS_RPATH += #will append to rpath dir #eval() ? !contains(CONFIG, $$lower($$NAME)-buildlib) { #The following may not need to change CONFIG *= link_prl #mac_framework { # LIBS += -F$$PROJECT_LIBDIR -framework $$PROJECT_TARGETNAME #} else { LIBS *= -L$$PROJECT_LIBDIR -l$${PROJECT_TARGETNAME} isEqual(STATICLINK, 1) { PRE_TARGETDEPS += $$PROJECT_LIBDIR/$$qtStaticLib($$NAME) } else { win32 { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME, $$LIB_VERSION) } else { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/lib$${PROJECT_TARGETNAME}.$${QMAKE_EXTENSION_SHLIB} } } #} } else { #Add your additional configuration first. e.g. # win32: LIBS += -lUser32 # The following may not need to change !CONFIG(plugin) { #TEMPLATE = lib VERSION = $$LIB_VERSION DESTDIR= $$PROJECT_LIBDIR } TARGET = $$PROJECT_TARGETNAME ##I commented out this before, why? CONFIG *= create_prl # DEFINES += BUILD_$$upper($$NAME)_LIB #win32-msvc* isEqual(STATICLINK, 1) { CONFIG -= shared dll ##otherwise the following shared is true, why? CONFIG *= staticlib } else { CONFIG *= shared #shared includes dll } shared { !CONFIG(plugin) { !isEqual(DESTDIR, $$BUILD_DIR/bin): DLLDESTDIR = $$BUILD_DIR/bin #copy shared lib there } CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP):!mac_framework: QMAKE_POST_LINK = -$$QMAKE_STRIP $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) #copy from the pro creator creates. symbian { MMP_RULES += EXPORTUNFROZEN TARGET.UID3 = 0xE4CC8061 TARGET.CAPABILITY = TARGET.EPOCALLOWDLLDATA = 1 addFiles.sources = $$qtSharedLib($$NAME, $$LIB_VERSION) addFiles.path = !:/sys/bin DEPLOYMENT += addFiles } } unix:!symbian { maemo5 { target.path = /opt/usr/lib } else { target.path = /usr/lib } INSTALLS += target } } !no_rpath:!cross_compile:set_rpath($$PROJECT_LIBDIR) unset(LIB_VERSION) unset(PROJECT_SRCPATH) unset(PROJECT_LIBDIR) unset(PROJECT_TARGETNAME) QMAKE_INFO_PLIST = $$PWD/Info.plist defineTest(genRC) { RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV $$1" export(RC_ICONS) export(QMAKE_TARGET_COMPANY) export(QMAKE_TARGET_DESCRIPTION) export(QMAKE_TARGET_COPYRIGHT) export(QMAKE_TARGET_PRODUCT) return(true) } QtAV-1.12.0/examples/common/qoptions.cpp000066400000000000000000000252241312235004300200720ustar00rootroot00000000000000/****************************************************************************** QOptions: make command line options easy. https://github.com/wang-bin/qoptions Copyright (C) 2011-2015 Wang Bin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ******************************************************************************/ #include "qoptions.h" #include #include QOption::QOption() {} QOption::QOption(const char *name, const QVariant &defaultValue, Type type, const QString &description) :mType(type),mDescription(description),mDefaultValue(defaultValue) { setName(QLatin1String(name)); } QOption::QOption(const char *name, Type type, const QString &description) :mType(type),mDescription(description),mDefaultValue(QVariant()) { //if (mType==QOption::NoToken) // mValue = false; setName(QLatin1String(name)); } /* QOption::QOption(const char *name, const QVariant& value, Type type, const QString &description) :mType(type),mDescription(description),mDefaultValue(*value),mValue(value) {qDebug("%s %s %d", __FILE__, __FUNCTION__, __LINE__); setName(name); } */ QString QOption::shortName() const { return mShortName; } QString QOption::longName() const { return mLongName; } QString QOption::formatName() const { if (mLongName.isEmpty()) return QLatin1String("-") + mShortName; if (mShortName.isEmpty()) return QLatin1String("--") + mLongName; return QString::fromLatin1("-%1 [--%2]").arg(mShortName).arg(mLongName); } QString QOption::description() const { return mDescription; } QVariant QOption::value() const { if (mValue.isValid()) return mValue; return mDefaultValue; } void QOption::setValue(const QVariant &value) { mValue = value; } bool QOption::isSet() const { return mValue.isValid(); } bool QOption::isValid() const { return !shortName().isEmpty() || !longName().isEmpty(); } void QOption::setType(QOption::Type type) { mType = type; if (mType==NoToken) mValue = false; } QOption::Type QOption::type() const { return mType; } QString QOption::help() const { QString message = formatName(); if (type()==QOption::SingleToken) message.append(QLatin1String(" value")); else if (type()==QOption::MultiToken) message.append(QLatin1String(" value1 ... valueN")); message = QString::fromLatin1("%1").arg(message, -33); message.append(mDescription); if (mDefaultValue.isValid() && !mDefaultValue.toString().isEmpty()) message.append(QLatin1String(" (default: ") + mDefaultValue.toString() + QLatin1String(")")); return message; } bool QOption::operator <(const QOption& o) const { return mType < o.type() || mShortName < o.shortName() || mLongName < o.longName() || mDescription < o.description(); } static QString get_short(const QString& name) { if (name.startsWith(QLatin1String("--"))) return QString(); if (name.startsWith(QLatin1String("-"))) return name.mid(1); return name; } static QString get_long(const QString& name) { if (name.startsWith(QLatin1String("--"))) return name.mid(2); if (name.startsWith(QLatin1String("-"))) return QString(); return name; } void QOption::setName(const QString &name) { int comma = name.indexOf(QLatin1Char(',')); if (comma>0) { QString name1 = name.left(comma); QString name2 = name.mid(comma+1); if (name1.startsWith(QLatin1String("--"))) { mLongName = name1.mid(2); mShortName = get_short(name2); } else if (name1.startsWith(QLatin1String("-"))) { mShortName = name1.mid(1); mLongName = get_long(name2); } else { if (name2.startsWith(QLatin1String("--"))) { mLongName = name2.mid(2); mShortName = name1; } else if (name2.startsWith(QLatin1String("-"))) { mShortName = name2.mid(1); mLongName = name1; } else { mShortName = name2; mLongName = name1; } } } else { if (name.startsWith(QLatin1String("--"))) mLongName = name.mid(2); else if (name.startsWith(QLatin1String("-"))) mShortName = name.mid(1); else mShortName = name; } } QOptions::QOptions() { } QOptions::~QOptions() { mOptionGroupMap.clear(); mOptions.clear(); } bool QOptions::parse(int argc, const char *const*argv) { if (mOptionGroupMap.isEmpty()) return false; if (argc==1) return true; bool result = true; QStringList args; for (int i=1;i::Iterator it_list; mOptions = mOptionGroupMap.keys(); while (it != args.end()) { if (it->startsWith(QLatin1String("--"))) { int e = it->indexOf(QLatin1Char('=')); for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) { if (it_list->longName() == it->mid(2,e-2)) { if (it_list->type()==QOption::NoToken) { it_list->setValue(true); //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); it = args.erase(it); break; } if (e>0) { // it_list->setValue(it->mid(e+1)); //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); } else { it = args.erase(it); if (it == args.end()) break; it_list->setValue(*it); //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); } it = args.erase(it); break; } } if (it_list == mOptions.end()) { qWarning() << "unknown option: " << *it; result = false; ++it; } //handle unknown option } else if (it->startsWith(QLatin1Char('-'))) { for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) { QString sname = it_list->shortName(); int sname_len = sname.length(); //usally is 1 //TODO: startsWith(-height,-h) Not endsWith, -oabco if (it->midRef(1).compare(sname) == 0) { if (it_list->type() == QOption::NoToken) { it_list->setValue(true); it = args.erase(it); break; } if (it->length() == sname_len+1) {//-o abco it = args.erase(it); if (it == args.end()) break; it_list->setValue(*it); //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); } else { it_list->setValue(it->mid(sname_len+1)); //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); } it = args.erase(it); break; } } if (it_list==mOptions.end()) { qWarning() << "unknown option: " << *it; result = false; ++it; } //handle unknown option } else { qWarning() << "unknown option: " << *it; ++it; } } if (!result) { print(); } return result; } QOptions& QOptions::add(const QString &group_description) { mCurrentDescription = group_description; return *this; } QOptions& QOptions::addDescription(const QString &description) { mDescription = description; return *this; } QOptions& QOptions::operator ()(const char* name, const QString& description) { QOption op(name, QOption::NoToken, description); mOptions.append(op); mOptionGroupMap.insert(op, mCurrentDescription); return *this; } QOptions& QOptions::operator ()(const char* name, QOption::Type type, const QString& description) { QOption op(name, type, description); mOptions.append(op); mOptionGroupMap.insert(op, mCurrentDescription); return *this; } QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, const QString& description) { QOption op(name, defaultValue, QOption::SingleToken, description); mOptions.append(op); mOptionGroupMap.insert(op, mCurrentDescription); return *this; } QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, QOption::Type type, const QString& description) { QOption op(name, defaultValue, type, description); mOptions.append(op); mOptionGroupMap.insert(op, mCurrentDescription); return *this; } /* QOptions& QOptions::operator ()(const char* name, const QVariant& value, QOption::Type type, const QString& description) { QOption op(name, value, type, description); mOptions.append(op); mOptionGroupMap.insert(op, mCurrentDescription); return *this; } */ QOption QOptions::option(const QString &name) const { if (mOptions.isEmpty()) return QOption(); QList::ConstIterator it_list; for (it_list=mOptions.constBegin(); it_list!=mOptions.constEnd(); ++it_list) { if (it_list->shortName()==name || it_list->longName()==name) { return *it_list; } } return QOption(); } QVariant QOptions::value(const QString& name) const { return option(name).value(); } QVariant QOptions::operator [](const QString& name) const { return value(name); } QString QOptions::help() const { QString message = mDescription; QStringList groups = mOptionGroupMap.values(); groups.removeDuplicates(); QList::ConstIterator it; QList::ConstIterator it_op; for (it=groups.constBegin(); it!=groups.constEnd(); ++it) { message.append(QLatin1String("\n")).append(*it); QList options = mOptionGroupMap.keys(*it); for (it_op=options.constBegin();it_op!=options.constEnd();++it_op) message.append(QLatin1String("\n ")).append(it_op->help()); } return message; } void QOptions::print() const { qDebug("%s", help().toUtf8().constData()); } QtAV-1.12.0/examples/common/qoptions.h000066400000000000000000000107631312235004300175410ustar00rootroot00000000000000/****************************************************************************** QOptions: make command line options easy. https://github.com/wang-bin/qoptions Copyright (C) 2011-2015 Wang Bin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ******************************************************************************/ #ifndef QOPTIONS_H #define QOPTIONS_H #include #include #include #if defined(BUILD_QOPT_LIB) # define QOPT_EXPORT Q_DECL_EXPORT #elif defined(BUILD_QOPT_IMPORT) # define QOPT_EXPORT Q_DECL_IMPORT //only for vc? #else # define QOPT_EXPORT #endif /* command line: some_cmd -short1 value --long1 value --long2=value -short_novalue --long_novalue... C++: QOptions get_global_options() { static QOptions ops = QOptions().addDescription("...") .add("common option group") ("long1,short1", default1, "description1") ("--long2", default2, "description2") ("short_novalue", "description3") ("--long_novalue", "description4") ; return ops; } QOptions options = get_global_options(); options.add("special option group")(....)...; options.parse(argc, argv); int v1 = options.value("short1").toInt(); */ class QOPT_EXPORT QOption { public: // TODO: MultiToken -name value1 -name value2 ... enum Type { NoToken, SingleToken, MultiToken }; QOption(); explicit QOption(const char* name, const QVariant& defaultValue, Type type, const QString& description); explicit QOption(const char* name, Type type, const QString& description); //explicit QOption(const char* name, const QVariant& value, Type type, const QString& description); QString shortName() const; QString longName() const; QString formatName() const; QString description() const; QVariant value() const; void setValue(const QVariant& value); bool isSet() const; bool isValid() const; void setType(QOption::Type type); QOption::Type type() const; QString help() const; void print() const; bool operator <(const QOption& o) const; private: /*! * \brief setName * short/long name format: * "--long", "-short", "short" * "long,short", "--long,short", "--long,-short", "long,-short" * "short,--long", "-short,long", "-short,--long" * \param name */ void setName(const QString& name); QOption::Type mType; QString mShortName, mLongName, mDescription; QVariant mDefaultValue; QVariant mValue; }; class QOPT_EXPORT QOptions { public: //e.g. application information, copyright etc. QOptions(); //QOptions(const QOptions& o); ~QOptions(); //QOptions& operator=(const QOptions& o); /*! * \brief parse * \param argc * \param argv * \return false if invalid option found */ bool parse(int argc, const char*const* argv); QOptions& add(const QString& group_description); QOptions& addDescription(const QString& description); QOptions& operator ()(const char* name, const QString& description = QString()); QOptions& operator ()(const char* name, QOption::Type type, const QString& description = QString()); QOptions& operator ()(const char* name, const QVariant& defaultValue, const QString& description); QOptions& operator ()(const char* name, const QVariant& defaultValue, QOption::Type type, const QString& description = QString()); //QOptions& operator ()(const char* name, QVariant* value, QOption::Type type, const QString& description = QString()); QOption option(const QString& name) const; QVariant value(const QString& name) const; QVariant operator [](const QString& name) const; QString help() const; void print() const; private: QString mDescription, mCurrentDescription; QList mOptions; QMap mOptionGroupMap; }; #endif // QOPTIONS_H QtAV-1.12.0/examples/common/theme/000077500000000000000000000000001312235004300166075ustar00rootroot00000000000000QtAV-1.12.0/examples/common/theme/README000066400000000000000000000000241312235004300174630ustar00rootroot00000000000000Written by Wang Bin QtAV-1.12.0/examples/common/theme/dark/000077500000000000000000000000001312235004300175305ustar00rootroot00000000000000QtAV-1.12.0/examples/common/theme/dark/backward.svg000066400000000000000000000005341312235004300220310ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/capture.svg000066400000000000000000000005501312235004300217140ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/close.svg000066400000000000000000000021071312235004300213560ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/forward.svg000066400000000000000000000005341312235004300217170ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/fullscreen.svg000066400000000000000000000011451312235004300224140ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/help.svg000066400000000000000000000005771312235004300212120ustar00rootroot00000000000000 ? QtAV-1.12.0/examples/common/theme/dark/info.svg000066400000000000000000000006001312235004300212000ustar00rootroot00000000000000 i QtAV-1.12.0/examples/common/theme/dark/menu.svg000066400000000000000000000007301312235004300212150ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/open.svg000066400000000000000000000005351312235004300212150ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/pause.svg000066400000000000000000000006221312235004300213660ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/play.svg000066400000000000000000000004441312235004300212200ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/sound.svg000066400000000000000000000005721312235004300214050ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/dark/stop.svg000066400000000000000000000004521312235004300212370ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/000077500000000000000000000000001312235004300202335ustar00rootroot00000000000000QtAV-1.12.0/examples/common/theme/default/backward.svg000066400000000000000000000007541312235004300225400ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/close.svg000066400000000000000000000021071312235004300220610ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/forward.svg000066400000000000000000000007541312235004300224260ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/fullscreen.svg000066400000000000000000000011451312235004300231170ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/help.svg000066400000000000000000000007441312235004300217110ustar00rootroot00000000000000 ? QtAV-1.12.0/examples/common/theme/default/info.svg000066400000000000000000000007451312235004300217150ustar00rootroot00000000000000 i QtAV-1.12.0/examples/common/theme/default/mute.svg000066400000000000000000000010511312235004300217230ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/open.svg000066400000000000000000000007231312235004300217170ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/pause.svg000066400000000000000000000007721312235004300220770ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/play.svg000066400000000000000000000006421312235004300217230ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/stop.svg000066400000000000000000000006661312235004300217510ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/default/volume.svg000066400000000000000000000005251312235004300222650ustar00rootroot00000000000000 QtAV-1.12.0/examples/common/theme/theme.qrc000066400000000000000000000020321312235004300204150ustar00rootroot00000000000000 default/backward.svg default/forward.svg default/help.svg default/info.svg default/open.svg default/pause.svg default/play.svg default/stop.svg default/fullscreen.svg default/close.svg dark/capture.svg dark/menu.svg dark/sound.svg dark/backward.svg dark/forward.svg dark/fullscreen.svg dark/help.svg dark/info.svg dark/open.svg dark/pause.svg dark/play.svg dark/stop.svg dark/fullscreen.svg dark/close.svg default/volume.svg default/mute.svg QtAV-1.12.0/examples/examples.pro000066400000000000000000000016671312235004300165670ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = common !android:!ios:!winrt { SUBDIRS += audiopipeline !no-widgets { SUBDIRS += \ sharedoutput \ simpletranscode \ simpleplayer \ player \ filters \ framereader \ videocapture \ videographicsitem \ videogroup \ videowall contains(QT_CONFIG, opengl): SUBDIRS += \ shader \ glslfilter player.depends += common sdk_build { SUBDIRS *= \ simpleplayer/simpleplayer_sdk.pro \ player/player_sdk.pro } } } greaterThan(QT_MAJOR_VERSION, 4) { isEqual(QT_MAJOR_VERSION,5):greaterThan(QT_MINOR_VERSION,3)|greaterThan(QT_MAJOR_VERSION,5) { contains(QT_CONFIG, opengl):!winrt:!ios:!android: SUBDIRS += window } # qtHaveModule does not exist in Qt5.0 isEqual(QT_MINOR_VERSION, 0)|qtHaveModule(quick) { SUBDIRS += QMLPlayer QMLPlayer.depends += common sdk_build: SUBDIRS *= QMLPlayer/QMLPlayer_sdk.pro } } OTHER_FILES = qml/*.qml QtAV-1.12.0/examples/filters/000077500000000000000000000000001312235004300156655ustar00rootroot00000000000000QtAV-1.12.0/examples/filters/SimpleFilter.cpp000066400000000000000000000110011312235004300207610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SimpleFilter.h" #include #include namespace QtAV { SimpleFilter::SimpleFilter(QObject *parent): VideoFilter(parent) , mCanRot(true) , mWave(true) { srand(QTime::currentTime().msec()); mStartValue = (qreal)(rand()%1000)/qreal(1000.0); mTime.start(); startTimer(100); } SimpleFilter::~SimpleFilter() { } void SimpleFilter::enableRotate(bool r) { mCanRot = r; } void SimpleFilter::enableWaveEffect(bool w) { mWave = w; } void SimpleFilter::setText(const QString &text) { mText = text; } QString SimpleFilter::text() const { return mText; } void SimpleFilter::setImage(const QImage &img) { mImage = img; } void SimpleFilter::prepare() { VideoFilterContext *ctx = static_cast(context()); if (!mText.isEmpty()) { ctx->font.setPixelSize(32); ctx->font.setBold(true); if (!mCanRot) return; mMat.translate(ctx->rect.center().x(), 0, 0); } else if (!mImage.isNull()) { if (!mCanRot) return; mMat.translate(ctx->rect.x() + mImage.width()/2, 0, 0); } if (mCanRot) { mMat.rotate(mStartValue*360, 0, 1, -0.1); } } void SimpleFilter::timerEvent(QTimerEvent *) { if (qobject_cast(parent())) ((QWidget*)parent())->update(); } void SimpleFilter::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(statistics); Q_UNUSED(frame); if (!isEnabled()) return; int t = mTime.elapsed()/100; VideoFilterContext *ctx = static_cast(context()); if (mCanRot) { mMat.rotate(2, 0, 1, -0.1); ctx->transform = mMat.toTransform(); } if (mText.isEmpty()) { if (mImage.isNull()) return; if (mCanRot) ctx->drawImage(QPointF(-mImage.width()/2, ctx->rect.y()), mImage, QRectF(0, 0, mImage.width(), mImage.height())); else ctx->drawImage(ctx->rect.topLeft(), mImage, QRectF(0, 0, mImage.width(), mImage.height())); return; } if (mWave) { static int sin_tbl[16] = { 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38 }; QFontMetrics fm(ctx->font); int h = fm.height(); int x = ctx->rect.x(); int y = ctx->rect.y() + h + fm.descent(); for (int i = 0; i < mText.size(); ++i) { int i16 = (t+i) & 15; ctx->pen.setColor(QColor::fromHsv((15-i16)*16, 255, 255)); if (mCanRot) ctx->drawPlainText(QPointF(x-fm.width(mText)/2-ctx->rect.x(), y-sin_tbl[i16]*h/400), mText.mid(i, 1)); else ctx->drawPlainText(QPointF(x, y-sin_tbl[i16]*h/400), mText.mid(i, 1)); x += fm.width(mText[i]); } } else { qreal c = fabs(sin((float)t)); c += mStartValue; c /= 2.0; QLinearGradient g(0, 0, 100, 32); g.setSpread(QGradient::ReflectSpread); g.setColorAt(0, QColor::fromHsvF(c, 1, 1, 1)); g.setColorAt(1, QColor::fromHsvF(c > 0.5?c-0.5:c+0.5, 1, 1, 1)); ctx->pen.setBrush(QBrush(g)); ctx->drawRichText(ctx->rect, mText); return; if (mCanRot) { QFontMetrics fm(ctx->font); ctx->drawPlainText(QRectF(-fm.width(mText)/2, ctx->rect.y(), ctx->rect.width(), ctx->rect.height()), Qt::TextWordWrap, mText); } else { ctx->drawPlainText(ctx->rect, Qt::TextWordWrap, mText); } } } } //namespace QtAV QtAV-1.12.0/examples/filters/SimpleFilter.h000066400000000000000000000040221312235004300204330ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_EXAMPLE_SIMPLEFILTER_H #define QTAV_EXAMPLE_SIMPLEFILTER_H #include #include #include namespace QtAV { class SimpleFilter : public VideoFilter { Q_OBJECT public: SimpleFilter(QObject *parent = 0); virtual ~SimpleFilter(); void enableRotate(bool r); void enableWaveEffect(bool w); bool isSupported(VideoFilterContext::Type ct) const { return ct == VideoFilterContext::QtPainter || ct == VideoFilterContext::X11; } void setText(const QString& text); QString text() const; //show image if text is null void setImage(const QImage& img); void prepare(); protected: virtual void process(Statistics* statistics, VideoFrame* frame); virtual void timerEvent(QTimerEvent *); private: bool mCanRot; bool mWave; QTime mTime; qreal mStartValue; QString mText; QMatrix4x4 mMat; QImage mImage; }; } //namespace QtAV #endif // QTAV_EXAMPLE_SIMPLEFILTER_H QtAV-1.12.0/examples/filters/filters.pro000066400000000000000000000005711312235004300200620ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = filters PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += \ main.cpp \ SimpleFilter.cpp HEADERS += \ SimpleFilter.h RESOURCES += \ res.qrc QtAV-1.12.0/examples/filters/main.cpp000066400000000000000000000124211312235004300173150ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "SimpleFilter.h" using namespace QtAV; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (a.arguments().contains(QString::fromLatin1("-h")) || a.arguments().contains(QString::fromLatin1("--help"))) { qDebug("Usage: %s [-vo qt/gl/d2d/gdi] [url/path]filename", a.applicationFilePath().section(QDir::separator(), -1).toUtf8().constData()); qDebug("\n%s", aboutQtAV_PlainText().toUtf8().constData()); return 0; } QString vo; int idx = a.arguments().indexOf(QString::fromLatin1("-vo")); if (idx > 0) { vo = a.arguments().at(idx+1); } else { QString exe(a.arguments().at(0)); qDebug("exe: %s", exe.toUtf8().constData()); int i = exe.lastIndexOf(QLatin1Char('-')); if (i > 0) { vo = exe.mid(i+1, exe.indexOf(QLatin1Char('.')) - i - 1); } } qDebug("vo: %s", vo.toUtf8().constData()); bool opt_has_file = argc > idx + 2; vo = vo.toLower(); if (vo != QLatin1String("gl") && vo != QLatin1String("d2d") && vo != QLatin1String("gdi") && vo != QLatin1String("xv") && vo != QLatin1String("x11")) vo = QString::fromLatin1("qpainter"); QString title = QString::fromLatin1("QtAV %1 wbsecg1@gmail.com").arg(QtAV_Version_String_Long()); VideoRenderer *renderer = 0; bool textfilter = false; if (vo == QLatin1String("gl")) { renderer = VideoRenderer::create(VideoRendererId_GLWidget2); textfilter = true; } else if (vo == QLatin1String("d2d")) { renderer = VideoRenderer::create(VideoRendererId_Direct2D); } else if (vo == QLatin1String("gdi")) { renderer = VideoRenderer::create(VideoRendererId_GDI); } else if (vo == QLatin1String("xv")) { renderer = VideoRenderer::create(VideoRendererId_XV); textfilter = true; } else if (vo == QLatin1String("x11")) { renderer = VideoRenderer::create(VideoRendererId_X11); textfilter = true; } else { renderer = VideoRenderer::create(VideoRendererId_Widget); textfilter = true; } if (!renderer) { QMessageBox::critical(0, QString::fromLatin1("QtAV"), QString::fromLatin1("vo '%1' not supported").arg(vo)); return 1; } renderer->widget()->setAttribute(Qt::WA_DeleteOnClose); renderer->widget()->setWindowTitle(title); renderer->setOutAspectRatioMode(VideoRenderer::VideoAspectRatio); renderer->widget()->resize(renderer->widget()->width(), renderer->widget()->width()*9/16); renderer->widget()->show(); AVPlayer player; player.addVideoRenderer(renderer); if (textfilter) { SimpleFilter *filter = new SimpleFilter(renderer->widget()); filter->setText(QString::fromLatin1("Filter on Renderer")); VideoFilterContext *ctx = static_cast(filter->context()); ctx->rect = QRect(200, 150, 400, 60); ctx->opacity = 0.7; filter->enableRotate(false); filter->prepare(); renderer->installFilter(filter); filter = new SimpleFilter(renderer->widget()); filter->setText(QString()); filter->setImage(QImage(QString::fromLatin1(":/images/qt-logo.png"))); ctx = static_cast(filter->context()); ctx->rect = QRect(400, 80, 200, 200); ctx->opacity = 0.618; filter->enableRotate(true); filter->enableWaveEffect(false); filter->prepare(); renderer->installFilter(filter); filter = new SimpleFilter(&player); filter->setText(QString::fromLatin1("

HTML Filter onVideo Frame

")); filter->enableWaveEffect(false); filter->enableRotate(true); ctx = static_cast(filter->context()); ctx->rect = QRect(200, 100, 400, 60); filter->prepare(); player.installFilter(filter); } SubtitleFilter *sub = new SubtitleFilter(&player); sub->setPlayer(&player); player.installFilter(sub); opt_has_file &= argc > idx + 2; if (opt_has_file) { player.play(a.arguments().last()); } int ret = a.exec(); return ret; } QtAV-1.12.0/examples/filters/qt-logo.png000066400000000000000000000071601312235004300177610ustar00rootroot00000000000000PNG  IHDRPPsBIT|d pHYs==+tEXtSoftwarewww.inkscape.org< IDATx\yp՝zFsi4˖50gQDLXv6E*bHbPٰ[b lA,%lٖ۲uYgh1g0\y_~C)ExTpZ *g,̶zifc`[P~e2ݩ*iϖC)Xx X,*OGr7KLQpHxrR~E Bl&)CLzj32Â;ɲ&RC(SH2L=[:<$ 0S(Liؐ*)U.U4vn bY<7L]`êąUȓ3H٭(cp0S&2NJ wjel@)UcAA,&Eu͵<3,؅U7g%ڎ)Ǫ &']@dZlGD+ƛ`,_qS&UOy /@TyH9mϿxf~? zuy2Z`۞xcZjSr$qi J=xdտ#Þ hͼ}x@Z@TP[*kL눲~hve ׯ XpAfǍ yР4kSs;xiW-0o4N_QK7\QN X[Y5IR}jw֬ĂE@-|l/_EwO;ЭW+0pQ*k+LI9"o@Ǫ8XvL1n980<&^ a0lBk  h,UNքC;&Rhs D@j@Hq {(jĸ0˾j<12a9Ef@%bfr ((,Uc30H:(i.)${`aV./h+L$\@OA9S~i~"#|b.5  XP< Jm_gf^kmcn|]Qߗ@$\tn6i8˔?'G_odu{~-1gNjL GIQO +1Dff{DKi־ rQ_*8u"5+[XD;4eBRĨb8U-):r[A) `Ǯ(;6_Z1w[ݺG"LwkJ)NG~ |.vf CczHJj0؅a| p骬`%*gn}gWuow}GuanϚP.AWfgQfS?q@A\:oվd+ i6ٹjǡah؍q$M@gKWiV/M>ʐcգ]񋝆Y'pEu+X;m=I]]PYvH"{QșYy 9 J7\? 2Â?](vpOšo}q?cdо?\1ڔDblv7uYay+~oWr\<3L](旹p(_SoƂc.:bgjÍi)Ywbmu,S۪caXt -)/lJ/rMIQ(Bw;Kjs!:f,?%/lHNK3KZ׽bȽڍUGU/@G 3R*`wyj` -ZWȒ41\NJ ˫2ᱢ Ts  )8)fgPٰX?}ڌ7vjpW:lI#NB ;/ ?ϥI$4P2Msg;P\@mocѬXK]¨` 9tVOdi[1i# K4!m8'x$\,K 0xOSa#_p7%g†GO%gw]97& % Ǫ#]P%dxo.['rG:,Ko m=5IG A']@6)2ݾD<S/%{jgx6 <.IYUϴM 1IENDB`QtAV-1.12.0/examples/filters/res.qrc000066400000000000000000000001401312235004300171600ustar00rootroot00000000000000 qt-logo.png QtAV-1.12.0/examples/framereader/000077500000000000000000000000001312235004300164725ustar00rootroot00000000000000QtAV-1.12.0/examples/framereader/framereader.pro000066400000000000000000000002351312235004300214710ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/examples/framereader/main.cpp000066400000000000000000000042031312235004300201210ustar00rootroot00000000000000/****************************************************************************** framereader: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); FrameReader r; r.setMedia(a.arguments().last()); QQueue t; int count = 0; qint64 t0 = QDateTime::currentMSecsSinceEpoch(); while (r.readMore()) { while (r.hasEnoughVideoFrames()) { const VideoFrame f = r.getVideoFrame(); //TODO: if eof if (!f) continue; count++; //r.readMore(); const qint64 now = QDateTime::currentMSecsSinceEpoch(); const qint64 dt = now - t0; t.enqueue(now); printf("decode @%.3f count: %d, elapsed: %lld, fps: %.1f/%.1f\r", f.timestamp(), count, dt, count*1000.0/dt, t.size()*1000.0/(now - t.first()));fflush(0); if (t.size() > 10) t.dequeue(); } } while (r.hasVideoFrame()) { const VideoFrame f = r.getVideoFrame(); qDebug("pts: %.3f", f.timestamp()); } qDebug("read done"); return 0; } QtAV-1.12.0/examples/glslfilter/000077500000000000000000000000001312235004300163645ustar00rootroot00000000000000QtAV-1.12.0/examples/glslfilter/glslfilter.pro000066400000000000000000000003441312235004300212560ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle greaterThan(QT_MAJOR_VERSION, 4): QT += widgets config_gl: QT += opengl PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/examples/glslfilter/main.cpp000066400000000000000000000062051312235004300200170ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include using namespace QtAV; #define GLSL(x) #x "" class WaveShader : public DynamicShaderObject { float t; qreal A; Q_OBJECT Q_PROPERTY(qreal u_A READ u_A WRITE setU_A NOTIFY u_AChanged) //you can use meta property public: qreal u_A() const {return A;} void setU_A(qreal v) { A=v; Q_EMIT u_AChanged();} WaveShader(QObject *parent = 0) : DynamicShaderObject(parent) , t(0) , A(0.06) { setProperty("u_t", 0); // you can use dynamic property //setProperty("u_A", 0.06); setProperty("u_omega", 5); setHeader(QLatin1String(GLSL( uniform float u_omega; uniform float u_A; uniform float u_t; ))); setSample(QLatin1String(GLSL( vec4 sample2d(sampler2D tex, vec2 pos, int p) { vec2 pulse = sin(u_t - u_omega * pos); vec2 coord = pos + u_A*vec2(pulse.x, -pulse.x); return texture(tex, coord); }))); startTimer(40); } Q_SIGNALS: void u_AChanged(); protected: void timerEvent(QTimerEvent*) { t+=2.0*M_PI/25.0; setProperty("u_t", t); } }; int main(int argc, char** argv) { QApplication a(argc, argv); if (a.arguments().size() < 2) { qDebug() << a.arguments().at(0) << " file"; return 0; } VideoOutput vo; if (!vo.opengl()) { qFatal("opengl renderer is required!"); return 1; } if (!vo.widget()) return 1; GLSLFilter *f = new GLSLFilter(); f->setOwnedByTarget(); f->opengl()->setUserShader(new WaveShader(f)); f->installTo(&vo); vo.widget()->show(); vo.widget()->resize(800, 500); vo.widget()->setWindowTitle(QLatin1String("GLSLFilter example")); AVPlayer player; player.setRenderer(&vo); player.play(a.arguments().last()); return a.exec(); } #include "main.moc" QtAV-1.12.0/examples/player/000077500000000000000000000000001312235004300155115ustar00rootroot00000000000000QtAV-1.12.0/examples/player/ClickableMenu.cpp000066400000000000000000000025361312235004300207210ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "ClickableMenu.h" #include ClickableMenu::ClickableMenu(QWidget *parent) : QMenu(parent) { } ClickableMenu::ClickableMenu(const QString &title, QWidget *parent) : QMenu(title, parent) { } void ClickableMenu::mouseReleaseEvent(QMouseEvent *e) { QAction *action = actionAt(e->pos()); if (action) { action->activate(QAction::Trigger); return; } QMenu::mouseReleaseEvent(e); } QtAV-1.12.0/examples/player/ClickableMenu.h000066400000000000000000000024171312235004300203640ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef CLICKABLEMENU_H #define CLICKABLEMENU_H #include class ClickableMenu : public QMenu { Q_OBJECT public: explicit ClickableMenu(QWidget *parent = 0); explicit ClickableMenu(const QString& title, QWidget *parent = 0); protected: virtual void mouseReleaseEvent(QMouseEvent *); }; #endif // CLICKABLEMENU_H QtAV-1.12.0/examples/player/EventFilter.cpp000066400000000000000000000313211312235004300204440ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "EventFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QtAV; // TODO: watch main window EventFilter::EventFilter(AVPlayer *player) : QObject(player), menu(0) { } EventFilter::~EventFilter() { if (menu) { delete menu; menu = 0; } } void EventFilter::openLocalFile() { QString file = QFileDialog::getOpenFileName(0, tr("Open a video")); if (file.isEmpty()) return; AVPlayer *player = static_cast(parent()); player->play(file); } void EventFilter::openUrl() { QString url = QInputDialog::getText(0, tr("Open an url"), tr("Url")); if (url.isEmpty()) return; AVPlayer *player = static_cast(parent()); player->play(url); } void EventFilter::about() { QtAV::about(); } void EventFilter::aboutFFmpeg() { QtAV::aboutFFmpeg(); } void EventFilter::help() { emit helpRequested(); return; static QString help = QString::fromLatin1("

") +tr("Drag and drop a file to player\n") + QString::fromLatin1("

" "

") + tr("A: switch aspect ratio") + QString::fromLatin1("

" "

") + tr("Double click to switch fullscreen") + QString::fromLatin1("

" "

") + tr("Shortcut:\n") + QString::fromLatin1("

" "

") + tr("Space: pause/continue\n") + QString::fromLatin1("

" "

") + tr("F: fullscreen on/off\n") + QString::fromLatin1("

" "

") + tr("T: stays on top on/off\n") + QString::fromLatin1("

" "

") + tr("N: show next frame. Continue the playing by pressing 'Space'\n") + QString::fromLatin1("

" "

") + tr("Ctrl+O: open a file\n") + QString::fromLatin1("

" "

") + tr("O: OSD\n") + QString::fromLatin1("

" "

") + tr("P: replay\n") + QString::fromLatin1("

" "

") + tr("Q/ESC: quit\n") + QString::fromLatin1("

" "

") + tr("S: stop\n") + QString::fromLatin1("

" "

") + tr("R: rotate 90") + QString::fromLatin1("

" "

") + tr("M: mute on/off\n") + QString::fromLatin1("

" "

") + tr("C: capture video") + QString::fromLatin1("

" "

") + tr("Up/Down: volume +/-\n") + QString::fromLatin1("

" "

") + tr("Ctrl+Up/Down: speed +/-\n") + QString::fromLatin1("

" "

") + tr("->/<-: seek forward/backward\n"); QMessageBox::about(0, tr("Help"), help); } bool EventFilter::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); AVPlayer *player = static_cast(parent()); if (!player || !player->renderer() || !player->renderer()->widget()) return false; if (qobject_cast(watched) != player->renderer()->widget()) { return false; } #ifndef QT_NO_DYNAMIC_CAST //dynamic_cast is defined as a macro to force a compile error if (player->renderer() != dynamic_cast(watched)) { // return false; } #endif QEvent::Type type = event->type(); switch (type) { case QEvent::KeyPress: { QKeyEvent *key_event = static_cast(event); int key = key_event->key(); Qt::KeyboardModifiers modifiers = key_event->modifiers(); switch (key) { case Qt::Key_0: player->seek(0LL); break; case Qt::Key_C: //capture player->videoCapture()->capture(); break; case Qt::Key_N: //check playing? player->stepForward(); break; case Qt::Key_B: player->stepBackward(); break; case Qt::Key_P: player->stop(); player->play(); break; case Qt::Key_Q: case Qt::Key_Escape: qApp->quit(); break; case Qt::Key_S: player->stop(); //check playing? break; case Qt::Key_Space: //check playing? qDebug("isPaused = %d", player->isPaused()); player->pause(!player->isPaused()); break; case Qt::Key_F: { //TODO: move to gui QWidget *w = qApp->activeWindow(); if (!w) return false; w->setWindowState(w->windowState() ^ Qt::WindowFullScreen); } break; case Qt::Key_U: player->setNotifyInterval(player->notifyInterval() + 100); break; case Qt::Key_D: player->setNotifyInterval(player->notifyInterval() - 100); break; case Qt::Key_Up: { AudioOutput *ao = player->audio(); if (modifiers == Qt::ControlModifier) { qreal s = player->speed(); if (s < 1.4) s += 0.02; else s += 0.05; if (qAbs(s-1.0) <= 0.01) s = 1.0; player->setSpeed(s); return true; } if (ao && ao->isAvailable()) { qreal v = player->audio()->volume(); if (v > 0.5) v += 0.1; else if (v > 0.1) v += 0.05; else v += 0.025; player->audio()->setVolume(v); qDebug("vol = %.3f", player->audio()->volume()); } } break; case Qt::Key_Down: { AudioOutput *ao = player->audio(); if (modifiers == Qt::ControlModifier) { qreal s = player->speed(); if (s < 1.4) s -= 0.02; else s -= 0.05; if (qAbs(s-1.0) <= 0.01) s = 1.0; s = qMax(s, 0.0); player->setSpeed(s); return true; } if (ao && ao->isAvailable()) { qreal v = player->audio()->volume(); if (v > 0.5) v -= 0.1; else if (v > 0.1) v -= 0.05; else v -= 0.025; player->audio()->setVolume(v); qDebug("vol = %.3f", player->audio()->volume()); } } break; case Qt::Key_O: { if (modifiers == Qt::ControlModifier) { //TODO: emit a signal so we can use custome dialogs? openLocalFile(); } else/* if (m == Qt::NoModifier) */{ emit showNextOSD(); } } break; case Qt::Key_Left: qDebug("<-"); player->setSeekType(key_event->isAutoRepeat() ? KeyFrameSeek : AccurateSeek); player->seekBackward(); break; case Qt::Key_Right: qDebug("->"); player->setSeekType(key_event->isAutoRepeat() ? KeyFrameSeek : AccurateSeek); player->seekForward(); break; case Qt::Key_M: if (player->audio()) { player->audio()->setMute(!player->audio()->isMute()); } break; case Qt::Key_A: { VideoRenderer* renderer = player->renderer(); VideoRenderer::OutAspectRatioMode r = renderer->outAspectRatioMode(); renderer->setOutAspectRatioMode(VideoRenderer::OutAspectRatioMode(((int)r+1)%2)); } break; case Qt::Key_R: { VideoRenderer* renderer = player->renderer(); renderer->setOrientation(renderer->orientation() + 90); qDebug("orientation: %d", renderer->orientation()); } break; case Qt::Key_T: { QWidget *w = qApp->activeWindow(); if (!w) return false; w->setWindowFlags(w->window()->windowFlags() ^ Qt::WindowStaysOnTopHint); //call setParent() when changing the flags, causing the widget to be hidden w->show(); } break; case Qt::Key_F1: help(); break; default: return false; } break; } case QEvent::DragEnter: case QEvent::DragMove: { QDropEvent *e = static_cast(event); if (e->mimeData()->hasUrls()) e->acceptProposedAction(); else e->ignore(); } break; case QEvent::Drop: { QDropEvent *e = static_cast(event); if (e->mimeData()->hasUrls()) { QString path = e->mimeData()->urls().first().toLocalFile(); player->stop(); player->play(path); e->acceptProposedAction(); } else { e->ignore(); } } break; case QEvent::GraphicsSceneContextMenu: { QGraphicsSceneContextMenuEvent *e = static_cast(event); showMenu(e->screenPos()); } break; case QEvent::ContextMenu: { QContextMenuEvent *e = static_cast(event); showMenu(e->globalPos()); } break; default: return false; } return true; //false: for text input } void EventFilter::showMenu(const QPoint &p) { if (!menu) { menu = new QMenu(); menu->addAction(tr("Open"), this, SLOT(openLocalFile())); menu->addAction(tr("Open Url"), this, SLOT(openUrl())); menu->addSeparator(); menu->addAction(tr("About"), this, SLOT(about())); menu->addAction(tr("Help"), this, SLOT(help())); menu->addSeparator(); } menu->exec(p); } WindowEventFilter::WindowEventFilter(QWidget *window) : QObject(window) , mpWindow(window) { } bool WindowEventFilter::eventFilter(QObject *watched, QEvent *event) { if (watched != mpWindow) return false; if (event->type() == QEvent::WindowStateChange) { QWindowStateChangeEvent *e = static_cast(event); mpWindow->updateGeometry(); if (mpWindow->windowState().testFlag(Qt::WindowFullScreen) || e->oldState().testFlag(Qt::WindowFullScreen)) { emit fullscreenChanged(); } return false; } if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *me = static_cast(event); Qt::MouseButton mbt = me->button(); if (mbt == Qt::LeftButton) { gMousePos = me->globalPos(); iMousePos = me->pos(); } return false; } if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *me = static_cast(event); Qt::MouseButton mbt = me->button(); if (mbt != Qt::LeftButton) return false; iMousePos = QPoint(); gMousePos = QPoint(); return false; } if (event->type() == QEvent::MouseMove) { if (iMousePos.isNull() || gMousePos.isNull()) return false; QMouseEvent *me = static_cast(event); int x = mpWindow->pos().x(); int y = mpWindow->pos().y(); int dx = me->globalPos().x() - gMousePos.x(); int dy = me->globalPos().y() - gMousePos.y(); gMousePos = me->globalPos(); mpWindow->move(x + dx, y + dy); return false; } return false; } QtAV-1.12.0/examples/player/EventFilter.h000066400000000000000000000040121312235004300201060ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef QTAV_EVENTFILTER_H #define QTAV_EVENTFILTER_H /* * This class is used interally as QtAV's default event filter. It is suite for single player object */ #include #include QT_BEGIN_NAMESPACE class QMenu; class QPoint; QT_END_NAMESPACE namespace QtAV { class AVPlayer; } //for internal use class EventFilter : public QObject { Q_OBJECT public: explicit EventFilter(QtAV::AVPlayer *player); virtual ~EventFilter(); signals: void helpRequested(); void showNextOSD(); public slots: void openLocalFile(); void openUrl(); void about(); void aboutFFmpeg(); void help(); protected: virtual bool eventFilter(QObject *, QEvent *); void showMenu(const QPoint& p); private: QMenu *menu; }; class WindowEventFilter : public QObject { Q_OBJECT public: WindowEventFilter(QWidget *window); signals: void fullscreenChanged(); protected: virtual bool eventFilter(QObject *watched, QEvent *event); private: QWidget *mpWindow; QPoint gMousePos, iMousePos; }; #endif // QTAV_EVENTFILTER_H QtAV-1.12.0/examples/player/MainWindow.cpp000066400000000000000000001554171312235004300203060ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "MainWindow.h" #include "EventFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ClickableMenu.h" #include "Slider.h" #include "StatisticsView.h" #include "TVView.h" #include "config/DecoderConfigPage.h" #include "config/VideoEQConfigPage.h" #include "config/ConfigDialog.h" #include "filters/OSDFilter.h" //#include "filters/AVFilterSubtitle.h" #include "playlist/PlayList.h" #include "../common/common.h" #ifndef QT_NO_OPENGL #include "QtAV/GLSLFilter.h" #endif /* *TODO: * disable a/v actions if player is 0; * use action's value to set player's parameters when start to play a new file */ #define AVDEBUG() \ qDebug("%s %s @%d", __FILE__, __FUNCTION__, __LINE__); using namespace QtAV; const qreal kVolumeInterval = 0.04; extern QStringList idsToNames(QVector ids); extern QVector idsFromNames(const QStringList& names); void QLabelSetElideText(QLabel *label, QString text, int W = 0) { QFontMetrics metrix(label->font()); int width = label->width() - label->indent() - label->margin(); if (W || label->parent()) { int w = W; if (!w) w = ((QWidget*)label->parent())->width(); width = qMax(w - label->indent() - label->margin() - 13*(30), 0); //TODO: why 30? } QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); label->setText(clippedText); } MainWindow::MainWindow(QWidget *parent) : QWidget(parent) , mIsReady(false) , mHasPendingPlay(false) , mControlOn(false) , mShowControl(2) , mRepeateMax(0) , mpVOAction(0) , mpPlayer(0) , mpRenderer(0) , mpVideoFilter(0) , mpAudioFilter(0) , mpStatisticsView(0) , mpOSD(0) , mpSubtitle(0) , m_preview(0) , m_shader(NULL) { #if defined(Q_OS_MACX) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QApplication::setStyle(QStyleFactory::create("Fusion")); #endif setWindowIcon(QIcon(QString::fromLatin1(":/QtAV.svg"))); mpOSD = new OSDFilter(this); mpSubtitle = new SubtitleFilter(this); mpChannelAction = 0; mpChannelMenu = 0; mpAudioTrackAction = 0; setMouseTracking(true); //mouseMoveEvent without press. connect(this, SIGNAL(ready()), SLOT(processPendingActions())); //QTimer::singleShot(10, this, SLOT(setupUi())); setupUi(); //setToolTip(tr("Click black area to use shortcut(see right click menu)")); WindowEventFilter *we = new WindowEventFilter(this); installEventFilter(we); connect(we, SIGNAL(fullscreenChanged()), SLOT(handleFullscreenChange())); } MainWindow::~MainWindow() { if (m_preview) { m_preview->close(); delete m_preview; } mpHistory->save(); mpPlayList->save(); if (mpVolumeSlider && !mpVolumeSlider->parentWidget()) { mpVolumeSlider->close(); delete mpVolumeSlider; mpVolumeSlider = 0; } if (mpStatisticsView) { delete mpStatisticsView; mpStatisticsView = 0; } } void MainWindow::initPlayer() { mpPlayer = new AVPlayer(this); mIsReady = true; VideoRenderer *vo = VideoRenderer::create((VideoRendererId)property("rendererId").toInt()); if (!vo || !vo->isAvailable() || !vo->widget()) { QMessageBox::critical(0, QString::fromLatin1("QtAV"), tr("Video renderer is ") + tr("not availabe on your platform!")); } setRenderer(vo); //mpSubtitle->installTo(mpPlayer); //filter on frame mpSubtitle->setPlayer(mpPlayer); //mpPlayer->setAudioOutput(AudioOutputFactory::create(AudioOutputId_OpenAL)); EventFilter *ef = new EventFilter(mpPlayer); qApp->installEventFilter(ef); connect(ef, SIGNAL(helpRequested()), SLOT(help())); connect(ef, SIGNAL(showNextOSD()), SLOT(showNextOSD())); onCaptureConfigChanged(); onAVFilterVideoConfigChanged(); onAVFilterAudioConfigChanged(); connect(&Config::instance(), SIGNAL(forceFrameRateChanged()), SLOT(setFrameRate())); connect(&Config::instance(), SIGNAL(captureDirChanged(QString)), SLOT(onCaptureConfigChanged())); connect(&Config::instance(), SIGNAL(captureFormatChanged(QString)), SLOT(onCaptureConfigChanged())); connect(&Config::instance(), SIGNAL(captureQualityChanged(int)), SLOT(onCaptureConfigChanged())); connect(&Config::instance(), SIGNAL(avfilterVideoChanged()), SLOT(onAVFilterVideoConfigChanged())); connect(&Config::instance(), SIGNAL(avfilterAudioChanged()), SLOT(onAVFilterAudioConfigChanged())); connect(&Config::instance(), SIGNAL(bufferValueChanged()), SLOT(onBufferValueChanged())); connect(&Config::instance(), SIGNAL(abortOnTimeoutChanged()), SLOT(onAbortOnTimeoutChanged())); connect(mpStopBtn, SIGNAL(clicked()), this, SLOT(stopUnload())); connect(mpForwardBtn, SIGNAL(clicked()), mpPlayer, SLOT(seekForward())); connect(mpBackwardBtn, SIGNAL(clicked()), mpPlayer, SLOT(seekBackward())); connect(mpVolumeBtn, SIGNAL(clicked()), SLOT(showHideVolumeBar())); connect(mpVolumeSlider, SIGNAL(sliderPressed()), SLOT(setVolume())); connect(mpVolumeSlider, SIGNAL(valueChanged(int)), SLOT(setVolume())); connect(mpPlayer, SIGNAL(seekFinished(qint64)), SLOT(onSeekFinished(qint64))); connect(mpPlayer, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), SLOT(onMediaStatusChanged())); connect(mpPlayer, SIGNAL(bufferProgressChanged(qreal)), SLOT(onBufferProgress(qreal))); connect(mpPlayer, SIGNAL(error(QtAV::AVError)), this, SLOT(handleError(QtAV::AVError))); connect(mpPlayer, SIGNAL(started()), this, SLOT(onStartPlay())); connect(mpPlayer, SIGNAL(stopped()), this, SLOT(onStopPlay())); connect(mpPlayer, SIGNAL(paused(bool)), this, SLOT(onPaused(bool))); connect(mpPlayer, SIGNAL(speedChanged(qreal)), this, SLOT(onSpeedChange(qreal))); connect(mpPlayer, SIGNAL(positionChanged(qint64)), this, SLOT(onPositionChange(qint64))); //connect(mpPlayer, SIGNAL(volumeChanged(qreal)), SLOT(syncVolumeUi(qreal))); connect(mpVideoEQ, SIGNAL(brightnessChanged(int)), this, SLOT(onBrightnessChanged(int))); connect(mpVideoEQ, SIGNAL(contrastChanged(int)), this, SLOT(onContrastChanged(int))); connect(mpVideoEQ, SIGNAL(hueChanegd(int)), this, SLOT(onHueChanged(int))); connect(mpVideoEQ, SIGNAL(saturationChanged(int)), this, SLOT(onSaturationChanged(int))); connect(mpCaptureBtn, SIGNAL(clicked()), mpPlayer->videoCapture(), SLOT(capture())); emit ready(); //emit this signal after connection. otherwise the slots may not be called for the first time } void MainWindow::onSeekFinished(qint64 pos) { qDebug("seek finished at %lld/%lld", pos, mpPlayer->position()); } void MainWindow::stopUnload() { mpPlayer->stop(); } void MainWindow::setupUi() { QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setSpacing(0); mainLayout->setContentsMargins(QMargins()); setLayout(mainLayout); mpPlayerLayout = new QVBoxLayout(); mpControl = new QWidget(this); mpControl->setMaximumHeight(30); //mpPreview = new QLable(this); mpTimeSlider = new Slider(mpControl); mpTimeSlider->setDisabled(true); mpTimeSlider->setTracking(true); mpTimeSlider->setOrientation(Qt::Horizontal); mpTimeSlider->setMinimum(0); mpCurrent = new QLabel(mpControl); mpCurrent->setToolTip(tr("Current time")); mpCurrent->setContentsMargins(QMargins(2, 2, 2, 2)); mpCurrent->setText(QString::fromLatin1("00:00:00")); mpEnd = new QLabel(mpControl); mpEnd->setToolTip(tr("Duration")); mpEnd->setContentsMargins(QMargins(2, 2, 2, 2)); mpEnd->setText(QString::fromLatin1("00:00:00")); mpTitle = new QLabel(mpControl); mpTitle->setToolTip(tr("Render engine")); mpTitle->setText(QString::fromLatin1("QPainter")); mpTitle->setIndent(8); mpSpeed = new QLabel(QString::fromLatin1("1.00")); mpSpeed->setContentsMargins(QMargins(1, 1, 1, 1)); mpSpeed->setToolTip(tr("Speed. Ctrl+Up/Down")); mpPlayPauseBtn = new QToolButton(mpControl); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/play.svg"))); mpStopBtn = new QToolButton(mpControl); mpStopBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/stop.svg"))); mpBackwardBtn = new QToolButton(mpControl); mpBackwardBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/backward.svg"))); mpForwardBtn = new QToolButton(mpControl); mpForwardBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/forward.svg"))); mpOpenBtn = new QToolButton(mpControl); mpOpenBtn->setToolTip(tr("Open")); mpOpenBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/open.svg"))); mpInfoBtn = new QToolButton(); mpInfoBtn->setToolTip(QString::fromLatin1("Media information")); mpInfoBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/info.svg"))); mpCaptureBtn = new QToolButton(); mpCaptureBtn->setToolTip(tr("Capture")); mpCaptureBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/capture.svg"))); mpVolumeBtn = new QToolButton(); mpVolumeBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/sound.svg"))); mpVolumeSlider = new Slider(); mpVolumeSlider->hide(); mpVolumeSlider->setOrientation(Qt::Horizontal); mpVolumeSlider->setMinimum(0); const int kVolumeSliderMax = 100; mpVolumeSlider->setMaximum(kVolumeSliderMax); //mpVolumeSlider->setMaximumHeight(12); mpVolumeSlider->setMaximumWidth(88); mpVolumeSlider->setValue(int(1.0/kVolumeInterval*qreal(kVolumeSliderMax)/100.0)); setVolume(); mpMenuBtn = new QToolButton(); mpMenuBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/menu.svg"))); mpMenuBtn->setAutoRaise(true); mpMenuBtn->setPopupMode(QToolButton::InstantPopup); QMenu *subMenu = 0; QWidgetAction *pWA = 0; mpMenu = new QMenu(mpMenuBtn); mpMenu->addAction(tr("Open Url"), this, SLOT(openUrl())); //mpMenu->addAction(tr("Online channels"), this, SLOT(onTVMenuClick())); mpMenu->addSeparator(); subMenu = new QMenu(tr("Play list")); mpMenu->addMenu(subMenu); mpPlayList = new PlayList(this); mpPlayList->setSaveFile(Config::instance().defaultDir() + QString::fromLatin1("/playlist.qds")); mpPlayList->load(); connect(mpPlayList, SIGNAL(aboutToPlay(QString)), SLOT(play(QString))); pWA = new QWidgetAction(0); pWA->setDefaultWidget(mpPlayList); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? subMenu = new QMenu(tr("History")); mpMenu->addMenu(subMenu); mpHistory = new PlayList(this); mpHistory->setMaxRows(20); mpHistory->setSaveFile(Config::instance().defaultDir() + QString::fromLatin1("/history.qds")); mpHistory->load(); connect(mpHistory, SIGNAL(aboutToPlay(QString)), SLOT(play(QString))); pWA = new QWidgetAction(0); pWA->setDefaultWidget(mpHistory); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? mpMenu->addSeparator(); //mpMenu->addAction(tr("Report"))->setEnabled(false); //report bug, suggestions etc. using maillist? mpMenu->addAction(tr("About"), this, SLOT(about())); mpMenu->addAction(tr("Help"), this, SLOT(help())); mpMenu->addAction(tr("Donate"), this, SLOT(donate())); mpMenu->addAction(tr("Setup"), this, SLOT(setup())); mpMenu->addSeparator(); mpMenuBtn->setMenu(mpMenu); mpMenu->addSeparator(); subMenu = new QMenu(tr("Speed")); mpMenu->addMenu(subMenu); QDoubleSpinBox *pSpeedBox = new QDoubleSpinBox(0); pSpeedBox->setRange(0.01, 20); pSpeedBox->setValue(1.0); pSpeedBox->setSingleStep(0.01); pSpeedBox->setCorrectionMode(QAbstractSpinBox::CorrectToPreviousValue); pWA = new QWidgetAction(0); pWA->setDefaultWidget(pSpeedBox); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? subMenu = new ClickableMenu(tr("Repeat")); mpMenu->addMenu(subMenu); //subMenu->setEnabled(false); mpRepeatEnableAction = subMenu->addAction(tr("Enable")); mpRepeatEnableAction->setCheckable(true); connect(mpRepeatEnableAction, SIGNAL(toggled(bool)), SLOT(toggleRepeat(bool))); // TODO: move to a func or class mpRepeatBox = new QSpinBox(0); mpRepeatBox->setMinimum(-1); mpRepeatBox->setValue(-1); mpRepeatBox->setToolTip(QString::fromLatin1("-1: ") + tr("infinity")); connect(mpRepeatBox, SIGNAL(valueChanged(int)), SLOT(setRepeateMax(int))); QLabel *pRepeatLabel = new QLabel(tr("Times")); QHBoxLayout *hb = new QHBoxLayout; hb->addWidget(pRepeatLabel); hb->addWidget(mpRepeatBox); QVBoxLayout *vb = new QVBoxLayout; vb->addLayout(hb); pRepeatLabel = new QLabel(tr("From")); mpRepeatA = new QTimeEdit(); mpRepeatA->setDisplayFormat(QString::fromLatin1("HH:mm:ss")); mpRepeatA->setToolTip(tr("negative value means from the end")); connect(mpRepeatA, SIGNAL(timeChanged(QTime)), SLOT(repeatAChanged(QTime))); hb = new QHBoxLayout; hb->addWidget(pRepeatLabel); hb->addWidget(mpRepeatA); vb->addLayout(hb); pRepeatLabel = new QLabel(tr("To")); mpRepeatB = new QTimeEdit(); mpRepeatB->setDisplayFormat(QString::fromLatin1("HH:mm:ss")); mpRepeatB->setToolTip(tr("negative value means from the end")); connect(mpRepeatB, SIGNAL(timeChanged(QTime)), SLOT(repeatBChanged(QTime))); hb = new QHBoxLayout; hb->addWidget(pRepeatLabel); hb->addWidget(mpRepeatB); vb->addLayout(hb); QWidget *wgt = new QWidget; wgt->setLayout(vb); pWA = new QWidgetAction(0); pWA->setDefaultWidget(wgt); pWA->defaultWidget()->setEnabled(false); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? mpRepeatAction = pWA; mpMenu->addSeparator(); subMenu = new ClickableMenu(tr("Clock")); mpMenu->addMenu(subMenu); QActionGroup *ag = new QActionGroup(subMenu); ag->setExclusive(true); connect(subMenu, SIGNAL(triggered(QAction*)), SLOT(changeClockType(QAction*))); subMenu->addAction(tr("Auto"))->setData(-1); subMenu->addAction(tr("Audio"))->setData(AVClock::AudioClock); subMenu->addAction(tr("Video"))->setData(AVClock::VideoClock); foreach(QAction* action, subMenu->actions()) { action->setActionGroup(ag); action->setCheckable(true); } QAction *autoClockAction = subMenu->actions().at(0); autoClockAction->setChecked(true); autoClockAction->setToolTip(tr("Take effect in next playback")); subMenu = new ClickableMenu(tr("Subtitle")); mpMenu->addMenu(subMenu); QAction *act = subMenu->addAction(tr("Enable")); act->setCheckable(true); act->setChecked(mpSubtitle->isEnabled()); connect(act, SIGNAL(toggled(bool)), SLOT(toggoleSubtitleEnabled(bool))); act = subMenu->addAction(tr("Auto load")); act->setCheckable(true); act->setChecked(mpSubtitle->autoLoad()); connect(act, SIGNAL(toggled(bool)), SLOT(toggleSubtitleAutoLoad(bool))); subMenu->addAction(tr("Open"), this, SLOT(openSubtitle())); wgt = new QWidget(); hb = new QHBoxLayout(); wgt->setLayout(hb); hb->addWidget(new QLabel(tr("Engine"))); QComboBox *box = new QComboBox(); hb->addWidget(box); pWA = new QWidgetAction(0); pWA->setDefaultWidget(wgt); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? box->addItem(QString::fromLatin1("FFmpeg"), QString::fromLatin1("FFmpeg")); box->addItem(QString::fromLatin1("LibASS"), QString::fromLatin1("LibASS")); connect(box, SIGNAL(activated(QString)), SLOT(setSubtitleEngine(QString))); mpSubtitle->setEngines(QStringList() << box->itemData(box->currentIndex()).toString()); box->setToolTip(tr("FFmpeg supports more subtitles but only render plain text") + QString::fromLatin1("\n") + tr("LibASS supports ass styles")); wgt = new QWidget(); hb = new QHBoxLayout(); wgt->setLayout(hb); hb->addWidget(new QLabel(tr("Charset"))); box = new QComboBox(); hb->addWidget(box); pWA = new QWidgetAction(0); pWA->setDefaultWidget(wgt); subMenu->addAction(pWA); //must add action after the widget action is ready. is it a Qt bug? box->addItem(tr("Auto detect"), QString::fromLatin1("AutoDetect")); box->addItem(tr("System"), QString::fromLatin1("System")); foreach (const QByteArray& cs, QTextCodec::availableCodecs()) { box->addItem(QString::fromLatin1(cs), QString::fromLatin1(cs)); } connect(box, SIGNAL(activated(QString)), SLOT(setSubtitleCharset(QString))); mpSubtitle->setCodec(box->itemData(box->currentIndex()).toByteArray()); box->setToolTip(tr("Auto detect requires libchardet")); subMenu = new ClickableMenu(tr("Audio track")); mpMenu->addMenu(subMenu); mpAudioTrackMenu = subMenu; connect(subMenu, SIGNAL(triggered(QAction*)), SLOT(changeAudioTrack(QAction*))); initAudioTrackMenu(); subMenu = new ClickableMenu(tr("Channel")); mpMenu->addMenu(subMenu); mpChannelMenu = subMenu; connect(subMenu, SIGNAL(triggered(QAction*)), SLOT(changeChannel(QAction*))); subMenu->addAction(tr("As input"))->setData(AudioFormat::ChannelLayout_Unsupported); //will set to input in resampler if not supported. subMenu->addAction(tr("Stereo"))->setData(AudioFormat::ChannelLayout_Stereo); subMenu->addAction(tr("Mono (center)"))->setData(AudioFormat::ChannelLayout_Center); subMenu->addAction(tr("Left"))->setData(AudioFormat::ChannelLayout_Left); subMenu->addAction(tr("Right"))->setData(AudioFormat::ChannelLayout_Right); ag = new QActionGroup(subMenu); ag->setExclusive(true); foreach(QAction* action, subMenu->actions()) { ag->addAction(action); action->setCheckable(true); } subMenu = new QMenu(tr("Aspect ratio"), mpMenu); mpMenu->addMenu(subMenu); connect(subMenu, SIGNAL(triggered(QAction*)), SLOT(switchAspectRatio(QAction*))); mpARAction = subMenu->addAction(tr("Video")); mpARAction->setData(0); subMenu->addAction(tr("Window"))->setData(-1); subMenu->addAction(QString::fromLatin1("4:3"))->setData(4.0/3.0); subMenu->addAction(QString::fromLatin1("16:9"))->setData(16.0/9.0); subMenu->addAction(tr("Custom"))->setData(-2); foreach(QAction* action, subMenu->actions()) { action->setCheckable(true); } mpARAction->setChecked(true); subMenu = new ClickableMenu(tr("Color space")); mpMenu->addMenu(subMenu); mpVideoEQ = new VideoEQConfigPage(); connect(mpVideoEQ, SIGNAL(engineChanged()), SLOT(onVideoEQEngineChanged())); pWA = new QWidgetAction(0); pWA->setDefaultWidget(mpVideoEQ); subMenu->addAction(pWA); subMenu = new ClickableMenu(tr("Decoder")); mpMenu->addMenu(subMenu); mpDecoderConfigPage = new DecoderConfigPage(); pWA = new QWidgetAction(0); pWA->setDefaultWidget(mpDecoderConfigPage); subMenu->addAction(pWA); subMenu = new ClickableMenu(tr("Renderer")); mpMenu->addMenu(subMenu); connect(subMenu, SIGNAL(triggered(QAction*)), SLOT(changeVO(QAction*))); //TODO: AVOutput.name,detail(description). check whether it is available VideoRendererId *vo = NULL; while ((vo = VideoRenderer::next(vo))) { // skip non-widget renderers if (*vo == VideoRendererId_OpenGLWindow || *vo == VideoRendererId_GraphicsItem) continue; QAction *voa = subMenu->addAction(QString::fromLatin1(VideoRenderer::name(*vo))); voa->setData(*vo); voa->setCheckable(true); if (!mpVOAction) mpVOAction = voa; } mpVOAction->setChecked(true); mVOActions = subMenu->actions(); mainLayout->addLayout(mpPlayerLayout); mainLayout->addWidget(mpTimeSlider); mainLayout->addWidget(mpControl); QHBoxLayout *controlLayout = new QHBoxLayout(); controlLayout->setSpacing(0); controlLayout->setContentsMargins(QMargins(1, 1, 1, 1)); mpControl->setLayout(controlLayout); controlLayout->addWidget(mpCurrent); controlLayout->addWidget(mpTitle); QSpacerItem *space = new QSpacerItem(mpPlayPauseBtn->width(), mpPlayPauseBtn->height(), QSizePolicy::MinimumExpanding); controlLayout->addSpacerItem(space); controlLayout->addWidget(mpVolumeSlider); controlLayout->addWidget(mpVolumeBtn); controlLayout->addWidget(mpCaptureBtn); controlLayout->addWidget(mpPlayPauseBtn); controlLayout->addWidget(mpStopBtn); controlLayout->addWidget(mpBackwardBtn); controlLayout->addWidget(mpForwardBtn); controlLayout->addWidget(mpOpenBtn); controlLayout->addWidget(mpInfoBtn); controlLayout->addWidget(mpSpeed); //controlLayout->addWidget(mpSetupBtn); controlLayout->addWidget(mpMenuBtn); controlLayout->addWidget(mpEnd); connect(pSpeedBox, SIGNAL(valueChanged(double)), SLOT(onSpinBoxChanged(double))); connect(mpOpenBtn, SIGNAL(clicked()), SLOT(openFile())); connect(mpPlayPauseBtn, SIGNAL(clicked()), SLOT(togglePlayPause())); connect(mpInfoBtn, SIGNAL(clicked()), SLOT(showInfo())); //valueChanged can be triggered by non-mouse event connect(mpTimeSlider, SIGNAL(sliderMoved(int)), SLOT(seek(int))); connect(mpTimeSlider, SIGNAL(sliderPressed()), SLOT(seek())); connect(mpTimeSlider, SIGNAL(onLeave()), SLOT(onTimeSliderLeave())); connect(mpTimeSlider, SIGNAL(onHover(int,int)), SLOT(onTimeSliderHover(int,int))); connect(&Config::instance(), SIGNAL(userShaderEnabledChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragHeaderChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragSampleChanged()), SLOT(onUserShaderChanged())); connect(&Config::instance(), SIGNAL(fragPostProcessChanged()), SLOT(onUserShaderChanged())); QTimer::singleShot(0, this, SLOT(initPlayer())); } void MainWindow::changeChannel(QAction *action) { if (action == mpChannelAction) { action->toggle(); return; } AudioFormat::ChannelLayout cl = (AudioFormat::ChannelLayout)action->data().toInt(); AudioOutput *ao = mpPlayer ? mpPlayer->audio() : 0; //getAO()? if (!ao) { qWarning("No audio output!"); return; } mpChannelAction->setChecked(false); mpChannelAction = action; mpChannelAction->setChecked(true); if (!ao->close()) { qWarning("close audio failed"); return; } AudioFormat af(ao->audioFormat()); af.setChannelLayout(cl); ao->setAudioFormat(af); if (!ao->open()) { qWarning("open audio failed"); return; } } void MainWindow::changeAudioTrack(QAction *action) { int track = action->data().toInt(); if (mpAudioTrackAction == action && track >= 0) { // external action is always clickable action->toggle(); return; } if (track < 0) { QString f = QFileDialog::getOpenFileName(0, tr("Open an external audio track")); if (f.isEmpty()) { action->toggle(); return; } mpPlayer->setExternalAudio(f); } else { mpPlayer->setExternalAudio(QString()); if (!mpPlayer->setAudioStream(track)) { action->toggle(); return; } } if (mpAudioTrackAction) mpAudioTrackAction->setChecked(false); mpAudioTrackAction = action; mpAudioTrackAction->setChecked(true); if (mpStatisticsView && mpStatisticsView->isVisible()) mpStatisticsView->setStatistics(mpPlayer->statistics()); } void MainWindow::changeVO(QAction *action) { if (action == mpVOAction) { action->toggle(); //check state changes if clicked return; } VideoRendererId vid = (VideoRendererId)action->data().toInt(); VideoRenderer *vo = VideoRenderer::create(vid); if (vo && vo->isAvailable()) { if (!setRenderer(vo)) action->toggle(); } else { action->toggle(); //check state changes if clicked QMessageBox::critical(0, QString::fromLatin1("QtAV"), tr("not availabe on your platform!")); return; } } void MainWindow::processPendingActions() { if (mHasPendingPlay) { mHasPendingPlay = false; play(mFile); } } void MainWindow::setAudioBackends(const QStringList& backends) { mAudioBackends = backends; } bool MainWindow::setRenderer(QtAV::VideoRenderer *renderer) { if (!renderer) return false; if (!renderer->widget()) { QMessageBox::warning(0, QString::fromLatin1("QtAV"), tr("Can not use this renderer")); return false; } mpOSD->uninstall(); mpSubtitle->uninstall(); renderer->widget()->setMouseTracking(true); //mouseMoveEvent without press. mpPlayer->setRenderer(renderer); QWidget *r = 0; if (mpRenderer) r = mpRenderer->widget(); //release old renderer and add new if (r) { mpPlayerLayout->removeWidget(r); if (r->testAttribute(Qt::WA_DeleteOnClose)) { r->close(); } else { r->close(); delete r; } r = 0; } mpRenderer = renderer; //setInSize? mpPlayerLayout->addWidget(renderer->widget()); if (mpVOAction) { mpVOAction->setChecked(false); } foreach (QAction *action, mVOActions) { if (action->data() == renderer->id()) { mpVOAction = action; break; } } mpVOAction->setChecked(true); mpTitle->setText(mpVOAction->text()); const VideoRendererId vid = mpPlayer->renderer()->id(); if (vid == VideoRendererId_GLWidget || vid == VideoRendererId_GLWidget2 || vid == VideoRendererId_OpenGLWidget ) { mpVideoEQ->setEngines(QVector() << VideoEQConfigPage::SWScale << VideoEQConfigPage::GLSL); mpVideoEQ->setEngine(VideoEQConfigPage::GLSL); mpPlayer->renderer()->forcePreferredPixelFormat(true); } else if (vid == VideoRendererId_XV) { mpVideoEQ->setEngines(QVector() << VideoEQConfigPage::XV); mpVideoEQ->setEngine(VideoEQConfigPage::XV); mpPlayer->renderer()->forcePreferredPixelFormat(true); } else { mpVideoEQ->setEngines(QVector() << VideoEQConfigPage::SWScale); mpVideoEQ->setEngine(VideoEQConfigPage::SWScale); mpPlayer->renderer()->forcePreferredPixelFormat(false); } onVideoEQEngineChanged(); mpOSD->installTo(mpRenderer); mpSubtitle->installTo(mpRenderer); onUserShaderChanged(); #define GL_ASS 0 #if GL_ASS GLSLFilter* glsl = new GLSLFilter(this); glsl->setOutputSize(QSize(4096, 2160)); //mpRenderer->installFilter(glsl); if (mpRenderer->opengl()) { connect(mpRenderer->opengl(), &OpenGLVideo::beforeRendering, [this](){ OpenGLVideo* glv = mpRenderer->opengl(); glv->setSubImages(mpSubtitle->subImages(glv->frameTime(), glv->frameWidth(), glv->frameHeight())); }); } #endif return true; } void MainWindow::play(const QString &name) { mFile = name; if (!mIsReady) { mHasPendingPlay = true; return; } mTitle = mFile; if (!mFile.contains(QLatin1String("://")) || mFile.startsWith(QLatin1String("file://"))) { mTitle = QFileInfo(mFile).fileName(); } setWindowTitle(mTitle); mpPlayer->stop(); //if no stop, in setPriority decoder will reopen mpPlayer->setFrameRate(Config::instance().forceFrameRate()); if (!mAudioBackends.isEmpty()) mpPlayer->audio()->setBackends(mAudioBackends); if (!mpRepeatEnableAction->isChecked()) mRepeateMax = 0; mpPlayer->setInterruptOnTimeout(Config::instance().abortOnTimeout()); mpPlayer->setInterruptTimeout(Config::instance().timeout()*1000.0); mpPlayer->setBufferMode(QtAV::BufferPackets); mpPlayer->setBufferValue(Config::instance().bufferValue()); mpPlayer->setRepeat(mRepeateMax); mpPlayer->setPriority(idsFromNames(Config::instance().decoderPriorityNames())); mpPlayer->setOptionsForAudioCodec(mpDecoderConfigPage->audioDecoderOptions()); mpPlayer->setOptionsForVideoCodec(mpDecoderConfigPage->videoDecoderOptions()); if (Config::instance().avformatOptionsEnabled()) mpPlayer->setOptionsForFormat(Config::instance().avformatOptions()); qDebug() << Config::instance().avformatOptions(); PlayListItem item; item.setUrl(mFile); item.setTitle(mTitle); item.setLastTime(0); mpHistory->remove(mFile); mpHistory->insertItemAt(item, 0); mpPlayer->play(name); } void MainWindow::play(const QUrl &url) { play(QUrl::fromPercentEncoding(url.toEncoded())); } void MainWindow::setVideoDecoderNames(const QStringList &vd) { QStringList vdnames; foreach (const QString& v, vd) { vdnames << v.toLower(); } QStringList vidp; QStringList vids = idsToNames(VideoDecoder::registered()); foreach (const QString& v, vids) { if (vdnames.contains(v.toLower())) { vidp.append(v); } } Config::instance().setDecoderPriorityNames(vidp); } void MainWindow::openFile() { QString file = QFileDialog::getOpenFileName(0, tr("Open a media file"), Config::instance().lastFile()); if (file.isEmpty()) return; Config::instance().setLastFile(file); play(file); } void MainWindow::togglePlayPause() { if (mpPlayer->isPlaying()) { qDebug("isPaused = %d", mpPlayer->isPaused()); mpPlayer->pause(!mpPlayer->isPaused()); } else { if (mFile.isEmpty()) return; if (!mpPlayer->isPlaying()) play(mFile); else mpPlayer->play(); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/pause.svg"))); } } void MainWindow::showNextOSD() { if (!mpOSD) return; mpOSD->useNextShowType(); } void MainWindow::onSpinBoxChanged(double v) { if (!mpPlayer) return; mpPlayer->setSpeed(v); } void MainWindow::onPaused(bool p) { if (p) { qDebug("start pausing..."); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/play.svg"))); } else { qDebug("stop pausing..."); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/pause.svg"))); } } void MainWindow::onStartPlay() { mpRenderer->setRegionOfInterest(QRectF()); mFile = mpPlayer->file(); //open from EventFilter's menu mTitle = mFile; if (!mFile.contains(QLatin1String("://")) || mFile.startsWith(QLatin1String("file://"))) mTitle = QFileInfo(mFile).fileName(); setWindowTitle(mTitle); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/pause.svg"))); mpTimeSlider->setMinimum(mpPlayer->mediaStartPosition()); mpTimeSlider->setMaximum(mpPlayer->mediaStopPosition()); mpTimeSlider->setValue(0); mpTimeSlider->setEnabled(mpPlayer->isSeekable()); mpEnd->setText(QTime(0, 0, 0).addMSecs(mpPlayer->mediaStopPosition()).toString(QString::fromLatin1("HH:mm:ss"))); setVolume(); mShowControl = 0; QTimer::singleShot(3000, this, SLOT(tryHideControlBar())); ScreenSaver::instance().disable(); initAudioTrackMenu(); mpRepeatA->setMinimumTime(QTime(0, 0, 0).addMSecs(mpPlayer->mediaStartPosition())); mpRepeatA->setMaximumTime(QTime(0, 0, 0).addMSecs(mpPlayer->mediaStopPosition())); mpRepeatB->setMinimumTime(QTime(0, 0, 0).addMSecs(mpPlayer->mediaStartPosition())); mpRepeatB->setMaximumTime(QTime(0, 0, 0).addMSecs(mpPlayer->mediaStopPosition())); mpRepeatA->setTime(QTime(0, 0, 0).addMSecs(mpPlayer->startPosition())); mpRepeatB->setTime(QTime(0, 0, 0).addMSecs(mpPlayer->stopPosition())); mCursorTimer = startTimer(3000); PlayListItem item = mpHistory->itemAt(0); item.setUrl(mFile); item.setTitle(mTitle); item.setDuration(mpPlayer->duration()); mpHistory->setItemAt(item, 0); updateChannelMenu(); if (mpStatisticsView && mpStatisticsView->isVisible()) mpStatisticsView->setStatistics(mpPlayer->statistics()); } void MainWindow::onStopPlay() { mpPlayer->setPriority(idsFromNames(Config::instance().decoderPriorityNames())); if (mpPlayer->currentRepeat() >= 0 && mpPlayer->currentRepeat() < mpPlayer->repeat()) return; // use shortcut to replay in EventFilter, the options will not be set, so set here mpPlayer->setFrameRate(Config::instance().forceFrameRate()); mpPlayer->setOptionsForAudioCodec(mpDecoderConfigPage->audioDecoderOptions()); mpPlayer->setOptionsForVideoCodec(mpDecoderConfigPage->videoDecoderOptions()); if (Config::instance().avformatOptionsEnabled()) mpPlayer->setOptionsForFormat(Config::instance().avformatOptions()); mpPlayPauseBtn->setIcon(QIcon(QString::fromLatin1(":/theme/dark/play.svg"))); mpTimeSlider->setValue(0); qDebug(">>>>>>>>>>>>>>disable slider"); mpTimeSlider->setDisabled(true); mpTimeSlider->setMinimum(0); mpTimeSlider->setMaximum(0); mpCurrent->setText(QString::fromLatin1("00:00:00")); mpEnd->setText(QString::fromLatin1("00:00:00")); tryShowControlBar(); ScreenSaver::instance().enable(); toggleRepeat(false); //mRepeateMax = 0; killTimer(mCursorTimer); unsetCursor(); if (m_preview) m_preview->setFile(QString()); } void MainWindow::onSpeedChange(qreal speed) { mpSpeed->setText(QString::fromLatin1("%1").arg(speed, 4, 'f', 2, QLatin1Char('0'))); } void MainWindow::setFrameRate() { if (!mpPlayer) return; mpPlayer->setFrameRate(Config::instance().forceFrameRate()); } void MainWindow::seek(int value) { mpPlayer->setSeekType(AccurateSeek); mpPlayer->seek((qint64)value); if (!m_preview || !Config::instance().previewEnabled()) return; m_preview->setTimestamp(value); m_preview->preview(); m_preview->setWindowFlags(m_preview->windowFlags() |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint); m_preview->resize(Config::instance().previewWidth(), Config::instance().previewHeight()); m_preview->show(); } void MainWindow::seek() { seek(mpTimeSlider->value()); } void MainWindow::showHideVolumeBar() { if (mpVolumeSlider->isHidden()) { mpVolumeSlider->show(); } else { mpVolumeSlider->hide(); } } void MainWindow::setVolume() { AudioOutput *ao = mpPlayer ? mpPlayer->audio() : 0; qreal v = qreal(mpVolumeSlider->value())*kVolumeInterval; if (ao) { if (qAbs(int(ao->volume()/kVolumeInterval) - mpVolumeSlider->value()) >= int(0.1/kVolumeInterval)) { ao->setVolume(v); } } mpVolumeSlider->setToolTip(QString::number(v)); mpVolumeBtn->setToolTip(QString::number(v)); } void MainWindow::closeEvent(QCloseEvent *e) { Q_UNUSED(e); if (mpPlayer) mpPlayer->stop(); qApp->quit(); } void MainWindow::resizeEvent(QResizeEvent *e) { Q_UNUSED(e); QWidget::resizeEvent(e); /* if (mpTitle) QLabelSetElideText(mpTitle, QFileInfo(mFile).fileName(), e->size().width()); */ } void MainWindow::timerEvent(QTimerEvent *e) { if (e->timerId() == mCursorTimer) { if (mpControl->isVisible()) return; setCursor(Qt::BlankCursor); } } void MainWindow::onPositionChange(qint64 pos) { if (mpPlayer->isSeekable()) mpTimeSlider->setValue(pos); mpCurrent->setText(QTime(0, 0, 0).addMSecs(pos).toString(QString::fromLatin1("HH:mm:ss"))); //setWindowTitle(QString::number(mpPlayer->statistics().video_only.currentDisplayFPS(), 'f', 2).append(" ").append(mTitle)); } void MainWindow::repeatAChanged(const QTime& t) { if (!mpPlayer) return; mpPlayer->setStartPosition(QTime(0, 0, 0).msecsTo(t)); } void MainWindow::repeatBChanged(const QTime& t) { if (!mpPlayer) return; // when this slot is called? even if only range is set? if (t <= mpRepeatA->time()) return; mpPlayer->setStopPosition(QTime(0, 0, 0).msecsTo(t)); } void MainWindow::keyPressEvent(QKeyEvent *e) { mControlOn = e->key() == Qt::Key_Control; } void MainWindow::keyReleaseEvent(QKeyEvent *e) { Q_UNUSED(e); mControlOn = false; } void MainWindow::mousePressEvent(QMouseEvent *e) { if (!mControlOn) return; mGlobalMouse = e->globalPos(); } void MainWindow::mouseMoveEvent(QMouseEvent *e) { unsetCursor(); if (e->pos().y() > height() - mpTimeSlider->height() - mpControl->height()) { if (mShowControl == 0) { mShowControl = 1; tryShowControlBar(); } } else { if (mShowControl == 1) { mShowControl = 0; QTimer::singleShot(3000, this, SLOT(tryHideControlBar())); } } if (mControlOn && e->button() == Qt::LeftButton) { if (!mpRenderer || !mpRenderer->widget()) return; QRectF roi = mpRenderer->realROI(); QPointF delta = e->pos() - mGlobalMouse; roi.moveLeft(-delta.x()); roi.moveTop(-delta.y()); mpRenderer->setRegionOfInterest(roi); } } void MainWindow::wheelEvent(QWheelEvent *e) { if (!mpRenderer || !mpRenderer->widget()) { return; } QPoint dp; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) qreal deg = e->angleDelta().y()/8; dp = e->pixelDelta(); #else qreal deg = e->delta()/8; #endif //QT_VERSION #if WHEEL_SPEED if (!mControlOn) { qreal speed = mpPlayer->speed(); mpPlayer->setSpeed(qMax(0.01, speed + deg/15.0*0.02)); return; } #endif //WHEEL_SPEED QPointF p = mpRenderer->widget()->mapFrom(this, e->pos()); QPointF fp = mpRenderer->mapToFrame(p); //qDebug() << p << fp; if (fp.x() < 0) fp.setX(0); if (fp.y() < 0) fp.setY(0); if (fp.x() > mpRenderer->videoFrameSize().width()) fp.setX(mpRenderer->videoFrameSize().width()); if (fp.y() > mpRenderer->videoFrameSize().height()) fp.setY(mpRenderer->videoFrameSize().height()); QRectF viewport = QRectF(mpRenderer->mapToFrame(QPointF(0, 0)), mpRenderer->mapToFrame(QPointF(mpRenderer->rendererWidth(), mpRenderer->rendererHeight()))); //qDebug("vo: (%.1f, %.1f)=> frame: (%.1f, %.1f)", p.x(), p.y(), fp.x(), fp.y()); qreal zoom = 1.0 + deg*3.14/180.0; if (!dp.isNull()) { zoom = 1.0 + (qreal)dp.y()/100.0; } static qreal z = 1.0; z *= zoom; if (z < 1.0) z = 1.0; qreal x0 = fp.x() - fp.x()/z; qreal y0 = fp.y() - fp.y()/z; //qDebug() << "fr: " << QRectF(x0, y0, qreal(mpRenderer->videoFrameSize().width())/z, qreal(mpRenderer->videoFrameSize().height())/z) << fp << z; mpRenderer->setRegionOfInterest(QRectF(x0, y0, qreal(mpRenderer->videoFrameSize().width())/z, qreal(mpRenderer->videoFrameSize().height())/z)); return; QTransform m; m.translate(fp.x(), fp.y()); m.scale(1.0/zoom, 1.0/zoom); m.translate(-fp.x(), -fp.y()); QRectF r = m.mapRect(mpRenderer->realROI()); mpRenderer->setRegionOfInterest((r | m.mapRect(viewport))&QRectF(QPointF(0,0), mpRenderer->videoFrameSize())); } void MainWindow::about() { QtAV::about(); } void MainWindow::help() { QString name = QString::fromLatin1("help-%1.html").arg(QLocale::system().name()); QFile f(qApp->applicationDirPath() + QString::fromLatin1("/") + name); if (!f.exists()) { f.setFileName(QString::fromLatin1(":/") + name); } if (!f.exists()) { f.setFileName(qApp->applicationDirPath() + QString::fromLatin1("/help.html")); } if (!f.exists()) { f.setFileName(QString::fromLatin1(":/help.html")); } if (!f.open(QIODevice::ReadOnly)) { qWarning("Failed to open help-%s.html and help.html: %s", qPrintable(QLocale::system().name()), qPrintable(f.errorString())); return; } QTextStream ts(&f); ts.setCodec("UTF-8"); QString text = ts.readAll(); QMessageBox::information(0, tr("Help"), text); } void MainWindow::openUrl() { QString url = QInputDialog::getText(0, tr("Open an url"), tr("Url")); if (url.isEmpty()) return; play(url); } void MainWindow::updateChannelMenu() { if (mpChannelAction) mpChannelAction->setChecked(false); AudioOutput *ao = mpPlayer ? mpPlayer->audio() : 0; //getAO()? if (!ao) { return; } AudioFormat::ChannelLayout cl = ao->audioFormat().channelLayout(); QList as = mpChannelMenu->actions(); foreach (QAction *action, as) { if (action->data().toInt() != (int)cl) continue; action->setChecked(true); mpChannelAction = action; break; } } void MainWindow::initAudioTrackMenu() { int track = -2; QAction *a = 0; QList as; int tracks = 0; if (!mpPlayer) { a = mpAudioTrackMenu->addAction(tr("External")); a->setData(-1); a->setCheckable(true); a->setChecked(false); as.push_back(a); mpAudioTrackAction = 0; goto end; } track = mpPlayer->currentAudioStream(); as = mpAudioTrackMenu->actions(); tracks = mpPlayer->audioStreamCount(); if (mpAudioTrackAction && tracks == as.size()-1 && mpAudioTrackAction->data().toInt() == track) return; while (tracks + 1 < as.size()) { a = as.takeLast(); mpAudioTrackMenu->removeAction(a); delete a; } if (as.isEmpty()) { a = mpAudioTrackMenu->addAction(tr("External")); a->setData(-1); a->setCheckable(true); a->setChecked(false); as.push_back(a); mpAudioTrackAction = 0; } while (tracks + 1 > as.size()) { a = mpAudioTrackMenu->addAction(QString::number(as.size()-1)); a->setData(as.size()-1); a->setCheckable(true); a->setChecked(false); as.push_back(a); } end: foreach(QAction *ac, as) { if (ac->data().toInt() == track && track >= 0) { if (mpPlayer && mpPlayer->externalAudio().isEmpty()) { qDebug("track found!!!!!"); mpAudioTrackAction = ac; ac->setChecked(true); } } else { ac->setChecked(false); } } if (mpPlayer && !mpPlayer->externalAudio().isEmpty()) { mpAudioTrackAction = as.first(); } if (mpAudioTrackAction) mpAudioTrackAction->setChecked(true); } void MainWindow::switchAspectRatio(QAction *action) { qreal r = action->data().toDouble(); if (action == mpARAction && r != -2) { action->toggle(); //check state changes if clicked return; } if (r == 0) { mpPlayer->renderer()->setOutAspectRatioMode(VideoRenderer::VideoAspectRatio); } else if (r == -1) { mpPlayer->renderer()->setOutAspectRatioMode(VideoRenderer::RendererAspectRatio); } else { if (r == -2) r = QInputDialog::getDouble(0, tr("Aspect ratio"), QString(), 1.0); mpPlayer->renderer()->setOutAspectRatioMode(VideoRenderer::CustomAspectRation); mpPlayer->renderer()->setOutAspectRatio(r); } mpARAction->setChecked(false); mpARAction = action; mpARAction->setChecked(true); } void MainWindow::toggleRepeat(bool r) { mpRepeatEnableAction->setChecked(r); mpRepeatAction->defaultWidget()->setEnabled(r); //why need defaultWidget? if (r) { mRepeateMax = mpRepeatBox->value(); } else { mRepeateMax = 0; } if (mpPlayer) { mpPlayer->setRepeat(mRepeateMax); if (r) { repeatAChanged(mpRepeatA->time()); repeatBChanged(mpRepeatB->time()); } else { mpPlayer->setTimeRange(0); } } } void MainWindow::setRepeateMax(int m) { mRepeateMax = m; if (mpPlayer) { mpPlayer->setRepeat(m); } } void MainWindow::playOnlineVideo(QAction *action) { mTitle = action->text(); play(action->data().toString()); } void MainWindow::onTVMenuClick() { static TVView *tvv = new TVView; tvv->show(); connect(tvv, SIGNAL(clicked(QString,QString)), SLOT(onPlayListClick(QString,QString))); tvv->setMaximumHeight(qApp->desktop()->height()); tvv->setMinimumHeight(tvv->width()*2); } void MainWindow::onPlayListClick(const QString &key, const QString &value) { mTitle = key; play(value); } void MainWindow::tryHideControlBar() { if (mShowControl > 0) { return; } if (mpControl->isHidden() && mpTimeSlider->isHidden()) return; mpControl->hide(); mpTimeSlider->hide(); workaroundRendererSize(); } void MainWindow::tryShowControlBar() { unsetCursor(); if (mpTimeSlider && mpTimeSlider->isHidden()) mpTimeSlider->show(); if (mpControl && mpControl->isHidden()) mpControl->show(); } void MainWindow::showInfo() { if (!mpStatisticsView) mpStatisticsView = new StatisticsView(); if (mpPlayer) mpStatisticsView->setStatistics(mpPlayer->statistics()); mpStatisticsView->show(); } void MainWindow::onTimeSliderHover(int pos, int value) { QPoint gpos = mapToGlobal(mpTimeSlider->pos() + QPoint(pos, 0)); QToolTip::showText(gpos, QTime(0, 0, 0).addMSecs(value).toString(QString::fromLatin1("HH:mm:ss"))); if (!Config::instance().previewEnabled()) return; if (!m_preview) m_preview = new VideoPreviewWidget(); m_preview->setFile(mpPlayer->file()); m_preview->setTimestamp(value); m_preview->preview(); const int w = Config::instance().previewWidth(); const int h = Config::instance().previewHeight(); m_preview->setWindowFlags(m_preview->windowFlags() |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint); m_preview->resize(w, h); m_preview->move(gpos - QPoint(w/2, h)); m_preview->show(); } void MainWindow::onTimeSliderLeave() { if (m_preview && m_preview->isVisible()) m_preview->hide(); } void MainWindow::handleError(const AVError &e) { QMessageBox::warning(0, tr("Player error"), e.string()); } void MainWindow::onMediaStatusChanged() { QString status; AVPlayer *player = reinterpret_cast(sender()); if (!player) { //why it happens? reinterpret_cast works. qWarning() << "invalid sender() " << sender() << player; return; } switch (player->mediaStatus()) { case NoMedia: status = tr("No media"); break; case InvalidMedia: status = tr("Invalid meida"); break; case BufferingMedia: status = tr("Buffering..."); break; case BufferedMedia: status = tr("Buffered"); break; case LoadingMedia: status = tr("Loading..."); break; case LoadedMedia: status = tr("Loaded"); break; case StalledMedia: status = tr("Stalled"); break; default: status = QString(); onStopPlay(); break; } qDebug() << "status changed " << status; setWindowTitle(status + QString::fromLatin1(" ") + mTitle); } void MainWindow::onBufferProgress(qreal percent) { const qreal bs = mpPlayer->bufferSpeed(); QString s; if (bs > 1024*1024*1024) s = QString("%1G/s").arg(bs/1024.0/1024.0/1024.0, 6, 'f', 1); else if (bs > 1024*1024) s = QString("%1M/s").arg(bs/1024.0/1024.0, 6, 'f', 1); else if (bs > 1024) s = QString("%1K/s").arg(bs/1024.0, 6, 'f', 1); else s = QString("%1B/s").arg(bs, 6, 'f', 1); setWindowTitle(QString::fromLatin1("Buffering... %1% @%2 ").arg(percent*100.0, 5, 'f', 1).arg(s) + mTitle); } void MainWindow::onVideoEQEngineChanged() { VideoRenderer *vo = mpPlayer->renderer(); VideoEQConfigPage::Engine e = mpVideoEQ->engine(); if (e == VideoEQConfigPage::SWScale && vo->id() != VideoRendererId_X11 // X11 scales in the renderer ) { vo->forcePreferredPixelFormat(true); vo->setPreferredPixelFormat(VideoFormat::Format_RGB32); } else { vo->forcePreferredPixelFormat(false); } onBrightnessChanged(mpVideoEQ->brightness()*100.0); onContrastChanged(mpVideoEQ->contrast()*100.0); onHueChanged(mpVideoEQ->hue()*100.0); onSaturationChanged(mpVideoEQ->saturation()*100.0); } void MainWindow::onBrightnessChanged(int b) { VideoRenderer *vo = mpPlayer->renderer(); if (mpVideoEQ->engine() != VideoEQConfigPage::SWScale && vo->setBrightness(mpVideoEQ->brightness())) { mpPlayer->setBrightness(0); } else { vo->setBrightness(0); mpPlayer->setBrightness(b); } } void MainWindow::onContrastChanged(int c) { VideoRenderer *vo = mpPlayer->renderer(); if (mpVideoEQ->engine() != VideoEQConfigPage::SWScale && vo->setContrast(mpVideoEQ->contrast())) { mpPlayer->setContrast(0); } else { vo->setContrast(0); mpPlayer->setContrast(c); } } void MainWindow::onHueChanged(int h) { VideoRenderer *vo = mpPlayer->renderer(); if (mpVideoEQ->engine() != VideoEQConfigPage::SWScale && vo->setHue(mpVideoEQ->hue())) { mpPlayer->setHue(0); } else { vo->setHue(0); mpPlayer->setHue(h); } } void MainWindow::onSaturationChanged(int s) { VideoRenderer *vo = mpPlayer->renderer(); if (mpVideoEQ->engine() != VideoEQConfigPage::SWScale && vo->setSaturation(mpVideoEQ->saturation())) { mpPlayer->setSaturation(0); } else { vo->setSaturation(0); mpPlayer->setSaturation(s); } } void MainWindow::onCaptureConfigChanged() { mpPlayer->videoCapture()->setCaptureDir(Config::instance().captureDir()); mpPlayer->videoCapture()->setQuality(Config::instance().captureQuality()); if (Config::instance().captureFormat().toLower() == QLatin1String("original")) { mpPlayer->videoCapture()->setOriginalFormat(true); } else { mpPlayer->videoCapture()->setOriginalFormat(false); mpPlayer->videoCapture()->setSaveFormat(Config::instance().captureFormat()); } mpCaptureBtn->setToolTip(QString::fromLatin1("%1\n%2: %3\n%4: %5") .arg(tr("Capture video frame")) .arg(tr("Save to")) .arg(mpPlayer->videoCapture()->captureDir()) .arg(tr("Format")) .arg(Config::instance().captureFormat())); } void MainWindow::onAVFilterVideoConfigChanged() { if (mpVideoFilter) { mpVideoFilter->uninstall(); delete mpVideoFilter; mpVideoFilter = 0; } mpVideoFilter = new LibAVFilterVideo(this); mpVideoFilter->setEnabled(Config::instance().avfilterVideoEnable()); mpPlayer->installFilter(mpVideoFilter); mpVideoFilter->setOptions(Config::instance().avfilterVideoOptions()); } void MainWindow::onAVFilterAudioConfigChanged() { if (mpAudioFilter) { mpAudioFilter->uninstall(); delete mpAudioFilter; mpAudioFilter = 0; } mpAudioFilter = new LibAVFilterAudio(this); mpAudioFilter->setEnabled(Config::instance().avfilterAudioEnable()); mpAudioFilter->installTo(mpPlayer); mpAudioFilter->setOptions(Config::instance().avfilterAudioOptions()); } void MainWindow::donate() { //QDesktopServices::openUrl(QUrl("https://sourceforge.net/p/qtav/wiki/Donate%20%E6%8D%90%E8%B5%A0/")); QDesktopServices::openUrl(QUrl(QString::fromLatin1("http://www.qtav.org/donate.html"))); } void MainWindow::onBufferValueChanged() { if (!mpPlayer) return; mpPlayer->setBufferValue(Config::instance().bufferValue()); } void MainWindow::onAbortOnTimeoutChanged() { if (!mpPlayer) return; mpPlayer->setInterruptOnTimeout(Config::instance().abortOnTimeout()); } void MainWindow::onUserShaderChanged() { if (!mpRenderer || !mpRenderer->opengl()) return; #ifndef QT_NO_OPENGL if (Config::instance().userShaderEnabled()) { if (!m_shader) m_shader = new DynamicShaderObject(this); m_shader->setHeader(Config::instance().fragHeader()); m_shader->setSample(Config::instance().fragSample()); m_shader->setPostProcess(Config::instance().fragPostProcess()); mpRenderer->opengl()->setUserShader(m_shader); } else { mpRenderer->opengl()->setUserShader(NULL); } #endif } void MainWindow::setup() { ConfigDialog::display(); } void MainWindow::handleFullscreenChange() { workaroundRendererSize(); // workaround renderer display size for ubuntu tryShowControlBar(); QTimer::singleShot(3000, this, SLOT(tryHideControlBar())); } void MainWindow::toggoleSubtitleEnabled(bool value) { mpSubtitle->setEnabled(value); } void MainWindow::toggleSubtitleAutoLoad(bool value) { mpSubtitle->setAutoLoad(value); } void MainWindow::openSubtitle() { QString file = QFileDialog::getOpenFileName(0, tr("Open a subtitle file")); if (file.isEmpty()) return; mpSubtitle->setFile(file); } void MainWindow::setSubtitleCharset(const QString &charSet) { Q_UNUSED(charSet); QComboBox *box = qobject_cast(sender()); if (!box) return; mpSubtitle->setCodec(box->itemData(box->currentIndex()).toByteArray()); } void MainWindow::setSubtitleEngine(const QString &value) { Q_UNUSED(value) QComboBox *box = qobject_cast(sender()); if (!box) return; mpSubtitle->setEngines(QStringList() << box->itemData(box->currentIndex()).toString()); } void MainWindow::changeClockType(QAction *action) { action->setChecked(true); int value = action->data().toInt(); if (value < 0) { mpPlayer->masterClock()->setClockAuto(true); // TODO: guess clock type return; } mpPlayer->masterClock()->setClockAuto(false); mpPlayer->masterClock()->setClockType(AVClock::ClockType(value)); } void MainWindow::syncVolumeUi(qreal value) { const int v(value/kVolumeInterval); if (mpVolumeSlider->value() == v) return; mpVolumeSlider->setValue(v); } void MainWindow::workaroundRendererSize() { if (!mpRenderer) return; QSize s = rect().size(); //resize(QSize(s.width()-1, s.height()-1)); //resize(s); //window resize to fullscreen size will create another fullScreenChange event mpRenderer->widget()->resize(QSize(s.width()+1, s.height()+1)); mpRenderer->widget()->resize(s); } QtAV-1.12.0/examples/player/MainWindow.h000066400000000000000000000140761312235004300177460ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QWidgetAction; class QToolButton; QT_END_NAMESPACE namespace QtAV { class AudioOutput; class AVError; class AVPlayer; class AVClock; class VideoRenderer; class LibAVFilterAudio; class LibAVFilterVideo; class SubtitleFilter; class VideoPreviewWidget; class DynamicShaderObject; } QT_BEGIN_NAMESPACE class QMenu; class QTimeEdit; class QVBoxLayout; class QLabel; class QPushButton; class QSpinBox; class QTimeEdit; QT_END_NAMESPACE class Button; class Slider; class PlayList; class DecoderConfigPage; class VideoEQConfigPage; class StatisticsView; class OSDFilter; class AVFilterSubtitle; class Preview; class MainWindow : public QWidget { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); void setAudioBackends(const QStringList& backends); bool setRenderer(QtAV::VideoRenderer* renderer); void setVideoDecoderNames(const QStringList& vd); public slots: void play(const QString& name); void play(const QUrl& url); void openFile(); void togglePlayPause(); void showNextOSD(); signals: void ready(); private slots: void stopUnload(); void about(); void help(); void openUrl(); void initAudioTrackMenu(); void updateChannelMenu(); void switchAspectRatio(QAction* action); void toggleRepeat(bool); void setRepeateMax(int); void changeVO(QAction* action); void changeChannel(QAction* action); void changeAudioTrack(QAction* action); void onTVMenuClick(); void playOnlineVideo(QAction *action); void onPlayListClick(const QString& key, const QString& value); void processPendingActions(); void initPlayer(); void setupUi(); void onSpinBoxChanged(double v); void onStartPlay(); void onStopPlay(); void onPaused(bool p); void onSpeedChange(qreal speed); void setFrameRate(); void seek(); void seek(int); void showHideVolumeBar(); void setVolume(); void tryHideControlBar(); void tryShowControlBar(); void showInfo(); void onPositionChange(qint64 pos); void repeatAChanged(const QTime& t); void repeatBChanged(const QTime& t); void onTimeSliderHover(int pos, int value); void onTimeSliderLeave(); void handleError(const QtAV::AVError& e); void onMediaStatusChanged(); void onBufferProgress(qreal percent); void onVideoEQEngineChanged(); void onBrightnessChanged(int b); void onContrastChanged(int c); void onHueChanged(int h); void onSaturationChanged(int s); void onSeekFinished(qint64 pos); void onCaptureConfigChanged(); void onAVFilterVideoConfigChanged(); void onAVFilterAudioConfigChanged(); void onBufferValueChanged(); void onAbortOnTimeoutChanged(); void onUserShaderChanged(); void donate(); void setup(); void handleFullscreenChange(); void toggoleSubtitleEnabled(bool value); void toggleSubtitleAutoLoad(bool value); void openSubtitle(); void setSubtitleCharset(const QString& charSet); void setSubtitleEngine(const QString& value); void changeClockType(QAction* action); void syncVolumeUi(qreal value); protected: virtual void closeEvent(QCloseEvent *e); virtual void resizeEvent(QResizeEvent *); virtual void timerEvent(QTimerEvent *); virtual void keyPressEvent(QKeyEvent *e); virtual void keyReleaseEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); void wheelEvent(QWheelEvent *e); private: void workaroundRendererSize(); private: bool mIsReady, mHasPendingPlay; bool mControlOn; int mCursorTimer; int mShowControl; //0: can hide, 1: show and playing, 2: always show(not playing) int mRepeateMax; QStringList mAudioBackends; QVBoxLayout *mpPlayerLayout; QWidget *mpControl; QLabel *mpCurrent, *mpEnd; QLabel *mpTitle; QLabel *mpSpeed; Slider *mpTimeSlider, *mpVolumeSlider; QToolButton *mpVolumeBtn; QToolButton *mpPlayPauseBtn; QToolButton *mpStopBtn, *mpForwardBtn, *mpBackwardBtn; QToolButton *mpOpenBtn; QToolButton *mpInfoBtn, *mpMenuBtn, *mpSetupBtn, *mpCaptureBtn; QMenu *mpMenu; QAction *mpVOAction, *mpARAction; //remove mpVOAction if vo.id() is supported QAction *mpRepeatEnableAction; QWidgetAction *mpRepeatAction; QSpinBox *mpRepeatBox; QTimeEdit *mpRepeatA, *mpRepeatB; QAction *mpAudioTrackAction; QMenu *mpAudioTrackMenu; QMenu *mpChannelMenu; QAction *mpChannelAction; QList mVOActions; QtAV::AVClock *mpClock; QtAV::AVPlayer *mpPlayer; QtAV::VideoRenderer *mpRenderer; QtAV::LibAVFilterVideo *mpVideoFilter; QtAV::LibAVFilterAudio *mpAudioFilter; QString mFile; QString mTitle; QLabel *mpPreview; DecoderConfigPage *mpDecoderConfigPage; VideoEQConfigPage *mpVideoEQ; PlayList *mpPlayList, *mpHistory; QPointF mGlobalMouse; StatisticsView *mpStatisticsView; OSDFilter *mpOSD; QtAV::SubtitleFilter *mpSubtitle; QtAV::VideoPreviewWidget *m_preview; QtAV::DynamicShaderObject *m_shader; }; #endif // MAINWINDOW_H QtAV-1.12.0/examples/player/Slider.cpp000066400000000000000000000152171312235004300174450ustar00rootroot00000000000000/* smplayer, GUI front-end for mplayer. Copyright (C) 2006-2010 Ricardo Villalba This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Trolltech ASA (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #define CODE_FOR_CLICK 1 // 0 = old code, 1 = code copied from QSlider, 2 = button swap #include "Slider.h" #include #include #if CODE_FOR_CLICK <= 1 #include #if CODE_FOR_CLICK == 1 #include #endif #endif #if QT_VERSION < 0x040300 #define initStyleOption initStyleOption_Qt430 #endif //QT_VERSION Slider::Slider(QWidget *parent): QSlider(parent) { setOrientation(Qt::Horizontal); setMouseTracking(true); //mouseMoveEvent without press. } Slider::~Slider() { } #if CODE_FOR_CLICK == 1 // Function copied from qslider.cpp inline int Slider::pick(const QPoint &pt) const { return orientation() == Qt::Horizontal ? pt.x() : pt.y(); } // Function copied from qslider.cpp and modified to make it compile void Slider::initStyleOption_Qt430(QStyleOptionSlider *option) const { if (!option) return; option->initFrom(this); option->subControls = QStyle::SC_None; option->activeSubControls = QStyle::SC_None; option->orientation = orientation(); option->maximum = maximum(); option->minimum = minimum(); option->tickPosition = (QSlider::TickPosition) tickPosition(); option->tickInterval = tickInterval(); option->upsideDown = (orientation() == Qt::Horizontal) ? (invertedAppearance() != (option->direction == Qt::RightToLeft)) : (!invertedAppearance()); option->direction = Qt::LeftToRight; // we use the upsideDown option instead option->sliderPosition = sliderPosition(); option->sliderValue = value(); option->singleStep = singleStep(); option->pageStep = pageStep(); if (orientation() == Qt::Horizontal) option->state |= QStyle::State_Horizontal; } // Function copied from qslider.cpp and modified to make it compile int Slider::pixelPosToRangeValue(int pos) const { QStyleOptionSlider opt; initStyleOption(&opt); QRect gr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this); QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); int sliderMin, sliderMax, sliderLength; if (orientation() == Qt::Horizontal) { sliderLength = sr.width(); sliderMin = gr.x(); sliderMax = gr.right() - sliderLength + 1; } else { sliderLength = sr.height(); sliderMin = gr.y(); sliderMax = gr.bottom() - sliderLength + 1; } return QStyle::sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin, opt.upsideDown); } void Slider::enterEvent(QEvent *event) { emit onEnter(); QSlider::enterEvent(event); } void Slider::leaveEvent(QEvent *e) { emit onLeave(); QSlider::leaveEvent(e); } void Slider::mouseMoveEvent(QMouseEvent *e) { const int o = style()->pixelMetric(QStyle::PM_SliderLength ) - 1; int v = QStyle::sliderValueFromPosition(minimum(), maximum(), e->pos().x()-o/2, width()-o, false); emit onHover(e->x(), v); QSlider::mouseMoveEvent(e); } // Based on code from qslider.cpp void Slider::mousePressEvent(QMouseEvent *e) { qDebug("pressed (%d, %d)", e->pos().x(), e->pos().y()); if (e->button() == Qt::LeftButton) { QStyleOptionSlider opt; initStyleOption(&opt); const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); const QPoint center = sliderRect.center() - sliderRect.topLeft(); // to take half of the slider off for the setSliderPosition call we use the center - topLeft if (!sliderRect.contains(e->pos())) { qDebug("accept"); e->accept(); int v = pixelPosToRangeValue(pick(e->pos() - center)); setSliderPosition(v); triggerAction(SliderMove); setRepeatAction(SliderNoAction); emit sliderMoved(v);//TODO: ok? emit sliderPressed(); //TODO: ok? } else { QSlider::mousePressEvent(e); } } else { QSlider::mousePressEvent(e); } } #endif // CODE_FOR_CLICK == 1 #if CODE_FOR_CLICK == 2 void Slider::mousePressEvent(QMouseEvent *e) { // Swaps middle button click with left click if (e->button() == Qt::LeftButton) { QMouseEvent ev2(QEvent::MouseButtonRelease, e->pos(), e->globalPos(), Qt::MidButton, Qt::MidButton, e->modifiers()); QSlider::mousePressEvent(&ev2); } else if (e->button() == Qt::MidButton) { QMouseEvent ev2(QEvent::MouseButtonRelease, e->pos(), e->globalPos(), Qt::LeftButton, Qt::LeftButton, e->modifiers()); QSlider::mousePressEvent(&ev2); } else { QSlider::mousePressEvent(e); } } #endif // CODE_FOR_CLICK == 2 #if CODE_FOR_CLICK == 0 void Slider::mousePressEvent(QMouseEvent *e) { // FIXME: // The code doesn't work well with right to left layout, // so it's disabled. if (qApp->isRightToLeft()) { QSlider::mousePressEvent(e); return; } int range = maximum()-minimum(); int pos = (e->x() * range) / width(); //qDebug( "width: %d x: %d", width(), e->x()); //qDebug( "range: %d pos: %d value: %d", range, pos, value()); // Calculate how many positions takes the slider handle int metric = qApp->style()->pixelMetric(QStyle::PM_SliderLength); double one_tick_pixels = (double)width() / range; int slider_handle_positions = (int)(metric / one_tick_pixels); /* qDebug("metric: %d", metric ); qDebug("one_tick_pixels :%f", one_tick_pixels); qDebug("width() :%d", width()); qDebug("slider_handle_positions: %d", slider_handle_positions); */ if (abs(pos - value()) > slider_handle_positions) { setValue(pos); emit sliderMoved(pos); } else { QSlider::mousePressEvent(e); } } #endif QtAV-1.12.0/examples/player/Slider.h000066400000000000000000000033751312235004300171140ustar00rootroot00000000000000/* smplayer, GUI front-end for mplayer. Copyright (C) 2006-2010 Ricardo Villalba This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Trolltech ASA (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ //TODO: hover support(like youtube and ExMplayer timeline preview) #ifndef SLIDER_H #define SLIDER_H #include class Slider : public QSlider { Q_OBJECT public: Slider(QWidget *parent = 0); ~Slider(); signals: void onEnter(); void onLeave(); void onHover(int pos, int value); protected: virtual void enterEvent(QEvent* event); virtual void leaveEvent(QEvent *e); virtual void mouseMoveEvent(QMouseEvent* event); virtual void mousePressEvent(QMouseEvent *event); //#if CODE_FOR_CLICK == 1 inline int pick(const QPoint &pt) const; int pixelPosToRangeValue(int pos) const; void initStyleOption_Qt430(QStyleOptionSlider *option) const; //#endif }; #endif //SLIDER_H QtAV-1.12.0/examples/player/StatisticsView.cpp000066400000000000000000000215641312235004300212120ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "StatisticsView.h" #include #include #include #include #include #include QStringList getBaseInfoKeys() { return QStringList() << QObject::tr("Url") << QObject::tr("Format") << QObject::tr("Bit rate") << QObject::tr("Start time") << QObject::tr("Duration") ; } QStringList getCommonInfoKeys() { return QStringList() << QObject::tr("Available") << QObject::tr("Codec") << QObject::tr("Decoder") << QObject::tr("Decoder detail") << QObject::tr("Total time") << QObject::tr("Start time") << QObject::tr("Bit rate") << QObject::tr("Frames") << QObject::tr("FPS") // avg_frame_rate. guessed by FFmpeg ; } QStringList getVideoInfoKeys() { return getCommonInfoKeys() << QObject::tr("FPS Now") //current display fps << QObject::tr("Pixel format") << QObject::tr("Size") //w x h << QObject::tr("Coded size") // w x h << QObject::tr("GOP size") ; } QStringList getAudioInfoKeys() { return getCommonInfoKeys() << QObject::tr("Sample format") << QObject::tr("Sample rate") << QObject::tr("Channels") << QObject::tr("Channel layout") << QObject::tr("Frame size") ; } QVariantList getBaseInfoValues(const Statistics& s) { return QVariantList() << s.url << s.format << QString::number(s.bit_rate/1000).append(QString::fromLatin1(" Kb/s")) << s.start_time.toString(QString::fromLatin1("HH:mm:ss")) << s.duration.toString(QString::fromLatin1("HH:mm:ss")) ; } QList getVideoInfoValues(const Statistics& s) { return QList() << s.video.available << QString::fromLatin1("%1 (%2)").arg(s.video.codec).arg(s.video.codec_long) << s.video.decoder << s.video.decoder_detail << s.video.total_time.toString(QString::fromLatin1("HH:mm:ss")) << s.video.start_time.toString(QString::fromLatin1("HH:mm:ss")) << QString::number(s.video.bit_rate/1000).append(QString::fromLatin1(" Kb/s")) << s.video.frames << s.video.frame_rate << s.video.frame_rate << s.video_only.pix_fmt << QString::fromLatin1("%1x%2").arg(s.video_only.width).arg(s.video_only.height) << QString::fromLatin1("%1x%2").arg(s.video_only.coded_width).arg(s.video_only.coded_height) << s.video_only.gop_size ; } QList getAudioInfoValues(const Statistics& s) { return QList() << s.audio.available << QString::fromLatin1("%1 (%2)").arg(s.audio.codec).arg(s.audio.codec_long) << s.audio.decoder << s.audio.decoder_detail << s.audio.total_time.toString(QString::fromLatin1("HH:mm:ss")) << s.audio.start_time.toString(QString::fromLatin1("HH:mm:ss")) << QString::number(s.audio.bit_rate/1000).append(QString::fromLatin1(" Kb/s")) << s.audio.frames << s.audio.frame_rate << s.audio_only.sample_fmt << QString::number(s.audio_only.sample_rate).append(QString::fromLatin1(" Hz")) << s.audio_only.channels << s.audio_only.channel_layout << s.audio_only.frame_size ; } StatisticsView::StatisticsView(QWidget *parent) : QDialog(parent) , mTimer(0) , mpFPS(0) , mpAudioBitRate(0) , mpVideoBitRate(0) { setWindowTitle(tr("Media info")); setModal(false); setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); mpView = new QTreeWidget(); mpView->setAnimated(true); mpView->setHeaderHidden(false); mpView->setColumnCount(2); mpView->headerItem()->setText(0, tr("Key")); mpView->headerItem()->setText(1, tr("Value")); initBaseItems(&mBaseItems); mpView->addTopLevelItems(mBaseItems); mpMetadata = new QTreeWidgetItem(); mpMetadata->setText(0, QObject::tr("Metadata")); mpView->addTopLevelItem(mpMetadata); QTreeWidgetItem *item = createNodeWithItems(mpView, QObject::tr("Video"), getVideoInfoKeys(), &mVideoItems); mpFPS = item->child(9); //mpVideoBitRate = mpVideoMetadata = new QTreeWidgetItem(item); mpVideoMetadata->setText(0, QObject::tr("Metadata")); mpView->addTopLevelItem(item); item = createNodeWithItems(mpView, QObject::tr("Audio"), getAudioInfoKeys(), &mAudioItems); //mpAudioBitRate = mpAudioMetadata = new QTreeWidgetItem(item); mpAudioMetadata->setText(0, QObject::tr("Metadata")); mpView->addTopLevelItem(item); mpView->resizeColumnToContents(0); //call this after content is done QPushButton *btn = new QPushButton(QObject::tr("Ok")); QHBoxLayout *btnLayout = new QHBoxLayout; btnLayout->addStretch(); btnLayout->addWidget(btn); QObject::connect(btn, SIGNAL(clicked()), SLOT(accept())); QVBoxLayout *vl = new QVBoxLayout(); vl->addWidget(mpView); vl->addLayout(btnLayout); setLayout(vl); } void StatisticsView::setStatistics(const Statistics& s) { mStatistics = s; QVariantList v = getBaseInfoValues(s); int i = 0; foreach(QTreeWidgetItem* item, mBaseItems) { if (item->data(1, Qt::DisplayRole) != v.at(i)) { item->setData(1, Qt::DisplayRole, v.at(i)); } ++i; } v = getVideoInfoValues(s); i = 0; foreach(QTreeWidgetItem* item, mVideoItems) { if (item->data(1, Qt::DisplayRole) != v.at(i)) { item->setData(1, Qt::DisplayRole, v.at(i)); } ++i; } v = getAudioInfoValues(s); i = 0; foreach(QTreeWidgetItem* item, mAudioItems) { if (item->data(1, Qt::DisplayRole) != v.at(i)) { item->setData(1, Qt::DisplayRole, v.at(i)); } ++i; } setMetadataItem(mpMetadata, s.metadata); setMetadataItem(mpVideoMetadata, s.video.metadata); setMetadataItem(mpAudioMetadata, s.audio.metadata); } void StatisticsView::hideEvent(QHideEvent *e) { QDialog::hideEvent(e); killTimer(mTimer); } void StatisticsView::showEvent(QShowEvent *e) { QDialog::showEvent(e); mTimer = startTimer(1000); } void StatisticsView::timerEvent(QTimerEvent *e) { if (e->timerId() != mTimer) return; if (mpFPS) { mpFPS->setData(1, Qt::DisplayRole, QString::number(mStatistics.video_only.currentDisplayFPS(), 'f', 2)); } } void StatisticsView::initBaseItems(QList *items) { QTreeWidgetItem *item = 0; foreach(const QString& key, getBaseInfoKeys()) { item = new QTreeWidgetItem(0); item->setData(0, Qt::DisplayRole, key); items->append(item); } } QTreeWidgetItem* StatisticsView::createNodeWithItems(QTreeWidget *view, const QString &name, const QStringList &itemNames, QList *items) { QTreeWidgetItem *nodeItem = new QTreeWidgetItem(view); nodeItem->setData(0, Qt::DisplayRole, name); QTreeWidgetItem *item = 0; foreach(const QString& key, itemNames) { item = new QTreeWidgetItem(nodeItem); item->setData(0, Qt::DisplayRole, key); nodeItem->addChild(item); if (items) items->append(item); } nodeItem->setExpanded(true); return nodeItem; } void StatisticsView::setMetadataItem(QTreeWidgetItem *parent, const QHash &metadata) { if (parent->childCount() > 0) { QList children(parent->takeChildren()); qDeleteAll(children); } QHash::const_iterator it = metadata.constBegin(); for (;it != metadata.constEnd(); ++it) { QTreeWidgetItem *item = new QTreeWidgetItem(parent); item->setText(0, it.key()); item->setText(1, it.value()); } } QtAV-1.12.0/examples/player/StatisticsView.h000066400000000000000000000042051312235004300206500ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef STATISTICSVIEW_H #define STATISTICSVIEW_H #include #include using namespace QtAV; QT_BEGIN_NAMESPACE class QTreeWidget; class QTreeWidgetItem; QT_END_NAMESPACE class StatisticsView : public QDialog { Q_OBJECT public: explicit StatisticsView(QWidget *parent = 0); void setStatistics(const Statistics &s); protected: virtual void hideEvent(QHideEvent* e); virtual void showEvent(QShowEvent* e); virtual void timerEvent(QTimerEvent *e); signals: public slots: private: void initBaseItems(QList* items); QTreeWidgetItem* createNodeWithItems(QTreeWidget* view, const QString& name, const QStringList& itemNames, QList* items = 0); void setMetadataItem(QTreeWidgetItem* parent, const QHash& metadata); QTreeWidget *mpView; QList mBaseItems; QList mVideoItems; //TODO: multiple streams QList mAudioItems; Statistics mStatistics; int mTimer; QTreeWidgetItem *mpFPS, *mpAudioBitRate, *mpVideoBitRate; QTreeWidgetItem *mpMetadata, *mpAudioMetadata, *mpVideoMetadata; }; #endif // STATISTICSVIEW_H QtAV-1.12.0/examples/player/TVView.cpp000066400000000000000000000065741312235004300174150ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "TVView.h" #include #include #include #include #include #include #include #include TVView::TVView(QWidget *parent) : QWidget(parent) { setWindowTitle(tr("Online TV channels")); //setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); mpView = new QTreeWidget(); mpView->setAnimated(true); mpView->setHeaderHidden(true); mpView->setColumnCount(1); connect(mpView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(onItemDoubleClick (QTreeWidgetItem*,int))); QVBoxLayout *vl = new QVBoxLayout(); vl->addWidget(mpView); setLayout(vl); QTimer::singleShot(0, this, SLOT(load())); } void TVView::load() { /* //codec problem QSettings tv(qApp->applicationDirPath() + "/tv.ini", QSettings::IniFormat); tv.setIniCodec("UTF-8"); foreach (QString key, tv.allKeys()) { subMenu->addAction(key)->setData(tv.value(key).toString()); } */ QFile tv_file(qApp->applicationDirPath() + QString::fromLatin1("/tv.ini")); if (!tv_file.exists()) tv_file.setFileName(QString::fromLatin1(":/tv.ini")); if (!tv_file.open(QIODevice::ReadOnly)) return; QTextStream ts(&tv_file); ts.setCodec("UTF-8"); QTreeWidgetItem *nodeItem = new QTreeWidgetItem(mpView); nodeItem->setData(0, Qt::DisplayRole, QString()); mpView->addTopLevelItem(nodeItem); nodeItem->setExpanded(true); QString line; while (!ts.atEnd()) { line = ts.readLine(); if (line.isEmpty() || line.startsWith(QLatin1String("#"))) continue; if (!line.contains(QLatin1String("="))) { nodeItem = new QTreeWidgetItem(mpView); nodeItem->setData(0, Qt::DisplayRole, line); mpView->addTopLevelItem(nodeItem); continue; } QString key = line.section(QLatin1Char('='), 0, 0); QString value = line.section(QLatin1Char('='), 1); QTreeWidgetItem *item = new QTreeWidgetItem(nodeItem); item->setData(0, Qt::DisplayRole, key); item->setData(1, Qt::EditRole, value); } mpView->resizeColumnToContents(0); //call this after content is done } void TVView::onItemDoubleClick(QTreeWidgetItem *item, int column) { Q_UNUSED(column); emit clicked(item->data(0, Qt::DisplayRole).toString(), item->data(1, Qt::EditRole).toString()); } QtAV-1.12.0/examples/player/TVView.h000066400000000000000000000030351312235004300170470ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef TVVIEW_H #define TVVIEW_H #include QT_BEGIN_NAMESPACE class QTreeWidget; class QTreeWidgetItem; QT_END_NAMESPACE class TVView : public QWidget { Q_OBJECT public: explicit TVView(QWidget *parent = 0); signals: void clicked(const QString& key, const QString& value); private slots: void load(); void onItemDoubleClick( QTreeWidgetItem * item, int column); private: QTreeWidgetItem* createNodeWithItems(QTreeWidget* view, const QString& name, const QStringList& itemNames, QList* items = 0); QTreeWidget *mpView; }; #endif // TVVIEW_H QtAV-1.12.0/examples/player/config/000077500000000000000000000000001312235004300167565ustar00rootroot00000000000000QtAV-1.12.0/examples/player/config/AVFilterConfigPage.cpp000066400000000000000000000074441312235004300230720ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "AVFilterConfigPage.h" #include #include #include #include #include #include #include "common/Config.h" AVFilterConfigPage::AVFilterConfigPage(QWidget *parent) : ConfigPageBase(parent) { setObjectName(QString::fromLatin1("avfilter")); QGridLayout *gl = new QGridLayout(); setLayout(gl); gl->setSizeConstraint(QLayout::SetFixedSize); int r = 0; m_ui[0].type = tr("Video"); m_ui[1].type = tr("Audio"); const int mw = 300; for (size_t i = 0; i < sizeof(m_ui)/sizeof(m_ui[0]); ++i) { m_ui[i].enable = new QCheckBox(tr("Enable") + QString::fromLatin1(" ") + m_ui[i].type); gl->addWidget(m_ui[i].enable, r++, 0); m_ui[i].name = new QComboBox(); m_ui[i].name->setToolTip(QString::fromLatin1("%1 %2 %3").arg(tr("Registered")).arg(m_ui[i].type).arg(tr("filters"))); gl->addWidget(m_ui[i].name, r++, 0); m_ui[i].description = new QLabel(); m_ui[i].description->setMaximumWidth(mw); gl->addWidget(m_ui[i].description, r++, 0); gl->addWidget(new QLabel(tr("Parameters")), r++, 0); m_ui[i].options = new QTextEdit(); m_ui[i].options->setMaximumWidth(mw); m_ui[i].options->setMaximumHeight(mw/6); gl->addWidget(m_ui[i].options, r++, 0); } m_ui[0].options->setToolTip(QString::fromLatin1("example: negate")); m_ui[1].options->setToolTip(QString::fromLatin1("example: volume=volume=2.0")); connect(m_ui[0].name, SIGNAL(currentIndexChanged(QString)), SLOT(videoFilterChanged(QString))); m_ui[0].name->addItems(QtAV::LibAVFilter::videoFilters()); connect(m_ui[1].name, SIGNAL(currentIndexChanged(QString)), SLOT(audioFilterChanged(QString))); m_ui[1].name->addItems(QtAV::LibAVFilter::audioFilters()); } QString AVFilterConfigPage::name() const { return QString::fromLatin1("AVFilter"); } void AVFilterConfigPage::applyFromUi() { Config::instance() .avfilterVideoOptions(m_ui[0].options->toPlainText()) .avfilterVideoEnable(m_ui[0].enable->isChecked()) .avfilterAudioOptions(m_ui[1].options->toPlainText()) .avfilterAudioEnable(m_ui[1].enable->isChecked()) ; } void AVFilterConfigPage::applyToUi() { m_ui[0].enable->setChecked(Config::instance().avfilterVideoEnable()); m_ui[0].options->setText(Config::instance().avfilterVideoOptions()); m_ui[1].enable->setChecked(Config::instance().avfilterAudioEnable()); m_ui[1].options->setText(Config::instance().avfilterAudioOptions()); } void AVFilterConfigPage::videoFilterChanged(const QString &name) { m_ui[0].description->setText(QtAV::LibAVFilter::filterDescription(name)); } void AVFilterConfigPage::audioFilterChanged(const QString &name) { m_ui[1].description->setText(QtAV::LibAVFilter::filterDescription(name)); } QtAV-1.12.0/examples/player/config/AVFilterConfigPage.h000066400000000000000000000032611312235004300225300ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef AVFILTERCONFIGPAGE_H #define AVFILTERCONFIGPAGE_H #include "ConfigPageBase.h" QT_BEGIN_NAMESPACE class QCheckBox; class QComboBox; class QLabel; class QTextEdit; QT_END_NAMESPACE class AVFilterConfigPage : public ConfigPageBase { Q_OBJECT public: AVFilterConfigPage(QWidget* parent = 0); virtual QString name() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private Q_SLOTS: void audioFilterChanged(const QString& name); void videoFilterChanged(const QString& name); private: struct { QString type; QCheckBox *enable; QComboBox *name; QLabel *description; QTextEdit *options; } m_ui[2]; //0: video, 1: audio }; #endif // AVFILTERCONFIGPAGE_H QtAV-1.12.0/examples/player/config/AVFormatConfigPage.cpp000066400000000000000000000063021312235004300230650ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "AVFormatConfigPage.h" #include #include #include #include #include #include #include "common/Config.h" AVFormatConfigPage::AVFormatConfigPage(QWidget *parent) : ConfigPageBase(parent) { setObjectName(QString::fromLatin1("avformat")); QGridLayout *gl = new QGridLayout(); setLayout(gl); gl->setSizeConstraint(QLayout::SetFixedSize); int r = 0; m_on = new QCheckBox(QString::fromLatin1("%1 avformat %2").arg(tr("Enable")).arg(tr("options"))); gl->addWidget(m_on, r++, 0); m_direct = new QCheckBox(tr("Reduce buffering")); gl->addWidget(m_direct, r++, 0); gl->addWidget(new QLabel(tr("Probe size")), r, 0, Qt::AlignRight); m_probeSize = new QSpinBox(); m_probeSize->setMaximum(std::numeric_limits::max()); m_probeSize->setMinimum(0); m_probeSize->setToolTip(tr("0: auto")); gl->addWidget(m_probeSize, r++, 1, Qt::AlignLeft); gl->addWidget(new QLabel(tr("Max analyze duration")), r, 0, Qt::AlignRight); m_analyzeDuration = new QSpinBox(); m_analyzeDuration->setMaximum(std::numeric_limits::max()); m_analyzeDuration->setToolTip(tr("0: auto. how many microseconds are analyzed to probe the input")); gl->addWidget(m_analyzeDuration, r++, 1, Qt::AlignLeft); gl->addWidget(new QLabel(tr("Extra")), r, 0, Qt::AlignRight); m_extra = new QLineEdit(); m_extra->setToolTip(QString::fromLatin1("key1=value1 key2=value2 ...")); gl->addWidget(m_extra, r++, 1, Qt::AlignLeft); } QString AVFormatConfigPage::name() const { return tr("AVFormat"); } void AVFormatConfigPage::applyFromUi() { Config::instance() .setAvformatOptionsEnabled(m_on->isChecked()) .probeSize(m_probeSize->value()) .analyzeDuration(m_analyzeDuration->value()) .reduceBuffering(m_direct->isChecked()) .avformatExtra(m_extra->text()); } void AVFormatConfigPage::applyToUi() { m_on->setChecked(Config::instance().avformatOptionsEnabled()); m_direct->setChecked(Config::instance().reduceBuffering()); m_probeSize->setValue(Config::instance().probeSize()); m_analyzeDuration->setValue(Config::instance().analyzeDuration()); m_extra->setText(Config::instance().avformatExtra()); } QtAV-1.12.0/examples/player/config/AVFormatConfigPage.h000066400000000000000000000030371312235004300225340ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef AVFORMATCONFIGPAGE_H #define AVFORMATCONFIGPAGE_H #include "ConfigPageBase.h" #include QT_BEGIN_NAMESPACE class QCheckBox; class QSpinBox; class QLineEdit; QT_END_NAMESPACE class AVFormatConfigPage : public ConfigPageBase { Q_OBJECT public: explicit AVFormatConfigPage(QWidget *parent = 0); virtual QString name() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private: QCheckBox* m_on; QCheckBox *m_direct; QSpinBox *m_probeSize; QSpinBox *m_analyzeDuration; QLineEdit *m_extra; }; #endif // AVFORMATCONFIGPAGE_H QtAV-1.12.0/examples/player/config/CaptureConfigPage.cpp000066400000000000000000000073371312235004300230220ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "CaptureConfigPage.h" #include "common/Config.h" #include #include #include #include #include #include #include #include "../Slider.h" CaptureConfigPage::CaptureConfigPage(QWidget *parent) : ConfigPageBase(parent) { QFormLayout *formLayout = new QFormLayout(); setLayout(formLayout); QHBoxLayout *hb = new QHBoxLayout(); mpDir = new QLineEdit(); hb->addWidget(mpDir); QToolButton *bt = new QToolButton(); bt->setText(QString::fromLatin1("...")); hb->addWidget(bt); connect(bt, SIGNAL(clicked()), SLOT(selectSaveDir())); bt = new QToolButton(); bt->setText(tr("Browse")); hb->addWidget(bt); connect(bt, SIGNAL(clicked()), SLOT(browseCaptureDir())); formLayout->addRow(tr("Save dir"), hb); mpDir->setEnabled(false); mpFormat = new QComboBox(); formLayout->addRow(tr("Save format"), mpFormat); QList formats; formats << "Original" << QImageWriter::supportedImageFormats(); foreach (const QByteArray& fmt, formats) { mpFormat->addItem(QString::fromLatin1(fmt)); } mpQuality = new Slider(); formLayout->addRow(tr("Quality"), mpQuality); mpQuality->setRange(0, 100); mpQuality->setOrientation(Qt::Horizontal); mpQuality->setSingleStep(1); mpQuality->setTickInterval(10); mpQuality->setTickPosition(QSlider::TicksBelow); connect(&Config::instance(), SIGNAL(captureDirChanged(QString)), mpDir, SLOT(setText(QString))); connect(&Config::instance(), SIGNAL(captureQualityChanged(int)), mpQuality, SLOT(setValue(int))); connect(mpDir, SIGNAL(textChanged(QString)), SLOT(changeDirByUi(QString))); connect(mpFormat, SIGNAL(currentIndexChanged(QString)), SLOT(changeFormatByUi(QString))); connect(mpQuality, SIGNAL(valueChanged(int)), SLOT(changeQualityByUi(int))); } QString CaptureConfigPage::name() const { return tr("Capture"); } void CaptureConfigPage::applyFromUi() { Config::instance().setCaptureDir(mpDir->text()) .setCaptureFormat(mpFormat->currentText()) .setCaptureQuality(mpQuality->value()); } void CaptureConfigPage::applyToUi() { mpDir->setText(Config::instance().captureDir()); int idx = mpFormat->findText(Config::instance().captureFormat()); if (idx >= 0) mpFormat->setCurrentIndex(idx); mpQuality->setValue(Config::instance().captureQuality()); } void CaptureConfigPage::selectSaveDir() { QString dir = QFileDialog::getExistingDirectory(0, tr("Save dir"), mpDir->text()); if (dir.isEmpty()) return; mpDir->setText(dir); } void CaptureConfigPage::browseCaptureDir() { qDebug("browse capture dir"); QDesktopServices::openUrl(QUrl(QString::fromLatin1("file:///") + mpDir->text())); } QtAV-1.12.0/examples/player/config/CaptureConfigPage.h000066400000000000000000000032371312235004300224620ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef CAPTURECONFIGPAGE_H #define CAPTURECONFIGPAGE_H #include "ConfigPageBase.h" #include #include #include /* * TODO: ConfigPageBase: auto save(true for menu ui, false for dialog ui) * virtual public slot: apply() */ class Slider; class CaptureConfigPage : public ConfigPageBase { Q_OBJECT public: explicit CaptureConfigPage(QWidget *parent = 0); virtual QString name() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private slots: // only emit signals. no value stores. void selectSaveDir(); void browseCaptureDir(); private: QLineEdit *mpDir; QComboBox *mpFormat; Slider *mpQuality; }; #endif // CAPTURECONFIGPAGE_H QtAV-1.12.0/examples/player/config/ConfigDialog.cpp000066400000000000000000000073251312235004300220160ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "ConfigDialog.h" #include #include #include #include "CaptureConfigPage.h" #include "DecoderConfigPage.h" #include "AVFormatConfigPage.h" #include "AVFilterConfigPage.h" #include "MiscPage.h" #include "ShaderPage.h" #include "common/Config.h" void ConfigDialog::display() { static ConfigDialog *dialog = new ConfigDialog(); dialog->show(); } ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent) { QVBoxLayout *vbl = new QVBoxLayout(); setLayout(vbl); vbl->setSizeConstraint(QLayout::SetFixedSize); mpContent = new QTabWidget(); mpContent->setTabPosition(QTabWidget::West); mpButtonBox = new QDialogButtonBox(Qt::Horizontal); mpButtonBox->addButton(tr("Reset"), QDialogButtonBox::ResetRole);// (QDialogButtonBox::Reset); mpButtonBox->addButton(tr("Ok"), QDialogButtonBox::AcceptRole); //QDialogButtonBox::Ok mpButtonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); mpButtonBox->addButton(tr("Apply"), QDialogButtonBox::ApplyRole); connect(mpButtonBox, SIGNAL(accepted()), SLOT(accept())); connect(mpButtonBox, SIGNAL(rejected()), SLOT(reject())); connect(mpButtonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(onButtonClicked(QAbstractButton*))); vbl->addWidget(mpContent); vbl->addWidget(mpButtonBox); mPages << new MiscPage() << new CaptureConfigPage() << new DecoderConfigPage() << new AVFormatConfigPage() << new AVFilterConfigPage() << new ShaderPage() ; foreach (ConfigPageBase* page, mPages) { page->applyToUi(); page->applyOnUiChange(false); mpContent->addTab(page, page->name()); } } void ConfigDialog::onButtonClicked(QAbstractButton *btn) { qDebug("QDialogButtonBox clicked role=%d", mpButtonBox->buttonRole(btn)); switch (mpButtonBox->buttonRole(btn)) { case QDialogButtonBox::ResetRole: qDebug("QDialogButtonBox ResetRole clicked"); onReset(); break; case QDialogButtonBox::AcceptRole: case QDialogButtonBox::ApplyRole: qDebug("QDialogButtonBox ApplyRole clicked"); onApply(); break; case QDialogButtonBox::DestructiveRole: case QDialogButtonBox::RejectRole: onCancel(); break; default: break; } } void ConfigDialog::onReset() { Config::instance().reset(); // TODO: check change foreach (ConfigPageBase* page, mPages) { page->reset(); } } void ConfigDialog::onApply() { // TODO: check change foreach (ConfigPageBase* page, mPages) { page->apply(); } Config::instance().save(); } void ConfigDialog::onCancel() { // TODO: check change foreach (ConfigPageBase* page, mPages) { page->cancel(); } } QtAV-1.12.0/examples/player/config/ConfigDialog.h000066400000000000000000000027701312235004300214620ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef CONFIGDIALOG_H #define CONFIGDIALOG_H #include #include #include #include class ConfigPageBase; class ConfigDialog : public QDialog { Q_OBJECT public: static void display(); signals: private slots: void onButtonClicked(QAbstractButton* btn); void onApply(); void onCancel(); void onReset(); private: explicit ConfigDialog(QWidget *parent = 0); QTabWidget *mpContent; QDialogButtonBox *mpButtonBox; QList mPages; }; #endif // CONFIGDIALOG_H QtAV-1.12.0/examples/player/config/ConfigPageBase.cpp000066400000000000000000000027441312235004300222660ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "ConfigPageBase.h" ConfigPageBase::ConfigPageBase(QWidget *parent) : QWidget(parent) , mApplyOnUiChange(true) { } void ConfigPageBase::applyOnUiChange(bool a) { mApplyOnUiChange = a; } bool ConfigPageBase::applyOnUiChange() const { return mApplyOnUiChange; } void ConfigPageBase::apply() { applyFromUi(); } void ConfigPageBase::cancel() { applyToUi(); } void ConfigPageBase::reset() { // NOTE: make sure Config::instance().reset() is called before it. It is called i ConfigDialog.reset() applyToUi(); } QtAV-1.12.0/examples/player/config/ConfigPageBase.h000066400000000000000000000036261312235004300217330ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef CONFIGPAGEBASE_H #define CONFIGPAGEBASE_H #include class ConfigPageBase : public QWidget { Q_OBJECT public: explicit ConfigPageBase(QWidget *parent = 0); virtual QString name() const = 0; void applyOnUiChange(bool a); // default is true. in dialog is false, must call ConfigDialog::apply() to apply bool applyOnUiChange() const; public slots: // deprecated. call applyFromUi() void apply(); // deprecated. call applyToUi(). void cancel(); //call applyToUi() after Config::instance().reset(); void reset(); /*! * \brief applyToUi * Apply configurations to UI. Call this in your page ctor when ui layout is ready. */ virtual void applyToUi() = 0; protected: /*! * \brief applyFromUi * Save configuration values from UI. Call Config::xxx(value) in your implementation */ virtual void applyFromUi() = 0; private: bool mApplyOnUiChange; }; #endif // CONFIGPAGEBASE_H QtAV-1.12.0/examples/player/config/DecoderConfigPage.cpp000066400000000000000000000312251312235004300227550ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ // scroll area code is from Xuno: https://github.com/Xuno/Xuno-QtAV/blob/master/examples/player/config/DecoderConfigPage.cpp #include "DecoderConfigPage.h" #include "common/Config.h" #include "PropertyEditor.h" #include #include #include #include #include #include #include #include #include #include #include using namespace QtAV; // shared static QVector sDecodersUi; static QVector sPriorityUi; QStringList idsToNames(QVector ids) { QStringList decs; foreach (int id, ids) { decs.append(QString::fromLatin1(VideoDecoder::name(id))); } return decs; } QVector idsFromNames(const QStringList& names) { QVector decs; foreach (QString name, names) { if (name.isEmpty()) continue; VideoDecoderId id = VideoDecoder::id(name.toLatin1().constData()); if (id == 0) continue; decs.append(id); } return decs; } using namespace QtAV; class DecoderConfigPage::DecoderItemWidget : public QFrame { Q_OBJECT public: DecoderItemWidget(QWidget* parent = 0) : QFrame(parent) { mpEditorWidget = 0; // why no frame? setFrameStyle(QFrame::Panel|QFrame::Raised); setLineWidth(2); mpEditor = new PropertyEditor(this); mSelected = false; QVBoxLayout *vb = new QVBoxLayout; setLayout(vb); QFrame *frame = new QFrame(); frame->setFrameShape(QFrame::HLine); vb->addWidget(frame); mpCheck = new QCheckBox(); QHBoxLayout *hb = new QHBoxLayout(); hb->addWidget(mpCheck); QToolButton *expandBtn = new QToolButton(); expandBtn->setText(QString::fromLatin1("+")); hb->addWidget(expandBtn); connect(expandBtn, SIGNAL(clicked()), SLOT(toggleEditorVisible())); mpDesc = new QLabel(); vb->addLayout(hb); vb->addWidget(mpDesc); connect(mpCheck, SIGNAL(pressed()), SLOT(checkPressed())); // no this->mousePressEvent connect(mpCheck, SIGNAL(toggled(bool)), this, SIGNAL(enableChanged())); } void buildUiFor(QObject *obj) { mpEditor->getProperties(obj); //mpEditor->set() QWidget *w = mpEditor->buildUi(obj); if (!w) return; mpEditorWidget = w; w->setEnabled(true); layout()->addWidget(w); w->setVisible(false); } QVariantHash getOptions() const { return mpEditor->exportAsHash(); } void select(bool s) { mSelected = s; update(); } void setChecked(bool c) { mpCheck->setChecked(c); } bool isChecked() const { return mpCheck->isChecked(); } void setName(const QString& name) { mpCheck->setText(name);} QString name() const { return mpCheck->text();} void setDescription(const QString& desc) { mpDesc->setText(desc); } QString description() const { return mpDesc->text();} signals: void enableChanged(); void selected(DecoderItemWidget*); private slots: void checkPressed() { select(true); emit selected(this); } void toggleEditorVisible() { if (!mpEditorWidget) return; mpEditorWidget->setVisible(!mpEditorWidget->isVisible()); QToolButton *b = qobject_cast(sender()); if (b) { b->setText(mpEditorWidget->isVisible()?QString::fromLatin1("-"):QString::fromLatin1("+")); } parentWidget()->adjustSize(); } protected: virtual void mousePressEvent(QMouseEvent *) { select(true); emit selected(this); } virtual void paintEvent(QPaintEvent *e) { if (mSelected) { QPainter p(this); p.fillRect(rect(), QColor(0, 100, 200, 100)); } QWidget::paintEvent(e); } private: bool mSelected; QCheckBox *mpCheck; QLabel *mpDesc; PropertyEditor *mpEditor; QWidget *mpEditorWidget; }; DecoderConfigPage::DecoderConfigPage(QWidget *parent) : ConfigPageBase(parent) { mpSelectedDec = 0; setWindowTitle(tr("Video decoder config page")); QVBoxLayout *vbs = new QVBoxLayout(this); QSpacerItem *horizontalSpacer = new QSpacerItem(320, 0, QSizePolicy::Minimum, QSizePolicy::Minimum); vbs->addItem(horizontalSpacer); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *scrollAreaWidgetContents = new QWidget(this); QVBoxLayout *vlsroll = new QVBoxLayout(scrollAreaWidgetContents); vlsroll->setSpacing(0); QVBoxLayout *vb = new QVBoxLayout; vb->setSpacing(0); vb->addWidget(new QLabel(QString::fromLatin1("%1 %2 (%3)").arg(tr("Decoder")).arg(tr("Priorities")).arg(tr("reopen is required")))); sPriorityUi = idsFromNames(Config::instance().decoderPriorityNames()); QStringList vds = Config::instance().decoderPriorityNames(); vds.removeDuplicates(); QVector vids = idsFromNames(vds); QVector vds_all = VideoDecoder::registered(); QVector all = vids; foreach (QtAV::VideoDecoderId vid, vds_all) { if (!vids.contains(vid)) all.push_back(vid); } mpDecLayout = new QVBoxLayout; foreach (QtAV::VideoDecoderId vid, all) { VideoDecoder *vd = VideoDecoder::create(vid); DecoderItemWidget *iw = new DecoderItemWidget(scrollAreaWidgetContents); iw->buildUiFor(vd); mDecItems.append(iw); iw->setName(vd->name()); iw->setDescription(vd->description()); iw->setChecked(vids.contains(vid)); connect(iw, SIGNAL(enableChanged()), SLOT(videoDecoderEnableChanged())); connect(iw, SIGNAL(selected(DecoderItemWidget*)), SLOT(onDecSelected(DecoderItemWidget*))); mpDecLayout->addWidget(iw); delete vd; }/* for (int i = 0; i < vds_all.size(); ++i) { VideoDecoder *vd = VideoDecoder::create(vds_all.at(i)); DecoderItemWidget *iw = new DecoderItemWidget(); iw->buildUiFor(vd); mDecItems.append(iw); iw->setName(vd->name()); iw->setDescription(vd->description()); iw->setChecked(vds.contains(vd->name())); connect(iw, SIGNAL(enableChanged()), SLOT(videoDecoderEnableChanged())); connect(iw, SIGNAL(selected(DecoderItemWidget*)), SLOT(onDecSelected(DecoderItemWidget*))); mpDecLayout->addWidget(iw); delete vd; }*/ vb->addLayout(mpDecLayout); vb->addSpacerItem(new QSpacerItem(width(), 10, QSizePolicy::Ignored, QSizePolicy::Expanding)); mpUp = new QToolButton(scrollAreaWidgetContents); mpUp->setText(tr("Up")); connect(mpUp, SIGNAL(clicked()), SLOT(priorityUp())); mpDown = new QToolButton(scrollAreaWidgetContents); mpDown->setText(tr("Down")); connect(mpDown, SIGNAL(clicked()), SLOT(priorityDown())); QHBoxLayout *hb = new QHBoxLayout; hb->addWidget(mpUp); hb->addWidget(mpDown); vb->addLayout(hb); vb->addSpacerItem(new QSpacerItem(width(), 10, QSizePolicy::Ignored, QSizePolicy::Expanding)); vlsroll->addLayout(vb); scrollArea->setWidget(scrollAreaWidgetContents); vbs->addWidget(scrollArea); connect(&Config::instance(), SIGNAL(decoderPriorityNamesChanged()), SLOT(onConfigChanged())); } QString DecoderConfigPage::name() const { return tr("Decoder"); } QVariantHash DecoderConfigPage::audioDecoderOptions() const { return QVariantHash(); } QVariantHash DecoderConfigPage::videoDecoderOptions() const { QVariantHash options; foreach (DecoderItemWidget* diw, mDecItems) { options[diw->name()] = diw->getOptions(); } return options; } void DecoderConfigPage::applyFromUi() { QStringList decs_all; QStringList decs; foreach (DecoderItemWidget *w, mDecItems) { decs_all.append(w->name()); if (w->isChecked()) decs.append(w->name()); } sPriorityUi = idsFromNames(decs); Config::instance().setDecoderPriorityNames(decs); } void DecoderConfigPage::applyToUi() { updateDecodersUi(); } void DecoderConfigPage::videoDecoderEnableChanged() { QStringList names; foreach (DecoderItemWidget *iw, mDecItems) { if (iw->isChecked()) names.append(iw->name()); } sPriorityUi = idsFromNames(names); if (applyOnUiChange()) { Config::instance().setDecoderPriorityNames(names); } else { // emit Config::instance().decoderPriorityChanged(sPriorityUi); } } void DecoderConfigPage::priorityUp() { if (!mpSelectedDec) return; int i = mDecItems.indexOf(mpSelectedDec); if (i == 0) return; DecoderItemWidget *iw = mDecItems.takeAt(i-1); mDecItems.insert(i, iw); mpDecLayout->removeWidget(iw); mpDecLayout->insertWidget(i, iw); QStringList decs_all; QStringList decs_p = Config::instance().decoderPriorityNames(); QStringList decs; foreach (DecoderItemWidget *w, mDecItems) { decs_all.append(w->name()); if (decs_p.contains(w->name())) decs.append(w->name()); } sDecodersUi = idsFromNames(decs_all); sPriorityUi = idsFromNames(decs); if (applyOnUiChange()) { Config::instance().setDecoderPriorityNames(decs); } else { //emit Config::instance().decoderPriorityChanged(idsFromNames(decs)); } } void DecoderConfigPage::priorityDown() { if (!mpSelectedDec) return; int i = mDecItems.indexOf(mpSelectedDec); if (i == mDecItems.size()-1) return; DecoderItemWidget *iw = mDecItems.takeAt(i+1); mDecItems.insert(i, iw); // why takeItemAt then insertItem does not work? mpDecLayout->removeWidget(iw); mpDecLayout->insertWidget(i, iw); QStringList decs_all; QStringList decs_p = Config::instance().decoderPriorityNames(); QStringList decs; foreach (DecoderItemWidget *w, mDecItems) { decs_all.append(w->name()); if (decs_p.contains(w->name())) decs.append(w->name()); } sDecodersUi = idsFromNames(decs_all); sPriorityUi = idsFromNames(decs); if (applyOnUiChange()) { Config::instance().setDecoderPriorityNames(decs); } else { //emit Config::instance().decoderPriorityChanged(idsFromNames(decs)); //emit Config::instance().registeredDecodersChanged(idsFromNames(decs)); } } void DecoderConfigPage::onDecSelected(DecoderItemWidget *iw) { if (mpSelectedDec == iw) return; if (mpSelectedDec) { mpSelectedDec->select(false); } mpSelectedDec = iw; } void DecoderConfigPage::updateDecodersUi() { QStringList names = idsToNames(sPriorityUi); QStringList all_names = idsToNames(sDecodersUi); //qDebug() << "updateDecodersUi " << this << " " << names << " all: " << all_names; int idx = 0; foreach (const QString& name, all_names) { DecoderItemWidget * iw = 0; for (int i = idx; i < mDecItems.size(); ++i) { if (mDecItems.at(i)->name() != name) continue; iw = mDecItems.at(i); break; } if (!iw) break; iw->setChecked(names.contains(iw->name())); int i = mDecItems.indexOf(iw); if (i != idx) { mDecItems.removeAll(iw); mDecItems.insert(idx, iw); } // why takeItemAt then insertItem does not work? if (mpDecLayout->indexOf(iw) != idx) { mpDecLayout->removeWidget(iw); mpDecLayout->insertWidget(idx, iw); } ++idx; } } void DecoderConfigPage::onConfigChanged() { sPriorityUi = idsFromNames(Config::instance().decoderPriorityNames()); sDecodersUi = VideoDecoder::registered(); updateDecodersUi(); } #include "DecoderConfigPage.moc" QtAV-1.12.0/examples/player/config/DecoderConfigPage.h000066400000000000000000000036631312235004300224270ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef DECODERCONFIGPAGE_H #define DECODERCONFIGPAGE_H #include #include #include "ConfigPageBase.h" QT_BEGIN_NAMESPACE class QListWidget; class QToolButton; class QSpinBox; class QVBoxLayout; QT_END_NAMESPACE class DecoderConfigPage : public ConfigPageBase { Q_OBJECT class DecoderItemWidget; public: explicit DecoderConfigPage(QWidget *parent = 0); virtual QString name() const; QVariantHash audioDecoderOptions() const; QVariantHash videoDecoderOptions() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private slots: void videoDecoderEnableChanged(); void priorityUp(); void priorityDown(); void onDecSelected(DecoderItemWidget* iw); void updateDecodersUi(); void onConfigChanged(); private: QSpinBox *mpThreads; QToolButton *mpUp, *mpDown; QList mDecItems; DecoderItemWidget *mpSelectedDec; QVBoxLayout *mpDecLayout; }; #endif // DECODERCONFIGPAGE_H QtAV-1.12.0/examples/player/config/MiscPage.cpp000066400000000000000000000140471312235004300211600ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "MiscPage.h" #include #include #include "common/Config.h" MiscPage::MiscPage() { QGridLayout *gl = new QGridLayout(); setLayout(gl); gl->setSizeConstraint(QLayout::SetFixedSize); int r = 0; m_preview_on = new QCheckBox(tr("Preview")); gl->addWidget(m_preview_on, r++, 0); m_preview_w = new QSpinBox(); m_preview_w->setRange(1, 1920); m_preview_h = new QSpinBox(); m_preview_h->setRange(1, 1080); gl->addWidget(new QLabel(QString::fromLatin1("%1 %2: ").arg(tr("Preview")).arg(tr("size"))), r, 0); QHBoxLayout *hb = new QHBoxLayout(); hb->addWidget(m_preview_w); hb->addWidget(new QLabel(QString::fromLatin1("x"))); hb->addWidget(m_preview_h); gl->addLayout(hb, r, 1); r++; gl->addWidget(new QLabel(tr("Force fps")), r, 0); m_fps = new QDoubleSpinBox(); m_fps->setMinimum(-m_fps->maximum()); m_fps->setToolTip(QString::fromLatin1("<= 0: ") + tr("Ignore")); gl->addWidget(m_fps, r++, 1); gl->addWidget(new QLabel(tr("Progress update interval") + QString::fromLatin1("(ms)")), r, 0); m_notify_interval = new QSpinBox(); m_notify_interval->setEnabled(false); gl->addWidget(m_notify_interval, r++, 1); gl->addWidget(new QLabel(tr("Buffer frames")), r, 0); m_buffer_value = new QSpinBox(); m_buffer_value->setRange(-1, 32767); m_buffer_value->setToolTip(QString::fromLatin1("-1: auto")); gl->addWidget(m_buffer_value, r++, 1); gl->addWidget(new QLabel(QString::fromLatin1("%1(%2)").arg(tr("Timeout")).arg(tr("s"))), r, 0); m_timeout = new QDoubleSpinBox(); m_timeout->setDecimals(3); m_timeout->setSingleStep(1.0); m_timeout->setMinimum(-0.5); m_timeout->setToolTip(QString::fromLatin1("<=0: never")); m_timeout_abort = new QCheckBox(tr("Abort")); hb = new QHBoxLayout(); hb->addWidget(m_timeout); hb->addWidget(m_timeout_abort); gl->addLayout(hb, r++, 1); gl->addWidget(new QLabel(tr("OpenGL type")), r, 0); m_opengl = new QComboBox(); m_opengl->addItem(QString::fromLatin1("Auto"), Config::Auto); m_opengl->addItem(QString::fromLatin1("Desktop"), Config::Desktop); m_opengl->addItem(QString::fromLatin1("OpenGLES"), Config::OpenGLES); m_opengl->addItem(QString::fromLatin1("Software"), Config::Software); m_opengl->setToolTip(tr("Windows only") + " Qt>=5.4 + dynamicgl" + QString::fromLatin1("\n") + tr("OpenGLES is Used by DXVA Zero Copy")); gl->addWidget(m_opengl, r, 1); m_angle_platform = new QComboBox(); m_angle_platform->setToolTip(tr("D3D9 has performance if ZeroCopy is disabled or for software decoders") + QString::fromLatin1("\n") + tr("RESTART REQUIRED")); m_angle_platform->addItems(QStringList() << QString::fromLatin1("D3D9") << QString::fromLatin1("D3D11") << QString::fromLatin1("AUTO") << QString::fromLatin1("WARP")); #ifndef QT_OPENGL_DYNAMIC m_opengl->setEnabled(false); m_angle_platform->setEnabled(false); #endif gl->addWidget(m_angle_platform, r++, 2); gl->addWidget(new QLabel("EGL"), r, 0); m_egl = new QCheckBox(); m_egl->setToolTip(tr("Currently only works for Qt>=5.5 XCB build")); #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) || !defined(Q_OS_LINUX) m_egl->setEnabled(false); #endif gl->addWidget(m_egl, r++, 1); gl->addWidget(new QLabel("Log"), r, 0); m_log = new QComboBox(); m_log->addItems(QStringList() << QString::fromLatin1("off") << QString::fromLatin1("warning") << QString::fromLatin1("debug") << QString::fromLatin1("all")); gl->addWidget(m_log, r++, 1); } QString MiscPage::name() const { return tr("Misc"); } void MiscPage::applyFromUi() { Config::instance().setPreviewEnabled(m_preview_on->isChecked()) .setPreviewWidth(m_preview_w->value()) .setPreviewHeight(m_preview_h->value()) .setEGL(m_egl->isChecked()) .setOpenGLType((Config::OpenGLType)m_opengl->itemData(m_opengl->currentIndex()).toInt()) .setANGLEPlatform(m_angle_platform->currentText().toLower()) .setForceFrameRate(m_fps->value()) .setBufferValue(m_buffer_value->value()) .setTimeout(m_timeout->value()) .setAbortOnTimeout(m_timeout_abort->isChecked()) .setLogLevel(m_log->currentText().toLower()) ; } void MiscPage::applyToUi() { m_preview_on->setChecked(Config::instance().previewEnabled()); m_preview_w->setValue(Config::instance().previewWidth()); m_preview_h->setValue(Config::instance().previewHeight()); m_opengl->setCurrentIndex(m_opengl->findData(Config::instance().openGLType())); m_angle_platform->setCurrentIndex(m_angle_platform->findText(Config::instance().getANGLEPlatform().toUpper())); m_egl->setChecked(Config::instance().isEGL()); m_fps->setValue(Config::instance().forceFrameRate()); //m_notify_interval->setValue(Config::instance().avfilterOptions()); m_buffer_value->setValue(Config::instance().bufferValue()); m_timeout->setValue(Config::instance().timeout()); m_timeout_abort->setChecked(Config::instance().abortOnTimeout()); m_log->setCurrentIndex(m_log->findText(Config::instance().logLevel().toLower())); } QtAV-1.12.0/examples/player/config/MiscPage.h000066400000000000000000000031601312235004300206170ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef MISCPAGE_H #define MISCPAGE_H #include "ConfigPageBase.h" #include #include #include class MiscPage : public ConfigPageBase { Q_OBJECT public: MiscPage(); virtual QString name() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private: QCheckBox *m_preview_on; QSpinBox *m_preview_w; QSpinBox *m_preview_h; QSpinBox *m_notify_interval; QDoubleSpinBox *m_fps; QSpinBox *m_buffer_value; QDoubleSpinBox *m_timeout; QCheckBox *m_timeout_abort; QComboBox *m_opengl; QComboBox *m_angle_platform; QCheckBox *m_egl; QComboBox *m_log; }; #endif // MISCPAGE_H QtAV-1.12.0/examples/player/config/PropertyEditor.cpp000066400000000000000000000267021312235004300224640ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "PropertyEditor.h" #include #include #include #include #include #include #include #include #include #include "../ClickableMenu.h" PropertyEditor::PropertyEditor(QObject *parent) : QObject(parent) { } void PropertyEditor::getProperties(QObject *obj) { mMetaProperties.clear(); mProperties.clear(); mPropertyDetails.clear(); if (!obj) return; const QMetaObject *mo = obj->metaObject(); for (int i = 0; i < mo->propertyCount(); ++i) { QMetaProperty mp = mo->property(i); mMetaProperties.append(mp); QVariant v(mp.read(obj)); if (mp.isEnumType()) { mProperties.insert(QString::fromLatin1(mp.name()), v.toInt());//mp.enumerator().valueToKey(v.toInt())); //always use string } else { mProperties.insert(QString::fromLatin1(mp.name()), v); } const QVariant detail = obj->property(QByteArray("detail_").append(mp.name()).constData()); if (!detail.isNull()) mPropertyDetails.insert(QString::fromLatin1(mp.name()), detail.toString()); } mProperties.remove(QString::fromLatin1("objectName")); } void PropertyEditor::set(const QVariantHash &hash) { QVariantHash::const_iterator it = mProperties.constBegin(); for (;it != mProperties.constEnd(); ++it) { QVariantHash::const_iterator ti = hash.find(it.key()); if (ti == hash.constEnd()) continue; mProperties[it.key()] = ti.value(); } } void PropertyEditor::set(const QString &) { } QString PropertyEditor::buildOptions() { QString result; foreach (QMetaProperty mp, mMetaProperties) { if (qstrcmp(mp.name(), "objectName") == 0) continue; result += QString::fromLatin1(" * %1: ").arg(QString::fromLatin1(mp.name())); if (mp.isEnumType()) { if (mp.isFlagType()) result += QString::fromLatin1("flag "); else result += QString::fromLatin1("enum "); QMetaEnum me(mp.enumerator()); for (int i = 0; i < me.keyCount(); ++i) { result += QString::fromLatin1(me.key(i)); result += QString::fromLatin1("="); result += QString::number(me.value(i)); if (i < me.keyCount() - 1) result += QString::fromLatin1(","); } } else if (mp.type() == QVariant::Int){ result += QString::fromLatin1("int"); } else if (mp.type() == QVariant::Double) { result += QString::fromLatin1("real"); } else if (mp.type() == QVariant::String) { result += QString::fromLatin1("text"); } else if (mp.type() == QVariant::Bool) { result += QString::fromLatin1("bool"); } const QVariant detail = mPropertyDetails.value(QString::fromLatin1(mp.name())); if (!detail.isNull()) result += QString::fromLatin1("\n > property detail: %1").arg(detail.toString()); result += QString::fromLatin1("\n"); } return result; } QWidget* PropertyEditor::buildUi(QObject *obj) { if (mMetaProperties.isEmpty()) return 0; QWidget *w = new QWidget(); QGridLayout *gl = new QGridLayout(); w->setLayout(gl); int row = 0; QVariant value; foreach (QMetaProperty mp, mMetaProperties) { if (qstrcmp(mp.name(), "objectName") == 0) continue; value = mProperties[QString::fromLatin1(mp.name())]; if (mp.isEnumType()) { if (mp.isFlagType()) { gl->addWidget(createWidgetForFlags(QString::fromLatin1(mp.name()), value, mp.enumerator(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 0, Qt::AlignLeft | Qt::AlignVCenter); } else { gl->addWidget(new QLabel(QObject::tr(mp.name())), row, 0, Qt::AlignRight | Qt::AlignVCenter); gl->addWidget(createWidgetForEnum(QString::fromLatin1(mp.name()), value, mp.enumerator(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 1, Qt::AlignLeft | Qt::AlignVCenter); } } else if (mp.type() == QVariant::Int || mp.type() == QVariant::UInt || mp.type() == QVariant::LongLong || mp.type() == QVariant::ULongLong){ gl->addWidget(new QLabel(QObject::tr(mp.name())), row, 0, Qt::AlignRight | Qt::AlignVCenter); gl->addWidget(createWidgetForInt(QString::fromLatin1(mp.name()), value.toInt(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 1, Qt::AlignLeft | Qt::AlignVCenter); } else if (mp.type() == QVariant::Double) { gl->addWidget(new QLabel(QObject::tr(mp.name())), row, 0, Qt::AlignRight | Qt::AlignVCenter); gl->addWidget(createWidgetForReal(QString::fromLatin1(mp.name()), value.toReal(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 1, Qt::AlignLeft | Qt::AlignVCenter); } else if (mp.type() == QVariant::Bool) { gl->addWidget(createWidgetForBool(QString::fromLatin1(mp.name()), value.toBool(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 0, 1, 2, Qt::AlignLeft); } else { gl->addWidget(new QLabel(QObject::tr(mp.name())), row, 0, Qt::AlignRight | Qt::AlignVCenter); gl->addWidget(createWidgetForText(QString::fromLatin1(mp.name()), value.toString(), !mp.isWritable(), obj ? obj->property(QByteArray("detail_").append(mp.name()).constData()).toString() : QString()), row, 1, Qt::AlignLeft | Qt::AlignVCenter); } ++row; } return w; } QVariantHash PropertyEditor::exportAsHash() { return mProperties; } QString PropertyEditor::exportAsConfig() { return QString(); } QWidget* PropertyEditor::createWidgetForFlags(const QString& name, const QVariant& value, QMetaEnum me, const QString &detail, QWidget* parent) { mProperties[name] = value; QToolButton *btn = new QToolButton(parent); if (!detail.isEmpty()) btn->setToolTip(detail); btn->setObjectName(name); btn->setText(QObject::tr(name.toUtf8().constData())); btn->setPopupMode(QToolButton::InstantPopup); ClickableMenu *menu = new ClickableMenu(btn); menu->setObjectName(name); btn->setMenu(menu); for (int i = 0; i < me.keyCount(); ++i) { QAction * a = menu->addAction(QString::fromLatin1(me.key(i))); a->setCheckable(true); a->setData(me.value(i)); a->setChecked(value.toInt() & me.value(i)); } connect(menu, SIGNAL(triggered(QAction*)), SLOT(onFlagChange(QAction*))); return btn; } QWidget* PropertyEditor::createWidgetForEnum(const QString& name, const QVariant& value, QMetaEnum me, const QString &detail, QWidget* parent) { mProperties[name] = value; QComboBox *box = new QComboBox(parent); if (!detail.isEmpty()) box->setToolTip(detail); box->setObjectName(name); box->setEditable(false); for (int i = 0; i < me.keyCount(); ++i) { box->addItem(QString::fromLatin1(me.key(i)), me.value(i)); } if (value.type() == QVariant::Int) { box->setCurrentIndex(box->findData(value)); } else { #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) box->setCurrentText(value.toString()); #else box->setCurrentIndex(box->findText(value.toString())); #endif } connect(box, SIGNAL(currentIndexChanged(int)), SLOT(onEnumChange(int))); return box; } QWidget* PropertyEditor::createWidgetForInt(const QString& name, int value, const QString& detail, QWidget* parent) { mProperties[name] = value; QSpinBox *box = new QSpinBox(parent); if (!detail.isEmpty()) box->setToolTip(detail); box->setObjectName(name); box->setValue(value); connect(box, SIGNAL(valueChanged(int)), SLOT(onIntChange(int))); return box; } QWidget* PropertyEditor::createWidgetForReal(const QString& name, qreal value, const QString &detail, QWidget* parent) { mProperties[name] = value; QDoubleSpinBox *box = new QDoubleSpinBox(parent); if (!detail.isEmpty()) box->setToolTip(detail); box->setObjectName(name); box->setValue(value); connect(box, SIGNAL(valueChanged(double)), SLOT(onRealChange(qreal))); return box; } QWidget* PropertyEditor::createWidgetForText(const QString& name, const QString& value, bool readOnly, const QString& detail, QWidget* parent) { mProperties[name] = value; QWidget *w = 0; if (readOnly) { QLabel *label = new QLabel(parent); w = label; label->setText(value); } else { QLineEdit *box = new QLineEdit(parent); w = box; box->setText(value); connect(box, SIGNAL(textChanged(QString)), SLOT(onTextChange(QString))); } if (!detail.isEmpty()) w->setToolTip(detail); w->setObjectName(name); return w; } QWidget* PropertyEditor::createWidgetForBool(const QString& name, bool value, const QString &detail, QWidget* parent) { mProperties[name] = value; QCheckBox *box = new QCheckBox(QObject::tr(name.toUtf8().constData()), parent); if (!detail.isEmpty()) box->setToolTip(detail); box->setObjectName(name); box->setChecked(value); connect(box, SIGNAL(clicked(bool)), SLOT(onBoolChange(bool))); return box; } void PropertyEditor::updatePropertyValue(const QString &name, const QVariant &value) { if (name.isEmpty()) return; if (!mProperties.contains(name)) return; qDebug() << name << " >>> " << value; mProperties[name] = value; } void PropertyEditor::onFlagChange(QAction *action) { int value = mProperties[sender()->objectName()].toInt(); int flag = action->data().toInt(); if (action->isChecked()) { value |= flag; } else { value &= ~flag; } updatePropertyValue(sender()->objectName(), value); } void PropertyEditor::onEnumChange(int value) { QComboBox *box = qobject_cast(sender()); updatePropertyValue(sender()->objectName(), box->itemData(value)); } void PropertyEditor::onIntChange(int value) { updatePropertyValue(sender()->objectName(), value); } void PropertyEditor::onRealChange(qreal value) { updatePropertyValue(sender()->objectName(), value); } void PropertyEditor::onTextChange(const QString& value) { updatePropertyValue(sender()->objectName(), value); } void PropertyEditor::onBoolChange(bool value) { updatePropertyValue(sender()->objectName(), value); } QtAV-1.12.0/examples/player/config/PropertyEditor.h000066400000000000000000000063431312235004300221300ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PROPERTYEDITOR_H #define PROPERTYEDITOR_H #include #include #include QT_BEGIN_NAMESPACE class QAction; class QWidget; QT_END_NAMESPACE class PropertyEditor : public QObject { Q_OBJECT public: explicit PropertyEditor(QObject *parent = 0); // call it before others void getProperties(QObject *obj); // from config file etc to init properties. call it before buildXXX void set(const QVariantHash& hash); void set(const QString& conf); /*! * \brief buildOptions * \return command line options */ QString buildOptions(); QWidget* buildUi(QObject* obj = 0); //obj: read dynamic properties("detail_property") QVariantHash exportAsHash(); QString exportAsConfig(); //json like private: /*! * name is property name. * 1. add property and value in hash * 2. add a widget and set value * 3. connect widget value change signal to a slot */ QWidget* createWidgetForFlags(const QString& name, const QVariant& value, QMetaEnum me, const QString& detail = QString(), QWidget* parent = 0); QWidget* createWidgetForEnum(const QString& name, const QVariant& value, QMetaEnum me, const QString& detail = QString(), QWidget* parent = 0); QWidget* createWidgetForInt(const QString& name, int value, const QString& detail = QString(), QWidget* parent = 0); QWidget* createWidgetForReal(const QString& name, qreal value, const QString& detail = QString(), QWidget* parent = 0); QWidget* createWidgetForText(const QString& name, const QString& value, bool readOnly, const QString& detail = QString(), QWidget* parent = 0); QWidget* createWidgetForBool(const QString& name, bool value, const QString& detail = QString(), QWidget* parent = 0); // called if value changed by ui (in onXXXChange) void updatePropertyValue(const QString& name, const QVariant& value); private slots: // updatePropertyValue void onFlagChange(QAction *action); void onEnumChange(int value); void onIntChange(int value); void onRealChange(qreal value); void onTextChange(const QString& value); void onBoolChange(bool value); private: QList mMetaProperties; QVariantHash mProperties; QVariantHash mPropertyDetails; }; #endif // PROPERTYEDITOR_H QtAV-1.12.0/examples/player/config/ShaderPage.cpp000066400000000000000000000055671312235004300215020ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "ShaderPage.h" #include #include #include "common/Config.h" ShaderPage::ShaderPage(QWidget *parent) : ConfigPageBase(parent) { QVBoxLayout *gl = new QVBoxLayout(); setLayout(gl); gl->setSizeConstraint(QLayout::SetMaximumSize); const int mw = 600; m_enable = new QCheckBox(tr("Enable")); gl->addWidget(m_enable); m_fbo = new QCheckBox(tr("Intermediate FBO")); gl->addWidget(m_fbo); gl->addWidget(new QLabel(tr("Fragment shader header"))); m_header = new QTextEdit(); //m_header->setMaximumWidth(mw); m_header->setMaximumHeight(mw/6); m_header->setToolTip(tr("Additional header code")); gl->addWidget(m_header); gl->addWidget(new QLabel(tr("Fragment shader texel sample function"))); m_sample = new QTextEdit(); //m_sample->setMaximumWidth(mw); m_sample->setMaximumHeight(mw/6); m_sample->setToolTip(QLatin1String("vec4 sample2d(sampler2D tex, vec2 pos, int p)")); gl->addWidget(m_sample); gl->addWidget(new QLabel(QLatin1String("Fragment shader RGB post process code"))); m_pp = new QTextEdit(); //m_pp->setMaximumWidth(mw); m_pp->setMaximumHeight(mw/6); gl->addWidget(m_pp); gl->addStretch(); } QString ShaderPage::name() const { return tr("Shader"); } void ShaderPage::applyToUi() { m_enable->setChecked(Config::instance().userShaderEnabled()); m_fbo->setChecked(Config::instance().intermediateFBO()); m_header->setText(Config::instance().fragHeader()); m_sample->setText(Config::instance().fragSample()); m_pp->setText(Config::instance().fragPostProcess()); } void ShaderPage::applyFromUi() { Config::instance() .setUserShaderEnabled(m_enable->isChecked()) .setIntermediateFBO(m_fbo->isChecked()) .setFragHeader(m_header->toPlainText()) .setFragSample(m_sample->toPlainText()) .setFragPostProcess(m_pp->toPlainText()) ; } QtAV-1.12.0/examples/player/config/ShaderPage.h000066400000000000000000000026341312235004300211370ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef SHADER_PAGE_H #define SHADER_PAGE_H #include "ConfigPageBase.h" #include #include class ShaderPage : public ConfigPageBase { public: ShaderPage(QWidget* parent = 0); virtual QString name() const; protected: virtual void applyToUi(); virtual void applyFromUi(); private: QCheckBox *m_enable; QCheckBox *m_fbo; QTextEdit *m_header; QTextEdit *m_sample; QTextEdit *m_pp; }; #endif //SHADER_PAGE_H QtAV-1.12.0/examples/player/config/VideoEQConfigPage.cpp000066400000000000000000000111071312235004300227010ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "VideoEQConfigPage.h" #include #include #include #include #include #include "../Slider.h" VideoEQConfigPage::VideoEQConfigPage(QWidget *parent) : QWidget(parent) { mEngine = SWScale; QGridLayout *gl = new QGridLayout(); setLayout(gl); QLabel *label = new QLabel(); label->setText(tr("Engine")); mpEngine = new QComboBox(); setEngines(QVector(1, SWScale)); connect(mpEngine, SIGNAL(currentIndexChanged(int)), SLOT(onEngineChangedByUI())); int r = 0, c = 0; gl->addWidget(label, r, c); gl->addWidget(mpEngine, r, c+1); r++; struct { QSlider **slider; QString text; } sliders[] = { { &mpBSlider, tr("Brightness") }, { &mpCSlider, tr("Constrast") }, { &mpHSlider, tr("Hue") }, { &mpSSlider, tr("Saturation") }, { 0, QString() } }; for (int i = 0; sliders[i].slider; ++i) { QLabel *label = new QLabel(sliders[i].text); *sliders[i].slider = new Slider(); QSlider *slider = *sliders[i].slider; slider->setOrientation(Qt::Horizontal); slider->setTickInterval(2); slider->setRange(-100, 100); slider->setValue(0); gl->addWidget(label, r, c); gl->addWidget(slider, r, c+1); r++; } mpGlobal = new QCheckBox(tr("Global")); mpGlobal->setEnabled(false); mpGlobal->setChecked(false); mpResetButton = new QPushButton(tr("Reset")); gl->addWidget(mpGlobal, r, c, Qt::AlignLeft); gl->addWidget(mpResetButton, r, c+1, Qt::AlignRight); r++; connect(mpBSlider, SIGNAL(valueChanged(int)), SIGNAL(brightnessChanged(int))); connect(mpCSlider, SIGNAL(valueChanged(int)), SIGNAL(contrastChanged(int))); connect(mpHSlider, SIGNAL(valueChanged(int)), SIGNAL(hueChanegd(int))); connect(mpSSlider, SIGNAL(valueChanged(int)), SIGNAL(saturationChanged(int))); connect(mpGlobal, SIGNAL(toggled(bool)), SLOT(onGlobalSet(bool))); connect(mpResetButton, SIGNAL(clicked()), SLOT(onReset())); } void VideoEQConfigPage::onGlobalSet(bool g) { Q_UNUSED(g); } void VideoEQConfigPage::setEngines(const QVector &engines) { mpEngine->clear(); QVector es(engines); qSort(es); mEngines = es; foreach (Engine e, es) { if (e == SWScale) { mpEngine->addItem(QString::fromLatin1("libswscale")); } else if (e == GLSL) { mpEngine->addItem(QString::fromLatin1("GLSL")); } else if (e == XV) { mpEngine->addItem(QString::fromLatin1("XV")); } } } void VideoEQConfigPage::setEngine(Engine engine) { if (engine == mEngine) return; mEngine = engine; if (!mEngines.isEmpty()) { mpEngine->setCurrentIndex(mEngines.indexOf(engine)); } emit engineChanged(); } VideoEQConfigPage::Engine VideoEQConfigPage::engine() const { return mEngine; } qreal VideoEQConfigPage::brightness() const { return (qreal)mpBSlider->value()/100.0; } qreal VideoEQConfigPage::contrast() const { return (qreal)mpCSlider->value()/100.0; } qreal VideoEQConfigPage::hue() const { return (qreal)mpHSlider->value()/100.0; } qreal VideoEQConfigPage::saturation() const { return (qreal)mpSSlider->value()/100.0; } void VideoEQConfigPage::onReset() { mpBSlider->setValue(0); mpCSlider->setValue(0); mpHSlider->setValue(0); mpSSlider->setValue(0); } void VideoEQConfigPage::onEngineChangedByUI() { if (mpEngine->currentIndex() >= mEngines.size() || mpEngine->currentIndex() < 0) return; mEngine = mEngines.at(mpEngine->currentIndex()); emit engineChanged(); } QtAV-1.12.0/examples/player/config/VideoEQConfigPage.h000066400000000000000000000040101312235004300223410ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef VIDEOEQCONFIGPAGE_H #define VIDEOEQCONFIGPAGE_H #include QT_BEGIN_NAMESPACE class QCheckBox; class QComboBox; class QPushButton; class QSlider; QT_END_NAMESPACE class VideoEQConfigPage : public QWidget { Q_OBJECT public: enum Engine { SWScale, GLSL, XV, }; explicit VideoEQConfigPage(QWidget *parent = 0); void setEngines(const QVector& engines); void setEngine(Engine engine); Engine engine() const; qreal brightness() const; qreal contrast() const; qreal hue() const; qreal saturation() const; signals: void engineChanged(); void brightnessChanged(int); void contrastChanged(int); void hueChanegd(int); void saturationChanged(int); private slots: void onGlobalSet(bool); void onReset(); void onEngineChangedByUI(); private: QCheckBox *mpGlobal; QComboBox *mpEngine; QSlider *mpBSlider, *mpCSlider, *mpSSlider; QSlider *mpHSlider; QPushButton *mpResetButton; Engine mEngine; QVector mEngines; }; #endif // VIDEOEQCONFIGPAGE_H QtAV-1.12.0/examples/player/filters/000077500000000000000000000000001312235004300171615ustar00rootroot00000000000000QtAV-1.12.0/examples/player/filters/AVFilterSubtitle.cpp000066400000000000000000000075641312235004300230710ustar00rootroot00000000000000#include "AVFilterSubtitle.h" #include #include #include #include AVFilterSubtitle::AVFilterSubtitle(QObject *parent) : LibAVFilterVideo(parent) , m_auto(true) , m_player(0) { //connect(this, SIGNAL(statusChanged()), SLOT(onStatusChanged())); } void AVFilterSubtitle::setPlayer(AVPlayer *player) { if (m_player == player) return; uninstall(); if (m_player) { disconnect(this); } m_player = player; if (!player) return; player->installFilter(this); if (m_auto) { // connect(player, SIGNAL(fileChanged(QString)), SLOT(findAndSetFile(QString))); connect(player, SIGNAL(started()), SLOT(onPlayerStart())); } } bool AVFilterSubtitle::setFile(const QString &filePath) { setOptions(QString()); if (m_file != filePath) { emit fileChanged(filePath); // DO NOT return here because option is null now } m_file = filePath; QString u = m_u8_files[filePath]; if (!u.isEmpty() && !QFile(u).exists()) u = QString(); if (u.isEmpty()) { QFile f(filePath); if (!f.open(QIODevice::ReadOnly)) { qWarning("open '%s' error: %s", filePath.toUtf8().constData(), f.errorString().toUtf8().constData()); return false; } QTextStream ts(&f); ts.setAutoDetectUnicode(true); QString s = ts.readAll(); QString tmp = setContent(s); if (!tmp.isEmpty()) { u = tmp; m_u8_files[filePath] = u; } else { // read the origin file qWarning("open cache file '%s' error, originanl subtitle file will be used", filePath.toUtf8().constData()); } } if (u.isEmpty()) u = filePath; // filter_name=argument. use ' to quote the argument, use \ to escaping chars within quoted text. on windows, path can be C:/a/b/c, ":" must be escaped u.replace(QLatin1String(":"), QLatin1String("\\:")); setOptions(QString::fromLatin1("subtitles='%1'").arg(u)); qDebug("subtitle loaded: %s", filePath.toUtf8().constData()); return true; } QString AVFilterSubtitle::file() const { return m_file; } QString AVFilterSubtitle::setContent(const QString &doc) { QString name = QFileInfo(m_file).fileName(); if (name.isEmpty()) name = QString::fromLatin1("QtAV_u8_sub_cache"); QFile w(QDir::temp().absoluteFilePath(name)); if (w.open(QIODevice::WriteOnly)) { w.write(doc.toUtf8()); w.close(); } else { return QString(); } return w.fileName(); } void AVFilterSubtitle::setAutoLoad(bool value) { if (m_auto == value) return; m_auto = value; emit autoLoadChanged(value); if (!m_player || !m_auto) return; connect(m_player, SIGNAL(started()), SLOT(onPlayerStart())); } bool AVFilterSubtitle::autoLoad() const { return m_auto; } void AVFilterSubtitle::findAndSetFile(const QString &path) { QFileInfo fi(path); QDir dir(fi.dir()); QString name = fi.completeBaseName(); // video suffix has only 1 dot QStringList list = dir.entryList(QStringList() << name + QString::fromLatin1("*.ass") << name + QString::fromLatin1("*.ssa"), QDir::Files); list.append(dir.entryList(QStringList() << QString::fromLatin1("*.srt"), QDir::Files)); foreach (const QString& f, list) { // why it happens? if (!f.startsWith(name)) continue; if (setFile(dir.absoluteFilePath(f))) break; } } void AVFilterSubtitle::onPlayerStart() { setOptions(QString()); if (!autoLoad()) return; findAndSetFile(m_player->file()); } void AVFilterSubtitle::onStatusChanged() { if (status() == ConfigureOk) { emit loaded(); } else if (status() == ConfigureFailed) { if (options().isEmpty()) return; emit loadError(); } } QtAV-1.12.0/examples/player/filters/AVFilterSubtitle.h000066400000000000000000000023071312235004300225240ustar00rootroot00000000000000#ifndef QTAV_AVFilterSubtitle_H #define QTAV_AVFilterSubtitle_H #include #include #include #include using namespace QtAV; class AVFilterSubtitle : public LibAVFilterVideo { Q_OBJECT Q_PROPERTY(bool autoLoad READ autoLoad WRITE setAutoLoad NOTIFY autoLoadChanged) Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged) public: explicit AVFilterSubtitle(QObject *parent = 0); void setPlayer(AVPlayer* player); QString setContent(const QString& doc); // return utf8 subtitle path bool setFile(const QString& filePath); QString file() const; bool autoLoad() const; signals: void loaded(); void loadError(); void fileChanged(const QString& path); void autoLoadChanged(bool value); public slots: // TODO: enable changed & autoload=> load void setAutoLoad(bool value); void findAndSetFile(const QString& path); void onPlayerStart(); private slots: void onStatusChanged(); private: bool m_auto; AVPlayer *m_player; QString m_file; // convert to utf8 to ensure ffmpeg can open it. QHash m_u8_files; }; #endif // QTAV_AVFilterSubtitle_H QtAV-1.12.0/examples/player/filters/OSD.cpp000066400000000000000000000062501312235004300203150ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "OSD.h" #include namespace QtAV { OSD::OSD(): mShowType(ShowNone) , mSecsTotal(-1) { } OSD::~OSD() { } void OSD::setShowType(ShowType type) { mShowType = type; } OSD::ShowType OSD::showType() const { return mShowType; } void OSD::useNextShowType() { if (mShowType == ShowNone) { mShowType = (ShowType)1; return; } if (mShowType + 1 == ShowNone) { mShowType = ShowNone; return; } mShowType = (ShowType)(mShowType << 1); } bool OSD::hasShowType(ShowType t) const { return (t&mShowType) == t; } QString OSD::text(Statistics *statistics) { QString text; Statistics::Common *av = &statistics->video; if (!av->available) av = &statistics->audio; if (hasShowType(ShowCurrentTime) || hasShowType(ShowCurrentAndTotalTime)) { text = av->current_time.toString(QString::fromLatin1("HH:mm:ss")); } //how to compute mSecsTotal only once? if (hasShowType(ShowCurrentAndTotalTime) || hasShowType(ShowPercent) /*mSecsTotal < 0*/) { if (statistics->duration.isNull()) return text; mSecsTotal = QTime(0, 0, 0).secsTo(statistics->duration); //why video.total_time may be wrong(mkv) } if (hasShowType(ShowCurrentAndTotalTime)) { if (mSecsTotal > 0) text += QString::fromLatin1("/").append(statistics->duration.toString(QString::fromLatin1("HH:mm:ss"))); else text += QString::fromLatin1("/--:--:--"); } if (hasShowType(ShowRemainTime)) { if (mSecsTotal > 0) text += QString::fromLatin1("-").append(QTime(0, 0, 0).addSecs(av->current_time.secsTo(statistics->duration)).toString(QString::fromLatin1("HH:mm:ss"))); else text += QString::fromLatin1("--:--:--"); } if (hasShowType(ShowPercent)) { if (mSecsTotal > 0) text += QString::number(qreal(QTime(0, 0, 0).secsTo(av->current_time)) /qreal(mSecsTotal)*100.0, 'f', 1).append(QString::fromLatin1("%")); else text += QString::fromLatin1("--.-%"); } return text; } } //namespace QtAV QtAV-1.12.0/examples/player/filters/OSD.h000066400000000000000000000032671312235004300177670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OSD_H #define QTAV_OSD_H #include #include #include namespace QtAV { class Statistics; class OSD { public: enum ShowType { ShowCurrentTime = 1, ShowCurrentAndTotalTime = 1<<1, ShowRemainTime = 1<<2, ShowPercent = 1<<3, ShowNone }; OSD(); virtual ~OSD(); void setShowType(ShowType type); ShowType showType() const; void useNextShowType(); bool hasShowType(ShowType t) const; QString text(Statistics* statistics); protected: ShowType mShowType; int mSecsTotal; }; }//namespace QtAV #endif // QTAV_OSD_H QtAV-1.12.0/examples/player/filters/OSDFilter.cpp000066400000000000000000000027631312235004300214700ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "OSDFilter.h" #include #include OSDFilter::OSDFilter(QObject *parent) : VideoFilter(parent) , OSD() {} void OSDFilter::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(frame); if (mShowType == ShowNone) return; //qDebug("ctx=%p tid=%p main tid=%p", ctx, QThread::currentThread(), qApp->thread()); context()->drawPlainText(context()->rect, Qt::AlignCenter, text(statistics)); } QtAV-1.12.0/examples/player/filters/OSDFilter.h000066400000000000000000000030301312235004300211210ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OSDFILTER_H #define QTAV_OSDFILTER_H #include #include #include "OSD.h" using namespace QtAV; class OSDFilter : public VideoFilter, public OSD { public: OSDFilter(QObject *parent = 0); bool isSupported(VideoFilterContext::Type ct) const { return ct == VideoFilterContext::QtPainter || ct == VideoFilterContext::X11; } protected: void process(Statistics* statistics, VideoFrame* frame); }; #endif // QTAV_OSDFILTER_H QtAV-1.12.0/examples/player/ios/000077500000000000000000000000001312235004300163035ustar00rootroot00000000000000QtAV-1.12.0/examples/player/ios/Info.plist000066400000000000000000000136161312235004300202620ustar00rootroot00000000000000 CFBundleIconFile @ICON@ CFBundlePackageType APPL CFBundleGetInfoString Created by Wang Bin CFBundleSignature ???? CFBundleExecutable Player CFBundleIdentifier org.qtav.player CFBundleDisplayName QtAV Player CFBundleName QtAV Player CFBundleShortVersionString 1.12 CFBundleVersion 1.12.0 LSRequiresIPhoneOS UIFileSharingEnabled UILaunchStoryboardName LaunchScreen UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight NOTE This file was generated by Qt/QMake. CFBundleDocumentTypes CFBundleTypeExtensions AAC AC3 AIFF M4A MKA MP3 OGG PCM VAW WAV WAW WMA aac ac3 aiff m4a mka mp3 ogg pcm vaw wav waw wma CFBundleTypeIconFile document.icns CFBundleTypeName Audio file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions * * 3GP 3IV 3gp 3iv ASF AVI CPK DAT DIVX DV FLAC FLI FLV H264 I263 M2TS M4V MKV MOV MP2 MP4 MPEG MPG MPG2 MPG4 NSV NUT NUV OGG OGM QT RM RMVB VCD VFW VOB WEBM WMV asf avi cpk dat divx dv flac fli flv h264 i263 m2ts m4v mkv mov mp2 mp4 mpeg mpg mpg2 mpg4 mts nsv nut nuv ogg ogm qt rm rmvb vcd vfw vob webm wmv f4v ts CFBundleTypeIconFile document.icns CFBundleTypeName Movie file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions AQT ASS JSS RT SMI SRT SSA SUB TXT UTF aqt ass jss rt smi srt ssa sub txt utf CFBundleTypeIconFile document.icns CFBundleTypeName Subtitles file CFBundleTypeRole Viewer LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLName Streaming Protocol CFBundleURLSchemes mms mmst http httpproxy rtp rtsp ftp udp smb QtAV-1.12.0/examples/player/main.cpp000066400000000000000000000143421312235004300171450ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include #include "config/PropertyEditor.h" #include "MainWindow.h" #include "../common/common.h" using namespace QtAV; static const struct { const char* name; VideoRendererId id; } vid_map[] = { { "opengl", VideoRendererId_OpenGLWidget }, { "gl", VideoRendererId_GLWidget2 }, { "d2d", VideoRendererId_Direct2D }, { "gdi", VideoRendererId_GDI }, { "xv", VideoRendererId_XV }, { "x11", VideoRendererId_X11 }, { "qt", VideoRendererId_Widget }, { 0, 0 } }; VideoRendererId rendererId_from_opt_name(const QString& name) { for (int i = 0; vid_map[i].name; ++i) { if (name == QLatin1String(vid_map[i].name)) return vid_map[i].id; } #ifndef QT_NO_OPENGL #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) return VideoRendererId_OpenGLWidget; // qglwidget is not suitable for android #endif return VideoRendererId_GLWidget2; #endif return VideoRendererId_Widget; } int main(int argc, char *argv[]) { qDebug() << aboutQtAV_PlainText(); Config::setName(QString::fromLatin1("Player")); QOptions options = get_common_options(); options.add(QString::fromLatin1("player options")) ("ffmpeg-log", QString(), QString::fromLatin1("ffmpeg log level. can be: quiet, panic, fatal, error, warn, info, verbose, debug. this can override env 'QTAV_FFMPEG_LOG'")) ("vd-list", QString::fromLatin1("List video decoders and their properties")) ("-vo", #ifndef QT_NO_OPENGL #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) QString::fromLatin1("opengl") #else QString::fromLatin1("gl") #endif #else QString::fromLatin1("qt") #endif , QString::fromLatin1("video renderer engine. can be gl, qt, d2d, gdi, xv, x11.")) ; options.parse(argc, argv); do_common_options_before_qapp(options); if (options.value(QString::fromLatin1("vd-list")).toBool()) { PropertyEditor pe; VideoDecoderId *vid = NULL; while ((vid = VideoDecoder::next(vid)) != NULL) { VideoDecoder *vd = VideoDecoder::create(*vid); pe.getProperties(vd); qDebug("- %s:", vd->name().toUtf8().constData()); qDebug() << pe.buildOptions().toUtf8().constData(); } exit(0); } QApplication a(argc, argv); a.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); qDebug() <applicationDirPath()); do_common_options(options); set_opengl_backend(options.option(QString::fromLatin1("gl")).value().toString(), a.arguments().first()); load_qm(QStringList() << QString::fromLatin1("player"), options.value(QString::fromLatin1("language")).toString()); QtAV::setFFmpegLogLevel(options.value(QString::fromLatin1("ffmpeg-log")).toByteArray()); QOption op = options.option(QString::fromLatin1("vo")); QString vo = op.value().toString(); if (!op.isSet()) { QString exe(a.arguments().at(0)); int i = exe.lastIndexOf(QLatin1Char('-')); if (i > 0) { vo = exe.mid(i+1, exe.indexOf(QLatin1Char('.')) - i - 1); } } qDebug("vo: %s", vo.toUtf8().constData()); MainWindow window; window.setProperty("rendererId", rendererId_from_opt_name(vo.toLower())); window.show(); window.setWindowTitle(QString::fromLatin1("QtAV %1 wbsecg1@gmail.com").arg(QtAV_Version_String_Long())); AppEventFilter ae(&window); qApp->installEventFilter(&ae); int x = window.x(); int y = window.y(); op = options.option(QString::fromLatin1("width")); int w = op.value().toInt(); op = options.option(QString::fromLatin1("height")); int h = op.value().toInt(); op = options.option(QString::fromLatin1("x")); if (op.isSet()) x = op.value().toInt(); op = options.option(QString::fromLatin1("y")); if (op.isSet()) y = op.value().toInt(); window.resize(w, h); window.move(x, y); if (options.value(QString::fromLatin1("fullscreen")).toBool()) window.showFullScreen(); op = options.option(QString::fromLatin1("ao")); if (op.isSet()) { QString aos(op.value().toString()); QStringList ao; if (aos.contains(QString::fromLatin1(";"))) ao = aos.split(QString::fromLatin1(";"), QString::SkipEmptyParts); else ao = aos.split(QString::fromLatin1(","), QString::SkipEmptyParts); window.setAudioBackends(ao); } op = options.option(QString::fromLatin1("vd")); if (op.isSet()) { QStringList vd = op.value().toString().split(QString::fromLatin1(";"), QString::SkipEmptyParts); if (!vd.isEmpty()) window.setVideoDecoderNames(vd); } op = options.option(QString::fromLatin1("file")); if (op.isSet()) { qDebug() << "-f set: " << op.value().toString(); window.play(op.value().toString()); } else { if (argc > 1 && !a.arguments().last().startsWith(QLatin1Char('-')) && !a.arguments().at(argc-2).startsWith(QLatin1Char('-'))) window.play(a.arguments().last()); } int ret = a.exec(); return ret; } QtAV-1.12.0/examples/player/player.pro000066400000000000000000000031331312235004300175270ustar00rootroot00000000000000TARGET = Player TEMPLATE = app contains(QT_CONFIG, opengl): QT += opengl QT += sql svg greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TRANSLATIONS = res/player_zh_CN.ts res/player.ts !static:VERSION = $$QTAV_VERSION # vc: will create exp and lib, result in static build error PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) STATICLINK=1 include($$PWD/../common/libcommon.pri) preparePaths($$OUT_PWD/../../out) INCLUDEPATH += $$PWD mac: RC_FILE = $$PROJECTROOT/src/QtAV.icns genRC($$TARGET) include(src.pri) unix:!android:!mac { #debian player_bins = Player QMLPlayer DEB_INSTALL_LIST = $$join(player_bins, \\n.$$[QT_INSTALL_BINS]/, .$$[QT_INSTALL_BINS]/) DEB_INSTALL_LIST *= \ usr/share/applications/Player.desktop \ usr/share/applications/QMLPlayer.desktop \ usr/share/icons/hicolor/scalable/apps/QtAV.svg deb_install_list.target = qtav-players.install deb_install_list.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${deb_install_list.target} QMAKE_EXTRA_TARGETS += deb_install_list target.depends *= $${deb_install_list.target} qtav_players_links.target = qtav-players.links qtav_players_links.commands = echo \"$$[QT_INSTALL_BINS]/Player /usr/bin/Player\n$$[QT_INSTALL_BINS]/QMLPlayer /usr/bin/QMLPlayer\" >$$PROJECTROOT/debian/$${qtav_players_links.target} QMAKE_EXTRA_TARGETS *= qtav_players_links target.depends *= $${qtav_players_links.target} } tv.files = res/tv.ini #BIN_INSTALLS += tv target.path = $$[QT_INSTALL_BINS] include($$PROJECTROOT/deploy.pri) RESOURCES += res/player.qrc QtAV-1.12.0/examples/player/player_sdk.pro000066400000000000000000000036721312235004300204000ustar00rootroot00000000000000TARGET = Player QT += sql svg ########## template for QtAV app project BEGIN ################ greaterThan(QT_MAJOR_VERSION, 4) { QT += avwidgets } else { CONFIG += avwidgets } #rpath for osx qt4 mac { RPATHDIR *= @loader_path/../Frameworks @executable_path/../Frameworks QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ isEmpty(QMAKE_LFLAGS_RPATH): QMAKE_LFLAGS_RPATH=-Wl,-rpath, for(R,RPATHDIR) { QMAKE_LFLAGS *= \'$${QMAKE_LFLAGS_RPATH}$$R\' } } ########## template for QtAV app project END ################ ################# Add your own code below ################### COMMON = $$PWD/../common INCLUDEPATH *= $$COMMON $$COMMON/.. RESOURCES += $$COMMON/theme/theme.qrc isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/../.. mac: RC_FILE = $$PROJECTROOT/src/QtAV.icns QMAKE_INFO_PLIST = $$COMMON/Info.plist ios: QMAKE_INFO_PLIST = ios/Info.plist videos.files = video videos.path = / QMAKE_BUNDLE_DATA += videos defineTest(genRC) { RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV $$1" export(RC_ICONS) export(QMAKE_TARGET_COMPANY) export(QMAKE_TARGET_DESCRIPTION) export(QMAKE_TARGET_COPYRIGHT) export(QMAKE_TARGET_PRODUCT) return(true) } genRC($$TARGET) #SystemParametersInfo !winrt:*msvc*: LIBS += -lUser32 DEFINES += BUILD_QOPT_LIB HEADERS = \ $$COMMON/common.h \ $$COMMON/Config.h \ $$COMMON/qoptions.h \ $$COMMON/ScreenSaver.h \ $$COMMON/common_export.h SOURCES = \ $$COMMON/common.cpp \ $$COMMON/Config.cpp \ $$COMMON/qoptions.cpp !macx: SOURCES += $$COMMON/ScreenSaver.cpp macx:!ios { #SOURCE is ok OBJECTIVE_SOURCES += $$COMMON/ScreenSaver.cpp LIBS += -framework CoreServices #-framework ScreenSaver } include(src.pri) QtAV-1.12.0/examples/player/playlist/000077500000000000000000000000001312235004300173525ustar00rootroot00000000000000QtAV-1.12.0/examples/player/playlist/PlayList.cpp000066400000000000000000000140611312235004300216210ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "PlayList.h" #include "PlayListModel.h" #include "PlayListDelegate.h" #include "common/common.h" #include #include #include #include #include #include PlayList::PlayList(QWidget *parent) : QWidget(parent) { mFirstShow = true; mMaxRows = -1; mpModel = new PlayListModel(this); mpDelegate = new PlayListDelegate(this); mpListView = new QListView; //mpListView->setResizeMode(QListView::Adjust); mpListView->setModel(mpModel); mpListView->setItemDelegate(mpDelegate); mpListView->setSelectionMode(QAbstractItemView::ExtendedSelection); //ctrl,shift mpListView->setEditTriggers(QAbstractItemView::NoEditTriggers); mpListView->setToolTip(QString::fromLatin1("Ctrl/Shift + ") + tr("Click to select multiple")); QVBoxLayout *vbl = new QVBoxLayout; setLayout(vbl); vbl->addWidget(mpListView); QHBoxLayout *hbl = new QHBoxLayout; mpClear = new QToolButton(0); mpClear->setText(tr("Clear")); mpRemove = new QToolButton(0); mpRemove->setText(QString::fromLatin1("-")); mpRemove->setToolTip(tr("Remove selected items")); mpAdd = new QToolButton(0); mpAdd->setText(QString::fromLatin1("+")); hbl->addWidget(mpClear); hbl->addSpacing(width()); hbl->addWidget(mpRemove); hbl->addWidget(mpAdd); vbl->addLayout(hbl); connect(mpClear, SIGNAL(clicked()), SLOT(clearItems())); connect(mpRemove, SIGNAL(clicked()), SLOT(removeSelectedItems())); connect(mpAdd, SIGNAL(clicked()), SLOT(addItems())); connect(mpListView, SIGNAL(doubleClicked(QModelIndex)), SLOT(onAboutToPlay(QModelIndex))); // enter to highight //connect(mpListView, SIGNAL(entered(QModelIndex)), SLOT(highlight(QModelIndex))); } PlayList::~PlayList() { qDebug("+++++++++++++~PlayList()"); save(); } void PlayList::setSaveFile(const QString &file) { mFile = file; } QString PlayList::saveFile() const { return mFile; } void PlayList::load() { QFile f(mFile); if (!f.exists()) return; if (!f.open(QIODevice::ReadOnly)) return; QDataStream ds(&f); QList list; ds >> list; for (int i = 0; i < list.size(); ++i) { insertItemAt(list.at(i), i); } } void PlayList::save() { QFile f(mFile); if (!f.open(QIODevice::WriteOnly)) { qWarning("File open error: %s", qPrintable(f.errorString())); return; } QDataStream ds(&f); ds << mpModel->items(); } PlayListItem PlayList::itemAt(int row) { if (mpModel->rowCount() < 0) { qWarning("Invalid rowCount"); return PlayListItem(); } return mpModel->data(mpModel->index(row), Qt::DisplayRole).value(); } void PlayList::insertItemAt(const PlayListItem &item, int row) { if (mMaxRows > 0 && mpModel->rowCount() >= mMaxRows) { // +1 because new row is to be inserted mpModel->removeRows(mMaxRows, mpModel->rowCount() - mMaxRows + 1); } int i = mpModel->items().indexOf(item, row+1); if (i > 0) { mpModel->removeRow(i); } if (!mpModel->insertRow(row)) return; if (row > 0) { i = mpModel->items().lastIndexOf(item, row - 1); if (i >= 0) mpModel->removeRow(i); } setItemAt(item, row); } void PlayList::setItemAt(const PlayListItem &item, int row) { mpModel->setData(mpModel->index(row), QVariant::fromValue(item), Qt::DisplayRole); } void PlayList::insert(const QString &url, int row) { PlayListItem item; item.setUrl(url); item.setDuration(0); item.setLastTime(0); QString title = url; if (!url.contains(QLatin1String("://")) || url.startsWith(QLatin1String("file://"))) { title = QFileInfo(url).fileName(); } item.setTitle(title); insertItemAt(item, row); } void PlayList::remove(const QString &url) { for (int i = mpModel->rowCount() - 1; i >= 0; --i) { PlayListItem item = mpModel->data(mpModel->index(i), Qt::DisplayRole).value(); if (item.url() == url) { mpModel->removeRow(i); } } } void PlayList::setMaxRows(int r) { mMaxRows = r; } int PlayList::maxRows() const { return mMaxRows; } void PlayList::removeSelectedItems() { QItemSelectionModel *selection = mpListView->selectionModel(); if (!selection->hasSelection()) return; QModelIndexList s = selection->selectedIndexes(); for (int i = s.size()-1; i >= 0; --i) { mpModel->removeRow(s.at(i).row()); } } void PlayList::clearItems() { mpModel->removeRows(0, mpModel->rowCount()); } void PlayList::addItems() { // TODO: add url; QStringList files = QFileDialog::getOpenFileNames(0, tr("Select files")); if (files.isEmpty()) return; // TODO: check playlist file: m3u, pls... In another thread for (int i = 0; i < files.size(); ++i) { QString file = files.at(i); if (!QFileInfo(file).isFile()) continue; insert(file, i); } } void PlayList::onAboutToPlay(const QModelIndex &index) { emit aboutToPlay(index.data(Qt::DisplayRole).value().url()); save(); } QtAV-1.12.0/examples/player/playlist/PlayList.h000066400000000000000000000042161312235004300212670ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYLIST_H #define PLAYLIST_H #include #include #include "PlayListItem.h" QT_BEGIN_NAMESPACE class QListView; class QToolButton; QT_END_NAMESPACE class PlayListDelegate; class PlayListModel; class PlayList : public QWidget { Q_OBJECT public: explicit PlayList(QWidget *parent = 0); ~PlayList(); void setSaveFile(const QString& file); QString saveFile() const; void load(); void save(); PlayListItem itemAt(int row); void insertItemAt(const PlayListItem& item, int row = 0); void setItemAt(const PlayListItem& item, int row = 0); void remove(const QString& url); void insert(const QString& url, int row = 0); void setMaxRows(int r); int maxRows() const; signals: void aboutToPlay(const QString& url); private slots: void removeSelectedItems(); void clearItems(); // void addItems(); void onAboutToPlay(const QModelIndex& index); //void highlight(const QModelIndex& index); private: QListView *mpListView; QToolButton *mpClear, *mpRemove, *mpAdd; PlayListDelegate *mpDelegate; PlayListModel *mpModel; int mMaxRows; QString mFile; bool mFirstShow; }; #endif // PLAYLIST_H QtAV-1.12.0/examples/player/playlist/PlayListDelegate.cpp000066400000000000000000000103021312235004300232460ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "PlayListDelegate.h" #include "PlayListItem.h" #include #include #include "PlayListModel.h" static const int kMarginLeft = 4; static const int kMarginTop = 2; static const int kWidth = 320; static const int kHeightMax = 30; static const int kHeightMin = 20; PlayListDelegate::PlayListDelegate(QObject *parent) : QStyledItemDelegate(parent) { mHighlightRow = -1; } // dynamic height:http://www.qtcentre.org/threads/18879-solved-QListView-dynamic-item-height void PlayListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.data().canConvert()) { QStyledItemDelegate::paint(painter, option, index); return; } painter->save(); painter->translate(option.rect.topLeft()); //!!! painter->setRenderHint(QPainter::Antialiasing, true); PlayListItem pli = qvariant_cast(index.data(Qt::DisplayRole)); //selected state has different background bool detail = false; //TODO: draw border. wrong rect. http://stackoverflow.com/questions/18568594/correct-highlighting-with-qt-custom-delegates //const QWidget *widget = option.widget; //QStyle *style = widget ? widget->style() : QApplication::style(); if (option.state & QStyle::State_Selected) { detail = true; mSelectedRows.append(index.row()); painter->fillRect(QRect(0, 0, kWidth, kHeightMax), QColor(0, 100, 200, 100)); //style->drawControl(QStyle::CE_ItemViewItem, &option, painter, widget); } else { mSelectedRows.removeAll(index.row()); } // if check QStyle::State_HasFocus, updateLayout() will be called infinitly, why? if (option.state & (QStyle::State_MouseOver)) { detail = true; PlayListModel *m = (PlayListModel*)index.model(); if (m && mHighlightRow != index.row()) { mHighlightRow = index.row(); m->updateLayout(); } painter->fillRect(QRect(0, 0, kWidth, kHeightMax), QColor(0, 100, 200, 30)); } QFont ft; ft.setBold(detail); ft.setPixelSize(12);//kHeightMin - 2*kMarginTop); painter->setFont(ft); painter->translate(kMarginLeft, kMarginTop); painter->drawText(QRect(0, 0, kWidth - 2*kMarginLeft, kHeightMin - 2*kMarginTop), pli.title()); painter->translate(0, kHeightMin + kMarginTop); if (detail) { painter->save(); ft.setBold(false); ft.setPixelSize(8);//(kHeightMax - kHeightMin - 2*kMarginTop)); painter->setFont(ft); painter->drawText(0, 0, pli.lastTimeString() + QString::fromLatin1("/") + pli.durationString()); painter->restore(); } painter->restore(); } // http://qt-project.org/forums/viewthread/5741 QSize PlayListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.data().canConvert()) { return QStyledItemDelegate::sizeHint(option, index); } bool detail = option.state & (QStyle::State_Selected|QStyle::State_MouseOver); // TODO: why detail always false? if (detail || mHighlightRow == index.row() || mSelectedRows.contains(index.row())) { return QSize(kWidth, kHeightMax); } return QSize(kWidth, kHeightMin); } QtAV-1.12.0/examples/player/playlist/PlayListDelegate.h000066400000000000000000000027301312235004300227210ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYLISTDELEGATE_H #define PLAYLISTDELEGATE_H #include class PlayListDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit PlayListDelegate(QObject *parent = 0); virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex & index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; private: mutable int mHighlightRow; mutable QList mSelectedRows; }; #endif // PLAYLISTDELEGATE_H QtAV-1.12.0/examples/player/playlist/PlayListItem.cpp000066400000000000000000000051711312235004300224420ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "PlayListItem.h" #include #include QDataStream& operator>> (QDataStream& s, PlayListItem& p) { int stars; qint64 duration, last_time; QString url, title; s >> url >> title >> duration >> last_time >> stars; p.setTitle(title); p.setUrl(url); p.setStars(stars); p.setDuration(duration); p.setLastTime(last_time); return s; } QDataStream& operator<< (QDataStream& s, const PlayListItem& p) { s << p.url() << p.title() << p.duration() << p.lastTime() << p.stars(); return s; } PlayListItem::PlayListItem() : mStars(0) , mLastTime(0) , mDuration(0) { } void PlayListItem::setTitle(const QString &title) { mTitle = title; } QString PlayListItem::title() const { return mTitle; } void PlayListItem::setUrl(const QString &url) { mUrl = url; } QString PlayListItem::url() const { return mUrl; } void PlayListItem::setStars(int s) { mStars = s; } int PlayListItem::stars() const { return mStars; } void PlayListItem::setLastTime(qint64 ms) { mLastTime = ms; mLastTimeS = QTime(0, 0, 0, 0).addMSecs(ms).toString(QString::fromLatin1("HH:mm:ss")); } qint64 PlayListItem::lastTime() const { return mLastTime; } QString PlayListItem::lastTimeString() const { return mLastTimeS; } void PlayListItem::setDuration(qint64 ms) { mDuration = ms; mDurationS = QTime(0, 0, 0, 0).addMSecs(ms).toString(QString::fromLatin1("HH:mm:ss")); } qint64 PlayListItem::duration() const { return mDuration; } QString PlayListItem::durationString() const { return mDurationS; } bool PlayListItem::operator ==(const PlayListItem& other) const { return url() == other.url(); } QtAV-1.12.0/examples/player/playlist/PlayListItem.h000066400000000000000000000035211312235004300221040ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYLISTITEM_H #define PLAYLISTITEM_H #include #include class PlayListItem { public: PlayListItem(); void setTitle(const QString& title); QString title() const; void setUrl(const QString& url); QString url() const; void setStars(int s); int stars() const; void setLastTime(qint64 ms); qint64 lastTime() const; QString lastTimeString() const; void setDuration(qint64 ms); qint64 duration() const; QString durationString() const; //icon bool operator ==(const PlayListItem& other) const; private: QString mTitle; QString mUrl; int mStars; qint64 mLastTime, mDuration; QString mLastTimeS, mDurationS; }; Q_DECLARE_METATYPE(PlayListItem); QDataStream& operator>> (QDataStream& s, PlayListItem& p); QDataStream& operator<< (QDataStream& s, const PlayListItem& p); #endif // PLAYLISTITEM_H QtAV-1.12.0/examples/player/playlist/PlayListModel.cpp000066400000000000000000000060551312235004300226060ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "PlayListModel.h" #include "PlayListItem.h" #include PlayListModel::PlayListModel(QObject *parent) : QAbstractListModel(parent) { } QList PlayListModel::items() const { return mItems; } Qt::ItemFlags PlayListModel::flags(const QModelIndex &index) const { if (!index.isValid()) return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;; return QAbstractListModel::flags(index) | Qt::ItemIsSelectable; } int PlayListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return mItems.size(); } QVariant PlayListModel::data(const QModelIndex &index, int role) const { QVariant v; if (index.row() < 0 || index.row() >= mItems.size()) return v; if (role == Qt::DisplayRole || role == Qt::EditRole) v.setValue(mItems.at(index.row())); return v; } bool PlayListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.row() >= 0 && index.row() < mItems.size() && (role == Qt::EditRole || role == Qt::DisplayRole)) { // TODO: compare value? mItems.replace(index.row(), value.value()); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) emit dataChanged(index, index, QVector() << role); #else emit dataChanged(index, index); #endif return true; } return false; } bool PlayListModel::insertRows(int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || row > rowCount(parent)) return false; beginInsertRows(QModelIndex(), row, row + count - 1); for (int r = 0; r < count; ++r) mItems.insert(row, PlayListItem()); endInsertRows(); return true; } bool PlayListModel::removeRows(int row, int count, const QModelIndex &parent) { if (count <= 0 || row < 0 || (row + count) > rowCount(parent)) return false; beginRemoveRows(QModelIndex(), row, row + count - 1); for (int r = 0; r < count; ++r) mItems.removeAt(row); endRemoveRows(); return true; } void PlayListModel::updateLayout() { emit layoutChanged(); } QtAV-1.12.0/examples/player/playlist/PlayListModel.h000066400000000000000000000034351312235004300222520ustar00rootroot00000000000000/****************************************************************************** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYLISTMODEL_H #define PLAYLISTMODEL_H #include #include "PlayListItem.h" class PlayListModel : public QAbstractListModel { Q_OBJECT Q_DISABLE_COPY(PlayListModel) public: explicit PlayListModel(QObject *parent = 0); QList items() const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); void updateLayout(); private: QList mItems; }; #endif // PLAYLISTMODEL_H QtAV-1.12.0/examples/player/res/000077500000000000000000000000001312235004300163025ustar00rootroot00000000000000QtAV-1.12.0/examples/player/res/help-zh_CN.html000066400000000000000000000025231312235004300211210ustar00rootroot00000000000000

命令行


player [-ao null] [-vo qt|gl|d2d|gdi|xv] [-vd "dxva[;cuda[;vaapi[;vda[;ffmpeg]]]]"] [--ffmpeg-log] [-f url|path|pipe:]

关闭音频 -ao null

选择渲染引擎用 -vo 选项。选项值可为qt, gl(默认), d2d, gdi, xv

选择解码器使用 -vd选项。可以是 dxva, cuda, vaapi, ffmpeg, 多个解码器用 ; 分隔表示解码器优先级。比如:

player -vd "dxva;cuda;ffmpeg" filename

表示优先使用dxva解码,若无法解码则选择cuda硬解,再不行则使用ffmpeg软解。

快捷键

  • 双击: 全屏切换
  • Ctrl+O: 打开文件
  • 空格: 暂停/播放
  • A: 切换比例
  • F: fullscreen on/off
  • T: 窗口置顶切换
  • N: 播放下一帧并暂停。按空格恢复持续播放
  • O: OSD
  • P: 重放
  • Q/ESC: 退出
  • S: 停止
  • R: 旋转90度
  • M: 切换静音
  • Up / Down: 音量 + / -
  • Ctrl+Up/Down: 速度 + / -
  • -> / <-: 前进/后退
  • Ctrl+滚轮: 缩放画面
  • 可以拖动文件到播放器显示区进行播放

更多信息请看wiki: https://github.com/wang-bin/QtAV/wiki/_pages

QtAV-1.12.0/examples/player/res/help.html000066400000000000000000000027141312235004300201240ustar00rootroot00000000000000

Command Line


player [-ao null] [-vo qt|gl|d2d|gdi|xv] [-vd "dxva[;cuda[;vaapi[;vda[;ffmpeg]]]]"] [--ffmpeg-log] [-f url|path|pipe:]

To disable audio output, add -ao null

Choose a render engine with -vo option(default is OpenGL). For example, in windows that support Direct2D, you can run

player -vo d2d filename

To select decoder, use -vd option. Value can be dxva, cuda, vaapi and ffmpeg, or a list separated by ; in priority order. For example:

player -vd "dxva;cuda;ffmpeg" filename

will use dxva if dxva can decode, use cuda if dxva failed to decode, and use ffmpeg if cuda can not decode.

Shortcuts

  • Double click: fullscreen switch
  • Ctrl+O: open a file
  • Space: pause/continue
  • A: aspect ratio
  • F: fullscreen on/off
  • T: stays on top on/off
  • N: show next frame. Continue the playing by pressing "Space"
  • O: OSD
  • P: replay
  • Q/ESC: quit
  • S: stop
  • R: rotate 90
  • M: mute on/off
  • Up / Down: volume + / -
  • Ctrl+Up/Down: speed + / -
  • -> / <-: seek forward / backward
  • Ctrl+Wheel: zoom in/out
  • Drag and drop a media file to player

read wiki for more information: https://github.com/wang-bin/QtAV/wiki/_pages

QtAV-1.12.0/examples/player/res/player.qrc000066400000000000000000000003561312235004300203110ustar00rootroot00000000000000 player_zh_CN.qm tv.ini help.html help-zh_CN.html QtAV-1.12.0/examples/player/res/player.ts000066400000000000000000001070511312235004300201520ustar00rootroot00000000000000 AVFilterConfigPage Video Audio Enable Registered filters Parameters AVFormatConfigPage Enable options Reduce buffering Probe size 0: auto Max analyze duration 0: auto. how many microseconds are analyzed to probe the input Extra AVFormat CaptureConfigPage Browse Save dir Save format Quality Capture ConfigDialog Reset Ok Cancel Apply DecoderConfigPage Video decoder config page Decoder Priorities reopen is required Up Down EventFilter Open a video Open an url Url Drag and drop a file to player A: switch aspect ratio Double click to switch fullscreen Shortcut: Space: pause/continue F: fullscreen on/off T: stays on top on/off N: show next frame. Continue the playing by pressing 'Space' Ctrl+O: open a file O: OSD P: replay Q/ESC: quit S: stop R: rotate 90 M: mute on/off C: capture video Up/Down: volume +/- Ctrl+Up/Down: speed +/- -&gt;/&lt;-: seek forward/backward Help Open Open Url About MainWindow Video renderer is not availabe on your platform! Current time Duration Render engine Speed. Ctrl+Up/Down Open Capture Open Url Play list History About Help Donate Setup Speed Repeat Enable infinity Times From negative value means from the end To Clock Auto Audio Video Take effect in next playback Subtitle Auto load Engine FFmpeg supports more subtitles but only render plain text LibASS supports ass styles Charset Auto detect System Auto detect requires libchardet Audio track Channel As input Stereo Mono (center) Left Right Aspect ratio Window Custom Color space Decoder Renderer Open an external audio track Can not use this renderer Open a media file Open an url Url External Player error No media Invalid meida Buffering... Buffered Loading... Loaded Stalled Capture video frame Save to Format Open a subtitle file MiscPage Preview size Force fps Ignore Progress update interval Buffer frames Timeout s Abort OpenGL type Windows only OpenGLES is Used by DXVA Zero Copy D3D9 has performance if ZeroCopy is disabled or for software decoders RESTART REQUIRED Currently only works for Qt>=5.5 XCB build Misc PlayList Click to select multiple Clear Remove selected items Select files QObject Url Format Bit rate Start time Duration Available Codec Decoder Decoder detail Total time Frames FPS FPS Now Pixel format Size Coded size GOP size Sample format Sample rate Channels Channel layout Frame size Metadata Video Audio Ok ShaderPage Enable Intermediate FBO Fragment shader header Additional header code Fragment shader texel sample function Shader StatisticsView Media info Key Value TVView Online TV channels VideoEQConfigPage Engine Brightness Constrast Hue Saturation Global Reset QtAV-1.12.0/examples/player/res/player_zh_CN.qm000066400000000000000000000247351312235004300212310ustar00rootroot00000000000000  2 Z Z@ Z ֗1  ~G Ɣ } HD[ UU'& cFT x9  k E 0Y $ " : JB Y" zj  m O   8#  l:   DZ Y | S"{tUP*_ >3o">S wdf} µ  i$+AudioAVFilterConfigPageT/u(EnableAVFilterConfigPageSep ParametersAVFilterConfigPage]lQv RegisteredAVFilterConfigPageƘVideoAVFilterConfigPagen\filtersAVFilterConfigPageAVFormatAVFormatConfigPageT/u(EnableAVFormatConfigPageYExtraAVFormatConfigPage yoptionsAVFormatConfigPagemOBrowseCaptureConfigPageb*VCaptureCaptureConfigPage(QualityCaptureConfigPageO[Xv_USave dirCaptureConfigPageO[Xh<_ Save formatCaptureConfigPage^u(Apply ConfigDialogSmCancel ConfigDialogxn[Ok ConfigDialognReset ConfigDialogxVhDecoderDecoderConfigPageN DownDecoderConfigPageOQH~ PrioritiesDecoderConfigPageN UpDecoderConfigPageƘxVhMnuVideo decoder config pageDecoderConfigPageebS_Ƙreopen is requiredDecoderConfigPage]/S: T/RM#->/<-: seek forward/backward  EventFilterA: Rcbf>y:kOA: switch aspect ratio EventFilterQsNAbout EventFilter C: b*ƘC: capture video EventFilterCtrl+O: bS_eNCtrl+O: open a file  EventFilter"Ctrl+N /N : de>^X/QCtrl+Up/Down: speed +/-  EventFilter SQRcbQh\O!Double click to switch fullscreen EventFilterSbR0eNR0zSۈLde>Drag and drop a file to player  EventFilterF: Qh\ORcbF: fullscreen on/off  EventFilter^.RHelp EventFilterM: RcbYM: mute on/off  EventFilter"N: N N^'^vfP\0c zzh<`bY de>=N: show next frame. Continue the playing by pressing 'Space'  EventFilterO: OSD  EventFilterbS_Open EventFilter bS_ UrlOpen Url EventFilterbS_Ƙ Open a video EventFilter bS_ url Open an url EventFilter P: e> P: replay  EventFilterQ/ESC: Q Q/ESC: quit  EventFilterR: eˏl90^ R: rotate 90 EventFilterSP\kbS: stop  EventFilter_cw.: Shortcut:  EventFilterzzh<: fP\/~~Space: pause/continue  EventFilterT: zSnvRcbT: stays on top on/off  EventFilterN /N : X/QUp/Down: volume +/-  EventFilterUrl EventFilterQsNAbout MainWindowT QeAs input MainWindowkO Aspect ratio MainWindowAudio MainWindowh Audio track MainWindowRAuto MainWindowRhmK Auto detect MainWindow"RhmKlibchardet^Auto detect requires libchardet MainWindowRR} Auto load MainWindow]QBuffered MainWindow QN-... Buffering... MainWindowelOu(n2gVhCan not use this renderer MainWindowb*VCapture MainWindowƘb*VCapture video frame MainWindowXSChannel MainWindow[W{&Charset MainWindoweClock MainWindowr_izz Color space MainWindowde>e Current time MainWindow[NICustom MainWindowxVhDecoder MainWindowcPRDonate MainWindowƘeDuration MainWindowT/u(Enable MainWindow_dEngine MainWindowYcExternal MainWindow0FFmpeg e/cfY[W^UOFQ[N~eg,_b_n2g9FFmpeg supports more subtitles but only render plain text MainWindowh<_Format MainWindowNFrom MainWindow^.RHelp MainWindowde>SSHistory MainWindoweeHZOS Invalid meida MainWindow]XSLeft MainWindowlibasse/cassh7_LibASS supports ass styles MainWindow]R}Loaded MainWindow R}N-... Loading... MainWindowSUXS (N-Y.) Mono (center) MainWindoweZOSNo media MainWindowbS_Open MainWindow bS_ UrlOpen Url MainWindow bS_ZOSeNOpen a media file MainWindow bS_[W^UeNOpen a subtitle file MainWindow bS_YhOpen an external audio track MainWindow bS_ url Open an url MainWindowde>Rh Play list MainWindowde> Player error MainWindown2g_d Render engine MainWindown2g_dRenderer MainWindowY Repeat MainWindowSXSRight MainWindowO[XSave to MainWindownSetup MainWindow^Speed MainWindow^. Ctrl+N /N Speed. Ctrl+Up/Down MainWindowP\kbStalled MainWindowzOSXStereo MainWindow[W^USubtitle MainWindow|~System MainWindow N k!de>ueHTake effect in next playback MainWindowk!epTimes MainWindowR0To MainWindowUrl MainWindowƘVideo MainWindow Ƙn2gVhN:Video renderer is  MainWindowzSWindow MainWindowePinfinity MainWindowepRNg+\>{!negative value means from the end MainWindowO`v^sSN N SOu(!not availabe on your platform! MainWindowN-kbde>AbortMiscPageQ^'ep Buffer framesMiscPage"vRMQt>=5.5 XCBe/c*Currently only works for Qt>=5.5 XCB buildMiscPage8[NoNxbDXVA^0bxl,D3D9S`'fY}ED3D9 has performance if ZeroCopy is disabled or for software decodersMiscPage_:R6^'s Force fpsMiscPageeIgnoreMiscPagegByMiscMiscPageOpenGL |{W OpenGL typeMiscPage,OpenGLES u(N DXVA 0bxl"OpenGLES is Used by DXVA Zero CopyMiscPagePreviewMiscPage ^feProgress update intervalMiscPageeT/Rde>VhRESTART REQUIREDMiscPageeTimeoutMiscPage Windows onlyMiscPageysMiscPageY'\sizeMiscPagendClearPlayList SUQ Y Click to select multiplePlayList ydb@ yvRemove selected itemsPlayList beN Select filesPlayListAudioQObjectSu( AvailableQObjectxsBit rateQObjectXS^\@Channel layoutQObjectXSepChannelsQObjectxCodecQObjectx\:[ Coded sizeQObjectxVhDecoderQObject xVh`Decoder detailQObjecteDurationQObjectFPSQObject _SRMFPSFPS NowQObjecth<_FormatQObject^'Y'\ Frame sizeQObject^'epFramesQObject GOP Y'\GOP sizeQObjectQCepcnMetadataQObjectxn[OkQObjectP} h<_ Pixel formatQObjecth7h<_ Sample formatQObjecth7s Sample rateQObject\:[SizeQObjectwYe Start timeQObjecte Total timeQObjectUrlQObjectƘVideoQObjectT/u(Enable ShaderPageIntermediate FBO ShaderPagew@rVhShader ShaderPageKeyStatisticsViewZOSO`o Media infoStatisticsViewValueStatisticsViewW(~u5Online TV channelsTVViewN^ BrightnessVideoEQConfigPage[k^ ConstrastVideoEQConfigPage_dEngineVideoEQConfigPageQh\@GlobalVideoEQConfigPagerHueVideoEQConfigPagenResetVideoEQConfigPageqT^ SaturationVideoEQConfigPageQtAV-1.12.0/examples/player/res/player_zh_CN.ts000066400000000000000000001047751312235004300212450ustar00rootroot00000000000000 AVFilterConfigPage Video 视频 Audio 音频 Enable 启用 Registered 已注册的 filters 滤镜 Parameters 参数 AVFormatConfigPage Enable 启用 options 选项 Reduce buffering Probe size 0: auto Max analyze duration 0: auto. how many microseconds are analyzed to probe the input Extra 额外 AVFormat CaptureConfigPage Browse 浏览 Save dir 保存目录 Save format 保存格式 Quality 质量 Capture 截图 ConfigDialog Reset 重置 Ok 确定 Cancel 取消 Apply 应用 DecoderConfigPage Video decoder config page 视频解码器配置页 Decoder 解码器 Priorities 优先级 reopen is required 需要重新打开视频 Up Down EventFilter Open a video 打开视频 Open an url 打开 url Url Drag and drop a file to player 可拖到文件到窗口进行播放 A: switch aspect ratio A: 切换显示比例 Double click to switch fullscreen 双击切换全屏 Shortcut: 快捷键: Space: pause/continue 空格: 暂停/继续 F: fullscreen on/off F: 全屏切换 T: stays on top on/off T: 窗口置顶切换 N: show next frame. Continue the playing by pressing 'Space' N: 下一帧并暂停。按空格恢复播放 Ctrl+O: open a file Ctrl+O: 打开文件 O: OSD P: replay P: 重放 Q/ESC: quit Q/ESC: 退出 S: stop S:停止 R: rotate 90 R: 旋转90度 M: mute on/off M: 切换静音 C: capture video C: 截视频 Up/Down: volume +/- 上/下: 音量增/减 Ctrl+Up/Down: speed +/- Ctrl+上/下: 播放速度增/减 -&gt;/&lt;-: seek forward/backward 左/右: 后退/前进 Help 帮助 Open 打开 Open Url 打开 Url About 关于 MainWindow Current time 播放时间 Duration 视频时长 Render engine 渲染引擎 Speed. Ctrl+Up/Down 速度. Ctrl+上/下 Open 打开 Capture 截图 Capture video frame 视频截图 Save to 保存至 Open Url 打开 Url Setup 设置 About 关于 Help 帮助 Speed 速度 Repeat 重复 Enable 启用 infinity 无限 Times 次数 From negative value means from the end 负数则从末尾计算 To Clock 时钟 Auto 自动 Audio 音频 Take effect in next playback 下次播放生效 Engine 引擎 FFmpeg supports more subtitles but only render plain text FFmpeg 支持更多字幕但内容以纯文本形式渲染 LibASS supports ass styles libass支持ass样式 Auto detect 自动检测 Aspect ratio 比例 Video 视频 Window 窗口 Custom 自定义 Audio track 音轨 Play list 播放列表 History 播放历史 Donate 捐助 Subtitle 字幕 Auto load 自动加载 Charset 字符集 System 系统 Auto detect requires libchardet 自动检测需要libchardet库 Channel 声道 As input 同输入 Stereo 立体声 Mono (center) 单声道 (中央) Left 左声道 Right 右声道 Color space 色彩空间 Decoder 解码器 Renderer 渲染引擎 Open an external audio track 打开外部音轨 not availabe on your platform! 你的平台上不可使用! Video renderer is 视频渲染器为 Can not use this renderer 无法使用该渲染器 Open a media file 打开媒体文件 Open an url 打开 url Url External 外挂 Player error 播放错误 No media 无媒体 Invalid meida 无效媒体 Buffering... 缓冲中... Buffered 已缓冲 Loading... 加载中... Loaded 已加载 Stalled 停止 Format 格式 Open a subtitle file 打开字幕文件 MiscPage Preview 预览 size 大小 Force fps 强制帧率 Ignore 无视 Progress update interval 进度更新间隔 Buffer frames 缓冲帧数 Timeout 超时 s Abort 中止播放 OpenGL type OpenGL 类型 Windows only OpenGLES is Used by DXVA Zero Copy OpenGLES 用于 DXVA 0拷贝硬解 D3D9 has performance if ZeroCopy is disabled or for software decoders 对于软件解码或DXVA非0拷贝硬解,D3D9可能性能更好 RESTART REQUIRED 需要重新启动播放器 Currently only works for Qt>=5.5 XCB build 目前需要Qt>=5.5 XCB支持 Misc 杂项 PlayList Click to select multiple 单击 多选 Clear 清除 Remove selected items 移除所选项目 Select files 选择文件 QObject Url Format 格式 Bit rate 码率 Start time 起始时间 Duration 时长 Available 可用 Codec 编码 Decoder 解码器 Decoder detail 解码器详情 Total time 时长 FPS Frames 帧数 FPS Now 当前FPS Pixel format 像素格式 Size 尺寸 Coded size 编码尺寸 GOP size GOP 大小 Sample format 采样格式 Sample rate 采样率 Channels 声道数 Channel layout 声道布局 Frame size 帧大小 Metadata 元数据 Ok 确定 Video 视频 Audio 音频 ShaderPage Enable 启用 Intermediate FBO Fragment shader header Additional header code Fragment shader texel sample function Fragment shader RGB post process code 片段着色器RGB后处理代码 Shader 着色器 StatisticsView Media info 媒体信息 Key Value TVView Online TV channels 在线电视 VideoEQConfigPage Engine 引擎 Brightness 亮度 Constrast 对比度 Hue 色调 Saturation 饱和度 Global 全局 Reset 重置 QtAV-1.12.0/examples/player/res/tv.ini000066400000000000000000000002351312235004300174340ustar00rootroot00000000000000#http://mraandtux.github.io/tv/ #m3u8: http://blog.csdn.net/perfect_promise/article/details/9713663 #CUTV: http://5mu5.com/forum.php?mod=viewthread&tid=6565 QtAV-1.12.0/examples/player/src.pri000066400000000000000000000024451312235004300170210ustar00rootroot00000000000000 SOURCES += main.cpp \ MainWindow.cpp \ ClickableMenu.cpp \ StatisticsView.cpp \ Slider.cpp \ TVView.cpp \ EventFilter.cpp \ config/ConfigDialog.cpp \ config/ConfigPageBase.cpp \ config/CaptureConfigPage.cpp \ config/VideoEQConfigPage.cpp \ config/DecoderConfigPage.cpp \ config/MiscPage.cpp \ filters/OSD.cpp \ filters/OSDFilter.cpp \ playlist/PlayListModel.cpp \ playlist/PlayListItem.cpp \ playlist/PlayListDelegate.cpp \ playlist/PlayList.cpp \ config/PropertyEditor.cpp \ config/AVFormatConfigPage.cpp \ config/AVFilterConfigPage.cpp \ config/ShaderPage.cpp \ filters/AVFilterSubtitle.cpp HEADERS += \ MainWindow.h \ ClickableMenu.h \ StatisticsView.h \ Slider.h \ TVView.h \ EventFilter.h \ config/ConfigDialog.h \ config/ConfigPageBase.h \ config/CaptureConfigPage.h \ config/VideoEQConfigPage.h \ config/DecoderConfigPage.h \ config/MiscPage.h \ filters/OSD.h \ filters/OSDFilter.h \ playlist/PlayListModel.h \ playlist/PlayListItem.h \ playlist/PlayListDelegate.h \ playlist/PlayList.h \ config/PropertyEditor.h \ config/AVFormatConfigPage.h \ config/AVFilterConfigPage.h \ config/ShaderPage.h \ filters/AVFilterSubtitle.h QtAV-1.12.0/examples/qml/000077500000000000000000000000001312235004300150065ustar00rootroot00000000000000QtAV-1.12.0/examples/qml/filter.qml000066400000000000000000000014321312235004300170060ustar00rootroot00000000000000import QtQuick 2.1 import QtAV 1.7 import QtQuick.Dialogs 1.0 Rectangle { width: 800 height: 500 color: "black" VideoFilter { id: negate type: VideoFilter.AVFilter avfilter: "negate" } Video { id: video autoPlay: true anchors.fill: parent subtitle.engines: ["FFmpeg"] videoFilters: [negate] } Text { color: "white" text: "Press key 'O' to open a file" } Item { anchors.fill: parent focus: true Keys.onPressed: { switch (event.key) { case Qt.Key_O: fileDialog.open() break } } } FileDialog { id: fileDialog onAccepted: video.source = fileUrl } } QtAV-1.12.0/examples/qml/glslfilter.qml000066400000000000000000000016301312235004300176700ustar00rootroot00000000000000import QtQuick 2.1 import QtAV 1.7 import QtQuick.Dialogs 1.0 Rectangle { width: 800 height: 500 color: "black" VideoFilter { id: negate type: VideoFilter.GLSLFilter shader: Shader { postProcess: "gl_FragColor.rgb = vec3(1.0-gl_FragColor.r, 1.0-gl_FragColor.g, 1.0-gl_FragColor.b);" } } Video { id: video autoPlay: true anchors.fill: parent subtitle.engines: ["FFmpeg"] videoFiltersGPU: [negate] } Text { color: "white" text: "Press key 'O' to open a file" } Item { anchors.fill: parent focus: true Keys.onPressed: { switch (event.key) { case Qt.Key_O: fileDialog.open() break } } } FileDialog { id: fileDialog onAccepted: video.source = fileUrl } } QtAV-1.12.0/examples/qml/multiwindow.qml000066400000000000000000000014771312235004300201140ustar00rootroot00000000000000import QtQuick 2.0 import QtQuick.Window 2.0 import QtAV 1.3 Item { MediaPlayer { //or AVPlayer id: player source: "test.mov" autoPlay: true } MouseArea { anchors.fill: parent onClicked: player.play() } VideoOutput { anchors.fill: parent source: player width: 400 height: 300 } Window { x: 0; y: 100; width: 600; height: 400; visible: true VideoOutput { anchors.fill: parent source: player regionOfInterest: Qt.rect(0, 0, 0.5, 1); } } Window { x: 600; y: 100; width: 400; height: 300; visible: true VideoOutput { anchors.fill: parent source: player regionOfInterest: Qt.rect(0.5, 0, 0.5, 1); } } } QtAV-1.12.0/examples/qml/simple.qml000066400000000000000000000012221312235004300170070ustar00rootroot00000000000000import QtQuick 2.1 import QtAV 1.4 import QtQuick.Dialogs 1.0 Rectangle { width: 800 height: 500 color: "black" Video { id: video autoPlay: true anchors.fill: parent subtitle.engines: ["FFmpeg"] } Text { color: "white" text: "Press key 'O' to open a file" } Item { anchors.fill: parent focus: true Keys.onPressed: { switch (event.key) { case Qt.Key_O: fileDialog.open() break } } } FileDialog { id: fileDialog onAccepted: video.source = fileUrl } } QtAV-1.12.0/examples/shader/000077500000000000000000000000001312235004300154635ustar00rootroot00000000000000QtAV-1.12.0/examples/shader/main.cpp000066400000000000000000000126511312235004300171200ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include using namespace QtAV; #define GLSL(x) #x "" class MyShader : public VideoShaderObject { public: MyShader() { startTimer(50);} protected: void timerEvent(QTimerEvent*) { static int t = 0; const float b = float(50 - t%100)/50.0; const float c = float(50 - 4*t%100)/50.0; ++t; setProperty("bs", QVariant::fromValue(QVector() << b << c)); } private: const char* userShaderHeader(QOpenGLShader::ShaderType type) const { if (type == QOpenGLShader::Vertex) return 0; return GLSL(uniform vec2 bs;); } const char* userPostProcess() const { return GLSL( float lr = 0.3086; float lg = 0.6094; float lb = 0.0820; float s = bs.g + 1.0; float is = 1.0-s; float ilr = is * lr; float ilg = is * lg; float ilb = is * lb; mat4 m = mat4( ilr+s, ilg , ilb , 0.0, ilr , ilg+s, ilb , 0.0, ilr , ilg , ilb+s, 0.0, 0.0 , 0.0 , 0.0 , 1.0); gl_FragColor = m*gl_FragColor+bs.r;); } }; class MediumBlurShader : public ConvolutionShader { public: MediumBlurShader() {setKernelRadius(2);} protected: virtual const float* kernel() const { const float v = 1.0/(float)kernelSize(); //radius 1 static QVector k; k.resize(kernelSize()); k.fill(v); return k.constData(); } }; class WaveShader : public QObject, public VideoShader { public: WaveShader(QObject *parent = 0) : QObject(parent) , t(0) , A(0.06) , omega(5) { startTimer(20); } protected: void timerEvent(QTimerEvent*) { t+=2.0*M_PI/50.0; } private: const char* userShaderHeader(QOpenGLShader::ShaderType type) const { if (type == QOpenGLShader::Vertex) return 0; return GLSL( uniform float u_omega; uniform float u_A; uniform float u_t; ); } const char* userSample() const { return GLSL( vec4 sample2d(sampler2D tex, vec2 pos, int p) { vec2 pulse = sin(u_t - u_omega * pos); vec2 coord = pos + u_A*vec2(pulse.x, -pulse.x); return texture(tex, coord); }); } bool setUserUniformValues() { // return false; // enable this line to call setUserUniformValue(Uniform&) program()->setUniformValue("u_A", A); program()->setUniformValue("u_omega", omega); program()->setUniformValue("u_t", t); return true; } // setUserUniformValues() must return false to call setUserUniformValue(Uniform&) void setUserUniformValue(Uniform &u) { if (u.name == "u_A") { u.set(A); } else if (u.name == "u_omega") { u.set(omega); } else if (u.name == "u_t") { u.set(t); } } private: float t; float A; float omega; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (a.arguments().size() < 2) { qDebug("./shader file"); return 0; } VideoOutput vo[4]; QScopedPointer shaders[4]; AVPlayer player; struct { QByteArray title; VideoShader *shader; } shader_list[] = { {"No shader", NULL}, {"Wave effect shader", new WaveShader()}, {"Blur shader", new MediumBlurShader()}, {"Brightness+Saturation. (VideoShaderObject dynamic properties)", new MyShader()} }; vo[0].widget()->move(0, 0); for (int i = 0; i < 4; ++i) { if (!vo[i].opengl()) qFatal("No opengl in the renderer"); player.addVideoRenderer(&vo[i]); vo[i].widget()->setWindowTitle(shader_list[i].title); vo[i].widget()->show(); vo[i].widget()->resize(500, 300); vo[i].widget()->move(vo[0].widget()->x() + (i%2)*vo[0].widget()->width(), vo[0].widget()->y() + (i/2)*vo[0].widget()->height()); vo[i].opengl()->setUserShader(shader_list[i].shader); shaders[i].reset(shader_list[i].shader); } player.play(a.arguments().at(1)); return a.exec(); } QtAV-1.12.0/examples/shader/shader.pro000066400000000000000000000003441312235004300174540ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle greaterThan(QT_MAJOR_VERSION, 4): QT += widgets config_gl: QT += opengl PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/examples/sharedoutput/000077500000000000000000000000001312235004300167445ustar00rootroot00000000000000QtAV-1.12.0/examples/sharedoutput/main.cpp000066400000000000000000000021271312235004300203760ustar00rootroot00000000000000/****************************************************************************** Shared output: shared renderer test Copyright (C) 2012-2013 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "widget.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); } QtAV-1.12.0/examples/sharedoutput/sharedoutput.pro000066400000000000000000000007641312235004300222240ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2013-01-20T13:10:48 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = sharedoutput TEMPLATE = app CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp \ widget.cpp HEADERS += widget.h QtAV-1.12.0/examples/sharedoutput/widget.cpp000066400000000000000000000067641312235004300207500ustar00rootroot00000000000000/****************************************************************************** Shared output: shared renderer test Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "widget.h" #include #include #include #include #include #include using namespace QtAV; Widget::Widget(QWidget *parent) : QWidget(parent) { QtAV::Widgets::registerRenderers(); setWindowTitle(QString::fromLatin1("A test for shared video renderer. QtAV%1 wbsecg1@gmail.com").arg(QtAV_Version_String_Long())); QVBoxLayout *main_layout = new QVBoxLayout; QHBoxLayout *btn_layout = new QHBoxLayout; renderer = new VideoOutput(); renderer->widget()->setFocusPolicy(Qt::StrongFocus); renderer->resizeRenderer(640, 480); for (int i = 0; i < 2; ++i) { player[i] = new AVPlayer(this); player[i]->setRenderer(renderer); QVBoxLayout *vb = new QVBoxLayout; play_btn[i] = new QPushButton(this); play_btn[i]->setText(QString::fromLatin1("Play-%1").arg(i)); file_btn[i] = new QPushButton(this); file_btn[i]->setText(QString::fromLatin1("Choose video-%1").arg(i)); connect(play_btn[i], SIGNAL(clicked()), SLOT(playVideo())); connect(file_btn[i], SIGNAL(clicked()), SLOT(setVideo())); vb->addWidget(play_btn[i]); vb->addWidget(file_btn[i]); btn_layout->addLayout(vb); } QPushButton *net_btn = new QPushButton(tr("Test online video(rtsp)")); connect(net_btn, SIGNAL(clicked()), SLOT(testRTSP())); main_layout->addWidget(renderer->widget()); main_layout->addWidget(net_btn); main_layout->addLayout(btn_layout); setLayout(main_layout); resize(720, 600); } Widget::~Widget() { } void Widget::playVideo() { for (int i = 0; i < 2; ++i) player[i]->pause(true); QPushButton *btn = qobject_cast(sender()); int idx = btn->text().section(QLatin1Char('-'), 1).toInt(); player[idx]->pause(false); } void Widget::setVideo() { QString v = QFileDialog::getOpenFileName(0, QString::fromLatin1("Select a video")); if (v.isEmpty()) return; QPushButton *btn = qobject_cast(sender()); int idx = btn->text().section(QLatin1Char('-'), 1).toInt(); QString oldv = player[idx]->file(); if (v == oldv) return; for (int i = 0; i < 2; ++i) player[i]->pause(true); player[idx]->stop(); player[idx]->setFile(v); player[idx]->play(); } void Widget::testRTSP() { for (int i = 0; i < 2; ++i) player[i]->stop(); player[0]->play(QString::fromLatin1("rtsp://122.192.35.80:554/live/tv11")); player[0]->pause(true); player[1]->play(QString::fromLatin1("rtsp://122.192.35.80:554/live/tv10")); } QtAV-1.12.0/examples/sharedoutput/widget.h000066400000000000000000000026711312235004300204060ustar00rootroot00000000000000/****************************************************************************** Shared output: shared renderer test Copyright (C) 2012-2013 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef WIDGET_H #define WIDGET_H #include #include namespace QtAV { class GLWidgetRenderer2; class AVPlayer; class VideoOutput; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); public slots: void setVideo(); void playVideo(); void testRTSP(); private: class QtAV::VideoOutput *renderer; QtAV::AVPlayer *player[2]; QPushButton *play_btn[2]; QPushButton *file_btn[2]; }; #endif // WIDGET_H QtAV-1.12.0/examples/simpleplayer/000077500000000000000000000000001312235004300167235ustar00rootroot00000000000000QtAV-1.12.0/examples/simpleplayer/CMakeLists.txt000066400000000000000000000005441312235004300214660ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) find_package(QtAV REQUIRED) find_package(Qt5Widgets REQUIRED) set(CMAKE_AUTOMOC ON) add_executable(simpleplayer main.cpp playerwindow.cpp playerwindow.h) target_include_directories(simpleplayer PRIVATE ${QTAVWIDGETS_INCLUDE_DIRS}) target_link_libraries(simpleplayer ${QTAVWIDGETS_LIBRARIES} Qt5::Widgets)QtAV-1.12.0/examples/simpleplayer/main.cpp000066400000000000000000000023211312235004300203510ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include "playerwindow.h" #include int main(int argc, char *argv[]) { QtAV::Widgets::registerRenderers(); QApplication a(argc, argv); PlayerWindow player; player.show(); player.resize(800, 600); return a.exec(); } QtAV-1.12.0/examples/simpleplayer/playerwindow.cpp000066400000000000000000000066231312235004300221620ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "playerwindow.h" #include #include #include #include #include using namespace QtAV; PlayerWindow::PlayerWindow(QWidget *parent) : QWidget(parent) { m_unit = 1000; setWindowTitle(QString::fromLatin1("QtAV simple player example")); m_player = new AVPlayer(this); QVBoxLayout *vl = new QVBoxLayout(); setLayout(vl); m_vo = new VideoOutput(this); if (!m_vo->widget()) { QMessageBox::warning(0, QString::fromLatin1("QtAV error"), tr("Can not create video renderer")); return; } m_player->setRenderer(m_vo); vl->addWidget(m_vo->widget()); m_slider = new QSlider(); m_slider->setOrientation(Qt::Horizontal); connect(m_slider, SIGNAL(sliderMoved(int)), SLOT(seekBySlider(int))); connect(m_slider, SIGNAL(sliderPressed()), SLOT(seekBySlider())); connect(m_player, SIGNAL(positionChanged(qint64)), SLOT(updateSlider(qint64))); connect(m_player, SIGNAL(started()), SLOT(updateSlider())); connect(m_player, SIGNAL(notifyIntervalChanged()), SLOT(updateSliderUnit())); vl->addWidget(m_slider); QHBoxLayout *hb = new QHBoxLayout(); vl->addLayout(hb); m_openBtn = new QPushButton(tr("Open")); m_playBtn = new QPushButton(tr("Play/Pause")); m_stopBtn = new QPushButton(tr("Stop")); hb->addWidget(m_openBtn); hb->addWidget(m_playBtn); hb->addWidget(m_stopBtn); connect(m_openBtn, SIGNAL(clicked()), SLOT(openMedia())); connect(m_playBtn, SIGNAL(clicked()), SLOT(playPause())); connect(m_stopBtn, SIGNAL(clicked()), m_player, SLOT(stop())); } void PlayerWindow::openMedia() { QString file = QFileDialog::getOpenFileName(0, tr("Open a video")); if (file.isEmpty()) return; m_player->play(file); } void PlayerWindow::seekBySlider(int value) { if (!m_player->isPlaying()) return; m_player->seek(qint64(value*m_unit)); } void PlayerWindow::seekBySlider() { seekBySlider(m_slider->value()); } void PlayerWindow::playPause() { if (!m_player->isPlaying()) { m_player->play(); return; } m_player->pause(!m_player->isPaused()); } void PlayerWindow::updateSlider(qint64 value) { m_slider->setRange(0, int(m_player->duration()/m_unit)); m_slider->setValue(int(value/m_unit)); } void PlayerWindow::updateSlider() { updateSlider(m_player->position()); } void PlayerWindow::updateSliderUnit() { m_unit = m_player->notifyInterval(); updateSlider(); } QtAV-1.12.0/examples/simpleplayer/playerwindow.h000066400000000000000000000032011312235004300216140ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYERWINDOW_H #define PLAYERWINDOW_H #include #include QT_BEGIN_NAMESPACE class QSlider; class QPushButton; QT_END_NAMESPACE class PlayerWindow : public QWidget { Q_OBJECT public: explicit PlayerWindow(QWidget *parent = 0); public Q_SLOTS: void openMedia(); void seekBySlider(int value); void seekBySlider(); void playPause(); private Q_SLOTS: void updateSlider(qint64 value); void updateSlider(); void updateSliderUnit(); private: QtAV::VideoOutput *m_vo; QtAV::AVPlayer *m_player; QSlider *m_slider; QPushButton *m_openBtn; QPushButton *m_playBtn; QPushButton *m_stopBtn; int m_unit; }; #endif // PLAYERWINDOW_H QtAV-1.12.0/examples/simpleplayer/simpleplayer.pro000066400000000000000000000003741312235004300221570ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle greaterThan(QT_MAJOR_VERSION, 4): QT += widgets PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) include(src.pri) QtAV-1.12.0/examples/simpleplayer/simpleplayer_sdk.pro000066400000000000000000000011001312235004300230040ustar00rootroot00000000000000########## template for QtAV app project BEGIN ################ greaterThan(QT_MAJOR_VERSION, 4) { QT += avwidgets } else { CONFIG += avwidgets } #rpath for osx qt4 mac { RPATHDIR *= @loader_path/../Frameworks @executable_path/../Frameworks QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ isEmpty(QMAKE_LFLAGS_RPATH): QMAKE_LFLAGS_RPATH=-Wl,-rpath, for(R,RPATHDIR) { QMAKE_LFLAGS *= \'$${QMAKE_LFLAGS_RPATH}$$R\' } } ########## template for QtAV app project END ################ ################# Add your own code below ################### include(src.pri) QtAV-1.12.0/examples/simpleplayer/src.pri000066400000000000000000000001141312235004300202220ustar00rootroot00000000000000SOURCES += main.cpp \ playerwindow.cpp HEADERS += \ playerwindow.h QtAV-1.12.0/examples/simpletranscode/000077500000000000000000000000001312235004300174115ustar00rootroot00000000000000QtAV-1.12.0/examples/simpletranscode/main.cpp000066400000000000000000000114201312235004300210370ustar00rootroot00000000000000/****************************************************************************** Simple Transcode: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug("QtAV simpletranscode"); qDebug("./simpletranscode -i infile -o outfile [-async] [-c:v video_codec (default: libx264)] [-hwdev dev] [-f format] [-an] [-ss HH:mm:ss.z]"); qDebug("-an: disable audio"); qDebug() << "examples:\n" << "./simpletranscode -i test.mp4 -o /tmp/test-%05d.png -f image2 -c:v png\n" << "./simpletranscode -i test.mp4 -o /tmp/bbb%04d.ts -f segment\n" << "./simpletranscode -i test.mp4 -o /tmp/test.mkv\n" ; if (a.arguments().contains(QString::fromLatin1("-h"))) { return 0; } QString file = QString::fromLatin1("test.avi"); int idx = a.arguments().indexOf(QLatin1String("-i")); if (idx > 0) file = a.arguments().at(idx + 1); QString outFile = QString::fromLatin1("/tmp/out.mp4"); idx = a.arguments().indexOf(QLatin1String("-o")); if (idx > 0) outFile = a.arguments().at(idx + 1); QDir().mkpath(outFile.left(outFile.lastIndexOf(QLatin1Char('/'))+1)); QString cv = QString::fromLatin1("libx264"); idx = a.arguments().indexOf(QLatin1String("-c:v")); if (idx > 0) cv = a.arguments().at(idx + 1); QString ca = QString::fromLatin1("aac"); idx = a.arguments().indexOf(QLatin1String("-c:a")); if (idx > 0) ca = a.arguments().at(idx + 1); QString fmt; idx = a.arguments().indexOf(QLatin1String("-f")); if (idx > 0) fmt = a.arguments().at(idx + 1); QString hwdev; idx = a.arguments().indexOf(QLatin1String("-hwdev")); if (idx > 0) hwdev = a.arguments().at(idx + 1); const bool an = a.arguments().contains(QLatin1String("-an")); const bool async = a.arguments().contains(QLatin1String("-async")); qint64 ss = 0; idx = a.arguments().indexOf(QLatin1String("-ss")); if (idx > 0) ss = QTime(0, 0, 0).msecsTo(QTime::fromString(a.arguments().at(idx + 1), QLatin1String("HH:mm:ss.z"))); QVariantHash muxopt, avfopt; avfopt[QString::fromLatin1("segment_time")] = 4; avfopt[QString::fromLatin1("segment_list_size")] = 0; avfopt[QString::fromLatin1("segment_list")] = outFile.left(outFile.lastIndexOf(QLatin1Char('/'))+1).arg(QString::fromLatin1("index.m3u8")); avfopt[QString::fromLatin1("segment_format")] = QString::fromLatin1("mpegts"); muxopt[QString::fromLatin1("avformat")] = avfopt; AVPlayer player; player.setFile(file); player.setFrameRate(10000.0); // as fast as possible. FIXME: why 1000 may block source player? player.audio()->setBackends(QStringList() << QString::fromLatin1("null")); AVTranscoder avt; if (ss > 0) avt.setStartTime(ss); avt.setMediaSource(&player); avt.setOutputMedia(outFile); avt.setOutputOptions(muxopt); if (!fmt.isEmpty()) avt.setOutputFormat(fmt); // segment, image2 if (!avt.createVideoEncoder()) { qWarning("Failed to create video encoder"); return 1; } VideoEncoder *venc = avt.videoEncoder(); venc->setCodecName(cv); // "png" venc->setBitRate(1024*1024); if (!hwdev.isEmpty()) venc->setProperty("hwdevice", hwdev); if (fmt == QLatin1String("image2")) venc->setPixelFormat(VideoFormat::Format_RGBA32); if (an) { avt.sourcePlayer()->setAudioStream(-1); } else { if (!avt.createAudioEncoder()) { qWarning("Failed to create audio encoder"); return 1; } AudioEncoder *aenc = avt.audioEncoder(); aenc->setCodecName(ca); } QObject::connect(&avt, SIGNAL(stopped()), qApp, SLOT(quit())); avt.setAsync(async); avt.start(); //start transcoder first player.play(); return a.exec(); } QtAV-1.12.0/examples/simpletranscode/simpletranscode.pro000066400000000000000000000001731312235004300233300ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) SOURCES += main.cpp QtAV-1.12.0/examples/videocapture/000077500000000000000000000000001312235004300167075ustar00rootroot00000000000000QtAV-1.12.0/examples/videocapture/main.cpp000066400000000000000000000022221312235004300203350ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include "playerwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); PlayerWindow player; player.show(); player.resize(800, 600); return a.exec(); } QtAV-1.12.0/examples/videocapture/playerwindow.cpp000066400000000000000000000072231312235004300221430ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "playerwindow.h" #include #include #include #include #include #include #include using namespace QtAV; PlayerWindow::PlayerWindow(QWidget *parent) : QWidget(parent) { QtAV::Widgets::registerRenderers(); setWindowTitle(QString::fromLatin1("QtAV simple player example")); m_player = new AVPlayer(this); QVBoxLayout *vl = new QVBoxLayout(); setLayout(vl); m_vo = new VideoOutput(this); if (!m_vo->widget()) { QMessageBox::warning(0, QString::fromLatin1("QtAV error"), QString::fromLatin1("Can not create video renderer")); return; } m_player->setRenderer(m_vo); vl->addWidget(m_vo->widget()); m_slider = new QSlider(); m_slider->setOrientation(Qt::Horizontal); connect(m_slider, SIGNAL(sliderMoved(int)), SLOT(seek(int))); connect(m_player, SIGNAL(positionChanged(qint64)), SLOT(updateSlider())); connect(m_player, SIGNAL(started()), SLOT(updateSlider())); vl->addWidget(m_slider); QHBoxLayout *hb = new QHBoxLayout(); vl->addLayout(hb); m_openBtn = new QPushButton(tr("Open")); m_captureBtn = new QPushButton(tr("Capture video frame")); hb->addWidget(m_openBtn); hb->addWidget(m_captureBtn); m_preview = new QLabel(tr("Capture preview")); m_preview->setFixedSize(200, 150); hb->addWidget(m_preview); connect(m_openBtn, SIGNAL(clicked()), SLOT(openMedia())); connect(m_captureBtn, SIGNAL(clicked()), SLOT(capture())); connect(m_player->videoCapture(), SIGNAL(imageCaptured(QImage)), SLOT(updatePreview(QImage))); connect(m_player->videoCapture(), SIGNAL(saved(QString)), SLOT(onCaptureSaved(QString))); connect(m_player->videoCapture(), SIGNAL(failed()), SLOT(onCaptureError())); } void PlayerWindow::openMedia() { QString file = QFileDialog::getOpenFileName(0, tr( "Open a video")); if (file.isEmpty()) return; m_player->play(file); } void PlayerWindow::seek(int pos) { if (!m_player->isPlaying()) return; m_player->seek(pos*1000LL); // to msecs } void PlayerWindow::updateSlider() { m_slider->setRange(0, int(m_player->duration()/1000LL)); m_slider->setValue(int(m_player->position()/1000LL)); } void PlayerWindow::updatePreview(const QImage &image) { m_preview->setPixmap(QPixmap::fromImage(image).scaled(m_preview->size())); } void PlayerWindow::capture() { //m_player->captureVideo(); m_player->videoCapture()->capture(); } void PlayerWindow::onCaptureSaved(const QString &path) { setWindowTitle(tr("saved to: ") + path); } void PlayerWindow::onCaptureError() { QMessageBox::warning(0, QString::fromLatin1("QtAV video capture"), tr("Failed to capture video frame")); } QtAV-1.12.0/examples/videocapture/playerwindow.h000066400000000000000000000032301312235004300216020ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef PLAYERWINDOW_H #define PLAYERWINDOW_H #include #include QT_BEGIN_NAMESPACE class QSlider; class QPushButton; class QLabel; class QCheckBox; QT_END_NAMESPACE class PlayerWindow : public QWidget { Q_OBJECT public: explicit PlayerWindow(QWidget *parent = 0); public Q_SLOTS: void openMedia(); void seek(int); void capture(); private Q_SLOTS: void updateSlider(); void updatePreview(const QImage& image); void onCaptureSaved(const QString& path); void onCaptureError(); private: QtAV::VideoOutput *m_vo; QtAV::AVPlayer *m_player; QSlider *m_slider; QPushButton *m_openBtn; QPushButton *m_captureBtn; QLabel *m_preview; }; #endif // PLAYERWINDOW_H QtAV-1.12.0/examples/videocapture/videocapture.pro000066400000000000000000000004671312235004300221320ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle greaterThan(QT_MAJOR_VERSION, 4): QT += widgets PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp \ playerwindow.cpp HEADERS += \ playerwindow.h QtAV-1.12.0/examples/videographicsitem/000077500000000000000000000000001312235004300177235ustar00rootroot00000000000000QtAV-1.12.0/examples/videographicsitem/main.cpp000066400000000000000000000011421312235004300213510ustar00rootroot00000000000000#include "videoplayer.h" #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QTranslator ts; if (ts.load(qApp->applicationDirPath().append(QLatin1String("/i18n/QtAV_")).append(QLocale::system().name()))) a.installTranslator(&ts); QTranslator qtts; if (qtts.load(QString::fromLatin1("qt_") + QLocale::system().name())) a.installTranslator(&qtts); VideoPlayer w; w.show(); if (a.arguments().size() > 1) w.play(a.arguments().last()); return a.exec(); } QtAV-1.12.0/examples/videographicsitem/videographicsitem.pro000066400000000000000000000005721312235004300241570ustar00rootroot00000000000000greaterThan(QT_MAJOR_VERSION, 4): QT += widgets contains(QT_CONFIG, opengl): QT += opengl CONFIG -= app_bundle TARGET = videographicsitem TEMPLATE = app PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp \ videoplayer.cpp HEADERS += videoplayer.h QtAV-1.12.0/examples/videographicsitem/videoplayer.cpp000066400000000000000000000075741312235004300227670ustar00rootroot00000000000000/****************************************************************************** this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "videoplayer.h" #include #ifndef QT_NO_OPENGL #include #endif #include #include #include #include #include #include using namespace QtAV; VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent) , videoItem(0) { videoItem = new GraphicsItemRenderer; videoItem->resizeRenderer(640, 360); videoItem->setOutAspectRatioMode(VideoRenderer::VideoAspectRatio); QGraphicsScene *scene = new QGraphicsScene(this); scene->addItem(videoItem); view = new QGraphicsView(scene); QSlider *rotateSlider = new QSlider(Qt::Horizontal); rotateSlider->setRange(-180, 180); rotateSlider->setValue(0); QSlider *scaleSlider = new QSlider(Qt::Horizontal); scaleSlider->setRange(0, 200); scaleSlider->setValue(100); QDial *orientation = new QDial(); orientation->setRange(0, 3); orientation->setValue(0); connect(orientation, SIGNAL(valueChanged(int)), SLOT(setOrientation(int))); connect(rotateSlider, SIGNAL(valueChanged(int)), SLOT(rotateVideo(int))); connect(scaleSlider, SIGNAL(valueChanged(int)), SLOT(scaleVideo(int))); QPushButton *openBtn = new QPushButton; openBtn->setText(tr("Open")); connect(openBtn, SIGNAL(clicked()), SLOT(open())); QCheckBox *glBox = new QCheckBox(); glBox->setText(QString::fromLatin1("OpenGL")); glBox->setChecked(false); connect(glBox, SIGNAL(toggled(bool)), SLOT(setOpenGL(bool))); QHBoxLayout *hb = new QHBoxLayout; hb->addWidget(glBox); hb->addWidget(openBtn); hb->addWidget(orientation); QBoxLayout *layout = new QVBoxLayout; layout->addWidget(view); layout->addWidget(rotateSlider); layout->addWidget(scaleSlider); layout->addLayout(hb); setLayout(layout); mediaPlayer.setRenderer(videoItem); } VideoPlayer::~VideoPlayer() { } void VideoPlayer::play(const QString &file) { mediaPlayer.play(file); } void VideoPlayer::setOpenGL(bool o) { videoItem->setOpenGL(o); if (!o) { view->setViewport(0); return; } #ifndef QT_NO_OPENGL QGLWidget *glw = new QGLWidget();//QGLFormat(QGL::SampleBuffers)); glw->setAutoFillBackground(false); view->setViewport(glw); view->setCacheMode(QGraphicsView::CacheNone); #endif } void VideoPlayer::setOrientation(int value) { videoItem->setOrientation(value*90); } void VideoPlayer::rotateVideo(int angle) { //rotate around the center of video element qreal x = videoItem->boundingRect().width() / 2.0; qreal y = videoItem->boundingRect().height() / 2.0; videoItem->setTransform(QTransform().translate(x, y).rotate(angle).translate(-x, -y)); } void VideoPlayer::scaleVideo(int value) { qreal v = (qreal)value/100.0; videoItem->setTransform(QTransform().scale(v, v)); } void VideoPlayer::open() { QString f = QFileDialog::getOpenFileName(0, tr("Open a video")); if (f.isEmpty()) return; play(f); } QtAV-1.12.0/examples/videographicsitem/videoplayer.h000066400000000000000000000032211312235004300224150ustar00rootroot00000000000000/****************************************************************************** this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef QTAV_VIDEOPLAYER_H #define QTAV_VIDEOPLAYER_H #include #include #include QT_BEGIN_NAMESPACE class QGraphicsView; QT_END_NAMESPACE class VideoPlayer : public QWidget { Q_OBJECT public: VideoPlayer(QWidget *parent = 0); ~VideoPlayer(); QSize sizeHint() const { return QSize(720, 640); } void play(const QString& file); public slots: void setOpenGL(bool o = true); private slots: void setOrientation(int value); void rotateVideo(int angle); void scaleVideo(int value); void open(); private: QtAV::AVPlayer mediaPlayer; QtAV::GraphicsItemRenderer *videoItem; QGraphicsView *view; }; #endif //QTAV_VIDEOPLAYER_H QtAV-1.12.0/examples/videogroup/000077500000000000000000000000001312235004300164005ustar00rootroot00000000000000QtAV-1.12.0/examples/videogroup/main.cpp000066400000000000000000000044421312235004300200340ustar00rootroot00000000000000/****************************************************************************** VideoWall: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include "videogroup.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); int r = 3, c = 3; int idx = 0; if ((idx = a.arguments().indexOf(QLatin1String("-r"))) > 0) r = a.arguments().at(idx + 1).toInt(); if ((idx = a.arguments().indexOf(QLatin1String("-c"))) > 0) c = a.arguments().at(idx + 1).toInt(); QString vo; idx = a.arguments().indexOf(QLatin1String("-vo")); if (idx > 0) { vo = a.arguments().at(idx+1); } else { QString exe(a.arguments().at(0)); qDebug("exe: %s", exe.toUtf8().constData()); int i = exe.lastIndexOf(QLatin1Char('-')); if (i > 0) { vo = exe.mid(i+1, exe.indexOf(QLatin1Char('.')) - i - 1); } } qDebug("vo: %s", vo.toUtf8().constData()); vo = vo.toLower(); if (vo != QLatin1String("gl") && vo != QLatin1String("d2d") && vo != QLatin1String("gdi") && vo != QLatin1String("xv")) vo = QString::fromLatin1("qpainter"); VideoGroup wall; wall.setVideoRendererTypeString(vo); wall.setRows(r); wall.setCols(c); wall.show(); QString file; if (a.arguments().size() > 1) file = a.arguments().last(); if (QFile(file).exists()) { wall.play(file); } return a.exec(); } QtAV-1.12.0/examples/videogroup/videogroup.cpp000066400000000000000000000202151312235004300212670ustar00rootroot00000000000000/****************************************************************************** VideoGroup: this file is part of QtAV examples Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "videogroup.h" #include #include #include #include #include #include #include #include using namespace QtAV; VideoGroup::VideoGroup(QObject *parent) : QObject(parent) , m1Window(false) , m1Frame(true) , mFrameless(false) , r(3),c(3),view(0) , vid(QString::fromLatin1("qpainter")) { mpPlayer = new AVPlayer(this); //mpPlayer->setPlayerEventFilter(0); mpBar = new QWidget(0, Qt::WindowStaysOnTopHint); mpBar->setMaximumSize(400, 60); mpBar->show(); mpBar->setLayout(new QHBoxLayout); mpOpen = new QPushButton(tr("Open")); mpPlay = new QPushButton(tr("Play")); mpStop = new QPushButton(tr("Stop")); mpPause = new QPushButton(tr("Pause")); mpPause->setCheckable(true); mpAdd = new QPushButton(QString::fromLatin1("+")); mpRemove = new QPushButton(QString::fromLatin1("-")); mp1Window = new QPushButton(tr("Single Window")); mp1Window->setCheckable(true); mp1Frame = new QPushButton(tr("Single Frame")); mp1Frame->setCheckable(true); mp1Frame->setChecked(true); mpFrameless = new QPushButton(tr("no window frame")); mpFrameless->setCheckable(true); connect(mpOpen, SIGNAL(clicked()), SLOT(openLocalFile())); connect(mpPlay, SIGNAL(clicked()), mpPlayer, SLOT(play())); connect(mpStop, SIGNAL(clicked()), mpPlayer, SLOT(stop())); connect(mpPause, SIGNAL(toggled(bool)), mpPlayer, SLOT(pause(bool))); connect(mpAdd, SIGNAL(clicked()), SLOT(addRenderer())); connect(mpRemove, SIGNAL(clicked()), SLOT(removeRenderer())); connect(mp1Window, SIGNAL(toggled(bool)), SLOT(setSingleWindow(bool))); connect(mp1Frame, SIGNAL(toggled(bool)), SLOT(toggleSingleFrame(bool))); connect(mpFrameless, SIGNAL(toggled(bool)), SLOT(toggleFrameless(bool))); mpBar->layout()->addWidget(mpOpen); mpBar->layout()->addWidget(mpPlay); mpBar->layout()->addWidget(mpStop); mpBar->layout()->addWidget(mpPause); mpBar->layout()->addWidget(mpAdd); mpBar->layout()->addWidget(mpRemove); mpBar->layout()->addWidget(mp1Window); mpBar->layout()->addWidget(mp1Frame); //mpBar->layout()->addWidget(mpFrameless); mpBar->resize(200, 25); } VideoGroup::~VideoGroup() { delete view; delete mpBar; } void VideoGroup::setSingleWindow(bool s) { m1Window = s; mRenderers = mpPlayer->videoOutputs(); if (mRenderers.isEmpty()) return; if (!s) { if (!view) return; foreach(VideoRenderer *vo, mRenderers) { view->layout()->removeWidget(vo->widget()); vo->widget()->setParent(0); Qt::WindowFlags wf = vo->widget()->windowFlags(); if (mFrameless) { wf &= ~Qt::FramelessWindowHint; } else { wf |= Qt::FramelessWindowHint; } vo->widget()->setWindowFlags(wf); vo->widget()->show(); } delete view; view = 0; } else { if (view) return; view = new QWidget; view->resize(qApp->desktop()->size()); QGridLayout *layout = new QGridLayout; layout->setSizeConstraint(QLayout::SetMaximumSize); layout->setSpacing(1); layout->setMargin(0); layout->setContentsMargins(0, 0, 0, 0); view->setLayout(layout); for (int i = 0; i < mRenderers.size(); ++i) { int x = i/cols(); int y = i%cols(); ((QGridLayout*)view->layout())->addWidget(mRenderers.at(i)->widget(), x, y); } view->show(); mRenderers.last()->widget()->showFullScreen(); } } void VideoGroup::toggleSingleFrame(bool s) { if (m1Frame == s) return; m1Frame = s; updateROI(); } void VideoGroup::toggleFrameless(bool f) { mFrameless = f; if (mRenderers.isEmpty()) return; VideoRenderer *renderer = mRenderers.first(); Qt::WindowFlags wf = renderer->widget()->windowFlags(); if (f) { wf &= ~Qt::FramelessWindowHint; } else { wf |= Qt::FramelessWindowHint; } foreach (VideoRenderer *rd, mRenderers) { rd->widget()->setWindowFlags(wf); } } void VideoGroup::setVideoRendererTypeString(const QString &vt) { vid = vt.toLower(); } void VideoGroup::setRows(int n) { r = n; } void VideoGroup::setCols(int n) { c = n; } int VideoGroup::rows() const { return r; } int VideoGroup::cols() const { return c; } void VideoGroup::show() { for (int i = 0; i < r; ++i) { for (int j = 0; j < c; ++j) { addRenderer(); } } } void VideoGroup::play(const QString &file) { mpPlayer->play(file); } void VideoGroup::openLocalFile() { QString file = QFileDialog::getOpenFileName(0, tr("Open a video")); if (file.isEmpty()) return; mpPlayer->stop(); mpPlayer->play(file); } void VideoGroup::addRenderer() { VideoRendererId v = VideoRendererId_Widget; if (vid == QLatin1String("gl")) v = VideoRendererId_GLWidget2; else if (vid == QLatin1String("d2d")) v = VideoRendererId_Direct2D; else if (vid == QLatin1String("gdi")) v = VideoRendererId_GDI; else if (vid == QLatin1String("xv")) v = VideoRendererId_XV; VideoRenderer* renderer = VideoRenderer::create(v); mRenderers = mpPlayer->videoOutputs(); mRenderers.append(renderer); renderer->widget()->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags wf = renderer->widget()->windowFlags(); if (mFrameless) { wf &= ~Qt::FramelessWindowHint; } else { wf |= Qt::FramelessWindowHint; } renderer->widget()->setWindowFlags(wf); int w = view ? view->frameGeometry().width()/c : qApp->desktop()->width()/c; int h = view ? view->frameGeometry().height()/r : qApp->desktop()->height()/r; renderer->widget()->resize(w, h); mpPlayer->addVideoRenderer(renderer); int i = (mRenderers.size()-1)/cols(); int j = (mRenderers.size()-1)%cols(); if (view) { view->resize(qApp->desktop()->size()); ((QGridLayout*)view->layout())->addWidget(renderer->widget(), i, j); view->show(); } else { renderer->widget()->move(j*w, i*h); renderer->widget()->show(); } updateROI(); } void VideoGroup::removeRenderer() { mRenderers = mpPlayer->videoOutputs(); if (mRenderers.isEmpty()) return; VideoRenderer *r = mRenderers.takeLast(); mpPlayer->removeVideoRenderer(r); if (view) { view->layout()->removeWidget(r->widget()); } delete r; updateROI(); } void VideoGroup::updateROI() { if (mRenderers.isEmpty()) return; if (!m1Frame) { foreach (VideoRenderer *renderer, mRenderers) { renderer->setRegionOfInterest(0, 0, 0, 0); } return; } int W = view ? view->frameGeometry().width() : qApp->desktop()->width(); int H = view ? view->frameGeometry().height() : qApp->desktop()->height(); int w = W / c; int h = H / r; for (int i = 0; i < mRenderers.size(); ++i) { VideoRenderer *renderer = mRenderers.at(i); renderer->setRegionOfInterest(qreal((i%c)*w)/qreal(W), qreal((i/c)*h)/qreal(H), qreal(w)/qreal(W), qreal(h)/qreal(H)); } } QtAV-1.12.0/examples/videogroup/videogroup.h000066400000000000000000000037651312235004300207470ustar00rootroot00000000000000/****************************************************************************** VideoGroup: this file is part of QtAV examples Copyright (C) 2013-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef QTAV_VIDEOWALL_H #define QTAV_VIDEOWALL_H #include #include #include #include class VideoGroup : public QObject { Q_OBJECT public: explicit VideoGroup(QObject *parent = 0); ~VideoGroup(); void setVideoRendererTypeString(const QString& vt); void setRows(int n); void setCols(int n); int rows() const; int cols() const; void show(); void play(const QString& file); void updateROI(); public slots: void setSingleWindow(bool s); void toggleSingleFrame(bool s); void toggleFrameless(bool f); void openLocalFile(); void addRenderer(); void removeRenderer(); private: bool m1Window; bool m1Frame; bool mFrameless; int r, c; int timer_id; QtAV::AVPlayer *mpPlayer; QWidget *view; QWidget *mpBar; QPushButton *mpAdd, *mpRemove, *mpOpen, *mpPlay, *mpStop, *mpPause, *mp1Window; QPushButton *mp1Frame; QPushButton *mpFrameless; QString vid; QList mRenderers; }; #endif // QTAV_VIDEOWALL_H QtAV-1.12.0/examples/videogroup/videogroup.pro000066400000000000000000000004751312235004300213130ustar00rootroot00000000000000TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle TARGET = videogroup PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp videogroup.cpp HEADERS += videogroup.h QtAV-1.12.0/examples/videowall/000077500000000000000000000000001312235004300162035ustar00rootroot00000000000000QtAV-1.12.0/examples/videowall/VideoWall.cpp000066400000000000000000000303731312235004300206030ustar00rootroot00000000000000/****************************************************************************** VideoWall: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "VideoWall.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QtAV; const int kSyncInterval = 2000; VideoWall::VideoWall(QObject *parent) : QObject(parent),r(3),c(3),view(0),menu(0) , vid(QString::fromLatin1("qpainter")) { QtAV::Widgets::registerRenderers(); clock = new AVClock(this); clock->setClockType(AVClock::ExternalClock); view = new QWidget; if (view) { qDebug("WA_OpaquePaintEvent=%d", view->testAttribute(Qt::WA_OpaquePaintEvent)); view->resize(qApp->desktop()->size()); view->move(QPoint(0, 0)); view->show(); } view->installEventFilter(this); } VideoWall::~VideoWall() { if (menu) { delete menu; menu = 0; } if (!players.isEmpty()) { foreach (AVPlayer *player, players) { player->stop(); VideoRenderer* renderer = player->renderer(); if (renderer->widget()) { renderer->widget()->close(); //TODO: rename if (!renderer->widget()->testAttribute(Qt::WA_DeleteOnClose) && !renderer->widget()->parent()) delete renderer; delete player; } } players.clear(); } delete view; } void VideoWall::setVideoRendererTypeString(const QString &vt) { vid = vt.toLower(); } void VideoWall::setRows(int n) { r = n; } void VideoWall::setCols(int n) { c = n; } int VideoWall::rows() const { return r; } int VideoWall::cols() const { return c; } void VideoWall::show() { if (!players.isEmpty()) { foreach (AVPlayer *player, players) { player->stop(); VideoRenderer* renderer = player->renderer(); if (renderer->widget()) { renderer->widget()->close(); if (!renderer->widget()->testAttribute(Qt::WA_DeleteOnClose) && !renderer->widget()->parent()) delete renderer; delete player; } } players.clear(); } qDebug("show wall: %d x %d", r, c); int w = view ? view->frameGeometry().width()/c : qApp->desktop()->width()/c; int h = view ? view->frameGeometry().height()/r : qApp->desktop()->height()/r; if (view) { QGridLayout *layout = new QGridLayout; layout->setSizeConstraint(QLayout::SetMaximumSize); layout->setSpacing(1); layout->setMargin(0); layout->setContentsMargins(0, 0, 0, 0); view->setLayout(layout); } VideoRendererId v = VideoRendererId_Widget; if (vid == QLatin1String("gl")) v = VideoRendererId_GLWidget2; else if (vid == QLatin1String("opengl")) v = VideoRendererId_OpenGLWidget; else if (vid == QLatin1String("d2d")) v = VideoRendererId_Direct2D; else if (vid == QLatin1String("gdi")) v = VideoRendererId_GDI; else if (vid == QLatin1String("x11")) v = VideoRendererId_X11; else if (vid == QLatin1String("xv")) v = VideoRendererId_XV; for (int i = 0; i < r; ++i) { for (int j = 0; j < c; ++j) { VideoRenderer* renderer = VideoRenderer::create(v); renderer->widget()->setWindowFlags(renderer->widget()->windowFlags()| Qt::FramelessWindowHint); renderer->widget()->setAttribute(Qt::WA_DeleteOnClose); renderer->widget()->resize(w, h); renderer->widget()->move(j*w, i*h); AVPlayer *player = new AVPlayer; player->setRenderer(renderer); connect(player, SIGNAL(started()), SLOT(changeClockType())); players.append(player); if (view) ((QGridLayout*)view->layout())->addWidget(renderer->widget(), i, j); } } } void VideoWall::play(const QString &file) { if (players.isEmpty()) return; clock->reset(); clock->start(); foreach (AVPlayer *player, players) { player->play(file); } timer_id = startTimer(kSyncInterval); } void VideoWall::stop() { clock->reset(); killTimer(timer_id); foreach (AVPlayer* player, players) { player->stop(); //check playing? } } void VideoWall::openLocalFile() { QString file = QFileDialog::getOpenFileName(0, tr("Open a video")); if (file.isEmpty()) return; stop(); clock->reset(); clock->start(); timer_id = startTimer(kSyncInterval); foreach (AVPlayer* player, players) { player->setFile(file); //TODO: load all players before play player->play(); } } void VideoWall::openUrl() { QString url = QInputDialog::getText(0, tr("Open an url"), tr("Url")); if (url.isEmpty()) return; stop(); clock->reset(); clock->start(); timer_id = startTimer(kSyncInterval); foreach (AVPlayer* player, players) { player->setFile(url); player->play(); //TODO: load all players before play } } void VideoWall::about() { QMessageBox::about(0, tr("About QtAV"), QString::fromLatin1("

%1

\n\n%2") .arg(tr("This is a demo for playing and synchronising multiple players")) .arg(aboutQtAV_HTML())); } void VideoWall::help() { QMessageBox::about(0, tr("Help"), tr("Command line: %1 [-r rows=3] [-c cols=3] path/of/video\n").arg(qApp->applicationFilePath()) + tr("Drag and drop a file to player\n") + tr("Shortcut:\n") + tr("Space: pause/continue\n") + tr("N: show next frame. Continue the playing by pressing 'Space'\n") + tr("O: open a file\n") + tr("P: replay\n") + tr("S: stop\n") + tr("M: mute on/off\n") + tr("C: capture video") + tr("Up/Down: volume +/-\n") + tr("->/<-: seek forward/backward\n")); } bool VideoWall::eventFilter(QObject *watched, QEvent *event) { //qDebug("EventFilter::eventFilter to %p", watched); Q_UNUSED(watched); if (players.isEmpty()) return false; QEvent::Type type = event->type(); switch (type) { case QEvent::KeyPress: { //qDebug("Event target = %p %p", watched, player->renderer); //avoid receive an event multiple times QKeyEvent *key_event = static_cast(event); int key = key_event->key(); Qt::KeyboardModifiers modifiers = key_event->modifiers(); switch (key) { case Qt::Key_F: { QWidget *w = qApp->activeWindow(); if (!w) return false; if (w->isFullScreen()) w->showNormal(); else w->showFullScreen(); } break; case Qt::Key_N: //check playing? foreach (AVPlayer* player, players) { player->stepForward(); } break; case Qt::Key_O: { if (modifiers == Qt::ControlModifier) { openLocalFile(); return true; } else/* if (m == Qt::NoModifier) */{ return false; } } break; case Qt::Key_P: clock->reset(); clock->start(); foreach (AVPlayer* player, players) { player->play(); } timer_id = startTimer(kSyncInterval); break; case Qt::Key_S: stop(); break; case Qt::Key_Space: //check playing? clock->pause(!clock->isPaused()); foreach (AVPlayer* player, players) { player->pause(!player->isPaused()); } break; case Qt::Key_Up: foreach (AVPlayer* player, players) { if (player->audio()) { qreal v = player->audio()->volume(); if (v > 0.5) v += 0.1; else if (v > 0.1) v += 0.05; else v += 0.025; player->audio()->setVolume(v); } } break; case Qt::Key_Down: foreach (AVPlayer* player, players) { if (player->audio()) { qreal v = player->audio()->volume(); if (v > 0.5) v -= 0.1; else if (v > 0.1) v -= 0.05; else v -= 0.025; player->audio()->setVolume(v); } } break; case Qt::Key_Left: { qDebug("<-"); const qint64 newPos = clock->value()*1000.0 - 2000.0; clock->updateExternalClock(newPos); foreach (AVPlayer* player, players) { player->setPosition(newPos); } } break; case Qt::Key_Right: { qDebug("->"); const qint64 newPos = clock->value()*1000.0 + 2000.0; clock->updateExternalClock(newPos); foreach (AVPlayer* player, players) { player->setPosition(newPos); } } break; case Qt::Key_M: foreach (AVPlayer* player, players) { if (player->audio()) { player->audio()->setMute(!player->audio()->isMute()); } } break; default: return false; } break; } case QEvent::ContextMenu: { QContextMenuEvent *e = static_cast(event); if (!menu) { menu = new QMenu(); menu->addAction(tr("Open"), this, SLOT(openLocalFile())); menu->addAction(tr("Open Url"), this, SLOT(openUrl())); menu->addSeparator(); menu->addAction(tr("About"), this, SLOT(about())); menu->addAction(tr("Help"), this, SLOT(help())); menu->addSeparator(); menu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); } menu->popup(e->globalPos()); menu->exec(); } case QEvent::DragEnter: case QEvent::DragMove: { QDropEvent *e = static_cast(event); e->acceptProposedAction(); } break; case QEvent::Drop: { QDropEvent *e = static_cast(event); QString path = e->mimeData()->urls().first().toLocalFile(); stop(); play(path); e->acceptProposedAction(); } break; default: return false; } return true; //false: for text input } void VideoWall::timerEvent(QTimerEvent *e) { if (e->timerId() != timer_id) { qDebug("Not clock id"); return; } if (!clock->isActive()) { qDebug("clock not running"); return; } foreach (AVPlayer *player, players) { player->masterClock()->updateExternalClock(*clock); } } void VideoWall::changeClockType() { AVPlayer* player = qobject_cast(sender()); player->masterClock()->setClockAuto(false); player->masterClock()->setClockType(AVClock::ExternalClock); } QtAV-1.12.0/examples/videowall/VideoWall.h000066400000000000000000000036021312235004300202430ustar00rootroot00000000000000/****************************************************************************** VideoWall: this file is part of QtAV examples Copyright (C) 2013-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #ifndef QTAV_VIDEOWALL_H #define QTAV_VIDEOWALL_H #include #include #include QT_BEGIN_NAMESPACE class QMenu; QT_END_NAMESPACE class VideoWall : public QObject { Q_OBJECT public: explicit VideoWall(QObject *parent = 0); ~VideoWall(); void setVideoRendererTypeString(const QString& vt); void setRows(int n); void setCols(int n); int rows() const; int cols() const; void show(); void play(const QString& file); public slots: void stop(); void openLocalFile(); void openUrl(); void about(); void help(); protected: virtual bool eventFilter(QObject *, QEvent *); virtual void timerEvent(QTimerEvent *e); private Q_SLOTS: void changeClockType(); private: int r, c; int timer_id; QtAV::AVClock *clock; QList players; QWidget *view; QMenu *menu; QString vid; }; #endif // QTAV_VIDEOWALL_H QtAV-1.12.0/examples/videowall/main.cpp000066400000000000000000000042121312235004300176320ustar00rootroot00000000000000/****************************************************************************** VideoWall: this file is part of QtAV examples Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include "VideoWall.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); int r = 3, c = 3; int idx = 0; if ((idx = a.arguments().indexOf(QLatin1String("-r"))) > 0) r = a.arguments().at(idx + 1).toInt(); if ((idx = a.arguments().indexOf(QLatin1String("-c"))) > 0) c = a.arguments().at(idx + 1).toInt(); QString vo; idx = a.arguments().indexOf(QLatin1String("-vo")); if (idx > 0) { vo = a.arguments().at(idx+1); } else { QString exe(a.arguments().at(0)); qDebug("exe: %s", exe.toUtf8().constData()); int i = exe.lastIndexOf(QLatin1Char('-')); if (i > 0) { vo = exe.mid(i+1, exe.indexOf(QLatin1Char('.')) - i - 1); } } qDebug("vo: %s", vo.toUtf8().constData()); VideoWall wall; wall.setVideoRendererTypeString(vo.toLower()); wall.setRows(r); wall.setCols(c); wall.show(); QString file; if (a.arguments().size() > 1) file = a.arguments().last(); if (QFile(file).exists()) { wall.play(file); } else { wall.help(); } return a.exec(); } QtAV-1.12.0/examples/videowall/videowall.pro000066400000000000000000000004721312235004300207160ustar00rootroot00000000000000TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle TARGET = videowall PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp VideoWall.cpp HEADERS += VideoWall.h QtAV-1.12.0/examples/window/000077500000000000000000000000001312235004300155245ustar00rootroot00000000000000QtAV-1.12.0/examples/window/main.cpp000066400000000000000000000036631312235004300171640ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include using namespace QtAV; class PlayerWindow : public OpenGLWindowRenderer { public: PlayerWindow(QWindow* parent = 0) : OpenGLWindowRenderer(NoPartialUpdate, parent) { player.setRenderer(this); } void play(const QString& file) { setTitle(file); player.setFile(file); player.play(); } protected: virtual void keyPressEvent(QKeyEvent *e) { int key = e->key(); if (key == Qt::Key_Space) { player.togglePause(); } else if (key == Qt::Key_Left) { player.seekBackward(); } else if (key == Qt::Key_Right) { player.seekForward(); } } private: AVPlayer player; }; int main(int argc, char *argv[]) { QGuiApplication a(argc, argv); if (a.arguments().size() < 2) { qDebug("./window file"); return 0; } PlayerWindow win; win.resize(600, 400); win.show(); win.play(a.arguments().at(1)); return a.exec(); } QtAV-1.12.0/examples/window/window.pro000066400000000000000000000002521312235004300175540ustar00rootroot00000000000000TEMPLATE = app CONFIG -= app_bundle QT = core gui PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/gpl-3.0.txt000066400000000000000000001045131312235004300142240ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . QtAV-1.12.0/lgpl-2.1.txt000066400000000000000000000636421312235004300144070ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! QtAV-1.12.0/msys2/000077500000000000000000000000001312235004300134545ustar00rootroot00000000000000QtAV-1.12.0/msys2/PKGBUILD000066400000000000000000000022501312235004300145770ustar00rootroot00000000000000realname=qtav pkgname="${MINGW_PACKAGE_PREFIX}-${realname}" pkgver=r3368.39e6eafe pkgrel=1 pkgdesc="Multimedia framework based on Qt and FFmpeg (mingw-w64)" arch=('any') license=('LGPL') depends=("${MINGW_PACKAGE_PREFIX}-uchardet" "${MINGW_PACKAGE_PREFIX}-ffmpeg" "${MINGW_PACKAGE_PREFIX}-openal" "${MINGW_PACKAGE_PREFIX}-qt5") makedepends=('git') source=("${realname}::git://github.com/wang-bin/QtAV.git") md5sums=('SKIP') pkgver() { cd "${srcdir}/${realname}" printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" } prepare() { cd "${srcdir}/${realname}" git submodule update --init } build() { cd "${srcdir}/${realname}" MSYS2_ARG_CONV_EXCL="-DCMAKE_INSTALL_PREFIX=" \ cmake \ -G"MSYS Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="${MINGW_PREFIX}" \ -DBUILD_EXAMPLES=OFF \ -DBUILD_PLAYERS=ON \ -DBUILD_TESTS=OFF \ ../${realname} make } package() { cd "${srcdir}/${realname}" make DESTDIR="${pkgdir}" install mkdir -p "${pkgdir}${MINGW_PREFIX}/share/licenses/${realname}" cp lgpl-2.1.txt "${pkgdir}${MINGW_PREFIX}/share/licenses/${realname}/LICENSE" } QtAV-1.12.0/pack.pri000066400000000000000000000054531312235004300140400ustar00rootroot00000000000000#Designed by Wang Bin(Lucas Wang). 2013 ##TODO: Why use function to add makefile target failed #defineTest(packageSet) { # isEmpty(1): warning("packageSet(version [, name]") # PACKAGE_VERSION = $$1 # isEmpty(2): PACKAGE_NAME = $$basename(_PRO_FILE_PWD_) # else: PACKAGE_NAME = $$2 symbian { TARGET.UID3 = 0xea6f847b # TARGET.CAPABILITY += TARGET.EPOCSTACKSIZE = 0x14000 TARGET.EPOCHEAPSIZE = 0x020000 0x800000 } OTHER_FILES += README.md TODO \ qtc_packaging/common/README \ qtc_packaging/common/copyright \ qtc_packaging/common/changelog \ qtc_packaging/debian_harmattan/manifest.aegis \ qtc_packaging/debian_harmattan/control \ qtc_packaging/debian_harmattan/rules \ qtc_packaging/debian_harmattan/compat \ qtc_packaging/debian_harmattan/$${PACKAGE_NAME}.desktop \ qtc_packaging/debian_fremantle/control \ qtc_packaging/debian_fremantle/rules \ qtc_packaging/debian_fremantle/compat \ qtc_packaging/debian_fremantle/$${PACKAGE_NAME}.desktop \ qtc_packaging/debian_generic/control \ qtc_packaging/debian_generic/$${PACKAGE_NAME}.desktop #export(OTHER_FILES) unix { # Add files and directories to ship with the application # by adapting the examples below. # file1.source = myfile # dir1.source = mydir #lang.files = i18n #DEPLOYMENTFOLDERS = lang# file1 dir1 #include(deployment.pri) #qtcAddDeployment() #TODO: add other platform packaging !isEmpty(MEEGO_VERSION_MAJOR): PLATFORM = harmattan #armMEEGO_VERSION_MAJORel else:maemo5: PLATFORM = fremantle else: PLATFORM = generic ARCH = `dpkg --print-architecture` *maemo*: ARCH = $$QT_ARCH #ARCH = $$QT_ARCH #what about harmattan? fakeroot.target = fakeroot fakeroot.depends = FORCE fakeroot.commands = $$quote(rm -rf fakeroot && mkdir -p fakeroot/usr/share/doc/$$PACKAGE_NAME && mkdir -p fakeroot/DEBIAN) fakeroot.commands += $$quote(chmod -R 755 fakeroot) ##control dir must be 755 fakeroot.commands = $$join(fakeroot.commands,$$escape_expand(\\n\\t)) deb.target = deb deb.depends += fakeroot deb.commands += $$quote(make install INSTALL_ROOT=\$\$PWD/fakeroot) deb.commands += $$quote(cd fakeroot; md5sum `find usr -type f |grep -v DEBIAN` > DEBIAN/md5sums; cd -) deb.commands += $$quote(cp $$PWD/qtc_packaging/debian_$${PLATFORM}/control fakeroot/DEBIAN) deb.commands += $$quote(sed -i \"s/%arch%/$${ARCH}/\" fakeroot/DEBIAN/control) deb.commands += $$quote(sed -i \"s/%version%/$${PACKAGE_VERSION}/\" fakeroot/DEBIAN/control) deb.commands += $$quote(gzip -9 fakeroot/usr/share/doc/$$PACKAGE_NAME/changelog) deb.commands += $$quote(dpkg -b fakeroot $${PACKAGE_NAME}_$${PACKAGE_VERSION}_$${PLATFORM}_$${ARCH}.deb) deb.commands = $$join(deb.commands,$$escape_expand(\\n\\t)) #message("deb.commands: $$deb.commands") QMAKE_EXTRA_TARGETS += fakeroot deb #export(QMAKE_EXTRA_TARGETS) #makeDeb($$PACKAGE_VERSION, $$PACKAGE_NAME) } #} #packageSet QtAV-1.12.0/qml/000077500000000000000000000000001312235004300131705ustar00rootroot00000000000000QtAV-1.12.0/qml/CMakeLists.txt000066400000000000000000000051221312235004300157300ustar00rootroot00000000000000set(MODULE QmlAV) set(EXTRA_DEFS -DBUILD_QMLAV_LIB) #URI = QtAV #uri used in QtAVQmlPlugin::registerTypes(uri) #qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI # not sure what moc does set(HEADERS QmlAV/Export.h QmlAV/MediaMetaData.h QmlAV/SGVideoNode.h QmlAV/QQuickItemRenderer.h QmlAV/QuickFilter.h QmlAV/QmlAVPlayer.h QmlAV/QuickSubtitle.h QmlAV/QuickSubtitleItem.h QmlAV/QuickVideoPreview.h ) set(SOURCES plugin.cpp QQuickItemRenderer.cpp SGVideoNode.cpp QmlAVPlayer.cpp QuickFilter.cpp QuickSubtitle.cpp MediaMetaData.cpp QuickSubtitleItem.cpp QuickVideoPreview.cpp ) if(NOT Qt5Qml_VERSION VERSION_LESS 5.2.0) list(APPEND HEADERS QmlAV/QuickFBORenderer.h) list(APPEND SOURCES QuickFBORenderer.cpp) endif() # add HEADERS for moc if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) target_link_libraries(${MODULE} LINK_PRIVATE ${EXTRA_LIBS} LINK_PUBLIC QtAV Qt5::Qml Qt5::Quick ) set_target_properties(${MODULE} PROPERTIES OUTPUT_NAME ${MODULE} CLEAN_DIRECT_OUTPUT 1 #LINK_SEARCH_START_STATIC 1 MACOSX_RPATH ON FRAMEWORK OFF LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/QtAV RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/QtAV ) #http://stackoverflow.com/questions/19866424/cmake-and-qt5-automoc-error #http://doc.qt.io/qt-5/cmake-manual.html set(CMAKE_INCLUDE_CURRENT_DIR ON) # for .moc target_include_directories(${MODULE} PUBLIC $ PUBLIC $ PUBLIC $ PUBLIC $ PRIVATE ${EXTRA_INCLUDE} ) target_compile_definitions(${MODULE} PRIVATE ${EXTRA_DEFS}) set(QML_FILES qmldir Video.qml plugins.qmltypes) add_custom_command(TARGET ${MODULE} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/QtAV COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QML_FILES} ${CMAKE_BINARY_DIR}/bin/QtAV WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) install(TARGETS ${MODULE} EXPORT ${MODULE}-targets RUNTIME DESTINATION ${QTAV_INSTALL_QML}/QtAV LIBRARY DESTINATION ${QTAV_INSTALL_QML}/QtAV ARCHIVE DESTINATION ${QTAV_INSTALL_QML}/QtAV FRAMEWORK DESTINATION ${QTAV_INSTALL_LIBS} ) install(EXPORT ${MODULE}-targets DESTINATION ${QTAV_INSTALL_LIBS}/cmake/${MODULE} FILE ${MODULE}-config.cmake ) install( FILES ${QML_FILES} DESTINATION ${QTAV_INSTALL_QML}/QtAV ) QtAV-1.12.0/qml/MediaMetaData.cpp000066400000000000000000000140561312235004300163220ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/MediaMetaData.h" #include #include #include #include using namespace QtAV; MediaMetaData::MediaMetaData(QObject *parent) : QObject(parent) { } void MediaMetaData::setValuesFromStatistics(const QtAV::Statistics &st) { m_metadata.clear(); //setValue(Size, st.); setValue(Duration, (qint64)QTime(0, 0, 0).msecsTo(st.duration)); setValue(StartTime, (qint64)QTime(0, 0, 0).msecsTo(st.start_time)); if (st.video.available) { setValue(MediaType, QStringLiteral("video")); setValue(VideoFrameRate, st.video.frame_rate); setValue(VideoBitRate, st.video.bit_rate); setValue(VideoCodec, st.video.codec); setValue(Resolution, QSize(st.video_only.width, st.video_only.height)); setValue(PixelFormat, st.video_only.pix_fmt); setValue(VideoFrames, st.video.frames); } if (st.audio.available) { // TODO: what if thumbnail? if (!st.video.available) setValue(MediaType, QStringLiteral("audio")); setValue(AudioBitRate, st.audio.bit_rate); setValue(AudioCodec, st.audio.codec); setValue(ChannelCount, st.audio_only.channels); setValue(SampleRate, st.audio_only.sample_rate); setValue(ChannelLayout, st.audio_only.channel_layout); setValue(SampleFormat, st.audio_only.sample_fmt); } QHash md(st.metadata); if (md.isEmpty()) return; QHash::const_iterator it = md.constBegin(); for (; it != md.constEnd(); ++it) { Key key = fromFFmpegName(it.key()); if ((int)key >= 0) { setValue(key, it.value()); continue; } const QString keyName(it.key().toLower()); if (keyName == QLatin1String("track")) { int slash = it.value().indexOf(QLatin1Char('/')); if (slash < 0) { setValue(TrackNumber, it.value().toInt()); continue; } setValue(TrackNumber, it.value().leftRef(slash).string()->toInt()); //QStringRef.toInt(): Qt5.2 setValue(TrackCount, it.value().midRef(slash+1).string()->toInt()); continue; } if (keyName == QLatin1String("date") || it.key().toLower() == QLatin1String("creation_time") ) { // ISO 8601 is preferred in ffmpeg bool ok = false; int year = it.value().toInt(&ok); if (ok) { //if (year > 1000) //? setValue(Year, year); continue; } setValue(Date, QDate::fromString(it.value(), Qt::ISODate)); continue; } if (keyName.contains(QLatin1String("genre"))) { setValue(Genre, it.value().split(QLatin1Char(','))); // ',' ? not sure continue; } } // qtmm read Size from metadata on win QFile f(st.url); if (f.exists()) setValue(Size, f.size()); } MediaMetaData::Key MediaMetaData::fromFFmpegName(const QString &name) const { typedef struct { Key key; const char* name; } key_t; key_t key_map[] = { { AlbumTitle, "album" }, //? { AlbumArtist, "album_artist" }, { Author, "artist" }, //? { Comment, "comment" }, { Composer, "composer" }, { Copyright, "copyright" }, // { Date, "date" }, //QDate { Language, "language" }, //maybe a list in ffmpeg { Language, "lang" }, { Publisher, "publisher" }, { Title, "title" }, //{ TrackNumber, "track" }, // can be "current/total" //{ TrackCount, "track" }, // can be "current/total" // below are keys not listed in ffmpeg generic tag names and value is a QString { Description, "description" }, //dx { (Key)-1, 0 }, }; for (int i = 0; (int)key_map[i].key >= 0; ++i) { if (name.toLower() == QLatin1String(key_map[i].name)) return key_map[i].key; } // below are keys not listed in ffmpeg generic tag names and value is a QString key_t wm_key[] = { { UserRating, "rating" }, //dx, WM/ { ParentalRating, "parentalrating" }, //dx, WM/ //{ RatingOrganization, "rating_organization" }, { Conductor, "conductor" }, //dx, WM/ { Lyrics, "lyrics" }, //dx, WM/ { Mood, "mood" }, //dx, WM/ { (Key)-1, 0 }, }; for (int i = 0; (int)wm_key[i].key >= 0; ++i) { if (name.toLower().contains(QLatin1String(wm_key[i].name))) return wm_key[i].key; } return (Key)-1; } void MediaMetaData::setValue(Key k, const QVariant &v) { if (value(k) == v) return; m_metadata[k] = v; emit metaDataChanged(); } QVariant MediaMetaData::value(Key k, const QVariant &defaultValue) const { return m_metadata.value(k, defaultValue); } QString MediaMetaData::name(Key k) const { int idx = staticMetaObject.indexOfEnumerator("Key"); const QMetaEnum me = staticMetaObject.enumerator(idx); return QString::fromLatin1(me.valueToKey(k)); } QtAV-1.12.0/qml/QQuickItemRenderer.cpp000066400000000000000000000313441312235004300174040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin theoribeiro * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/QQuickItemRenderer.h" #include "QtCore/QCoreApplication" #include #include #include #include #include #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "QtAV/private/VideoRenderer_p.h" #include "QmlAV/QmlAVPlayer.h" #include "QmlAV/SGVideoNode.h" namespace QtAV { static const VideoRendererId VideoRendererId_QQuickItem = mkid::id32base36_6<'Q','Q','I','t','e','m'>::value; FACTORY_REGISTER(VideoRenderer, QQuickItem, "QQuickItem") class QQuickItemRendererPrivate : public VideoRendererPrivate { public: QQuickItemRendererPrivate(): VideoRendererPrivate() , opengl(true) , frame_changed(false) , fill_mode(QQuickItemRenderer::PreserveAspectFit) , texture(0) , node(0) , source(0) { } virtual ~QQuickItemRendererPrivate() { if (texture) { delete texture; texture = 0; } } virtual void setupQuality() { if (!node) return; if (opengl) return; if (quality == VideoRenderer::QualityFastest) { ((QSGSimpleTextureNode*)node)->setFiltering(QSGTexture::Nearest); } else { ((QSGSimpleTextureNode*)node)->setFiltering(QSGTexture::Linear); } } bool opengl; bool frame_changed; QQuickItemRenderer::FillMode fill_mode; QSGTexture* texture; QSGNode *node; QObject *source; QImage image; QList filters; }; QQuickItemRenderer::QQuickItemRenderer(QQuickItem *parent) : QQuickItem(parent) , VideoRenderer(*new QQuickItemRendererPrivate()) { Q_UNUSED(parent); setFlag(QQuickItem::ItemHasContents, true); connect(this, SIGNAL(windowChanged(QQuickWindow*)), SLOT(handleWindowChange(QQuickWindow*))); } VideoRendererId QQuickItemRenderer::id() const { return VideoRendererId_QQuickItem; } bool QQuickItemRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { if (pixfmt == VideoFormat::Format_RGB48BE || pixfmt == VideoFormat::Format_Invalid ) return false; if (!isOpenGL()) return VideoFormat::isRGB(pixfmt); // TODO: rectangle texture is not supported (VDA) return OpenGLVideo::isSupported(pixfmt); } bool QQuickItemRenderer::event(QEvent *e) { if (e->type() != QEvent::User) return QQuickItem::event(e); update(); return true; } void QQuickItemRenderer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickItem::geometryChanged(newGeometry, oldGeometry); //geometry will be updated DPTR_D(QQuickItemRenderer); resizeRenderer(newGeometry.size().toSize()); if (d.fill_mode == PreserveAspectCrop) { QSizeF scaled = d.out_rect.size(); scaled.scale(boundingRect().size(), Qt::KeepAspectRatioByExpanding); d.out_rect = QRect(QPoint(), scaled.toSize()); d.out_rect.moveCenter(boundingRect().center().toPoint()); } } bool QQuickItemRenderer::receiveFrame(const VideoFrame &frame) { DPTR_D(QQuickItemRenderer); d.video_frame = frame; if (!isOpenGL()) { d.image = QImage((uchar*)frame.constBits(), frame.width(), frame.height(), frame.bytesPerLine(), frame.imageFormat()); QRect r = realROI(); if (r != QRect(0, 0, frame.width(), frame.height())) d.image = d.image.copy(r); } d.frame_changed = true; // update(); // why update slow? because of calling in a different thread? //QMetaObject::invokeMethod(this, "update"); // slower than directly postEvent QCoreApplication::postEvent(this, new QEvent(QEvent::User)); return true; } QObject* QQuickItemRenderer::source() const { return d_func().source; } void QQuickItemRenderer::setSource(QObject *source) { DPTR_D(QQuickItemRenderer); if (d.source == source) return; d.source = source; Q_EMIT sourceChanged(); if (!source) return; ((QmlAVPlayer*)source)->player()->addVideoRenderer(this); } QQuickItemRenderer::FillMode QQuickItemRenderer::fillMode() const { return d_func().fill_mode; } void QQuickItemRenderer::setFillMode(FillMode mode) { DPTR_D(QQuickItemRenderer); if (d.fill_mode == mode) return; d_func().fill_mode = mode; if (d.fill_mode == Stretch) { setOutAspectRatioMode(RendererAspectRatio); } else {//compute out_rect fits video aspect ratio then compute again if crop setOutAspectRatioMode(VideoAspectRatio); } //m_geometryDirty = true; //update(); Q_EMIT fillModeChanged(mode); } QRectF QQuickItemRenderer::contentRect() const { return videoRect(); } QRectF QQuickItemRenderer::sourceRect() const { return QRectF(QPointF(), videoFrameSize()); } QPointF QQuickItemRenderer::mapPointToItem(const QPointF &point) const { if (videoFrameSize().isEmpty()) return QPointF(); // Just normalize and use that function // m_nativeSize is transposed in some orientations if (orientation()%180 == 0) return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height())); else return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width())); } QRectF QQuickItemRenderer::mapRectToItem(const QRectF &rectangle) const { return QRectF(mapPointToItem(rectangle.topLeft()), mapPointToItem(rectangle.bottomRight())).normalized(); } QPointF QQuickItemRenderer::mapNormalizedPointToItem(const QPointF &point) const { qreal dx = point.x(); qreal dy = point.y(); if (orientation()%180 == 0) { dx *= contentRect().width(); dy *= contentRect().height(); } else { dx *= contentRect().height(); dy *= contentRect().width(); } switch (orientation()) { case 0: default: return contentRect().topLeft() + QPointF(dx, dy); case 90: return contentRect().bottomLeft() + QPointF(dy, -dx); case 180: return contentRect().bottomRight() + QPointF(-dx, -dy); case 270: return contentRect().topRight() + QPointF(-dy, dx); } } QRectF QQuickItemRenderer::mapNormalizedRectToItem(const QRectF &rectangle) const { return QRectF(mapNormalizedPointToItem(rectangle.topLeft()), mapNormalizedPointToItem(rectangle.bottomRight())).normalized(); } QPointF QQuickItemRenderer::mapPointToSource(const QPointF &point) const { QPointF norm = mapPointToSourceNormalized(point); if (orientation()%180 == 0) return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height()); else return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width()); } QRectF QQuickItemRenderer::mapRectToSource(const QRectF &rectangle) const { return QRectF(mapPointToSource(rectangle.topLeft()), mapPointToSource(rectangle.bottomRight())).normalized(); } QPointF QQuickItemRenderer::mapPointToSourceNormalized(const QPointF &point) const { if (contentRect().isEmpty()) return QPointF(); // Normalize the item source point qreal nx = (point.x() - contentRect().x()) / contentRect().width(); qreal ny = (point.y() - contentRect().y()) / contentRect().height(); switch (orientation()) { case 0: default: return QPointF(nx, ny); case 90: return QPointF(1.0 - ny, nx); case 180: return QPointF(1.0 - nx, 1.0 - ny); case 270: return QPointF(ny, 1.0 - nx); } } QRectF QQuickItemRenderer::mapRectToSourceNormalized(const QRectF &rectangle) const { return QRectF(mapPointToSourceNormalized(rectangle.topLeft()), mapPointToSourceNormalized(rectangle.bottomRight())).normalized(); } bool QQuickItemRenderer::isOpenGL() const { return d_func().opengl; } void QQuickItemRenderer::setOpenGL(bool o) { DPTR_D(QQuickItemRenderer); if (d.opengl == o) return; d.opengl = o; emit openGLChanged(); } QQmlListProperty QQuickItemRenderer::filters() { return QQmlListProperty(this, NULL, vf_append, vf_count, vf_at, vf_clear); } void QQuickItemRenderer::vf_append(QQmlListProperty *property, QuickVideoFilter *value) { QQuickItemRenderer* self = static_cast(property->object); self->d_func().filters.append(value); self->installFilter(value); } int QQuickItemRenderer::vf_count(QQmlListProperty *property) { QQuickItemRenderer* self = static_cast(property->object); return self->d_func().filters.size(); } QuickVideoFilter* QQuickItemRenderer::vf_at(QQmlListProperty *property, int index) { QQuickItemRenderer* self = static_cast(property->object); return self->d_func().filters.at(index); } void QQuickItemRenderer::vf_clear(QQmlListProperty *property) { QQuickItemRenderer* self = static_cast(property->object); foreach (QuickVideoFilter *f, self->d_func().filters) { self->uninstallFilter(f); } self->d_func().filters.clear(); } void QQuickItemRenderer::drawFrame() { DPTR_D(QQuickItemRenderer); if (!d.node) return; if (isOpenGL()) { SGVideoNode *sgvn = static_cast(d.node); Q_ASSERT(sgvn); if (d.frame_changed) sgvn->setCurrentFrame(d.video_frame); d.frame_changed = false; sgvn->setTexturedRectGeometry(d.out_rect, normalizedROI(), d.orientation); return; } if (!d.frame_changed) { static_cast(d.node)->setRect(d.out_rect); d.node->markDirty(QSGNode::DirtyGeometry); return; } if (d.image.isNull()) { d.image = QImage(rendererSize(), QImage::Format_RGB32); d.image.fill(Qt::black); } static_cast(d.node)->setRect(d.out_rect); if (d.texture) delete d.texture; if (d.orientation == 0) { d.texture = window()->createTextureFromImage(d.image); } else if (d.orientation == 180) { d.texture = window()->createTextureFromImage(d.image.mirrored(true, true)); } static_cast(d.node)->setTexture(d.texture); d.node->markDirty(QSGNode::DirtyGeometry); d.frame_changed = false; } QSGNode *QQuickItemRenderer::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); DPTR_D(QQuickItemRenderer); if (d.frame_changed) { if (!node) { if (isOpenGL()) { node = new SGVideoNode(); } else { node = new QSGSimpleTextureNode(); } } } if (!node) { d.frame_changed = false; return 0; } d.node = node; handlePaintEvent(); d.node = 0; return node; } void QQuickItemRenderer::handleWindowChange(QQuickWindow *win) { if (!win) return; connect(win, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()), Qt::DirectConnection); connect(win, SIGNAL(afterRendering()), this, SLOT(afterRendering()), Qt::DirectConnection); } void QQuickItemRenderer::beforeRendering() { d_func().img_mutex.lock(); // TODO: what if current frame not rendered but new frame comes? } void QQuickItemRenderer::afterRendering() { d_func().img_mutex.unlock(); } bool QQuickItemRenderer::onSetOrientation(int value) { Q_UNUSED(value); if (!isOpenGL()) { if (value == 90 || value == 270) return false; } return true; } } // namespace QtAV QtAV-1.12.0/qml/QmlAV/000077500000000000000000000000001312235004300141505ustar00rootroot00000000000000QtAV-1.12.0/qml/QmlAV/Export.h000066400000000000000000000024641312235004300156100ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2013-2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_EXPORT_H #define QTAV_QML_EXPORT_H #include #if defined(BUILD_QMLAV_LIB) # undef QMLAV_EXPORT # define QMLAV_EXPORT Q_DECL_EXPORT #else # undef QMLAV_EXPORT # define QMLAV_EXPORT Q_DECL_IMPORT //only for vc? #endif #endif // QTAV_QML_EXPORT_H QtAV-1.12.0/qml/QmlAV/MediaMetaData.h000066400000000000000000000242061312235004300167450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_MEDIAMETADATA_H #define QTAV_QML_MEDIAMETADATA_H #include #include #include #include #include #include namespace QtAV { class Statistics; } /*! * \brief The MediaMetaData class * TODO: Music, Movie */ class MediaMetaData : public QObject { Q_OBJECT Q_PROPERTY(QVariant title READ title NOTIFY metaDataChanged) Q_PROPERTY(QVariant subTitle READ subTitle NOTIFY metaDataChanged) Q_PROPERTY(QVariant author READ author NOTIFY metaDataChanged) Q_PROPERTY(QVariant comment READ comment NOTIFY metaDataChanged) Q_PROPERTY(QVariant description READ description NOTIFY metaDataChanged) Q_PROPERTY(QVariant category READ category NOTIFY metaDataChanged) Q_PROPERTY(QVariant genre READ genre NOTIFY metaDataChanged) Q_PROPERTY(QVariant year READ year NOTIFY metaDataChanged) Q_PROPERTY(QVariant date READ date NOTIFY metaDataChanged) Q_PROPERTY(QVariant userRating READ userRating NOTIFY metaDataChanged) Q_PROPERTY(QVariant keywords READ keywords NOTIFY metaDataChanged) Q_PROPERTY(QVariant language READ language NOTIFY metaDataChanged) Q_PROPERTY(QVariant publisher READ publisher NOTIFY metaDataChanged) Q_PROPERTY(QVariant copyright READ copyright NOTIFY metaDataChanged) Q_PROPERTY(QVariant parentalRating READ parentalRating NOTIFY metaDataChanged) Q_PROPERTY(QVariant ratingOrganization READ ratingOrganization NOTIFY metaDataChanged) Q_PROPERTY(QVariant size READ size NOTIFY metaDataChanged) Q_PROPERTY(QVariant mediaType READ mediaType NOTIFY metaDataChanged) Q_PROPERTY(QVariant duration READ duration NOTIFY metaDataChanged) Q_PROPERTY(QVariant startTime READ startTime NOTIFY metaDataChanged) Q_PROPERTY(QVariant audioBitRate READ audioBitRate NOTIFY metaDataChanged) Q_PROPERTY(QVariant audioCodec READ audioCodec NOTIFY metaDataChanged) Q_PROPERTY(QVariant averageLevel READ averageLevel NOTIFY metaDataChanged) Q_PROPERTY(QVariant channelCount READ channelCount NOTIFY metaDataChanged) Q_PROPERTY(QVariant channelLayout READ channelLayout NOTIFY metaDataChanged) Q_PROPERTY(QVariant peakValue READ peakValue NOTIFY metaDataChanged) Q_PROPERTY(QVariant sampleFormat READ sampleFormat NOTIFY metaDataChanged) Q_PROPERTY(QVariant sampleRate READ sampleRate NOTIFY metaDataChanged) Q_PROPERTY(QVariant albumTitle READ albumTitle NOTIFY metaDataChanged) Q_PROPERTY(QVariant albumArtist READ albumArtist NOTIFY metaDataChanged) Q_PROPERTY(QVariant contributingArtist READ contributingArtist NOTIFY metaDataChanged) Q_PROPERTY(QVariant composer READ composer NOTIFY metaDataChanged) Q_PROPERTY(QVariant conductor READ conductor NOTIFY metaDataChanged) Q_PROPERTY(QVariant lyrics READ lyrics NOTIFY metaDataChanged) Q_PROPERTY(QVariant mood READ mood NOTIFY metaDataChanged) Q_PROPERTY(QVariant trackNumber READ trackNumber NOTIFY metaDataChanged) Q_PROPERTY(QVariant trackCount READ trackCount NOTIFY metaDataChanged) Q_PROPERTY(QVariant coverArtUrlSmall READ coverArtUrlSmall NOTIFY metaDataChanged) Q_PROPERTY(QVariant coverArtUrlLarge READ coverArtUrlLarge NOTIFY metaDataChanged) Q_PROPERTY(QVariant resolution READ resolution NOTIFY metaDataChanged) Q_PROPERTY(QVariant pixelAspectRatio READ pixelAspectRatio NOTIFY metaDataChanged) Q_PROPERTY(QVariant videoFrameRate READ videoFrameRate NOTIFY metaDataChanged) Q_PROPERTY(QVariant videoBitRate READ videoBitRate NOTIFY metaDataChanged) Q_PROPERTY(QVariant videoCodec READ videoCodec NOTIFY metaDataChanged) Q_PROPERTY(QVariant pixelFormat READ pixelFormat NOTIFY metaDataChanged) Q_PROPERTY(QVariant videoFrames READ videoFrames NOTIFY metaDataChanged) Q_PROPERTY(QVariant posterUrl READ posterUrl NOTIFY metaDataChanged) Q_PROPERTY(QVariant chapterNumber READ chapterNumber NOTIFY metaDataChanged) Q_PROPERTY(QVariant director READ director NOTIFY metaDataChanged) Q_PROPERTY(QVariant leadPerformer READ leadPerformer NOTIFY metaDataChanged) Q_PROPERTY(QVariant writer READ writer NOTIFY metaDataChanged) Q_ENUMS(Key) public: // The same as Qt.Multimedia. Some are not implemented, marked as // X enum Key { Title, SubTitle, // X Author, Comment, Description, // ? Category, // X. dx Genre, // X Year, // X Date, UserRating, // ?. dx Keywords, // x. dx Language, Publisher, Copyright, ParentalRating, // X RatingOrganization, // X // Media Size, // X. Qt only implemented in dshow "FileSize" MediaType, // ? Duration, StartTime, // Audio AudioBitRate, AudioCodec, AverageLevel, // X. dx ChannelCount, PeakValue, // X. dx SampleRate, SampleFormat, ChannelLayout, // Music AlbumTitle, // ? AlbumArtist, ContributingArtist, // X Composer, Conductor, // ? Lyrics, // ? Mood, // ? TrackNumber, TrackCount, CoverArtUrlSmall, // X CoverArtUrlLarge, // X // Image/Video Resolution, PixelAspectRatio, // X, AVStream.sample_aspect_ratio // Video VideoFrameRate, VideoBitRate, VideoCodec, PixelFormat, VideoFrames, PosterUrl, // X // Movie // X ChapterNumber, Director, LeadPerformer, Writer, PosterImage, // video CoverArtImage, // music }; explicit MediaMetaData(QObject *parent = 0); void setValuesFromStatistics(const QtAV::Statistics& st); void setValue(Key k, const QVariant& v); QVariant value(Key k, const QVariant& defaultValue = QVariant()) const; QString name(Key k) const; QVariant title() const { return value(Title); } QVariant subTitle() const { return value(SubTitle); } QVariant author() const { return value(Author); } QVariant comment() const { return value(Comment); } QVariant description() const { return value(Description); } QVariant category() const { return value(Category); } QVariant genre() const { return value(Genre); } QVariant year() const { return value(Year); } QVariant date() const { return value(Date); } QVariant userRating() const { return value(UserRating); } QVariant keywords() const { return value(Keywords); } QVariant language() const { return value(Language); } QVariant publisher() const { return value(Publisher); } QVariant copyright() const { return value(Copyright); } QVariant parentalRating() const { return value(ParentalRating); } QVariant ratingOrganization() const { return value(RatingOrganization); } QVariant size() const { return value(Size); } QVariant mediaType() const { return value(MediaType); } QVariant duration() const { return value(Duration); } QVariant startTime() const { return value(StartTime); } QVariant audioBitRate() const { return value(AudioBitRate); } QVariant audioCodec() const { return value(AudioCodec); } QVariant averageLevel() const { return value(AverageLevel); } QVariant channelCount() const { return value(ChannelCount); } QVariant channelLayout() const { return value(ChannelLayout); } QVariant peakValue() const { return value(PeakValue); } QVariant sampleFormat() const { return value(SampleFormat); } QVariant sampleRate() const { return value(SampleRate); } QVariant albumTitle() const { return value(AlbumTitle); } QVariant albumArtist() const { return value(AlbumArtist); } QVariant contributingArtist() const { return value(ContributingArtist); } QVariant composer() const { return value(Composer); } QVariant conductor() const { return value(Conductor); } QVariant lyrics() const { return value(Lyrics); } QVariant mood() const { return value(Mood); } QVariant trackNumber() const { return value(TrackNumber); } QVariant trackCount() const { return value(TrackCount); } QVariant coverArtUrlSmall() const { return value(CoverArtUrlSmall); } QVariant coverArtUrlLarge() const { return value(CoverArtUrlLarge); } QVariant resolution() const { return value(Resolution); } QVariant pixelAspectRatio() const { return value(PixelAspectRatio); } QVariant videoFrameRate() const { return value(VideoFrameRate); } QVariant videoBitRate() const { return value(VideoBitRate); } QVariant videoCodec() const { return value(VideoCodec); } QVariant pixelFormat() const { return value(PixelFormat); } QVariant videoFrames() const { return value(VideoFrames); } QVariant posterUrl() const { return value(PosterUrl); } QVariant chapterNumber() const { return value(ChapterNumber); } QVariant director() const { return value(Director); } QVariant leadPerformer() const { return value(LeadPerformer); } QVariant writer() const { return value(Writer); } Q_SIGNALS: void metaDataChanged(); private: Key fromFFmpegName(const QString& name) const; QHash m_metadata; }; #endif // QTAV_QML_MEDIAMETADATA_H QtAV-1.12.0/qml/QmlAV/QQuickItemRenderer.h000066400000000000000000000123421312235004300200260ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin theoribeiro * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_QQUICKRENDERER_H #define QTAV_QML_QQUICKRENDERER_H #include #include #include namespace QtAV { class QQuickItemRendererPrivate; class QQuickItemRenderer : public QQuickItem, public VideoRenderer { Q_OBJECT Q_DISABLE_COPY(QQuickItemRenderer) DPTR_DECLARE_PRIVATE(QQuickItemRenderer) Q_PROPERTY(bool opengl READ isOpenGL WRITE setOpenGL NOTIFY openGLChanged) Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRectF contentRect READ contentRect NOTIFY contentRectChanged) Q_PROPERTY(QRectF sourceRect READ sourceRect NOTIFY videoFrameSizeChanged) // regionOfInterest > sourceRect Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_PROPERTY(QSize frameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QQmlListProperty filters READ filters) Q_ENUMS(FillMode) public: enum FillMode { Stretch = Qt::IgnoreAspectRatio, PreserveAspectFit = Qt::KeepAspectRatio, PreserveAspectCrop = Qt::KeepAspectRatioByExpanding }; explicit QQuickItemRenderer(QQuickItem *parent = 0); VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; QObject *source() const; void setSource(QObject *source); FillMode fillMode() const; void setFillMode(FillMode mode); QRectF contentRect() const; QRectF sourceRect() const; Q_INVOKABLE QPointF mapPointToItem(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToItem(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapNormalizedPointToItem(const QPointF &point) const; Q_INVOKABLE QRectF mapNormalizedRectToItem(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapPointToSource(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToSource(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapPointToSourceNormalized(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToSourceNormalized(const QRectF &rectangle) const; bool isOpenGL() const; void setOpenGL(bool o); QQmlListProperty filters(); Q_SIGNALS: void sourceChanged(); void fillModeChanged(QQuickItemRenderer::FillMode); void orientationChanged() Q_DECL_OVERRIDE; void contentRectChanged() Q_DECL_OVERRIDE; void regionOfInterestChanged() Q_DECL_OVERRIDE; void openGLChanged(); void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; protected: bool event(QEvent *e) Q_DECL_OVERRIDE; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; bool receiveFrame(const VideoFrame &frame) Q_DECL_OVERRIDE; void drawFrame() Q_DECL_OVERRIDE; // QQuickItem interface QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) Q_DECL_OVERRIDE; private slots: void handleWindowChange(QQuickWindow *win); void beforeRendering(); void afterRendering(); private: bool onSetOrientation(int value) Q_DECL_OVERRIDE; static void vf_append(QQmlListProperty *property, QuickVideoFilter *value); static int vf_count(QQmlListProperty *property); static QuickVideoFilter *vf_at(QQmlListProperty *property, int index); static void vf_clear(QQmlListProperty *property); }; typedef QQuickItemRenderer VideoRendererQQuickItem; } QML_DECLARE_TYPE(QtAV::QQuickItemRenderer) #endif // QTAV_QML_QQUICKRENDERER_H QtAV-1.12.0/qml/QmlAV/QmlAVPlayer.h000066400000000000000000000341671312235004300164710ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_AVPLAYER_H #define QTAV_QML_AVPLAYER_H #include #include //5.0 #include #include #include #include #include #include namespace QtAV { class AVPlayer; } using namespace QtAV; class QmlAVPlayer : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(Error error READ error NOTIFY errorChanged) Q_PROPERTY(int duration READ duration NOTIFY durationChanged) Q_PROPERTY(int position READ position NOTIFY positionChanged) Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool hasAudio READ hasAudio NOTIFY hasAudioChanged) Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged) Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged) Q_PROPERTY(bool autoLoad READ isAutoLoad WRITE setAutoLoad NOTIFY autoLoadChanged) Q_PROPERTY(qreal playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged) Q_PROPERTY(qreal bufferProgress READ bufferProgress NOTIFY bufferProgressChanged) Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged) Q_PROPERTY(MediaMetaData *metaData READ metaData CONSTANT) Q_PROPERTY(QObject *mediaObject READ mediaObject NOTIFY mediaObjectChanged SCRIPTABLE false DESIGNABLE false) Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) Q_ENUMS(Loop) Q_ENUMS(PlaybackState) Q_ENUMS(Status) Q_ENUMS(Error) Q_ENUMS(ChannelLayout) // not supported by QtMultimedia Q_ENUMS(PositionValue) Q_PROPERTY(int startPosition READ startPosition WRITE setStartPosition NOTIFY startPositionChanged) Q_PROPERTY(int stopPosition READ stopPosition WRITE setStopPosition NOTIFY stopPositionChanged) Q_PROPERTY(bool fastSeek READ isFastSeek WRITE setFastSeek NOTIFY fastSeekChanged) Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged) Q_PROPERTY(bool abortOnTimeout READ abortOnTimeout WRITE setAbortOnTimeout NOTIFY abortOnTimeoutChanged) Q_PROPERTY(ChannelLayout channelLayout READ channelLayout WRITE setChannelLayout NOTIFY channelLayoutChanged) Q_PROPERTY(QStringList videoCodecs READ videoCodecs) Q_PROPERTY(QStringList videoCodecPriority READ videoCodecPriority WRITE setVideoCodecPriority NOTIFY videoCodecPriorityChanged) Q_PROPERTY(QVariantMap videoCodecOptions READ videoCodecOptions WRITE setVideoCodecOptions NOTIFY videoCodecOptionsChanged) Q_PROPERTY(QVariantMap avFormatOptions READ avFormatOptions WRITE setAVFormatOptions NOTIFY avFormatOptionsChanged) Q_PROPERTY(bool useWallclockAsTimestamps READ useWallclockAsTimestamps WRITE setWallclockAsTimestamps NOTIFY useWallclockAsTimestampsChanged) Q_PROPERTY(QtAV::VideoCapture *videoCapture READ videoCapture CONSTANT) Q_PROPERTY(int audioTrack READ audioTrack WRITE setAudioTrack NOTIFY audioTrackChanged) Q_PROPERTY(int videoTrack READ videoTrack WRITE setVideoTrack NOTIFY videoTrackChanged) Q_PROPERTY(int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged) Q_PROPERTY(QUrl externalAudio READ externalAudio WRITE setExternalAudio NOTIFY externalAudioChanged) Q_PROPERTY(QVariantList internalAudioTracks READ internalAudioTracks NOTIFY internalAudioTracksChanged) Q_PROPERTY(QVariantList internalVideoTracks READ internalVideoTracks NOTIFY internalVideoTracksChanged) Q_PROPERTY(QVariantList externalAudioTracks READ externalAudioTracks NOTIFY externalAudioTracksChanged) Q_PROPERTY(QVariantList internalSubtitleTracks READ internalSubtitleTracks NOTIFY internalSubtitleTracksChanged) // internal subtitle, e.g. mkv embedded subtitles Q_PROPERTY(int internalSubtitleTrack READ internalSubtitleTrack WRITE setInternalSubtitleTrack NOTIFY internalSubtitleTrackChanged) Q_PROPERTY(QQmlListProperty audioFilters READ audioFilters) Q_PROPERTY(QQmlListProperty videoFilters READ videoFilters) // TODO: startPosition/stopPosition Q_PROPERTY(QStringList audioBackends READ audioBackends WRITE setAudioBackends NOTIFY audioBackendsChanged) Q_PROPERTY(QStringList supportedAudioBackends READ supportedAudioBackends) public: enum Loop { Infinite = -1 }; // use (1<<31)-1 enum PositionValue { PositionMax = int(~0)^(1<<(sizeof(int)*8-1))}; enum PlaybackState { StoppedState, PlayingState, PausedState }; enum Status { UnknownStatus = QtAV::UnknownMediaStatus, // e.g. user status after interrupt NoMedia = QtAV::NoMedia, Loading = QtAV::LoadingMedia, // when source is set Loaded = QtAV::LoadedMedia, // if auto load and source is set. player is stopped state Stalled = QtAV::StalledMedia, Buffering = QtAV::BufferingMedia, Buffered = QtAV::BufferedMedia, // when playing EndOfMedia = QtAV::EndOfMedia, InvalidMedia = QtAV::InvalidMedia }; enum Error { NoError, ResourceError, FormatError, NetworkError, AccessDenied, ServiceMissing }; // currently supported channels<3. enum ChannelLayout { ChannelLayoutAuto, //the same as source if channels<=2. otherwise resamples to stereo Left, Right, Mono, Stereo }; explicit QmlAVPlayer(QObject *parent = 0); //derived virtual void classBegin(); virtual void componentComplete(); // add QtAV::AVPlayer::isAudioAvailable()? bool hasAudio() const; bool hasVideo() const; QUrl source() const; /*! * \brief setSource * If url is changed and auto load is true, current playback will stop. */ void setSource(const QUrl& url); // 0,1: play once. MediaPlayer.Infinite: forever. // >1: play loopCount() - 1 times. different from Qt int loopCount() const; void setLoopCount(int c); QObject* videoOut(); void setVideoOut(QObject* out); qreal volume() const; void setVolume(qreal value); bool isMuted() const; void setMuted(bool m); int duration() const; int position() const; bool isSeekable() const; int startPosition() const; void setStartPosition(int value); int stopPosition() const; /*! * \brief setStopPosition * You can use MediaPlayer.PositionMax to play until the end of stream. */ void setStopPosition(int value); bool isFastSeek() const; void setFastSeek(bool value); qreal bufferProgress() const; Status status() const; Error error() const; QString errorString() const; PlaybackState playbackState() const; void setPlaybackState(PlaybackState playbackState); qreal playbackRate() const; void setPlaybackRate(qreal s); Q_INVOKABLE void play(const QUrl& url); AVPlayer *player(); bool isAutoLoad() const; void setAutoLoad(bool autoLoad); bool autoPlay() const; void setAutoPlay(bool autoplay); MediaMetaData *metaData() const; QObject *mediaObject() const; QtAV::VideoCapture *videoCapture() const; // "FFmpeg", "CUDA", "DXVA", "VAAPI" etc QStringList videoCodecs() const; QStringList videoCodecPriority() const; void setVideoCodecPriority(const QStringList& p); QVariantMap videoCodecOptions() const; void setVideoCodecOptions(const QVariantMap& value); QVariantMap avFormatOptions() const; void setAVFormatOptions(const QVariantMap& value); bool useWallclockAsTimestamps() const; void setWallclockAsTimestamps(bool use_wallclock_as_timestamps); void setAudioChannels(int channels); int audioChannels() const; void setChannelLayout(ChannelLayout channel); ChannelLayout channelLayout() const; void setTimeout(int value); // ms int timeout() const; void setAbortOnTimeout(bool value); bool abortOnTimeout() const; /*! * \brief audioTrack * The audio stream number in current media or external audio. * Value can be: 0, 1, 2.... 0 means the 1st audio stream in current media or external audio */ int audioTrack() const; void setAudioTrack(int value); QVariantList internalAudioTracks() const; /*! /*! * \brief videoTrack * The video stream number in current media. * Value can be: 0, 1, 2.... 0 means the 1st video stream in current media */ int videoTrack() const; void setVideoTrack(int value); QVariantList internalVideoTracks() const; int bufferSize() const; void setBufferSize(int value); /*! * \brief externalAudio * If externalAudio url is valid, player will use audioTrack of external audio as audio source. * Set an invalid url to disable external audio * \return external audio url or an invalid url if use internal audio tracks */ QUrl externalAudio() const; void setExternalAudio(const QUrl& url); QVariantList externalAudioTracks() const; int internalSubtitleTrack() const; void setInternalSubtitleTrack(int value); QVariantList internalSubtitleTracks() const; QQmlListProperty audioFilters(); QQmlListProperty videoFilters(); QStringList supportedAudioBackends() const; QStringList audioBackends() const; void setAudioBackends(const QStringList& value); public Q_SLOTS: void play(); void pause(); void stop(); void stepForward(); void stepBackward(); void seek(int offset); void seekForward(); void seekBackward(); Q_SIGNALS: void volumeChanged(); void mutedChanged(); // TODO: signal from QtAV::AVPlayer void hasAudioChanged(); void hasVideoChanged(); void durationChanged(); void positionChanged(); void sourceChanged(); void autoLoadChanged(); void loopCountChanged(); void videoOutChanged(); void playbackStateChanged(); void autoPlayChanged(); void playbackRateChanged(); void paused(); void stopped(); void playing(); void startPositionChanged(); void stopPositionChanged(); void seekableChanged(); void seekFinished(); //WARNING: position() now is not the seek finished video timestamp void fastSeekChanged(); void bufferProgressChanged(); void videoCodecPriorityChanged(); void videoCodecOptionsChanged(); void avFormatOptionsChanged(); void useWallclockAsTimestampsChanged(); void channelLayoutChanged(); void timeoutChanged(); void abortOnTimeoutChanged(); void audioTrackChanged(); void internalAudioTracksChanged(); void videoTrackChanged(); void internalVideoTracksChanged(); void externalAudioChanged(); void externalAudioTracksChanged(); void internalSubtitleTrackChanged(); void internalSubtitleTracksChanged(); void bufferSizeChanged(); void errorChanged(); void error(Error error, const QString &errorString); void statusChanged(); void mediaObjectChanged(); void audioBackendsChanged(); private Q_SLOTS: // connect to signals from player void _q_error(const QtAV::AVError& e); void _q_statusChanged(); void _q_started(); void _q_stopped(); void _q_paused(bool); private Q_SLOTS: void applyVolume(); void applyChannelLayout(); private: static void af_append(QQmlListProperty *property, QuickAudioFilter *value); static int af_count(QQmlListProperty *property); static QuickAudioFilter *af_at(QQmlListProperty *property, int index); static void af_clear(QQmlListProperty *property); static void vf_append(QQmlListProperty *property, QuickVideoFilter *value); static int vf_count(QQmlListProperty *property); static QuickVideoFilter *vf_at(QQmlListProperty *property, int index); static void vf_clear(QQmlListProperty *property); Q_DISABLE_COPY(QmlAVPlayer) bool mUseWallclockAsTimestamps; bool m_complete; bool m_mute; bool mAutoPlay; bool mAutoLoad; bool mHasAudio, mHasVideo; bool m_fastSeek; bool m_loading; int mLoopCount; int mStart, mStop; qreal mPlaybackRate; qreal mVolume; PlaybackState mPlaybackState; Error mError; QString mErrorString; QtAV::AVPlayer *mpPlayer; QUrl mSource; QStringList mVideoCodecs; ChannelLayout mChannelLayout; int m_timeout; bool m_abort_timeout; int m_audio_track; int m_video_track; QUrl m_audio; int m_sub_track; QScopedPointer m_metaData; QVariantMap vcodec_opt; QVariantMap avfmt_opt; QList m_afilters; QList m_vfilters; QStringList m_ao; }; #endif // QTAV_QML_AVPLAYER_H QtAV-1.12.0/qml/QmlAV/QuickFBORenderer.h000066400000000000000000000137741312235004300174270ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QUICKFBORENDERER_H #define QTAV_QUICKFBORENDERER_H #include #include #include #include namespace QtAV { class QuickFBORendererPrivate; class QuickFBORenderer : public QQuickFramebufferObject, public VideoRenderer { Q_OBJECT Q_DISABLE_COPY(QuickFBORenderer) DPTR_DECLARE_PRIVATE(QuickFBORenderer) Q_PROPERTY(bool opengl READ isOpenGL WRITE setOpenGL NOTIFY openGLChanged) Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRectF contentRect READ contentRect NOTIFY contentRectChanged) Q_PROPERTY(QRectF sourceRect READ sourceRect NOTIFY videoFrameSizeChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_PROPERTY(QSize frameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QQmlListProperty filters READ filters) Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_ENUMS(FillMode) public: enum FillMode { Stretch = Qt::IgnoreAspectRatio, PreserveAspectFit = Qt::KeepAspectRatio, PreserveAspectCrop = Qt::KeepAspectRatioByExpanding }; Renderer *createRenderer() const Q_DECL_OVERRIDE; explicit QuickFBORenderer(QQuickItem *parent = 0); VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; OpenGLVideo* opengl() const Q_DECL_OVERRIDE; QObject *source() const; void setSource(QObject *source); FillMode fillMode() const; void setFillMode(FillMode mode); QRectF contentRect() const; QRectF sourceRect() const; Q_INVOKABLE QPointF mapPointToItem(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToItem(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapNormalizedPointToItem(const QPointF &point) const; Q_INVOKABLE QRectF mapNormalizedRectToItem(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapPointToSource(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToSource(const QRectF &rectangle) const; Q_INVOKABLE QPointF mapPointToSourceNormalized(const QPointF &point) const; Q_INVOKABLE QRectF mapRectToSourceNormalized(const QRectF &rectangle) const; bool isOpenGL() const; void setOpenGL(bool o); void fboSizeChanged(const QSize& size); void renderToFbo(QOpenGLFramebufferObject *fbo); QQmlListProperty filters(); Q_SIGNALS: void sourceChanged(); void fillModeChanged(QuickFBORenderer::FillMode); void orientationChanged() Q_DECL_OVERRIDE; void contentRectChanged() Q_DECL_OVERRIDE; void regionOfInterestChanged() Q_DECL_OVERRIDE; void openGLChanged(); void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; protected: bool event(QEvent *e) Q_DECL_OVERRIDE; bool receiveFrame(const VideoFrame &frame) Q_DECL_OVERRIDE; void drawBackground() Q_DECL_OVERRIDE; void drawFrame() Q_DECL_OVERRIDE; private: bool onSetOrientation(int value) Q_DECL_OVERRIDE; void onSetOutAspectRatio(qreal ratio) Q_DECL_OVERRIDE; void onSetOutAspectRatioMode(OutAspectRatioMode mode) Q_DECL_OVERRIDE; bool onSetBrightness(qreal b) Q_DECL_OVERRIDE; bool onSetContrast(qreal c) Q_DECL_OVERRIDE; bool onSetHue(qreal h) Q_DECL_OVERRIDE; bool onSetSaturation(qreal s) Q_DECL_OVERRIDE; void updateRenderRect(); static void vf_append(QQmlListProperty *property, QuickVideoFilter *value); static int vf_count(QQmlListProperty *property); static QuickVideoFilter *vf_at(QQmlListProperty *property, int index); static void vf_clear(QQmlListProperty *property); }; typedef QuickFBORenderer VideoRendererQuickFBO; } //namespace QtAV QML_DECLARE_TYPE(QtAV::QuickFBORenderer) #endif // QTAV_QUICKFBORENDERER_H QtAV-1.12.0/qml/QmlAV/QuickFilter.h000066400000000000000000000075001312235004300165450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QUICKFILTER_H #define QTAV_QUICKFILTER_H #include #include //namespace QtAV { //FIXME: why has error 'Invalid property assignment: "videoFilters" is a read-only property' if use namespace? //http://wiki.qt.io/How_to_use_a_C_class_declared_in_a_namespace_with_Q_PROPERTY_and_QML using namespace QtAV; class QuickVideoFilterPrivate; class QuickVideoFilter : public VideoFilter { Q_OBJECT DPTR_DECLARE_PRIVATE(QuickVideoFilter) Q_PROPERTY(QString avfilter READ avfilter WRITE setAVFilter NOTIFY avfilterChanged) Q_PROPERTY(QStringList supportedAVFilters READ supportedAVFilters) Q_PROPERTY(VideoFilter* userFilter READ userFilter WRITE setUserFilter NOTIFY userFilterChanged) Q_PROPERTY(QtAV::DynamicShaderObject* shader READ shader WRITE setShader NOTIFY shaderChanged) Q_PROPERTY(FilterType type READ type WRITE setType NOTIFY typeChanged) Q_ENUMS(FilterType) public: enum FilterType { AVFilter, GLSLFilter, UserFilter }; QuickVideoFilter(QObject* parent = 0); bool isSupported(VideoFilterContext::Type ct) const Q_DECL_OVERRIDE; FilterType type() const; void setType(FilterType value); QStringList supportedAVFilters() const; QString avfilter() const; void setAVFilter(const QString& options); VideoFilter *userFilter() const; void setUserFilter(VideoFilter* f); DynamicShaderObject* shader() const; void setShader(DynamicShaderObject* value); Q_SIGNALS: void avfilterChanged(); void userFilterChanged(); void shaderChanged(); void typeChanged(); protected: void process(Statistics* statistics, VideoFrame* frame = 0) Q_DECL_OVERRIDE; }; class QuickAudioFilterPrivate; class QuickAudioFilter : public AudioFilter { Q_OBJECT DPTR_DECLARE_PRIVATE(QuickAudioFilter) Q_PROPERTY(QString avfilter READ avfilter WRITE setAVFilter NOTIFY avfilterChanged) Q_PROPERTY(QStringList supportedAVFilters READ supportedAVFilters) Q_PROPERTY(AudioFilter* userFilter READ userFilter WRITE setUserFilter NOTIFY userFilterChanged) Q_PROPERTY(FilterType type READ type WRITE setType NOTIFY typeChanged) Q_ENUMS(FilterType) public: enum FilterType { AVFilter, UserFilter }; QuickAudioFilter(QObject* parent = 0); FilterType type() const; void setType(FilterType value); QStringList supportedAVFilters() const; QString avfilter() const; void setAVFilter(const QString& options); AudioFilter* userFilter() const; void setUserFilter(AudioFilter *f); Q_SIGNALS: void avfilterChanged(); void userFilterChanged(); void typeChanged(); protected: void process(Statistics* statistics, AudioFrame* frame = 0) Q_DECL_OVERRIDE; }; //} //namespace QtAV #endif //QTAV_QUICKFILTER_H QtAV-1.12.0/qml/QmlAV/QuickSubtitle.h000066400000000000000000000121701312235004300171120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_QUICKSUBTITLE_H #define QTAV_QML_QUICKSUBTITLE_H #include #include class QuickSubtitleObserver { public: virtual void update(const QImage& image, const QRect& r, int width, int height) = 0; }; namespace QtAV { class PlayerSubtitle; } class QmlAVPlayer; /*! * \brief The QuickSubtitle class * high level Subtitle processor for QML. No rendering. * Subtitle load priority: user specified file (setFile(...)) > auto load external (autoLoad() must be true) > embedded subtitle */ class QuickSubtitle : public QObject, public QtAV::SubtitleAPIProxy { Q_OBJECT Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(QObject* player READ player WRITE setPlayer) // proxy api Q_PROPERTY(QByteArray codec READ codec WRITE setCodec NOTIFY codecChanged) Q_PROPERTY(QStringList engines READ engines WRITE setEngines NOTIFY enginesChanged) Q_PROPERTY(QString engine READ engine NOTIFY engineChanged) Q_PROPERTY(bool fuzzyMatch READ fuzzyMatch WRITE setFuzzyMatch NOTIFY fuzzyMatchChanged) //Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) Q_PROPERTY(QStringList dirs READ dirs WRITE setDirs NOTIFY dirsChanged) Q_PROPERTY(QStringList suffixes READ suffixes WRITE setSuffixes NOTIFY suffixesChanged) Q_PROPERTY(QStringList supportedSuffixes READ supportedSuffixes NOTIFY supportedSuffixesChanged) Q_PROPERTY(qreal delay READ delay WRITE setDelay NOTIFY delayChanged) Q_PROPERTY(bool canRender READ canRender NOTIFY canRenderChanged) //PlayerSubtitle api Q_PROPERTY(bool autoLoad READ autoLoad WRITE setAutoLoad NOTIFY autoLoadChanged) Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged) // Q_PROPERTY(QString text READ getText) // font properties for libass engine Q_PROPERTY(QString fontFile READ fontFile WRITE setFontFile NOTIFY fontFileChanged) Q_PROPERTY(QString fontsDir READ fontsDir WRITE setFontsDir NOTIFY fontsDirChanged) Q_PROPERTY(bool fontFileForced READ isFontFileForced WRITE setFontFileForced NOTIFY fontFileForcedChanged) public: explicit QuickSubtitle(QObject *parent = 0); Q_INVOKABLE QString getText() const; // observer is only for ass image subtitle void addObserver(QuickSubtitleObserver* ob); void removeObserver(QuickSubtitleObserver* ob); // 0: notify all void notifyObservers(const QImage& image, const QRect& r, int width, int height, QuickSubtitleObserver* ob = 0); /*! * \brief setPlayer * if player is set, subtitle will automatically loaded if playing file changed. * \param player */ void setPlayer(QObject* player); QObject* player(); // TODO: enableRenderImage + enabled void setEnabled(bool value = true); //AVComponent.enabled bool isEnabled() const; /*! * \brief setFile * Load user selected subtitle. The subtitle will not change unless you manually setFile(QString()). */ void setFile(const QString& file); QString file() const; /*! * \brief autoLoad * Auto find and load a suitable external subtitle if file() is not empty. */ bool autoLoad() const; //void setAssFrameSize(int width, int height); public Q_SLOTS: // TODO: enable changed & autoload=> load void setAutoLoad(bool value); Q_SIGNALS: void fileChanged(); void canRenderChanged(); void loaded(const QString& path); void enabledChanged(bool value); void autoLoadChanged(); void codecChanged(); void enginesChanged(); void fuzzyMatchChanged(); void contentChanged(); //void fileNameChanged(); void dirsChanged(); void suffixesChanged(); void supportedSuffixesChanged(); void engineChanged(); void delayChanged(); void fontFileChanged(); void fontsDirChanged(); void fontFileForcedChanged(); private: bool m_enable; QmlAVPlayer *m_player; QtAV::PlayerSubtitle *m_player_sub; class Filter; Filter *m_filter; QMutex m_mutex; QList m_observers; }; #endif // QTAV_QML_QUICKSUBTITLE_H QtAV-1.12.0/qml/QmlAV/QuickSubtitleItem.h000066400000000000000000000052011312235004300177260ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QML_QUICKSUBTITLEITEM_H #define QTAV_QML_QUICKSUBTITLEITEM_H #include #include #include #include class QuickSubtitleItem : public QQuickItem, public QuickSubtitleObserver { Q_OBJECT Q_DISABLE_COPY(QuickSubtitleItem) Q_PROPERTY(QuickSubtitle* source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(int fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) public: explicit QuickSubtitleItem(QQuickItem *parent = 0); ~QuickSubtitleItem(); //QQuickItemRenderer* target() const; void setSource(QuickSubtitle* s); QuickSubtitle* source() const; /*! * \brief setFillMode * keep the value the same as VideoOutput: * fillMode: vo.fillMode */ void setFillMode(int value); int fillMode() const; virtual void update(const QImage& image, const QRect& r, int width, int height); Q_SIGNALS: void sourceChanged(); void fillModeChanged(); protected: QRectF mapSubRect(const QRect& r, qreal w, qreal h); virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data); virtual bool event(QEvent *e); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); private: QuickSubtitle *m_sub; QSGTexture* m_texture; bool m_remap; int m_fillMode; int m_w_sub, m_h_sub; //subtitle frame width height QImage m_image; QRect m_rect; // subtitle rect in subtitle frame coorinate QRectF m_rect_mapped; QMutex m_mutex; }; #endif // QTAV_QML_QUICKSUBTITLEITEM_H QtAV-1.12.0/qml/QmlAV/QuickVideoPreview.h000066400000000000000000000042371312235004300177340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QUICKVIDEOPREVIEW_H #define QTAV_QUICKVIDEOPREVIEW_H #include #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) #include typedef QuickFBORenderer BaseQuickRenderer; #else #include typedef QQuickItemRenderer BaseQuickRenderer; #endif namespace QtAV { class QuickVideoPreview : public BaseQuickRenderer { Q_OBJECT // position conflicts with QQuickItem.position Q_PROPERTY(int timestamp READ timestamp WRITE setTimestamp NOTIFY timestampChanged) // source is already in VideoOutput Q_PROPERTY(QUrl file READ file WRITE setFile NOTIFY fileChanged) public: explicit QuickVideoPreview(QQuickItem *parent = 0); void setTimestamp(int value); int timestamp() const; void setFile(const QUrl& value); QUrl file() const; signals: void timestampChanged(); void fileChanged(); private slots: void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame void displayNoFrame(); private: QUrl m_file; VideoFrameExtractor m_extractor; }; } //namespace QtAV #endif // QUICKVIDEOPREVIEW_H QtAV-1.12.0/qml/QmlAV/SGVideoNode.h000066400000000000000000000033431312235004300164320ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SGVIDEONODE_H #define QTAV_SGVIDEONODE_H #include #include namespace QtAV { class SGVideoMaterial; class SGVideoNode : public QSGGeometryNode { public: SGVideoNode(); ~SGVideoNode(); virtual void setCurrentFrame(const VideoFrame &frame); /* Update the vertices and texture coordinates. Orientation must be in {0,90,180,270} */ void setTexturedRectGeometry(const QRectF &boundingRect, const QRectF &textureRect, int orientation); private: SGVideoMaterial *m_material; QRectF m_rect; QRectF m_textureRect; int m_orientation; qreal m_validWidth; }; } //namespace QtAV #endif // QTAV_SGVIDEONODE_H QtAV-1.12.0/qml/QmlAVPlayer.cpp000066400000000000000000000577571312235004300160560ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/QmlAVPlayer.h" #include #include #include template static QStringList idsToNames(QVector ids) { QStringList decs; if (!ids.isEmpty()) { decs.reserve(ids.size()); foreach (ID id, ids) { decs.append(QString::fromLatin1(T::name(id))); } } return decs; } static inline QStringList VideoDecodersToNames(QVector ids) { return idsToNames(ids); } QmlAVPlayer::QmlAVPlayer(QObject *parent) : QObject(parent) , mUseWallclockAsTimestamps(false) , m_complete(false) , m_mute(false) , mAutoPlay(false) , mAutoLoad(false) , mHasAudio(false) , mHasVideo(false) , m_fastSeek(false) , m_loading(false) , mLoopCount(1) , mStart(0) , mStop(PositionMax) , mPlaybackRate(1.0) , mVolume(1.0) , mPlaybackState(StoppedState) , mError(NoError) , mpPlayer(0) , mChannelLayout(ChannelLayoutAuto) , m_timeout(30000) , m_abort_timeout(true) , m_audio_track(0) , m_video_track(0) , m_sub_track(0) , m_ao(AudioOutput::backendsAvailable()) { classBegin(); } void QmlAVPlayer::classBegin() { if (mpPlayer) return; mpPlayer = new AVPlayer(this); connect(mpPlayer, SIGNAL(internalSubtitleTracksChanged(QVariantList)), SIGNAL(internalSubtitleTracksChanged())); connect(mpPlayer, SIGNAL(internalAudioTracksChanged(QVariantList)), SIGNAL(internalAudioTracksChanged())); connect(mpPlayer, SIGNAL(internalVideoTracksChanged(QVariantList)), SIGNAL(internalVideoTracksChanged())); connect(mpPlayer, SIGNAL(externalAudioTracksChanged(QVariantList)), SIGNAL(externalAudioTracksChanged())); connect(mpPlayer, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged())); connect(mpPlayer, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), SLOT(_q_statusChanged())); connect(mpPlayer, SIGNAL(error(QtAV::AVError)), SLOT(_q_error(QtAV::AVError))); connect(mpPlayer, SIGNAL(paused(bool)), SLOT(_q_paused(bool))); connect(mpPlayer, SIGNAL(started()), SLOT(_q_started())); connect(mpPlayer, SIGNAL(stopped()), SLOT(_q_stopped())); connect(mpPlayer, SIGNAL(positionChanged(qint64)), SIGNAL(positionChanged())); connect(mpPlayer, SIGNAL(seekableChanged()), SIGNAL(seekableChanged())); connect(mpPlayer, SIGNAL(seekFinished(qint64)), this, SIGNAL(seekFinished()), Qt::DirectConnection); connect(mpPlayer, SIGNAL(bufferProgressChanged(qreal)), SIGNAL(bufferProgressChanged())); connect(this, SIGNAL(channelLayoutChanged()), SLOT(applyChannelLayout())); // direct connection to ensure volume() in slots is correct connect(mpPlayer->audio(), SIGNAL(volumeChanged(qreal)), SLOT(applyVolume()), Qt::DirectConnection); connect(mpPlayer->audio(), SIGNAL(muteChanged(bool)), SLOT(applyVolume()), Qt::DirectConnection); mVideoCodecs << QStringLiteral("FFmpeg"); m_metaData.reset(new MediaMetaData()); Q_EMIT mediaObjectChanged(); } void QmlAVPlayer::componentComplete() { // TODO: set player parameters if (mSource.isValid() && (mAutoLoad || mAutoPlay)) { if (mAutoLoad) mpPlayer->load(); if (mAutoPlay) mpPlayer->play(); } m_complete = true; } bool QmlAVPlayer::hasAudio() const { if (!m_complete) return false; return mHasAudio; } bool QmlAVPlayer::hasVideo() const { if (!m_complete) return false; return mHasVideo; } QUrl QmlAVPlayer::source() const { return mSource; } void QmlAVPlayer::setSource(const QUrl &url) { if (mSource == url) return; mSource = url; if (url.isLocalFile() || url.scheme().isEmpty() || url.scheme().startsWith("qrc") || url.scheme().startsWith("avdevice") // TODO: what about custom io? ) mpPlayer->setFile(QUrl::fromPercentEncoding(url.toEncoded())); else mpPlayer->setFile(url.toEncoded()); Q_EMIT sourceChanged(); //TODO: Q_EMIT only when player loaded a new source if (mHasAudio) { mHasAudio = false; Q_EMIT hasAudioChanged(); } if (mHasVideo) { mHasVideo = false; Q_EMIT hasVideoChanged(); } // TODO: in componentComplete()? if (m_complete && (mAutoLoad || mAutoPlay)) { mError = NoError; mErrorString = tr("No error"); Q_EMIT error(mError, mErrorString); Q_EMIT errorChanged(); stop(); // TODO: no stop for autoLoad? if (mAutoLoad) mpPlayer->load(); if (mAutoPlay) { //mPlaybackState is actually changed in slots. But if set to a new source the state may not change before call play() mPlaybackState = StoppedState; play(); } } } bool QmlAVPlayer::isAutoLoad() const { return mAutoLoad; } void QmlAVPlayer::setAutoLoad(bool autoLoad) { if (mAutoLoad == autoLoad) return; mAutoLoad = autoLoad; Q_EMIT autoLoadChanged(); } bool QmlAVPlayer::autoPlay() const { return mAutoPlay; } void QmlAVPlayer::setAutoPlay(bool autoplay) { if (mAutoPlay == autoplay) return; mAutoPlay = autoplay; Q_EMIT autoPlayChanged(); } MediaMetaData* QmlAVPlayer::metaData() const { return m_metaData.data(); } QObject* QmlAVPlayer::mediaObject() const { return mpPlayer; } VideoCapture *QmlAVPlayer::videoCapture() const { return mpPlayer->videoCapture(); } QStringList QmlAVPlayer::videoCodecs() const { return VideoDecodersToNames(VideoDecoder::registered()); } void QmlAVPlayer::setVideoCodecPriority(const QStringList &p) { if (mVideoCodecs == p) return; mVideoCodecs = p; Q_EMIT videoCodecPriorityChanged(); if (!mpPlayer) { qWarning("player not ready"); return; } QVariantHash vcopt; for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) { vcopt[cit.key()] = cit.value(); } if (!vcopt.isEmpty()) mpPlayer->setOptionsForVideoCodec(vcopt); mpPlayer->setVideoDecoderPriority(p); } QStringList QmlAVPlayer::videoCodecPriority() const { return mVideoCodecs; } QVariantMap QmlAVPlayer::videoCodecOptions() const { return vcodec_opt; } void QmlAVPlayer::setVideoCodecOptions(const QVariantMap &value) { if (value == vcodec_opt) return; vcodec_opt = value; Q_EMIT videoCodecOptionsChanged(); if (!mpPlayer) { qWarning("player not ready"); return; } QVariantHash vcopt; for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) { vcopt[cit.key()] = cit.value(); } if (!vcopt.isEmpty()) mpPlayer->setOptionsForVideoCodec(vcopt); mpPlayer->setVideoDecoderPriority(videoCodecPriority()); } QVariantMap QmlAVPlayer::avFormatOptions() const { return avfmt_opt; } void QmlAVPlayer::setAVFormatOptions(const QVariantMap &value) { if (value == avfmt_opt) return; avfmt_opt = value; Q_EMIT avFormatOptionsChanged(); if (!mpPlayer) { qWarning("player not ready"); return; } QVariantHash avfopt; for (QVariantMap::const_iterator cit = avfmt_opt.cbegin(); cit != avfmt_opt.cend(); ++cit) { avfopt[cit.key()] = cit.value(); } if (!avfopt.isEmpty()) mpPlayer->setOptionsForFormat(avfopt); } bool QmlAVPlayer::useWallclockAsTimestamps() const { return mUseWallclockAsTimestamps; } void QmlAVPlayer::setWallclockAsTimestamps(bool use_wallclock_as_timestamps) { if (mUseWallclockAsTimestamps == use_wallclock_as_timestamps) return; mUseWallclockAsTimestamps = use_wallclock_as_timestamps; QVariantHash opt = mpPlayer->optionsForFormat(); if (use_wallclock_as_timestamps) { opt[QStringLiteral("use_wallclock_as_timestamps")] = 1; mpPlayer->setBufferValue(1); } else { opt.remove(QStringLiteral("use_wallclock_as_timestamps")); mpPlayer->setBufferValue(-1); } mpPlayer->setOptionsForFormat(opt); Q_EMIT useWallclockAsTimestampsChanged(); } static AudioFormat::ChannelLayout toAudioFormatChannelLayout(QmlAVPlayer::ChannelLayout ch) { struct { QmlAVPlayer::ChannelLayout ch; AudioFormat::ChannelLayout a; } map[] = { { QmlAVPlayer::Left, AudioFormat::ChannelLayout_Left }, { QmlAVPlayer::Right, AudioFormat::ChannelLayout_Right }, { QmlAVPlayer::Mono, AudioFormat::ChannelLayout_Mono }, { QmlAVPlayer::Stereo, AudioFormat::ChannelLayout_Stereo }, }; for (uint i = 0; i < sizeof(map)/sizeof(map[0]); ++i) { if (map[i].ch == ch) return map[i].a; } return AudioFormat::ChannelLayout_Unsupported; } void QmlAVPlayer::setChannelLayout(ChannelLayout channel) { if (mChannelLayout == channel) return; mChannelLayout = channel; Q_EMIT channelLayoutChanged(); } QmlAVPlayer::ChannelLayout QmlAVPlayer::channelLayout() const { return mChannelLayout; } void QmlAVPlayer::setTimeout(int value) { if (m_timeout == value) return; m_timeout = value; Q_EMIT timeoutChanged(); if (mpPlayer) mpPlayer->setInterruptTimeout(m_timeout); } int QmlAVPlayer::timeout() const { return m_timeout; } void QmlAVPlayer::setAbortOnTimeout(bool value) { if (m_abort_timeout == value) return; m_abort_timeout = value; Q_EMIT abortOnTimeoutChanged(); if (mpPlayer) mpPlayer->setInterruptOnTimeout(value); } bool QmlAVPlayer::abortOnTimeout() const { return m_abort_timeout; } int QmlAVPlayer::audioTrack() const { return m_audio_track; } void QmlAVPlayer::setAudioTrack(int value) { if (m_audio_track == value) return; m_audio_track = value; Q_EMIT audioTrackChanged(); if (mpPlayer) mpPlayer->setAudioStream(value); } int QmlAVPlayer::videoTrack() const { return m_video_track; } void QmlAVPlayer::setVideoTrack(int value) { if (m_video_track == value) return; m_video_track = value; Q_EMIT videoTrackChanged(); if (mpPlayer) mpPlayer->setVideoStream(value); } int QmlAVPlayer::bufferSize() const { return mpPlayer->bufferValue(); } void QmlAVPlayer::setBufferSize(int value) { if (mpPlayer->bufferValue() == value) return; if (mpPlayer) { mpPlayer->setBufferValue(value); Q_EMIT bufferSizeChanged(); } } QUrl QmlAVPlayer::externalAudio() const { return m_audio; } void QmlAVPlayer::setExternalAudio(const QUrl &url) { if (m_audio == url) return; m_audio = url; mpPlayer->setExternalAudio(QUrl::fromPercentEncoding(m_audio.toEncoded())); Q_EMIT externalAudioChanged(); } QVariantList QmlAVPlayer::externalAudioTracks() const { return mpPlayer ? mpPlayer->externalAudioTracks() : QVariantList(); } QVariantList QmlAVPlayer::internalAudioTracks() const { return mpPlayer ? mpPlayer->internalAudioTracks() : QVariantList(); } QVariantList QmlAVPlayer::internalVideoTracks() const { return mpPlayer ? mpPlayer->internalVideoTracks() : QVariantList(); } int QmlAVPlayer::internalSubtitleTrack() const { return m_sub_track; } void QmlAVPlayer::setInternalSubtitleTrack(int value) { if (m_sub_track == value) return; m_sub_track = value; Q_EMIT internalSubtitleTrackChanged(); if (mpPlayer) mpPlayer->setSubtitleStream(value); } QVariantList QmlAVPlayer::internalSubtitleTracks() const { return mpPlayer ? mpPlayer->internalSubtitleTracks() : QVariantList(); } QQmlListProperty QmlAVPlayer::audioFilters() { return QQmlListProperty(this, NULL, af_append, af_count, af_at, af_clear); } QQmlListProperty QmlAVPlayer::videoFilters() { return QQmlListProperty(this, NULL, vf_append, vf_count, vf_at, vf_clear); } void QmlAVPlayer::af_append(QQmlListProperty *property, QuickAudioFilter *value) { QmlAVPlayer* self = static_cast(property->object); self->m_afilters.append(value); if (self->mpPlayer) self->mpPlayer->installFilter(value); } int QmlAVPlayer::af_count(QQmlListProperty *property) { QmlAVPlayer* self = static_cast(property->object); return self->m_afilters.size(); } QuickAudioFilter* QmlAVPlayer::af_at(QQmlListProperty *property, int index) { QmlAVPlayer* self = static_cast(property->object); return self->m_afilters.at(index); } void QmlAVPlayer::af_clear(QQmlListProperty *property) { QmlAVPlayer* self = static_cast(property->object); if (self->mpPlayer) { foreach (QuickAudioFilter *f, self->m_afilters) { self->mpPlayer->uninstallFilter(f); } } self->m_afilters.clear(); } void QmlAVPlayer::vf_append(QQmlListProperty *property, QuickVideoFilter *value) { QmlAVPlayer* self = static_cast(property->object); self->m_vfilters.append(value); if (self->mpPlayer) self->mpPlayer->installFilter(value); } int QmlAVPlayer::vf_count(QQmlListProperty *property) { QmlAVPlayer* self = static_cast(property->object); return self->m_vfilters.size(); } QuickVideoFilter* QmlAVPlayer::vf_at(QQmlListProperty *property, int index) { QmlAVPlayer* self = static_cast(property->object); return self->m_vfilters.at(index); } void QmlAVPlayer::vf_clear(QQmlListProperty *property) { QmlAVPlayer* self = static_cast(property->object); if (self->mpPlayer) { foreach (QuickVideoFilter *f, self->m_vfilters) { self->mpPlayer->uninstallFilter(f); } } self->m_vfilters.clear(); } QStringList QmlAVPlayer::audioBackends() const { return m_ao; } void QmlAVPlayer::setAudioBackends(const QStringList &value) { if (m_ao == value) return; m_ao = value; Q_EMIT audioBackendsChanged(); } QStringList QmlAVPlayer::supportedAudioBackends() const { return AudioOutput::backendsAvailable(); } int QmlAVPlayer::loopCount() const { return mLoopCount; } void QmlAVPlayer::setLoopCount(int c) { if (c == 0) c = 1; else if (c < -1) c = -1; if (mLoopCount == c) { return; } mLoopCount = c; Q_EMIT loopCountChanged(); } qreal QmlAVPlayer::volume() const { return mVolume; } // mVolume, m_mute are required by qml properties. player.audio()->setXXX is not enought because player maybe not created void QmlAVPlayer::setVolume(qreal value) { if (mVolume < 0) { qWarning("volume must > 0"); return; } if (qFuzzyCompare(mVolume + 1.0, value + 1.0)) return; mVolume = value; Q_EMIT volumeChanged(); applyVolume(); } bool QmlAVPlayer::isMuted() const { return m_mute; } void QmlAVPlayer::setMuted(bool m) { if (isMuted() == m) return; m_mute = m; Q_EMIT mutedChanged(); applyVolume(); } int QmlAVPlayer::duration() const { return mpPlayer ? mpPlayer->duration() : 0; } int QmlAVPlayer::position() const { return mpPlayer ? mpPlayer->position() : 0; } bool QmlAVPlayer::isSeekable() const { return mpPlayer && mpPlayer->isSeekable(); } int QmlAVPlayer::startPosition() const { return mStart; } void QmlAVPlayer::setStartPosition(int value) { if (mStart == value) return; mStart = value; Q_EMIT startPositionChanged(); if (mpPlayer) { mpPlayer->setStartPosition(mStart); } } int QmlAVPlayer::stopPosition() const { return mStop; } void QmlAVPlayer::setStopPosition(int value) { if (mStop == value) return; mStop = value; Q_EMIT stopPositionChanged(); if (mpPlayer) { if (value == PositionMax) mpPlayer->setStopPosition(); else mpPlayer->setStopPosition(value); } } bool QmlAVPlayer::isFastSeek() const { return m_fastSeek; } void QmlAVPlayer::setFastSeek(bool value) { if (m_fastSeek == value) return; m_fastSeek = value; Q_EMIT fastSeekChanged(); } qreal QmlAVPlayer::bufferProgress() const { if (!mpPlayer) return 0; return mpPlayer->bufferProgress(); } QmlAVPlayer::Status QmlAVPlayer::status() const { if (!mpPlayer) return NoMedia; return (Status)mpPlayer->mediaStatus(); } QmlAVPlayer::Error QmlAVPlayer::error() const { return mError; } QString QmlAVPlayer::errorString() const { return mErrorString; } QmlAVPlayer::PlaybackState QmlAVPlayer::playbackState() const { return mPlaybackState; } void QmlAVPlayer::setPlaybackState(PlaybackState playbackState) { if (mPlaybackState == playbackState) { return; } if (!m_complete || !mpPlayer) return; switch (playbackState) { case PlayingState: if (mpPlayer->isPaused()) { mpPlayer->pause(false); } else { mpPlayer->setInterruptTimeout(m_timeout); mpPlayer->setInterruptOnTimeout(m_abort_timeout); mpPlayer->setRepeat(mLoopCount - 1); mpPlayer->setAudioStream(m_audio_track); mpPlayer->setVideoStream(m_video_track); // will check in case of error mpPlayer->setSubtitleStream(m_sub_track); if (!vcodec_opt.isEmpty()) { QVariantHash vcopt; for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) { vcopt[cit.key()] = cit.value(); } if (!vcopt.isEmpty()) mpPlayer->setOptionsForVideoCodec(vcopt); } if (!avfmt_opt.isEmpty()) { QVariantHash avfopt; for (QVariantMap::const_iterator cit = avfmt_opt.cbegin(); cit != avfmt_opt.cend(); ++cit) { avfopt[cit.key()] = cit.value(); } if (!avfopt.isEmpty()) mpPlayer->setOptionsForFormat(avfopt); } mpPlayer->setStartPosition(startPosition()); if (stopPosition() == PositionMax) mpPlayer->setStopPosition(); else mpPlayer->setStopPosition(stopPosition()); m_loading = true; // TODO: change backends is not thread safe now, so change when stopped mpPlayer->audio()->setBackends(m_ao); mpPlayer->play(); } break; case PausedState: mpPlayer->pause(true); mPlaybackState = PausedState; break; case StoppedState: mpPlayer->stop(); m_loading = false; mPlaybackState = StoppedState; break; default: break; } } qreal QmlAVPlayer::playbackRate() const { return mPlaybackRate; } void QmlAVPlayer::setPlaybackRate(qreal s) { if (playbackRate() == s) return; mPlaybackRate = s; if (mpPlayer) mpPlayer->setSpeed(s); Q_EMIT playbackRateChanged(); } AVPlayer* QmlAVPlayer::player() { return mpPlayer; } void QmlAVPlayer::play(const QUrl &url) { if (mSource == url && (playbackState() != StoppedState || m_loading)) return; setSource(url); if (!autoPlay()) play(); } void QmlAVPlayer::play() { // if not autoPlay, maybe a different source was set and play() was not called if (isAutoLoad() && (playbackState() == PlayingState || m_loading)) return; setPlaybackState(PlayingState); } void QmlAVPlayer::pause() { setPlaybackState(PausedState); } void QmlAVPlayer::stop() { setPlaybackState(StoppedState); } void QmlAVPlayer::stepForward() { if (!mpPlayer) return; mpPlayer->stepForward(); } void QmlAVPlayer::stepBackward() { if (!mpPlayer) return; return mpPlayer->stepBackward(); } void QmlAVPlayer::seek(int offset) { if (!mpPlayer) return; mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek); mpPlayer->seek(qint64(offset)); } void QmlAVPlayer::seekForward() { if (!mpPlayer) return; mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek); mpPlayer->seekForward(); } void QmlAVPlayer::seekBackward() { if (!mpPlayer) return; mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek); mpPlayer->seekBackward(); } void QmlAVPlayer::_q_error(const AVError &e) { mError = NoError; mErrorString = e.string(); const AVError::ErrorCode ec = e.error(); if (ec <= AVError::NoError) mError = NoError; else if (ec <= AVError::NetworkError) mError = NetworkError; else if (ec <= AVError::ResourceError) mError = ResourceError; else if (ec <= AVError::FormatError) mError = FormatError; else if (ec <= AVError::AccessDenied) mError = AccessDenied; //else // err = ServiceMissing; if (ec != AVError::NoError) m_loading = false; Q_EMIT error(mError, mErrorString); Q_EMIT errorChanged(); } void QmlAVPlayer::_q_statusChanged() { Q_EMIT statusChanged(); } void QmlAVPlayer::_q_paused(bool p) { if (p) { mPlaybackState = PausedState; Q_EMIT paused(); } else { mPlaybackState = PlayingState; Q_EMIT playing(); } Q_EMIT playbackStateChanged(); } void QmlAVPlayer::_q_started() { m_loading = false; mPlaybackState = PlayingState; applyChannelLayout(); // applyChannelLayout() first because it may reopen audio device applyVolume(); //sender is AVPlayer mpPlayer->audio()->setMute(isMuted()); mpPlayer->setSpeed(playbackRate()); // TODO: in load()? m_metaData->setValuesFromStatistics(mpPlayer->statistics()); if (!mHasAudio) { mHasAudio = !mpPlayer->internalAudioTracks().isEmpty(); if (mHasAudio) Q_EMIT hasAudioChanged(); } if (!mHasVideo) { mHasVideo = mpPlayer->videoStreamCount() > 0; if (mHasVideo) Q_EMIT hasVideoChanged(); } Q_EMIT playing(); Q_EMIT playbackStateChanged(); } void QmlAVPlayer::_q_stopped() { mPlaybackState = StoppedState; Q_EMIT stopped(); Q_EMIT playbackStateChanged(); } void QmlAVPlayer::applyVolume() { AudioOutput *ao = mpPlayer->audio(); if (!ao || !ao->isAvailable()) return; if (!sender() || qobject_cast(sender()) != ao) { ao->setVolume(volume()); // will omit if value is not changed ao->setMute(isMuted()); return; } // from ao.reportVolume() reportMute() setVolume(ao->volume());// will omit if value is not changed setMuted(ao->isMute()); } void QmlAVPlayer::applyChannelLayout() { AudioOutput *ao = mpPlayer->audio(); if (!ao || !ao->isAvailable()) return; AudioFormat af = ao->audioFormat(); AudioFormat::ChannelLayout ch = toAudioFormatChannelLayout(channelLayout()); if (channelLayout() == ChannelLayoutAuto || ch == af.channelLayout()) return; af.setChannelLayout(ch); if (!ao->close()) { qWarning("close audio failed"); return; } ao->setAudioFormat(af); if (!ao->open()) { qWarning("open audio failed"); return; } } QtAV-1.12.0/qml/QuickFBORenderer.cpp000066400000000000000000000313031312235004300167660ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/QuickFBORenderer.h" #include "QmlAV/QmlAVPlayer.h" #include "QtAV/AVPlayer.h" #include "QtAV/OpenGLVideo.h" #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include #include // for dynamicgl. qglfunctions before qt5.3 does not have portable gl functions // use qt gl func if possible to avoid linking to opengl directly #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) #include #define DYGL(glFunc) QOpenGLContext::currentContext()->functions()->glFunc #else #define DYGL(glFunc) glFunc #endif namespace QtAV { static const VideoRendererId VideoRendererId_QuickFBO = mkid::id32base36_4<'Q','F','B','O'>::value; FACTORY_REGISTER(VideoRenderer, QuickFBO, "QuickFBO") class FBORenderer : public QQuickFramebufferObject::Renderer { public: FBORenderer(QuickFBORenderer* item) : m_item(item) {} QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) Q_DECL_OVERRIDE { m_item->fboSizeChanged(size); return QQuickFramebufferObject::Renderer::createFramebufferObject(size); } void render() Q_DECL_OVERRIDE { Q_ASSERT(m_item); m_item->renderToFbo(framebufferObject()); } void synchronize(QQuickFramebufferObject *item) Q_DECL_OVERRIDE { m_item = static_cast(item); } private: QuickFBORenderer *m_item; }; class QuickFBORendererPrivate : public VideoRendererPrivate { public: QuickFBORendererPrivate(): VideoRendererPrivate() , frame_changed(false) , opengl(true) , fill_mode(QuickFBORenderer::PreserveAspectFit) , node(0) , source(0) , glctx(0) {} void setupAspectRatio() { //TODO: call when out_rect, renderer_size, orientation changed matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); if (orientation) matrix.rotate(orientation, 0, 0, 1); // Z axis // FIXME: why x/y is mirrored? if (orientation%180) matrix.scale(-1, 1); else matrix.scale(1, -1); } bool frame_changed; bool opengl; QuickFBORenderer::FillMode fill_mode; QSGNode *node; QObject *source; QOpenGLContext *glctx; QMatrix4x4 matrix; OpenGLVideo glv; QOpenGLFramebufferObject *fbo; QList filters; }; QuickFBORenderer::QuickFBORenderer(QQuickItem *parent) : QQuickFramebufferObject(parent) , VideoRenderer(*new QuickFBORendererPrivate()) { setPreferredPixelFormat(VideoFormat::Format_YUV420P); } VideoRendererId QuickFBORenderer::id() const { return VideoRendererId_QuickFBO; } QQuickFramebufferObject::Renderer* QuickFBORenderer::createRenderer() const { return new FBORenderer((QuickFBORenderer*)this); } bool QuickFBORenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { if (pixfmt == VideoFormat::Format_RGB48BE || pixfmt == VideoFormat::Format_Invalid) return false; if (!isOpenGL()) return VideoFormat::isRGB(pixfmt); return OpenGLVideo::isSupported(pixfmt); } OpenGLVideo* QuickFBORenderer::opengl() const { return const_cast(&d_func().glv); } bool QuickFBORenderer::receiveFrame(const VideoFrame &frame) { DPTR_D(QuickFBORenderer); d.video_frame = frame; d.frame_changed = true; // update(); // why update slow? because of calling in a different thread? //QMetaObject::invokeMethod(this, "update"); // slower than directly postEvent QCoreApplication::postEvent(this, new QEvent(QEvent::User)); return true; } QObject* QuickFBORenderer::source() const { return d_func().source; } void QuickFBORenderer::setSource(QObject *source) { DPTR_D(QuickFBORenderer); if (d.source == source) return; d.source = source; Q_EMIT sourceChanged(); if (!source) return; ((QmlAVPlayer*)source)->player()->addVideoRenderer(this); } QuickFBORenderer::FillMode QuickFBORenderer::fillMode() const { return d_func().fill_mode; } void QuickFBORenderer::setFillMode(FillMode mode) { DPTR_D(QuickFBORenderer); if (d.fill_mode == mode) return; d_func().fill_mode = mode; updateRenderRect(); Q_EMIT fillModeChanged(mode); } QRectF QuickFBORenderer::contentRect() const { return videoRect(); } QRectF QuickFBORenderer::sourceRect() const { return QRectF(QPointF(), videoFrameSize()); } QPointF QuickFBORenderer::mapPointToItem(const QPointF &point) const { if (videoFrameSize().isEmpty()) return QPointF(); // Just normalize and use that function // m_nativeSize is transposed in some orientations if (orientation()%180 == 0) return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().width(), point.y() / videoFrameSize().height())); else return mapNormalizedPointToItem(QPointF(point.x() / videoFrameSize().height(), point.y() / videoFrameSize().width())); } QRectF QuickFBORenderer::mapRectToItem(const QRectF &rectangle) const { return QRectF(mapPointToItem(rectangle.topLeft()), mapPointToItem(rectangle.bottomRight())).normalized(); } QPointF QuickFBORenderer::mapNormalizedPointToItem(const QPointF &point) const { qreal dx = point.x(); qreal dy = point.y(); if (orientation()%180 == 0) { dx *= contentRect().width(); dy *= contentRect().height(); } else { dx *= contentRect().height(); dy *= contentRect().width(); } switch (orientation()) { case 0: default: return contentRect().topLeft() + QPointF(dx, dy); case 90: return contentRect().bottomLeft() + QPointF(dy, -dx); case 180: return contentRect().bottomRight() + QPointF(-dx, -dy); case 270: return contentRect().topRight() + QPointF(-dy, dx); } } QRectF QuickFBORenderer::mapNormalizedRectToItem(const QRectF &rectangle) const { return QRectF(mapNormalizedPointToItem(rectangle.topLeft()), mapNormalizedPointToItem(rectangle.bottomRight())).normalized(); } QPointF QuickFBORenderer::mapPointToSource(const QPointF &point) const { QPointF norm = mapPointToSourceNormalized(point); if (orientation()%180 == 0) return QPointF(norm.x() * videoFrameSize().width(), norm.y() * videoFrameSize().height()); else return QPointF(norm.x() * videoFrameSize().height(), norm.y() * videoFrameSize().width()); } QRectF QuickFBORenderer::mapRectToSource(const QRectF &rectangle) const { return QRectF(mapPointToSource(rectangle.topLeft()), mapPointToSource(rectangle.bottomRight())).normalized(); } QPointF QuickFBORenderer::mapPointToSourceNormalized(const QPointF &point) const { if (contentRect().isEmpty()) return QPointF(); // Normalize the item source point qreal nx = (point.x() - contentRect().x()) / contentRect().width(); qreal ny = (point.y() - contentRect().y()) / contentRect().height(); switch (orientation()) { case 0: default: return QPointF(nx, ny); case 90: return QPointF(1.0 - ny, nx); case 180: return QPointF(1.0 - nx, 1.0 - ny); case 270: return QPointF(ny, 1.0 - nx); } } QRectF QuickFBORenderer::mapRectToSourceNormalized(const QRectF &rectangle) const { return QRectF(mapPointToSourceNormalized(rectangle.topLeft()), mapPointToSourceNormalized(rectangle.bottomRight())).normalized(); } bool QuickFBORenderer::isOpenGL() const { return d_func().opengl; } void QuickFBORenderer::setOpenGL(bool o) { DPTR_D(QuickFBORenderer); if (d.opengl == o) return; d.opengl = o; Q_EMIT openGLChanged(); if (o) setPreferredPixelFormat(VideoFormat::Format_YUV420P); else setPreferredPixelFormat(VideoFormat::Format_RGB32); } void QuickFBORenderer::fboSizeChanged(const QSize &size) { DPTR_D(QuickFBORenderer); d.update_background = true; resizeRenderer(size); if (d.glctx != QOpenGLContext::currentContext()) { d.glctx = QOpenGLContext::currentContext(); d.glv.setOpenGLContext(d.glctx); // will set viewport. but maybe wrong value for hi dpi } // ensure viewport is correct set d.glv.setProjectionMatrixToRect(QRectF(0, 0, size.width(), size.height())); d.setupAspectRatio(); } void QuickFBORenderer::renderToFbo(QOpenGLFramebufferObject *fbo) { d_func().fbo = fbo; handlePaintEvent(); } QQmlListProperty QuickFBORenderer::filters() { return QQmlListProperty(this, NULL, vf_append, vf_count, vf_at, vf_clear); } void QuickFBORenderer::vf_append(QQmlListProperty *property, QuickVideoFilter *value) { QuickFBORenderer* self = static_cast(property->object); self->d_func().filters.append(value); self->installFilter(value); } int QuickFBORenderer::vf_count(QQmlListProperty *property) { QuickFBORenderer* self = static_cast(property->object); return self->d_func().filters.size(); } QuickVideoFilter* QuickFBORenderer::vf_at(QQmlListProperty *property, int index) { QuickFBORenderer* self = static_cast(property->object); return self->d_func().filters.at(index); } void QuickFBORenderer::vf_clear(QQmlListProperty *property) { QuickFBORenderer* self = static_cast(property->object); foreach (QuickVideoFilter *f, self->d_func().filters) { self->uninstallFilter(f); } self->d_func().filters.clear(); } void QuickFBORenderer::drawBackground() { if (backgroundRegion().isEmpty()) return; DPTR_D(QuickFBORenderer); d.fbo->bind(); DYGL(glViewport(0, 0, d.fbo->width(), d.fbo->height())); d.glv.fill(backgroundColor()); } void QuickFBORenderer::drawFrame() { DPTR_D(QuickFBORenderer); d.fbo->bind(); DYGL(glViewport(0, 0, d.fbo->width(), d.fbo->height())); if (!d.video_frame.isValid()) { d.glv.fill(QColor(0, 0, 0, 0)); return; } if (d.frame_changed) { d.glv.setCurrentFrame(d.video_frame); d.frame_changed = false; } d.glv.render(QRectF(), realROI(), d.matrix); } bool QuickFBORenderer::event(QEvent *e) { if (e->type() != QEvent::User) return QQuickFramebufferObject::event(e); update(); return true; } bool QuickFBORenderer::onSetOrientation(int value) { Q_UNUSED(value); d_func().setupAspectRatio(); return true; } void QuickFBORenderer::onSetOutAspectRatio(qreal ratio) { Q_UNUSED(ratio); DPTR_D(QuickFBORenderer); d.setupAspectRatio(); } void QuickFBORenderer::onSetOutAspectRatioMode(OutAspectRatioMode mode) { Q_UNUSED(mode); DPTR_D(QuickFBORenderer); d.setupAspectRatio(); } bool QuickFBORenderer::onSetBrightness(qreal b) { d_func().glv.setBrightness(b); return true; } bool QuickFBORenderer::onSetContrast(qreal c) { d_func().glv.setContrast(c); return true; } bool QuickFBORenderer::onSetHue(qreal h) { d_func().glv.setHue(h); return true; } bool QuickFBORenderer::onSetSaturation(qreal s) { d_func().glv.setSaturation(s); return true; } void QuickFBORenderer::updateRenderRect() { DPTR_D(QuickFBORenderer); if (d.fill_mode == Stretch) { setOutAspectRatioMode(RendererAspectRatio); } else {//compute out_rect fits video aspect ratio then compute again if crop setOutAspectRatioMode(VideoAspectRatio); } //update(); d.setupAspectRatio(); } } //namespace QtAV QtAV-1.12.0/qml/QuickFilter.cpp000066400000000000000000000106431312235004300161220ustar00rootroot00000000000000#include "QmlAV/QuickFilter.h" #include "QtAV/private/Filter_p.h" #include "QtAV/LibAVFilter.h" #include "QtAV/GLSLFilter.h" #include "QtAV/VideoShaderObject.h" #include "QtAV/OpenGLVideo.h" //namespace QtAV { class QuickVideoFilterPrivate : public VideoFilterPrivate { public: QuickVideoFilterPrivate() : type(QuickVideoFilter::AVFilter) , avfilter(new LibAVFilterVideo()) , glslfilter(new GLSLFilter()) { filter = avfilter.data(); } QuickVideoFilter::FilterType type; VideoFilter *filter; QScopedPointer user_filter; QScopedPointer avfilter; QScopedPointer glslfilter; }; QuickVideoFilter::QuickVideoFilter(QObject *parent) : VideoFilter(*new QuickVideoFilterPrivate(), parent) { DPTR_D(QuickVideoFilter); connect(d.avfilter.data(), SIGNAL(optionsChanged()), this, SIGNAL(avfilterChanged())); } bool QuickVideoFilter::isSupported(VideoFilterContext::Type ct) const { DPTR_D(const QuickVideoFilter); if (d.filter) return d.filter->isSupported(ct); return false; } QuickVideoFilter::FilterType QuickVideoFilter::type() const { return d_func().type; } void QuickVideoFilter::setType(FilterType value) { DPTR_D(QuickVideoFilter); if (d.type == value) return; d.type = value; if (value == GLSLFilter) d.filter = d.glslfilter.data(); else if (value == AVFilter) d.filter = d.avfilter.data(); else d.filter = d.user_filter.data(); Q_EMIT typeChanged(); } QStringList QuickVideoFilter::supportedAVFilters() const { return d_func().avfilter->filters(); } QString QuickVideoFilter::avfilter() const { return d_func().avfilter->options(); } void QuickVideoFilter::setAVFilter(const QString &options) { d_func().avfilter->setOptions(options); } VideoFilter* QuickVideoFilter::userFilter() const { return d_func().user_filter.data(); } void QuickVideoFilter::setUserFilter(VideoFilter *f) { DPTR_D(QuickVideoFilter); if (d.user_filter.data() == f) return; d.user_filter.reset(f); Q_EMIT userFilterChanged(); } DynamicShaderObject* QuickVideoFilter::shader() const { return static_cast(d_func().glslfilter->opengl()->userShader()); } void QuickVideoFilter::setShader(DynamicShaderObject *value) { DPTR_D(QuickVideoFilter); if (shader() == value) return; d.glslfilter->opengl()->setUserShader(value); Q_EMIT shaderChanged(); } void QuickVideoFilter::process(Statistics *statistics, VideoFrame *frame) { DPTR_D(QuickVideoFilter); if (!d.filter) return; d.filter->apply(statistics, frame); } class QuickAudioFilterPrivate : public AudioFilterPrivate { public: QuickAudioFilterPrivate() : AudioFilterPrivate() , type(QuickAudioFilter::AVFilter) , avfilter(new LibAVFilterAudio()) { filter = avfilter.data(); } QuickAudioFilter::FilterType type; AudioFilter *filter; QScopedPointer user_filter; QScopedPointer avfilter; }; QuickAudioFilter::QuickAudioFilter(QObject *parent) : AudioFilter(*new QuickAudioFilterPrivate(), parent) { DPTR_D(QuickAudioFilter); connect(d.avfilter.data(), SIGNAL(optionsChanged()), this, SIGNAL(avfilterChanged())); } QuickAudioFilter::FilterType QuickAudioFilter::type() const { return d_func().type; } void QuickAudioFilter::setType(FilterType value) { DPTR_D(QuickAudioFilter); if (d.type == value) return; d.type = value; if (value == AVFilter) d.filter = d.avfilter.data(); else d.filter = d.user_filter.data(); Q_EMIT typeChanged(); } QStringList QuickAudioFilter::supportedAVFilters() const { return d_func().avfilter->filters(); } QString QuickAudioFilter::avfilter() const { return d_func().avfilter->options(); } void QuickAudioFilter::setAVFilter(const QString &options) { d_func().avfilter->setOptions(options); } AudioFilter *QuickAudioFilter::userFilter() const { return d_func().user_filter.data(); } void QuickAudioFilter::setUserFilter(AudioFilter *f) { DPTR_D(QuickAudioFilter); if (d.user_filter.data() == f) return; d.user_filter.reset(f); Q_EMIT userFilterChanged(); } void QuickAudioFilter::process(Statistics *statistics, AudioFrame *frame) { DPTR_D(QuickAudioFilter); if (!d.filter) return; d.filter->apply(statistics, frame); } //} //namespace QtAV QtAV-1.12.0/qml/QuickSubtitle.cpp000066400000000000000000000115401312235004300164650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/QuickSubtitle.h" #include "QmlAV/QmlAVPlayer.h" #include #include #include using namespace QtAV; class QuickSubtitle::Filter : public QtAV::VideoFilter { public: Filter(Subtitle *sub, QuickSubtitle *parent) : VideoFilter(parent) , m_empty_image(false) , m_sub(sub) , m_subject(parent) {} protected: virtual void process(Statistics* statistics, VideoFrame* frame) { Q_UNUSED(statistics); if (!m_sub) return; if (frame && frame->timestamp() > 0.0) { m_sub->setTimestamp(frame->timestamp()); //TODO: set to current display video frame's timestamp QRect r; QImage image(m_sub->getImage(frame->width(), frame->height(), &r)); if (image.isNull()) { if (m_empty_image) return; m_empty_image = true; } else { m_empty_image = false; } m_subject->notifyObservers(image, r, frame->width(), frame->height()); } } private: bool m_empty_image; Subtitle *m_sub; QuickSubtitle *m_subject; }; QuickSubtitle::QuickSubtitle(QObject *parent) : QObject(parent) , SubtitleAPIProxy(this) , m_enable(true) , m_player(0) , m_player_sub(new PlayerSubtitle(this)) , m_filter(0) { QmlAVPlayer *p = qobject_cast(parent); if (p) setPlayer(p); m_filter = new Filter(m_player_sub->subtitle(), this); setSubtitle(m_player_sub->subtitle()); //for proxy connect(this, SIGNAL(enabledChanged(bool)), m_player_sub, SLOT(onEnabledChanged(bool))); ////// connect(m_player_sub, SIGNAL(autoLoadChanged(bool)), this, SIGNAL(autoLoadChanged())); connect(m_player_sub, SIGNAL(fileChanged()), this, SIGNAL(fileChanged())); } QString QuickSubtitle::getText() const { return m_player_sub->subtitle()->getText(); } void QuickSubtitle::addObserver(QuickSubtitleObserver *ob) { if (!m_observers.contains(ob)) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); m_observers.append(ob); } } void QuickSubtitle::removeObserver(QuickSubtitleObserver *ob) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); m_observers.removeAll(ob); } void QuickSubtitle::notifyObservers(const QImage &image, const QRect &r, int width, int height, QuickSubtitleObserver *ob) { if (ob) { ob->update(image, r, width, height); return; } QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (m_observers.isEmpty()) return; foreach (QuickSubtitleObserver* o, m_observers) { o->update(image, r, width, height); } } void QuickSubtitle::setPlayer(QObject *player) { QmlAVPlayer *p = qobject_cast(player); if (m_player == p) return; if (m_player) m_filter->uninstall(); m_player = p; if (!p) return; m_filter->installTo(p->player()); // ~Filter() can not call uninstall() unless player is still exists // TODO: check AVPlayer null? m_player_sub->setPlayer(p->player()); } QObject* QuickSubtitle::player() { return m_player; } void QuickSubtitle::setEnabled(bool value) { if (m_enable == value) return; m_enable = value; Q_EMIT enabledChanged(value); m_filter->setEnabled(m_enable); if (!m_enable) { //display nothing notifyObservers(QImage(), QRect(), 0, 0); } } bool QuickSubtitle::isEnabled() const { return m_enable; } void QuickSubtitle::setFile(const QString &file) { m_player_sub->setFile(file); } QString QuickSubtitle::file() const { return m_player_sub->file(); } void QuickSubtitle::setAutoLoad(bool value) { m_player_sub->setAutoLoad(value); } bool QuickSubtitle::autoLoad() const { return m_player_sub->autoLoad(); } QtAV-1.12.0/qml/QuickSubtitleItem.cpp000066400000000000000000000110701312235004300173020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QuickSubtitleItem.h" #include #include #include QuickSubtitleItem::QuickSubtitleItem(QQuickItem *parent) : QQuickItem(parent) , m_sub(0) , m_texture(0) , m_remap(false) , m_fillMode(Qt::KeepAspectRatio) , m_w_sub(0) , m_h_sub(0) { setFlag(QQuickItem::ItemHasContents, true); } QuickSubtitleItem::~QuickSubtitleItem() { if (m_texture) { delete m_texture; m_texture = 0; } } void QuickSubtitleItem::setSource(QuickSubtitle *s) { if (m_sub == s) return; if (m_sub) m_sub->removeObserver(this); m_sub = s; emit sourceChanged(); if (m_sub) m_sub->addObserver(this); } QuickSubtitle* QuickSubtitleItem::source() const { return m_sub; } void QuickSubtitleItem::setFillMode(int value) { if (m_fillMode == value) return; m_fillMode = value; m_remap = true; emit fillModeChanged(); } int QuickSubtitleItem::fillMode() const { return m_fillMode; } void QuickSubtitleItem::update(const QImage &image, const QRect &r, int width, int height) { { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); m_image = image; //lock if (m_rect != r || m_w_sub != width || m_h_sub != height) { m_remap = true; m_rect = r; m_w_sub = width; m_h_sub = height; } } QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } QRectF QuickSubtitleItem::mapSubRect(const QRect &rect, qreal w, qreal h) { if (w == 0 || h == 0) return QRectF(); if (!m_remap) return m_rect_mapped; m_remap = false; qreal ww = width(); qreal hh = height(); qreal dx = 0; qreal dy = 0; if (m_fillMode == Qt::KeepAspectRatio) { if (ww*h > w*hh) { //item is too wide ww = hh*w/h; dx = (width() - ww)/2.0; } else { hh = ww*h/w; dy = (height() - hh)/2.0; } } m_rect_mapped.setX(qreal(rect.x())*ww/w); m_rect_mapped.setY(qreal(rect.y())*hh/h); m_rect_mapped.setWidth(qreal(rect.width())*ww/w); m_rect_mapped.setHeight(qreal(rect.height())*hh/h); m_rect_mapped.moveTo(m_rect_mapped.topLeft() + QPointF(dx, dy)); //qDebug() << boundingRect() << " " << width <<"x"<(node); if (!node) { node = new QSGSimpleTextureNode(); stn = static_cast(node); stn->setFiltering(QSGTexture::Linear); } stn->setRect(mapSubRect(m_rect, m_w_sub, m_h_sub)); if (m_texture) delete m_texture; { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); m_texture = window()->createTextureFromImage(m_image); } stn->setTexture(m_texture); node->markDirty(QSGNode::DirtyGeometry); return node; } bool QuickSubtitleItem::event(QEvent *e) { if (e->type() != QEvent::User) return QQuickItem::event(e); QQuickItem::update(); return true; } void QuickSubtitleItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickItem::geometryChanged(newGeometry, oldGeometry); //geometry will be updated m_remap = true; QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } QtAV-1.12.0/qml/QuickVideoPreview.cpp000066400000000000000000000051401312235004300173010ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/QuickVideoPreview.h" #include namespace QtAV { QuickVideoPreview::QuickVideoPreview(QQuickItem *parent) : BaseQuickRenderer(parent) { connect(&m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged())); connect(&m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame))); connect(&m_extractor, SIGNAL(error()), SLOT(displayNoFrame())); connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame())); } void QuickVideoPreview::setTimestamp(int value) { m_extractor.setPosition((qint64)value); } int QuickVideoPreview::timestamp() const { return (int)m_extractor.position(); } void QuickVideoPreview::setFile(const QUrl &value) { if (m_file == value) return; m_file = value; emit fileChanged(); m_extractor.setSource(QUrl::fromPercentEncoding(m_file.toEncoded())); } QUrl QuickVideoPreview::file() const { return m_file; } void QuickVideoPreview::displayFrame(const QtAV::VideoFrame &frame) { int diff = qAbs(qint64(frame.timestamp()*1000.0) - m_extractor.position()); if (diff > m_extractor.precision()) { //qWarning("timestamp difference (%d/%lld) is too large! ignore", diff); } if (isOpenGL() || frame.imageFormat() != QImage::Format_Invalid) { receive(frame); return; } VideoFrame f(frame.to(VideoFormat::Format_RGB32, boundingRect().toRect().size())); if (!f.isValid()) return; receive(f); } void QuickVideoPreview::displayNoFrame() { receive(VideoFrame()); } } //namespace QtAV QtAV-1.12.0/qml/SGVideoNode.cpp000066400000000000000000000155471312235004300160160ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QmlAV/SGVideoNode.h" #include "QtAV/VideoShader.h" #include "QtAV/VideoFrame.h" #include #include #include // all in QSGRenderThread namespace QtAV { class SGVideoMaterialShader : public QSGMaterialShader { public: SGVideoMaterialShader(VideoShader* s) : QSGMaterialShader() , m_shader(s) {} virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); virtual char const *const *attributeNames() const { return m_shader->attributeNames();} protected: virtual const char *vertexShader() const { return m_shader->vertexShader();} virtual const char *fragmentShader() const { return m_shader->fragmentShader();} virtual void initialize() { m_shader->initialize(program());} int textureLocationCount() const { return m_shader->textureLocationCount();} int textureLocation(int index) const { return m_shader->textureLocation(index);} int matrixLocation() const { return m_shader->matrixLocation();} int colorMatrixLocation() const { return m_shader->colorMatrixLocation();} int opacityLocation() const { return m_shader->opacityLocation();} private: QScopedPointer m_shader; }; class SGVideoMaterial : public QSGMaterial { public: SGVideoMaterial() : QSGMaterial(), m_opacity(1.0) {} virtual QSGMaterialType *type() const { return reinterpret_cast((quintptr)m_material.type()); } virtual QSGMaterialShader *createShader() const { return new SGVideoMaterialShader(m_material.createShader()); } virtual int compare(const QSGMaterial *other) const { Q_ASSERT(other && type() == other->type()); const SGVideoMaterial *m = static_cast(other); return m_material.compare(&m->m_material); } /* void updateBlending() { setFlag(Blending, m_material.hasAlpha() || !qFuzzyCompare(m_opacity, qreal(1.0))); } */ void setCurrentFrame(const VideoFrame &frame) { m_material.setCurrentFrame(frame); setFlag(Blending, frame.format().hasAlpha()); } VideoMaterial* videoMaterial() { return &m_material;} qreal m_opacity; VideoMaterial m_material; }; void SGVideoMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) { Q_UNUSED(oldMaterial); SGVideoMaterial *mat = static_cast(newMaterial); if (!m_shader->update(&mat->m_material)) //material not ready. e.g. video item have not got a frame return; //mat->updateBlending(); if (state.isOpacityDirty()) { mat->m_opacity = state.opacity(); program()->setUniformValue(opacityLocation(), GLfloat(mat->m_opacity)); } if (state.isMatrixDirty()) program()->setUniformValue(matrixLocation(), state.combinedMatrix()); } SGVideoNode::SGVideoNode() : QSGGeometryNode() , m_material(new SGVideoMaterial()) , m_validWidth(1.0) { setFlag(QSGNode::OwnsGeometry); setFlag(QSGNode::OwnsMaterial); setMaterial(m_material); } SGVideoNode::~SGVideoNode() {} void SGVideoNode::setCurrentFrame(const VideoFrame &frame) { m_material->setCurrentFrame(frame); markDirty(DirtyMaterial); } /* Helpers */ static inline void qSetGeom(QSGGeometry::TexturedPoint2D *v, const QPointF &p) { v->x = p.x(); v->y = p.y(); } static inline void qSetTex(QSGGeometry::TexturedPoint2D *v, const QPointF &p) { v->tx = p.x(); v->ty = p.y(); } void SGVideoNode::setTexturedRectGeometry(const QRectF &rect, const QRectF &textureRect, int orientation) { if (m_validWidth == m_material->videoMaterial()->validTextureWidth() && rect == m_rect && textureRect == m_textureRect && orientation == m_orientation) return; QRectF validTexRect = m_material->videoMaterial()->normalizedROI(textureRect); if (!validTexRect.isEmpty()) { m_validWidth = m_material->videoMaterial()->validTextureWidth(); m_rect = rect; m_textureRect = textureRect; m_orientation = orientation; } //qDebug() << ">>>>>>>valid: " << m_validWidth << " roi: " << validTexRect; QSGGeometry *g = geometry(); if (g == 0) g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); QSGGeometry::TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D(); // Set geometry first qSetGeom(v + 0, rect.topLeft()); qSetGeom(v + 1, rect.bottomLeft()); qSetGeom(v + 2, rect.topRight()); qSetGeom(v + 3, rect.bottomRight()); // and then texture coordinates switch (orientation) { default: // tl, bl, tr, br qSetTex(v + 0, validTexRect.topLeft()); qSetTex(v + 1, validTexRect.bottomLeft()); qSetTex(v + 2, validTexRect.topRight()); qSetTex(v + 3, validTexRect.bottomRight()); break; case 90: // tr, tl, br, bl qSetTex(v + 0, validTexRect.topRight()); qSetTex(v + 1, validTexRect.topLeft()); qSetTex(v + 2, validTexRect.bottomRight()); qSetTex(v + 3, validTexRect.bottomLeft()); break; case 180: // br, tr, bl, tl qSetTex(v + 0, validTexRect.bottomRight()); qSetTex(v + 1, validTexRect.topRight()); qSetTex(v + 2, validTexRect.bottomLeft()); qSetTex(v + 3, validTexRect.topLeft()); break; case 270: // bl, br, tl, tr qSetTex(v + 0, validTexRect.bottomLeft()); qSetTex(v + 1, validTexRect.bottomRight()); qSetTex(v + 2, validTexRect.topLeft()); qSetTex(v + 3, validTexRect.topRight()); break; } if (!geometry()) setGeometry(g); markDirty(DirtyGeometry); } } //namespace QtAV QtAV-1.12.0/qml/Video.qml000066400000000000000000000550141312235004300147560ustar00rootroot00000000000000 import QtQuick 2.0 import QtAV 1.7 /*! \qmltype Video \inherits Item \ingroup multimedia_qml \ingroup multimedia_video_qml \inqmlmodule QtAV \brief A convenience type for showing a specified video. \c Video is a convenience type combining the functionality of a \l MediaPlayer and a \l VideoOutput into one. It provides simple video playback functionality without having to declare multiple types. \qml import QtQuick 2.0 import QtAV 1.7 Video { id: video width : 800 height : 600 source: "video.avi" MouseArea { anchors.fill: parent onClicked: { video.play() } } focus: true Keys.onSpacePressed: video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play() Keys.onLeftPressed: video.seek(video.position - 5000) Keys.onRightPressed: video.seek(video.position + 5000) } \endqml \c Video supports untransformed, stretched, and uniformly scaled video presentation. For a description of stretched uniformly scaled presentation, see the \l fillMode property description. \sa MediaPlayer, VideoOutput \section1 Screen Saver If it is likely that an application will be playing video for an extended period of time without user interaction, it may be necessary to disable the platform's screen saver. The \l ScreenSaver (from \l QtSystemInfo) may be used to disable the screensaver in this fashion: \qml import QtSystemInfo 5.0 ScreenSaver { screenSaverEnabled: false } \endqml */ Item { id: video property alias startPosition: player.startPosition property alias stopPosition: player.stopPosition property alias videoFiltersGPU: videoOut.filters property alias audioFilters: player.audioFilters property alias videoFilters: player.videoFilters property alias audioBackends: player.audioBackends property alias supportedAudioBackends: player.supportedAudioBackends property alias backgroundColor: videoOut.backgroundColor property alias brightness: videoOut.brightness property alias contrast: videoOut.contrast property alias hue: videoOut.hue property alias saturation: videoOut.saturation property alias frameSize: videoOut.frameSize property alias sourceAspectRatio: videoOut.sourceAspectRatio property alias opengl: videoOut.opengl property alias fastSeek: player.fastSeek property alias timeout: player.timeout property alias abortOnTimeout: player.abortOnTimeout property alias subtitle: subtitle property alias subtitleText: text_sub // not for ass. property alias videoCapture: player.videoCapture property alias audioTrack: player.audioTrack property alias videoTrack: player.videoTrack property alias externalAudio: player.externalAudio property alias internalAudioTracks: player.internalAudioTracks property alias externalAudioTracks: player.externalAudioTracks property alias internalVideoTracks: player.internalVideoTracks /*** Properties of VideoOutput ***/ /*! \qmlproperty enumeration Video::fillMode Set this property to define how the video is scaled to fit the target area. \list \li VideoOutput.Stretch - the video is scaled to fit \li VideoOutput.PreserveAspectFit - the video is scaled uniformly to fit without cropping \li VideoOutput.PreserveAspectCrop - the video is scaled uniformly to fill, cropping if necessary \endlist Because this type is for convenience in QML, it does not support enumerations directly, so enumerations from \c VideoOutput are used to access the available fill modes. The default fill mode is preserveAspectFit. */ property alias fillMode: videoOut.fillMode /*! \qmlproperty int Video::orientation The orientation of the \c Video in degrees. Only multiples of 90 degrees is supported, that is 0, 90, 180, 270, 360, etc. */ property alias orientation: videoOut.orientation /*** Properties of MediaPlayer ***/ /*! A list of video codec names in priority order. Example: videoCodecPriority: ["VAAPI", "FFmpeg"] Default is ["FFmpeg"] s*/ property alias videoCodecPriority: player.videoCodecPriority property alias channelLayout: player.channelLayout /*! \qmlproperty enumeration Video::playbackState This read only property indicates the playback state of the media. \list \li MediaPlayer.PlayingState - the media is playing \li MediaPlayer.PausedState - the media is paused \li MediaPlayer.StoppedState - the media is stopped \endlist The default state is MediaPlayer.StoppedState. */ property alias playbackState: player.playbackState /*! \qmlproperty bool Video::autoLoad This property indicates if loading of media should begin immediately. Defaults to true, if false media will not be loaded until playback is started. */ property alias autoLoad: player.autoLoad /*! \qmlproperty real Video::bufferProgress This property holds how much of the data buffer is currently filled, from 0.0 (empty) to 1.0 (full). */ property alias bufferProgress: player.bufferProgress /*! \qmlproperty int Video::bufferSize This property holds the buffer value. */ property alias bufferSize: player.bufferSize /*! \qmlproperty int Video::duration This property holds the duration of the media in milliseconds. If the media doesn't have a fixed duration (a live stream for example) this will be 0. */ property alias duration: player.duration /*! \qmlproperty enumeration Video::error This property holds the error state of the video. It can be one of: \list \li MediaPlayer.NoError - there is no current error. \li MediaPlayer.ResourceError - the video cannot be played due to a problem allocating resources. \li MediaPlayer.FormatError - the video format is not supported. \li MediaPlayer.NetworkError - the video cannot be played due to network issues. \li MediaPlayer.AccessDenied - the video cannot be played due to insufficient permissions. \li MediaPlayer.ServiceMissing - the video cannot be played because the media service could not be instantiated. \endlist */ property alias error: player.error /*! \qmlproperty string Video::errorString This property holds a string describing the current error condition in more detail. */ property alias errorString: player.errorString /*! \qmlproperty enumeration Video::availability Returns the availability state of the video instance. This is one of: \table \header \li Value \li Description \row \li MediaPlayer.Available \li The video player is available to use. \row \li MediaPlayer.Busy \li The video player is usually available, but some other process is utilizing the hardware necessary to play media. \row \li MediaPlayer.Unavailable \li There are no supported video playback facilities. \row \li MediaPlayer.ResourceMissing \li There is one or more resources missing, so the video player cannot be used. It may be possible to try again at a later time. \endtable */ //property alias availability: player.availability /*! \qmlproperty bool Video::hasAudio This property holds whether the current media has audio content. */ property alias hasAudio: player.hasAudio /*! \qmlproperty bool Video::hasVideo This property holds whether the current media has video content. */ property alias hasVideo: player.hasVideo /* documented below due to length of metaData documentation */ property alias metaData: player.metaData /*! \qmlproperty bool Video::muted This property holds whether the audio output is muted. */ property alias muted: player.muted /*! \qmlproperty real Video::playbackRate This property holds the rate at which video is played at as a multiple of the normal rate. */ property alias playbackRate: player.playbackRate /*! \qmlproperty int Video::position This property holds the current playback position in milliseconds. To change this position, use the \l seek() method. \sa seek() */ property alias position: player.position /*! \qmlproperty bool Video::seekable This property holds whether the playback position of the video can be changed. If true, calling the \l seek() method will cause playback to seek to the new position. */ property alias seekable: player.seekable /*! \qmlproperty url Video::source This property holds the source URL of the media. */ property alias source: player.source /*! \qmlproperty enumeration Video::status This property holds the status of media loading. It can be one of: \list \li MediaPlayer.NoMedia - no media has been set. \li MediaPlayer.Loading - the media is currently being loaded. \li MediaPlayer.Loaded - the media has been loaded. \li MediaPlayer.Buffering - the media is buffering data. \li MediaPlayer.Stalled - playback has been interrupted while the media is buffering data. \li MediaPlayer.Buffered - the media has buffered data. \li MediaPlayer.EndOfMedia - the media has played to the end. \li MediaPlayer.InvalidMedia - the media cannot be played. \li MediaPlayer.UnknownStatus - the status of the media cannot be determined. \endlist */ property alias status: player.status /*! \qmlproperty real Video::volume This property holds the volume of the audio output, from 0.0 (silent) to 1.0 (maximum volume). */ property alias volume: player.volume /*! \qmlproperty bool Video::autoPlay This property determines whether the media should begin playback automatically. Setting to \c true also sets \l autoLoad to \c true. The default is \c false. */ property alias autoPlay: player.autoPlay /*! \qmlsignal Video::paused() This signal is emitted when playback is paused. */ signal paused /*! \qmlsignal Video::stopped() This signal is emitted when playback is stopped. */ signal stopped /*! \qmlsignal Video::playing() This signal is emitted when playback is started or continued. */ signal playing signal seekFinished VideoOutput2 { id: videoOut anchors.fill: video source: player } SubtitleItem { id: ass_sub rotation: -videoOut.orientation fillMode: videoOut.fillMode source: subtitle anchors.fill: videoOut } Text { id: text_sub rotation: -videoOut.orientation horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignBottom font { pointSize: 20 bold: true } style: Text.Outline styleColor: "blue" color: "white" anchors.fill: videoOut } MediaPlayer { id: player onPaused: video.paused() onStopped: video.stopped() onPlaying: video.playing() onSeekFinished: video.seekFinished() } function stepForward() { player.stepForward() } function stepBackward() { player.stepBackward() } /*! \qmlmethod Video::play() Starts playback of the media. */ function play() { player.play(); } /*! \qmlmethod Video::pause() Pauses playback of the media. */ function pause() { player.pause(); } /*! \qmlmethod Video::stop() Stops playback of the media. */ function stop() { player.stop(); } /*! \qmlmethod Video::seek(offset) If the \l seekable property is true, seeks the current playback position to \a offset. Seeking may be asynchronous, so the \l position property may not be updated immediately. \sa seekable, position */ function seek(offset) { player.seek(offset); } Subtitle { id: subtitle player: player onContentChanged: { if (!canRender || !ass_sub.visible) text_sub.text = text } onEngineChanged: { // assume a engine canRender is only used as a renderer ass_sub.visible = canRender text_sub.visible = !canRender } onEnabledChanged: { ass_sub.visible = enabled text_sub.visible = enabled } } } // *************************************** // Documentation for meta-data properties. // *************************************** /*! \qmlproperty variant Video::metaData This property holds a collection of all the meta-data for the media. You can access individual properties like \l {Video::metaData.title}{metaData.title} or \l {Video::metaData.trackNumber} {metaData.trackNumber}. */ /*! \qmlproperty variant Video::metaData.title This property holds the title of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.subTitle This property holds the sub-title of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.author This property holds the author of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.comment This property holds a user comment about the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.description This property holds a description of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.category This property holds the category of the media \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.genre This property holds the genre of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.year This property holds the year of release of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.date This property holds the date of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.userRating This property holds a user rating of the media in the range of 0 to 100. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.keywords This property holds a list of keywords describing the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.language This property holds the language of the media, as an ISO 639-2 code. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.publisher This property holds the publisher of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.copyright This property holds the media's copyright notice. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.parentalRating This property holds the parental rating of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.ratingOrganization This property holds the name of the rating organization responsible for the parental rating of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.size This property property holds the size of the media in bytes. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.mediaType This property holds the type of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.audioBitRate This property holds the bit rate of the media's audio stream in bits per second. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.audioCodec This property holds the encoding of the media audio stream. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.averageLevel This property holds the average volume level of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.channelCount This property holds the number of channels in the media's audio stream. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.peakValue This property holds the peak volume of the media's audio stream. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.sampleRate This property holds the sample rate of the media's audio stream in Hertz. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.albumTitle This property holds the title of the album the media belongs to. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.albumArtist This property holds the name of the principal artist of the album the media belongs to. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.contributingArtist This property holds the names of artists contributing to the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.composer This property holds the composer of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.conductor This property holds the conductor of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.lyrics This property holds the lyrics to the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.mood This property holds the mood of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.trackNumber This property holds the track number of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.trackCount This property holds the number of track on the album containing the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.coverArtUrlSmall This property holds the URL of a small cover art image. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.coverArtUrlLarge This property holds the URL of a large cover art image. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.resolution This property holds the dimension of an image or video. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.pixelAspectRatio This property holds the pixel aspect ratio of an image or video. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.videoFrameRate This property holds the frame rate of the media's video stream. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.videoBitRate This property holds the bit rate of the media's video stream in bits per second. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.videoCodec This property holds the encoding of the media's video stream. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.posterUrl This property holds the URL of a poster image. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.chapterNumber This property holds the chapter number of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.director This property holds the director of the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.leadPerformer This property holds the lead performer in the media. \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.writer This property holds the writer of the media. \sa {QMediaMetaData} */ // The remaining properties are related to photos, and are technically // available but will certainly never have values. /*! \qmlproperty variant Video::metaData.cameraManufacturer \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.cameraModel \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.event \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.subject \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.orientation \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.exposureTime \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.fNumber \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.exposureProgram \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.isoSpeedRatings \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.exposureBiasValue \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.dateTimeDigitized \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.subjectDistance \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.meteringMode \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.lightSource \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.flash \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.focalLength \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.exposureMode \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.whiteBalance \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.DigitalZoomRatio \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.focalLengthIn35mmFilm \sa {QMediaMetaData::FocalLengthIn35mmFile} */ /*! \qmlproperty variant Video::metaData.sceneCaptureType \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.gainControl \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.contrast \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.saturation \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.sharpness \sa {QMediaMetaData} */ /*! \qmlproperty variant Video::metaData.deviceSettingDescription \sa {QMediaMetaData} */ QtAV-1.12.0/qml/libQmlAV.pri000066400000000000000000000107141312235004300153560ustar00rootroot00000000000000# qmake library building template pri file # Copyright (C) 2011-2015 Wang Bin # Shanghai, China. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # ############################## HOW TO ################################## # Suppose the library name is XX # Usually what you need to change are: staticlink, LIB_VERSION, NAME and DLLDESTDIR. # And rename xx-buildlib and LIBXX_PRI_INCLUDED # the contents of libXX.pro is: # TEMPLATE = lib # QT -= gui # CONFIG *= xx-buildlib # STATICLINK = 1 #optional. default is 0, i.e. dynamically link # PROJECTROOT = $$PWD/.. # include(libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # ... # the content of other pro using this library is: # TEMPLATE = app # PROJECTROOT = $$PWD/.. # STATICLINK = 1 #or 0 # include(dir_of_XX/libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # NAME = QmlAV !isEmpty(LIB$$upper($$NAME)_PRI_INCLUDED): { error("lib$${NAME}.pri already included") unset(NAME) } eval(LIB$$upper($$NAME)_PRI_INCLUDED = 1) LIB_VERSION = $$QTAV_VERSION #0.x.y may be wrong for dll # If user haven't supplied STATICLINK, then auto-detect isEmpty(STATICLINK) { static|contains(CONFIG, staticlib) { STATICLINK = 1 } else { STATICLINK = 0 } # Override for ios. Dynamic link is only supported # in iOS 8.1. ios:STATICLINK = 1 } isEqual(STATICLINK, 1):DEFINES += BUILD_$$upper($$NAME)_STATIC TEMPLATE += fakelib PROJECT_TARGETNAME = $$qtLibraryTarget($$NAME) TEMPLATE -= fakelib isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/.. include($${PROJECTROOT}/common.pri) preparePaths($$OUT_PWD/../out) CONFIG += depend_includepath #? PROJECT_SRCPATH = $$PWD PROJECT_LIBDIR = $$qtLongName($$BUILD_DIR/lib) INCLUDEPATH *= $$PROJECT_SRCPATH $$PROJECT_SRCPATH/.. $$PROJECT_SRCPATH/$$NAME DEPENDPATH *= $$PROJECT_SRCPATH #QMAKE_LFLAGS_RPATH += #will append to rpath dir #eval() ? !contains(CONFIG, $$lower($$NAME)-buildlib) { #The following may not need to change CONFIG *= link_prl LIBS *= -L$$PROJECT_LIBDIR -l$$qtLibName($$NAME) isEqual(STATICLINK, 1) { PRE_TARGETDEPS += $$PROJECT_LIBDIR/$$qtStaticLib($$NAME) } else { win32 { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME, $$LIB_VERSION) } else { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) } } } else { #Add your additional configuration first. e.g. # win32: LIBS += -lUser32 # The following may not need to change !CONFIG(plugin) { #TEMPLATE = lib VERSION = $$LIB_VERSION DESTDIR = $$PROJECT_LIBDIR } TARGET = $$PROJECT_TARGETNAME ##I commented out this before, why? CONFIG *= create_prl # DEFINES += BUILD_$$upper($$NAME)_LIB #win32-msvc* isEqual(STATICLINK, 1) { CONFIG -= shared dll ##otherwise the following shared is true, why? CONFIG *= staticlib } else { CONFIG *= shared #shared includes dll } shared { !plugin { !isEqual(DESTDIR, $$BUILD_DIR/bin): DLLDESTDIR = $$BUILD_DIR/bin #copy shared lib there } #QMAKE_POST_LINK+=: just append as a string to previous QMAKE_POST_LINK CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP):!mac_framework: QMAKE_POST_LINK = $$quote(-$$QMAKE_STRIP $$shell_path($$DESTDIR/$$qtSharedLib($$NAME))) #copy from the pro creator creates. symbian { MMP_RULES += EXPORTUNFROZEN TARGET.UID3 = 0xE4CC8061 TARGET.CAPABILITY = TARGET.EPOCALLOWDLLDATA = 1 addFiles.sources = $$qtSharedLib($$NAME, $$LIB_VERSION) addFiles.path = !:/sys/bin DEPLOYMENT += addFiles } } unix:!symbian { maemo5 { target.path = /opt/usr/lib } else { target.path = /usr/lib } INSTALLS += target } } !no_rpath:!cross_compile:set_rpath($$PROJECT_LIBDIR) unset(LIB_VERSION) unset(PROJECT_SRCPATH) unset(PROJECT_LIBDIR) unset(PROJECT_TARGETNAME) QtAV-1.12.0/qml/libQmlAV.pro000066400000000000000000000102271312235004300153630ustar00rootroot00000000000000TEMPLATE = lib CONFIG += qt plugin TARGET = QmlAV QT += quick qml CONFIG *= qmlav-buildlib #QMAKE_RPATHLINKDIR #CONFIG *= qml_module relative_qt_rpath #var with '_' can not pass to pri? PROJECTROOT = $$PWD/.. !include($$PROJECTROOT/src/libQtAV.pri): error("could not find libQtAV.pri") !include(libQmlAV.pri): error("could not find libQmlAV.pri") preparePaths($$OUT_PWD/../out) #https://github.com/wang-bin/QtAV/issues/368#issuecomment-73246253 #http://qt-project.org/forums/viewthread/38438 # mkspecs/features/qml_plugin.prf URI = QtAV #uri used in QtAVQmlPlugin::registerTypes(uri) qtAtLeast(5, 3): QMAKE_MOC_OPTIONS += -Muri=$$URI # not sure what moc does #DESTDIR = $$BUILD_DIR/bin/QtAV qtav_qml.files = qmldir Video.qml plugins.qmltypes !static { #static lib copy error before ranlib. copy only in sdk_install plugin.files = $$DESTDIR/$$qtSharedLib($$NAME) } plugin.path = $$BUILD_DIR/bin/QtAV/ mkpath($$plugin.path) #plugin.depends = #makefile target #windows: copy /y file1+file2+... dir. need '+'. $(COPY_FILE) is exists in makefile, not in vc projects (MAKEFILE_GENERATOR is MSBUILD or MSVC.NET) if(equals(MAKEFILE_GENERATOR, MSVC.NET)|equals(MAKEFILE_GENERATOR, MSBUILD)) { TRY_COPY = $$QMAKE_COPY } else { TRY_COPY = -$$QMAKE_COPY #makefile. or -\$\(COPY_FILE\) } for(f, plugin.files) { plugin.commands += $$escape_expand(\\n\\t)$$TRY_COPY $$shell_path($$f) $$shell_path($$plugin.path) } #join values separated by space. so quote is needed #plugin.commands = $$join(plugin.commands,$$escape_expand(\\n\\t)) OTHER_FILES += $$qtav_qml.files #just append as a string to $$QMAKE_POST_LINK isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK = $$plugin.commands else: QMAKE_POST_LINK = $${QMAKE_POST_LINK}$$escape_expand(\\n\\t)$$plugin.commands #QMAKE_EXTRA_TARGETS = plugin #POST_TARGETDEPS = plugin #vs, xcode does not support #no write permision. do it in makefile #mkpath($$[QT_INSTALL_QML]/QtAV) #http://stackoverflow.com/questions/14260542/qmake-extra-compilers-processing-steps #http://danny-pope.com/?p=86 #custom compiler: auto update if source is newer # sa mkspecs/features/qml_plugin.prf extra_copy.output = $$shell_path($$plugin.path)${QMAKE_FILE_BASE}${QMAKE_FILE_EXT} # QMAKE_COPY_FILE, QMAKE_MKDIR_CMD ? extra_copy.commands = $$TRY_COPY ${QMAKE_FILE_NAME} $$shell_path($$plugin.path) #extra_copy.depends = $$EXTRA_COPY_FILES #.input is already the depends extra_copy.input = EXTRA_COPY_FILES extra_copy.CONFIG += no_link extra_copy.variable_out = POST_TARGETDEPS QMAKE_EXTRA_COMPILERS += extra_copy # # CAN NOT put $$TARGET here otherwise may result in circular dependency. # update EXTRA_COPY_FILES will result in target relink EXTRA_COPY_FILES = $$qtav_qml.files QMAKE_WRITE_DEFAULT_RC = 1 QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV QML module. QtAV Multimedia framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV QML" SOURCES += \ plugin.cpp \ QQuickItemRenderer.cpp \ SGVideoNode.cpp \ QmlAVPlayer.cpp \ QuickFilter.cpp \ QuickSubtitle.cpp \ MediaMetaData.cpp \ QuickSubtitleItem.cpp \ QuickVideoPreview.cpp HEADERS += \ QmlAV/QuickSubtitle.h \ QmlAV/QuickSubtitleItem.h \ QmlAV/QuickVideoPreview.h SDK_HEADERS += \ QmlAV/Export.h \ QmlAV/MediaMetaData.h \ QmlAV/SGVideoNode.h \ QmlAV/QQuickItemRenderer.h \ QmlAV/QuickFilter.h \ QmlAV/QmlAVPlayer.h HEADERS *= \ $$SDK_HEADERS greaterThan(QT_MINOR_VERSION, 1) { HEADERS += QmlAV/QuickFBORenderer.h SOURCES += QuickFBORenderer.cpp } unix:!android:!mac { #debian qml_module_files = qmldir Video.qml plugins.qmltypes libQmlAV.so DEB_INSTALL_LIST = $$join(qml_module_files, \\n.$$[QT_INSTALL_QML]/QtAV/, .$$[QT_INSTALL_QML]/QtAV/) deb_install_list.target = qml-module-qtav.install deb_install_list.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${deb_install_list.target} QMAKE_EXTRA_TARGETS += deb_install_list target.depends += $${deb_install_list.target} } target.path = $$[QT_INSTALL_QML]/QtAV qtav_qml.path = $$[QT_INSTALL_QML]/QtAV !contains(QMAKE_HOST.os, Windows):INSTALLS *= target qtav_qml QtAV-1.12.0/qml/plugin.cpp000066400000000000000000000053221312235004300151740ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include "QmlAV/QQuickItemRenderer.h" #include "QmlAV/QmlAVPlayer.h" #include "QmlAV/QuickFilter.h" #include "QmlAV/QuickSubtitle.h" #include "QmlAV/QuickSubtitleItem.h" #include "QmlAV/MediaMetaData.h" #include "QmlAV/QuickVideoPreview.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) #include "QmlAV/QuickFBORenderer.h" #endif namespace QtAV { class QtAVQmlPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtAV")); qmlRegisterType(uri, 1, 3, "VideoOutput"); qmlRegisterType(uri, 1, 3, "AVPlayer"); qmlRegisterType(uri, 1, 3, "MediaPlayer"); qmlRegisterType(uri, 1, 4, "Subtitle"); qmlRegisterType(uri, 1, 4, "SubtitleItem"); qmlRegisterType(uri, 1, 4, "VideoPreview"); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) qmlRegisterType(uri, 1, 5, "VideoOutput2"); #endif qmlRegisterUncreatableType(uri, 1, 6, "VideoCapture", trUtf8("VideoCapture is provided by MediaPlayer")); qmlRegisterType(); // FIXME: if version is 2.x, some qtav types will be undefined, why? // 1.7 qmlRegisterType(uri, 1, 7, "AudioFilter"); qmlRegisterType(uri, 1, 7, "VideoFilter"); qmlRegisterType(uri, 1, 7, "Shader"); } }; } //namespace QtAV #include "plugin.moc" QtAV-1.12.0/qml/plugins.qmltypes000066400000000000000000000562071312235004300164630ustar00rootroot00000000000000import QtQuick.tooling 1.1 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated by: // 'qmlplugindump -nonrelocatable QtAV 1.7' Module { dependencies: ["QtQuick 2.0"] Component { name: "MediaMetaData" prototype: "QObject" Enum { name: "Key" values: { "Title": 0, "SubTitle": 1, "Author": 2, "Comment": 3, "Description": 4, "Category": 5, "Genre": 6, "Year": 7, "Date": 8, "UserRating": 9, "Keywords": 10, "Language": 11, "Publisher": 12, "Copyright": 13, "ParentalRating": 14, "RatingOrganization": 15, "Size": 16, "MediaType": 17, "Duration": 18, "StartTime": 19, "AudioBitRate": 20, "AudioCodec": 21, "AverageLevel": 22, "ChannelCount": 23, "PeakValue": 24, "SampleRate": 25, "SampleFormat": 26, "ChannelLayout": 27, "AlbumTitle": 28, "AlbumArtist": 29, "ContributingArtist": 30, "Composer": 31, "Conductor": 32, "Lyrics": 33, "Mood": 34, "TrackNumber": 35, "TrackCount": 36, "CoverArtUrlSmall": 37, "CoverArtUrlLarge": 38, "Resolution": 39, "PixelAspectRatio": 40, "VideoFrameRate": 41, "VideoBitRate": 42, "VideoCodec": 43, "PixelFormat": 44, "VideoFrames": 45, "PosterUrl": 46, "ChapterNumber": 47, "Director": 48, "LeadPerformer": 49, "Writer": 50, "PosterImage": 51, "CoverArtImage": 52 } } Property { name: "title"; type: "QVariant"; isReadonly: true } Property { name: "subTitle"; type: "QVariant"; isReadonly: true } Property { name: "author"; type: "QVariant"; isReadonly: true } Property { name: "comment"; type: "QVariant"; isReadonly: true } Property { name: "description"; type: "QVariant"; isReadonly: true } Property { name: "category"; type: "QVariant"; isReadonly: true } Property { name: "genre"; type: "QVariant"; isReadonly: true } Property { name: "year"; type: "QVariant"; isReadonly: true } Property { name: "date"; type: "QVariant"; isReadonly: true } Property { name: "userRating"; type: "QVariant"; isReadonly: true } Property { name: "keywords"; type: "QVariant"; isReadonly: true } Property { name: "language"; type: "QVariant"; isReadonly: true } Property { name: "publisher"; type: "QVariant"; isReadonly: true } Property { name: "copyright"; type: "QVariant"; isReadonly: true } Property { name: "parentalRating"; type: "QVariant"; isReadonly: true } Property { name: "ratingOrganization"; type: "QVariant"; isReadonly: true } Property { name: "size"; type: "QVariant"; isReadonly: true } Property { name: "mediaType"; type: "QVariant"; isReadonly: true } Property { name: "duration"; type: "QVariant"; isReadonly: true } Property { name: "startTime"; type: "QVariant"; isReadonly: true } Property { name: "audioBitRate"; type: "QVariant"; isReadonly: true } Property { name: "audioCodec"; type: "QVariant"; isReadonly: true } Property { name: "averageLevel"; type: "QVariant"; isReadonly: true } Property { name: "channelCount"; type: "QVariant"; isReadonly: true } Property { name: "channelLayout"; type: "QVariant"; isReadonly: true } Property { name: "peakValue"; type: "QVariant"; isReadonly: true } Property { name: "sampleFormat"; type: "QVariant"; isReadonly: true } Property { name: "sampleRate"; type: "QVariant"; isReadonly: true } Property { name: "albumTitle"; type: "QVariant"; isReadonly: true } Property { name: "albumArtist"; type: "QVariant"; isReadonly: true } Property { name: "contributingArtist"; type: "QVariant"; isReadonly: true } Property { name: "composer"; type: "QVariant"; isReadonly: true } Property { name: "conductor"; type: "QVariant"; isReadonly: true } Property { name: "lyrics"; type: "QVariant"; isReadonly: true } Property { name: "mood"; type: "QVariant"; isReadonly: true } Property { name: "trackNumber"; type: "QVariant"; isReadonly: true } Property { name: "trackCount"; type: "QVariant"; isReadonly: true } Property { name: "coverArtUrlSmall"; type: "QVariant"; isReadonly: true } Property { name: "coverArtUrlLarge"; type: "QVariant"; isReadonly: true } Property { name: "resolution"; type: "QVariant"; isReadonly: true } Property { name: "pixelAspectRatio"; type: "QVariant"; isReadonly: true } Property { name: "videoFrameRate"; type: "QVariant"; isReadonly: true } Property { name: "videoBitRate"; type: "QVariant"; isReadonly: true } Property { name: "videoCodec"; type: "QVariant"; isReadonly: true } Property { name: "pixelFormat"; type: "QVariant"; isReadonly: true } Property { name: "videoFrames"; type: "QVariant"; isReadonly: true } Property { name: "posterUrl"; type: "QVariant"; isReadonly: true } Property { name: "chapterNumber"; type: "QVariant"; isReadonly: true } Property { name: "director"; type: "QVariant"; isReadonly: true } Property { name: "leadPerformer"; type: "QVariant"; isReadonly: true } Property { name: "writer"; type: "QVariant"; isReadonly: true } Signal { name: "metaDataChanged" } } Component { name: "QQuickFramebufferObject" defaultProperty: "data" prototype: "QQuickItem" Property { name: "textureFollowsItemSize"; type: "bool" } Property { name: "mirrorVertically"; type: "bool" } Signal { name: "textureFollowsItemSizeChanged" Parameter { type: "bool" } } Signal { name: "mirrorVerticallyChanged" Parameter { type: "bool" } } } Component { name: "QmlAVPlayer" prototype: "QObject" exports: ["QtAV/AVPlayer 1.3", "QtAV/MediaPlayer 1.3"] exportMetaObjectRevisions: [0, 0] Enum { name: "Loop" values: { "Infinite": -1 } } Enum { name: "PositionValue" values: { "PositionMax": 2147483647 } } Enum { name: "PlaybackState" values: { "StoppedState": 0, "PlayingState": 1, "PausedState": 2 } } Enum { name: "Status" values: { "UnknownStatus": 0, "NoMedia": 1, "Loading": 2, "Loaded": 3, "Stalled": 4, "Buffering": 5, "Buffered": 6, "EndOfMedia": 7, "InvalidMedia": 8 } } Enum { name: "Error" values: { "NoError": 0, "ResourceError": 1, "FormatError": 2, "NetworkError": 3, "AccessDenied": 4, "ServiceMissing": 5 } } Enum { name: "ChannelLayout" values: { "ChannelLayoutAuto": 0, "Left": 1, "Right": 2, "Mono": 3, "Stereo": 4 } } Property { name: "volume"; type: "double" } Property { name: "status"; type: "Status"; isReadonly: true } Property { name: "error"; type: "Error"; isReadonly: true } Property { name: "duration"; type: "int"; isReadonly: true } Property { name: "position"; type: "int"; isReadonly: true } Property { name: "muted"; type: "bool" } Property { name: "hasAudio"; type: "bool"; isReadonly: true } Property { name: "hasVideo"; type: "bool"; isReadonly: true } Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true } Property { name: "autoPlay"; type: "bool" } Property { name: "autoLoad"; type: "bool" } Property { name: "playbackRate"; type: "double" } Property { name: "source"; type: "QUrl" } Property { name: "loops"; type: "int" } Property { name: "bufferProgress"; type: "double"; isReadonly: true } Property { name: "seekable"; type: "bool"; isReadonly: true } Property { name: "metaData"; type: "MediaMetaData"; isReadonly: true; isPointer: true } Property { name: "mediaObject"; type: "QObject"; isReadonly: true; isPointer: true } Property { name: "errorString"; type: "string"; isReadonly: true } Property { name: "startPosition"; type: "int" } Property { name: "stopPosition"; type: "int" } Property { name: "fastSeek"; type: "bool" } Property { name: "timeout"; type: "int" } Property { name: "abortOnTimeout"; type: "bool" } Property { name: "channelLayout"; type: "ChannelLayout" } Property { name: "videoCodecs"; type: "QStringList"; isReadonly: true } Property { name: "videoCodecPriority"; type: "QStringList" } Property { name: "videoCodecOptions"; type: "QVariantMap" } Property { name: "avFormatOptions"; type: "QVariantMap" } Property { name: "useWallclockAsTimestamps"; type: "bool" } Property { name: "videoCapture"; type: "QtAV::VideoCapture"; isReadonly: true; isPointer: true } Property { name: "audioTrack"; type: "int" } Property { name: "bufferSize"; type: "int" } Property { name: "externalAudio"; type: "QUrl" } Property { name: "internalAudioTracks"; type: "QVariantList"; isReadonly: true } Property { name: "externalAudioTracks"; type: "QVariantList"; isReadonly: true } Property { name: "internalSubtitleTracks"; type: "QVariantList"; isReadonly: true } Property { name: "internalSubtitleTrack"; type: "int" } Property { name: "audioFilters"; type: "QuickAudioFilter"; isList: true; isReadonly: true } Property { name: "videoFilters"; type: "QuickVideoFilter"; isList: true; isReadonly: true } Property { name: "audioBackends"; type: "QStringList" } Property { name: "supportedAudioBackends"; type: "QStringList"; isReadonly: true } Signal { name: "loopCountChanged" } Signal { name: "videoOutChanged" } Signal { name: "paused" } Signal { name: "stopped" } Signal { name: "playing" } Signal { name: "seekFinished" } Signal { name: "error" Parameter { name: "error"; type: "Error" } Parameter { name: "errorString"; type: "string" } } Method { name: "play" } Method { name: "pause" } Method { name: "stop" } Method { name: "stepForward" } Method { name: "stepBackward" } Method { name: "seek" Parameter { name: "offset"; type: "int" } } Method { name: "seekForward" } Method { name: "seekBackward" } Method { name: "play" Parameter { name: "url"; type: "QUrl" } } } Component { name: "QtAV::AudioFilter"; prototype: "QtAV::Filter" } Component { name: "QtAV::DynamicShaderObject" prototype: "QtAV::VideoShaderObject" exports: ["QtAV/Shader 1.7"] exportMetaObjectRevisions: [0] Property { name: "header"; type: "string" } Property { name: "sample"; type: "string" } Property { name: "postProcess"; type: "string" } } Component { name: "QtAV::Filter" prototype: "QObject" Property { name: "enabled"; type: "bool" } Signal { name: "enabledChanged" Parameter { type: "bool" } } Method { name: "setEnabled" Parameter { name: "enabled"; type: "bool" } } Method { name: "setEnabled" } } Component { name: "QtAV::QQuickItemRenderer" defaultProperty: "data" prototype: "QQuickItem" exports: ["QtAV/VideoOutput 1.3"] exportMetaObjectRevisions: [0] Enum { name: "FillMode" values: { "Stretch": 0, "PreserveAspectFit": 1, "PreserveAspectCrop": 2 } } Property { name: "opengl"; type: "bool" } Property { name: "source"; type: "QObject"; isPointer: true } Property { name: "fillMode"; type: "FillMode" } Property { name: "orientation"; type: "int" } Property { name: "contentRect"; type: "QRectF"; isReadonly: true } Property { name: "sourceRect"; type: "QRectF"; isReadonly: true } Property { name: "regionOfInterest"; type: "QRectF" } Property { name: "sourceAspectRatio"; type: "double"; isReadonly: true } Property { name: "videoFrameSize"; type: "QSize"; isReadonly: true } Property { name: "frameSize"; type: "QSize"; isReadonly: true } Property { name: "backgroundColor"; type: "QColor" } Property { name: "filters"; type: "QuickVideoFilter"; isList: true; isReadonly: true } Signal { name: "fillModeChanged" Parameter { type: "QQuickItemRenderer::FillMode" } } Signal { name: "openGLChanged" } Signal { name: "sourceAspectRatioChanged" Parameter { name: "value"; type: "double" } } Method { name: "mapPointToItem" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToItem" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapNormalizedPointToItem" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapNormalizedRectToItem" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapPointToSource" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToSource" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapPointToSourceNormalized" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToSourceNormalized" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } } Component { name: "QtAV::QuickFBORenderer" defaultProperty: "data" prototype: "QQuickFramebufferObject" exports: ["QtAV/VideoOutput2 1.5"] exportMetaObjectRevisions: [0] Enum { name: "FillMode" values: { "Stretch": 0, "PreserveAspectFit": 1, "PreserveAspectCrop": 2 } } Property { name: "opengl"; type: "bool" } Property { name: "source"; type: "QObject"; isPointer: true } Property { name: "fillMode"; type: "FillMode" } Property { name: "orientation"; type: "int" } Property { name: "contentRect"; type: "QRectF"; isReadonly: true } Property { name: "sourceRect"; type: "QRectF"; isReadonly: true } Property { name: "regionOfInterest"; type: "QRectF" } Property { name: "sourceAspectRatio"; type: "double"; isReadonly: true } Property { name: "videoFrameSize"; type: "QSize"; isReadonly: true } Property { name: "frameSize"; type: "QSize"; isReadonly: true } Property { name: "backgroundColor"; type: "QColor" } Property { name: "filters"; type: "QuickVideoFilter"; isList: true; isReadonly: true } Property { name: "brightness"; type: "double" } Property { name: "contrast"; type: "double" } Property { name: "hue"; type: "double" } Property { name: "saturation"; type: "double" } Signal { name: "fillModeChanged" Parameter { type: "QuickFBORenderer::FillMode" } } Signal { name: "openGLChanged" } Signal { name: "sourceAspectRatioChanged" Parameter { name: "value"; type: "double" } } Signal { name: "brightnessChanged" Parameter { name: "value"; type: "double" } } Signal { name: "contrastChanged" Parameter { type: "double" } } Signal { name: "hueChanged" Parameter { type: "double" } } Signal { name: "saturationChanged" Parameter { type: "double" } } Method { name: "mapPointToItem" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToItem" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapNormalizedPointToItem" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapNormalizedRectToItem" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapPointToSource" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToSource" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } Method { name: "mapPointToSourceNormalized" type: "QPointF" Parameter { name: "point"; type: "QPointF" } } Method { name: "mapRectToSourceNormalized" type: "QRectF" Parameter { name: "rectangle"; type: "QRectF" } } } Component { name: "QtAV::QuickVideoPreview" defaultProperty: "data" prototype: "QtAV::QuickFBORenderer" exports: ["QtAV/VideoPreview 1.4"] exportMetaObjectRevisions: [0] Property { name: "timestamp"; type: "int" } Property { name: "file"; type: "QUrl" } } Component { name: "QtAV::VideoCapture" prototype: "QObject" exports: ["QtAV/VideoCapture 1.6"] isCreatable: false exportMetaObjectRevisions: [0] Property { name: "async"; type: "bool" } Property { name: "autoSave"; type: "bool" } Property { name: "originalFormat"; type: "bool" } Property { name: "saveFormat"; type: "string" } Property { name: "quality"; type: "int" } Property { name: "captureName"; type: "string" } Property { name: "captureDir"; type: "string" } Signal { name: "requested" } Signal { name: "frameAvailable" Parameter { name: "frame"; type: "QtAV::VideoFrame" } } Signal { name: "imageCaptured" Parameter { name: "image"; type: "QImage" } } Signal { name: "failed" } Signal { name: "saved" Parameter { name: "path"; type: "string" } } Method { name: "capture" } } Component { name: "QtAV::VideoFilter"; prototype: "QtAV::Filter" } Component { name: "QtAV::VideoShaderObject"; prototype: "QObject" } Component { name: "QuickAudioFilter" prototype: "QtAV::AudioFilter" exports: ["QtAV/AudioFilter 1.7"] exportMetaObjectRevisions: [0] Enum { name: "FilterType" values: { "AVFilter": 0, "UserFilter": 1 } } Property { name: "avfilter"; type: "string" } Property { name: "supportedAVFilters"; type: "QStringList"; isReadonly: true } Property { name: "userFilter"; type: "AudioFilter"; isPointer: true } Property { name: "type"; type: "FilterType" } } Component { name: "QuickSubtitle" prototype: "QObject" exports: ["QtAV/Subtitle 1.4"] exportMetaObjectRevisions: [0] Property { name: "enabled"; type: "bool" } Property { name: "player"; type: "QObject"; isPointer: true } Property { name: "codec"; type: "QByteArray" } Property { name: "engines"; type: "QStringList" } Property { name: "engine"; type: "string"; isReadonly: true } Property { name: "fuzzyMatch"; type: "bool" } Property { name: "dirs"; type: "QStringList" } Property { name: "suffixes"; type: "QStringList" } Property { name: "supportedSuffixes"; type: "QStringList"; isReadonly: true } Property { name: "delay"; type: "double" } Property { name: "canRender"; type: "bool"; isReadonly: true } Property { name: "autoLoad"; type: "bool" } Property { name: "file"; type: "string" } Property { name: "text"; type: "string"; isReadonly: true } Property { name: "fontFile"; type: "string" } Property { name: "fontsDir"; type: "string" } Property { name: "fontFileForced"; type: "bool" } Signal { name: "loaded" Parameter { name: "path"; type: "string" } } Signal { name: "enabledChanged" Parameter { name: "value"; type: "bool" } } Signal { name: "contentChanged" } Method { name: "setAutoLoad" Parameter { name: "value"; type: "bool" } } Method { name: "getText"; type: "string" } } Component { name: "QuickSubtitleItem" defaultProperty: "data" prototype: "QQuickItem" exports: ["QtAV/SubtitleItem 1.4"] exportMetaObjectRevisions: [0] Property { name: "source"; type: "QuickSubtitle"; isPointer: true } Property { name: "fillMode"; type: "int" } } Component { name: "QuickVideoFilter" prototype: "QtAV::VideoFilter" exports: ["QtAV/VideoFilter 1.7"] exportMetaObjectRevisions: [0] Enum { name: "FilterType" values: { "AVFilter": 0, "GLSLFilter": 1, "UserFilter": 2 } } Property { name: "avfilter"; type: "string" } Property { name: "supportedAVFilters"; type: "QStringList"; isReadonly: true } Property { name: "userFilter"; type: "VideoFilter"; isPointer: true } Property { name: "shader"; type: "QtAV::DynamicShaderObject"; isPointer: true } Property { name: "type"; type: "FilterType" } } } QtAV-1.12.0/qml/qmldir000066400000000000000000000001371312235004300144040ustar00rootroot00000000000000module QtAV plugin QmlAV classname QtAVQmlPlugin typeinfo plugins.qmltypes Video 1.3 Video.qml QtAV-1.12.0/qtc_packaging/000077500000000000000000000000001312235004300151725ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/common/000077500000000000000000000000001312235004300164625ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/common/README000077700000000000000000000000001312235004300212422../../README.mdustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/common/changelog000077700000000000000000000000001312235004300225672../../Changelogustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/common/copyright000066400000000000000000000025261312235004300204220ustar00rootroot00000000000000This package was debianized by Wang Bin on Fri, 15 Mar 2013 10:06:55 +0800. It was downloaded from Upstream Author(s): Wang Bin(Lucas Wang) wbsecg1@gmail.com Copyright: Copyright (C) 2013 Wang Bin(Lucas Wang) License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU Lesser General Public License can be found in `/usr/share/common-licenses/LGPL'. The Debian packaging is (C) 2013, Wang Bin and is licensed under the LGPL, see above. # Please also look if there are files or directories which have a # different copyright/license attached and list them here. QtAV-1.12.0/qtc_packaging/debian_fremantle/000077500000000000000000000000001312235004300204515ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/debian_fremantle/compat000066400000000000000000000000021312235004300216470ustar00rootroot000000000000007 QtAV-1.12.0/qtc_packaging/debian_fremantle/control000066400000000000000000000006261312235004300220600ustar00rootroot00000000000000Source: qtav Section: user/hidden Priority: optional Maintainer: wbin Build-Depends: debhelper (>= 5), libqt4-dev Standards-Version: 3.7.3 Homepage: Package: qtav Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: XB-Maemo-Display-Name: QtAV QtAV-1.12.0/qtc_packaging/debian_fremantle/rules000066400000000000000000000035751312235004300215400ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 configure: configure-stamp configure-stamp: dh_testdir # qmake PREFIX=/usr# Uncomment this line for use without Qt Creator touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. # $(MAKE) # Uncomment this line for use without Qt Creator #docbook-to-man debian/qtav.sgml > qtav.1 touch $@ clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. $(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/qtav. $(MAKE) INSTALL_ROOT="$(CURDIR)"/debian/qtav install # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_install # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_python # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_makeshlibs dh_installdeb # dh_shlibdeps # Uncomment this line for use without Qt Creator dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure QtAV-1.12.0/qtc_packaging/debian_generic/000077500000000000000000000000001312235004300201105ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/debian_generic/Player.desktop000066400000000000000000000024251312235004300227420ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=QtAV Player GenericName=QtAV player Comment=QtAV Multimedia Player Comment[zh]=多媒体播放器 Icon=QtAV TryExec=/usr/bin/Player Exec=Player -f %U Terminal=false Categories=Qt;AudioVideo;Audio;Video;Player;TV; MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac; X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb QtAV-1.12.0/qtc_packaging/debian_generic/QMLPlayer.desktop000066400000000000000000000024431312235004300233140ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=QtAV QML Player GenericName=QtAV QML player Comment=QtAV Multimedia Player Comment[zh]=多媒体播放器 Icon=QtAV TryExec=/usr/bin/QMLPlayer Exec=QMLPlayer -f %U Terminal=false Categories=Qt;AudioVideo;Audio;Video;Player;TV; MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac; X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb QtAV-1.12.0/qtc_packaging/debian_generic/control000066400000000000000000000005731312235004300215200ustar00rootroot00000000000000Package: QtAV Version: %version% Architecture: %arch% Maintainer: Wang Bin(Lucas Wang) Source: QtAV Section: video Priority: optional Homepage: http://qtav.org Depends: Description: A media playing framework based on Qt and FFmpeg QtAV is a media playing framework based on Qt and FFmpeg. It can help you to write a player with less effort than ever before. QtAV-1.12.0/qtc_packaging/debian_harmattan/000077500000000000000000000000001312235004300204535ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/debian_harmattan/compat000066400000000000000000000000021312235004300216510ustar00rootroot000000000000007 QtAV-1.12.0/qtc_packaging/debian_harmattan/control000066400000000000000000000011331312235004300220540ustar00rootroot00000000000000Source: qtav Section: user/other Priority: optional Maintainer: wbin Build-Depends: debhelper (>= 5), libqt4-dev Standards-Version: 3.7.3 Homepage: Package: qtav Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: XSBC-Maemo-Display-Name: QtAV XB-Maemo-Flags: visible XB-MeeGo-Desktop-Entry-Filename: QtAV_harmattan XB-MeeGo-Desktop-Entry: [Desktop Entry] Type=Application Name=QtAV Icon=/usr/share/icons/hicolor/80x80/apps/QtAV80.png QtAV-1.12.0/qtc_packaging/debian_harmattan/manifest.aegis000066400000000000000000000041521312235004300232750ustar00rootroot00000000000000AutoGenerateAegisFile QtAV-1.12.0/qtc_packaging/debian_harmattan/rules000066400000000000000000000035751312235004300215420ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 configure: configure-stamp configure-stamp: dh_testdir # qmake PREFIX=/usr# Uncomment this line for use without Qt Creator touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. # $(MAKE) # Uncomment this line for use without Qt Creator #docbook-to-man debian/qtav.sgml > qtav.1 touch $@ clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. $(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/qtav. $(MAKE) INSTALL_ROOT="$(CURDIR)"/debian/qtav install # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_install # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_python # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_makeshlibs dh_installdeb # dh_shlibdeps # Uncomment this line for use without Qt Creator dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure QtAV-1.12.0/qtc_packaging/ifw/000077500000000000000000000000001312235004300157575ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/build.sh000077500000000000000000000216311312235004300174200ustar00rootroot00000000000000# QtAV installer creator. [ $# -lt 1 ] && { echo "need QtAV build dir as a parameter" exit 1 } BUILD=$1 QTBIN=`grep QT_BIN $BUILD/.qmake.cache |head -n 1 |cut -d "=" -f 2 | tr -d ' '` # for windows QTBIN=`echo /$QTBIN|sed 's,:,,'` export PATH=$QTBIN:$PWD/../../tools:$PATH echo "QTBIN=$QTBIN" ARCH=`grep TARGET_ARCH $BUILD/.qmake.cache |grep -v TARGET_ARCH_SUB |cut -d "=" -f 2` ARCH=`echo $ARCH` #trim echo "$ARCH" MKSPEC=`grep mkspecs_cached $BUILD/.qmake.cache |cut -d "=" -f 2` MKSPEC=`echo $MKSPEC` TARGET=${MKSPEC}-${ARCH} mkdir -p $TARGET # grep -m 1 is not supported for msys QTAV_VER_MAJOR=`grep QTAV_MAJOR_VERSION ../../.qmake.conf |head -n 1 |cut -d "=" -f 2 | tr -d ' '` QTAV_VER_MINOR=`grep QTAV_MINOR_VERSION ../../.qmake.conf |head -n 1 |cut -d "=" -f 2 | tr -d ' '` QTAV_VER_PATCH=`grep QTAV_PATCH_VERSION ../../.qmake.conf |head -n 1 |cut -d "=" -f 2 | tr -d ' '` QTAV_VER=${QTAV_VER_MAJOR}.${QTAV_VER_MINOR}.${QTAV_VER_PATCH} echo "QtAV $QTAV_VER" host_is() { local name=$1 #TODO: osx=>darwin local line=`uname -a |grep -i $name` test -n "$line" && return 0 || return 1 } lib_name() { local lib_base=$1 local lib_ver=$2 if host_is Darwin; then echo lib${lib_base}.${lib_ver}.dylib elif host_is MinGW; then echo ${lib_base}${lib_ver}.dll elif host_is MSYS; then echo ${lib_base}${lib_ver}.dll else echo lib${lib_base}.so.${lib_ver} fi } libd_name() { local lib_base=$1 local lib_ver=$2 if host_is Darwin; then echo lib${lib_base}_debug.${lib_ver}.dylib elif host_is MinGW; then echo ${lib_base}d${lib_ver}.dll elif host_is MSYS; then echo ${lib_base}d${lib_ver}.dll else echo lib${lib_base}.so.${lib_ver} fi } qt5lib_name() { local m=$1 if host_is Darwin; then echo libQt5${m}.${lib_ver}.dylib elif host_is MinGW || host_is MSYS; then echo Qt5${m}.dll else echo libQt5${m}.so.5 fi } EXE= host_is MinGW || host_is MSYS && EXE=.exe LIBDIR=`find $BUILD/lib* -name "*Qt*AV.prl"` LIBDIR=${LIBDIR%/*} echo "LIBDIR=$LIBDIR" echo "creating directories..." cp -af packages $TARGET mkdir -p $TARGET/packages/com.qtav.product.runtime/data/bin/{plugins,qml} mkdir -p $TARGET/packages/com.qtav.product.player/data/bin mkdir -p $TARGET/packages/com.qtav.product.examples/data/bin mkdir -p $TARGET/packages/com.qtav.product.dev/data/bin/QtAV mkdir -p $TARGET/packages/com.qtav.product.dev/data/include mkdir -p $TARGET/packages/com.qtav.product.dev/data/lib mkdir -p $TARGET/packages/com.qtav.product/data/ ### runtime echo "coping runtime files..." RT_DIR=$TARGET/packages/com.qtav.product.runtime declare -a QTMODULES=(Core Gui OpenGL Widgets Qml Quick Network Svg Sql) #TODO: use readelf, objdump or otool to get depends host_is Linux && QTMODULES+=(DBus XcbQpa X11Extras) cp -af $BUILD/bin/* $RT_DIR/data/bin rm -rf $RT_DIR/data/bin/QtAV*d${QTAV_VER_MAJOR}.* cat > $RT_DIR/data/bin/qt.conf <$TARGET/config.xml fi cp config/control.js $TARGET type -p binarycreator || { echo "Can not create installer. Make sure Qt Installer Framework tools can be found in \$PATH" exit 0 } INSTALLER=QtAV${QTAV_VER}-${TARGET} binarycreator --offline-only -c $TARGET/config.xml -p $TARGET/packages --ignore-translations -v $INSTALLER du -h $INSTALLER QtAV-1.12.0/qtc_packaging/ifw/config/000077500000000000000000000000001312235004300172245ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/config/config.xml000066400000000000000000000020211312235004300212060ustar00rootroot00000000000000 QtAV 1.12.0 QtAV Multimedia framework WangBin wbsecg1@gmail.com http://qtav.org ../../src/QtAV.png ../../src/QtAV ../../src/QtAV.png control.js true QtAV @rootDir@/QtAV @rootDir@/QtAV false zh_CN.qm QtAV-1.12.0/qtc_packaging/ifw/config/control.js000066400000000000000000000022071312235004300212430ustar00rootroot00000000000000//https://doc.qt.io/qtinstallerframework/noninteractive.html //https://doc.qt.io/qtinstallerframework/scripting-installer.html function Controller() { //installer.installationFinished.connect(onInstalled) installer.uninstallationFinished.connect(function(){ console.log("uninstall done") if (installer.value("os") === "win") { var unreg = "delete;HKEY_CURRENT_USER\\SOFTWARE\\Classes\\*\\shell\\Open with QtAV;/f" var unreg2 = "delete;HKEY_CURRENT_USER\\SOFTWARE\\Classes\\*\\shell\\Open with QtAV QML;/f" installer.executeDetached("reg", unreg.split(";")) installer.executeDetached("reg", unreg2.split(";")) //QMessageBox.information("", "finish", "unreg: " + unreg2.split(";")) } }) } Controller.prototype.LicenseAgreementPageCallback = function() { var w = gui.currentPageWidget() if (w != null) w.AcceptLicenseRadioButton.checked = true } /* Controller.prototype.PerformInstallationPageCallback() { if (installer.isInstaller()) { } if (installer.isUninstaller()) { } var page = gui.currentPageWidget() if (!page) return } */ QtAV-1.12.0/qtc_packaging/ifw/packages/000077500000000000000000000000001312235004300175355ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/000077500000000000000000000000001312235004300235215ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/data/000077500000000000000000000000001312235004300244325ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.bat000077500000000000000000000022461312235004300272660ustar00rootroot00000000000000@echo QtAV SDK deployment tool for installer @echo off choice /c iu /m "Press 'I' to install QtAV SDK, 'U' to uninstall" set ACT=%errorlevel% @echo Input your absolute Qt folder contains bin, include, lib etc. @echo Or drag Qt folder here @echo Then press ENTER @echo example: C:\Qt5.6.0\5.6\mingw492_32 set /p QTDIR= if "%ACT%" == "2" ( goto uninstall ) else ( goto install ) :install copy /y bin\Qt*AV*.dll %QTDIR%\bin copy /y bin\av*.dll %QTDIR%\bin copy /y bin\sw*.dll %QTDIR%\bin copy /y bin\*openal*.dll %QTDIR%\bin copy /y bin\*portaudio*.dll %QTDIR%\bin xcopy /syi include %QTDIR%\include copy /y lib\*Qt*AV*.lib %QTDIR%\lib copy /y lib\*Qt*AV*.a %QTDIR%\lib xcopy /syi qml\QtAV %QTDIR%\qml\QtAV xcopy /syi mkspecs %QTDIR%\mkspecs @echo QtAV SDK is installed goto END :uninstall del %QTDIR%\bin\Qt*AV*.dll del %QTDIR%\bin\av*.dll del %QTDIR%\bin\sw*.dll del %QTDIR%\bin\*openal*.dll del %QTDIR%\bin\*portaudio*.dll rd /s /q %QTDIR%\include\QtAV rd /s /q %QTDIR%\include\QtAVWidgets del %QTDIR%\lib\*Qt*AV* del %QTDIR%\mkspecs\modules\qt_lib_av*.pri del %QTDIR%\mkspecs\features\av*.prf rd /s /q %QTDIR%\qml\QtAV @echo QtAV SDK is uninstalled goto END :END pause @echo on QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/data/sdk_deploy.sh000077500000000000000000000056661312235004300271430ustar00rootroot00000000000000# QtAV SDK deployment tool for installer THIS=$0 IN=$PWD help() { echo "$THIS -install|-uninstall qmake_path" exit 1 } check_qmake() { [ -n "$QMAKE" ] || { echo "Input your absolute path of qmake. Make sure the major and minor version and target architecture. That path MUST have subdir bin, include, lib, mkspecs etc." echo "example: $HOME/Qt5.6.0/5.6/gcc_64/bin/qmake" read QMAKE } [ ! -d $QMAKE -a -x $QMAKE ] || { echo "qmake is not available ($QMAKE)" help } QT_INSTALL_VER=`$QMAKE -query QT_VERSION` [ "$ACT" != "0" ] && return echo "Checking Qt version..." QTAV_MAJOR=`grep -m1 QTAV_MAJOR include/QtAV/version.h |sed 's,.*QTAV_MAJOR \([0-9]*\).*,\1,'` QTAV_MAJOR=${QTAV_MAJOR:-1} QT_BUILD_VER=`strings $IN/bin/libQt*AV.so.$QTAV_MAJOR |grep "Qt-.* licensee: "|sed 's,Qt-\(.*\) licensee: ,\1,'` echo QT_BUILD_VER=$QT_BUILD_VER #install: skip version check if QT_BUILD_VER is empty if [ -n "$QT_BUILD_VER" -a "${QT_BUILD_VER:0:4}" != "${QT_INSTALL_VER:0:4}" ]; then echo "Qt runtime version ($QT_INSTALL_VER) mismatches. QtAV is built with $QT_BUILD_VER" echo "Major and minor version MUST be the same" help fi } ACT=0 #0 iinstall, 1 uninstall if [ "$1" = "-install" ]; then ACT=0 elif [ "$1" = "-uninstall" ]; then ACT=1 elif [ "${THIS##*-}" = "install" ]; then ACT=0 elif [ "${THIS##*-}" = "uninstall" ]; then ACT=1 else echo "Select action: 0 Install(default), 1 uninstall" read ACT if [ "$ACT" = "1" -o "$ACT" = "uninstall" ]; then ACT=1 fi fi if [ $# -gt 1 ]; then QMAKE=$2 fi check_qmake echo $QMAKE QT_INSTALL_BINS=`$QMAKE -query QT_INSTALL_BINS` QT_INSTALL_HEADERS=`$QMAKE -query QT_INSTALL_HEADERS` QT_INSTALL_LIBS=`$QMAKE -query QT_INSTALL_LIBS` QT_INSTALL_QML=`$QMAKE -query QT_INSTALL_QML` if [ "$ACT" = "1" ]; then rm -rvf $QT_INSTALL_BINS/*Qt*AV* rm -rvf $QT_INSTALL_HEADERS/QtAV* rm -rvf $QT_INSTALL_LIBS/*Qt*AV* rm -rvf $QT_INSTALL_QML/QtAV rm -rvf $QT_INSTALL_BINS/../mkspecs/modules/qt_lib_av*.pri rm -rvf $QT_INSTALL_BINS/../mkspecs/features/av*.prf echo "QtAV uninstalled" exit 0 fi cp -avf $IN/bin/*Qt*AV*.so* $QT_INSTALL_BINS cp -avf $IN/include/* `$QMAKE -query QT_INSTALL_HEADERS` cp -avf $IN/lib/* $QT_INSTALL_LIBS cp -avf $IN/qml/QtAV `$QMAKE -query QT_INSTALL_QML` [ -d $QT_INSTALL_BINS/../mkspecs ] && cp -avf $IN/mkspecs/* $QT_INSTALL_BINS/../mkspecs || echo "Warning: mkspecs not found" test -f $QT_INSTALL_LIBS/libQt5AV.so || test -f $QT_INSTALL_LIBS/libQtAV.so || { cd $QT_INSTALL_LIBS if [ -f $QT_INSTALL_BINS/libQt5AV.so.$QTAV_MAJOR ]; then ln -sf $QT_INSTALL_BINS/libQt5AV.so.$QTAV_MAJOR libQt5AV.so ln -sf $QT_INSTALL_BINS/libQt5AVWidgets.so.$QTAV_MAJOR libQt5AVWidgets.so elif [ -f $QT_INSTALL_BINS/libQtAV.so.$QTAV_MAJOR ]; then ln -sf $QT_INSTALL_BINS/libQtAV.so.$QTAV_MAJOR libQtAV.so ln -sf $QT_INSTALL_BINS/libQtAVWidgets.so.$QTAV_MAJOR libQtAVWidgets.so fi cd - } echo "QtAV installed" QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/000077500000000000000000000000001312235004300244475ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/installscript.qs000066400000000000000000000043761312235004300277210ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { // constructor } Component.prototype.isDefault = function() { // select the component by default return false; } Component.prototype.createOperations = function() { try { // call the base create operations function component.createOperations(); } catch (e) { print(e); } } QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.dev/meta/package.xml000066400000000000000000000005471312235004300265720ustar00rootroot00000000000000 Development files Install QtAV headers and lib. 1.12.0-0 2017-06-20 com.qtav.product.dev script QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.examples/000077500000000000000000000000001312235004300245615ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/000077500000000000000000000000001312235004300255075ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/installscript.qs000066400000000000000000000043761312235004300307610ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { // constructor } Component.prototype.isDefault = function() { // select the component by default return false; } Component.prototype.createOperations = function() { try { // call the base create operations function component.createOperations(); } catch (e) { print(e); } } QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.examples/meta/package.xml000066400000000000000000000005341312235004300276260ustar00rootroot00000000000000 Examples Install QtAV examples. 1.12.0-0 2017-06-20 com.qtav.product.examples script QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/000077500000000000000000000000001312235004300242375ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/000077500000000000000000000000001312235004300251505ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/bin/000077500000000000000000000000001312235004300257205ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/bin/QMLPlayer.sh000077500000000000000000000001441312235004300300640ustar00rootroot00000000000000 D=`readlink -f $0` D=${D%/*} export LD_LIBRARY_PATH=$D:$D/lib $D/QMLPlayer "$@" # name with spaces QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/bin/player.sh000077500000000000000000000001411312235004300275470ustar00rootroot00000000000000 D=`readlink -f $0` D=${D%/*} export LD_LIBRARY_PATH=$D:$D/lib $D/Player "$@" # name with spaces QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/install.bat000066400000000000000000000007671312235004300273200ustar00rootroot00000000000000:: reg add "HKEY_CLASSES_ROOT\*\shell\OpenWithQtAV\command" /ve /t REG_SZ /d """"%~dp0player.exe""" """%%1"""" /f reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV\command" /ve /t REG_SZ /d """"%~dp0bin\player.exe""" """-vo""" """gl""" """-f""" """%%1"""" /f reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV QML\command" /ve /t REG_SZ /d """"%~dp0bin\QMLPlayer.exe""" """-f""" """%%1"""" /f @echo Right click an video/music file, choose "Open with QtAV" to play pause QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/player.bat000066400000000000000000000000331312235004300271300ustar00rootroot00000000000000start bin\player.exe -vo glQtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/data/uninstall.bat000066400000000000000000000002351312235004300276510ustar00rootroot00000000000000reg delete "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV" /f reg delete "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV QML" /f pauseQtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/meta/000077500000000000000000000000001312235004300251655ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/meta/installscript.qs000077500000000000000000000150031312235004300304270ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { // constructor } Component.prototype.isDefault = function() { // select the component by default return true; } Component.prototype.createOperations = function() { try { // call the base create operations function component.createOperations(); } catch (e) { print(e); } if (installer.value("os") === "win") { component.addOperation("CreateShortcut", "@TargetDir@/bin/player.exe", "@StartMenuDir@/QtAV.Player.lnk", "workingDirectory=@TargetDir@/bin", "iconPath=@TargetDir@/bin/player.exe", "iconId=0"); component.addOperation("CreateShortcut", "@TargetDir@/bin/QMLPlayer.exe", "@StartMenuDir@/QtAV.Player.QML.lnk", "workingDirectory=@TargetDir@/bin", "iconPath=@TargetDir@/bin/QMLPlayer.exe", "iconId=0"); component.addOperation("CreateShortcut", "@TargetDir@/maintenancetool.exe", "@StartMenuDir@/QtAV.Uninstall.lnk", "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/maintenancetool.exe", "iconId=0"); //component.addOperation("Settings", "formate=native", //"path=HKEY_CURRENT_USER/SOFTWARE/Classes/*/shell/OpenWithQtAV/command", "method=set", "key=Default", "value=@TargetDir@/bin/player.exe -vo gl -f %%1"); component.addOperation("GlobalConfig", "HKEY_CURRENT_USER\\SOFTWARE\\Classes\\*\\shell\\Open With QtAV\\command", "Default", "\"@TargetDir@/bin/player.exe\" -vo gl -f \"%1\""); component.addOperation("GlobalConfig", "HKEY_CURRENT_USER\\SOFTWARE\\Classes\\*\\shell\\Open With QtAV QML\\command", "Default", "\"@TargetDir@/bin/QMLPlayer.exe\" -f \"%1\""); } if (installer.value("os") === "x11") { var d = "Type=Application\nComment=A media playing framework based on Qt and FFmpeg\nKeywords=movie;video;player;multimedia;\nIcon=QtAV\nTerminal=false\nHomepage=https://github.com/wang-bin/QtAV\nCategories=Qt;AudioVideo;Player;Video;Development;\nMimeType=application/mxf;application/ogg;application/ram;application/sdp;application/smil;application/smil+xml;application/vnd.ms-wpl;application/vnd.rn-realmedia;application/x-extension-m4a;application/x-extension-mp4;application/x-flac;application/x-flash-video;application/x-matroska;application/x-netshow-channel;application/x-ogg;application/x-quicktime-media-link;application/x-quicktimeplayer;application/x-shorten;application/x-smil;application/xspf+xml;audio/3gpp;audio/ac3;audio/AMR;audio/AMR-WB;audio/basic;audio/midi;audio/mp2;audio/mp4;audio/mpeg;audio/mpegurl;audio/ogg;audio/prs.sid;audio/vnd.rn-realaudio;audio/x-aiff;audio/x-ape;audio/x-flac;audio/x-gsm;audio/x-it;audio/x-m4a;audio/x-matroska;audio/x-mod;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-asf;audio/x-ms-asx;audio/x-ms-wax;audio/x-ms-wma;audio/x-musepack;audio/x-pn-aiff;audio/x-pn-au;audio/x-pn-realaudio;audio/x-pn-realaudio-plugin;audio/x-pn-wav;audio/x-pn-windows-acm;audio/x-realaudio;audio/x-real-audio;audio/x-sbc;audio/x-scpls;audio/x-speex;audio/x-tta;audio/x-wav;audio/x-wavpack;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-xm;image/vnd.rn-realpix;image/x-pict;misc/ultravox;text/google-video-pointer;text/x-google-video-pointer;video/3gpp;video/dv;video/fli;video/flv;video/mp2t;video/mp4;video/mp4v-es;video/mpeg;video/msvideo;video/ogg;video/quicktime;video/vivo;video/vnd.divx;video/vnd.rn-realvideo;video/vnd.vivo;video/webm;video/x-anim;video/x-avi;video/x-flc;video/x-fli;video/x-flic;video/x-flv;video/x-m4v;video/x-matroska;video/x-mpeg;video/x-ms-asf;video/x-ms-asx;video/x-msvideo;video/x-ms-wm;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvx;video/x-nsv;video/x-ogm+ogg;video/x-theora+ogg;video/x-totem-stream;x-content/video-dvd;x-content/video-vcd;x-content/video-svcd;x-scheme-handler/pnm;x-scheme-handler/mms;x-scheme-handler/net;x-scheme-handler/rtp;x-scheme-handler/rtsp;x-scheme-handler/mmsh;x-scheme-handler/uvox;x-scheme-handler/icy;x-scheme-handler/icyx;\nKeywords=Player;DVD;Audio;Video;"; var player = "Name=QtAV Player\nGenericName=QtAV Player\nTryExec=@TargetDir@/bin/player.sh\nExec=@TargetDir@/bin/player.sh -f %U\n" + d; var qmlplayer = "Name=QtAV QMLPlayer\nGenericName=QtAV QMLPlayer\nTryExec=@TargetDir@/bin/QMLPlayer.sh\nExec=@TargetDir@/bin/QMLPlayer.sh -f %U\n" + d; component.addOperation("CreateDesktopEntry", "QtAV.Player.desktop", player); component.addOperation("CreateDesktopEntry", "QtAV.QMLPlayer.desktop", qmlplayer); component.addOperation("CreateDesktopEntry", "QtAV.Uninstall.desktop", "Name=QtAV Uninstall\nGenericName=QtAV Uninstall\nTryExec=@TargetDir@/uninstall\nExec=@TargetDir@/uninstall\nType=Application\nIcon=QtAV\nTerminal=false"); } } QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.player/meta/package.xml000066400000000000000000000006531312235004300273060ustar00rootroot00000000000000 Player Default player. 1.12.0-0 2017-06-20 com.qtav.product.player true true 7 QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.runtime/000077500000000000000000000000001312235004300244265ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/000077500000000000000000000000001312235004300253545ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product.runtime/meta/package.xml000066400000000000000000000010231312235004300274650ustar00rootroot00000000000000 Runtime library Install QtAV runtime library. 1.12.0-0 2017-06-20 com.qtav.product.runtime zh_CN.qm true true true 8 QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product/000077500000000000000000000000001312235004300227445ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product/meta/000077500000000000000000000000001312235004300236725ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product/meta/lgpl-2.1.txt000066400000000000000000000636421312235004300257020ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! QtAV-1.12.0/qtc_packaging/ifw/packages/com.qtav.product/meta/package.xml000066400000000000000000000007401312235004300260100ustar00rootroot00000000000000 QtAV Install QtAV multimedia library 1.12.0-0 2017-06-20 com.qtav.product zh_CN.qm true QtAV-1.12.0/qtc_packaging/windows/000077500000000000000000000000001312235004300166645ustar00rootroot00000000000000QtAV-1.12.0/qtc_packaging/windows/install.bat000066400000000000000000000007671312235004300210340ustar00rootroot00000000000000:: reg add "HKEY_CLASSES_ROOT\*\shell\OpenWithQtAV\command" /ve /t REG_SZ /d """"%~dp0player.exe""" """%%1"""" /f reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV\command" /ve /t REG_SZ /d """"%~dp0bin\player.exe""" """-vo""" """gl""" """-f""" """%%1"""" /f reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV QML\command" /ve /t REG_SZ /d """"%~dp0bin\QMLPlayer.exe""" """-f""" """%%1"""" /f @echo Right click an video/music file, choose "Open with QtAV" to play pause QtAV-1.12.0/qtc_packaging/windows/uninstall.bat000066400000000000000000000002351312235004300213650ustar00rootroot00000000000000reg delete "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV" /f reg delete "HKEY_CURRENT_USER\SOFTWARE\Classes\*\shell\Open with QtAV QML" /f pauseQtAV-1.12.0/root.pri000066400000000000000000000153351312235004300141050ustar00rootroot00000000000000#Designed by Wang Bin(Lucas Wang). 2013 ## TODO: put in .qmake.conf for Qt5? exists(user.conf): include(user.conf) ##TODO: BUILD_DIR=>BUILD_ROOT #if not empty, it means the parent project may already set it isEmpty(out_dir):out_dir = $$OUT_PWD out_dir ~= s,\\\\,/,g #avoid warning for '\'. qmake knows how to deal with '/' isEmpty(SOURCE_ROOT):SOURCE_ROOT = $$PWD SOURCE_ROOT ~= s,\\\\,/,g #avoid warning for '\'. qmake knows how to deal with '/' isEmpty(BUILD_DIR):BUILD_DIR=$$out_dir message("BUILD_DIR=$$BUILD_DIR") greaterThan(QT_MAJOR_VERSION, 4) { mkspecs_build = $$[QMAKE_SPEC] #recheck:write_file($$BUILD_DIR/.qmake.cache) #FIXME: empty_file result in no qtCompileTest result in cache #load(configure) # why MAKEFILE_GENERATOR is detected as XCODE in qtc before running qmake? } else { mkspecs_build = $$[QMAKE_MKSPECS] _QMAKE_CACHE_QT4_ = $$_QMAKE_CACHE_ #_QMAKE_CACHE_QT4_ is built in and always not empty isEmpty(_QMAKE_CACHE_QT4_)|isEqual(_QMAKE_CACHE_QT4_,) { _QMAKE_CACHE_QT4_=$$BUILD_DIR/.qmake.cache } include(common.pri) } SUPPORTED_MAKEFILE_GENERATOR = UNIX MINGW MSVC.NET MSBUILD message(MAKEFILE_GENERATOR=$$MAKEFILE_GENERATOR) greaterThan(QT_MAJOR_VERSION, 4):contains(SUPPORTED_MAKEFILE_GENERATOR, $$MAKEFILE_GENERATOR) { #workaround for android on windows. I don't know how qt deal with it equals(MAKEFILE_GENERATOR, UNIX):equals(QMAKE_HOST.os, Windows):MAKEFILE_GENERATOR=MINGW #configure.prf error if makefile generator is not supported and no display in qtcreator load(configure) #FIXME: ios can not set CONFIG+=iphoneos } else { #recheck:write_file($$BUILD_DIR/.qmake.cache) #FIXME: empty_file result in no qtCompileTest result in cache #use the following lines when building as a sub-project, write cache to this project src dir. #if build this project alone and do not have sub-project depends on this lib, those lines are not necessary ####ASSUME compile tests and .qmake.cache is in project out root dir #vars in .qmake.cache will affect all projects in subdirs, even if qmake's working dir is not in .qmake.cache dir #write_file($$BUILD_DIR/.qmake.cache) ##TODO: erase the existing lines!! include(configure.pri) #clear config.log iff reconfigure is required write_file($$QMAKE_CONFIG_LOG) #cache() is available after include configure.pri #load(configure.pri) #what's the difference? message("cache: $$_QMAKE_CACHE_QT4_") } mkspecs_build ~= s,\\\\,/,g #avoid warning for '\'. qmake knows how to deal with '/' defineTest(qtRunCommandQuitly) { #win32 always call windows command win32 { #QMAKE_HOST.os? system("$$1 2>&1 >nul")|return(false) #system always call win32 cmd } else { system("$$1 2>&1 >/dev/null")|return(false) } return(true) } lessThan(QT_MAJOR_VERSION, 5) { include(.qmake.conf) QTAV_VERSION = $${QTAV_MAJOR_VERSION}.$${QTAV_MINOR_VERSION}.$${QTAV_PATCH_VERSION} message("QTAV_VERSION not set, cache the default $$QTAV_VERSION") cache(QTAV_MAJOR_VERSION, set, QTAV_MAJOR_VERSION) cache(QTAV_MINOR_VERSION, set, QTAV_MINOR_VERSION) cache(QTAV_PATCH_VERSION, set, QTAV_PATCH_VERSION) cache(QTAV_VERSION, set, QTAV_VERSION) } defineTest(testArch) { test_dir = $$_PRO_FILE_PWD_/tests/arch test_out_dir = $$shadowed($$test_dir) qtRunCommandQuitly("$$QMAKE_MKDIR $$system_path($$test_out_dir)") #mkpath. but common.pri may not included contains(QMAKE_HOST.os,Windows): test_cmd_base = "cd /d $$system_quote($$system_path($$test_out_dir)) &&" else:test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&" # Disable qmake features which are typically counterproductive for tests qmake_configs = "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\"" iphoneos: qmake_configs += "\"CONFIG+=iphoneos\"" iphonesimulator: qmake_configs += "\"CONFIG+=iphonesimulator\"" # Clean up after previous run exists($$test_out_dir/Makefile):qtRunCommandQuitly("$$test_cmd_base $$QMAKE_MAKE distclean") SPEC = !isEmpty(QMAKESPEC): SPEC = "-spec $$QMAKESPEC" #message("$$test_cmd_base $$system_quote($$system_path($$QMAKE_QMAKE)) $$SPEC $$qmake_configs $$system_path($$test_dir)") qtRunCommandQuitly("$$test_cmd_base $$system_quote($$system_path($$QMAKE_QMAKE)) $$SPEC $$qmake_configs $$system_path($$test_dir)") { MSG=$$system("$$test_cmd_base $$QMAKE_MAKE 2>&1") } V = $$find(MSG, ARCH.*=.*) ARCH= ARCH_SUB= for(v, V) { # "ARCH=x86". can not evalate with ". why \" may fail? eval("expr") v=$$replace(v, \", ) eval("$$v") } export(ARCH) export(ARCH_SUB) cache(TARGET_ARCH, set, ARCH) cache(TARGET_ARCH_SUB, set, ARCH_SUB) cache(CONFIG, add, ARCH) cache(CONFIG, add, ARCH_SUB) #cached values will not affect current pro. subdir pro will be affected message("target arch: $$ARCH") message("target arch features: $$ARCH_SUB") } #cache mkspecs. compare mkspec with cached one. if not equal, remove old cache to run new compile tests #Qt5 does not have QMAKE_MKSPECS, use QMAKE_SPEC, QMAKE_XSPEC isEmpty(mkspecs_cached)|!isEqual(mkspecs_cached, $$mkspecs_build) { CONFIG += recheck testArch() } else { isEmpty(TARGET_ARCH):testArch() } QT_BIN=$$[QT_HOST_BINS] isEmpty(QT_BIN): QT_BIN=$$[QT_INSTALL_BINS] cache(QT_BIN, set, QT_BIN) cache(BUILD_DIR, set, BUILD_DIR) #cache(BUILD_ROOT, set, BUILD_DIR) cache(SOURCE_ROOT, set, SOURCE_ROOT) cache(mkspecs_cached, set, mkspecs_build) # no-framework can be defined in user.conf. default is the same as QT_CONFIG MAC_LIB = mac_dylib no-framework { cache(CONFIG, add, MAC_LIB) } else { cache(CONFIG, sub, MAC_LIB) } NO_WIDGETS = no_widgets no-widgets { cache(CONFIG, add, NO_WIDGETS) } else { cache(CONFIG, sub, NO_WIDGETS) } defineTest(runConfigTests) { no_config_tests:return(false) #config.tests !isEmpty(EssentialDepends) { for(d, EssentialDepends) { !config_$$d { CONFIG *= recheck } qtCompileTest($$d)|error("$$d is required, but compiler can not find it") # CONFIG -= recheck } } !isEmpty(OptionalDepends) { message("checking for optional features...") for(d, OptionalDepends) { qtCompileTest($$d) } } !isEmpty(EssentialDepends)|!isEmpty(OptionalDepends) { message("To recheck the dependencies, delete '.qmake.cache' in the root of build dir, run qmake with argument 'CONFIG+=recheck' or '-config recheck'") } return(true) } message("To disable config tests, you can use 1 of the following methods") message("1. create '.qmake.conf' in the root source dir, add 'CONFIG += no_config_tests'(Qt5)") message("2. pass 'CONFIG += no_config_tests' or '-config no_config_tests' to qmake") message("3. add 'CONFIG += no_config_tests' in $$PWD/user.conf") message("To manually set a config test result to true, disable config tests and enable config_name like above") QtAV-1.12.0/src/000077500000000000000000000000001312235004300131665ustar00rootroot00000000000000QtAV-1.12.0/src/AVClock.cpp000066400000000000000000000146321312235004300151620ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include #include #include "utils/Logger.h" namespace QtAV { enum { kRunning, kPaused, kStopped }; AVClock::AVClock(AVClock::ClockType c, QObject *parent): QObject(parent) , auto_clock(true) , m_state(kStopped) , clock_type(c) , mSpeed(1.0) , value0(0) , avg_err(0) , nb_restarted(0) , nb_sync(0) , sync_id(0) { last_pts = pts_ = pts_v = delay_ = 0; } AVClock::AVClock(QObject *parent): QObject(parent) , auto_clock(true) , m_state(kStopped) , clock_type(AudioClock) , mSpeed(1.0) , value0(0) , avg_err(0) , nb_restarted(0) , nb_sync(0) , sync_id(0) { last_pts = pts_ = pts_v = delay_ = 0; } void AVClock::setClockType(ClockType ct) { if (clock_type == ct) return; clock_type = ct; QTimer::singleShot(0, this, SLOT(restartCorrectionTimer())); } AVClock::ClockType AVClock::clockType() const { return clock_type; } bool AVClock::isActive() const { return clock_type == AudioClock || timer.isValid(); } void AVClock::setInitialValue(double v) { value0 = v; qDebug("Clock initial value: %f", v); } double AVClock::initialValue() const { return value0; } void AVClock::setClockAuto(bool a) { auto_clock = a; } bool AVClock::isClockAuto() const { return auto_clock; } void AVClock::updateExternalClock(qint64 msecs) { if (clock_type == AudioClock) return; qDebug("External clock change: %f ==> %f", value(), double(msecs) * kThousandth); pts_ = double(msecs) * kThousandth; //can not use msec/1000. if (!isPaused()) timer.restart(); last_pts = pts_; t = QDateTime::currentMSecsSinceEpoch(); if (clockType() == VideoClock) pts_v = pts_; } void AVClock::updateExternalClock(const AVClock &clock) { if (clock_type != ExternalClock) return; qDebug("External clock change: %f ==> %f", value(), clock.value()); pts_ = clock.value(); if (!isPaused()) timer.restart(); last_pts = pts_; t = QDateTime::currentMSecsSinceEpoch(); } void AVClock::setSpeed(qreal speed) { mSpeed = speed; } bool AVClock::isPaused() const { return m_state == kPaused; } int AVClock::syncStart(int count) { static int sId = 0; nb_sync = count; if (sId == -1) sId = 0; sync_id = ++sId; return sId; } bool AVClock::syncEndOnce(int id) { if (id != sync_id) { qWarning("bad sync id: %d, current: %d", id, sync_id); return true; } if (!nb_sync.deref()) sync_id = 0; return sync_id; } void AVClock::start() { m_state = kRunning; qDebug("AVClock started!!!!!!!!"); timer.start(); QTimer::singleShot(0, this, SLOT(restartCorrectionTimer())); Q_EMIT started(); } //remember last value because we don't reset pts_, pts_v, delay_ void AVClock::pause(bool p) { if (isPaused() == p) return; if (clock_type == AudioClock) return; m_state = p ? kPaused : kRunning; if (p) { QTimer::singleShot(0, this, SLOT(stopCorrectionTimer())); #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) timer.invalidate(); #else timer.stop(); #endif //QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) Q_EMIT paused(); } else { timer.start(); QTimer::singleShot(0, this, SLOT(restartCorrectionTimer())); Q_EMIT resumed(); } t = QDateTime::currentMSecsSinceEpoch(); Q_EMIT paused(p); } void AVClock::reset() { nb_sync = 0; sync_id = 0; // keep mSpeed m_state = kStopped; value0 = 0; pts_ = pts_v = delay_ = 0; QTimer::singleShot(0, this, SLOT(stopCorrectionTimer())); #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) timer.invalidate(); #else timer.stop(); #endif //QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) t = QDateTime::currentMSecsSinceEpoch(); Q_EMIT resetted(); } void AVClock::timerEvent(QTimerEvent *event) { Q_ASSERT_X(clockType() != AudioClock, "AVClock::timerEvent", "Internal error. AudioClock can not call this"); if (event->timerId() != correction_schedule_timer.timerId()) return; if (isPaused()) return; const double delta_pts = (value() - last_pts)/speed(); //const double err = double(correction_timer.restart()) * kThousandth - delta_pts; const qint64 now = QDateTime::currentMSecsSinceEpoch(); const double err = double(now - t) * kThousandth - delta_pts; t = now; // FIXME: avfoundation camera error is large (about -0.6s) if (qAbs(err*10.0) < kCorrectionInterval || clock_type == VideoClock) { avg_err += err/(nb_restarted+1); } //qDebug("correction timer event. error = %f, avg_err=%f, nb_restarted=%d", err, avg_err, nb_restarted); last_pts = value(); nb_restarted = 0; } void AVClock::restartCorrectionTimer() { nb_restarted = 0; avg_err = 0; correction_schedule_timer.stop(); if (clockType() == AudioClock) // TODO: for all clock type return; // parameters are reset. do not start correction timer if not running if (m_state != kRunning) return; // timer is always started in AVClock::start() if (!timer.isValid()) return; t = QDateTime::currentMSecsSinceEpoch(); correction_schedule_timer.start(kCorrectionInterval*1000, this); } void AVClock::stopCorrectionTimer() { nb_restarted = 0; avg_err = 0; correction_schedule_timer.stop(); } } //namespace QtAV QtAV-1.12.0/src/AVCompat.cpp000066400000000000000000000343201312235004300153460ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AVCompat.h" #include "QtAV/version.h" #if !FFMPEG_MODULE_CHECK(LIBAVFORMAT, 56, 4, 101) int avio_feof(AVIOContext *s) { #if QTAV_USE_FFMPEG(LIBAVFORMAT) return url_feof(s); #else return s && s->eof_reached; #endif } #endif #if QTAV_USE_LIBAV(LIBAVFORMAT) int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename) { AVFormatContext *s = avformat_alloc_context(); int ret = 0; *avctx = NULL; if (!s) goto nomem; if (!oformat) { if (format) { oformat = av_guess_format(format, NULL, NULL); if (!oformat) { av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); ret = AVERROR(EINVAL); goto error; } } else { oformat = av_guess_format(NULL, filename, NULL); if (!oformat) { ret = AVERROR(EINVAL); av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", filename); goto error; } } } s->oformat = oformat; if (s->oformat->priv_data_size > 0) { s->priv_data = av_mallocz(s->oformat->priv_data_size); if (!s->priv_data) goto nomem; if (s->oformat->priv_class) { *(const AVClass**)s->priv_data= s->oformat->priv_class; av_opt_set_defaults(s->priv_data); } } else s->priv_data = NULL; if (filename) av_strlcpy(s->filename, filename, sizeof(s->filename)); *avctx = s; return 0; nomem: av_log(s, AV_LOG_ERROR, "Out of memory\n"); ret = AVERROR(ENOMEM); error: avformat_free_context(s); return ret; } #endif //QTAV_USE_LIBAV(LIBAVFORMAT) #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0) static const struct { const char *name; int nb_channels; uint64_t layout; } channel_layout_map[] = { { "mono", 1, AV_CH_LAYOUT_MONO }, { "stereo", 2, AV_CH_LAYOUT_STEREO }, { "4.0", 4, AV_CH_LAYOUT_4POINT0 }, { "quad", 4, AV_CH_LAYOUT_QUAD }, { "5.0", 5, AV_CH_LAYOUT_5POINT0 }, { "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK }, { "5.1", 6, AV_CH_LAYOUT_5POINT1 }, { "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK }, { "5.1+downmix", 8, AV_CH_LAYOUT_5POINT1|AV_CH_LAYOUT_STEREO_DOWNMIX, }, { "7.1", 8, AV_CH_LAYOUT_7POINT1 }, { "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE }, { "7.1+downmix", 10, AV_CH_LAYOUT_7POINT1|AV_CH_LAYOUT_STEREO_DOWNMIX, }, { 0 } }; int64_t av_get_default_channel_layout(int nb_channels) { int i; for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++) if (nb_channels == channel_layout_map[i].nb_channels) return channel_layout_map[i].layout; return 0; } #endif /* * always need this function if avresample available * use AVAudioResampleContext to avoid func type confliction when swr is also available */ #if QTAV_HAVE(AVRESAMPLE) AVAudioResampleContext *swr_alloc_set_opts(AVAudioResampleContext *s , int64_t out_ch_layout , enum AVSampleFormat out_sample_fmt , int out_sample_rate , int64_t in_ch_layout , enum AVSampleFormat in_sample_fmt , int in_sample_rate , int log_offset, void *log_ctx) { //DO NOT use swr_alloc() because it's not defined as a macro in QtAV_Compat.h if (!s) s = avresample_alloc_context(); if (!s) return 0; Q_UNUSED(log_offset); Q_UNUSED(log_ctx); av_opt_set_int(s, "out_channel_layout", out_ch_layout , 0); av_opt_set_int(s, "out_sample_fmt" , out_sample_fmt , 0); av_opt_set_int(s, "out_sample_rate" , out_sample_rate, 0); av_opt_set_int(s, "in_channel_layout" , in_ch_layout , 0); av_opt_set_int(s, "in_sample_fmt" , in_sample_fmt , 0); av_opt_set_int(s, "in_sample_rate" , in_sample_rate , 0); return s; } #endif #if !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100) extern const AVPixFmtDescriptor av_pix_fmt_descriptors[]; const AVPixFmtDescriptor *av_pix_fmt_desc_get(AVPixelFormat pix_fmt) { if (pix_fmt < 0 || pix_fmt >= QTAV_PIX_FMT_C(NB)) return NULL; return &av_pix_fmt_descriptors[pix_fmt]; } const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev) { if (!prev) return &av_pix_fmt_descriptors[0]; // can not use sizeof(av_pix_fmt_descriptors) while (prev - av_pix_fmt_descriptors < QTAV_PIX_FMT_C(NB) - 1) { prev++; if (prev->name) return prev; } return NULL; } AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc) { if (desc < av_pix_fmt_descriptors || desc >= av_pix_fmt_descriptors + QTAV_PIX_FMT_C(NB)) return QTAV_PIX_FMT_C(NONE); return AVPixelFormat(desc - av_pix_fmt_descriptors); } #endif // !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100) #if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 48, 101) enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame) { if (!frame) return AVCOL_SPC_NB; #if LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 16, 0) //8c02adc return frame->colorspace; #endif return AVCOL_SPC_NB; } enum AVColorRange av_frame_get_color_range(const AVFrame *frame) { if (!frame) return AVCOL_RANGE_UNSPECIFIED; #if LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 16, 0) //8c02adc return frame->color_range; #endif return AVCOL_RANGE_UNSPECIFIED; } #endif //!FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 28, 101) #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100) int av_pix_fmt_count_planes(AVPixelFormat pix_fmt) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); int i, planes[4] = { 0 }, ret = 0; if (!desc) return AVERROR(EINVAL); for (i = 0; i < desc->nb_components; i++) planes[desc->comp[i].plane] = 1; for (i = 0; i < (int)FF_ARRAY_ELEMS(planes); i++) ret += planes[i]; return ret; } #endif //AV_VERSION_INT(52, 38, 100) #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 73, 101) int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset, int src_offset, int nb_samples, int nb_channels, enum AVSampleFormat sample_fmt) { int planar = av_sample_fmt_is_planar(sample_fmt); int planes = planar ? nb_channels : 1; int block_align = av_get_bytes_per_sample(sample_fmt) * (planar ? 1 : nb_channels); int data_size = nb_samples * block_align; int i; dst_offset *= block_align; src_offset *= block_align; if((dst[0] < src[0] ? src[0] - dst[0] : dst[0] - src[0]) >= data_size) { for (i = 0; i < planes; i++) memcpy(dst[i] + dst_offset, src[i] + src_offset, data_size); } else { for (i = 0; i < planes; i++) memmove(dst[i] + dst_offset, src[i] + src_offset, data_size); } return 0; } #endif //AV_VERSION_INT(51, 73, 101) #if QTAV_USE_LIBAV(LIBAVCODEC) const char *avcodec_get_name(enum AVCodecID id) { const AVCodecDescriptor *cd; AVCodec *codec; if (id == AV_CODEC_ID_NONE) return "none"; cd = avcodec_descriptor_get(id); if (cd) return cd->name; av_log(NULL, AV_LOG_WARNING, "Codec 0x%x is not in the full list.\n", id); codec = avcodec_find_decoder(id); if (codec) return codec->name; codec = avcodec_find_encoder(id); if (codec) return codec->name; return "unknown_codec"; } #endif #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 55, 0, 68, 100) void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb) { if (pkt->pts != (int64_t)AV_NOPTS_VALUE) pkt->pts = av_rescale_q(pkt->pts, src_tb, dst_tb); if (pkt->dts != (int64_t)AV_NOPTS_VALUE) pkt->dts = av_rescale_q(pkt->dts, src_tb, dst_tb); if (pkt->duration > 0) pkt->duration = av_rescale_q(pkt->duration, src_tb, dst_tb); if (pkt->convergence_duration > 0) pkt->convergence_duration = av_rescale_q(pkt->convergence_duration, src_tb, dst_tb); } #endif // since libav-11, ffmpeg-2.1 #if !LIBAV_MODULE_CHECK(LIBAVCODEC, 56, 1, 0) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100) int av_packet_copy_props(AVPacket *dst, const AVPacket *src) { dst->pts = src->pts; dst->dts = src->dts; dst->pos = src->pos; dst->duration = src->duration; dst->convergence_duration = src->convergence_duration; dst->flags = src->flags; dst->stream_index = src->stream_index; for (int i = 0; i < src->side_data_elems; i++) { enum AVPacketSideDataType type = src->side_data[i].type; int size = src->side_data[i].size; uint8_t *src_data = src->side_data[i].data; uint8_t *dst_data = av_packet_new_side_data(dst, type, size); if (!dst_data) { av_packet_free_side_data(dst); return AVERROR(ENOMEM); } memcpy(dst_data, src_data, size); } return 0; } #endif // since libav-10, ffmpeg-2.1 #if !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100) void av_packet_free_side_data(AVPacket *pkt) { for (int i = 0; i < pkt->side_data_elems; ++i) av_freep(&pkt->side_data[i].data); av_freep(&pkt->side_data); pkt->side_data_elems = 0; } #endif #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1, 39, 101) int av_packet_ref(AVPacket *dst, const AVPacket *src) { #if QTAV_USE_FFMPEG(LIBAVCODEC) return av_copy_packet(dst, const_cast(src)); // not const in these versions #else // libav <=11 has no av_copy_packet #define DUP_DATA(dst, src, size, padding) \ do { \ void *data; \ if (padding) { \ if ((unsigned)(size) > \ (unsigned)(size) + FF_INPUT_BUFFER_PADDING_SIZE) \ goto failed_alloc; \ data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); \ } else { \ data = av_malloc(size); \ } \ if (!data) \ goto failed_alloc; \ memcpy(data, src, size); \ if (padding) \ memset((uint8_t*)data + size, 0, \ FF_INPUT_BUFFER_PADDING_SIZE); \ *((void**)&dst) = data; \ } while (0) *dst = *src; dst->data = NULL; dst->side_data = NULL; DUP_DATA(dst->data, src->data, dst->size, 1); dst->destruct = av_destruct_packet; if (dst->side_data_elems) { int i; DUP_DATA(dst->side_data, src->side_data, dst->side_data_elems * sizeof(*dst->side_data), 0); memset(dst->side_data, 0, dst->side_data_elems * sizeof(*dst->side_data)); for (i = 0; i < dst->side_data_elems; i++) { DUP_DATA(dst->side_data[i].data, src->side_data[i].data, src->side_data[i].size, 1); dst->side_data[i].size = src->side_data[i].size; dst->side_data[i].type = src->side_data[i].type; } } return 0; failed_alloc: av_destruct_packet(dst); return AVERROR(ENOMEM); #endif } #endif #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 52, 0, 63, 100) void avcodec_free_context(AVCodecContext **pavctx) { AVCodecContext *avctx = *pavctx; if (!avctx) return; avcodec_close(avctx); av_freep(&avctx->extradata); av_freep(&avctx->subtitle_header); av_freep(&avctx->intra_matrix); av_freep(&avctx->inter_matrix); av_freep(&avctx->rc_override); av_freep(pavctx); } #endif const char *get_codec_long_name(enum AVCodecID id) { if (id == AV_CODEC_ID_NONE) return "none"; const AVCodecDescriptor *cd = avcodec_descriptor_get(id); if (cd) return cd->long_name; av_log(NULL, AV_LOG_WARNING, "Codec 0x%x is not in the full list.\n", id); AVCodec *codec = avcodec_find_decoder(id); if (codec) return codec->long_name; codec = avcodec_find_encoder(id); if (codec) return codec->long_name; return "unknown_codec"; } #if QTAV_HAVE(AVFILTER) #if !AV_MODULE_CHECK(LIBAVFILTER, 2, 22, 0, 79, 100) //FF_API_AVFILTERPAD_PUBLIC const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx) { return pads[pad_idx].name; } enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx) { return pads[pad_idx].type; } #endif #endif //QTAV_HAVE(AVFILTER) QtAV-1.12.0/src/AVDemuxThread.cpp000066400000000000000000000610101312235004300163310ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "AVDemuxThread.h" #include #include "QtAV/AVClock.h" #include "QtAV/AVDemuxer.h" #include "QtAV/AVDecoder.h" #include "VideoThread.h" #include #include "utils/Logger.h" #define RESUME_ONCE_ON_SEEK 0 namespace QtAV { class AutoSem { QSemaphore *s; public: AutoSem(QSemaphore* sem) : s(sem) { s->release();} ~AutoSem() { if (s->available() > 0) s->acquire(s->available()); } }; class QueueEmptyCall : public PacketBuffer::StateChangeCallback { public: QueueEmptyCall(AVDemuxThread* thread): mDemuxThread(thread) {} virtual void call() { if (!mDemuxThread) return; if (mDemuxThread->isEnd()) return; if (mDemuxThread->atEndOfMedia()) return; mDemuxThread->updateBufferState(); // ensure detect buffering immediately AVThread *thread = mDemuxThread->videoThread(); //qDebug("try wake up video queue"); if (thread) thread->packetQueue()->blockFull(false); //qDebug("try wake up audio queue"); thread = mDemuxThread->audioThread(); if (thread) thread->packetQueue()->blockFull(false); } private: AVDemuxThread *mDemuxThread; }; AVDemuxThread::AVDemuxThread(QObject *parent) : QThread(parent) , paused(false) , user_paused(false) , end(false) , end_action(MediaEndAction_Default) , m_buffering(false) , m_buffer(0) , demuxer(0) , ademuxer(0) , audio_thread(0) , video_thread(0) , clock_type(-1) { seek_tasks.setCapacity(1); seek_tasks.blockFull(false); } AVDemuxThread::AVDemuxThread(AVDemuxer *dmx, QObject *parent) : QThread(parent) , paused(false) , end(false) , m_buffering(false) , m_buffer(0) , audio_thread(0) , video_thread(0) { setDemuxer(dmx); seek_tasks.setCapacity(1); seek_tasks.blockFull(false); } void AVDemuxThread::setDemuxer(AVDemuxer *dmx) { demuxer = dmx; } void AVDemuxThread::setAudioDemuxer(AVDemuxer *demuxer) { //QMutexLocker locker(&buffer_mutex); //Q_UNUSED(locker); ademuxer = demuxer; } void AVDemuxThread::setAVThread(AVThread*& pOld, AVThread *pNew) { if (pOld == pNew) return; if (pOld) { if (pOld->isRunning()) pOld->stop(); pOld->disconnect(this, SLOT(onAVThreadQuit())); } pOld = pNew; if (!pNew) return; pOld->packetQueue()->setEmptyCallback(new QueueEmptyCall(this)); connect(pOld, SIGNAL(finished()), SLOT(onAVThreadQuit())); } void AVDemuxThread::setAudioThread(AVThread *thread) { setAVThread(audio_thread, thread); } void AVDemuxThread::setVideoThread(AVThread *thread) { setAVThread(video_thread, thread); } AVThread* AVDemuxThread::videoThread() { return video_thread; } AVThread* AVDemuxThread::audioThread() { return audio_thread; } void AVDemuxThread::stepBackward() { if (!video_thread) return; AVThread *t = video_thread; const qreal pre_pts = video_thread->previousHistoryPts(); if (pre_pts == 0.0) { qWarning("can not get previous pts"); return; } end = false; // queue maybe blocked by put() if (audio_thread) { audio_thread->packetQueue()->clear(); // will put new packets before task run } class stepBackwardTask : public QRunnable { public: stepBackwardTask(AVDemuxThread *dt, qreal t) : demux_thread(dt) , pts(t) {} void run() { AVThread *avt = demux_thread->videoThread(); avt->packetQueue()->clear(); // clear here if (pts <= 0) { demux_thread->demuxer->seek(qint64(-pts*1000.0) - 500LL); QVector ts; qreal t = -1.0; while (t < -pts) { demux_thread->demuxer->readFrame(); if (demux_thread->demuxer->stream() != demux_thread->demuxer->videoStream()) continue; t = demux_thread->demuxer->packet().pts; ts.push_back(t); } const qreal t0 = ts.back(); ts.pop_back(); const qreal dt = t0 - ts.back(); pts = ts.back(); // FIXME: sometimes can not seek to the previous pts, the result pts is always current pts, so let the target pts a little earlier pts -= dt/2.0; } qDebug("step backward: %lld, %f", qint64(pts*1000.0), pts); demux_thread->video_thread->setDropFrameOnSeek(false); demux_thread->seekInternal(qint64(pts*1000.0), AccurateSeek); } private: AVDemuxThread *demux_thread; qreal pts; }; pause(true); t->packetQueue()->clear(); // will put new packets before task run t->packetQueue(); Packet pkt; pkt.pts = pre_pts; t->packetQueue()->put(pkt); // clear and put a seek packet to ensure not frames other than previous frame will be decoded and rendered video_thread->pause(false); newSeekRequest(new stepBackwardTask(this, pre_pts)); } void AVDemuxThread::seek(qint64 pos, SeekType type) { end = false; // queue maybe blocked by put() if (audio_thread) { audio_thread->packetQueue()->clear(); } if (video_thread) { video_thread->packetQueue()->clear(); } class SeekTask : public QRunnable { public: SeekTask(AVDemuxThread *dt, qint64 t, SeekType st) : demux_thread(dt) , type(st) , position(t) {} void run() { if (demux_thread->video_thread) demux_thread->video_thread->setDropFrameOnSeek(true); demux_thread->seekInternal(position, type); } private: AVDemuxThread *demux_thread; SeekType type; qint64 position; }; newSeekRequest(new SeekTask(this, pos, type)); } void AVDemuxThread::seekInternal(qint64 pos, SeekType type) { AVThread* av[] = { audio_thread, video_thread}; qDebug("seek to %s %lld ms (%f%%)", QTime(0, 0, 0).addMSecs(pos).toString().toUtf8().constData(), pos, double(pos - demuxer->startTime())/double(demuxer->duration())*100.0); demuxer->setSeekType(type); demuxer->seek(pos); if (ademuxer) { ademuxer->setSeekType(type); ademuxer->seek(pos); } AVThread *watch_thread = 0; // TODO: why queue may not empty? int sync_id = 0; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread *t = av[i]; if (!t) continue; if (!sync_id) sync_id = t->clock()->syncStart(!!audio_thread + (!!video_thread && !demuxer->hasAttacedPicture())); Q_ASSERT(sync_id != 0); qDebug("demuxer sync id: %d/%d", sync_id, t->clock()->syncId()); t->packetQueue()->clear(); t->requestSeek(); // TODO: the first frame (key frame) will not be decoded correctly if flush() is called. //PacketBuffer *pb = t->packetQueue(); //qDebug("%s put seek packet. %d/%d-%.3f, progress: %.3f", t->metaObject()->className(), pb->buffered(), pb->bufferValue(), pb->bufferMax(), pb->bufferProgress()); t->packetQueue()->setBlocking(false); // aqueue bufferValue can be small (1), we can not put and take Packet pkt; pkt.pts = qreal(pos)/1000.0; pkt.position = sync_id; t->packetQueue()->put(pkt); t->packetQueue()->setBlocking(true); // blockEmpty was false when eof is read. if (isPaused()) { //TODO: deal with pause in AVThread? t->pause(false); watch_thread = t; } } if (watch_thread) { pauseInternal(false); Q_EMIT requestClockPause(false); // need direct connection // direct connection is fine here connect(watch_thread, SIGNAL(seekFinished(qint64)), this, SLOT(seekOnPauseFinished()), Qt::DirectConnection); } } void AVDemuxThread::newSeekRequest(QRunnable *r) { if (seek_tasks.size() >= seek_tasks.capacity()) { QRunnable *r = seek_tasks.take(); if (r && r->autoDelete()) delete r; } seek_tasks.put(r); } void AVDemuxThread::processNextSeekTask() { if (seek_tasks.isEmpty()) return; QRunnable *task = seek_tasks.take(); if (!task) return; task->run(); if (task->autoDelete()) delete task; } void AVDemuxThread::pauseInternal(bool value) { paused = value; } bool AVDemuxThread::isPaused() const { return paused; } bool AVDemuxThread::isEnd() const { return end; } bool AVDemuxThread::atEndOfMedia() const { return demuxer->atEnd(); } PacketBuffer* AVDemuxThread::buffer() { return m_buffer; } void AVDemuxThread::updateBufferState() { if (!m_buffer) return; if (m_buffering) { // always report progress when buffering Q_EMIT bufferProgressChanged(m_buffer->bufferProgress()); } if (m_buffering == m_buffer->isBuffering()) return; m_buffering = m_buffer->isBuffering(); Q_EMIT mediaStatusChanged(m_buffering ? QtAV::BufferingMedia : QtAV::BufferedMedia); // state change to buffering, report progress immediately. otherwise we have to wait to read 1 packet. if (m_buffering) { Q_EMIT bufferProgressChanged(m_buffer->bufferProgress()); } } //No more data to put. So stop blocking the queue to take the reset elements void AVDemuxThread::stop() { //this will not affect the pause state if we pause the output //TODO: why remove blockFull(false) can not play another file? AVThread* av[] = { audio_thread, video_thread}; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread* t = av[i]; if (!t) continue; t->packetQueue()->clear(); t->packetQueue()->blockFull(false); //?? while (t->isRunning()) { qDebug() << "stopping thread " << t; t->stop(); t->wait(500); } } pause(false); cond.wakeAll(); qDebug("all avthread finished. try to exit demux thread<<<<<<"); end = true; } void AVDemuxThread::pause(bool p, bool wait) { user_paused = p; if (paused == p) return; paused = p; if (!paused) cond.wakeAll(); else { if (wait) { // block until current loop finished buffer_mutex.lock(); buffer_mutex.unlock(); } } } void AVDemuxThread::setMediaEndAction(MediaEndAction value) { end_action = value; } MediaEndAction AVDemuxThread::mediaEndAction() const { return end_action; } void AVDemuxThread::stepForward() { if (end) return; // clock type will be wrong if no lock because slot frameDeliveredOnStepForward() is in video thread QMutexLocker locker(&next_frame_mutex); Q_UNUSED(locker); pause(true); // must pause AVDemuxThread (set user_paused true) AVThread* av[] = {video_thread, audio_thread}; bool connected = false; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread *t = av[i]; if (!t) continue; // set clock first if (clock_type < 0) clock_type = (int)t->clock()->isClockAuto() + 2*(int)t->clock()->clockType(); t->clock()->setClockType(AVClock::VideoClock); t->scheduleFrameDrop(false); t->pause(false); t->packetQueue()->blockFull(false); if (!connected) { connect(t, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward()), Qt::DirectConnection); connect(t, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward()), Qt::DirectConnection); connected = true; } } Q_EMIT requestClockPause(false); pauseInternal(false); } void AVDemuxThread::seekOnPauseFinished() { AVThread *thread = video_thread ? video_thread : audio_thread; Q_ASSERT(thread); disconnect(thread, SIGNAL(seekFinished(qint64)), this, SLOT(seekOnPauseFinished())); if (user_paused) { pause(true); // restore pause state Q_EMIT requestClockPause(true); // need direct connection // pause video/audio thread if (video_thread) video_thread->pause(true); if (audio_thread) audio_thread->pause(true); } } void AVDemuxThread::frameDeliveredOnStepForward() { AVThread *thread = video_thread ? video_thread : audio_thread; Q_ASSERT(thread); QMutexLocker locker(&next_frame_mutex); Q_UNUSED(locker); disconnect(thread, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward())); disconnect(thread, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward())); if (user_paused) { pause(true); // restore pause state Q_EMIT requestClockPause(true); // need direct connection // pause both video and audio thread if (video_thread) video_thread->pause(true); if (audio_thread) audio_thread->pause(true); } if (clock_type >= 0) { thread->clock()->setClockAuto(clock_type & 1); thread->clock()->setClockType(AVClock::ClockType(clock_type/2)); clock_type = -1; thread->clock()->updateExternalClock((thread->previousHistoryPts() - thread->clock()->initialValue())*1000.0); } Q_EMIT stepFinished(); } void AVDemuxThread::eofDecodedOnStepForward() { AVThread *thread = video_thread ? video_thread : audio_thread; Q_ASSERT(thread); QMutexLocker locker(&next_frame_mutex); Q_UNUSED(locker); disconnect(thread, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward())); disconnect(thread, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward())); pause(false); end = true; if (clock_type >= 0) { thread->clock()->setClockAuto(clock_type & 1); thread->clock()->setClockType(AVClock::ClockType(clock_type/2)); clock_type = -1; } Q_EMIT stepFinished(); } void AVDemuxThread::onAVThreadQuit() { AVThread* av[] = { audio_thread, video_thread}; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { if (!av[i]) continue; if (av[i]->isRunning()) return; } end = true; //(!audio_thread || !audio_thread->isRunning()) && } bool AVDemuxThread::waitForStarted(int msec) { if (!sem.tryAcquire(1, msec > 0 ? msec : std::numeric_limits::max())) return false; sem.release(1); //ensure other waitForStarted() calls continue return true; } void AVDemuxThread::run() { m_buffering = false; end = false; if (audio_thread && !audio_thread->isRunning()) audio_thread->start(QThread::HighPriority); if (video_thread && !video_thread->isRunning()) video_thread->start(); int stream = 0; Packet pkt; pause(false); qDebug("get av queue a/v thread = %p %p", audio_thread, video_thread); PacketBuffer *aqueue = audio_thread ? audio_thread->packetQueue() : 0; PacketBuffer *vqueue = video_thread ? video_thread->packetQueue() : 0; // aqueue as a primary buffer: music with/without cover AVThread* thread = !video_thread || (audio_thread && demuxer->hasAttacedPicture()) ? audio_thread : video_thread; m_buffer = thread->packetQueue(); const qint64 buf2 = aqueue ? aqueue->bufferValue() : 1; // TODO: may be changed by user. Deal with audio track change if (aqueue) { aqueue->clear(); aqueue->setBlocking(true); } if (vqueue) { vqueue->clear(); vqueue->setBlocking(true); } connect(thread, SIGNAL(seekFinished(qint64)), this, SIGNAL(seekFinished(qint64)), Qt::DirectConnection); seek_tasks.clear(); int was_end = 0; if (ademuxer) { ademuxer->seek(0LL); } qreal last_apts = 0; qreal last_vpts = 0; AutoSem as(&sem); Q_UNUSED(as); while (!end) { processNextSeekTask(); //vthread maybe changed by AVPlayer.setPriority() from no dec case vqueue = video_thread ? video_thread->packetQueue() : 0; if (demuxer->atEnd()) { // if avthread may skip 1st eof packet because of a/v sync const int kMaxEof = 1;//if buffer packet, we can use qMax(aqueue->bufferValue(), vqueue->bufferValue()) and not call blockEmpty(false); if (aqueue && (!was_end || aqueue->isEmpty())) { if (was_end < kMaxEof) aqueue->put(Packet::createEOF()); const qreal dpts = last_vpts - last_apts; if (dpts > 0.1) { Packet fake_apkt; fake_apkt.duration = last_vpts - qMin(thread->clock()->videoTime(), thread->clock()->value()); // FIXME: when clock value < 0? qDebug("audio is too short than video: %.3f, fake_apkt.duration: %.3f", dpts, fake_apkt.duration); last_apts = last_vpts = 0; // if not reset to 0, for example real eof pts, then no fake apkt after seek because dpts < 0 aqueue->put(fake_apkt); } aqueue->blockEmpty(was_end >= kMaxEof); // do not block if buffer is not enough. block again on seek } if (vqueue && (!was_end || vqueue->isEmpty())) { if (was_end < kMaxEof) vqueue->put(Packet::createEOF()); vqueue->blockEmpty(was_end >= kMaxEof); } if (m_buffering) { m_buffering = false; Q_EMIT mediaStatusChanged(QtAV::BufferedMedia); } was_end = qMin(was_end + 1, kMaxEof); bool exit_thread = !user_paused; if (aqueue) exit_thread &= aqueue->isEmpty(); if (vqueue) exit_thread &= vqueue->isEmpty(); if (exit_thread) { if (!(mediaEndAction() & MediaEndAction_Pause)) break; pause(true); Q_EMIT requestClockPause(true); if (aqueue) aqueue->blockEmpty(true); if (vqueue) vqueue->blockEmpty(true); } // wait for a/v thread finished msleep(100); continue; } if (demuxer->mediaStatus() == StalledMedia) { qDebug("stalled media. exiting demuxing thread"); break; } was_end = 0; if (tryPause()) { continue; //the queue is empty and will block } updateBufferState(); if (!demuxer->readFrame()) { continue; } stream = demuxer->stream(); pkt = demuxer->packet(); Packet apkt; bool audio_has_pic = demuxer->hasAttacedPicture(); int a_ext = 0; if (ademuxer) { QMutexLocker locker(&buffer_mutex); Q_UNUSED(locker); if (ademuxer) { a_ext = -1; audio_has_pic = ademuxer->hasAttacedPicture(); // FIXME: buffer full but buffering!!! // avoid read external track everytime. aqueue may not block full // vqueue will not block if aqueue is not enough if (!aqueue->isFull() || aqueue->isBuffering()) { if (ademuxer->readFrame()) { if (ademuxer->stream() == ademuxer->audioStream()) { a_ext = 1; apkt = ademuxer->packet(); } } // no continue otherwise. ademuxer finished earlier than demuxer } } } //qDebug("vqueue: %d, aqueue: %d/isbuffering %d isfull: %d, buffer: %d/%d", vqueue->size(), aqueue->size(), aqueue->isBuffering(), aqueue->isFull(), aqueue->buffered(), aqueue->bufferValue()); //QMutexLocker locker(&buffer_mutex); //TODO: seems we do not need to lock //Q_UNUSED(locker); /*1 is empty but another is enough, then do not block to ensure the empty one can put packets immediatly. But usually it will not happen, why? */ /* demux thread will be blocked only when 1 queue is full and still put * if vqueue is full and aqueue becomes empty, then demux thread * will be blocked. so we should wake up another queue when empty(or threshold?). * TODO: the video stream and audio stream may be group by group. provide it * stream data: aaaaaaavvvvvvvaaaaaaaavvvvvvvvvaaaaaa, it happens * stream data: aavavvavvavavavavavavavavvvaavavavava, it's ok */ //TODO: use cache queue, take from cache queue if not empty? const bool a_internal = stream == demuxer->audioStream(); if (a_internal || a_ext > 0) {//apkt.isValid()) { if (a_internal && !a_ext) // internal is always read even if external audio used apkt = demuxer->packet(); last_apts = apkt.pts; /* if vqueue if not blocked and full, and aqueue is empty, then put to * vqueue will block demuex thread */ if (aqueue) { if (!audio_thread || !audio_thread->isRunning()) { aqueue->clear(); continue; } // must ensure bufferValue set correctly before continue if (m_buffer != aqueue) aqueue->setBufferValue(m_buffer->isBuffering() ? std::numeric_limits::max() : buf2); // always block full if no vqueue because empty callback may set false // attached picture is cover for song, 1 frame aqueue->blockFull(!video_thread || !video_thread->isRunning() || !vqueue || audio_has_pic); // external audio: a_ext < 0, stream = audio_idx=>put invalid packet if (a_ext >= 0) aqueue->put(apkt); //affect video_thread } } // always check video stream if use external audio if (stream == demuxer->videoStream()) { if (vqueue) { if (!video_thread || !video_thread->isRunning()) { vqueue->clear(); continue; } vqueue->blockFull(!audio_thread || !audio_thread->isRunning() || !aqueue || aqueue->isEnough()); vqueue->put(pkt); //affect audio_thread last_vpts = pkt.pts; } } else if (demuxer->subtitleStreams().contains(stream)) { //subtitle Q_EMIT internalSubtitlePacketRead(demuxer->subtitleStreams().indexOf(stream), pkt); } else { continue; } } m_buffering = false; m_buffer = 0; while (audio_thread && audio_thread->isRunning()) { qDebug("waiting audio thread......."); Packet quit_pkt(Packet::createEOF()); quit_pkt.position = 0; aqueue->put(quit_pkt); aqueue->blockEmpty(false); //FIXME: why need this audio_thread->pause(false); audio_thread->wait(500); } while (video_thread && video_thread->isRunning()) { qDebug("waiting video thread......."); Packet quit_pkt(Packet::createEOF()); quit_pkt.position = 0; vqueue->put(quit_pkt); vqueue->blockEmpty(false); video_thread->pause(false); video_thread->wait(500); } thread->disconnect(this, SIGNAL(seekFinished(qint64))); qDebug("Demux thread stops running...."); if (demuxer->atEnd()) Q_EMIT mediaStatusChanged(QtAV::EndOfMedia); else Q_EMIT mediaStatusChanged(QtAV::StalledMedia); } bool AVDemuxThread::tryPause(unsigned long timeout) { if (!paused) return false; QMutexLocker lock(&buffer_mutex); Q_UNUSED(lock); cond.wait(&buffer_mutex, timeout); return true; } } //namespace QtAV QtAV-1.12.0/src/AVDemuxThread.h000066400000000000000000000073241312235004300160060ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_DEMUXTHREAD_H #define QAV_DEMUXTHREAD_H #include #include #include #include #include "PacketBuffer.h" namespace QtAV { class AVDemuxer; class AVThread; class AVDemuxThread : public QThread { Q_OBJECT public: explicit AVDemuxThread(QObject *parent = 0); explicit AVDemuxThread(AVDemuxer *dmx, QObject *parent = 0); void setDemuxer(AVDemuxer *dmx); void setAudioDemuxer(AVDemuxer *demuxer); //not thread safe void setAudioThread(AVThread *thread); AVThread* audioThread(); void setVideoThread(AVThread *thread); AVThread* videoThread(); void stepForward(); // show next video frame and pause void stepBackward(); void seek(qint64 pos, SeekType type); //ms //AVDemuxer* demuxer bool isPaused() const; bool isEnd() const; bool atEndOfMedia() const; PacketBuffer* buffer(); void updateBufferState(); void stop(); //TODO: remove it? void pause(bool p, bool wait = false); MediaEndAction mediaEndAction() const; void setMediaEndAction(MediaEndAction value); bool waitForStarted(int msec = -1); Q_SIGNALS: void requestClockPause(bool value); void mediaStatusChanged(QtAV::MediaStatus); void bufferProgressChanged(qreal); void seekFinished(qint64 timestamp); void stepFinished(); void internalSubtitlePacketRead(int index, const QtAV::Packet& packet); private slots: void seekOnPauseFinished(); void frameDeliveredOnStepForward(); void eofDecodedOnStepForward(); void onAVThreadQuit(); protected: virtual void run(); /* * If the pause state is true setted by pause(true), then block the thread and wait for pause state changed, i.e. pause(false) * and return true. Otherwise, return false immediatly. */ bool tryPause(unsigned long timeout = 100); private: void setAVThread(AVThread *&pOld, AVThread* pNew); void newSeekRequest(QRunnable *r); void processNextSeekTask(); void seekInternal(qint64 pos, SeekType type); //must call in AVDemuxThread void pauseInternal(bool value); bool paused; bool user_paused; volatile bool end; MediaEndAction end_action; bool m_buffering; PacketBuffer *m_buffer; AVDemuxer *demuxer; AVDemuxer *ademuxer; AVThread *audio_thread, *video_thread; int audio_stream, video_stream; QMutex buffer_mutex; QWaitCondition cond; BlockingQueue seek_tasks; QSemaphore sem; QMutex next_frame_mutex; int clock_type; // change happens in different threads(direct connection) friend class SeekTask; friend class stepBackwardTask; }; } //namespace QtAV #endif // QAV_DEMUXTHREAD_H QtAV-1.12.0/src/AVDemuxer.cpp000066400000000000000000001212601312235004300155340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AVDemuxer.h" #include "QtAV/MediaIO.h" #include "QtAV/private/AVCompat.h" #include #include #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #include #else #include typedef QTime QElapsedTimer; #endif #include "utils/internal.h" #include "utils/Logger.h" namespace QtAV { static const char kFileScheme[] = "file:"; class AVDemuxer::InterruptHandler : public AVIOInterruptCB { public: enum Action { Unknown = -1, Open, FindStreamInfo, Read }; //default network timeout: 30000 InterruptHandler(AVDemuxer* demuxer, int timeout = 30000) : mStatus(0) , mTimeout(timeout) , mTimeoutAbort(true) , mEmitError(true) //, mLastTime(0) , mAction(Unknown) , mpDemuxer(demuxer) { callback = handleTimeout; opaque = this; } ~InterruptHandler() { #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) mTimer.invalidate(); #else mTimer.stop(); #endif } void begin(Action act) { if (mStatus > 0) mStatus = 0; mEmitError = true; mAction = act; mTimer.start(); } void end() { #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) mTimer.invalidate(); #else mTimer.stop(); #endif switch (mAction) { case Read: //mpDemuxer->setMediaStatus(BufferedMedia); break; default: break; } mAction = Unknown; } qint64 getTimeout() const { return mTimeout; } void setTimeout(qint64 timeout) { mTimeout = timeout; } bool setInterruptOnTimeout(bool value) { if (mTimeoutAbort == value) return false; mTimeoutAbort = value; if (mTimeoutAbort) { mEmitError = true; } return true; } bool isInterruptOnTimeout() const {return mTimeoutAbort;} int getStatus() const { return mStatus; } void setStatus(int status) { mStatus = status; } /* * metodo per interruzione loop ffmpeg * @param void*obj: classe attuale * @return * >0 Interruzione loop di ffmpeg! */ static int handleTimeout(void* obj) { InterruptHandler* handler = static_cast(obj); if (!handler) { qWarning("InterruptHandler is null"); return -1; } //check manual interruption if (handler->getStatus() < 0) { qDebug("User Interrupt: -> quit!"); // DO NOT call setMediaStatus() here. /* MUST make sure blocking functions (open, read) return before we change the status * because demuxer may be closed in another thread at the same time if status is not LoadingMedia * use handleError() after blocking functions return is good */ // 1: blocking operation will be aborted. return 1;//interrupt } // qApp->processEvents(); //FIXME: qml crash switch (handler->mAction) { case Unknown: //callback is not called between begin()/end() //qWarning("Unknown timeout action"); break; case Open: case FindStreamInfo: //qDebug("set loading media for %d from: %d", handler->mAction, handler->mpDemuxer->mediaStatus()); handler->mpDemuxer->setMediaStatus(LoadingMedia); break; case Read: //handler->mpDemuxer->setMediaStatus(BufferingMedia); default: break; } if (handler->mTimeout < 0) return 0; if (!handler->mTimer.isValid()) { //qDebug("timer is not valid, start it"); handler->mTimer.start(); //handler->mLastTime = handler->mTimer.elapsed(); return 0; } //use restart #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) if (!handler->mTimer.hasExpired(handler->mTimeout)) #else if (handler->mTimer.elapsed() < handler->mTimeout) #endif return 0; qDebug("status: %d, Timeout expired: %lld/%lld -> quit!", (int)handler->mStatus, handler->mTimer.elapsed(), handler->mTimeout); handler->mTimer.invalidate(); if (handler->mStatus == 0) { AVError::ErrorCode ec(AVError::ReadTimedout); if (handler->mAction == Open) { ec = AVError::OpenTimedout; } else if (handler->mAction == FindStreamInfo) { ec = AVError::ParseStreamTimedOut; } else if (handler->mAction == Read) { ec = AVError::ReadTimedout; } handler->mStatus = (int)ec; // maybe changed in other threads //handler->mStatus.testAndSetAcquire(0, ec); } if (handler->mTimeoutAbort) return 1; // emit demuxer error, handleerror if (handler->mEmitError) { handler->mEmitError = false; AVError::ErrorCode ec = AVError::ErrorCode(handler->mStatus); //FIXME: maybe changed in other threads QString es; handler->mpDemuxer->handleError(AVERROR_EXIT, &ec, es); } return 0; } private: int mStatus; qint64 mTimeout; bool mTimeoutAbort; bool mEmitError; //qint64 mLastTime; Action mAction; AVDemuxer *mpDemuxer; QElapsedTimer mTimer; }; class AVDemuxer::Private { public: Private() : media_status(NoMedia) , seekable(false) , network(false) , has_attached_pic(false) , started(false) , max_pts(0.0) , eof(false) , media_changed(true) , buf_pos(0) , stream(-1) , format_ctx(0) , input_format(0) , input(0) , seek_unit(SeekByTime) , seek_type(AccurateSeek) , dict(0) , interrupt_hanlder(0) {} ~Private() { delete interrupt_hanlder; if (dict) { av_dict_free(&dict); dict = 0; } if (input) { delete input; input = 0; } } void applyOptionsForDict(); void applyOptionsForContext(); void resetStreams() { stream = -1; if (media_changed) astream = vstream = sstream = StreamInfo(); else astream.avctx = vstream.avctx = sstream.avctx = 0; audio_streams.clear(); video_streams.clear(); subtitle_streams.clear(); } void checkNetwork() { // FIXME: is there a good way to check network? now use URLContext.flags == URL_PROTOCOL_FLAG_NETWORK // not network: concat cache pipe avdevice crypto? if (!file.isEmpty() && file.contains(QLatin1String(":")) && (file.startsWith(QLatin1String("http")) //http, https, httpproxy || file.startsWith(QLatin1String("rtmp")) //rtmp{,e,s,te,ts} || file.startsWith(QLatin1String("mms")) //mms{,h,t} || file.startsWith(QLatin1String("ffrtmp")) //ffrtmpcrypt, ffrtmphttp || file.startsWith(QLatin1String("rtp:")) || file.startsWith(QLatin1String("rtsp:")) || file.startsWith(QLatin1String("sctp:")) || file.startsWith(QLatin1String("tcp:")) || file.startsWith(QLatin1String("tls:")) || file.startsWith(QLatin1String("udp:")) || file.startsWith(QLatin1String("gopher:")) )) { network = true; } } bool checkSeekable() { bool s = false; if (!format_ctx) return s; // io.seekable: byte seeking if (input) s |= input->isSeekable(); if (format_ctx->pb) s |= !!format_ctx->pb->seekable; // format.read_seek: time seeking. For example, seeking on hls stream steps: find segment, read packet in segment and drop until desired pts got s |= format_ctx->iformat->read_seek || format_ctx->iformat->read_seek2; return s; } // set wanted_xx_stream. call openCodecs() to read new stream frames // stream < 0 is choose best bool setStream(AVDemuxer::StreamType st, int streamValue); //called by loadFile(). if change to a new stream, call it(e.g. in AVPlayer) bool prepareStreams(); MediaStatus media_status; bool seekable; bool network; bool has_attached_pic; bool started; qreal max_pts; // max pts read bool eof; bool media_changed; mutable qptrdiff buf_pos; // detect eof for dynamic size (growing) stream even if detectDynamicStreamInterval() is not set Packet pkt; int stream; QList audio_streams, video_streams, subtitle_streams; AVFormatContext *format_ctx; //copy the info, not parse the file when constructed, then need member vars QString file; QString file_orig; AVInputFormat *input_format; QString format_forced; MediaIO *input; SeekUnit seek_unit; SeekType seek_type; AVDictionary *dict; QVariantHash options; typedef struct StreamInfo { StreamInfo() : stream(-1) , wanted_stream(-1) , index(-1) , wanted_index(-1) , avctx(0) {} // wanted_stream is REQUIRED. e.g. always set -1 to indicate the default stream, -2 to disable int stream, wanted_stream; // -1 default, selected by ff int index, wanted_index; // index in a kind of streams AVCodecContext *avctx; } StreamInfo; StreamInfo astream, vstream, sstream; AVDemuxer::InterruptHandler *interrupt_hanlder; QMutex mutex; //TODO: remove if load, read, seek is called in 1 thread }; AVDemuxer::AVDemuxer(QObject *parent) : QObject(parent) , d(new Private()) { // TODO: xxx_register_all already use static var class AVInitializer { public: AVInitializer() { avcodec_register_all(); #if QTAV_HAVE(AVDEVICE) avdevice_register_all(); #endif av_register_all(); avformat_network_init(); } ~AVInitializer() { avformat_network_deinit(); } }; static AVInitializer sAVInit; Q_UNUSED(sAVInit); d->interrupt_hanlder = new InterruptHandler(this); } AVDemuxer::~AVDemuxer() { unload(); } static void getFFmpegInputFormats(QStringList* formats, QStringList* extensions) { static QStringList exts; static QStringList fmts; if (exts.isEmpty() && fmts.isEmpty()) { av_register_all(); // MUST register all input/output formats AVInputFormat *i = NULL; QStringList e, f; while ((i = av_iformat_next(i))) { if (i->extensions) e << QString::fromLatin1(i->extensions).split(QLatin1Char(','), QString::SkipEmptyParts); if (i->name) f << QString::fromLatin1(i->name).split(QLatin1Char(','), QString::SkipEmptyParts); } foreach (const QString& v, e) { exts.append(v.trimmed()); } foreach (const QString& v, f) { fmts.append(v.trimmed()); } exts.removeDuplicates(); fmts.removeDuplicates(); } if (formats) *formats = fmts; if (extensions) *extensions = exts; } const QStringList& AVDemuxer::supportedFormats() { static QStringList fmts; if (fmts.isEmpty()) getFFmpegInputFormats(&fmts, NULL); return fmts; } const QStringList& AVDemuxer::supportedExtensions() { static QStringList exts; if (exts.isEmpty()) getFFmpegInputFormats(NULL, &exts); return exts; } const QStringList &AVDemuxer::supportedProtocols() { static QStringList protocols; if (!protocols.isEmpty()) return protocols; #if QTAV_HAVE(AVDEVICE) protocols << QStringLiteral("avdevice"); #endif av_register_all(); // MUST register all input/output formats void* opq = 0; const char* protocol = avio_enum_protocols(&opq, 0); while (protocol) { // static string, no deep copy needed. but QByteArray::fromRawData(data,size) assumes data is not null terminated and we must give a size protocols.append(QString::fromUtf8(protocol)); protocol = avio_enum_protocols(&opq, 0); } return protocols; } MediaStatus AVDemuxer::mediaStatus() const { return d->media_status; } bool AVDemuxer::readFrame() { QMutexLocker lock(&d->mutex); Q_UNUSED(lock); if (!d->format_ctx) return false; d->pkt = Packet(); // no lock required because in AVDemuxThread read and seek are in the same thread AVPacket packet; av_init_packet(&packet); d->interrupt_hanlder->begin(InterruptHandler::Read); int ret = av_read_frame(d->format_ctx, &packet); //0: ok, <0: error/end d->interrupt_hanlder->end(); // TODO: why return 0 if interrupted by user? if (ret < 0) { //end of file. FIXME: why no d->eof if replaying by seek(0)? // ffplay also check pb && pb->error and exit read thread if (ret == AVERROR_EOF || avio_feof(d->format_ctx->pb)) { if (!d->eof) { if (getInterruptStatus()) { AVError::ErrorCode ec(AVError::ReadError); QString msg(tr("error reading stream data")); handleError(ret, &ec, msg); } if (mediaStatus() != StalledMedia) { d->eof = true; #if 0 // EndOfMedia when demux thread finished d->started = false; setMediaStatus(EndOfMedia); Q_EMIT finished(); #endif qDebug("End of file. erreof=%d feof=%d", ret == AVERROR_EOF, avio_feof(d->format_ctx->pb)); } } av_packet_unref(&packet); //important! return false; } if (ret == AVERROR(EAGAIN)) { qWarning("demuxer EAGAIN :%s", av_err2str(ret)); av_packet_unref(&packet); //important! return false; } AVError::ErrorCode ec(AVError::ReadError); QString msg(tr("error reading stream data")); handleError(ret, &ec, msg); qWarning("[AVDemuxer] error: %s", av_err2str(ret)); av_packet_unref(&packet); //important! return false; } d->stream = packet.stream_index; //check whether the 1st frame is alreay got. emit only once if (!d->started) { d->started = true; Q_EMIT started(); } if (d->stream != videoStream() && d->stream != audioStream() && d->stream != subtitleStream()) { //qWarning("[AVDemuxer] unknown stream index: %d", stream); av_packet_unref(&packet); //important! return false; } // TODO: v4l2 copy d->pkt = Packet::fromAVPacket(&packet, av_q2d(d->format_ctx->streams[d->stream]->time_base)); av_packet_unref(&packet); //important! d->eof = false; if (d->pkt.pts > qreal(duration())/1000.0) { d->max_pts = d->pkt.pts; } return true; } Packet AVDemuxer::packet() const { return d->pkt; } int AVDemuxer::stream() const { return d->stream; } bool AVDemuxer::atEnd() const { if (!d->format_ctx) return false; if (d->format_ctx->pb) { AVIOContext *pb = d->format_ctx->pb; //qDebug("pb->error: %#x, eof: %d, pos: %lld, bufptr: %p", pb->error, pb->eof_reached, pb->pos, pb->buf_ptr); if (d->eof && (qptrdiff)pb->buf_ptr == d->buf_pos) return true; d->buf_pos = (qptrdiff)pb->buf_ptr; return false; } return d->eof; } bool AVDemuxer::isSeekable() const { return d->seekable; } void AVDemuxer::setSeekUnit(SeekUnit unit) { d->seek_unit = unit; } SeekUnit AVDemuxer::seekUnit() const { return d->seek_unit; } void AVDemuxer::setSeekType(SeekType target) { d->seek_type = target; } SeekType AVDemuxer::seekType() const { return d->seek_type; } //TODO: seek by byte bool AVDemuxer::seek(qint64 pos) { if (!isLoaded()) return false; //duration: unit is us (10^-6 s, AV_TIME_BASE) qint64 upos = pos*1000LL; // TODO: av_rescale if (upos > startTimeUs() + durationUs() || pos < 0LL) { if (pos >= 0LL && d->input && d->input->isSeekable() && d->input->isVariableSize()) { qDebug("Seek for variable size hack. %lld %.2f. valid range [%lld, %lld]", upos, double(upos)/double(durationUs()), startTimeUs(), startTimeUs()+durationUs()); } else if (d->max_pts > qreal(duration())/1000.0) { //FIXME qDebug("Seek (%lld) when video duration is growing %lld=>%lld", pos, duration(), qint64(d->max_pts*1000.0)); } else { qWarning("Invalid seek position %lld %.2f. valid range [%lld, %lld]", upos, double(upos)/double(durationUs()), startTimeUs(), startTimeUs()+durationUs()); return false; } } d->eof = false; // no lock required because in AVDemuxThread read and seek are in the same thread #if 0 //t: unit is s qreal t = q;// * (double)d->format_ctx->duration; // int ret = av_seek_frame(d->format_ctx, -1, (int64_t)(t*AV_TIME_BASE), t > d->pkt.pts ? 0 : AVSEEK_FLAG_BACKWARD); qDebug("[AVDemuxer] seek to %f %f %lld / %lld", q, d->pkt.pts, (int64_t)(t*AV_TIME_BASE), durationUs()); #else //TODO: d->pkt.pts may be 0, compute manually. bool backward = d->seek_type == AccurateSeek || upos <= (int64_t)(d->pkt.pts*AV_TIME_BASE); //qDebug("[AVDemuxer] seek to %f %f %lld / %lld backward=%d", double(upos)/double(durationUs()), d->pkt.pts, upos, durationUs(), backward); //AVSEEK_FLAG_BACKWARD has no effect? because we know the timestamp // FIXME: back flag is opposite? otherwise seek is bad and may crash? /* If stread->inputdex is (-1), a default * stream is selected, and timestamp is automatically converted * from AV_TIME_BASE units to the stream specific time_base. */ int seek_flag = (backward ? AVSEEK_FLAG_BACKWARD : 0); if (d->seek_type == AccurateSeek) { seek_flag = AVSEEK_FLAG_BACKWARD; } if (d->seek_type == AnyFrameSeek) { seek_flag |= AVSEEK_FLAG_ANY; } //qDebug("seek flag: %d", seek_flag); //bool seek_bytes = !!(d->format_ctx->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", d->format_ctx->iformat->name); int ret = av_seek_frame(d->format_ctx, -1, upos, seek_flag); //int ret = avformat_seek_file(d->format_ctx, -1, INT64_MIN, upos, upos, seek_flag); //avformat_seek_file() if (ret < 0 && (seek_flag & AVSEEK_FLAG_BACKWARD)) { // seek to 0? qDebug("av_seek_frame error with flag AVSEEK_FLAG_BACKWARD: %s. try to seek without the flag", av_err2str(ret)); seek_flag &= ~AVSEEK_FLAG_BACKWARD; ret = av_seek_frame(d->format_ctx, -1, upos, seek_flag); } //qDebug("av_seek_frame ret: %d", ret); #endif if (ret < 0) { AVError::ErrorCode ec(AVError::SeekError); QString msg(tr("seek error")); handleError(ret, &ec, msg); return false; } // TODO: replay if (upos <= startTime()) { qDebug("************seek to beginning. started = false"); d->started = false; //??? if (d->astream.avctx) d->astream.avctx->frame_number = 0; if (d->vstream.avctx) d->vstream.avctx->frame_number = 0; //TODO: why frame_number not changed after seek? if (d->sstream.avctx) d->sstream.avctx->frame_number = 0; } return true; } bool AVDemuxer::seek(qreal q) { if (duration() <= 0) { qWarning("duration() must be valid for percentage seek"); return false; } return seek(qint64(q*(double)duration())); } QString AVDemuxer::fileName() const { return d->file_orig; } QIODevice* AVDemuxer::ioDevice() const { if (!d->input) return 0; if (d->input->name() != QLatin1String("QIODevice")) return 0; return d->input->property("device").value(); } MediaIO* AVDemuxer::mediaIO() const { return d->input; } bool AVDemuxer::setMedia(const QString &fileName) { if (d->input) { delete d->input; d->input = 0; } d->file_orig = fileName; const QString url_old(d->file); d->file = fileName.trimmed(); if (d->file.startsWith(QLatin1String("mms:"))) d->file.insert(3, QLatin1Char('h')); else if (d->file.startsWith(QLatin1String(kFileScheme))) d->file = Internal::Path::toLocal(d->file); int colon = d->file.indexOf(QLatin1Char(':')); if (colon == 1) { #ifdef Q_OS_WINRT d->file.prepend(QStringLiteral("qfile:")); #endif } d->media_changed = url_old != d->file; if (d->media_changed) { d->format_forced.clear(); } // a local file. return here to avoid protocol checking. If path contains ":", protocol checking will fail if (d->file.startsWith(QLatin1Char('/'))) return d->media_changed; // use MediaIO to support protocols not supported by ffmpeg colon = d->file.indexOf(QLatin1Char(':')); if (colon >= 0) { #ifdef Q_OS_WIN if (colon == 1 && d->file.at(0).isLetter()) return d->media_changed; #endif const QString scheme = colon == 0 ? QStringLiteral("qrc") : d->file.left(colon); // supportedProtocols() is not complete. so try MediaIO 1st, if not found, fallback to libavformat d->input = MediaIO::createForProtocol(scheme); if (d->input) { d->input->setUrl(d->file); } } return d->media_changed; } bool AVDemuxer::setMedia(QIODevice* device) { d->file = QString(); d->file_orig = QString(); if (d->input) { if (d->input->name() != QLatin1String("QIODevice")) { delete d->input; d->input = 0; } } if (!d->input) d->input = MediaIO::create("QIODevice"); QIODevice* old_dev = d->input->property("device").value(); d->media_changed = old_dev != device; if (d->media_changed) { d->format_forced.clear(); } d->input->setProperty("device", QVariant::fromValue(device)); //open outside? return d->media_changed; } bool AVDemuxer::setMedia(MediaIO *in) { d->media_changed = in != d->input; if (d->media_changed) { d->format_forced.clear(); } d->file = QString(); d->file_orig = QString(); if (!d->input) d->input = in; if (d->input != in) { delete d->input; d->input = in; } return d->media_changed; } void AVDemuxer::setFormat(const QString &fmt) { d->format_forced = fmt; } QString AVDemuxer::formatForced() const { return d->format_forced; } bool AVDemuxer::load() { unload(); qDebug("all closed and reseted"); if (d->file.isEmpty() && !d->input) { setMediaStatus(NoMedia); return false; } QMutexLocker lock(&d->mutex); // TODO: load in AVDemuxThread and remove all locks Q_UNUSED(lock); setMediaStatus(LoadingMedia); d->checkNetwork(); #if QTAV_HAVE(AVDEVICE) static const QString avd_scheme(QStringLiteral("avdevice:")); if (d->file.startsWith(avd_scheme)) { QStringList parts = d->file.split(QStringLiteral(":")); int s0 = avd_scheme.size(); const int s1 = d->file.indexOf(QChar(':'), s0); if (s1 < 0) { qDebug("invalid avdevice specification"); setMediaStatus(InvalidMedia); return false; } if (d->file.at(s0) == QChar('/') && d->file.at(s0+1) == QChar('/')) { // avdevice://avfoundation:device_name s0 += 2; } // else avdevice:video4linux2:file_name d->input_format = av_find_input_format(d->file.mid(s0, s1-s0).toUtf8().constData()); d->file = d->file.mid(s1+1); } #endif //alloc av format context if (!d->format_ctx) d->format_ctx = avformat_alloc_context(); d->format_ctx->flags |= AVFMT_FLAG_GENPTS; //install interrupt callback d->format_ctx->interrupt_callback = *d->interrupt_hanlder; d->applyOptionsForDict(); // check special dict keys // d->format_forced can be set from AVFormatContext.format_whitelist if (!d->format_forced.isEmpty()) { d->input_format = av_find_input_format(d->format_forced.toUtf8().constData()); qDebug() << "force format: " << d->format_forced; } int ret = 0; // used dict entries will be removed in avformat_open_input d->interrupt_hanlder->begin(InterruptHandler::Open); if (d->input) { if (d->input->accessMode() == MediaIO::Write) { qWarning("wrong MediaIO accessMode. MUST be Read"); } d->format_ctx->pb = (AVIOContext*)d->input->avioContext(); d->format_ctx->flags |= AVFMT_FLAG_CUSTOM_IO; qDebug("avformat_open_input: d->format_ctx:'%p'..., MediaIO('%s'): %p", d->format_ctx, d->input->name().toUtf8().constData(), d->input); ret = avformat_open_input(&d->format_ctx, "MediaIO", d->input_format, d->options.isEmpty() ? NULL : &d->dict); qDebug("avformat_open_input: (with MediaIO) ret:%d", ret); } else { qDebug("avformat_open_input: d->format_ctx:'%p', url:'%s'...",d->format_ctx, qPrintable(d->file)); ret = avformat_open_input(&d->format_ctx, d->file.toUtf8().constData(), d->input_format, d->options.isEmpty() ? NULL : &d->dict); qDebug("avformat_open_input: url:'%s' ret:%d",qPrintable(d->file), ret); } d->interrupt_hanlder->end(); if (ret < 0) { // d->format_ctx is 0 AVError::ErrorCode ec = AVError::OpenError; QString msg = tr("failed to open media"); handleError(ret, &ec, msg); qWarning() << "Can't open media: " << msg; if (mediaStatus() == LoadingMedia) //workaround for timeout but not interrupted setMediaStatus(InvalidMedia); Q_EMIT unloaded(); //context not ready. so will not emit in unload() return false; } //deprecated //if(av_find_stread->inputfo(d->format_ctx)<0) { //TODO: avformat_find_stread->inputfo is too slow, only useful for some video format d->interrupt_hanlder->begin(InterruptHandler::FindStreamInfo); ret = avformat_find_stream_info(d->format_ctx, NULL); d->interrupt_hanlder->end(); if (ret < 0) { setMediaStatus(InvalidMedia); AVError::ErrorCode ec(AVError::ParseStreamError); QString msg(tr("failed to find stream info")); handleError(ret, &ec, msg); qWarning() << "Can't find stream info: " << msg; // context is ready. unloaded() will be emitted in unload() if (mediaStatus() == LoadingMedia) //workaround for timeout but not interrupted setMediaStatus(InvalidMedia); return false; } if (!d->prepareStreams()) { if (mediaStatus() == LoadingMedia) setMediaStatus(InvalidMedia); return false; } d->started = false; setMediaStatus(LoadedMedia); Q_EMIT loaded(); const bool was_seekable = d->seekable; d->seekable = d->checkSeekable(); if (was_seekable != d->seekable) Q_EMIT seekableChanged(); qDebug("avfmtctx.flags: %d, iformat.flags", d->format_ctx->flags, d->format_ctx->iformat->flags); if (getInterruptStatus() < 0) { QString msg; qDebug("AVERROR_EXIT: %d", AVERROR_EXIT); handleError(AVERROR_EXIT, 0, msg); qWarning() << "User interupted: " << msg; return false; } return true; } bool AVDemuxer::unload() { QMutexLocker lock(&d->mutex); Q_UNUSED(lock); /* if (d->seekable) { d->seekable = false; // Q_EMIT seekableChanged(); } */ d->network = false; d->has_attached_pic = false; d->eof = false; // true and set false in load()? d->buf_pos = 0; d->started = false; d->max_pts = 0.0; d->resetStreams(); d->interrupt_hanlder->setStatus(0); //av_close_input_file(d->format_ctx); //deprecated if (d->format_ctx) { qDebug("closing d->format_ctx"); avformat_close_input(&d->format_ctx); //libavf > 53.10.0 d->format_ctx = 0; d->input_format = 0; // no delete. may be used in next load if (d->input) d->input->release(); Q_EMIT unloaded(); } return true; } bool AVDemuxer::isLoaded() const { return d->format_ctx && (d->astream.avctx || d->vstream.avctx || d->sstream.avctx); } bool AVDemuxer::hasAttacedPicture() const { return d->has_attached_pic; } bool AVDemuxer::setStreamIndex(StreamType st, int index) { QList *streams = 0; Private::StreamInfo *si = 0; if (st == AudioStream) { // TODO: use a struct si = &d->astream; streams = &d->audio_streams; } else if (st == VideoStream) { si = &d->vstream; streams = &d->video_streams; } else if (st == SubtitleStream) { si = &d->sstream; streams = &d->subtitle_streams; } if (!si) { qWarning("stream type %d for index %d not found", st, index); return false; } if (index >= streams->size()) {// || index < 0) { //TODO: disable if <0 //si->wanted_stream = -1; qWarning("invalid index %d (valid is 0~%d) for stream type %d.", index, streams->size(), st); return false; } if (index < 0) { qDebug("disable %d stream", st); si->stream = -1; si->wanted_index = -1; si->wanted_stream = -1; return true; } if (!d->setStream(st, streams->at(index))) return false; si->wanted_index = index; return true; } AVFormatContext* AVDemuxer::formatContext() { return d->format_ctx; } QString AVDemuxer::formatName() const { if (!d->format_ctx) return QString(); return QLatin1String(d->format_ctx->iformat->name); } QString AVDemuxer::formatLongName() const { if (!d->format_ctx) return QString(); return QLatin1String(d->format_ctx->iformat->long_name); } // convert to s using AV_TIME_BASE then *1000? qint64 AVDemuxer::startTime() const { return startTimeUs()/1000LL; //TODO: av_rescale } qint64 AVDemuxer::duration() const { return durationUs()/1000LL; //time base: AV_TIME_BASE TODO: av_rescale } //AVFrameContext use AV_TIME_BASE as time base. AVStream use their own timebase qint64 AVDemuxer::startTimeUs() const { // start time may be not null for network stream if (!d->format_ctx || d->format_ctx->start_time == AV_NOPTS_VALUE) return 0; return d->format_ctx->start_time; } qint64 AVDemuxer::durationUs() const { if (!d->format_ctx || d->format_ctx->duration == AV_NOPTS_VALUE) return 0; return d->format_ctx->duration; //time base: AV_TIME_BASE } int AVDemuxer::bitRate() const { return d->format_ctx->bit_rate; } qreal AVDemuxer::frameRate() const { if (videoStream() < 0) return 0; AVStream *stream = d->format_ctx->streams[videoStream()]; return av_q2d(stream->avg_frame_rate); //codecCtx->time_base.den / codecCtx->time_base.num } qint64 AVDemuxer::frames(int stream) const { if (stream == -1) { stream = videoStream(); if (stream < 0) stream = audioStream(); if (stream < 0) return 0; } return d->format_ctx->streams[stream]->nb_frames; } int AVDemuxer::currentStream(StreamType st) const { if (st == AudioStream) return audioStream(); else if (st == VideoStream) return videoStream(); else if (st == SubtitleStream) return subtitleStream(); return -1; } QList AVDemuxer::streams(StreamType st) const { if (st == AudioStream) return audioStreams(); else if (st == VideoStream) return videoStreams(); else if (st == SubtitleStream) return subtitleStreams(); return QList(); } int AVDemuxer::audioStream() const { return d->astream.stream; } QList AVDemuxer::audioStreams() const { return d->audio_streams; } int AVDemuxer::videoStream() const { return d->vstream.stream; } QList AVDemuxer::videoStreams() const { return d->video_streams; } int AVDemuxer::subtitleStream() const { return d->sstream.stream; } QList AVDemuxer::subtitleStreams() const { return d->subtitle_streams; } AVCodecContext* AVDemuxer::audioCodecContext(int stream) const { if (stream < 0) return d->astream.avctx; if (stream > (int)d->format_ctx->nb_streams) return 0; AVCodecContext *avctx = d->format_ctx->streams[stream]->codec; if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) return avctx; return 0; } AVCodecContext* AVDemuxer::videoCodecContext(int stream) const { if (stream < 0) return d->vstream.avctx; if (stream > (int)d->format_ctx->nb_streams) return 0; AVCodecContext *avctx = d->format_ctx->streams[stream]->codec; if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) return avctx; return 0; } AVCodecContext* AVDemuxer::subtitleCodecContext(int stream) const { if (stream < 0) return d->sstream.avctx; if (stream > (int)d->format_ctx->nb_streams) return 0; AVCodecContext *avctx = d->format_ctx->streams[stream]->codec; if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) return avctx; return 0; } /** * @brief getInterruptTimeout return the interrupt timeout * @return */ qint64 AVDemuxer::getInterruptTimeout() const { return d->interrupt_hanlder->getTimeout(); } /** * @brief setInterruptTimeout set the interrupt timeout * @param timeout * @return */ void AVDemuxer::setInterruptTimeout(qint64 timeout) { d->interrupt_hanlder->setTimeout(timeout); } bool AVDemuxer::isInterruptOnTimeout() const { return d->interrupt_hanlder->isInterruptOnTimeout(); } void AVDemuxer::setInterruptOnTimeout(bool value) { d->interrupt_hanlder->setInterruptOnTimeout(value); } int AVDemuxer::getInterruptStatus() const { return d->interrupt_hanlder->getStatus(); } void AVDemuxer::setInterruptStatus(int interrupt) { d->interrupt_hanlder->setStatus(interrupt); } void AVDemuxer::setOptions(const QVariantHash &dict) { d->options = dict; d->applyOptionsForContext(); // apply even if avformat context is open } QVariantHash AVDemuxer::options() const { return d->options; } void AVDemuxer::setMediaStatus(MediaStatus status) { if (d->media_status == status) return; //if (status == NoMedia || status == InvalidMedia) // Q_EMIT durationChanged(0); d->media_status = status; Q_EMIT mediaStatusChanged(d->media_status); } void AVDemuxer::Private::applyOptionsForDict() { if (dict) { av_dict_free(&dict); dict = 0; //aready 0 in av_free } if (options.isEmpty()) return; QVariant opt(options); if (options.contains(QStringLiteral("avformat"))) opt = options.value(QStringLiteral("avformat")); Internal::setOptionsToDict(opt, &dict); if (opt.type() == QVariant::Map) { QVariantMap avformat_dict(opt.toMap()); if (avformat_dict.contains(QStringLiteral("format_whitelist"))) { const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString()); if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty()) format_forced = fmts; // reset when media changed } } else if (opt.type() == QVariant::Hash) { QVariantHash avformat_dict(opt.toHash()); if (avformat_dict.contains(QStringLiteral("format_whitelist"))) { const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString()); if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty()) format_forced = fmts; // reset when media changed } } } void AVDemuxer::Private::applyOptionsForContext() { if (!format_ctx) return; if (options.isEmpty()) { //av_opt_set_defaults(format_ctx); //can't set default values! result maybe unexpected return; } QVariant opt(options); if (options.contains(QStringLiteral("avformat"))) opt = options.value(QStringLiteral("avformat")); Internal::setOptionsToFFmpegObj(opt, format_ctx); } void AVDemuxer::handleError(int averr, AVError::ErrorCode *errorCode, QString &msg) { if (averr >= 0) return; // d->format_ctx is 0 // TODO: why sometimes AVERROR_EXIT does not work? bool interrupted = (averr == AVERROR_EXIT) || getInterruptStatus(); QString err_msg(msg); if (interrupted) { // interrupted by callback, so can not determine whether the media is valid // insufficient buffering or other interruptions if (getInterruptStatus() < 0) { setMediaStatus(StalledMedia); Q_EMIT userInterrupted(); err_msg += QStringLiteral(" [%1]").arg(tr("interrupted by user")); } else { // FIXME: if not interupt on timeout and ffmpeg exits, still LoadingMedia if (isInterruptOnTimeout()) setMediaStatus(StalledMedia); // averr is eof for open timeout err_msg += QStringLiteral(" [%1]").arg(tr("timeout")); } } else { if (mediaStatus() == LoadingMedia) setMediaStatus(InvalidMedia); } msg = err_msg; if (!errorCode) return; AVError::ErrorCode ec(*errorCode); if (averr == AVERROR_INVALIDDATA) { // leave it if reading if (*errorCode == AVError::OpenError) ec = AVError::FormatError; } else { // Input/output error etc. if (d->network) ec = AVError::NetworkError; } AVError err(ec, err_msg, averr); Q_EMIT error(err); *errorCode = ec; } bool AVDemuxer::Private::setStream(AVDemuxer::StreamType st, int streamValue) { if (streamValue < -1) streamValue = -1; QList *streams = 0; Private::StreamInfo *si = 0; if (st == AudioStream) { // TODO: use a struct si = &astream; streams = &audio_streams; } else if (st == VideoStream) { si = &vstream; streams = &video_streams; } else if (st == SubtitleStream) { si = &sstream; streams = &subtitle_streams; } if (!si /*|| si->wanted_stream == streamValue*/) { //init -2 qWarning("stream type %d not found", st); return false; } //if (!streams->contains(si->stream)) { // qWarning("%d is not a valid stream for stream type %d", si->stream, st); //return false; //} bool index_valid = si->wanted_index >= 0 && si->wanted_index < streams->size(); int s = AVERROR_STREAM_NOT_FOUND; if (streamValue >= 0 || !index_valid) { // or simply set s to streamValue if value is contained in streams? s = av_find_best_stream(format_ctx , st == AudioStream ? AVMEDIA_TYPE_AUDIO : st == VideoStream ? AVMEDIA_TYPE_VIDEO : st == SubtitleStream ? AVMEDIA_TYPE_SUBTITLE : AVMEDIA_TYPE_UNKNOWN , streamValue, -1, NULL, 0); // streamValue -1 is ok } else { //index_valid s = streams->at(si->wanted_index); } if (s == AVERROR_STREAM_NOT_FOUND) return false; // don't touch wanted index si->stream = s; si->wanted_stream = streamValue; si->avctx = format_ctx->streams[s]->codec; has_attached_pic = !!(format_ctx->streams[s]->disposition & AV_DISPOSITION_ATTACHED_PIC); return true; } bool AVDemuxer::Private::prepareStreams() { has_attached_pic = false; resetStreams(); if (!format_ctx) return false; AVMediaType type = AVMEDIA_TYPE_UNKNOWN; for (unsigned int i = 0; i < format_ctx->nb_streams; ++i) { type = format_ctx->streams[i]->codec->codec_type; if (type == AVMEDIA_TYPE_VIDEO) { video_streams.push_back(i); } else if (type == AVMEDIA_TYPE_AUDIO) { audio_streams.push_back(i); } else if (type == AVMEDIA_TYPE_SUBTITLE) { subtitle_streams.push_back(i); } } if (audio_streams.isEmpty() && video_streams.isEmpty() && subtitle_streams.isEmpty()) return false; setStream(AVDemuxer::AudioStream, -1); setStream(AVDemuxer::VideoStream, -1); setStream(AVDemuxer::SubtitleStream, -1); return true; } } //namespace QtAV QtAV-1.12.0/src/AVError.cpp000066400000000000000000000142001312235004300152070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AVError.h" #include "QtAV/private/AVCompat.h" #ifndef QT_NO_DEBUG_STREAM #include #endif namespace QtAV { namespace { class RegisterMetaTypes { public: RegisterMetaTypes() { qRegisterMetaType("QtAV::AVError"); } } _registerMetaTypes; } static AVError::ErrorCode errorFromFFmpeg(int fe) { typedef struct { int ff; AVError::ErrorCode e; } err_entry; static const err_entry err_map[] = { { AVERROR_BSF_NOT_FOUND, AVError::FormatError }, #ifdef AVERROR_BUFFER_TOO_SMALL { AVERROR_BUFFER_TOO_SMALL, AVError::ResourceError }, #endif //AVERROR_BUFFER_TOO_SMALL { AVERROR_DECODER_NOT_FOUND, AVError::CodecError }, { AVERROR_ENCODER_NOT_FOUND, AVError::CodecError }, { AVERROR_DEMUXER_NOT_FOUND, AVError::FormatError }, { AVERROR_MUXER_NOT_FOUND, AVError::FormatError }, { AVERROR_PROTOCOL_NOT_FOUND, AVError::ResourceError }, { AVERROR_STREAM_NOT_FOUND, AVError::ResourceError }, { 0, AVError::UnknowError } }; for (int i = 0; err_map[i].ff; ++i) { if (err_map[i].ff == fe) return err_map[i].e; } return AVError::UnknowError; } /* * correct wrong AVError to a right category by ffmpeg error code * does nothing if ffmpeg error code is 0 */ static void correct_error_by_ffmpeg(AVError::ErrorCode *e, int fe) { if (!fe || !e) return; const AVError::ErrorCode ec = errorFromFFmpeg(fe); if (*e > ec) *e = ec; } AVError::AVError() : mError(NoError) , mFFmpegError(0) { } AVError::AVError(ErrorCode code, int ffmpegError) : mError(code) , mFFmpegError(ffmpegError) { correct_error_by_ffmpeg(&mError, mFFmpegError); } AVError::AVError(ErrorCode code, const QString &detail, int ffmpegError) : mError(code) , mFFmpegError(ffmpegError) , mDetail(detail) { correct_error_by_ffmpeg(&mError, mFFmpegError); } AVError::AVError(const AVError& other) : mError(other.mError) , mFFmpegError(other.mFFmpegError) , mDetail(other.mDetail) { } AVError& AVError::operator=(const AVError& other) { mError = other.mError; mFFmpegError = other.mFFmpegError; return *this; } bool AVError::operator==(const AVError& other) const { return (mError == other.mError && mFFmpegError == other.mFFmpegError); } void AVError::setError(ErrorCode ec) { mError = ec; } AVError::ErrorCode AVError::error() const { return mError; } QString AVError::string() const { QString errStr(mDetail); if (errStr.isEmpty()) { switch (mError) { case NoError: errStr = QObject::tr("No error"); break; case OpenError: errStr = QObject::tr("Open error"); break; case OpenTimedout: errStr = QObject::tr("Open timed out"); break; case ParseStreamTimedOut: errStr = QObject::tr("Parse stream timed out"); break; case ParseStreamError: errStr = QObject::tr("Parse stream error"); break; case StreamNotFound: errStr = QObject::tr("Stream not found"); break; case ReadTimedout: errStr = QObject::tr("Read packet timed out"); break; case ReadError: errStr = QObject::tr("Read error"); break; case SeekError: errStr = QObject::tr("Seek error"); break; case ResourceError: errStr = QObject::tr("Resource error"); break; case OpenCodecError: errStr = QObject::tr("Open codec error"); break; case CloseCodecError: errStr = QObject::tr("Close codec error"); break; case VideoCodecNotFound: errStr = QObject::tr("Video codec not found"); break; case AudioCodecNotFound: errStr = QObject::tr("Audio codec not found"); break; case SubtitleCodecNotFound: errStr = QObject::tr("Subtitle codec not found"); break; case CodecError: errStr = QObject::tr("Codec error"); break; case FormatError: errStr = QObject::tr("Format error"); break; case NetworkError: errStr = QObject::tr("Network error"); break; case AccessDenied: errStr = QObject::tr("Access denied"); break; default: errStr = QObject::tr("Unknow error"); break; } } if (mFFmpegError != 0) { errStr += QStringLiteral("\n(FFmpeg %1: %2)").arg(mFFmpegError, 0, 16).arg(ffmpegErrorString()); } return errStr; } int AVError::ffmpegErrorCode() const { return mFFmpegError; } QString AVError::ffmpegErrorString() const { if (mFFmpegError == 0) return QString(); return QString::fromUtf8(av_err2str(mFFmpegError)); } } //namespace QtAV #ifndef QT_NO_DEBUG_STREAM //class QDebug; QDebug operator<<(QDebug debug, const QtAV::AVError &error) { debug << error.string(); return debug; } #endif QtAV-1.12.0/src/AVMuxer.cpp000066400000000000000000000405761312235004300152350ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AVMuxer.h" #include "QtAV/private/AVCompat.h" #include "QtAV/MediaIO.h" #include "QtAV/VideoEncoder.h" #include "QtAV/AudioEncoder.h" #include "utils/internal.h" #include "utils/Logger.h" namespace QtAV { static const char kFileScheme[] = "file:"; #define CHAR_COUNT(s) (sizeof(s) - 1) // tail '\0' // Packet::asAVPacket() assumes time base is 0.001 static const AVRational kTB = {1, 1000}; class AVMuxer::Private { public: Private() : seekable(false) , network(false) , started(false) , eof(false) , media_changed(true) , format_ctx(0) , format(0) , io(0) , dict(0) , aenc(0) , venc(0) { av_register_all(); } ~Private() { //delete interrupt_hanlder; if (dict) { av_dict_free(&dict); dict = 0; } if (io) { delete io; io = 0; } } AVStream* addStream(AVFormatContext* ctx, const QString& codecName, AVCodecID codecId); bool prepareStreams(); void applyOptionsForDict(); void applyOptionsForContext(); bool seekable; bool network; bool started; bool eof; bool media_changed; AVFormatContext *format_ctx; //copy the info, not parse the file when constructed, then need member vars QString file; QString file_orig; AVOutputFormat *format; QString format_forced; MediaIO *io; AVDictionary *dict; QVariantHash options; QList audio_streams, video_streams, subtitle_streams; AudioEncoder *aenc; // not owner VideoEncoder *venc; // not owner }; AVStream *AVMuxer::Private::addStream(AVFormatContext* ctx, const QString &codecName, AVCodecID codecId) { AVCodec *codec = NULL; if (!codecName.isEmpty()) { codec = avcodec_find_encoder_by_name(codecName.toUtf8().constData()); if (!codec) { const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(codecName.toUtf8().constData()); if (cd) { codec = avcodec_find_encoder(cd->id); } } if (!codec) qWarning("Can not find encoder for %s", codecName.toUtf8().constData()); } else if (codecId != QTAV_CODEC_ID(NONE)) { codec = avcodec_find_encoder(codecId); if (!codec) qWarning("Can not find encoder for %s", avcodec_get_name(codecId)); } if (!codec) return 0; AVStream *s = avformat_new_stream(ctx, codec); if (!s) { qWarning("Can not allocate stream"); return 0; } // set by avformat if unset s->id = ctx->nb_streams - 1; s->time_base = kTB; AVCodecContext *c = s->codec; c->codec_id = codec->id; // Using codec->time_base is deprecated, but needed for older lavf. c->time_base = s->time_base; /* Some formats want stream headers to be separate. */ if (ctx->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; // expose avctx to encoder and set properties in encoder? // list codecs for a given format in ui return s; } bool AVMuxer::Private::prepareStreams() { audio_streams.clear(); video_streams.clear(); subtitle_streams.clear(); AVOutputFormat* fmt = format_ctx->oformat; if (venc) { AVStream *s = addStream(format_ctx, venc->codecName(), fmt->video_codec); if (s) { AVCodecContext *c = s->codec; c->bit_rate = venc->bitRate(); c->width = venc->width(); c->height = venc->height(); /// MUST set after encoder is open to ensure format is valid and the same c->pix_fmt = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(venc->pixelFormat()); video_streams.push_back(s->id); } } if (aenc) { AVStream *s = addStream(format_ctx, aenc->codecName(), fmt->audio_codec); if (s) { AVCodecContext *c = s->codec; c->bit_rate = aenc->bitRate(); /// MUST set after encoder is open to ensure format is valid and the same c->sample_rate = aenc->audioFormat().sampleRate(); c->sample_fmt = (AVSampleFormat)aenc->audioFormat().sampleFormatFFmpeg(); c->channel_layout = aenc->audioFormat().channelLayoutFFmpeg(); c->channels = aenc->audioFormat().channels(); c->bits_per_raw_sample = aenc->audioFormat().bytesPerSample()*8; // need?? audio_streams.push_back(s->id); } } return !(audio_streams.isEmpty() && video_streams.isEmpty() && subtitle_streams.isEmpty()); } static void getFFmpegOutputFormats(QStringList* formats, QStringList* extensions) { static QStringList exts; static QStringList fmts; if (exts.isEmpty() && fmts.isEmpty()) { av_register_all(); // MUST register all input/output formats AVOutputFormat *o = NULL; QStringList e, f; while ((o = av_oformat_next(o))) { if (o->extensions) e << QString::fromLatin1(o->extensions).split(QLatin1Char(','), QString::SkipEmptyParts); if (o->name) f << QString::fromLatin1(o->name).split(QLatin1Char(','), QString::SkipEmptyParts); } foreach (const QString& v, e) { exts.append(v.trimmed()); } foreach (const QString& v, f) { fmts.append(v.trimmed()); } exts.removeDuplicates(); fmts.removeDuplicates(); } if (formats) *formats = fmts; if (extensions) *extensions = exts; } const QStringList& AVMuxer::supportedFormats() { static QStringList fmts; if (fmts.isEmpty()) getFFmpegOutputFormats(&fmts, NULL); return fmts; } const QStringList& AVMuxer::supportedExtensions() { static QStringList exts; if (exts.isEmpty()) getFFmpegOutputFormats(NULL, &exts); return exts; } // TODO: move to QtAV::supportedFormats(bool out). custom protols? const QStringList &AVMuxer::supportedProtocols() { static bool called = false; static QStringList protocols; if (called) return protocols; called = true; if (!protocols.isEmpty()) return protocols; #if QTAV_HAVE(AVDEVICE) protocols << QStringLiteral("avdevice"); #endif av_register_all(); // MUST register all input/output formats void* opq = 0; const char* protocol = avio_enum_protocols(&opq, 1); while (protocol) { // static string, no deep copy needed. but QByteArray::fromRawData(data,size) assumes data is not null terminated and we must give a size protocols.append(QString::fromUtf8(protocol)); protocol = avio_enum_protocols(&opq, 1); } return protocols; } AVMuxer::AVMuxer(QObject *parent) : QObject(parent) , d(new Private()) { } AVMuxer::~AVMuxer() { close(); } QString AVMuxer::fileName() const { return d->file_orig; } QIODevice* AVMuxer::ioDevice() const { if (!d->io) return 0; if (d->io->name() != QLatin1String("QIODevice")) return 0; return d->io->property("device").value(); } MediaIO* AVMuxer::mediaIO() const { return d->io; } bool AVMuxer::setMedia(const QString &fileName) { if (d->io) { delete d->io; d->io = 0; } d->file_orig = fileName; const QString url_old(d->file); d->file = fileName.trimmed(); if (d->file.startsWith(QLatin1String("mms:"))) d->file.insert(3, QLatin1Char('h')); else if (d->file.startsWith(QLatin1String(kFileScheme))) d->file = Internal::Path::toLocal(d->file); int colon = d->file.indexOf(QLatin1Char(':')); if (colon == 1) { #ifdef Q_OS_WINRT d->file.prepend(QStringLiteral("qfile:")); #endif } d->media_changed = url_old != d->file; if (d->media_changed) { d->format_forced.clear(); } // a local file. return here to avoid protocol checking. If path contains ":", protocol checking will fail if (d->file.startsWith(QLatin1Char('/'))) return d->media_changed; // use MediaIO to support protocols not supported by ffmpeg colon = d->file.indexOf(QLatin1Char(':')); if (colon >= 0) { #ifdef Q_OS_WIN if (colon == 1 && d->file.at(0).isLetter()) return d->media_changed; #endif const QString scheme = colon == 0 ? QStringLiteral("qrc") : d->file.left(colon); // supportedProtocols() is not complete. so try MediaIO 1st, if not found, fallback to libavformat d->io = MediaIO::createForProtocol(scheme); if (d->io) { d->io->setUrl(d->file); } } return d->media_changed; } bool AVMuxer::setMedia(QIODevice* device) { d->file = QString(); d->file_orig = QString(); if (d->io) { if (d->io->name() != QLatin1String("QIODevice")) { delete d->io; d->io = 0; } } if (!d->io) d->io = MediaIO::create("QIODevice"); QIODevice* old_dev = d->io->property("device").value(); d->media_changed = old_dev != device; if (d->media_changed) { d->format_forced.clear(); } d->io->setProperty("device", QVariant::fromValue(device)); //open outside? return d->media_changed; } bool AVMuxer::setMedia(MediaIO *in) { d->media_changed = in != d->io; if (d->media_changed) { d->format_forced.clear(); } d->file = QString(); d->file_orig = QString(); if (!d->io) d->io = in; if (d->io != in) { delete d->io; d->io = in; } return d->media_changed; } void AVMuxer::setFormat(const QString &fmt) { d->format_forced = fmt; } QString AVMuxer::formatForced() const { return d->format_forced; } bool AVMuxer::open() { // avformatcontext will be allocated in avformat_alloc_output_context2() //d->format_ctx->interrupt_callback = *d->interrupt_hanlder; d->applyOptionsForDict(); // check special dict keys // d->format_forced can be set from AVFormatContext.format_whitelist if (!d->format_forced.isEmpty()) { d->format = av_guess_format(d->format_forced.toUtf8().constData(), NULL, NULL); qDebug() << "force format: " << d->format_forced; } //d->interrupt_hanlder->begin(InterruptHandler::Open); if (d->io) { if (d->io->accessMode() == MediaIO::Read) { qWarning("wrong MediaIO accessMode. MUST be Write"); } AV_ENSURE_OK(avformat_alloc_output_context2(&d->format_ctx, d->format, d->format_forced.isEmpty() ? 0 : d->format_forced.toUtf8().constData(), ""), false); d->format_ctx->pb = (AVIOContext*)d->io->avioContext(); d->format_ctx->flags |= AVFMT_FLAG_CUSTOM_IO; //d->format_ctx->flags |= AVFMT_FLAG_GENPTS; } else { AV_ENSURE_OK(avformat_alloc_output_context2(&d->format_ctx, d->format, d->format_forced.isEmpty() ? 0 : d->format_forced.toUtf8().constData(), fileName().toUtf8().constData()), false); } //d->interrupt_hanlder->end(); if (!d->prepareStreams()) { return false; } // TODO: AVFMT_NOFILE ? examples/muxing.c only check AVFMT_NOFILE // a custome io does not need avio_open. it open resource in it's own way, e.g. QIODevice.open if (!(d->format_ctx->oformat->flags & AVFMT_NOFILE) && !(d->format_ctx->flags & AVFMT_FLAG_CUSTOM_IO)) { // avio_open2? AV_ENSURE_OK(avio_open(&d->format_ctx->pb, fileName().toUtf8().constData(), AVIO_FLAG_WRITE), false); } // d->format_ctx->start_time_realtime AV_ENSURE_OK(avformat_write_header(d->format_ctx, &d->dict), false); d->started = false; return true; } bool AVMuxer::close() { if (!isOpen()) return true; av_write_trailer(d->format_ctx); // close AVCodecContext* in encoder // custom io will call avio_close in ~MediaIO() if (!(d->format_ctx->oformat->flags & AVFMT_NOFILE) && !(d->format_ctx->flags & AVFMT_FLAG_CUSTOM_IO)) { if (d->format_ctx->pb) { avio_flush(d->format_ctx->pb); avio_close(d->format_ctx->pb); d->format_ctx->pb = 0; } } avformat_free_context(d->format_ctx); d->format_ctx = 0; d->audio_streams.clear(); d->video_streams.clear(); d->subtitle_streams.clear(); d->started = false; return true; } bool AVMuxer::isOpen() const { return d->format_ctx; } bool AVMuxer::writeAudio(const QtAV::Packet& packet) { AVPacket *pkt = (AVPacket*)packet.asAVPacket(); //FIXME pkt->stream_index = d->audio_streams[0]; //FIXME AVStream *s = d->format_ctx->streams[pkt->stream_index]; // stream.time_base is set in avformat_write_header av_packet_rescale_ts(pkt, kTB, s->time_base); av_interleaved_write_frame(d->format_ctx, pkt); d->started = true; return true; } bool AVMuxer::writeVideo(const QtAV::Packet& packet) { AVPacket *pkt = (AVPacket*)packet.asAVPacket(); pkt->stream_index = d->video_streams[0]; AVStream *s = d->format_ctx->streams[pkt->stream_index]; // stream.time_base is set in avformat_write_header av_packet_rescale_ts(pkt, kTB, s->time_base); //av_write_frame av_interleaved_write_frame(d->format_ctx, pkt); #if 0 qDebug("mux packet.pts: %.3f dts:%.3f duration: %.3f, avpkt.pts: %lld,dts:%lld,duration:%lld" , packet.pts, packet.dts, packet.duration , pkt->pts, pkt->dts, pkt->duration); qDebug("stream: %d duration: %lld, end: %lld. tb:{%d/%d}" , pkt->stream_index, s->duration , av_stream_get_end_pts(s) , s->time_base.num, s->time_base.den ); #endif d->started = true; return true; } void AVMuxer::copyProperties(VideoEncoder *enc) { d->venc = enc; } void AVMuxer::copyProperties(AudioEncoder *enc) { d->aenc = enc; } void AVMuxer::setOptions(const QVariantHash &dict) { d->options = dict; d->applyOptionsForContext(); // apply even if avformat context is open } QVariantHash AVMuxer::options() const { return d->options; } void AVMuxer::Private::applyOptionsForDict() { if (dict) { av_dict_free(&dict); dict = 0; //aready 0 in av_free } if (options.isEmpty()) return; QVariant opt(options); if (options.contains(QStringLiteral("avformat"))) opt = options.value(QStringLiteral("avformat")); Internal::setOptionsToDict(opt, &dict); if (opt.type() == QVariant::Map) { QVariantMap avformat_dict(opt.toMap()); if (avformat_dict.contains(QStringLiteral("format_whitelist"))) { const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString()); if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty()) format_forced = fmts; // reset when media changed } } else if (opt.type() == QVariant::Hash) { QVariantHash avformat_dict(opt.toHash()); if (avformat_dict.contains(QStringLiteral("format_whitelist"))) { const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString()); if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty()) format_forced = fmts; // reset when media changed } } } void AVMuxer::Private::applyOptionsForContext() { if (!format_ctx) return; if (options.isEmpty()) { //av_opt_set_defaults(format_ctx); //can't set default values! result maybe unexpected return; } QVariant opt(options); if (options.contains(QStringLiteral("avformat"))) opt = options.value(QStringLiteral("avformat")); Internal::setOptionsToFFmpegObj(opt, format_ctx); } } //namespace QtAV QtAV-1.12.0/src/AVPlayer.cpp000066400000000000000000001355241312235004300153670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "AVPlayerPrivate.h" #include #include #include #include #include #include #include #include "QtAV/AVDemuxer.h" #include "QtAV/Packet.h" #include "QtAV/AudioDecoder.h" #include "QtAV/MediaIO.h" #include "QtAV/VideoRenderer.h" #include "QtAV/AVClock.h" #include "QtAV/VideoCapture.h" #include "QtAV/VideoCapture.h" #include "filter/FilterManager.h" #include "output/OutputSet.h" #include "AudioThread.h" #include "VideoThread.h" #include "AVDemuxThread.h" #include "QtAV/private/AVCompat.h" #include "utils/internal.h" #include "utils/Logger.h" #define EOF_ISSUE_SOLVED 0 namespace QtAV { namespace { static const struct RegisterMetaTypes { inline RegisterMetaTypes() { qRegisterMetaType(); // required by invoke() parameters } } _registerMetaTypes; } //namespace static const qint64 kSeekMS = 10000; Q_GLOBAL_STATIC(QThreadPool, loaderThreadPool) /// Supported input protocols. A static string list const QStringList& AVPlayer::supportedProtocols() { return AVDemuxer::supportedProtocols(); } AVPlayer::AVPlayer(QObject *parent) : QObject(parent) , d(new Private()) { d->vos = new OutputSet(this); d->aos = new OutputSet(this); connect(this, SIGNAL(started()), this, SLOT(onStarted())); /* * call stop() before the window(d->vo) closed to stop the waitcondition * If close the d->vo widget, the the d->vo may destroy before waking up. */ connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuitApp())); //d->clock->setClockType(AVClock::ExternalClock); connect(&d->demuxer, SIGNAL(started()), masterClock(), SLOT(start())); connect(&d->demuxer, SIGNAL(error(QtAV::AVError)), this, SIGNAL(error(QtAV::AVError))); connect(&d->demuxer, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), this, SLOT(updateMediaStatus(QtAV::MediaStatus)), Qt::DirectConnection); connect(&d->demuxer, SIGNAL(loaded()), this, SIGNAL(loaded())); connect(&d->demuxer, SIGNAL(seekableChanged()), this, SIGNAL(seekableChanged())); d->read_thread = new AVDemuxThread(this); d->read_thread->setDemuxer(&d->demuxer); //direct connection can not sure slot order? connect(d->read_thread, SIGNAL(finished()), this, SLOT(stopFromDemuxerThread()), Qt::DirectConnection); connect(d->read_thread, SIGNAL(requestClockPause(bool)), masterClock(), SLOT(pause(bool)), Qt::DirectConnection); connect(d->read_thread, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), this, SLOT(updateMediaStatus(QtAV::MediaStatus))); connect(d->read_thread, SIGNAL(bufferProgressChanged(qreal)), this, SIGNAL(bufferProgressChanged(qreal))); connect(d->read_thread, SIGNAL(seekFinished(qint64)), this, SLOT(onSeekFinished(qint64)), Qt::DirectConnection); connect(d->read_thread, SIGNAL(internalSubtitlePacketRead(int, QtAV::Packet)), this, SIGNAL(internalSubtitlePacketRead(int, QtAV::Packet)), Qt::DirectConnection); d->vcapture = new VideoCapture(this); } AVPlayer::~AVPlayer() { stop(); QMutexLocker lock(&d->load_mutex); Q_UNUSED(lock); // if not uninstall here, player's qobject children filters will call uninstallFilter too late that player is almost be destroyed QList filters(FilterManager::instance().videoFilters(this)); foreach (Filter* f, filters) { uninstallFilter(reinterpret_cast(f)); } filters = FilterManager::instance().audioFilters(this); foreach (Filter* f, filters) { uninstallFilter(reinterpret_cast(f)); } } AVClock* AVPlayer::masterClock() { return d->clock; } void AVPlayer::addVideoRenderer(VideoRenderer *renderer) { if (!renderer) { qWarning("add a null renderer!"); return; } renderer->setStatistics(&d->statistics); d->vos->addOutput(renderer); } void AVPlayer::removeVideoRenderer(VideoRenderer *renderer) { d->vos->removeOutput(renderer); } void AVPlayer::clearVideoRenderers() { d->vos->clearOutputs(); } void AVPlayer::setRenderer(VideoRenderer *r) { VideoRenderer *vo = renderer(); if (vo && r) { VideoRenderer::OutAspectRatioMode oar = vo->outAspectRatioMode(); //r->resizeRenderer(vo->rendererSize()); r->setOutAspectRatioMode(oar); if (oar == VideoRenderer::CustomAspectRation) { r->setOutAspectRatio(vo->outAspectRatio()); } } clearVideoRenderers(); if (!r) return; r->resizeRenderer(r->rendererSize()); //IMPORTANT: the swscaler will resize r->setStatistics(&d->statistics); addVideoRenderer(r); } VideoRenderer *AVPlayer::renderer() { //QList assert empty in debug mode if (!d->vos || d->vos->outputs().isEmpty()) return 0; return static_cast(d->vos->outputs().last()); } QList AVPlayer::videoOutputs() { if (!d->vos) return QList(); QList vos; vos.reserve(d->vos->outputs().size()); foreach (AVOutput *out, d->vos->outputs()) { vos.append(static_cast(out)); } return vos; } AudioOutput* AVPlayer::audio() { return d->ao; } void AVPlayer::setSpeed(qreal speed) { if (speed == d->speed) return; setFrameRate(0); // will set clock to default d->speed = speed; //TODO: check clock type? if (d->ao && d->ao->isAvailable()) { qDebug("set speed %.2f", d->speed); d->ao->setSpeed(d->speed); } masterClock()->setSpeed(d->speed); Q_EMIT speedChanged(d->speed); } qreal AVPlayer::speed() const { return d->speed; } void AVPlayer::setInterruptTimeout(qint64 ms) { if (ms < 0LL) ms = -1LL; if (d->interrupt_timeout == ms) return; d->interrupt_timeout = ms; Q_EMIT interruptTimeoutChanged(); d->demuxer.setInterruptTimeout(ms); } qint64 AVPlayer::interruptTimeout() const { return d->interrupt_timeout; } void AVPlayer::setInterruptOnTimeout(bool value) { if (isInterruptOnTimeout() == value) return; d->demuxer.setInterruptOnTimeout(value); Q_EMIT interruptOnTimeoutChanged(); } bool AVPlayer::isInterruptOnTimeout() const { return d->demuxer.isInterruptOnTimeout(); } void AVPlayer::setFrameRate(qreal value) { d->force_fps = value; // clock set here will be reset in playInternal() // also we can't change user's setting of ClockType and autoClock here if force frame rate is disabled. if (!isPlaying()) return; d->applyFrameRate(); } qreal AVPlayer::forcedFrameRate() const { return d->force_fps; } const Statistics& AVPlayer::statistics() const { return d->statistics; } bool AVPlayer::installFilter(AudioFilter *filter, int index) { if (!FilterManager::instance().registerAudioFilter((Filter*)filter, this, index)) return false; if (!d->athread) return false; //install later when avthread created return d->athread->installFilter((Filter*)filter, index); } bool AVPlayer::installFilter(VideoFilter *filter, int index) { if (!FilterManager::instance().registerVideoFilter((Filter*)filter, this, index)) return false; if (!d->vthread) return false; //install later when avthread created return d->vthread->installFilter((Filter*)filter, index); } bool AVPlayer::uninstallFilter(AudioFilter *filter) { FilterManager::instance().unregisterAudioFilter(filter, this); AVThread *avthread = d->athread; if (!avthread) return false; if (!avthread->filters().contains(filter)) return false; return avthread->uninstallFilter(filter, true); } bool AVPlayer::uninstallFilter(VideoFilter *filter) { FilterManager::instance().unregisterVideoFilter(filter, this); AVThread *avthread = d->vthread; if (!avthread) return false; if (!avthread->filters().contains(filter)) return false; return avthread->uninstallFilter(filter, true); } QList AVPlayer::audioFilters() const { return FilterManager::instance().audioFilters((AVPlayer*)this); } QList AVPlayer::videoFilters() const { return FilterManager::instance().videoFilters((AVPlayer*)this); } void AVPlayer::setPriority(const QVector &ids) { d->vc_ids = ids; if (!isPlaying()) return; // TODO: add an option to apply immediatly? if (!d->vthread || !d->vthread->isRunning()) { qint64 pos = position(); d->setupVideoThread(this); if (d->vdec) { d->vthread->start(); setPosition(pos); } return; } #ifndef ASYNC_DECODER_OPEN class ChangeDecoderTask : public QRunnable { AVPlayer* player; public: ChangeDecoderTask(AVPlayer *p) : player(p) {} void run() Q_DECL_OVERRIDE { player->d->tryApplyDecoderPriority(player); } }; d->vthread->scheduleTask(new ChangeDecoderTask(this)); #else // maybe better experience class NewDecoderTask : public QRunnable { AVPlayer *player; public: NewDecoderTask(AVPlayer *p) : player(p) {} void run() Q_DECL_OVERRIDE { VideoDecoder *vd = NULL; AVCodecContext *avctx = player->d->demuxer.videoCodecContext(); foreach(VideoDecoderId vid, player->d->vc_ids) { qDebug("**********trying video decoder: %s...", VideoDecoderFactory::name(vid).c_str()); vd = VideoDecoder::create(vid); if (!vd) continue; vd->setCodecContext(avctx); // It's fine because AVDecoder copy the avctx properties vd->setOptions(player->d->vc_opt); if (vd->open()) { qDebug("**************Video decoder found:%p", vd); break; } delete vd; vd = 0; } if (!vd) { Q_EMIT player->error(AVError(AVError::VideoCodecNotFound)); return; } if (vd->id() == player->d->vdec->id()) { qDebug("Video decoder does not change"); delete vd; return; } class ApplyNewDecoderTask : public QRunnable { AVPlayer *player; VideoDecoder *dec; public: ApplyNewDecoderTask(AVPlayer *p, VideoDecoder *d) : player(p), dec(d) {} void run() Q_DECL_OVERRIDE { qint64 pos = player->position(); VideoThread *vthread = player->d->vthread; vthread->packetQueue()->clear(); vthread->setDecoder(dec); // MUST delete decoder after video thread set the decoder to ensure the deleted vdec will not be used in vthread! if (player->d->vdec) delete player->d->vdec; player->d->vdec = dec; QObject::connect(player->d->vdec, SIGNAL(error(QtAV::AVError)), player, SIGNAL(error(QtAV::AVError))); player->d->initVideoStatistics(player->d->demuxer.videoStream()); // If no seek, drop packets until a key frame packet is found. But we may drop too many packets, and also a/v sync is a problem. player->setPosition(pos); } }; player->d->vthread->scheduleTask(new ApplyNewDecoderTask(player, vd)); } }; QThreadPool::globalInstance()->start(new NewDecoderTask(this)); #endif } template static QVector idsFromNames(const QStringList& names) { QVector decs; if (!names.isEmpty()) { decs.reserve(names.size()); foreach (const QString& name, names) { if (name.isEmpty()) continue; ID id = T::id(name.toLatin1().constData()); if (id == 0) continue; decs.append(id); } } return decs; } void AVPlayer::setVideoDecoderPriority(const QStringList &names) { setPriority(idsFromNames(names)); } template static QStringList idsToNames(QVector ids) { QStringList decs; if (!ids.isEmpty()) { decs.reserve(ids.size()); foreach (ID id, ids) { decs.append(QString::fromLatin1(T::name(id))); } } return decs; } QStringList AVPlayer::videoDecoderPriority() const { return idsToNames(d->vc_ids); } void AVPlayer::setOptionsForFormat(const QVariantHash &dict) { d->demuxer.setOptions(dict); } QVariantHash AVPlayer::optionsForFormat() const { return d->demuxer.options(); } void AVPlayer::setOptionsForAudioCodec(const QVariantHash &dict) { d->ac_opt = dict; } QVariantHash AVPlayer::optionsForAudioCodec() const { return d->ac_opt; } void AVPlayer::setOptionsForVideoCodec(const QVariantHash &dict) { d->vc_opt = dict; const QVariant p(dict.contains(QStringLiteral("priority"))); if (p.type() == QVariant::StringList) { setVideoDecoderPriority(p.toStringList()); d->vc_opt.remove(QStringLiteral("priority")); } } QVariantHash AVPlayer::optionsForVideoCodec() const { return d->vc_opt; } void AVPlayer::setMediaEndAction(MediaEndAction value) { if (d->end_action == value) return; d->end_action = value; Q_EMIT mediaEndActionChanged(value); d->read_thread->setMediaEndAction(value); } MediaEndAction AVPlayer::mediaEndAction() const { return d->end_action; } /* * loaded state is the state of current setted file. * For replaying, we can avoid load a seekable file again. * For playing a new file, load() is required. */ void AVPlayer::setFile(const QString &path) { // file() is used somewhere else. ensure it is correct QString p(path); // QFile does not support "file:" if (p.startsWith(QLatin1String("file:"))) p = Internal::Path::toLocal(p); d->reset_state = d->current_source.type() != QVariant::String || d->current_source.toString() != p; d->current_source = p; // TODO: d->reset_state = d->demuxer2.setMedia(path); if (d->reset_state) { d->audio_track = d->video_track = d->subtitle_track = 0; Q_EMIT sourceChanged(); //Q_EMIT error(AVError(AVError::NoError)); } // TODO: use absoluteFilePath? d->loaded = false; // } QString AVPlayer::file() const { if (d->current_source.type() == QVariant::String) return d->current_source.toString(); return QString(); } void AVPlayer::setIODevice(QIODevice* device) { // TODO: d->reset_state = d->demuxer2.setMedia(device); if (d->current_source.type() == QVariant::String) { d->reset_state = true; } else { if (d->current_source.canConvert()) { d->reset_state = d->current_source.value() != device; } else { // MediaIO d->reset_state = true; } } d->loaded = false; d->current_source = QVariant::fromValue(device); if (d->reset_state) { d->audio_track = d->video_track = d->subtitle_track = 0; Q_EMIT sourceChanged(); } } void AVPlayer::setInput(MediaIO *in) { // TODO: d->reset_state = d->demuxer2.setMedia(in); if (d->current_source.type() == QVariant::String) { d->reset_state = true; } else { if (d->current_source.canConvert()) { d->reset_state = true; } else { // MediaIO d->reset_state = d->current_source.value() != in; } } d->loaded = false; d->current_source = QVariant::fromValue(in); if (d->reset_state) { d->audio_track = d->video_track = d->subtitle_track = 0; Q_EMIT sourceChanged(); } } MediaIO* AVPlayer::input() const { if (d->current_source.type() == QVariant::String) return 0; if (!d->current_source.canConvert()) return 0; return d->current_source.value(); } VideoCapture* AVPlayer::videoCapture() const { return d->vcapture; } void AVPlayer::play(const QString& path) { setFile(path); play(); } bool AVPlayer::isPlaying() const { return (d->read_thread &&d->read_thread->isRunning()) || (d->athread && d->athread->isRunning()) || (d->vthread && d->vthread->isRunning()); } void AVPlayer::togglePause() { pause(!isPaused()); } void AVPlayer::pause(bool p) { if (!isPlaying()) return; if (isPaused() == p) return; audio()->pause(p); //pause thread. check pause state? d->read_thread->pause(p); if (d->athread) d->athread->pause(p); if (d->vthread) d->vthread->pause(p); d->clock->pause(p); d->state = p ? PausedState : PlayingState; Q_EMIT stateChanged(d->state); Q_EMIT paused(p); } bool AVPlayer::isPaused() const { return (d->read_thread && d->read_thread->isPaused()) || (d->athread && d->athread->isPaused()) || (d->vthread && d->vthread->isPaused()); } MediaStatus AVPlayer::mediaStatus() const { return d->status; } void AVPlayer::setAutoLoad(bool value) { if (d->auto_load == value) return; d->auto_load = value; Q_EMIT autoLoadChanged(); } bool AVPlayer::isAutoLoad() const { return d->auto_load; } void AVPlayer::setAsyncLoad(bool value) { if (d->async_load == value) return; d->async_load = value; Q_EMIT asyncLoadChanged(); } bool AVPlayer::isAsyncLoad() const { return d->async_load; } bool AVPlayer::isLoaded() const { return d->loaded; } void AVPlayer::loadInternal() { QMutexLocker lock(&d->load_mutex); Q_UNUSED(lock); // release codec ctx //close decoders here to make sure open and close in the same thread if not async load if (isLoaded()) { if (d->adec) d->adec->setCodecContext(0); if (d->vdec) d->vdec->setCodecContext(0); } qDebug() << "Loading " << d->current_source << " ..."; if (d->current_source.type() == QVariant::String) { d->demuxer.setMedia(d->current_source.toString()); } else { if (d->current_source.canConvert()) { d->demuxer.setMedia(d->current_source.value()); } else { // MediaIO d->demuxer.setMedia(d->current_source.value()); } } d->loaded = d->demuxer.load(); d->status = d->demuxer.mediaStatus(); if (!d->loaded) { d->statistics.reset(); qWarning("Load failed!"); d->audio_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::AudioStream); Q_EMIT internalAudioTracksChanged(d->audio_tracks); d->video_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::VideoStream); Q_EMIT internalVideoTracksChanged(d->video_tracks); d->subtitle_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::SubtitleStream); Q_EMIT internalSubtitleTracksChanged(d->subtitle_tracks); return; } d->subtitle_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::SubtitleStream); Q_EMIT internalSubtitleTracksChanged(d->subtitle_tracks); d->applySubtitleStream(d->subtitle_track, this); d->audio_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::AudioStream); Q_EMIT internalAudioTracksChanged(d->audio_tracks); d->video_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::VideoStream); Q_EMIT internalVideoTracksChanged(d->video_tracks); Q_EMIT durationChanged(duration()); // setup parameters from loaded media d->media_start_pts = d->demuxer.startTime(); // TODO: what about other proctols? some vob duration() == 0 if (duration() > 0) d->media_end = mediaStartPosition() + duration(); else d->media_end = kInvalidPosition; d->start_position_norm = normalizedPosition(d->start_position); d->stop_position_norm = normalizedPosition(d->stop_position); int interval = qAbs(d->notify_interval); d->initStatistics(); if (interval != qAbs(d->notify_interval)) Q_EMIT notifyIntervalChanged(); } void AVPlayer::unload() { if (!isLoaded()) return; QMutexLocker lock(&d->load_mutex); Q_UNUSED(lock); d->loaded = false; d->demuxer.setInterruptStatus(-1); if (d->adec) { // FIXME: crash if audio external=>internal then replay d->adec->setCodecContext(0); delete d->adec; d->adec = 0; } if (d->vdec) { d->vdec->setCodecContext(0); delete d->vdec; d->vdec = 0; } d->demuxer.unload(); Q_EMIT durationChanged(0LL); // for ui, slider is invalid. use stopped instead, and remove this signal here? // ?? d->audio_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::AudioStream); Q_EMIT internalAudioTracksChanged(d->audio_tracks); d->video_tracks = d->getTracksInfo(&d->demuxer, AVDemuxer::VideoStream); Q_EMIT internalVideoTracksChanged(d->video_tracks); } void AVPlayer::setRelativeTimeMode(bool value) { if (d->relative_time_mode == value) return; d->relative_time_mode = value; Q_EMIT relativeTimeModeChanged(); } bool AVPlayer::relativeTimeMode() const { return d->relative_time_mode; } qint64 AVPlayer::absoluteMediaStartPosition() const { return d->media_start_pts; } qreal AVPlayer::durationF() const { return double(d->demuxer.durationUs())/double(AV_TIME_BASE); //AVFrameContext.duration time base: AV_TIME_BASE } qint64 AVPlayer::duration() const { return d->demuxer.duration(); } qint64 AVPlayer::mediaStartPosition() const { if (relativeTimeMode()) return 0; return d->demuxer.startTime(); } qint64 AVPlayer::mediaStopPosition() const { if (d->media_end == kInvalidPosition && duration() > 0) { // called in stop() return mediaStartPosition() + duration(); } return d->media_end; } qreal AVPlayer::mediaStartPositionF() const { if (relativeTimeMode()) return 0; return double(d->demuxer.startTimeUs())/double(AV_TIME_BASE); } qint64 AVPlayer::normalizedPosition(qint64 pos) { if (!isLoaded()) return pos; qint64 p0 = mediaStartPosition(); qint64 p1 = mediaStopPosition(); if (relativeTimeMode()) { p0 = 0; if (p1 != kInvalidPosition) p1 -= p0; //duration } if (pos < 0) { if (p1 == kInvalidPosition) pos = kInvalidPosition; else pos += p1; } return qMax(qMin(pos, p1), p0); } qint64 AVPlayer::startPosition() const { return d->start_position; } void AVPlayer::setStartPosition(qint64 pos) { d->start_position = pos; d->start_position_norm = normalizedPosition(pos); Q_EMIT startPositionChanged(d->start_position); } qint64 AVPlayer::stopPosition() const { return d->stop_position; } void AVPlayer::setStopPosition(qint64 pos) { d->stop_position = pos; d->stop_position_norm = normalizedPosition(pos); Q_EMIT stopPositionChanged(d->stop_position); } void AVPlayer::setTimeRange(qint64 start, qint64 stop) { if (start > stop) { qWarning("Invalid time range"); return; } setStopPosition(stop); setStartPosition(start); } bool AVPlayer::isSeekable() const { return d->demuxer.isSeekable(); } qint64 AVPlayer::position() const { // TODO: videoTime()? const qint64 pts = d->clock->value()*1000.0; if (relativeTimeMode()) return pts - absoluteMediaStartPosition(); return pts; } void AVPlayer::setPosition(qint64 position) { // FIXME: strange things happen if seek out of eof if (position > d->stop_position_norm) return; if (!isPlaying()) return; qint64 pos_pts = position; if (pos_pts < 0) pos_pts = 0; // position passed in is relative to the start pts in relative time mode if (relativeTimeMode()) pos_pts += absoluteMediaStartPosition(); d->seeking = true; masterClock()->updateValue(double(pos_pts)/1000.0); //what is duration == 0 masterClock()->updateExternalClock(pos_pts); //in msec. ignore usec part using t/1000 d->read_thread->seek(pos_pts, seekType()); Q_EMIT positionChanged(position); //emit relative position } int AVPlayer::repeat() const { return d->repeat_max; } int AVPlayer::currentRepeat() const { return d->repeat_current; } // TODO: reset current_repeat? void AVPlayer::setRepeat(int max) { d->repeat_max = max; if (d->repeat_max < 0) d->repeat_max = std::numeric_limits::max(); Q_EMIT repeatChanged(d->repeat_max); } bool AVPlayer::setExternalAudio(const QString &file) { // TODO: update statistics int stream = currentAudioStream(); if (!isLoaded() && stream < 0) stream = 0; return setAudioStream(file, stream); } QString AVPlayer::externalAudio() const { return d->external_audio; } const QVariantList& AVPlayer::externalAudioTracks() const { return d->external_audio_tracks; } const QVariantList &AVPlayer::internalAudioTracks() const { return d->audio_tracks; } const QVariantList &AVPlayer::internalVideoTracks() const { return d->video_tracks; } bool AVPlayer::setAudioStream(const QString &file, int n) { QString path(file); // QFile does not support "file:" if (path.startsWith(QLatin1String("file:"))) path = Internal::Path::toLocal(path); if (d->audio_track == n && d->external_audio == path) return true; const bool audio_changed = d->audio_demuxer.fileName() != path; if (path.isEmpty()) { if (isLoaded()) { if (n >= d->demuxer.audioStreams().size()) { qWarning("Invalid audio stream number %d/%d", n, d->demuxer.audioStreams().size()-1); return false; } } if (audio_changed) { d->external_audio_tracks = QVariantList(); Q_EMIT externalAudioTracksChanged(d->external_audio_tracks); } } else { if (!audio_changed && d->audio_demuxer.isLoaded()) { if (n >= d->audio_demuxer.audioStreams().size()) { qWarning("Invalid external audio stream number %d/%d", n, d->audio_demuxer.audioStreams().size()-1); return false; } } } d->audio_track = n; d->external_audio = path; d->audio_demuxer.setMedia(d->external_audio); struct scoped_pause { scoped_pause() : was_paused(false), player(0) {} void set(bool old, AVPlayer* p) { was_paused = old; player = p; if (player) player->pause(true); } ~scoped_pause() { if (player && !was_paused) { player->pause(false); } } bool was_paused; AVPlayer* player; }; scoped_pause sp; if (!isPlaying()) { qDebug("set audio track when not playing"); goto update_demuxer; } // pause demuxer, clear queues, set demuxer stream, set decoder, set ao, resume sp.set(isPaused(), this); //before read_thread->pause(true, true) if (!d->external_audio.isEmpty()) d->read_thread->pause(true, true); // wait to safe set ademuxer update_demuxer: if (!d->external_audio.isEmpty()) { if (audio_changed || !d->audio_demuxer.isLoaded()) { if (!d->audio_demuxer.load()) { qWarning("Failed to load audio track %d@%s", d->audio_track, d->external_audio.toUtf8().constData()); d->external_audio_tracks = QVariantList(); Q_EMIT externalAudioTracksChanged(d->external_audio_tracks); return false; } d->external_audio_tracks = d->getTracksInfo(&d->audio_demuxer, AVDemuxer::AudioStream); Q_EMIT externalAudioTracksChanged(d->external_audio_tracks); d->read_thread->setAudioDemuxer(&d->audio_demuxer); } } if (!isPlaying()) { if (d->external_audio.isEmpty()) { if (audio_changed) { d->read_thread->setAudioDemuxer(0); d->audio_demuxer.unload(); } } return true; } if (!d->setupAudioThread(this)) { // adec will be deleted. so audio_demuxer must unload later stop(); return false; } if (d->external_audio.isEmpty()) { if (audio_changed) { d->read_thread->setAudioDemuxer(0); d->audio_demuxer.unload(); } } else { d->audio_demuxer.seek(position()); } return true; } bool AVPlayer::setAudioStream(int n) { return setAudioStream(externalAudio(), n); } bool AVPlayer::setVideoStream(int n) { if (n < 0) return false; if (d->video_track == n) return true; if (isLoaded()) { if (n >= d->demuxer.videoStreams().size()) return false; } d->video_track = n; d->demuxer.setStreamIndex(AVDemuxer::VideoStream, n); // if (!isPlaying()) // return true; // // pause demuxer, clear queues, set demuxer stream, set decoder, set ao, resume // bool p = isPaused(); // //int bv = bufferValue(); // setBufferMode(BufferTime); // pause(true); // if (!d->setupVideoThread(this)) { // stop(); // return false; // } // if (!p) pause(false); // //QTimer::singleShot(10000, this, SLOT(setBufferValue(bv))); return true; } const QVariantList& AVPlayer::internalSubtitleTracks() const { return d->subtitle_tracks; } bool AVPlayer::setSubtitleStream(int n) { if (d->subtitle_track == n) return true; d->subtitle_track = n; Q_EMIT subtitleStreamChanged(n); if (!d->demuxer.isLoaded()) return true; return d->applySubtitleStream(n, this); } int AVPlayer::currentAudioStream() const { return d->demuxer.audioStreams().indexOf(d->demuxer.audioStream()); } int AVPlayer::currentVideoStream() const { return d->demuxer.videoStreams().indexOf(d->demuxer.videoStream()); } int AVPlayer::currentSubtitleStream() const { return d->demuxer.subtitleStreams().indexOf(d->demuxer.subtitleStream()); } int AVPlayer::audioStreamCount() const { return d->demuxer.audioStreams().size(); } int AVPlayer::videoStreamCount() const { return d->demuxer.videoStreams().size(); } int AVPlayer::subtitleStreamCount() const { return d->demuxer.subtitleStreams().size(); } AVPlayer::State AVPlayer::state() const { return d->state; } void AVPlayer::setState(State value) { if (d->state == value) return; if (value == StoppedState) { stop(); return; } if (value == PausedState) { pause(true); return; } // value == PlayingState if (d->state == StoppedState) { play(); return; } if (d->state == PausedState) { pause(false); return; } } bool AVPlayer::load() { if (!d->current_source.isValid()) { qDebug("Invalid media source. No file or IODevice was set."); return false; } if (!d->checkSourceChange() && (mediaStatus() == QtAV::LoadingMedia || mediaStatus() == LoadedMedia)) return true; if (isLoaded()) { // release codec ctx. if not loaded, they are released by avformat. TODO: always let avformat release them? if (d->adec) d->adec->setCodecContext(0); if (d->vdec) d->vdec->setCodecContext(0); } d->loaded = false; d->status = LoadingMedia; if (!isAsyncLoad()) { loadInternal(); return d->loaded; } class LoadWorker : public QRunnable { public: LoadWorker(AVPlayer *player) : m_player(player) {} virtual void run() { if (!m_player) return; m_player->loadInternal(); } private: AVPlayer* m_player; }; // TODO: thread pool has a max thread limit loaderThreadPool()->start(new LoadWorker(this)); return true; } void AVPlayer::play() { //FIXME: bad delay after play from here if (isPlaying()) { qDebug("play() when playing"); if (!d->checkSourceChange()) return; stop(); } if (!load()) { qWarning("load error"); return; } if (isLoaded()) { // !asyncLoad() is here because load() returned true playInternal(); return; } connect(this, SIGNAL(loaded()), this, SLOT(playInternal())); } void AVPlayer::playInternal() { { QMutexLocker lock(&d->load_mutex); Q_UNUSED(lock); if (!d->demuxer.isLoaded()) return; d->start_position_norm = normalizedPosition(d->start_position); d->stop_position_norm = normalizedPosition(d->stop_position); // FIXME: if call play() frequently playInternal may not be called if disconnect here disconnect(this, SIGNAL(loaded()), this, SLOT(playInternal())); if (!d->setupAudioThread(this)) { d->read_thread->setAudioThread(0); //set 0 before delete. ptr is used in demux thread when set 0 if (d->athread) { qDebug("release audio thread."); delete d->athread; d->athread = 0;//shared ptr? } } if (!d->setupVideoThread(this)) { d->read_thread->setVideoThread(0); //set 0 before delete. ptr is used in demux thread when set 0 if (d->vthread) { qDebug("release video thread."); delete d->vthread; d->vthread = 0;//shared ptr? } } if (!d->athread && !d->vthread) { d->loaded = false; qWarning("load failed"); return; } // setup clock before avthread.start() becuase avthreads use clock. after avthreads setup because of ao check masterClock()->reset(); // TODO: add isVideo() or hasVideo()? if (masterClock()->isClockAuto()) { qDebug("auto select clock: audio > external"); if (!d->demuxer.audioCodecContext() || !d->ao || !d->ao->isOpen() || !d->athread) { masterClock()->setClockType(AVClock::ExternalClock); qDebug("No audio found or audio not supported. Using ExternalClock."); } else { qDebug("Using AudioClock"); masterClock()->setClockType(AVClock::AudioClock); } } masterClock()->setInitialValue((double)absoluteMediaStartPosition()/1000.0); // from previous play() if (d->demuxer.audioCodecContext() && d->athread) { qDebug("Starting audio thread..."); d->athread->start(); } if (d->demuxer.videoCodecContext() && d->vthread) { qDebug("Starting video thread..."); d->vthread->start(); } d->read_thread->setMediaEndAction(mediaEndAction()); d->read_thread->start(); if (d->demuxer.audioCodecContext() && d->athread) d->athread->waitForStarted(); if (d->demuxer.videoCodecContext() && d->vthread) d->vthread->waitForStarted(); /// demux thread not started, seek tasks will be cleared d->read_thread->waitForStarted(); if (d->timer_id < 0) { //d->timer_id = startNotifyTimer(); //may fail if not in this thread QMetaObject::invokeMethod(this, "startNotifyTimer", Qt::AutoConnection); } d->state = PlayingState; if (d->repeat_current < 0) d->repeat_current = 0; } //end lock scoped here to avoid dead lock if connect started() to a slot that call unload()/play() if (d->start_position_norm > 0) { if (relativeTimeMode()) setPosition(qint64((d->start_position_norm + absoluteMediaStartPosition()))); else setPosition((qint64)(d->start_position_norm)); } Q_EMIT stateChanged(PlayingState); Q_EMIT started(); //we called stop(), so must emit started() } void AVPlayer::stopFromDemuxerThread() { qDebug("demuxer thread emit finished. repeat: %d/%d", currentRepeat(), repeat()); d->seeking = false; if (currentRepeat() < 0 || (currentRepeat() >= repeat() && repeat() >= 0)) { qreal stop_pts = masterClock()->videoTime(); if (stop_pts <= 0) stop_pts = masterClock()->value(); masterClock()->reset(); QMetaObject::invokeMethod(this, "stopNotifyTimer"); // vars not set by user can be reset d->repeat_current = -1; d->start_position_norm = 0; d->stop_position_norm = kInvalidPosition; // already stopped. so not 0 but invalid. 0 can stop the playback in timerEvent d->media_end = kInvalidPosition; qDebug("avplayer emit stopped()"); d->state = StoppedState; QMetaObject::invokeMethod(this, "stateChanged", Q_ARG(QtAV::AVPlayer::State, d->state)); QMetaObject::invokeMethod(this, "stopped"); QMetaObject::invokeMethod(this, "stoppedAt", Q_ARG(qint64, qint64(stop_pts*1000.0))); //Q_EMIT stateChanged(d->state); //Q_EMIT stopped(); //Q_EMIT stoppedAt(stop_pts*1000.0); /* * currently preload is not supported. so always unload. Then some properties will be reset, e.g. duration() */ unload(); //TODO: invoke? } else { d->repeat_current++; QMetaObject::invokeMethod(this, "play"); //ensure play() is called from player thread } } void AVPlayer::aboutToQuitApp() { d->reset_state = true; stop(); while (isPlaying()) { qApp->processEvents(); qDebug("about to quit....."); pause(false); // may be paused. then aboutToQuitApp will not finish stop(); } d->demuxer.setInterruptStatus(-1); loaderThreadPool()->waitForDone(); } void AVPlayer::setNotifyInterval(int msec) { if (d->notify_interval == msec) return; if (d->notify_interval < 0 && msec <= 0) return; const int old = qAbs(d->notify_interval); d->notify_interval = msec; d->updateNotifyInterval(); Q_EMIT notifyIntervalChanged(); if (d->timer_id < 0) return; if (old != qAbs(d->notify_interval)) { stopNotifyTimer(); startNotifyTimer(); } } int AVPlayer::notifyInterval() const { return qAbs(d->notify_interval); } void AVPlayer::startNotifyTimer() { d->timer_id = startTimer(qAbs(d->notify_interval)); } void AVPlayer::stopNotifyTimer() { if (d->timer_id < 0) return; killTimer(d->timer_id); d->timer_id = -1; } void AVPlayer::onStarted() { if (d->speed != 1.0) { //TODO: check clock type? if (d->ao && d->ao->isAvailable()) { d->ao->setSpeed(d->speed); } masterClock()->setSpeed(d->speed); } else { d->applyFrameRate(); } } void AVPlayer::updateMediaStatus(QtAV::MediaStatus status) { if (status == d->status) return; d->status = status; Q_EMIT mediaStatusChanged(d->status); } void AVPlayer::onSeekFinished(qint64 value) { d->seeking = false; Q_EMIT seekFinished(value); //d->clock->updateValue(value/1000.0); if (relativeTimeMode()) Q_EMIT positionChanged(value - absoluteMediaStartPosition()); else Q_EMIT positionChanged(value); } void AVPlayer::tryClearVideoRenderers() { if (!d->vthread) { qWarning("internal error"); return; } if (!(mediaEndAction() & MediaEndAction_KeepDisplay)) { d->vthread->clearRenderers(); } } void AVPlayer::stop() { // check d->timer_id, <0 return? if (d->reset_state) { /* * must kill in main thread! If called from user, may be not in main thread. * then timer goes on, and player find that d->stop_position reached(d->stop_position is already * 0 after user call stop), * stop() is called again by player and reset state. but this call is later than demuxer stop. * so if user call play() immediatly, may be stopped by AVPlayer */ // TODO: invokeMethod "stopNotifyTimer" if (d->timer_id >= 0) { qDebug("timer: %d, current thread: %p, player thread: %p", d->timer_id, QThread::currentThread(), thread()); if (QThread::currentThread() == thread()) { //called by user in the same thread as player stopNotifyTimer(); } else { //TODO: post event. } } // vars not set by user can be reset d->start_position_norm = 0; d->stop_position_norm = 0; // 0 can stop play in timerEvent d->media_end = kInvalidPosition; } else { //called by player stopNotifyTimer(); } d->seeking = false; d->reset_state = true; d->repeat_current = -1; if (!isPlaying()) { qDebug("Not playing~"); if (mediaStatus() == LoadingMedia || mediaStatus() == LoadedMedia) { qDebug("loading media: %d", mediaStatus() == LoadingMedia); d->demuxer.setInterruptStatus(-1); } return; } while (d->read_thread->isRunning()) { qDebug("stopping demuxer thread..."); d->read_thread->stop(); d->read_thread->wait(500); // interrupt to quit av_read_frame quickly. d->demuxer.setInterruptStatus(-1); } qDebug("all audio/video threads stopped... state: %d", d->state); } void AVPlayer::timerEvent(QTimerEvent *te) { if (te->timerId() == d->timer_id) { // killTimer() should be in the same thread as object. kill here? if (isPaused()) { //return; //ensure positionChanged emitted for stepForward() } // active only when playing const qint64 t = position(); if (d->stop_position_norm == kInvalidPosition) { // or check d->stop_position_norm < 0 // not seekable. network stream Q_EMIT positionChanged(t); return; } if (t < d->start_position_norm) { //qDebug("position %lld < startPosition %lld", t, d->start_position_norm); // or set clock initial value to get correct t if (d->start_position_norm != mediaStartPosition()) { setPosition(d->start_position_norm); return; } } if (t <= d->stop_position_norm) { if (!d->seeking) { Q_EMIT positionChanged(t); } return; } // atEnd() supports dynamic changed duration. but we can not break A-B repeat mode, so check stoppos and mediastoppos if ((!d->demuxer.atEnd() || d->read_thread->isRunning()) && stopPosition() >= mediaStopPosition()) { if (!d->seeking) { Q_EMIT positionChanged(t); } return; } // TODO: remove. kill timer in an event; if (d->stop_position_norm == 0) { //stop() by user in other thread, state is already reset d->reset_state = false; qDebug("stopPosition() == 0, stop"); stop(); } // t < d->start_position is ok. d->repeat_max<0 means repeat forever if (currentRepeat() >= repeat() && repeat() >= 0) { d->reset_state = true; // true is default, can remove here qDebug("stopPosition() %lld/%lld reached and no repeat: %d", t, stopPosition(), repeat()); stop(); return; } // FIXME: now stop instead of seek if reach media's end. otherwise will not get eof again if (d->stop_position_norm == mediaStopPosition() || !isSeekable()) { // if not seekable, how it can start to play at specified position? qDebug("normalized stopPosition() == mediaStopPosition() or !seekable. d->repeat_current=%d", d->repeat_current); d->reset_state = false; stop(); // repeat after all threads stopped } else { d->repeat_current++; qDebug("noramlized stopPosition() != mediaStopPosition() and seekable. d->repeat_current=%d", d->repeat_current); setPosition(d->start_position_norm); } } } //FIXME: If not playing, it will just play but not play one frame. void AVPlayer::stepForward() { // pause clock pause(true); // must pause AVDemuxThread (set user_paused true) d->read_thread->stepForward(); } void AVPlayer::stepBackward() { d->clock->pause(true); d->state = PausedState; Q_EMIT stateChanged(d->state); Q_EMIT paused(true); d->read_thread->stepBackward(); } void AVPlayer::seek(qreal r) { seek(qint64(r*double(duration()))); } void AVPlayer::seek(qint64 pos) { setPosition(pos); } void AVPlayer::seekForward() { seek(position() + kSeekMS); } void AVPlayer::seekBackward() { seek(position() - kSeekMS); } void AVPlayer::setSeekType(SeekType type) { d->seek_type = type; } SeekType AVPlayer::seekType() const { return d->seek_type; } qreal AVPlayer::bufferProgress() const { const PacketBuffer* buf = d->read_thread->buffer(); return buf ? buf->bufferProgress() : 0; } qreal AVPlayer::bufferSpeed() const { const PacketBuffer* buf = d->read_thread->buffer(); return buf ? buf->bufferSpeedInBytes() : 0; } qint64 AVPlayer::buffered() const { const PacketBuffer* buf = d->read_thread->buffer(); return buf ? buf->buffered() : 0LL; } void AVPlayer::setBufferMode(BufferMode mode) { d->buffer_mode = mode; } BufferMode AVPlayer::bufferMode() const { return d->buffer_mode; } void AVPlayer::setBufferValue(qint64 value) { if (d->buffer_value == value) return; d->buffer_value = value; d->updateBufferValue(); } int AVPlayer::bufferValue() const { return d->buffer_value; } void AVPlayer::updateClock(qint64 msecs) { d->clock->updateExternalClock(msecs); } int AVPlayer::brightness() const { return d->brightness; } void AVPlayer::setBrightness(int val) { if (d->brightness == val) return; d->brightness = val; Q_EMIT brightnessChanged(d->brightness); if (d->vthread) { d->vthread->setBrightness(val); } } int AVPlayer::contrast() const { return d->contrast; } void AVPlayer::setContrast(int val) { if (d->contrast == val) return; d->contrast = val; Q_EMIT contrastChanged(d->contrast); if (d->vthread) { d->vthread->setContrast(val); } } int AVPlayer::hue() const { return 0; } void AVPlayer::setHue(int val) { Q_UNUSED(val); } int AVPlayer::saturation() const { return d->saturation; } void AVPlayer::setSaturation(int val) { if (d->saturation == val) return; d->saturation = val; Q_EMIT saturationChanged(d->saturation); if (d->vthread) { d->vthread->setSaturation(val); } } } //namespace QtAV QtAV-1.12.0/src/AVPlayerPrivate.cpp000066400000000000000000000551071312235004300167200ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "AVPlayerPrivate.h" #include "filter/FilterManager.h" #include "output/OutputSet.h" #include "QtAV/AudioDecoder.h" #include "QtAV/AudioFormat.h" #include "QtAV/AudioResampler.h" #include "QtAV/MediaIO.h" #include "QtAV/VideoCapture.h" #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" namespace QtAV { namespace Internal { int computeNotifyPrecision(qint64 duration, qreal fps) { if (duration <= 0 || duration > 60*1000) // no duration or 10min return 500; if (duration > 20*1000) return 250; int dt = 500; if (fps > 1) { dt = qMin(250, int(qreal(dt*2)/fps)); } else { dt = duration / 80; //<= 250 } return qMax(20, dt); } } // namespace Internal static bool correct_audio_channels(AVCodecContext *ctx) { if (ctx->channels <= 0) { if (ctx->channel_layout) { ctx->channels = av_get_channel_layout_nb_channels(ctx->channel_layout); } } else { if (!ctx->channel_layout) { ctx->channel_layout = av_get_default_channel_layout(ctx->channels); } } return ctx->channel_layout > 0 && ctx->channels > 0; } AVPlayer::Private::Private() : auto_load(false) , async_load(true) , loaded(false) , relative_time_mode(true) , media_start_pts(0) , media_end(kInvalidPosition) , reset_state(true) , start_position(0) , stop_position(kInvalidPosition) , start_position_norm(0) , stop_position_norm(kInvalidPosition) , repeat_max(0) , repeat_current(-1) , timer_id(-1) , audio_track(0) , video_track(0) , subtitle_track(0) , buffer_mode(BufferPackets) , buffer_value(-1) , read_thread(0) , clock(new AVClock(AVClock::AudioClock)) , vo(0) , ao(new AudioOutput()) , adec(0) , vdec(0) , athread(0) , vthread(0) , vcapture(0) , speed(1.0) , vos(0) , aos(0) , brightness(0) , contrast(0) , saturation(0) , seeking(false) , seek_type(AccurateSeek) , interrupt_timeout(30000) , force_fps(0) , notify_interval(-500) , status(NoMedia) , state(AVPlayer::StoppedState) , end_action(MediaEndAction_Default) { demuxer.setInterruptTimeout(interrupt_timeout); /* * reset_state = true; * must be the same value at the end of stop(), and must be different from value in * stopFromDemuxerThread()(which is false), so the initial value must be true */ vc_ids #if QTAV_HAVE(DXVA) //<< VideoDecoderId_DXVA #endif //QTAV_HAVE(DXVA) #if QTAV_HAVE(VAAPI) //<< VideoDecoderId_VAAPI #endif //QTAV_HAVE(VAAPI) #if QTAV_HAVE(CEDARV) << VideoDecoderId_Cedarv #endif //QTAV_HAVE(CEDARV) << VideoDecoderId_FFmpeg; } AVPlayer::Private::~Private() { // TODO: scoped ptr if (ao) { delete ao; ao = 0; } if (adec) { delete adec; adec = 0; } if (vdec) { delete vdec; vdec = 0; } if (vos) { vos->clearOutputs(); delete vos; vos = 0; } if (aos) { aos->clearOutputs(); delete aos; aos = 0; } if (vcapture) { delete vcapture; vcapture = 0; } if (clock) { delete clock; clock = 0; } if (read_thread) { delete read_thread; read_thread = 0; } } bool AVPlayer::Private::checkSourceChange() { if (current_source.type() == QVariant::String) return demuxer.fileName() != current_source.toString(); if (current_source.canConvert()) return demuxer.ioDevice() != current_source.value(); return demuxer.mediaIO() != current_source.value(); } void AVPlayer::Private::updateNotifyInterval() { if (notify_interval <= 0) { notify_interval = -Internal::computeNotifyPrecision(demuxer.duration(), demuxer.frameRate()); } qDebug("notify_interval: %d", qAbs(notify_interval)); } void AVPlayer::Private::applyFrameRate() { qreal vfps = force_fps; bool force = vfps > 0; const bool ao_null = ao && ao->backend().toLower() == QLatin1String("null"); if (athread && !ao_null) { // TODO: no null ao check. null ao block internally force = vfps > 0 && !!vthread; } else if (!force) { force = !!vthread; vfps = statistics.video.frame_rate > 0 ? statistics.video.frame_rate : 25; // vfps<0: try to use pts (ExternalClock). if no pts (raw codec), try the default fps(VideoClock) vfps = -vfps; } qreal r = speed; if (force) { clock->setClockAuto(false); // vfps>0: force video fps to vfps. clock must be external clock->setClockType(vfps > 0 ? AVClock::VideoClock : AVClock::ExternalClock); vthread->setFrameRate(vfps); if (statistics.video.frame_rate > 0) r = qAbs(qreal(vfps))/statistics.video.frame_rate; } else { clock->setClockAuto(true); clock->setClockType(athread && ao->isOpen() ? AVClock::AudioClock : AVClock::ExternalClock); if (vthread) vthread->setFrameRate(0.0); ao->setSpeed(1); clock->setSpeed(1); } ao->setSpeed(r); clock->setSpeed(r); } void AVPlayer::Private::initStatistics() { initBaseStatistics(); initAudioStatistics(demuxer.audioStream()); initVideoStatistics(demuxer.videoStream()); //initSubtitleStatistics(demuxer.subtitleStream()); } //TODO: av_guess_frame_rate in latest ffmpeg void AVPlayer::Private::initBaseStatistics() { statistics.reset(); statistics.url = current_source.type() == QVariant::String ? current_source.toString() : QString(); statistics.start_time = QTime(0, 0, 0).addMSecs(int(demuxer.startTime())); statistics.duration = QTime(0, 0, 0).addMSecs((int)demuxer.duration()); AVFormatContext *fmt_ctx = demuxer.formatContext(); if (!fmt_ctx) { qWarning("demuxer.formatContext()==null. internal error"); updateNotifyInterval(); return; } statistics.bit_rate = fmt_ctx->bit_rate; statistics.format = QString().sprintf("%s - %s", fmt_ctx->iformat->name, fmt_ctx->iformat->long_name); //AV_TIME_BASE_Q: msvc error C2143 //fmt_ctx->duration may be AV_NOPTS_VALUE. AVDemuxer.duration deals with this case AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { statistics.metadata.insert(QString::fromUtf8(tag->key), QString::fromUtf8(tag->value)); } updateNotifyInterval(); } void AVPlayer::Private::initCommonStatistics(int s, Statistics::Common *st, AVCodecContext *avctx) { AVFormatContext *fmt_ctx = demuxer.formatContext(); if (!fmt_ctx) { qWarning("demuxer.formatContext()==null. internal error"); return; } AVStream *stream = fmt_ctx->streams[s]; qDebug("stream: %d, duration=%lld (%lld ms), time_base=%f", s, stream->duration, qint64(qreal(stream->duration)*av_q2d(stream->time_base)*1000.0), av_q2d(stream->time_base)); // AVCodecContext.codec_name is deprecated. use avcodec_get_name. check null avctx->codec? st->codec = QLatin1String(avcodec_get_name(avctx->codec_id)); st->codec_long = QLatin1String(get_codec_long_name(avctx->codec_id)); st->total_time = QTime(0, 0, 0).addMSecs(stream->duration == (qint64)AV_NOPTS_VALUE ? 0 : int(qreal(stream->duration)*av_q2d(stream->time_base)*1000.0)); st->start_time = QTime(0, 0, 0).addMSecs(stream->start_time == (qint64)AV_NOPTS_VALUE ? 0 : int(qreal(stream->start_time)*av_q2d(stream->time_base)*1000.0)); qDebug("codec: %s(%s)", qPrintable(st->codec), qPrintable(st->codec_long)); st->bit_rate = avctx->bit_rate; //fmt_ctx st->frames = stream->nb_frames; if (stream->avg_frame_rate.den && stream->avg_frame_rate.num) st->frame_rate = av_q2d(stream->avg_frame_rate); #if (defined FF_API_R_FRAME_RATE && FF_API_R_FRAME_RATE) //removed in libav10 //FIXME: which 1 should we choose? avg_frame_rate may be nan, r_frame_rate may be wrong(guessed value) else if (stream->r_frame_rate.den && stream->r_frame_rate.num) { st->frame_rate = av_q2d(stream->r_frame_rate); qDebug("%d/%d", stream->r_frame_rate.num, stream->r_frame_rate.den); } #endif //FF_API_R_FRAME_RATE //http://ffmpeg.org/faq.html#AVStream_002er_005fframe_005frate-is-wrong_002c-it-is-much-larger-than-the-frame-rate_002e //http://libav-users.943685.n4.nabble.com/Libav-user-Reading-correct-frame-rate-fps-of-input-video-td4657666.html //qDebug("time: %f~%f, nb_frames=%lld", st->start_time, st->total_time, stream->nb_frames); //why crash on mac? av_q2d({0,0})? AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get(stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { st->metadata.insert(QString::fromUtf8(tag->key), QString::fromUtf8(tag->value)); } } void AVPlayer::Private::initAudioStatistics(int s) { AVCodecContext *avctx = demuxer.audioCodecContext(); statistics.audio = Statistics::Common(); statistics.audio_only = Statistics::AudioOnly(); if (!avctx) return; statistics.audio.available = s == demuxer.audioStream(); initCommonStatistics(s, &statistics.audio, avctx); if (adec) { statistics.audio.decoder = adec->name(); statistics.audio.decoder_detail = adec->description(); } correct_audio_channels(avctx); statistics.audio_only.block_align = avctx->block_align; statistics.audio_only.channels = avctx->channels; char cl[128]; // // nb_channels -1: will use av_get_channel_layout_nb_channels av_get_channel_layout_string(cl, sizeof(cl), avctx->channels, avctx->channel_layout); statistics.audio_only.channel_layout = QLatin1String(cl); statistics.audio_only.sample_fmt = QLatin1String(av_get_sample_fmt_name(avctx->sample_fmt)); statistics.audio_only.frame_size = avctx->frame_size; statistics.audio_only.sample_rate = avctx->sample_rate; } void AVPlayer::Private::initVideoStatistics(int s) { AVCodecContext *avctx = demuxer.videoCodecContext(); statistics.video = Statistics::Common(); statistics.video_only = Statistics::VideoOnly(); if (!avctx) return; statistics.video.available = s == demuxer.videoStream(); initCommonStatistics(s, &statistics.video, avctx); if (vdec) { statistics.video.decoder = vdec->name(); statistics.video.decoder_detail = vdec->description(); } statistics.video_only.coded_height = avctx->coded_height; statistics.video_only.coded_width = avctx->coded_width; statistics.video_only.gop_size = avctx->gop_size; statistics.video_only.pix_fmt = QLatin1String(av_get_pix_fmt_name(avctx->pix_fmt)); statistics.video_only.height = avctx->height; statistics.video_only.width = avctx->width; } // notify statistics change after audio/video thread is set bool AVPlayer::Private::setupAudioThread(AVPlayer *player) { AVDemuxer *ademuxer = &demuxer; if (!external_audio.isEmpty()) ademuxer = &audio_demuxer; ademuxer->setStreamIndex(AVDemuxer::AudioStream, audio_track); // pause demuxer, clear queues, set demuxer stream, set decoder, set ao, resume // clear packets before stream changed if (athread) { athread->packetQueue()->clear(); athread->setDecoder(0); athread->setOutput(0); } AVCodecContext *avctx = ademuxer->audioCodecContext(); if (!avctx) { // TODO: close ao? //TODO: check pulseaudio perapp control if closed return false; } qDebug("has audio"); // TODO: no delete, just reset avctx and reopen if (adec) { adec->disconnect(); delete adec; adec = 0; } adec = AudioDecoder::create(); if (!adec) { qWarning("failed to create audio decoder"); return false; } QObject::connect(adec, SIGNAL(error(QtAV::AVError)), player, SIGNAL(error(QtAV::AVError))); adec->setCodecContext(avctx); adec->setOptions(ac_opt); if (!adec->open()) { AVError e(AVError::AudioCodecNotFound); qWarning() << e.string(); emit player->error(e); return false; } correct_audio_channels(avctx); AudioFormat af; af.setSampleRate(avctx->sample_rate); af.setSampleFormatFFmpeg(avctx->sample_fmt); af.setChannelLayoutFFmpeg(avctx->channel_layout); if (!af.isValid()) { qWarning("invalid audio format. audio stream will be disabled"); return false; } //af.setChannels(avctx->channels); // always reopen to ensure internal buffer queue inside audio backend(openal) is clear. also make it possible to change backend when replay. //if (ao->audioFormat() != af) { //qDebug("ao audio format is changed. reopen ao"); ao->setAudioFormat(af); /// set before close to workaround OpenAL context lost ao->close(); qDebug() << "AudioOutput format: " << ao->audioFormat() << "; requested: " << ao->requestedFormat(); if (!ao->open()) { return false; } //} adec->resampler()->setOutAudioFormat(ao->audioFormat()); // no need to set resampler if AudioFrame is used #if !USE_AUDIO_FRAME adec->resampler()->inAudioFormat().setSampleFormatFFmpeg(avctx->sample_fmt); adec->resampler()->inAudioFormat().setSampleRate(avctx->sample_rate); adec->resampler()->inAudioFormat().setChannels(avctx->channels); adec->resampler()->inAudioFormat().setChannelLayoutFFmpeg(avctx->channel_layout); #endif if (audio_track < 0) return true; if (!athread) { qDebug("new audio thread"); athread = new AudioThread(player); athread->setClock(clock); athread->setStatistics(&statistics); athread->setOutputSet(aos); qDebug("demux thread setAudioThread"); read_thread->setAudioThread(athread); //reconnect if disconnected QList filters = FilterManager::instance().audioFilters(player); //TODO: isEmpty()==false but size() == 0 in debug mode, it's a Qt bug? we can not just foreach without check empty in debug mode if (filters.size() > 0) { foreach (Filter *filter, filters) { athread->installFilter(filter); } } } athread->setDecoder(adec); setAVOutput(ao, ao, athread); updateBufferValue(athread->packetQueue()); initAudioStatistics(ademuxer->audioStream()); return true; } QVariantList AVPlayer::Private::getTracksInfo(AVDemuxer *demuxer, AVDemuxer::StreamType st) { QVariantList info; if (!demuxer) return info; QList streams; switch (st) { case AVDemuxer::AudioStream: streams = demuxer->audioStreams(); break; case AVDemuxer::SubtitleStream: streams = demuxer->subtitleStreams(); break; case AVDemuxer::VideoStream: streams = demuxer->videoStreams(); default: break; } if (streams.isEmpty()) return info; foreach (int s, streams) { QVariantMap t; t[QStringLiteral("id")] = info.size(); t[QStringLiteral("file")] = demuxer->fileName(); AVStream *stream = demuxer->formatContext()->streams[s]; AVCodecContext *ctx = stream->codec; if (ctx) { t[QStringLiteral("codec")] = QByteArray(avcodec_descriptor_get(ctx->codec_id)->name); if (ctx->extradata) t[QStringLiteral("extra")] = QByteArray((const char*)ctx->extradata, ctx->extradata_size); } AVDictionaryEntry *tag = av_dict_get(stream->metadata, "language", NULL, 0); if (!tag) tag = av_dict_get(stream->metadata, "lang", NULL, 0); if (tag) { t[QStringLiteral("language")] = QString::fromUtf8(tag->value); } tag = av_dict_get(stream->metadata, "title", NULL, 0); if (tag) { t[QStringLiteral("title")] = QString::fromUtf8(tag->value); } info.push_back(t); } //QVariantMap t; //t[QStringLiteral("id")] = -1; //info.prepend(t); return info; } bool AVPlayer::Private::applySubtitleStream(int n, AVPlayer *player) { if (!demuxer.setStreamIndex(AVDemuxer::SubtitleStream, n)) return false; AVCodecContext *ctx = demuxer.subtitleCodecContext(); if (!ctx) return false; // FIXME: AVCodecDescriptor.name and AVCodec.name are different! const AVCodecDescriptor *codec_desc = avcodec_descriptor_get(ctx->codec_id); QByteArray codec(codec_desc->name); if (ctx->extradata) Q_EMIT player->internalSubtitleHeaderRead(codec, QByteArray((const char*)ctx->extradata, ctx->extradata_size)); else Q_EMIT player->internalSubtitleHeaderRead(codec, QByteArray()); return true; } bool AVPlayer::Private::tryApplyDecoderPriority(AVPlayer *player) { // TODO: add an option to apply the new decoder even if not available qint64 pos = player->position(); VideoDecoder *vd = NULL; AVCodecContext *avctx = demuxer.videoCodecContext(); foreach(VideoDecoderId vid, vc_ids) { qDebug("**********trying video decoder: %s...", VideoDecoder::name(vid)); vd = VideoDecoder::create(vid); if (!vd) continue; vd->setCodecContext(avctx); // It's fine because AVDecoder copy the avctx properties vd->setOptions(vc_opt); if (vd->open()) { qDebug("**************Video decoder found:%p", vd); break; } delete vd; vd = 0; } qDebug("**************set new decoder:%p -> %p", vdec, vd); if (!vd) { Q_EMIT player->error(AVError(AVError::VideoCodecNotFound)); return false; } if (vd->id() == vdec->id() && vd->options() == vdec->options()) { qDebug("Video decoder does not change"); delete vd; return true; } vthread->packetQueue()->clear(); vthread->setDecoder(vd); // MUST delete decoder after video thread set the decoder to ensure the deleted vdec will not be used in vthread! if (vdec) delete vdec; vdec = vd; QObject::connect(vdec, SIGNAL(error(QtAV::AVError)), player, SIGNAL(error(QtAV::AVError))); initVideoStatistics(demuxer.videoStream()); // If no seek, drop packets until a key frame packet is found. But we may drop too many packets, and also a/v sync is a problem. player->setPosition(pos); return true; } bool AVPlayer::Private::setupVideoThread(AVPlayer *player) { demuxer.setStreamIndex(AVDemuxer::VideoStream, video_track); // pause demuxer, clear queues, set demuxer stream, set decoder, set ao, resume // clear packets before stream changed if (vthread) { vthread->packetQueue()->clear(); vthread->setDecoder(0); } AVCodecContext *avctx = demuxer.videoCodecContext(); if (!avctx) { return false; } if (vdec) { vdec->disconnect(); delete vdec; vdec = 0; } foreach(VideoDecoderId vid, vc_ids) { qDebug("**********trying video decoder: %s...", VideoDecoder::name(vid)); VideoDecoder *vd = VideoDecoder::create(vid); if (!vd) { continue; } //vd->isAvailable() //TODO: the value is wrong now vd->setCodecContext(avctx); vd->setOptions(vc_opt); if (vd->open()) { vdec = vd; qDebug("**************Video decoder found:%p", vdec); break; } delete vd; } if (!vdec) { // DO NOT emit error signals in VideoDecoder::open(). 1 signal is enough AVError e(AVError::VideoCodecNotFound); qWarning() << e.string(); emit player->error(e); return false; } QObject::connect(vdec, SIGNAL(error(QtAV::AVError)), player, SIGNAL(error(QtAV::AVError))); if (!vthread) { vthread = new VideoThread(player); vthread->setClock(clock); vthread->setStatistics(&statistics); vthread->setVideoCapture(vcapture); vthread->setOutputSet(vos); read_thread->setVideoThread(vthread); QList filters = FilterManager::instance().videoFilters(player); if (filters.size() > 0) { foreach (Filter *filter, filters) { vthread->installFilter(filter); } } QObject::connect(vthread, SIGNAL(finished()), player, SLOT(tryClearVideoRenderers()), Qt::DirectConnection); } vthread->setDecoder(vdec); vthread->setBrightness(brightness); vthread->setContrast(contrast); vthread->setSaturation(saturation); updateBufferValue(vthread->packetQueue()); initVideoStatistics(demuxer.videoStream()); return true; } // TODO: set to a lower value when buffering void AVPlayer::Private::updateBufferValue(PacketBuffer* buf) { const bool video = vthread && buf == vthread->packetQueue(); const qreal fps = qMax(24.0, statistics.video.frame_rate); qint64 bv = 0.5*fps; if (!video) { // if has video, then audio buffer should not block the video buffer (bufferValue == 1, modified in AVDemuxThread) bv = statistics.audio.frame_rate > 0 && statistics.audio.frame_rate < 60 ? statistics.audio.frame_rate : 3LL; } if (buffer_mode == BufferTime) bv = 600LL; //ms else if (buffer_mode == BufferBytes) bv = 1024LL; // no block for music with cover if (video) { if (demuxer.hasAttacedPicture() || (statistics.video.frames > 0 && statistics.video.frames < bv)) bv = qMax(1LL, statistics.video.frames); } buf->setBufferMode(buffer_mode); buf->setBufferValue(buffer_value < 0LL ? bv : buffer_value); } void AVPlayer::Private::updateBufferValue() { if (athread) updateBufferValue(athread->packetQueue()); if (vthread) updateBufferValue(vthread->packetQueue()); } } //namespace QtAV QtAV-1.12.0/src/AVPlayerPrivate.h000066400000000000000000000127141312235004300163620ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2014-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVPLAYER_PRIVATE_H #define QTAV_AVPLAYER_PRIVATE_H #include "QtAV/AVDemuxer.h" #include "QtAV/AVPlayer.h" #include "AudioThread.h" #include "VideoThread.h" #include "AVDemuxThread.h" #include "utils/Logger.h" namespace QtAV { static const qint64 kInvalidPosition = std::numeric_limits::max(); class AVPlayer::Private { public: Private(); ~Private(); bool checkSourceChange(); void updateNotifyInterval(); void applyFrameRate(); void initStatistics(); void initBaseStatistics(); void initCommonStatistics(int s, Statistics::Common* st, AVCodecContext* avctx); void initAudioStatistics(int s); void initVideoStatistics(int s); void initSubtitleStatistics(int s); QVariantList getTracksInfo(AVDemuxer* demuxer, AVDemuxer::StreamType st); bool applySubtitleStream(int n, AVPlayer *player); bool setupAudioThread(AVPlayer *player); bool setupVideoThread(AVPlayer *player); bool tryApplyDecoderPriority(AVPlayer *player); // TODO: what if buffer mode changed during playback? void updateBufferValue(PacketBuffer *buf); void updateBufferValue(); //TODO: addAVOutput() template void setAVOutput(Out *&pOut, Out *pNew, AVThread *thread) { Out *old = pOut; bool delete_old = false; if (pOut == pNew) { qDebug("output not changed: %p", pOut); if (thread && thread->output() == pNew) {//avthread already set that output qDebug("avthread already set that output"); return; } } else { pOut = pNew; delete_old = true; } if (!thread) { qDebug("avthread not ready. can not set output."); //no avthread, we can delete it safely //AVOutput must be allocated in heap. Just like QObject's children. if (delete_old) { delete old; old = 0; } return; } //FIXME: what if isPaused()==false but pause(true) in another thread? //bool need_lock = isPlaying() && !thread->isPaused(); //if (need_lock) // thread->lock(); qDebug("set AVThread output"); thread->setOutput(pOut); if (pOut) { pOut->setStatistics(&statistics); //if (need_lock) // thread->unlock(); //??why here? } //now the old avoutput is not used by avthread, we can delete it safely //AVOutput must be allocated in heap. Just like QObject's children. if (delete_old) { delete old; old = 0; } } bool auto_load; bool async_load; // can be QString, QIODevice* QVariant current_source, pendding_source; bool loaded; // for current source bool relative_time_mode; qint64 media_start_pts; // read from media stream qint64 media_end; bool reset_state; qint64 start_position, stop_position; qint64 start_position_norm, stop_position_norm; // real position int repeat_max, repeat_current; int timer_id; //notify position change and check AB repeat range. active when playing int audio_track, video_track, subtitle_track; QVariantList subtitle_tracks; QVariantList video_tracks; QString external_audio; AVDemuxer audio_demuxer; QVariantList external_audio_tracks; QVariantList audio_tracks; BufferMode buffer_mode; qint64 buffer_value; //the following things are required and must be set not null AVDemuxer demuxer; AVDemuxThread *read_thread; AVClock *clock; VideoRenderer *vo; //list? // TODO: remove AudioOutput *ao; // TODO: remove AudioDecoder *adec; VideoDecoder *vdec; AudioThread *athread; VideoThread *vthread; VideoCapture *vcapture; Statistics statistics; qreal speed; OutputSet *vos, *aos; QVector vc_ids; int brightness, contrast, saturation; QVariantHash ac_opt, vc_opt; bool seeking; SeekType seek_type; qint64 interrupt_timeout; qreal force_fps; // timerEvent interval in ms. can divide 1000. depends on media duration, fps etc. // <0: auto compute internally, |notify_interval| is the real interval int notify_interval; MediaStatus status; // status changes can be from demuxer or demux thread AVPlayer::State state; MediaEndAction end_action; QMutex load_mutex; }; } //namespace QtAV #endif // QTAV_AVPLAYER_PRIVATE_H QtAV-1.12.0/src/AVThread.cpp000066400000000000000000000227741312235004300153440ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "AVThread.h" #include #include "AVThread_p.h" #include "QtAV/AVClock.h" #include "QtAV/AVDecoder.h" #include "QtAV/AVOutput.h" #include "QtAV/Filter.h" #include "output/OutputSet.h" #include "utils/Logger.h" namespace QtAV { QVariantHash AVThreadPrivate::dec_opt_framedrop; QVariantHash AVThreadPrivate::dec_opt_normal; AVThreadPrivate::~AVThreadPrivate() { stop = true; if (!paused) { qDebug("~AVThreadPrivate wake up paused thread"); paused = false; next_pause = false; cond.wakeAll(); } packets.setBlocking(true); //??? packets.clear(); QList::iterator it = filters.begin(); while (it != filters.end()) { if ((*it)->isOwnedByTarget() && !(*it)->parent()) delete *it; ++it; } filters.clear(); } AVThread::AVThread(QObject *parent) : QThread(parent) { connect(this, SIGNAL(started()), SLOT(onStarted()), Qt::DirectConnection); connect(this, SIGNAL(finished()), SLOT(onFinished()), Qt::DirectConnection); } AVThread::AVThread(AVThreadPrivate &d, QObject *parent) :QThread(parent),DPTR_INIT(&d) { connect(this, SIGNAL(started()), SLOT(onStarted()), Qt::DirectConnection); connect(this, SIGNAL(finished()), SLOT(onFinished()), Qt::DirectConnection); } AVThread::~AVThread() { //d_ptr destroyed automatically } bool AVThread::isPaused() const { DPTR_D(const AVThread); //if d.next_pause is true, the thread will pause soon, may happens before you can handle the result return d.paused || d.next_pause; } bool AVThread::installFilter(Filter *filter, int index, bool lock) { DPTR_D(AVThread); int p = index; if (p < 0) p += d.filters.size(); if (p < 0) p = 0; if (p > d.filters.size()) p = d.filters.size(); const int old = d.filters.indexOf(filter); // already installed at desired position if (p == old) return true; if (lock) { QMutexLocker locker(&d.mutex); if (p >= 0) d.filters.removeAt(p); d.filters.insert(p, filter); } else { if (p >= 0) d.filters.removeAt(p); d.filters.insert(p, filter); } return true; } bool AVThread::uninstallFilter(Filter *filter, bool lock) { DPTR_D(AVThread); if (lock) { QMutexLocker locker(&d.mutex); return d.filters.removeOne(filter); } return d.filters.removeOne(filter); } const QList& AVThread::filters() const { return d_func().filters; } void AVThread::scheduleTask(QRunnable *task) { d_func().tasks.put(task); } void AVThread::requestSeek() { class SeekPTS : public QRunnable { AVThread *self; public: SeekPTS(AVThread* thread) : self(thread) {} void run() Q_DECL_OVERRIDE { self->d_func().seek_requested = true; } }; scheduleTask(new SeekPTS(this)); } void AVThread::scheduleFrameDrop(bool value) { class FrameDropTask : public QRunnable { AVDecoder *decoder; bool drop; public: FrameDropTask(AVDecoder *dec, bool value) : decoder(dec), drop(value) {} void run() Q_DECL_OVERRIDE { if (!decoder) return; if (drop) decoder->setOptions(AVThreadPrivate::dec_opt_framedrop); else decoder->setOptions(AVThreadPrivate::dec_opt_normal); } }; scheduleTask(new FrameDropTask(decoder(), value)); } qreal AVThread::previousHistoryPts() const { DPTR_D(const AVThread); if (d.pts_history.empty()) { qDebug("pts history is EMPTY"); return 0; } if (d.pts_history.size() == 1) return -d.pts_history.back(); const qreal current_pts = d.pts_history.back(); for (int i = d.pts_history.size() - 2; i > 0; --i) { if (d.pts_history.at(i) < current_pts) return d.pts_history.at(i); } return -d.pts_history.front(); } qreal AVThread::decodeFrameRate() const { DPTR_D(const AVThread); if (d.pts_history.size() <= 1) return 0; const qreal dt = d.pts_history.back() - d.pts_history.front(); if (dt <= 0) return 0; return d.pts_history.size()/dt; } void AVThread::setDropFrameOnSeek(bool value) { d_func().drop_frame_seek = value; } // TODO: shall we close decoder here? void AVThread::stop() { DPTR_D(AVThread); d.stop = true; //stop as soon as possible QMutexLocker locker(&d.mutex); Q_UNUSED(locker); d.packets.setBlocking(false); //stop blocking take() d.packets.clear(); pause(false); //terminate(); } //TODO: output set void AVThread::pause(bool p) { DPTR_D(AVThread); if (d.paused == p) return; d.paused = p; if (!d.paused) { qDebug("wake up paused thread"); d.next_pause = false; d.cond.wakeAll(); } } void AVThread::nextAndPause() { DPTR_D(AVThread); d.next_pause = true; d.paused = true; d.cond.wakeAll(); } void AVThread::lock() { d_func().mutex.lock(); } void AVThread::unlock() { d_func().mutex.unlock(); } void AVThread::setClock(AVClock *clock) { d_func().clock = clock; } AVClock* AVThread::clock() const { return d_func().clock; } PacketBuffer* AVThread::packetQueue() const { return const_cast(&d_func().packets); } void AVThread::setDecoder(AVDecoder *decoder) { DPTR_D(AVThread); QMutexLocker lock(&d.mutex); Q_UNUSED(lock); d.dec = decoder; } AVDecoder* AVThread::decoder() const { return d_func().dec; } void AVThread::setOutput(AVOutput *out) { DPTR_D(AVThread); QMutexLocker lock(&d.mutex); Q_UNUSED(lock); if (!d.outputSet) return; if (!out) { d.outputSet->clearOutputs(); return; } d.outputSet->addOutput(out); } AVOutput* AVThread::output() const { DPTR_D(const AVThread); if (!d.outputSet || d.outputSet->outputs().isEmpty()) return 0; return d.outputSet->outputs().first(); } // TODO: remove? void AVThread::setOutputSet(OutputSet *set) { d_func().outputSet = set; } OutputSet* AVThread::outputSet() const { return d_func().outputSet; } void AVThread::onStarted() { d_func().sem.release(); } void AVThread::onFinished() { if (d_func().sem.available() > 0) d_func().sem.acquire(d_func().sem.available()); } void AVThread::resetState() { DPTR_D(AVThread); pause(false); d.pts_history = ring(d.pts_history.capacity()); d.tasks.clear(); d.render_pts0 = -1; d.stop = false; d.packets.setBlocking(true); d.packets.clear(); d.wait_err = 0; d.wait_timer.invalidate(); } bool AVThread::tryPause(unsigned long timeout) { DPTR_D(AVThread); if (!isPaused()) return false; QMutexLocker lock(&d.mutex); Q_UNUSED(lock); return d.cond.wait(&d.mutex, timeout); qDebug("paused thread waked up!!!"); return true; } bool AVThread::processNextTask() { DPTR_D(AVThread); if (d.tasks.isEmpty()) return true; QRunnable *task = d.tasks.take(); task->run(); if (task->autoDelete()) { delete task; } return true; } void AVThread::setStatistics(Statistics *statistics) { DPTR_D(AVThread); d.statistics = statistics; } bool AVThread::waitForStarted(int msec) { if (!d_func().sem.tryAcquire(1, msec > 0 ? msec : std::numeric_limits::max())) return false; d_func().sem.release(1); //ensure another waitForStarted() continues return true; } void AVThread::waitAndCheck(ulong value, qreal pts) { DPTR_D(AVThread); if (value <= 0) return; value += d.wait_err; d.wait_timer.restart(); //qDebug("wating for %lu msecs", value); ulong us = value * 1000UL; const ulong ms = value; static const ulong kWaitSlice = 20 * 1000UL; //20ms while (us > kWaitSlice) { usleep(kWaitSlice); if (d.stop) us = 0; else us -= kWaitSlice; if (pts > 0) us = qMin(us, ulong((double)(qMax(0, pts - d.clock->value()))*1000000.0)); //qDebug("us: %lu/%lu, pts: %f, clock: %f", us, ms-et.elapsed(), pts, d.clock->value()); processNextTask(); us = qMin(us, (ms-d.wait_timer.elapsed())*1000); } if (us > 0) usleep(us); //qDebug("wait elapsed: %lu %d/%lld", us, ms, et.elapsed()); const int de = ((ms-d.wait_timer.elapsed()) - d.wait_err); if (de > -3 && de < 3) d.wait_err += de; else d.wait_err += de > 0 ? 1 : -1; //qDebug("err: %lld", d.wait_err); } } //namespace QtAV QtAV-1.12.0/src/AVThread.h000066400000000000000000000074061312235004300150040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVTHREAD_H #define QTAV_AVTHREAD_H #include #include #include #include "PacketBuffer.h" //TODO: pause functions. AVOutput may be null, use AVThread's pause state namespace QtAV { class AVDecoder; class AVThreadPrivate; class AVOutput; class AVClock; class Filter; class Statistics; class OutputSet; class AVThread : public QThread { Q_OBJECT DPTR_DECLARE_PRIVATE(AVThread) public: explicit AVThread(QObject *parent = 0); virtual ~AVThread(); //used for changing some components when running Q_DECL_DEPRECATED void lock(); Q_DECL_DEPRECATED void unlock(); void setClock(AVClock *clock); AVClock* clock() const; PacketBuffer* packetQueue() const; void setDecoder(AVDecoder *decoder); AVDecoder *decoder() const; void setOutput(AVOutput *out); //Q_DECL_DEPRECATED AVOutput* output() const; //Q_DECL_DEPRECATED void setOutputSet(OutputSet *set); OutputSet* outputSet() const; void setDemuxEnded(bool ended); bool isPaused() const; bool waitForStarted(int msec = -1); bool installFilter(Filter *filter, int index = 0x7FFFFFFF, bool lock = true); bool uninstallFilter(Filter *filter, bool lock = true); const QList &filters() const; // TODO: resample, resize task etc. void scheduleTask(QRunnable *task); void requestSeek(); void scheduleFrameDrop(bool value = true); qreal previousHistoryPts() const; //move to statistics? qreal decodeFrameRate() const; //move to statistics? void setDropFrameOnSeek(bool value); public slots: virtual void stop(); /*change pause state. the pause/continue action will do in the next loop*/ void pause(bool p); //processEvents when waiting? void nextAndPause(); //process 1 frame and pause Q_SIGNALS: void frameDelivered(); /*! * \brief seekFinished * \param timestamp the frame pts after seek */ void seekFinished(qint64 timestamp); void eofDecoded(); private Q_SLOTS: void onStarted(); void onFinished(); protected: AVThread(AVThreadPrivate& d, QObject *parent = 0); void resetState(); /* * If the pause state is true setted by pause(true), then block the thread and wait for pause state changed, i.e. pause(false) * and return true. Otherwise, return false immediatly. */ // has timeout so that the pending tasks can be processed bool tryPause(unsigned long timeout = 100); bool processNextTask(); //in AVThread // pts > 0: compare pts and clock when waiting void waitAndCheck(ulong value, qreal pts); DPTR_DECLARE(AVThread) private: void setStatistics(Statistics* statistics); friend class AVPlayer; }; } #endif // QTAV_AVTHREAD_H QtAV-1.12.0/src/AVThread_p.h000066400000000000000000000063701312235004300153220ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVTHREAD_P_H #define QTAV_AVTHREAD_P_H #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #include #else #include typedef QTime QElapsedTimer; #endif #include "PacketBuffer.h" #include "utils/ring.h" QT_BEGIN_NAMESPACE class QRunnable; QT_END_NAMESPACE namespace QtAV { const double kSyncThreshold = 0.2; // 200 ms class AVDecoder; class AVOutput; class AVClock; class Filter; class Statistics; class OutputSet; class AVThreadPrivate : public DPtrPrivate { public: AVThreadPrivate(): paused(false) , next_pause(false) , stop(false) , clock(0) , dec(0) , outputSet(0) , delay(0) , statistics(0) , seek_requested(false) , render_pts0(-1) , drop_frame_seek(true) , pts_history(30) , wait_err(0) { tasks.blockFull(false); QVariantHash opt; opt[QString::fromLatin1("skip_frame")] = 8; // 8 for "avcodec", "NoRef" for "FFmpeg". see AVDiscard dec_opt_framedrop[QString::fromLatin1("avcodec")] = opt; opt[QString::fromLatin1("skip_frame")] = 0; // 0 for "avcodec", "Default" for "FFmpeg". see AVDiscard dec_opt_normal[QString::fromLatin1("avcodec")] = opt; // avcodec need correct string or value in libavcodec } virtual ~AVThreadPrivate(); bool paused, next_pause; volatile bool stop; //true when packets is empty and demux is end. AVClock *clock; PacketBuffer packets; AVDecoder *dec; OutputSet *outputSet; QMutex mutex; QWaitCondition cond; //pause qreal delay; QList filters; Statistics *statistics; //not obj. Statistics is unique for the player, which is in AVPlayer BlockingQueue tasks; QSemaphore sem; bool seek_requested; //only decode video without display or skip decode audio until pts reaches qreal render_pts0; static QVariantHash dec_opt_framedrop, dec_opt_normal; bool drop_frame_seek; ring pts_history; qint64 wait_err; QElapsedTimer wait_timer; }; } //namespace QtAV #endif // QTAV_AVTHREAD_P_H QtAV-1.12.0/src/AVTranscoder.cpp000066400000000000000000000251231312235004300162300ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AVTranscoder.h" #include "QtAV/AVPlayer.h" #include "QtAV/AVMuxer.h" #include "QtAV/EncodeFilter.h" #include "QtAV/Statistics.h" #include "utils/BlockingQueue.h" #include "utils/Logger.h" namespace QtAV { class AVTranscoder::Private { public: Private() : started(false) , async(false) , encoded_frames(0) , start_time(0) , source_player(0) , afilter(0) , vfilter(0) {} ~Private() { muxer.close(); if (afilter) { delete afilter; } if (vfilter) { delete vfilter; } } bool started; bool async; int encoded_frames; qint64 start_time; AVPlayer *source_player; AudioEncodeFilter *afilter; VideoEncodeFilter *vfilter; //BlockingQueue aqueue, vqueue; // TODO: 1 queue if packet.mediaType is enabled AVMuxer muxer; QString format; QVector filters; }; AVTranscoder::AVTranscoder(QObject *parent) : QObject(parent) , d(new Private()) { } AVTranscoder::~AVTranscoder() { stop(); //TODO: wait for stopped() } void AVTranscoder::setAsync(bool value) { if (d->async == value) return; d->async = value; Q_EMIT asyncChanged(); if (d->afilter) { d->afilter->setAsync(value); } if (d->vfilter) { d->vfilter->setAsync(value); } } bool AVTranscoder::isAsync() const { return d->async; } void AVTranscoder::setMediaSource(AVPlayer *player) { if (d->source_player) { if (d->afilter) disconnect(d->source_player, SIGNAL(stopped()), d->afilter, SLOT(finish())); if (d->vfilter) disconnect(d->source_player, SIGNAL(stopped()), d->vfilter, SLOT(finish())); disconnect(d->source_player, SIGNAL(started()), this, SLOT(onSourceStarted())); } d->source_player = player; // direct connect to ensure it's called before encoders open in filters connect(d->source_player, SIGNAL(started()), this, SLOT(onSourceStarted()), Qt::DirectConnection); } AVPlayer* AVTranscoder::sourcePlayer() const { return d->source_player; } QString AVTranscoder::outputFile() const { return d->muxer.fileName(); } QIODevice* AVTranscoder::outputDevice() const { return d->muxer.ioDevice(); } MediaIO* AVTranscoder::outputMediaIO() const { return d->muxer.mediaIO(); } void AVTranscoder::setOutputMedia(const QString &fileName) { d->muxer.setMedia(fileName); } void AVTranscoder::setOutputMedia(QIODevice *dev) { d->muxer.setMedia(dev); } void AVTranscoder::setOutputMedia(MediaIO *io) { d->muxer.setMedia(io); } void AVTranscoder::setOutputFormat(const QString &fmt) { d->format = fmt; d->muxer.setFormat(fmt); } QString AVTranscoder::outputFormatForced() const { return d->format; } void AVTranscoder::setOutputOptions(const QVariantHash &dict) { d->muxer.setOptions(dict); } QVariantHash AVTranscoder::outputOptions() const { return d->muxer.options(); } bool AVTranscoder::createVideoEncoder(const QString &name) { if (!d->vfilter) { d->vfilter = new VideoEncodeFilter(); d->vfilter->setAsync(isAsync()); // BlockingQueuedConnection: ensure muxer open()/close() in the same thread, and is open when packet is encoded connect(d->vfilter, SIGNAL(readyToEncode()), SLOT(prepareMuxer()), Qt::BlockingQueuedConnection); // direct: can ensure delayed frames (when stop()) are written at last connect(d->vfilter, SIGNAL(frameEncoded(QtAV::Packet)), SLOT(writeVideo(QtAV::Packet)), Qt::DirectConnection); connect(d->vfilter, SIGNAL(finished()), SLOT(tryFinish())); } return !!d->vfilter->createEncoder(name); } VideoEncoder* AVTranscoder::videoEncoder() const { if (!d->vfilter) return 0; return d->vfilter->encoder(); } bool AVTranscoder::createAudioEncoder(const QString &name) { if (!d->afilter) { d->afilter = new AudioEncodeFilter(); d->afilter->setAsync(isAsync()); // BlockingQueuedConnection: ensure muxer open()/close() in the same thread, and is open when packet is encoded connect(d->afilter, SIGNAL(readyToEncode()), SLOT(prepareMuxer()), Qt::BlockingQueuedConnection); // direct: can ensure delayed frames (when stop()) are written at last connect(d->afilter, SIGNAL(frameEncoded(QtAV::Packet)), SLOT(writeAudio(QtAV::Packet)), Qt::DirectConnection); connect(d->afilter, SIGNAL(finished()), SLOT(tryFinish())); } return !!d->afilter->createEncoder(name); } AudioEncoder* AVTranscoder::audioEncoder() const { if (!d->afilter) return 0; return d->afilter->encoder(); } bool AVTranscoder::isRunning() const { return d->started; } bool AVTranscoder::isPaused() const { if (d->vfilter) { if (d->vfilter->isEnabled()) return false; return true; } if (d->afilter) { if (d->afilter->isEnabled()) return false; return true; } return false; //stopped } qint64 AVTranscoder::startTime() const { return d->start_time; } void AVTranscoder::setStartTime(qint64 ms) { if (d->start_time == ms) return; d->start_time = ms; Q_EMIT startTimeChanged(ms); if (d->afilter) d->afilter->setStartTime(startTime()); if (d->vfilter) d->vfilter->setStartTime(startTime()); } void AVTranscoder::start() { if (!videoEncoder()) return; if (!sourcePlayer()) return; d->encoded_frames = 0; d->started = true; d->filters.clear(); if (sourcePlayer()) { if (d->afilter) { d->filters.append(d->afilter); d->afilter->setStartTime(startTime()); sourcePlayer()->installFilter(d->afilter); disconnect(sourcePlayer(), SIGNAL(stopped()), d->afilter, SLOT(finish())); connect(sourcePlayer(), SIGNAL(stopped()), d->afilter, SLOT(finish()), Qt::DirectConnection); } if (d->vfilter) { d->filters.append(d->vfilter); d->vfilter->setStartTime(startTime()); qDebug("framerate: %.3f/%.3f", videoEncoder()->frameRate(), sourcePlayer()->statistics().video.frame_rate); if (videoEncoder()->frameRate() <= 0) { // use source frame rate. set before install filter (so before open) videoEncoder()->setFrameRate(sourcePlayer()->statistics().video.frame_rate); } sourcePlayer()->installFilter(d->vfilter); disconnect(sourcePlayer(), SIGNAL(stopped()), d->vfilter, SLOT(finish())); connect(sourcePlayer(), SIGNAL(stopped()), d->vfilter, SLOT(finish()), Qt::DirectConnection); } } Q_EMIT started(); } void AVTranscoder::stop() { if (!isRunning()) return; if (!d->muxer.isOpen()) return; // uninstall encoder filters first then encoders can be closed safely if (sourcePlayer()) { sourcePlayer()->uninstallFilter(d->afilter); sourcePlayer()->uninstallFilter(d->vfilter); } if (d->afilter) d->afilter->finish(); //FIXME: thread of sync mode if (d->vfilter) d->vfilter->finish(); } void AVTranscoder::stopInternal() { d->muxer.close(); d->started = false; Q_EMIT stopped(); qDebug("AVTranscoder stopped"); } void AVTranscoder::pause(bool value) { if (d->vfilter) d->vfilter->setEnabled(!value); if (d->afilter) d->afilter->setEnabled(!value); Q_EMIT paused(value); } void AVTranscoder::onSourceStarted() { if (d->vfilter) { qDebug("onSourceStarted framerate: %.3f/%.3f", videoEncoder()->frameRate(), sourcePlayer()->statistics().video.frame_rate); if (videoEncoder()->frameRate() <= 0) { // use source frame rate. set before install filter (so before open) videoEncoder()->setFrameRate(sourcePlayer()->statistics().video.frame_rate); } } } void AVTranscoder::prepareMuxer() { // TODO: lock here? // open muxer only if all encoders are open if (audioEncoder() && videoEncoder()) { if (!audioEncoder()->isOpen() || !videoEncoder()->isOpen()) { qDebug("encoders are not readly a:%d v:%d", audioEncoder()->isOpen(), videoEncoder()->isOpen()); return; } } if (audioEncoder()) d->muxer.copyProperties(audioEncoder()); if (videoEncoder()) d->muxer.copyProperties(videoEncoder()); if (!d->format.isEmpty()) d->muxer.setFormat(d->format); // clear when media changed if (!d->muxer.open()) { qWarning("Failed to open muxer"); return; } } void AVTranscoder::writeAudio(const QtAV::Packet &packet) { // TODO: muxer maybe is not open. queue the packet if (!d->muxer.isOpen()) { //d->aqueue.put(packet); return; } d->muxer.writeAudio(packet); Q_EMIT audioFrameEncoded(packet.pts); if (d->vfilter) return; // TODO: startpts, duration, encoded size d->encoded_frames++; //qDebug("encoded frames: %d, pos: %lld", d->encoded_frames, packet.position); } void AVTranscoder::writeVideo(const QtAV::Packet &packet) { // TODO: muxer maybe is not open. queue the packet if (!d->muxer.isOpen()) return; d->muxer.writeVideo(packet); Q_EMIT videoFrameEncoded(packet.pts); // TODO: startpts, duration, encoded size d->encoded_frames++; printf("encoded frames: %d, @%.3f pos: %lld\r", d->encoded_frames, packet.pts, packet.position);fflush(0); } void AVTranscoder::tryFinish() { Filter* f = qobject_cast(sender()); d->filters.remove(d->filters.indexOf(f)); if (d->filters.isEmpty()) stopInternal(); } } //namespace QtAV QtAV-1.12.0/src/AudioFormat.cpp000066400000000000000000000326371312235004300161170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioFormat.h" #include "QtAV/private/AVCompat.h" #ifndef QT_NO_DEBUG_STREAM #include #endif //QT_NO_DEBUG_STREAM //FF_API_OLD_SAMPLE_FMT. e.g. FFmpeg 0.9 #ifdef SampleFormat #undef SampleFormat #endif namespace QtAV { const qint64 kHz = 1000000LL; typedef struct { AVSampleFormat avfmt; AudioFormat::SampleFormat fmt; const char* name; } sample_fmt_entry; static const sample_fmt_entry samplefmts[] = { { AV_SAMPLE_FMT_U8, AudioFormat::SampleFormat_Unsigned8, "u8" }, { AV_SAMPLE_FMT_S16, AudioFormat::SampleFormat_Signed16, "s16" }, { AV_SAMPLE_FMT_S32, AudioFormat::SampleFormat_Signed32, "s32" }, { AV_SAMPLE_FMT_FLT, AudioFormat::SampleFormat_Float, "float" }, { AV_SAMPLE_FMT_DBL, AudioFormat::SampleFormat_Double, "double" }, { AV_SAMPLE_FMT_U8P, AudioFormat::SampleFormat_Unsigned8Planar, "u8p" }, { AV_SAMPLE_FMT_S16P, AudioFormat::SampleFormat_Signed16Planar, "s16p" }, { AV_SAMPLE_FMT_S32P, AudioFormat::SampleFormat_Signed32Planar, "s32p" }, { AV_SAMPLE_FMT_FLTP, AudioFormat::SampleFormat_FloatPlanar, "floatp" }, { AV_SAMPLE_FMT_DBLP, AudioFormat::SampleFormat_DoublePlanar, "doublep" }, { AV_SAMPLE_FMT_NONE, AudioFormat::SampleFormat_Unknown, "unknown" } }; AudioFormat::SampleFormat AudioFormat::sampleFormatFromFFmpeg(int fffmt) { for (int i = 0; samplefmts[i].fmt != AudioFormat::SampleFormat_Unknown; ++i) { if ((int)samplefmts[i].avfmt == fffmt) return samplefmts[i].fmt; } return AudioFormat::SampleFormat_Unknown; } int AudioFormat::sampleFormatToFFmpeg(AudioFormat::SampleFormat fmt) { for (int i = 0; samplefmts[i].fmt != AudioFormat::SampleFormat_Unknown; ++i) { if (samplefmts[i].fmt == fmt) return (int)samplefmts[i].avfmt; } return (int)AV_SAMPLE_FMT_NONE; } typedef struct { qint64 ff; AudioFormat::ChannelLayout cl; } ChannelLayoutMap; static const ChannelLayoutMap kChannelLayoutMap[] = { { AV_CH_FRONT_LEFT, AudioFormat::ChannelLayout_Left }, { AV_CH_FRONT_RIGHT, AudioFormat::ChannelLayout_Right }, { AV_CH_FRONT_CENTER, AudioFormat::ChannelLayout_Center }, { AV_CH_LAYOUT_MONO, AudioFormat::ChannelLayout_Mono }, { AV_CH_LAYOUT_STEREO, AudioFormat::ChannelLayout_Stereo }, { 0, AudioFormat::ChannelLayout_Unsupported} }; AudioFormat::ChannelLayout AudioFormat::channelLayoutFromFFmpeg(qint64 clff) { for (unsigned int i = 0; i < sizeof(kChannelLayoutMap)/sizeof(ChannelLayoutMap); ++i) { if (kChannelLayoutMap[i].ff == clff) return kChannelLayoutMap[i].cl; } return AudioFormat::ChannelLayout_Unsupported; } qint64 AudioFormat::channelLayoutToFFmpeg(AudioFormat::ChannelLayout cl) { for (unsigned int i = 0; i < sizeof(kChannelLayoutMap)/sizeof(ChannelLayoutMap); ++i) { if (kChannelLayoutMap[i].cl == cl) return kChannelLayoutMap[i].ff; } return 0; } class AudioFormatPrivate : public QSharedData { public: AudioFormatPrivate() : sample_fmt(AudioFormat::SampleFormat_Input) , av_sample_fmt(AV_SAMPLE_FMT_NONE) , channels(0) , sample_rate(0) , channel_layout(AudioFormat::ChannelLayout_Unsupported) , channel_layout_ff(0) {} void setChannels(int cs) { channels = cs; if (av_get_channel_layout_nb_channels(channel_layout_ff) != channels) { channel_layout_ff = av_get_default_channel_layout(channels); channel_layout = AudioFormat::channelLayoutFromFFmpeg(channel_layout_ff); } } void setChannelLayoutFF(qint64 clff) { channel_layout_ff = clff; if (av_get_channel_layout_nb_channels(channel_layout_ff) != channels) { channels = av_get_channel_layout_nb_channels(channel_layout_ff); } } AudioFormat::SampleFormat sample_fmt; AVSampleFormat av_sample_fmt; int channels; int sample_rate; AudioFormat::ChannelLayout channel_layout; qint64 channel_layout_ff; }; AudioFormat::SampleFormat AudioFormat::make(int bytesPerSample, bool isFloat, bool isUnsigned, bool isPlanar) { int f = bytesPerSample; if (isFloat) f |= kFloat; if (isUnsigned) f |= kUnsigned; if (isPlanar) f |= kPlanar; return SampleFormat(f); } AudioFormat::AudioFormat(): d(new AudioFormatPrivate()) { } AudioFormat::AudioFormat(const AudioFormat &other): d(other.d) { } AudioFormat::~AudioFormat() { } /*! Assigns \a other to this AudioFormat implementation. */ AudioFormat& AudioFormat::operator=(const AudioFormat &other) { d = other.d; return *this; } /*! Returns true if this AudioFormat is equal to the \a other AudioFormat; otherwise returns false. All elements of AudioFormat are used for the comparison. */ bool AudioFormat::operator==(const AudioFormat &other) const { return d->sample_rate == other.d->sample_rate && //compare channel layout first because it determines channel count d->channel_layout_ff == other.d->channel_layout_ff && d->channel_layout == other.d->channel_layout && d->channels == other.d->channels && d->sample_fmt == other.d->sample_fmt; } /*! Returns true if this AudioFormat is not equal to the \a other AudioFormat; otherwise returns false. All elements of AudioFormat are used for the comparison. */ bool AudioFormat::operator!=(const AudioFormat& other) const { return !(*this == other); } /*! Returns true if all of the parameters are valid-> */ bool AudioFormat::isValid() const { return d->sample_rate > 0 && (d->channels > 0 || d->channel_layout > 0) && d->sample_fmt != AudioFormat::SampleFormat_Unknown; } bool AudioFormat::isFloat() const { return d->sample_fmt & kFloat; } bool AudioFormat::isUnsigned() const { return IsUnsigned(d->sample_fmt); } bool AudioFormat::isPlanar() const { return IsPlanar(d->sample_fmt); } int AudioFormat::planeCount() const { return isPlanar() ? channels() : 1; } /*! Sets the sample rate to \a samplerate Hertz. */ void AudioFormat::setSampleRate(int sampleRate) { d->sample_rate = sampleRate; } /*! Returns the current sample rate in Hertz. */ int AudioFormat::sampleRate() const { return d->sample_rate; } /*! Sets the channel layout to \a layout. Currently use FFmpeg's. see avutil/channel_layout.h */ void AudioFormat::setChannelLayoutFFmpeg(qint64 layout) { //FFmpeg channel layout is more complete, so we just it d->channel_layout = channelLayoutFromFFmpeg(layout); d->setChannelLayoutFF(layout); } qint64 AudioFormat::channelLayoutFFmpeg() const { return d->channel_layout_ff; } void AudioFormat::setChannelLayout(ChannelLayout layout) { qint64 clff = channelLayoutToFFmpeg(layout); d->channel_layout = layout; //TODO: shall we set ffmpeg channel layout to 0(not valid value)? if (!clff) return; d->setChannelLayoutFF(clff); } AudioFormat::ChannelLayout AudioFormat::channelLayout() const { return d->channel_layout; } QString AudioFormat::channelLayoutName() const { char cl[128]; av_get_channel_layout_string(cl, sizeof(cl), -1, channelLayoutFFmpeg()); //TODO: ff version return QLatin1String(cl); } /*! Sets the channel count to \a channels. */ void AudioFormat::setChannels(int channels) { d->setChannels(channels); } /*! Returns the current channel count value. */ int AudioFormat::channels() const { return d->channels; } /*! Sets the sampleFormat to \a sampleFormat. */ void AudioFormat::setSampleFormat(AudioFormat::SampleFormat sampleFormat) { d->sample_fmt = sampleFormat; d->av_sample_fmt = (AVSampleFormat)AudioFormat::sampleFormatToFFmpeg(sampleFormat); } /*! Returns the current SampleFormat value. */ AudioFormat::SampleFormat AudioFormat::sampleFormat() const { return d->sample_fmt; } void AudioFormat::setSampleFormatFFmpeg(int ffSampleFormat) { d->sample_fmt = AudioFormat::sampleFormatFromFFmpeg(ffSampleFormat); d->av_sample_fmt = (AVSampleFormat)ffSampleFormat; } int AudioFormat::sampleFormatFFmpeg() const { return d->av_sample_fmt; } QString AudioFormat::sampleFormatName() const { if (d->av_sample_fmt == AV_SAMPLE_FMT_NONE) { for (int i = 0; samplefmts[i].fmt != SampleFormat_Unknown; ++i) { if (samplefmts[i].fmt == d->sample_fmt) return QLatin1String(samplefmts[i].name); } } return QLatin1String(av_get_sample_fmt_name((AVSampleFormat)sampleFormatFFmpeg())); } /*! Returns the number of bytes required for this audio format for \a duration microseconds. Returns 0 if this format is not valid-> Note that some rounding may occur if \a duration is not an exact fraction of the sampleRate(). \sa durationForBytes() */ qint32 AudioFormat::bytesForDuration(qint64 duration) const { return bytesPerFrame() * framesForDuration(duration); } /*! Returns the number of microseconds represented by \a bytes in this format. Returns 0 if this format is not valid-> Note that some rounding may occur if \a bytes is not an exact multiple of the number of bytes per frame. \sa bytesForDuration() */ qint64 AudioFormat::durationForBytes(qint32 bytes) const { if (!isValid() || bytes <= 0) return 0; // We round the byte count to ensure whole frames return qint64(kHz * (bytes / bytesPerFrame())) / sampleRate(); } /*! Returns the number of bytes required for \a frameCount frames of this format. Returns 0 if this format is not valid-> \sa bytesForDuration() */ qint32 AudioFormat::bytesForFrames(qint32 frameCount) const { return frameCount * bytesPerFrame(); } /*! Returns the number of frames represented by \a byteCount in this format. Note that some rounding may occur if \a byteCount is not an exact multiple of the number of bytes per frame. Each frame has one sample per channel. \sa framesForDuration() */ qint32 AudioFormat::framesForBytes(qint32 byteCount) const { int size = bytesPerFrame(); if (size > 0) return byteCount / size; return 0; } /*! Returns the number of frames required to represent \a duration microseconds in this format. Note that some rounding may occur if \a duration is not an exact fraction of the \l sampleRate(). */ qint32 AudioFormat::framesForDuration(qint64 duration) const { if (!isValid()) return 0; return qint32((duration * sampleRate()) / kHz); } /*! Return the number of microseconds represented by \a frameCount frames in this format. */ qint64 AudioFormat::durationForFrames(qint32 frameCount) const { if (!isValid() || frameCount <= 0) return 0; return (frameCount * kHz) / sampleRate(); } int AudioFormat::bytesPerFrame() const { if (!isValid()) return 0; return bytesPerSample() * channels(); } // kSize: assume 12 bytes(long double) at most int AudioFormat::bytesPerSample() const { return d->sample_fmt & ((1<<(kSize+1)) - 1); } int AudioFormat::sampleSize() const { return bytesPerSample(); } int AudioFormat::bitRate() const { return bytesPerSecond() << 3; } int AudioFormat::bytesPerSecond() const { return channels() * bytesPerSample() * sampleRate(); } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QtAV::AudioFormat &fmt) { dbg.nospace() << "QtAV::AudioFormat(" << fmt.sampleRate(); dbg.nospace() << "Hz, " << fmt.bytesPerSample(); dbg.nospace() << "Bytes, channelCount:" << fmt.channels(); dbg.nospace() << ", channelLayout: " << fmt.channelLayoutName(); dbg.nospace() << ", sampleFormat: " << fmt.sampleFormatName(); dbg.nospace() << ")"; return dbg.space(); } QDebug operator<<(QDebug dbg, QtAV::AudioFormat::SampleFormat sampleFormat) { dbg.nospace() << av_get_sample_fmt_name((AVSampleFormat)AudioFormat::sampleFormatToFFmpeg(sampleFormat)); return dbg.space(); } QDebug operator<<(QDebug dbg, QtAV::AudioFormat::ChannelLayout channelLayout) { char cl[128]; av_get_channel_layout_string(cl, sizeof(cl), -1, AudioFormat::channelLayoutToFFmpeg(channelLayout)); //TODO: ff version dbg.nospace() << cl; return dbg.space(); } #endif namespace { class AudioFormatPrivateRegisterMetaTypes { public: AudioFormatPrivateRegisterMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } } _registerMetaTypes; } } //namespace QtAV QtAV-1.12.0/src/AudioFrame.cpp000066400000000000000000000151661312235004300157170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioFrame.h" #include "QtAV/private/Frame_p.h" #include "QtAV/AudioResampler.h" #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" namespace QtAV { namespace{ static const struct RegisterMetaTypes { inline RegisterMetaTypes() { qRegisterMetaType("QtAV::AudioFrame"); } } _registerMetaTypes; } class AudioFramePrivate : public FramePrivate { public: AudioFramePrivate(const AudioFormat& fmt) : FramePrivate() , format(fmt) , samples_per_ch(0) , conv(0) { if (!format.isValid()) return; const int nb_planes(format.planeCount()); planes.reserve(nb_planes); planes.resize(nb_planes); line_sizes.reserve(nb_planes); line_sizes.resize(nb_planes); } AudioFormat format; int samples_per_ch; AudioResampler *conv; }; /*! Constructs a shallow copy of \a other. Since AudioFrame is explicitly shared, these two instances will reflect the same frame. */ AudioFrame::AudioFrame(const AudioFrame &other) : Frame(other) { } AudioFrame::AudioFrame(const AudioFormat &format, const QByteArray& data) : Frame(new AudioFramePrivate(format)) { if (data.isEmpty()) return; Q_D(AudioFrame); d->format = format; d->data = data; if (!d->format.isValid()) return; if (d->data.isEmpty()) return; d->samples_per_ch = data.size() / d->format.channels() / d->format.bytesPerSample(); const int nb_planes(d->format.planeCount()); const int bpl(d->data.size()/nb_planes); for (int i = 0; i < nb_planes; ++i) { setBytesPerLine(bpl, i); setBits((uchar*)d->data.constData() + i*bpl, i); } //init(); } /*! Assigns the contents of \a other to this video frame. Since AudioFrame is explicitly shared, these two instances will reflect the same frame. */ AudioFrame &AudioFrame::operator =(const AudioFrame &other) { d_ptr = other.d_ptr; return *this; } AudioFrame::~AudioFrame() { } bool AudioFrame::isValid() const { Q_D(const AudioFrame); return d->samples_per_ch > 0 && d->format.isValid(); } QByteArray AudioFrame::data() { if (!isValid()) return QByteArray(); Q_D(AudioFrame); if (d->data.isEmpty()) { AudioFrame a(clone()); d->data = a.data(); } return d->data; } int AudioFrame::channelCount() const { Q_D(const AudioFrame); if (!d->format.isValid()) return 0; return d->format.channels(); } AudioFrame AudioFrame::clone() const { Q_D(const AudioFrame); if (d->format.sampleFormatFFmpeg() == AV_SAMPLE_FMT_NONE || d->format.channels() <= 0) return AudioFrame(); if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0) return AudioFrame(format()); QByteArray buf(bytesPerLine()*planeCount(), 0); char *dst = buf.data(); //must before buf is shared, otherwise data will be detached. for (int i = 0; i < planeCount(); ++i) { const int plane_size = bytesPerLine(i); memcpy(dst, constBits(i), plane_size); dst += plane_size; } AudioFrame f(d->format, buf); f.setSamplesPerChannel(samplesPerChannel()); f.setTimestamp(timestamp()); // meta data? return f; } AudioFormat AudioFrame::format() const { return d_func()->format; } void AudioFrame::setSamplesPerChannel(int samples) { Q_D(AudioFrame); if (!d->format.isValid()) { qWarning() << "can not set spc for an invalid format: " << d->format; return; } d->samples_per_ch = samples; const int nb_planes = d->format.planeCount(); const int bpl(d->line_sizes[0] > 0 ? d->line_sizes[0] : d->samples_per_ch*d->format.bytesPerSample() * (d->format.isPlanar() ? 1 : d->format.channels())); for (int i = 0; i < nb_planes; ++i) { setBytesPerLine(bpl, i); } if (d->data.isEmpty()) return; if (!constBits(0)) { setBits((quint8*)d->data.constData(), 0); } for (int i = 1; i < nb_planes; ++i) { if (!constBits(i)) { setBits((uchar*)constBits(i-1) + bpl, i); } } } int AudioFrame::samplesPerChannel() const { return d_func()->samples_per_ch; } void AudioFrame::setAudioResampler(AudioResampler *conv) { d_func()->conv = conv; } qint64 AudioFrame::duration() const { Q_D(const AudioFrame); return d->format.durationForBytes(d->data.size()); } AudioFrame AudioFrame::to(const AudioFormat &fmt) const { if (!isValid() || !constBits(0)) return AudioFrame(); //if (fmt == format()) // return clone(); //FIXME: clone a frame from ffmpeg is not enough? Q_D(const AudioFrame); // TODO: use a pool AudioResampler *conv = d->conv; QScopedPointer c; if (!conv) { conv = AudioResampler::create(AudioResamplerId_FF); if (!conv) conv = AudioResampler::create(AudioResamplerId_Libav); if (!conv) { qWarning("no audio resampler is available"); return AudioFrame(); } c.reset(conv); } conv->setInAudioFormat(format()); conv->setOutAudioFormat(fmt); //conv->prepare(); // already called in setIn/OutFormat conv->setInSampesPerChannel(samplesPerChannel()); //TODO if (!conv->convert((const quint8**)d->planes.constData())) { qWarning() << "AudioFrame::to error: " << format() << "=>" << fmt; return AudioFrame(); } AudioFrame f(fmt, conv->outData()); f.setSamplesPerChannel(conv->outSamplesPerChannel()); f.setTimestamp(timestamp()); f.d_ptr->metadata = d->metadata; // need metadata? return f; } } //namespace QtAV QtAV-1.12.0/src/AudioResampler.cpp000066400000000000000000000113771312235004300166170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioResampler.h" #include "QtAV/AudioFormat.h" #include "QtAV/private/AudioResampler_p.h" #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(AudioResampler) AudioResamplerId AudioResamplerId_FF = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value; AudioResamplerId AudioResamplerId_Libav = mkid::id32base36_5<'L', 'i', 'b', 'a', 'v'>::value; extern bool RegisterAudioResamplerFF_Man(); extern bool RegisterAudioResamplerLibav_Man(); void AudioResampler::registerAll() { static bool done = false; if (done) return; done = true; #if QTAV_HAVE(SWRESAMPLE) RegisterAudioResamplerFF_Man(); #endif #if QTAV_HAVE(AVRESAMPLE) RegisterAudioResamplerLibav_Man(); #endif } AudioResampler::AudioResampler(AudioResamplerPrivate& d):DPTR_INIT(&d) { } AudioResampler::~AudioResampler() { } QByteArray AudioResampler::outData() const { return d_func().data_out; } bool AudioResampler::prepare() { if (!inAudioFormat().isValid()) { qWarning("src audio parameters 'channel layout(or channels), sample rate and sample format must be set before initialize resampler"); return false; } return true; } bool AudioResampler::convert(const quint8 **data) { Q_UNUSED(data); return false; } void AudioResampler::setSpeed(qreal speed) { DPTR_D(AudioResampler); if (d.speed == speed) return; d.speed = speed; prepare(); } qreal AudioResampler::speed() const { return d_func().speed; } void AudioResampler::setInAudioFormat(const AudioFormat& format) { DPTR_D(AudioResampler); if (d.in_format == format) return; d.in_format = format; prepare(); } AudioFormat& AudioResampler::inAudioFormat() { return d_func().in_format; } const AudioFormat& AudioResampler::inAudioFormat() const { return d_func().in_format; } void AudioResampler::setOutAudioFormat(const AudioFormat& format) { DPTR_D(AudioResampler); if (d.out_format == format) return; d.out_format = format; prepare(); } AudioFormat& AudioResampler::outAudioFormat() { return d_func().out_format; } const AudioFormat& AudioResampler::outAudioFormat() const { return d_func().out_format; } void AudioResampler::setInSampesPerChannel(int samples) { d_func().in_samples_per_channel = samples; } int AudioResampler::outSamplesPerChannel() const { return d_func().out_samples_per_channel; } //channel count can be computed by av_get_channel_layout_nb_channels(chl) void AudioResampler::setInSampleRate(int isr) { AudioFormat af(d_func().in_format); af.setSampleRate(isr); setInAudioFormat(af); } void AudioResampler::setOutSampleRate(int osr) { AudioFormat af(d_func().out_format); af.setSampleRate(osr); setOutAudioFormat(af); } void AudioResampler::setInSampleFormat(int isf) { AudioFormat af(d_func().in_format); af.setSampleFormatFFmpeg(isf); setInAudioFormat(af); } void AudioResampler::setOutSampleFormat(int osf) { AudioFormat af(d_func().out_format); af.setSampleFormatFFmpeg(osf); setOutAudioFormat(af); } void AudioResampler::setInChannelLayout(qint64 icl) { AudioFormat af(d_func().in_format); af.setChannelLayoutFFmpeg(icl); setInAudioFormat(af); } void AudioResampler::setOutChannelLayout(qint64 ocl) { AudioFormat af(d_func().out_format); af.setChannelLayoutFFmpeg(ocl); setOutAudioFormat(af); } void AudioResampler::setInChannels(int channels) { AudioFormat af(d_func().in_format); af.setChannels(channels); setInAudioFormat(af); } void AudioResampler::setOutChannels(int channels) { AudioFormat af(d_func().out_format); af.setChannels(channels); setOutAudioFormat(af); } } //namespace QtAV QtAV-1.12.0/src/AudioResamplerFF.cpp000066400000000000000000000021251312235004300170220ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #define BUILD_SWR #include "AudioResamplerTemplate.cpp" QtAV-1.12.0/src/AudioResamplerLibav.cpp000066400000000000000000000021661312235004300175710ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #define QTAV_HAVE_SWR_AVR_MAP 1 #define BUILD_AVR #include "AudioResamplerTemplate.cpp" QtAV-1.12.0/src/AudioResamplerTemplate.cpp000066400000000000000000000277351312235004300203200ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #if defined(BUILD_AVR) || defined(BUILD_SWR) // no this macro is fine too for qmake #include "QtAV/AudioResampler.h" #include "QtAV/private/AudioResampler_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { #if QTAV_HAVE(SWR_AVR_MAP) #define AudioResamplerFF AudioResamplerLibav #define AudioResamplerFFPrivate AudioResamplerLibavPrivate #define AudioResamplerId_FF AudioResamplerId_Libav #define RegisterAudioResamplerFF_Man RegisterAudioResamplerLibav_Man #define FF Libav static const char kName[] = "Libav"; #else static const char kName[] = "FFmpeg"; #endif class AudioResamplerFFPrivate; class AudioResamplerFF : public AudioResampler { DPTR_DECLARE_PRIVATE(AudioResampler) public: AudioResamplerFF(); virtual bool convert(const quint8** data); virtual bool prepare(); }; extern AudioResamplerId AudioResamplerId_FF; FACTORY_REGISTER(AudioResampler, FF, kName) class AudioResamplerFFPrivate : public AudioResamplerPrivate { public: AudioResamplerFFPrivate(): context(0) {} ~AudioResamplerFFPrivate() { if (context) { swr_free(&context); context = 0; } } SwrContext *context; // defined in swr<1 #ifndef SWR_CH_MAX #define SWR_CH_MAX 64 #endif int channel_map[SWR_CH_MAX]; }; AudioResamplerFF::AudioResamplerFF(): AudioResampler(*new AudioResamplerFFPrivate()) { } bool AudioResamplerFF::convert(const quint8 **data) { DPTR_D(AudioResamplerFF); /* * swr_get_delay: Especially when downsampling by a large value, the output sample rate may be a poor choice to represent * the delay, similarly upsampling and the input sample rate. */ qreal osr = d.out_format.sampleRate(); if (!qFuzzyCompare(d.speed, 1.0)) osr /= d.speed; d.out_samples_per_channel = av_rescale_rnd( #if HAVE_SWR_GET_DELAY swr_get_delay(d.context, qMax(d.in_format.sampleRate(), d.out_format.sampleRate())) + #else 128 + //TODO: QtAV_Compat #endif //HAVE_SWR_GET_DELAY d.in_samples_per_channel //TODO: wanted_samples(ffplay mplayer2) , osr, d.in_format.sampleRate(), AV_ROUND_UP); //TODO: why crash for swr 0.5? //int out_size = av_samples_get_buffer_size(NULL/*out linesize*/, d.out_channels, d.out_samples_per_channel, (AVSampleFormat)d.out_sample_format, 0/*alignment default*/); int size_per_sample_with_channels = d.out_format.channels()*d.out_format.bytesPerSample(); int out_size = d.out_samples_per_channel*size_per_sample_with_channels; if (out_size > d.data_out.size()) d.data_out.resize(out_size); uint8_t *out[] = {(uint8_t*)d.data_out.data()}; // detach if implicitly shared by others //number of input/output samples available in one channel int converted_samplers_per_channel = swr_convert(d.context, out, d.out_samples_per_channel, data, d.in_samples_per_channel); d.out_samples_per_channel = converted_samplers_per_channel; if (converted_samplers_per_channel < 0) { qWarning("[AudioResamplerFF] %s", av_err2str(converted_samplers_per_channel)); return false; } //TODO: converted_samplers_per_channel==out_samples_per_channel means out_size is too small, see mplayer2 //converted_samplers_per_channel*d.out_channels*av_get_bytes_per_sample(d.out_sample_format) //av_samples_get_buffer_size(0, d.out_channels, converted_samplers_per_channel, d.out_sample_format, 0) //if (converted_samplers_per_channel != out_size) d.data_out.resize(converted_samplers_per_channel*size_per_sample_with_channels); return true; } /* *TODO: broken sample rate(AAC), see mplayer */ bool AudioResamplerFF::prepare() { DPTR_D(AudioResamplerFF); if (!d.in_format.isValid()) { qWarning("src audio parameters 'channel layout(or channels), sample rate and sample format must be set before initialize resampler"); return false; } //TODO: also in do this statistics if (!d.in_format.channels()) { if (!d.in_format.channelLayoutFFmpeg()) { //FIXME: already return d.in_format.setChannels(2); d.in_format.setChannelLayoutFFmpeg(av_get_default_channel_layout(d.in_format.channels())); //from mplayer2 qWarning("both channels and channel layout are not available, assume channels=%d, channel layout=%lld", d.in_format.channels(), d.in_format.channelLayoutFFmpeg()); } else { d.in_format.setChannels(av_get_channel_layout_nb_channels(d.in_format.channelLayoutFFmpeg())); } } if (!d.in_format.channels()) d.in_format.setChannels(2); //TODO: why av_get_channel_layout_nb_channels() may return 0? if (!d.in_format.channelLayoutFFmpeg()) { qWarning("channel layout not available, use default layout"); d.in_format.setChannelLayoutFFmpeg(av_get_default_channel_layout(d.in_format.channels())); } if (!d.out_format.channels()) { if (d.out_format.channelLayoutFFmpeg()) { d.out_format.setChannels(av_get_channel_layout_nb_channels(d.out_format.channelLayoutFFmpeg())); } else { d.out_format.setChannels(d.in_format.channels()); d.out_format.setChannelLayoutFFmpeg(d.in_format.channelLayoutFFmpeg()); } } if (d.out_format.channelLayout() == AudioFormat::ChannelLayout_Unsupported) { d.out_format.setChannels(d.in_format.channels()); d.out_format.setChannelLayoutFFmpeg(d.in_format.channelLayoutFFmpeg()); } //now we have out channels if (!d.out_format.channelLayoutFFmpeg()) d.out_format.setChannelLayoutFFmpeg(av_get_default_channel_layout(d.out_format.channels())); if (!d.out_format.sampleRate()) d.out_format.setSampleRate(inAudioFormat().sampleRate()); if (d.speed <= 0) d.speed = 1.0; //DO NOT set sample rate here, we should keep the original and multiply 1/speed when needed //if (d.speed != 1.0) // d.out_format.setSampleRate(int(qreal(d.out_format.sampleFormat())/d.speed)); qDebug("swr speed=%.2f", d.speed); //d.in_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.in_sample_format) ? d.in_channels : 1; //d.out_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.out_sample_format) ? d.out_channels : 1; if (d.context) swr_free(&d.context); //TODO: if no free(of cause free is required), why channel mapping and layout not work if change from left to stereo? //If use swr_alloc() need to set the parameters (av_opt_set_xxx() manually or with swr_alloc_set_opts()) before calling swr_init() d.context = swr_alloc_set_opts(d.context , d.out_format.channelLayoutFFmpeg() , (enum AVSampleFormat)outAudioFormat().sampleFormatFFmpeg() , qreal(outAudioFormat().sampleRate())/d.speed , d.in_format.channelLayoutFFmpeg() , (enum AVSampleFormat)inAudioFormat().sampleFormatFFmpeg() , inAudioFormat().sampleRate() , 0 /*log_offset*/, 0 /*log_ctx*/); /* av_opt_set_int(d.context, "in_channel_layout", d.in_channel_layout, 0); av_opt_set_int(d.context, "in_sample_rate", d.in_format.sampleRate(), 0); av_opt_set_sample_fmt(d.context, "in_sample_fmt", (enum AVSampleFormat)in_format.sampleFormatFFmpeg(), 0); av_opt_set_int(d.context, "out_channel_layout", d.out_channel_layout, 0); av_opt_set_int(d.context, "out_sample_rate", d.out_format.sampleRate(), 0); av_opt_set_sample_fmt(d.context, "out_sample_fmt", (enum AVSampleFormat)out_format.sampleFormatFFmpeg(), 0); */ qDebug("out: {cl: %lld, fmt: %s, freq: %d}" , d.out_format.channelLayoutFFmpeg() , qPrintable(d.out_format.sampleFormatName()) , d.out_format.sampleRate()); qDebug("in {cl: %lld, fmt: %s, freq: %d}" , d.in_format.channelLayoutFFmpeg() , qPrintable(d.in_format.sampleFormatName()) , d.in_format.sampleRate()); if (!d.context) { qWarning("Allocat swr context failed!"); return false; } //avresample 0.0.2(FFmpeg 0.11)~1.0.1(FFmpeg 1.1) has no channel mapping. but has remix matrix, so does swresample //TODO: why crash if use channel mapping for L or R? #if QTAV_HAVE(SWR_AVR_MAP) //LIBAVRESAMPLE_VERSION_INT < AV_VERSION_INT(1, 1, 0) bool remix = false; int in_c = d.in_format.channels(); int out_c = d.out_format.channels(); /* * matrix[i + stride * o] is the weight of input channel i in output channel o. */ double *matrix = 0; if (d.out_format.channelLayout() == AudioFormat::ChannelLayout_Left) { remix = true; matrix = (double*)calloc(in_c*out_c, sizeof(double)); for (int o = 0; o < out_c; ++o) { matrix[0 + in_c * o] = 1; } } if (d.out_format.channelLayout() == AudioFormat::ChannelLayout_Right) { remix = true; matrix = (double*)calloc(in_c*out_c, sizeof(double)); for (int o = 0; o < out_c; ++o) { matrix[1 + in_c * o] = 1; } } if (!remix && in_c < out_c) { remix = true; //double matrix[in_c*out_c]; //C99, VLA matrix = (double*)calloc(in_c*out_c, sizeof(double)); for (int i = 0, o = 0; o < out_c; ++o) { matrix[i + in_c * o] = 1; i = (i + i)%in_c; } } if (remix && matrix) { avresample_set_matrix(d.context, matrix, in_c); free(matrix); } #else bool use_channel_map = false; if (d.out_format.channelLayout() == AudioFormat::ChannelLayout_Left) { use_channel_map = true; memset(d.channel_map, 0, sizeof(d.channel_map)); for (int i = 0; i < d.out_format.channels(); ++i) { d.channel_map[i] = 0; } } if (d.out_format.channelLayout() == AudioFormat::ChannelLayout_Right) { use_channel_map = true; memset(d.channel_map, 0, sizeof(d.channel_map)); for (int i = 0; i < d.out_format.channels(); ++i) { d.channel_map[i] = 1; } } if (!use_channel_map && d.in_format.channels() < d.out_format.channels()) { use_channel_map = true; memset(d.channel_map, 0, sizeof(d.channel_map)); for (int i = 0; i < d.out_format.channels(); ++i) { d.channel_map[i] = i % d.in_format.channels(); } } if (use_channel_map) { av_opt_set_int(d.context, "icl", d.out_format.channelLayoutFFmpeg(), 0); //TODO: why crash if layout is mono and set uch(i.e. always the next line) av_opt_set_int(d.context, "uch", d.out_format.channels(), 0); swr_set_channel_mapping(d.context, d.channel_map); } #endif //QTAV_HAVE(SWR_AVR_MAP) int ret = swr_init(d.context); if (ret < 0) { qWarning("swr_init failed: %s", av_err2str(ret)); swr_free(&d.context); return false; } return true; } } //namespace QtAV #endif //defined(BUILD_AVR) || defined(BUILD_SWR) QtAV-1.12.0/src/AudioThread.cpp000066400000000000000000000401101312235004300160570ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "AudioThread.h" #include "AVThread_p.h" #include "QtAV/AudioDecoder.h" #include "QtAV/Packet.h" #include "QtAV/AudioFormat.h" #include "QtAV/AudioOutput.h" #include "QtAV/AudioResampler.h" #include "QtAV/AVClock.h" #include "QtAV/Filter.h" #include "output/OutputSet.h" #include "QtAV/private/AVCompat.h" #include #include #include "utils/Logger.h" namespace QtAV { class AudioThreadPrivate : public AVThreadPrivate { public: void init() { resample = false; last_pts = 0; } bool resample; qreal last_pts; //used when audio output is not available, to calculate the aproximate sleeping time }; AudioThread::AudioThread(QObject *parent) :AVThread(*new AudioThreadPrivate(), parent) { } void AudioThread::applyFilters(AudioFrame &frame) { DPTR_D(AudioThread); //QMutexLocker locker(&d.mutex); //Q_UNUSED(locker); if (!d.filters.isEmpty()) { //sort filters by format. vo->defaultFormat() is the last foreach (Filter *filter, d.filters) { AudioFilter *af = static_cast(filter); if (!af->isEnabled()) continue; af->apply(d.statistics, &frame); } } } /* *TODO: * if output is null or dummy, the use duration to wait */ void AudioThread::run() { DPTR_D(AudioThread); //No decoder or output. No audio output is ok, just display picture if (!d.dec || !d.dec->isAvailable() || !d.outputSet) return; resetState(); Q_ASSERT(d.clock != 0); d.init(); Packet pkt; qint64 fake_duration = 0LL; qint64 fake_pts = 0LL; int sync_id = 0; while (!d.stop) { processNextTask(); if (d.render_pts0 < 0) { // no pause when seeking if (tryPause()) { //DO NOT continue, or stepForward() will fail if (d.stop) break; //the queue is empty and may block. should setBlocking(false) wake up cond empty? } else { if (isPaused()) continue; } } if (d.seek_requested) { d.seek_requested = false; qDebug("request seek audio thread"); pkt = Packet(); // last decode failed and pkt is valid, reset pkt to force take the next packet if seek is requested msleep(1); } else { // d.render_pts0 < 0 means seek finished here if (d.clock->syncId() > 0) { qDebug("audio thread wait to sync end for sync id: %d", d.clock->syncId()); if (d.render_pts0 < 0 && sync_id > 0) { msleep(10); continue; } } else { sync_id = 0; } } if (!pkt.isValid()) { // can't seek back if eof packet is read //qDebug("eof pkt: %d valid: %d, aqueue size: %d, abuffer: %d %.3f %d, fake_duration: %lld", pkt.isEOF(), pkt.isValid(), d.packets.size(), d.packets.bufferValue(), d.packets.bufferMax(), d.packets.isFull(), fake_duration); // If seek requested but last decode failed if (!pkt.isEOF() && (fake_duration <= 0 || !d.packets.isEmpty())) { pkt = d.packets.take(); //wait to dequeue } if (pkt.isEOF()) { fake_duration = 0; //avoid endless wait qDebug("audio thread gets an eof packet. pkt.pts: %.3f, d.render_pts0:%.3f", pkt.pts, d.render_pts0); } if (!pkt.isValid()) { if (pkt.pts >= 0) { // check seek first qDebug("Invalid packet! flush audio codec context!!!!!!!! audio queue size=%d", d.packets.size()); QMutexLocker locker(&d.mutex); Q_UNUSED(locker); if (d.dec) //maybe set to null in setDecoder() d.dec->flush(); d.render_pts0 = pkt.pts; sync_id = pkt.position; qDebug("audio seek: %.3f, id: %d", d.render_pts0, sync_id); pkt = Packet(); //mark invalid to take next if (fake_duration > 0) { //qDebug("fake_duration update on seek: %ul + %ul - %.3f", fake_duration, fake_pts, d.render_pts0); fake_duration = fake_duration + fake_pts - d.render_pts0*1000.0; fake_pts = d.render_pts0*1000.0; } d.clock->updateValue(d.render_pts0); d.clock->updateDelay(0); continue; } if (pkt.duration > 0) { fake_duration = pkt.duration * 1000.0; fake_pts = d.last_pts*1000.0; pkt = Packet(); //mark invalid to avoid run here in the next loop //qDebug("get fake apkt: %.3f+%ul", pkt.pts, fake_duration); continue; } } if (fake_duration > 0) { static const ulong kSleepMs = 20; const ulong ms = qMin(fake_duration, kSleepMs); fake_duration -= ms; fake_pts += ms; //qDebug("fake_wait: %ul, fake_duration: %lld, delay: %.3f", ms, fake_duration, d.clock->delay()); d.clock->updateDelay(d.clock->delay() + qreal(ms)/1000.0); msleep(ms); continue; } } if (!pkt.isValid() && !pkt.isEOF()) // decode it will cause crash continue; qreal dts = pkt.dts; //FIXME: pts and dts // no key frame for audio. so if pts reaches, try decode and skip render if got frame pts does not reach bool skip_render = pkt.pts >= 0 && pkt.pts < d.render_pts0; // if audio stream is too short, seeking will fail and d.render_pts0 keeps >0 // audio has no key frame, skip rendering equals to skip decoding if (skip_render) { d.clock->updateValue(pkt.pts); /* * audio may be too fast than video if skip without sleep * a frame is about 20ms. sleep time must be << frame time */ qreal a_v = dts - d.clock->videoTime(); qDebug("skip audio decode at %f/%f v=%f a-v=%fms", dts, d.render_pts0, d.clock->videoTime(), a_v*1000.0); if (a_v > 0) { msleep(qMin((ulong)20, ulong(a_v*1000.0))); } else { // audio maybe too late compared with video packet before seeking backword. so just ignore msleep(0); //wait video seek done if audio done early } pkt = Packet(); //mark invalid to take next continue; } const bool is_external_clock = d.clock->clockType() == AVClock::ExternalClock; if (is_external_clock && !pkt.isEOF()) { d.delay = dts - d.clock->value(); /* *after seeking forward, a packet may be the old, v packet may be *the new packet, then the d.delay is very large, omit it. *TODO: 1. how to choose the value * 2. use last delay when seeking */ if (qAbs(d.delay) < 2.0) { if (d.delay < -kSyncThreshold) { //Speed up. drop frame? resample? qDebug("audio is late compared with external clock. skip decoding. %.3f-%.3f=%.3f", dts, d.clock->value(), d.delay); pkt = Packet(); //mark invalid to take next continue; } if (d.delay > 0) waitAndCheck(d.delay, dts); } else { //when to drop off? if (d.delay > 0) { msleep(64); } else { //audio packet not cleaned up? qDebug("audio is too late compared with external clock. skip decoding. %.3f-%.3f=%.3f", dts, d.clock->value(), d.delay); pkt = Packet(); //mark invalid to take next continue; } } } /* lock here to ensure decoder and ao can complete current work before they are changed * current packet maybe not supported by new decoder */ // TODO: smaller scope QMutexLocker locker(&d.mutex); Q_UNUSED(locker); AudioDecoder *dec = static_cast(d.dec); if (!dec) { pkt = Packet(); //mark invalid to take next continue; } AudioOutput *ao = 0; // first() is not null even if list empty if (!d.outputSet->outputs().isEmpty()) ao = static_cast(d.outputSet->outputs().first()); //DO NOT decode and convert if ao is not available or mute! bool has_ao = ao && ao->isAvailable(); //if (!has_ao) {//do not decode? // TODO: move resampler to AudioFrame, like VideoFrame does if (has_ao && dec->resampler()) { if (dec->resampler()->speed() != ao->speed() || dec->resampler()->outAudioFormat() != ao->audioFormat()) { //resample later to ensure thread safe. TODO: test if (d.resample) { qDebug() << "ao.format " << ao->audioFormat(); qDebug() << "swr.format " << dec->resampler()->outAudioFormat(); qDebug("decoder set speed: %.2f", ao->speed()); dec->resampler()->setOutAudioFormat(ao->audioFormat()); dec->resampler()->setSpeed(ao->speed()); dec->resampler()->prepare(); d.resample = false; } else { d.resample = true; } } } else { if (dec->resampler() && dec->resampler()->speed() != d.clock->speed()) { if (d.resample) { qDebug("decoder set speed: %.2f", d.clock->speed()); dec->resampler()->setSpeed(d.clock->speed()); dec->resampler()->prepare(); d.resample = false; } else { d.resample = true; } } } if (d.stop) { qDebug("audio thread stop before decode()"); break; } //qDebug("apkt: %.3f, %lld %p", pkt.pts, pkt.asAVPacket()->pts, pkt.asAVPacket()->data); if (!dec->decode(pkt)) { qWarning("Decode audio failed. undecoded: %d", dec->undecodedSize()); if (pkt.isEOF()) { qDebug("audio decode eof done"); Q_EMIT eofDecoded(); if (d.render_pts0 >= 0) { qDebug("audio seek done at eof pts: %.3f. id: %d", pkt.pts, sync_id); d.render_pts0 = -1; d.clock->syncEndOnce(sync_id); Q_EMIT seekFinished(qint64(pkt.pts*1000.0)); //TODO: pts } if (!pkt.position) break; } qreal dt = dts - d.last_pts; if (dt > 0.5 || dt < 0) { dt = 0; } if (!qFuzzyIsNull(dt)) { msleep((unsigned long)(dt*1000.0)); } pkt = Packet(); d.last_pts = d.clock->value(); //not pkt.pts! the delay is updated! continue; } // reduce here to ensure to decode the rest data in the next loop if (!pkt.isEOF()) pkt.skip(pkt.data.size() - dec->undecodedSize()); #if USE_AUDIO_FRAME AudioFrame frame(dec->frame()); if (!frame) continue; //pkt data is updated after decode, no reset here if (frame.timestamp() <= 0) frame.setTimestamp(pkt.pts); // pkt.pts is wrong. >= real timestamp if (d.render_pts0 >= 0.0) { // seeking d.clock->updateValue(frame.timestamp()); if (frame.timestamp() < d.render_pts0) { qDebug("skip audio rendering: %f-%f", frame.timestamp(), d.render_pts0); continue; //pkt data is updated after decode, no reset here } qDebug("audio seek finished @%.3f. id: %d", frame.timestamp(), sync_id); d.render_pts0 = -1.0; d.clock->syncEndOnce(sync_id); Q_EMIT seekFinished(qint64(frame.timestamp()*1000.0)); if (has_ao) { ao->clear(); } } if (has_ao) { applyFilters(frame); frame.setAudioResampler(dec->resampler()); //!!! // FIXME: resample ONCE is required for audio frames from ffmpeg //if (ao->audioFormat() != frame.format()) { frame = frame.to(ao->audioFormat()); //} } QByteArray decoded(frame.data()); #else QByteArray decoded(dec->data()); #endif int decodedSize = decoded.size(); int decodedPos = 0; qreal delay = 0; const qreal byte_rate = frame.format().bytesPerSecond(); qreal pts = frame.timestamp(); //qDebug("frame samples: %d @%.3f+%lld", frame.samplesPerChannel()*frame.channelCount(), frame.timestamp(), frame.duration()/1000LL); while (decodedSize > 0) { if (d.stop) { qDebug("audio thread stop after decode()"); break; } const int chunk = qMin(decodedSize, has_ao ? ao->bufferSize() : 512*frame.format().bytesPerFrame());//int(max_len*byte_rate)); //AudioFormat.bytesForDuration const qreal chunk_delay = (qreal)chunk/(qreal)byte_rate; if (has_ao && ao->isOpen()) { QByteArray decodedChunk = QByteArray::fromRawData(decoded.constData() + decodedPos, chunk); //qDebug("ao.timestamp: %.3f, pts: %.3f, pktpts: %.3f", ao->timestamp(), pts, pkt.pts); ao->play(decodedChunk, pts); if (!is_external_clock && ao->timestamp() > 0) {//TODO: clear ao buffer // const qreal da = qAbs(pts - ao->timestamp()); // if (da > 1.0) { // what if frame duration is long? // } // TODO: check seek_requested(atomic bool) d.clock->updateValue(ao->timestamp()); } } else { d.clock->updateDelay(delay += chunk_delay); /* * why need this even if we add delay? and usleep sounds weird * the advantage is if no audio device, the play speed is ok too * So is portaudio blocking the thread when playing? */ //TODO: avoid acummulative error. External clock? msleep((unsigned long)(chunk_delay * 1000.0)); } decodedPos += chunk; decodedSize -= chunk; pts += chunk_delay; pkt.pts += chunk_delay; // packet not fully decoded, use new pts in the next decoding pkt.dts += chunk_delay; } if (has_ao) emit frameDelivered(); d.last_pts = d.clock->value(); //not pkt.pts! the delay is updated! } d.packets.clear(); qDebug("Audio thread stops running..."); } } //namespace QtAV QtAV-1.12.0/src/AudioThread.h000066400000000000000000000027141312235004300155340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AUDIOTHREAD_H #define QAV_AUDIOTHREAD_H #include "AVThread.h" namespace QtAV { class AudioDecoder; class AudioFrame; class AudioThreadPrivate; class AudioThread : public AVThread { Q_OBJECT DPTR_DECLARE_PRIVATE(AudioThread) public: explicit AudioThread(QObject *parent = 0); protected: void applyFilters(AudioFrame& frame); virtual void run(); }; } //namespace QtAV #endif // QAV_AUDIOTHREAD_H QtAV-1.12.0/src/CMakeLists.txt000066400000000000000000000401651312235004300157340ustar00rootroot00000000000000set(MODULE QtAV) # TODO: translations, rpath, uchardet, x11extras, android include(CheckCCompilerFlag) include(CheckCXXSourceCompiles) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckLibraryExists) INCLUDE(CheckTypeSize) # set qt dirs first so we can find depends in qt install dir include_directories(${QTDIR}/include) #TODO: remove. use external/include get_filename_component(QTDIR "${QTDIR}" ABSOLUTE) list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/external/include) list(APPEND EXTRA_LIBS ${CMAKE_LIBRARY_PATH_FLAG}${CMAKE_SOURCE_DIR}/external/lib) if(APPLE) if(IOS) #set_xcode_property(myioslib IPHONEOS_DEPLOYMENT_TARGET "8.0") else() list(APPEND EXTRA_INCLUDE /usr/local/include) #macOS only list(APPEND EXTRA_LIBS -L/usr/local/lib) endif() endif() if(EXISTS ${CMAKE_SOURCE_DIR}/contrib/capi/capi.h) set(HAVE_CAPI 1) list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/capi) # TODO: only files use capi.h list(APPEND EXTRA_DEFS -DQTAV_HAVE_CAPI=1) endif() # check ffmpeg headers. assume libs exist. lib order matters if they are static set(AVMODULES avformat avcodec swscale avutil) # avdevice avfilter avresample swscale) # CMAKE_FIND_ROOT_PATH_BOTH: ensure find_file can search from given paths for cross compiling, may be set to ONLY in toolchain file foreach(av ${AVMODULES}) string(TOUPPER ${av} AV) find_file(HAVE_${AV} lib${av}/${av}.h HINTS ${EXTRA_INCLUDE} PATHS ${EXTRA_INCLUDE} CMAKE_FIND_ROOT_PATH_BOTH) message("HAVE_${AV}: ${HAVE_${AV}}") if(NOT ${HAVE_${AV}} MATCHES HAVE_${AV}-NOTFOUND) set(HAVE_${AV} 1) list(APPEND EXTRA_LIBS ${av}) list(APPEND EXTRA_DEFS "-DQTAV_HAVE_${AV}=1") else() message(FATAL_ERROR "${av} is required") endif() #check_include_file(lib${av}/${av}.h HAVE_${AV}) # -D__STDC_CONSTANT_MACROS is required! endforeach() foreach(av avfilter avdevice avresample swresample) string(TOUPPER ${av} AV) find_file(HAVE_${AV} lib${av}/${av}.h HINTS ${EXTRA_INCLUDE} PATHS ${EXTRA_INCLUDE} CMAKE_FIND_ROOT_PATH_BOTH) message("HAVE_${AV}: ${HAVE_${AV}}") if(NOT ${HAVE_${AV}} MATCHES HAVE_${AV}-NOTFOUND) set(HAVE_${AV} 1) list(APPEND EXTRA_LIBS ${av}) list(APPEND EXTRA_DEFS "-DQTAV_HAVE_${AV}=1") list(APPEND AVMODULES ${av}) endif() endforeach() if (NOT HAVE_SWRESAMPLE) if (NOT HAVE_AVRESAMPLE) message(FATAL_ERROR "swresample or avresample is required") endif() endif() list(APPEND EXTRA_DEFS -DBUILD_QTAV_LIB -D__STDC_CONSTANT_MACROS) check_include_file(ass/ass.h HAVE_ASS_H) if(HAVE_ASS_H) if(HAVE_CAPI) # dynamic load if(IOS OR ANDROID OR WindowsPhone OR WindowsStore) else() set(HAVE_ASS 1) endif() endif() if(NOT HAVE_ASS) # link to ass check_library_exists(ass ass_library_init "" HAVE_ASS) if(HAVE_ASS) set(DYNAMIC_ASS 0) list(APPEND EXTRA_DEFS -DCAPI_LINK_ASS) list(APPEND EXTRA_LIBS ass) endif() endif() endif() if(HAVE_ASS) list(APPEND EXTRA_DEFS -DQTAV_HAVE_LIBASS=1) list(APPEND SOURCES capi/ass_api.cpp subtitle/SubtitleProcessorLibASS.cpp ) endif() set(DYNAMIC_OPENAL ${HAVE_CAPI}) if(APPLE) set(HAVE_OPENAL 1) if(NOT DYNAMIC_OPENAL) list(APPEND EXTRA_LIBS "-framework OpenAL") endif() list(APPEND EXTRA_DEFS -DHEADER_OPENAL_PREFIX) else() set(OLD_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${EXTRA_INCLUDE}) check_include_files(AL/al.h HAVE_AL_H) set(CMAKE_REQUIRED_INCLUDES ${OLD_REQUIRED_INCLUDES}) if(HAVE_AL_H) if(HAVE_CAPI) #TODO: option(LINK_OPENAL...) set(HAVE_OPENAL 1) # DYNAMIC_OPENAL is set by user else() if(WIN32) check_library_exists(OpenAL alGetError "" HAVE_OPENAL) else() check_library_exists(openal alGetError "" HAVE_OPENAL) if(NOT HAVE_OPENAL) check_library_exists(OpenAL alGetError "" HAVE_OPENAL) # blackberry if(HAVE_OPENAL) list(APPEND EXTRA_LIBS OpenAL) endif() else() list(APPEND EXTRA_LIBS openal) endif() endif() if(HAVE_OPENAL) set(DYNAMIC_OPENAL 0) endif() endif() endif() endif() if(HAVE_OPENAL AND NOT DYNAMIC_OPENAL) list(APPEND EXTRA_DEFS -DCAPI_LINK_OPENAL) endif() if(HAVE_OPENAL) list(APPEND SOURCES capi/openal_api.cpp output/audio/AudioOutputOpenAL.cpp ) endif() if (APPLE) set(CMAKE_CXX_SYSTEM_FRAMEWORK_SEARCH_FLAG "-F ") # original is "-iframework", lots of build errors set(CMAKE_C_SYSTEM_FRAMEWORK_SEARCH_FLAG "-F ") set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} avcodec) check_c_source_compiles( "#include int main() { av_videotoolbox_alloc_context(); return 0; }" HAVE_VIDEOTOOLBOX) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) if (HAVE_VIDEOTOOLBOX) list(APPEND EXTRA_DEFS -DQTAV_HAVE_VIDEOTOOLBOX=1) list(APPEND EXTRA_LIBS "-framework CoreMedia" "-framework VideoToolbox" "-framework CoreVideo") list(APPEND SOURCES codec/video/VideoDecoderVideoToolbox.cpp codec/video/SurfaceInteropCV.cpp ) if (IOS) list(APPEND SOURCES codec/video/SurfaceInteropCVOpenGLES.mm ) else() list(APPEND SOURCES codec/video/SurfaceInteropIOSurface.cpp ) endif() endif() list(APPEND SOURCES output/audio/AudioOutputAudioToolbox.cpp) list(APPEND EXTRA_LIBS "-framework CoreFoundation" "-framework AudioToolbox" "-framework OpenGL") endif() set(DEP_H OPENAL VIDEOTOOLBOX) foreach (D ${DEP_H}) if (${D}) ADD_DEFINITIONS(-DQTAV_HAVE_${D}=1) endif() endforeach() if (HAVE_AVRESAMPLE) list(APPEND SOURCES AudioResamplerLibav.cpp) endif() if (HAVE_SWRESAMPLE) list(APPEND SOURCES AudioResamplerFF.cpp) endif() set(HAVE_EGL 0) set(DYNAMIC_GL 0) set(OPENGL 0) #desktop opengl only set(OPENGLES 0) #glesv2 only set(HAVE_OPENGL 1) # qt with opengl enabled. 1 of DYNAMIC_GL, OPENGL and OPENGLES is true #get_target_property(QtGui_location Qt5::Gui LOCATION) #dll path if(Qt5Gui_OPENGL_IMPLEMENTATION STREQUAL GL) # windows always set to GL if(EXISTS ${QT_INSTALL_HEADERS}/QtANGLE) set(DYNAMIC_GL 1) # assume Qt5Gui_OPENGL_IMPLEMENTATION is not GL if build with ANGLE only if(NOT DEFINED Qt5Gui_EGL_INCLUDE_DIRS) set(Qt5Gui_EGL_INCLUDE_DIRS ${QT_INSTALL_HEADERS}/QtANGLE) endif() else() set(OPENGL 1) endif() if(DEFINED Qt5Gui_EGL_LIBRARIES) # x11? set(HAVE_EGL 1) endif() else() set(OPENGLES 1) endif() if(OPENGLES OR DYNAMIC_GL) set(HAVE_EGL 1) endif() if(NOT OPENGL AND NOT OPENGLES AND NOT DYNAMIC_GL) set(HAVE_OPENGL 0) endif() if(HAVE_OPENGL) if(WIN32 AND NOT DYNAMIC_GL AND NOT OPENGLES) # msys2 find_package(OpenGL REQUIRED) list(APPEND EXTRA_LIBS ${OPENGL_gl_LIBRARY}) endif() endif() file(GLOB SDK_HEADERS QtAV/*.h) list(APPEND SDK_HEADERS QtAV/QtAV) file(GLOB SDK_PRIVATE_HEADERS QtAV/private/*.h) list(APPEND SOURCES AVCompat.cpp QtAV_Global.cpp subtitle/CharsetDetector.cpp subtitle/PlainText.cpp subtitle/PlayerSubtitle.cpp subtitle/Subtitle.cpp subtitle/SubtitleProcessor.cpp subtitle/SubtitleProcessorFFmpeg.cpp subtitle/SubImage.cpp utils/GPUMemCopy.cpp utils/Logger.cpp AudioThread.cpp utils/internal.cpp AVThread.cpp AudioFormat.cpp AudioFrame.cpp AudioResampler.cpp AudioResamplerTemplate.cpp codec/audio/AudioDecoder.cpp codec/audio/AudioDecoderFFmpeg.cpp codec/audio/AudioEncoder.cpp codec/audio/AudioEncoderFFmpeg.cpp codec/AVDecoder.cpp codec/AVEncoder.cpp AVMuxer.cpp AVDemuxer.cpp AVDemuxThread.cpp ColorTransform.cpp Frame.cpp FrameReader.cpp filter/Filter.cpp filter/FilterContext.cpp filter/FilterManager.cpp filter/LibAVFilter.cpp filter/SubtitleFilter.cpp filter/EncodeFilter.cpp ImageConverter.cpp ImageConverterFF.cpp Packet.cpp PacketBuffer.cpp AVError.cpp AVPlayer.cpp AVPlayerPrivate.cpp AVTranscoder.cpp AVClock.cpp VideoCapture.cpp VideoFormat.cpp VideoFrame.cpp io/MediaIO.cpp io/QIODeviceIO.cpp output/audio/AudioOutput.cpp output/audio/AudioOutputBackend.cpp output/audio/AudioOutputNull.cpp output/video/VideoRenderer.cpp output/video/VideoOutput.cpp output/video/QPainterRenderer.cpp output/AVOutput.cpp output/OutputSet.cpp Statistics.cpp codec/video/VideoDecoder.cpp codec/video/VideoDecoderFFmpegBase.cpp codec/video/VideoDecoderFFmpeg.cpp codec/video/VideoDecoderFFmpegHW.cpp codec/video/VideoEncoder.cpp codec/video/VideoEncoderFFmpeg.cpp VideoThread.cpp VideoFrameExtractor.cpp ) if(HAVE_OPENGL) aux_source_directory(opengl SRC_OPENGL) list(APPEND SOURCES ${SRC_OPENGL}) list(APPEND SOURCES filter/GLSLFilter.cpp output/video/OpenGLRendererBase.cpp ) if(NOT Qt5Gui_VERSION VERSION_LESS 5.4.0) list(APPEND SDK_HEADERS QtAV/OpenGLWindowRenderer.h) list(APPEND SOURCES output/video/OpenGLWindowRenderer.cpp) endif() endif() if(HAVE_EGL) list(APPEND SOURCES capi/egl_api.cpp) # TODO: X11Extras if(DEFINED Qt5Gui_EGL_INCLUDE_DIRS) # ANGLE list(APPEND EXTRA_INCLUDE ${Qt5Gui_EGL_INCLUDE_DIRS}) #list(APPEND EXTRA_DEFS -DQTAV_HAVE_QT_EGL=1) if(HAVE_CAPI AND NOT IOS) list(APPEND EXTRA_DEFS -DQTAV_HAVE_EGL_CAPI=1) if(WindowsPhone OR WindowsStore) list(APPEND EXTRA_DEFS -DCAPI_LINK_EGL) endif() endif() endif() endif() if(WIN32 OR WindowsStore OR WindowsPhone) check_include_files(XAudio2.h HAVE_XAUDIO2_H) if(NOT HAVE_XAUDIO2_H) list(APPEND EXTRA_INCLUDE ${CMAKE_SOURCE_DIR}/contrib/dxsdk) endif() message("Qt5Gui_EGL_INCLUDE_DIRS: ${Qt5Gui_EGL_INCLUDE_DIRS}") list(APPEND HEADERS output/audio/xaudio2_compat.h codec/video/VideoDecoderD3D.h ) list(APPEND SOURCES utils/DirectXHelper.cpp output/audio/AudioOutputXAudio2.cpp codec/video/VideoDecoderD3D.cpp ) list(APPEND EXTRA_DEFS -DQTAV_HAVE_XAUDIO2=1) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} avcodec) # TODO: lib dir set(OLD_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${EXTRA_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}) check_cxx_source_compiles(" #include \"directx/dxcompat.h\" #include extern \"C\" { #include } #include //ComPtr is used in QtAV int main() { av_d3d11va_alloc_context(); D3D11_VIDEO_PROCESSOR_STREAM s; //used by vp return 0; } " HAVE_D3D11) set(CMAKE_REQUIRED_INCLUDES ${OLD_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) if(HAVE_D3D11) # https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/ (global and specified target) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) list(APPEND EXTRA_DEFS -DQTAV_HAVE_D3D11VA=1) list(APPEND SOURCES codec/video/VideoDecoderD3D11.cpp directx/SurfaceInteropD3D11.cpp directx/D3D11VP.cpp ) if(HAVE_EGL) list(APPEND SOURCES directx/SurfaceInteropD3D11EGL.cpp) endif() if(HAVE_OPENGL AND NOT OPENGLES) # dynamic gl or desktop list(APPEND SOURCES directx/SurfaceInteropD3D11GL.cpp) endif() endif() if(WindowsStore OR WindowsPhone) list(APPEND SOURCES io/WinRTIO.cpp) list(APPEND EXTRA_LIBS xaudio2 #only for xbox or >=win8 shcore ) if(HAVE_D3D11) list(APPEND EXTRA_LIBS d3d11) endif() else() list(APPEND EXTRA_LIBS ole32 user32) #ole32: CoInitializeEx for vs2008, but can not find the symbol at runtime list(APPEND SOURCES output/audio/AudioOutputDSound.cpp directx/SurfaceInteropD3D9.cpp codec/video/VideoDecoderDXVA.cpp ) if(HAVE_EGL) list(APPEND SOURCES directx/SurfaceInteropD3D9EGL.cpp) endif() if(OPENGL OR DYNAMIC_GL) list(APPEND SOURCES directx/SurfaceInteropD3D9GL.cpp) endif() list(APPEND EXTRA_DEFS -DQTAV_HAVE_DSOUND=1 -DQTAV_HAVE_DXVA=1 ) endif() endif() check_library_exists(portaudio Pa_Initialize "" HAVE_PORTAUDIO) if(HAVE_PORTAUDIO) list(APPEND SOURCES output/audio/AudioOutputPortAudio.cpp) list(APPEND EXTRA_DEFS -DQTAV_HAVE_PORTAUDIO=1) list(APPEND EXTRA_LIBS portaudio) endif() check_library_exists(pulse pa_threaded_mainloop_new "" HAVE_PULSE) if(HAVE_PULSE) list(APPEND SOURCES output/audio/AudioOutputPulse.cpp) list(APPEND EXTRA_DEFS -DQTAV_HAVE_PULSEAUDIO=1) list(APPEND EXTRA_LIBS pulse) endif() check_library_exists(OpenSLES slCreateEngine "" HAVE_OPENSL) if(HAVE_OPENSL) list(APPEND SOURCES output/audio/AudioOutputOpenSL.cpp) list(APPEND EXTRA_DEFS -DQTAV_HAVE_OPENSL=1) list(APPEND EXTRA_LIBS OpenSLES) endif() check_library_exists(va vaInitialize "" HAVE_VAAPI) if(HAVE_VAAPI) list(APPEND SOURCES vaapi/vaapi_helper.cpp vaapi/SurfaceInteropVAAPI.cpp codec/video/VideoDecoderVAAPI.cpp ) list(APPEND EXTRA_DEFS -DQTAV_HAVE_VAAPI=1) list(APPEND EXTRA_LIBS va X11) endif() if(NOT APPLE) list(APPEND EXTRA_DEFS -DQTAV_HAVE_CUDA=1) list(APPEND EXTRA_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/cuda) list(APPEND SOURCES cuda/cuda_api.cpp codec/video/SurfaceInteropCUDA.cpp codec/video/VideoDecoderCUDA.cpp ) endif() list(APPEND HEADERS ${SDK_HEADERS} ${SDK_PRIVATE_HEADERS} AVPlayerPrivate.h AVDemuxThread.h AVThread.h AVThread_p.h AudioThread.h PacketBuffer.h VideoThread.h ImageConverter.h ImageConverter_p.h codec/video/VideoDecoderFFmpegBase.h codec/video/VideoDecoderFFmpegHW.h codec/video/VideoDecoderFFmpegHW_p.h filter/FilterManager.h subtitle/CharsetDetector.h subtitle/PlainText.h utils/BlockingQueue.h utils/GPUMemCopy.h utils/Logger.h utils/SharedPtr.h utils/ring.h utils/internal.h output/OutputSet.h ColorTransform.h ) # TODO: rc template SET(RESOURCES ${MODULE}.qrc shaders/shaders.qrc) SOURCE_GROUP("Resources" FILES ${RESOURCES}) QT5_ADD_RESOURCES(RESOURCES_SOURCES ${RESOURCES}) set_source_files_properties(${RESOURCES_SOURCES} PROPERTIES GENERATED ON) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() # add HEADERS for moc add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) list(REMOVE_DUPLICATES EXTRA_LIBS) # qt5_use_modules(since 2.8.9) is deprecated since cmake 2.8.11 target_link_libraries(${MODULE} LINK_PRIVATE ${EXTRA_LIBS} LINK_PUBLIC Qt5::Core Qt5::Gui # will add include dirs, macros etc. ) set_target_properties(${MODULE} PROPERTIES MACOSX_RPATH ON FRAMEWORK ON # MACOSX_FRAMEWORK_INFO_PLIST qtav-Info.plist #PUBLIC_HEADER ${SDK_HEADERS} #PRIVATE_HEADER ${SDK_PRIVATE_HEADERS} XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" VERSION ${PROJECT_VERSION} SOVERSION ${SO_VERSION} OUTPUT_NAME ${MODULE} CLEAN_DIRECT_OUTPUT 1 #LINK_SEARCH_START_STATIC 1 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) #http://stackoverflow.com/questions/19866424/cmake-and-qt5-automoc-error #http://doc.qt.io/qt-5/cmake-manual.html set(CMAKE_INCLUDE_CURRENT_DIR ON) # for .moc target_include_directories(${MODULE} PUBLIC $ PUBLIC $ PUBLIC $ PUBLIC $ PRIVATE ${EXTRA_INCLUDE} ) target_compile_definitions(${MODULE} PRIVATE ${EXTRA_DEFS}) install(FILES ${SDK_HEADERS} DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE} ) install(FILES ${SDK_PRIVATE_HEADERS} DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE}/private ) install(TARGETS ${MODULE} EXPORT ${MODULE}-targets #PUBLIC_HEADER DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE} # list is too long #PRIVATE_HEADER DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE}/private RUNTIME DESTINATION ${QTAV_INSTALL_BINS} LIBRARY DESTINATION ${QTAV_INSTALL_LIBS} ARCHIVE DESTINATION ${QTAV_INSTALL_LIBS} FRAMEWORK DESTINATION ${QTAV_INSTALL_LIBS} ) # FIXME: why QTAV_INCLUDE_DIRS is empty? install(EXPORT ${MODULE}-targets DESTINATION ${QTAV_INSTALL_LIBS}/cmake/${MODULE} FILE ${MODULE}-config.cmake ) QtAV-1.12.0/src/ColorTransform.cpp000066400000000000000000000302601312235004300166450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "ColorTransform.h" #include namespace QtAV { // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html static const QMatrix4x4 kXYZ2sRGB(3.2404542f, -1.5371385f, -0.4985314f, 0.0f, -0.9692660f, 1.8760108f, 0.0415560f, 0.0f, 0.0556434f, -0.2040259f, 1.0572252f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); // http://www.cs.utah.edu/~halzahaw/CS7650/Project2/project2_index.html no gamma correction static const QMatrix4x4 kXYZ_RGB(2.5623f, -1.1661f, -0.3962f, 0.0f, -1.0215f, 1.9778f, 0.0437f, 0.0f, 0.0752f, -0.2562f, 1.1810f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); static const QMatrix4x4 kGBR2RGB = QMatrix4x4(0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1); static const QMatrix4x4 yuv2rgb_bt601 = QMatrix4x4( 1.0f, 0.000f, 1.402f, 0.0f, 1.0f, -0.344f, -0.714f, 0.0f, 1.0f, 1.772f, 0.000f, 0.0f, 0.0f, 0.000f, 0.000f, 1.0f) * QMatrix4x4( 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f) ; static const QMatrix4x4 yuv2rgb_bt709 = QMatrix4x4( 1.0f, 0.000f, 1.5701f, 0.0f, 1.0f, -0.187f, -0.4664f, 0.0f, 1.0f, 1.8556f, 0.000f, 0.0f, 0.0f, 0.000f, 0.000f, 1.0f) * QMatrix4x4( 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f) ; const QMatrix4x4& ColorTransform::YUV2RGB(ColorSpace cs) { switch (cs) { case ColorSpace_BT601: return yuv2rgb_bt601; case ColorSpace_BT709: return yuv2rgb_bt709; default: return yuv2rgb_bt601; } return yuv2rgb_bt601; } // For yuv->rgb, assume yuv is full range before convertion, rgb is full range after convertion. so if input yuv is limited range, transform to full range first. If display rgb is limited range, transform to limited range at last. // *ColorRangeYUV(...) static QMatrix4x4 ColorRangeYUV(ColorRange from, ColorRange to) { if (from == to) return QMatrix4x4(); static const qreal Y2 = 235, Y1 = 16, C2 = 240, C1 = 16; static const qreal s = 255; //TODO: can be others if (from == ColorRange_Limited) { //TODO: Unknown. But what if realy want unknown? qDebug("input yuv limited range"); // [Y1, Y2] => [0, s] QMatrix4x4 m; m.scale(s/(Y2 - Y1), s/(C2 - C1), s/(C2 - C1)); m.translate(-Y1/s, -C1/s, -C1/s); return m; } if (from == ColorRange_Full) { // [0, s] => [Y1, Y2] QMatrix4x4 m; m.translate(Y1/s, C1/s, C1/s); m.scale((Y2 - Y1)/s, (C2 - C1)/s, (C2 - C1)/s); return m; } // ColorRange_Unknown return QMatrix4x4(); } // ColorRangeRGB(...)* static QMatrix4x4 ColorRangeRGB(ColorRange from, ColorRange to) { if (from == to) return QMatrix4x4(); static const qreal R2 = 235, R1 = 16; static const qreal s = 255; if (to == ColorRange_Limited) { qDebug("output rgb limited range"); QMatrix4x4 m; m.translate(R1/s, R1/s, R1/s); m.scale((R2 - R1)/s, (R2 - R1)/s, (R2 - R1)/s); return m; } if (to == ColorRange_Full) { // TODO: Unknown QMatrix4x4 m; m.scale(s/(R2 - R1), s/(R2 - R1), s/(R2 - R1)); m.translate(-s/R1, -s/R1, -s/R1); return m; } return QMatrix4x4(); } class ColorTransform::Private : public QSharedData { public: Private() : recompute(true) , cs_in(ColorSpace_RGB) , cs_out(ColorSpace_RGB) , range_in(ColorRange_Limited) , range_out(ColorRange_Full) , hue(0) , saturation(0) , contrast(0) , brightness(0) , bpc_scale(1.0) , a_bpc_scale(false) {} Private(const Private& other) : QSharedData(other) , recompute(true) , cs_in(ColorSpace_RGB) , cs_out(ColorSpace_RGB) , range_in(ColorRange_Limited) , range_out(ColorRange_Full) , hue(0) , saturation(0) , contrast(0) , brightness(0) , bpc_scale(1.0) , a_bpc_scale(false) {} ~Private() {} void reset() { recompute = true; //cs_in = cs_out = ColorSpace_RGB; /// //range_in = range_out = ColorRange_Unknown; hue = 0; saturation = 0; contrast = 0; brightness = 0; bpc_scale = 1.0; a_bpc_scale = false; M.setToIdentity(); } // TODO: optimize for other color spaces void compute() const { recompute = false; //http://docs.rainmeter.net/tips/colormatrix-guide //http://www.graficaobscura.com/matrix/index.html //http://beesbuzz.biz/code/hsv_color_transforms.php // ?? const float b = brightness; // brightness R,G,B QMatrix4x4 B(1, 0, 0, b, 0, 1, 0, b, 0, 0, 1, b, 0, 0, 0, 1); // Contrast (offset) R,G,B const float c = contrast+1.0; const float t = (1.0 - c) / 2.0; QMatrix4x4 C(c, 0, 0, t, 0, c, 0, t, 0, 0, c, t, 0, 0, 0, 1); // Saturation const float wr = 0.3086f; const float wg = 0.6094f; const float wb = 0.0820f; float s = saturation + 1.0f; QMatrix4x4 S( (1.0f - s)*wr + s, (1.0f - s)*wg , (1.0f - s)*wb , 0.0f, (1.0f - s)*wr , (1.0f - s)*wg + s, (1.0f - s)*wb , 0.0f, (1.0f - s)*wr , (1.0f - s)*wg , (1.0f - s)*wb + s, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); // Hue const float n = 1.0f / sqrtf(3.0f); // normalized hue rotation axis: sqrt(3)*(1 1 1) const float h = hue*M_PI; // hue rotation angle const float hc = cosf(h); const float hs = sinf(h); QMatrix4x4 H( // conversion of angle/axis representation to matrix representation n*n*(1.0f - hc) + hc , n*n*(1.0f - hc) - n*hs, n*n*(1.0f - hc) + n*hs, 0.0f, n*n*(1.0f - hc) + n*hs, n*n*(1.0f - hc) + hc , n*n*(1.0f - hc) - n*hs, 0.0f, n*n*(1.0f - hc) - n*hs, n*n*(1.0f - hc) + n*hs, n*n*(1.0f - hc) + hc , 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); // B*C*S*H*rgb_range_mat(*yuv2rgb*yuv_range_mat)*bpc_scale M = B*C*S*H; // M *= rgb_range_translate*rgb_range_scale // TODO: transform to output color space other than RGB switch (cs_out) { case ColorSpace_XYZ: M = kXYZ2sRGB.inverted() * M; break; case ColorSpace_RGB: M *= ColorRangeRGB(ColorRange_Full, range_out); break; case ColorSpace_GBR: M *= ColorRangeRGB(ColorRange_Full, range_out); M = kGBR2RGB.inverted() * M; break; default: M = YUV2RGB(cs_out).inverted() * M; break; } switch (cs_in) { case ColorSpace_XYZ: M *= kXYZ2sRGB; break; case ColorSpace_RGB: break; case ColorSpace_GBR: M *= kGBR2RGB; break; default: M *= YUV2RGB(cs_in)*ColorRangeYUV(range_in, ColorRange_Full); break; } if (bpc_scale != 1.0 && cs_in != ColorSpace_XYZ) { // why no range correction for xyz? //qDebug("bpc scale: %f", bpc_scale); M *= QMatrix4x4(bpc_scale, 0, 0, 0, 0, bpc_scale, 0, 0, 0, 0, bpc_scale, 0, 0, 0, 0, a_bpc_scale ? bpc_scale : 1); // scale alpha channel too } //qDebug() << "color mat: " << M; } mutable bool recompute; ColorSpace cs_in, cs_out; ColorRange range_in, range_out; qreal hue, saturation, contrast, brightness; qreal bpc_scale; bool a_bpc_scale; mutable QMatrix4x4 M; // count the transformations between spaces }; ColorTransform::ColorTransform() : d(new Private()) { } ColorTransform::~ColorTransform() { } ColorSpace ColorTransform::inputColorSpace() const { return d->cs_in; } void ColorTransform::setInputColorSpace(ColorSpace cs) { if (d->cs_in == cs) return; d->cs_in = cs; d->recompute = true; //TODO: only recompute color space transform } ColorSpace ColorTransform::outputColorSpace() const { return d->cs_out; } void ColorTransform::setOutputColorSpace(ColorSpace cs) { if (d->cs_out == cs) return; d->cs_out = cs; d->recompute = true; //TODO: only recompute color space transform } ColorRange ColorTransform::inputColorRange() const { return d->range_in; } void ColorTransform::setInputColorRange(ColorRange value) { if (d->range_in == value) return; d->range_in = value; d->recompute = true; } ColorRange ColorTransform::outputColorRange() const { return d->range_out; } void ColorTransform::setOutputColorRange(ColorRange value) { if (d->range_out == value) return; d->range_out = value; d->recompute = true; } QMatrix4x4 ColorTransform::matrix() const { if (d->recompute) d->compute(); return d->M; } const QMatrix4x4& ColorTransform::matrixRef() const { if (d->recompute) d->compute(); return d->M; } /*! * \brief reset * set to identity */ void ColorTransform::reset() { d->reset(); } void ColorTransform::setBrightness(qreal brightness) { if (d->brightness == brightness) return; d->brightness = brightness; d->recompute = true; } qreal ColorTransform::brightness() const { return d->brightness; } // -1~1 void ColorTransform::setHue(qreal hue) { if (d->hue == hue) return; d->hue = hue; d->recompute = true; } qreal ColorTransform::hue() const { return d->hue; } void ColorTransform::setContrast(qreal contrast) { if (d->contrast == contrast) return; d->contrast = contrast; d->recompute = true; } qreal ColorTransform::contrast() const { return d->contrast; } void ColorTransform::setSaturation(qreal saturation) { if (d->saturation == saturation) return; d->saturation = saturation; d->recompute = true; } qreal ColorTransform::saturation() const { return d->saturation; } void ColorTransform::setChannelDepthScale(qreal value, bool scaleAlpha) { if (d->bpc_scale == value && d->a_bpc_scale == scaleAlpha) return; qDebug("ColorTransform bpc_scale %f=>%f, scale alpha: %d=>%d", d->bpc_scale, value, d->a_bpc_scale, scaleAlpha); d->bpc_scale = value; d->a_bpc_scale = scaleAlpha; d->recompute = true; } } //namespace QtAV QtAV-1.12.0/src/ColorTransform.h000066400000000000000000000073371312235004300163230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_COLORTRANSFORM_H #define QTAV_COLORTRANSFORM_H #include #include #include // TODO: kernel QGenericMatrix //http://www.graficaobscura.com/matrix/index.html namespace QtAV { /*! * \brief The ColorTransform class * A convenience class to get color transformation matrix. * Implicitly shared. */ class ColorTransform { public: //http://msdn.microsoft.com/en-us/library/dd206750.aspx // cs: BT601 or BT709 static const QMatrix4x4& YUV2RGB(ColorSpace cs); ColorTransform(); ~ColorTransform(); //required by QSharedDataPointer if Private is forward declared /*! * \brief inputColorSpace * if inputColorSpace is different from outputColorSpace, then the result matrix(), matrixRef() and * matrixData() will count the transformation between in/out color space. * default in/output color space is rgb * \param cs */ ColorSpace inputColorSpace() const; void setInputColorSpace(ColorSpace cs); ColorSpace outputColorSpace() const; void setOutputColorSpace(ColorSpace cs); /// Currently assume input is yuv, output is rgb void setInputColorRange(ColorRange value); ColorRange inputColorRange() const; void setOutputColorRange(ColorRange value); ColorRange outputColorRange() const; /*! * \brief matrix * \return result matrix to transform from inputColorSpace to outputColorSpace with given brightness, * contrast, saturation and hue */ QMatrix4x4 matrix() const; const QMatrix4x4& matrixRef() const; /*! * Get the matrix in column-major order. Used by OpenGL */ template void matrixData(T* M) const { const QMatrix4x4 &m = matrixRef(); M[0] = m(0,0), M[4] = m(0,1), M[8] = m(0,2), M[12] = m(0,3), M[1] = m(1,0), M[5] = m(1,1), M[9] = m(1,2), M[13] = m(1,3), M[2] = m(2,0), M[6] = m(2,1), M[10] = m(2,2), M[14] = m(2,3), M[3] = m(3,0), M[7] = m(3,1), M[11] = m(3,2), M[15] = m(3,3); } /*! * \brief reset * only set in-space transform to identity. other parameters such as in/out color space does not change */ void reset(); // -1~1 void setBrightness(qreal brightness); qreal brightness() const; // -1~1 void setHue(qreal hue); qreal hue() const; // -1~1 void setContrast(qreal contrast); qreal contrast() const; // -1~1 void setSaturation(qreal saturation); qreal saturation() const; void setChannelDepthScale(qreal value, bool scaleAlpha = false); private: class Private; QSharedDataPointer d; }; } //namespace QtAV #endif // QTAV_COLORTRANSFORM_H QtAV-1.12.0/src/Frame.cpp000066400000000000000000000117251312235004300147320ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Frame.h" #include "QtAV/private/Frame_p.h" #include "utils/Logger.h" namespace QtAV { Frame::Frame(const Frame &other) :d_ptr(other.d_ptr) { } Frame::Frame(FramePrivate *d): d_ptr(d) { } Frame::~Frame() { } Frame &Frame::operator =(const Frame &other) { d_ptr = other.d_ptr; return *this; } int Frame::bytesPerLine(int plane) const { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return 0; } return d_func()->line_sizes[plane]; } QByteArray Frame::frameData() const { return d_func()->data; } QByteArray Frame::data(int plane) const { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return QByteArray(); } return QByteArray((char*)d_func()->planes[plane], bytesPerLine(plane)); } uchar* Frame::bits(int plane) { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return 0; } return d_func()->planes[plane]; } const uchar* Frame::constBits(int plane) const { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return 0; } return d_func()->planes[plane]; } void Frame::setBits(uchar *b, int plane) { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return; } Q_D(Frame); d->planes[plane] = b; } void Frame::setBits(const QVector &b) { Q_D(Frame); const int nb_planes = planeCount(); d->planes = b; if (d->planes.size() > nb_planes) { d->planes.reserve(nb_planes); d->planes.resize(nb_planes); } } void Frame::setBits(quint8 *slice[]) { for (int i = 0; i < planeCount(); ++i ) { setBits(slice[i], i); } } void Frame::setBytesPerLine(int lineSize, int plane) { if (plane < 0 || plane >= planeCount()) { qWarning("Invalid plane! Valid range is [0, %d)", planeCount()); return; } Q_D(Frame); d->line_sizes[plane] = lineSize; } void Frame::setBytesPerLine(const QVector &lineSize) { Q_D(Frame); const int nb_planes = planeCount(); d->line_sizes = lineSize; if (d->line_sizes.size() > nb_planes) { d->line_sizes.reserve(nb_planes); d->line_sizes.resize(nb_planes); } } void Frame::setBytesPerLine(int stride[]) { for (int i = 0; i < planeCount(); ++i ) { setBytesPerLine(stride[i], i); } } int Frame::planeCount() const { Q_D(const Frame); return d->planes.size(); } int Frame::channelCount() const { return planeCount(); } /*! Returns any extra metadata associated with this frame. */ QVariantMap Frame::availableMetaData() const { Q_D(const Frame); return d->metadata; } /*! Returns any metadata for this frame for the given \a key. This might include frame specific information from a camera, or subtitles from a decoded video stream. See the documentation for the relevant video frame producer for further information about available metadata. */ QVariant Frame::metaData(const QString &key) const { Q_D(const Frame); return d->metadata.value(key); } /*! Sets the metadata for the given \a key to \a value. If \a value is a null variant, any metadata for this key will be removed-> The producer of the video frame might use this to associate certain data with this frame, or for an intermediate processor to add information for a consumer of this frame. */ void Frame::setMetaData(const QString &key, const QVariant &value) { Q_D(Frame); if (!value.isNull()) d->metadata.insert(key, value); else d->metadata.remove(key); } qreal Frame::timestamp() const { return d_func()->timestamp; } void Frame::setTimestamp(qreal ts) { d_func()->timestamp = ts; } } //namespace QtAV QtAV-1.12.0/src/FrameReader.cpp000066400000000000000000000277141312235004300160620ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/FrameReader.h" #include #include #include "QtAV/AVDemuxer.h" #include "QtAV/VideoDecoder.h" #include "utils/BlockingQueue.h" #include "utils/Logger.h" namespace QtAV { typedef QtAV::BlockingQueue VideoFrameQueue; const int kQueueMin = 2; static QVariantHash dec_opt_framedrop; static QVariantHash dec_opt_normal; class FrameReader::Private { public: Private() : nb_seek(0) { QVariantHash opt; opt[QString::fromLatin1("skip_frame")] = 8; // 8 for "avcodec", "NoRef" for "FFmpeg". see AVDiscard opt[QString::fromLatin1("skip_loop_filter")] = 8; //skip all? //skip_dict is slower dec_opt_framedrop[QString::fromLatin1("avcodec")] = opt; opt[QString::fromLatin1("skip_frame")] = 0; // 0 for "avcodec", "Default" for "FFmpeg". see AVDiscard opt[QString::fromLatin1("skip_loop_filter")] = 0; dec_opt_normal[QString::fromLatin1("avcodec")] = opt; // avcodec need correct string or value in libavcodec //decs = QStringList() << "VideoToolbox" << "FFmpeg"; vframes.setCapacity(4); vframes.setThreshold(kQueueMin); // } ~Private() { if (read_thread.isRunning()) { read_thread.quit(); read_thread.wait(); } } bool tryLoad(); qint64 seekInternal(qint64 pos); QString url; QStringList vdecs; AVDemuxer demuxer; QScopedPointer decoder; VideoFrameQueue vframes; QThread read_thread; int nb_seek; }; bool FrameReader::Private::tryLoad() { const bool loaded = demuxer.fileName() == url && demuxer.isLoaded(); if (loaded && decoder) return true; if (decoder) { // new source decoder->close(); decoder.reset(0); } if (!loaded || demuxer.atEnd()) { demuxer.unload(); demuxer.setMedia(url); if (!demuxer.load()) { return false; } } const bool has_video = demuxer.videoStreams().size() > 0; if (!has_video) { demuxer.unload(); return false; } if (vdecs.isEmpty()) { VideoDecoder *vd = VideoDecoder::create("FFmpeg"); if (vd) { decoder.reset(vd); decoder->setCodecContext(demuxer.videoCodecContext()); if (!decoder->open()) decoder.reset(0); } } else { foreach (const QString& c, vdecs) { VideoDecoder *vd = VideoDecoder::create(c.toLatin1().constData()); if (!vd) continue; decoder.reset(vd); decoder->setCodecContext(demuxer.videoCodecContext()); decoder->setProperty("copyMode", "OptimizedCopy"); if (!decoder->open()) { decoder.reset(0); continue; } break; } } nb_seek = 0; qDebug("decoder: %p", decoder.data()); vframes.setThreshold(kQueueMin); return !!decoder; } // code is from QtAV VideoFrameExtractor.cpp qint64 FrameReader::Private::seekInternal(qint64 value) { if (!tryLoad()) { qDebug("load error"); return -1; } VideoFrame frame; int range = 100; demuxer.seek(value); const int vstream = demuxer.videoStream(); Packet pkt; qint64 pts0 = -1; bool warn_bad_seek = true; bool warn_out_of_range = true; while (!demuxer.atEnd()) { if (!demuxer.readFrame()) continue; if (demuxer.stream() != vstream) continue; pkt = demuxer.packet(); if (pts0 < 0LL) pts0 = (qint64)(pkt.pts*1000.0); if ((qint64)(pkt.pts*1000.0) - value > (qint64)range) { if (warn_out_of_range) qDebug("read packet out of range"); warn_out_of_range = false; // No return because decoder needs more packets before the desired frame is decoded //return false; } //qDebug("video packet: %f", pkt.pts); // TODO: always key frame? if (pkt.hasKeyFrame) break; if (warn_bad_seek) qWarning("Not seek to key frame!!!"); warn_bad_seek = false; } // enlarge range if seek to key-frame failed const qint64 key_pts = (qint64)(pkt.pts*1000.0); const bool enlarge_range = pts0 >= 0LL && key_pts - pts0 > 0LL; if (enlarge_range) { range = qMax(key_pts - value, range); qDebug() << "enlarge range ==>>>> " << range; } if (!pkt.isValid()) { qWarning("FrameReader failed to get a packet at %lld", value); return -1; } decoder->flush(); //must flush otherwise old frames will be decoded at the beginning decoder->setOptions(dec_opt_normal); // must decode key frame int k = 0; while (k < 2 && !frame.isValid()) { //qWarning("invalid key frame!!!!! undecoded: %d", decoder->undecodedSize()); if (decoder->decode(pkt)) { frame = decoder->frame(); } ++k; } // if seek backward correctly to key frame, diff0 = t - value <= 0 // but sometimes seek to no-key frame(and range is enlarged), diff0 >= 0 // decode key frame const int diff0 = qint64(frame.timestamp()*1000.0) - value; if (qAbs(diff0) <= range) { //TODO: flag forward: result pts must >= value if (frame.isValid()) { qDebug() << "VideoFrameExtractor: key frame found @" << frame.timestamp() <<" diff=" << diff0 << ". format: " << frame.format(); return qint64(frame.timestamp()*1000.0); } } QVariantHash* dec_opt = &dec_opt_normal; // 0: default, 1: framedrop // decode at the given position while (!demuxer.atEnd()) { if (!demuxer.readFrame()) continue; if (demuxer.stream() != vstream) continue; pkt = demuxer.packet(); const qreal t = pkt.pts; //qDebug("video packet: %f, delta=%lld", t, value - qint64(t*1000.0)); if (!pkt.isValid()) { qWarning("invalid packet. no decode"); continue; } if (pkt.hasKeyFrame) { // FIXME: //qCritical("Internal error. Can not be a key frame!!!!"); //return false; //?? } qint64 diff = qint64(t*1000.0) - value; QVariantHash *dec_opt_old = dec_opt; if (nb_seek == 0 || diff >= 0) dec_opt = &dec_opt_normal; else dec_opt = &dec_opt_framedrop; if (dec_opt != dec_opt_old) decoder->setOptions(*dec_opt); // invalid packet? if (!decoder->decode(pkt)) { qWarning("!!!!!!!!!decode failed!!!!"); frame = VideoFrame(); return -1; } // store the last decoded frame because next frame may be out of range const VideoFrame f = decoder->frame(); if (!f.isValid()) { //qDebug("VideoFrameExtractor: invalid frame!!!"); continue; } frame = f; const qreal pts = frame.timestamp(); const qint64 pts_ms = pts*1000.0; if (pts_ms < value) continue; // diff = pts_ms - value; if (qAbs(diff) <= (qint64)range) { qDebug("got frame at %fs, diff=%lld", pts, diff); break; } // if decoder was not flushed, we may get old frame which is acceptable if (diff > range && t > pts) { qWarning("out pts out of range. diff=%lld, range=%d", diff, range); frame = VideoFrame(); return -1; } } ++nb_seek; return qint64(frame.timestamp()*1000.0); } FrameReader::FrameReader(QObject *parent) : QObject(parent) , d(new Private()) { moveToThread(&d->read_thread); connect(this, SIGNAL(readMoreRequested()), SLOT(readMoreInternal())); connect(this, SIGNAL(readEnd()), &d->read_thread, SLOT(quit())); connect(this, SIGNAL(seekRequested(qint64)), SLOT(seekInternal(qint64))); } FrameReader::~FrameReader() { } void FrameReader::setMedia(const QString &url) { if (url == d->url) return; d->url = url; } QString FrameReader::mediaUrl() const { return d->url; } void FrameReader::setVideoDecoders(const QStringList &names) { if (names == d->vdecs) return; d->vdecs = names; } QStringList FrameReader::videoDecoders() const { return d->vdecs; } VideoFrame FrameReader::getVideoFrame() { return d->vframes.take(); } bool FrameReader::hasVideoFrame() const { return !d->vframes.isEmpty(); } bool FrameReader::hasEnoughVideoFrames() const { return d->vframes.isEnough(); } bool FrameReader::readMore() { if (d->demuxer.isLoaded() && d->demuxer.atEnd()) { if (!d->read_thread.isRunning()) return false; qDebug("wait for read thread quit"); d->read_thread.quit(); d->read_thread.wait(); // sync return false; } if (!d->read_thread.isRunning()) d->read_thread.start(); Q_EMIT readMoreRequested(); return true; } bool FrameReader::seek(qint64 pos) { if (!d->read_thread.isRunning()) d->read_thread.start(); Q_EMIT seekRequested(pos); return true; } void FrameReader::readMoreInternal() { if (!d->tryLoad()) { qDebug("load error"); return; } //TODO: decode eof packets if (d->demuxer.atEnd()) return; const int vstream = d->demuxer.videoStream(); Packet pkt; while (!d->demuxer.atEnd()) { if (!d->demuxer.readFrame()) { // qDebug("demuxer read error"); continue; } if (d->demuxer.stream() != vstream) { // qDebug("not video stream"); continue; } pkt = d->demuxer.packet(); if (d->decoder->decode(pkt)) { const VideoFrame frame(d->decoder->frame()); if (!frame) { qDebug("no frame got, continue to decoder"); continue; } d->vframes.put(frame); Q_EMIT frameRead(frame); //qDebug("frame got @%.3f, queue enough: %d", frame.timestamp(), vframes.isEnough()); if (d->vframes.isEnough()) break; } else { qDebug("dec error, continue to decoder"); } } if (d->demuxer.atEnd()) { d->vframes.setThreshold(1); d->vframes.blockFull(false); while (d->decoder->decode(Packet::createEOF())) { qDebug("decoded buffered packets"); const VideoFrame frame(d->decoder->frame()); d->vframes.put(frame); Q_EMIT frameRead(frame); qDebug("put decoded buffered packets @%.3f", frame.timestamp()); } d->vframes.put(VideoFrame()); //make sure take() will not be blocked d->vframes.blockFull(true); qDebug("eof"); Q_EMIT readEnd(); } } bool FrameReader::seekInternal(qint64 value) { qint64 t = !d->seekInternal(value); if (t < 0) return false; // now we get the final frame Q_EMIT seekFinished(t); return true; } } //namespace QtAV QtAV-1.12.0/src/ImageConverter.cpp000066400000000000000000000145521312235004300166130ustar00rootroot00000000000000/****************************************************************************** ImageConverter: Base class for image resizing & color model convertion Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "ImageConverter_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "ImageConverter.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(ImageConverter) ImageConverter::ImageConverter() { } ImageConverter::ImageConverter(ImageConverterPrivate& d) : DPTR_INIT(&d) { } ImageConverter::~ImageConverter() { } QByteArray ImageConverter::outData() const { return d_func().data_out; } bool ImageConverter::check() const { DPTR_D(const ImageConverter); return d.w_in > 0 && d.w_out > 0 && d.h_in > 0 && d.h_out > 0 && d.fmt_in != QTAV_PIX_FMT_C(NONE) && d.fmt_out != QTAV_PIX_FMT_C(NONE); } void ImageConverter::setInSize(int width, int height) { DPTR_D(ImageConverter); if (d.w_in == width && d.h_in == height) return; d.w_in = width; d.h_in = height; } // TODO: default is in size void ImageConverter::setOutSize(int width, int height) { DPTR_D(ImageConverter); if (d.w_out == width && d.h_out == height) return; d.w_out = width; d.h_out = height; d.update_data = true; prepareData(); d.update_data = false; } void ImageConverter::setInFormat(const VideoFormat& format) { d_func().fmt_in = (AVPixelFormat)format.pixelFormatFFmpeg(); } void ImageConverter::setInFormat(VideoFormat::PixelFormat format) { d_func().fmt_in = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(format); } void ImageConverter::setInFormat(int format) { d_func().fmt_in = (AVPixelFormat)format; } void ImageConverter::setOutFormat(const VideoFormat& format) { setOutFormat(format.pixelFormatFFmpeg()); } void ImageConverter::setOutFormat(VideoFormat::PixelFormat format) { setOutFormat(VideoFormat::pixelFormatToFFmpeg(format)); } void ImageConverter::setOutFormat(int format) { DPTR_D(ImageConverter); if (d.fmt_out == format) return; d.fmt_out = (AVPixelFormat)format; d.update_data = true; prepareData(); d.update_data = false; } void ImageConverter::setInRange(ColorRange range) { DPTR_D(ImageConverter); if (d.range_in == range) return; d.range_in = range; // TODO: do not call setupColorspaceDetails(). use a wrapper convert() func and call there d.setupColorspaceDetails(); } ColorRange ImageConverter::inRange() const { return d_func().range_in; } void ImageConverter::setOutRange(ColorRange range) { DPTR_D(ImageConverter); if (d.range_out == range) return; d.range_out = range; d.setupColorspaceDetails(); } ColorRange ImageConverter::outRange() const { return d_func().range_out; } void ImageConverter::setBrightness(int value) { DPTR_D(ImageConverter); if (d.brightness == value) return; d.brightness = value; d.setupColorspaceDetails(); } int ImageConverter::brightness() const { return d_func().brightness; } void ImageConverter::setContrast(int value) { DPTR_D(ImageConverter); if (d.contrast == value) return; d.contrast = value; d.setupColorspaceDetails(); } int ImageConverter::contrast() const { return d_func().contrast; } void ImageConverter::setSaturation(int value) { DPTR_D(ImageConverter); if (d.saturation == value) return; d.saturation = value; d.setupColorspaceDetails(); } int ImageConverter::saturation() const { return d_func().saturation; } QVector ImageConverter::outPlanes() const { return d_func().bits; } QVector ImageConverter::outLineSizes() const { return d_func().pitchs; } bool ImageConverter::convert(const quint8 * const src[], const int srcStride[]) { DPTR_D(ImageConverter); if (d.update_data && !prepareData()) { qWarning("prepair output data error"); return false; } else { d.update_data = false; } return convert(src, srcStride, (uint8_t**)d.bits.constData(), d.pitchs.constData()); } bool ImageConverter::prepareData() { DPTR_D(ImageConverter); if (d.fmt_out == QTAV_PIX_FMT_C(NONE) || d.w_out <=0 || d.h_out <= 0) return false; AV_ENSURE(av_image_check_size(d.w_out, d.h_out, 0, NULL), false); const int nb_planes = qMax(av_pix_fmt_count_planes(d.fmt_out), 0); d.bits.resize(nb_planes); d.pitchs.resize(nb_planes); // alignment is 16. sws in ffmpeg is 16, libav10 is 8 const int kAlign = 16; AV_ENSURE(av_image_fill_linesizes((int*)d.pitchs.constData(), d.fmt_out, kAlign > 7 ? FFALIGN(d.w_out, 8) : d.w_out), false); for (int i = 0; i < d.pitchs.size(); ++i) d.pitchs[i] = FFALIGN(d.pitchs[i], kAlign); int s = av_image_fill_pointers((uint8_t**)d.bits.constData(), d.fmt_out, d.h_out, NULL, d.pitchs.constData()); if (s < 0) return false; d.data_out.resize(s + kAlign-1); const int offset = (kAlign - ((uintptr_t)d.data_out.constData() & (kAlign-1))) & (kAlign-1); AV_ENSURE(av_image_fill_pointers((uint8_t**)d.bits.constData(), d.fmt_out, d.h_out, (uint8_t*)d.data_out.constData()+offset, d.pitchs.constData()), false); // TODO: special formats //if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) // avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt); for (int i = 0; i < d.pitchs.size(); ++i) { Q_ASSERT(d.pitchs[i]%kAlign == 0); Q_ASSERT(qptrdiff(d.bits[i])%kAlign == 0); } return true; } } //namespace QtAV QtAV-1.12.0/src/ImageConverter.h000066400000000000000000000110401312235004300162450ustar00rootroot00000000000000/****************************************************************************** ImageConverter: Base class for image resizing & color model convertion Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_IMAGECONVERTER_H #define QTAV_IMAGECONVERTER_H #include #include #include namespace QtAV { typedef int ImageConverterId; class ImageConverterPrivate; class ImageConverter //export is not needed { DPTR_DECLARE_PRIVATE(ImageConverter) public: ImageConverter(); virtual ~ImageConverter(); QByteArray outData() const; // return false if i/o format not supported, or size is not valid. // TODO: use isSupported(i/o format); virtual bool check() const; void setInSize(int width, int height); void setOutSize(int width, int height); void setInFormat(const VideoFormat& format); void setInFormat(VideoFormat::PixelFormat format); void setInFormat(int format); void setOutFormat(const VideoFormat& format); void setOutFormat(VideoFormat::PixelFormat format); void setOutFormat(int formate); // default is full range. void setInRange(ColorRange range); ColorRange inRange() const; // default is full range void setOutRange(ColorRange range); ColorRange outRange() const; /*! * brightness, contrast, saturation: -100~100 * If value changes, setup sws */ void setBrightness(int value); int brightness() const; void setContrast(int value); int contrast() const; void setSaturation(int value); int saturation() const; QVector outPlanes() const; QVector outLineSizes() const; virtual bool convert(const quint8 *const src[], const int srcStride[]); virtual bool convert(const quint8 *const src[], const int srcStride[], quint8 *const dst[], const int dstStride[]) = 0; public: template static bool Register(ImageConverterId id, const char* name) { return Register(id, create, name);} static ImageConverter* create(ImageConverterId id); static ImageConverter* create(const char* name); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static ImageConverterId* next(ImageConverterId* id = 0); static const char* name(ImageConverterId id); static ImageConverterId id(const char* name); private: template static ImageConverter* create() { return new C();} typedef ImageConverter* (*ImageConverterCreator)(); static bool Register(ImageConverterId id, ImageConverterCreator, const char *name); protected: ImageConverter(ImageConverterPrivate& d); //Allocate memory for out data. Called in setOutFormat() virtual bool prepareData(); //Allocate memory for out data DPTR_DECLARE(ImageConverter) }; class ImageConverterFFPrivate; /*! * \brief The ImageConverterFF class * based on libswscale */ class ImageConverterFF Q_DECL_FINAL: public ImageConverter //Q_AV_EXPORT is not needed { DPTR_DECLARE_PRIVATE(ImageConverterFF) public: ImageConverterFF(); bool check() const Q_DECL_OVERRIDE; // FIXME: why match to the pure virtual one if not declare here? bool convert(const quint8 *const src[], const int srcStride[]) Q_DECL_OVERRIDE { return ImageConverter::convert(src, srcStride);} bool convert(const quint8 *const src[], const int srcStride[], quint8 *const dst[], const int dstStride[]) Q_DECL_OVERRIDE; }; typedef ImageConverterFF ImageConverterSWS; //ImageConverter* c = ImageConverter::create(ImageConverterId_FF); extern ImageConverterId ImageConverterId_FF; extern ImageConverterId ImageConverterId_IPP; } //namespace QtAV #endif // QTAV_IMAGECONVERTER_H QtAV-1.12.0/src/ImageConverterFF.cpp000066400000000000000000000112231312235004300170170ustar00rootroot00000000000000/****************************************************************************** ImageConverterFF: Image resizing & color model convertion using FFmpeg swscale Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "ImageConverter.h" #include "ImageConverter_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { ImageConverterId ImageConverterId_FF = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value; FACTORY_REGISTER(ImageConverter, FF, "FFmpeg") class ImageConverterFFPrivate Q_DECL_FINAL: public ImageConverterPrivate { public: ImageConverterFFPrivate() : sws_ctx(0) , update_eq(true) {} ~ImageConverterFFPrivate() { if (sws_ctx) { sws_freeContext(sws_ctx); sws_ctx = 0; } } virtual bool setupColorspaceDetails(bool force = true) Q_DECL_FINAL; SwsContext *sws_ctx; bool update_eq; }; ImageConverterFF::ImageConverterFF() :ImageConverter(*new ImageConverterFFPrivate()) { } bool ImageConverterFF::check() const { if (!ImageConverter::check()) return false; DPTR_D(const ImageConverterFF); if (sws_isSupportedInput((AVPixelFormat)d.fmt_in) <= 0) { qWarning("Input pixel format not supported (%s)", av_get_pix_fmt_name((AVPixelFormat)d.fmt_in)); return false; } if (sws_isSupportedOutput((AVPixelFormat)d.fmt_out) <= 0) { qWarning("Output pixel format not supported (%s)", av_get_pix_fmt_name((AVPixelFormat)d.fmt_out)); return false; } return true; } bool ImageConverterFF::convert(const quint8 *const src[], const int srcStride[], quint8 *const dst[], const int dstStride[]) { DPTR_D(ImageConverterFF); //Check out dimension. equals to in dimension if not setted. TODO: move to another common func if (d.w_out == 0 || d.h_out == 0) { if (d.w_in == 0 || d.h_in == 0) return false; setOutSize(d.w_in, d.h_in); } //TODO: move those code to prepare() d.sws_ctx = sws_getCachedContext(d.sws_ctx , d.w_in, d.h_in, (AVPixelFormat)d.fmt_in , d.w_out, d.h_out, (AVPixelFormat)d.fmt_out , (d.w_in == d.w_out && d.h_in == d.h_out) ? SWS_POINT : SWS_FAST_BILINEAR //SWS_BICUBIC , NULL, NULL, NULL ); //int64_t flags = SWS_CPU_CAPS_SSE2 | SWS_CPU_CAPS_MMX | SWS_CPU_CAPS_MMX2; //av_opt_set_int(d.sws_ctx, "sws_flags", flags, 0); if (!d.sws_ctx) return false; d.setupColorspaceDetails(false); int result_h = sws_scale(d.sws_ctx, src, srcStride, 0, d.h_in, dst, dstStride); if (result_h != d.h_out) { qDebug("convert failed: %d, %d", result_h, d.h_out); return false; } Q_UNUSED(result_h); for (int i = 0; i < d.pitchs.size(); ++i) { d.bits[i] = dst[i]; d.pitchs[i] = dstStride[i]; } return true; } bool ImageConverterFFPrivate::setupColorspaceDetails(bool force) { if (!sws_ctx) { update_eq = true; return false; } if (force) update_eq = true; if (!update_eq) { return true; } const int srcRange = range_in == ColorRange_Limited ? 0 : 1; int dstRange = range_out == ColorRange_Limited ? 0 : 1; // TODO: color space bool supported = sws_setColorspaceDetails(sws_ctx, sws_getCoefficients(SWS_CS_DEFAULT) , srcRange, sws_getCoefficients(SWS_CS_DEFAULT) , dstRange , ((brightness << 16) + 50)/100 , (((contrast + 100) << 16) + 50)/100 , (((saturation + 100) << 16) + 50)/100 ) >= 0; //sws_init_context(d.sws_ctx, NULL, NULL); update_eq = false; return supported; } } //namespace QtAV QtAV-1.12.0/src/ImageConverterIPP.cpp000066400000000000000000000072001312235004300171540ustar00rootroot00000000000000/****************************************************************************** ImageConverterIPP: Image resizing & color model convertion using Intel IPP Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "ImageConverter.h" #include "ImageConverter_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #if QTAV_HAVE(IPP) #include #endif #include "utils/Logger.h" namespace QtAV { class ImageConverterIPPPrivate; class ImageConverterIPP : public ImageConverter //Q_AV_EXPORT is not needed { DPTR_DECLARE_PRIVATE(ImageConverterIPP) public: ImageConverterIPP(); virtual bool convert(const quint8 *const srcSlice[], const int srcStride[]); protected: virtual bool prepareData(); //Allocate memory for out data }; ImageConverterId ImageConverterId_IPP = mkid::id32base36_3<'I', 'P', 'P'>::value; FACTORY_REGISTER(ImageConverter, IPP, "IPP") class ImageConverterIPPPrivate : public ImageConverterPrivate { public: ImageConverterIPPPrivate():need_scale(true) {} bool need_scale; QByteArray orig_ori_rgb; }; ImageConverterIPP::ImageConverterIPP() :ImageConverter(*new ImageConverterIPPPrivate()) { } bool ImageConverterIPP::convert(const quint8 *const srcSlice[], const int srcStride[]) { DPTR_D(ImageConverterIPP); //color convertion, no scale #if QTAV_HAVE(IPP) struct { const quint8 *data[3]; int linesize[3]; } yuv = { { srcSlice[0], srcSlice[2], srcSlice[1] }, { srcStride[0], srcStride[2], srcStride[1] } }; //ippiSwapChannels ippiYUV420ToRGB_8u_P3AC4R(const_cast(yuv.data), const_cast(yuv.linesize), (Ipp8u*)(d.orig_ori_rgb.data()) , 4*sizeof(quint8)*d.w_in, (IppiSize){d.w_in, d.h_in}); d.data_out = d.orig_ori_rgb; return true; if (d.need_scale) { qDebug("rs"); ippiResize_8u_AC4R((const Ipp8u*)d.orig_ori_rgb.data(), (IppiSize){d.w_in, d.h_in}, 4*sizeof(quint8)*d.w_in, (IppiRect){0, 0, d.w_in, d.h_in} , (Ipp8u*)d.data_out.data(), 4*sizeof(quint8)*d.w_in, (IppiSize){d.w_out, d.h_out} , (double)d.w_out/(double)d.w_in, (double)d.h_out/(double)d.h_in, IPPI_INTER_CUBIC); } else { d.data_out = d.orig_ori_rgb; } #endif return true; } //TODO: call it when out format is setted. and avoid too much calls bool ImageConverterIPP::prepareData() { DPTR_D(ImageConverterIPP); //for color convertion if (d.w_in > 0 && d.h_in > 0) { qDebug("in size=%d x %d", d.w_in, d.h_in); int bytes = avpicture_get_size((PixelFormat)d.fmt_out, d.w_in, d.h_in); //if(d.orig_ori_rgb.size() < bytes) { d.orig_ori_rgb.resize(bytes); //} } return ImageConverter::prepareData(); } } //namespace QtAV QtAV-1.12.0/src/ImageConverter_p.h000066400000000000000000000041071312235004300165720ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_IMAGECONVERTER_P_H #define QTAV_IMAGECONVERTER_P_H #include #include namespace QtAV { class ImageConverter; class ImageConverterPrivate : public DPtrPrivate { public: ImageConverterPrivate() : w_in(0),h_in(0) , w_out(0),h_out(0) , fmt_in(QTAV_PIX_FMT_C(YUV420P)) , fmt_out(QTAV_PIX_FMT_C(RGB32)) , range_in(ColorRange_Unknown) , range_out(ColorRange_Unknown) , brightness(0) , contrast(0) , saturation(0) , update_data(true) { bits.reserve(8); pitchs.reserve(8); } virtual bool setupColorspaceDetails(bool force = true) { Q_UNUSED(force); return true; } int w_in, h_in, w_out, h_out; AVPixelFormat fmt_in, fmt_out; ColorRange range_in, range_out; int brightness, contrast, saturation; bool update_data; QByteArray data_out; QVector bits; QVector pitchs; }; } //namespace QtAV #endif // QTAV_IMAGECONVERTER_P_H QtAV-1.12.0/src/Packet.cpp000066400000000000000000000166261312235004300151140ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Packet.h" #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" namespace QtAV { namespace { static const struct RegisterMetaTypes { inline RegisterMetaTypes() { qRegisterMetaType(); } } _registerMetaTypes; } //namespace class PacketPrivate : public QSharedData { public: PacketPrivate() : QSharedData() , initialized(false) { av_init_packet(&avpkt); } PacketPrivate(const PacketPrivate& o) : QSharedData(o) , initialized(o.initialized) { //used by QSharedDataPointer.detach() av_init_packet(&avpkt); av_packet_ref(&avpkt, (AVPacket*)&o.avpkt); } ~PacketPrivate() { av_packet_unref(&avpkt); } bool initialized; AVPacket avpkt; }; Packet Packet::createEOF() { Packet pkt; pkt.data = QByteArray("eof"); return pkt; } bool Packet::isEOF() const { return data == "eof" && pts < 0.0 && dts < 0.0; } Packet Packet::fromAVPacket(const AVPacket *avpkt, double time_base) { Packet pkt; if (fromAVPacket(&pkt, avpkt, time_base)) return pkt; return Packet(); } // time_base: av_q2d(format_context->streams[stream_idx]->time_base) bool Packet::fromAVPacket(Packet* pkt, const AVPacket *avpkt, double time_base) { if (!pkt || !avpkt) return false; pkt->position = avpkt->pos; pkt->hasKeyFrame = !!(avpkt->flags & AV_PKT_FLAG_KEY); // what about marking avpkt as invalid and do not use isCorrupt? pkt->isCorrupt = !!(avpkt->flags & AV_PKT_FLAG_CORRUPT); if (pkt->isCorrupt) qDebug("currupt packet. pts: %f", pkt->pts); // from av_read_frame: pkt->pts can be AV_NOPTS_VALUE if the video format has B-frames, so it is better to rely on pkt->dts if you do not decompress the payload. // old code set pts as dts is valid if (avpkt->pts != (qint64)AV_NOPTS_VALUE) pkt->pts = avpkt->pts * time_base; else if (avpkt->dts != (qint64)AV_NOPTS_VALUE) // is it ok? pkt->pts = avpkt->dts * time_base; else pkt->pts = 0; // TODO: init value if (avpkt->dts != (qint64)AV_NOPTS_VALUE) //has B-frames pkt->dts = avpkt->dts * time_base; else pkt->dts = pkt->pts; //qDebug("avpacket pts %lld, dts: %lld ", avpkt->pts, avpkt->dts); //TODO: pts must >= 0? look at ffplay pkt->pts = qMax(0, pkt->pts); pkt->dts = qMax(0, pkt->dts); if (avpkt->duration > 0) pkt->duration = avpkt->duration * time_base; else pkt->duration = 0; #if (LIBAVCODEC_VERSION_MAJOR < 57) //FF_API_CONVERGENCE_DURATION since 57 // subtitle always has a key frame? convergence_duration may be 0 if (avpkt->convergence_duration > 0 && pkt->hasKeyFrame #if 0 && codec->codec_type == AVMEDIA_TYPE_SUBTITLE #endif ) pkt->duration = avpkt->convergence_duration * time_base; #endif //qDebug("AVPacket.pts=%f, duration=%f, dts=%lld", pkt->pts, pkt->duration, packet.dts); pkt->data.clear(); // TODO: pkt->avpkt. data is not necessary now. see mpv new_demux_packet_from_avpacket // copy properties and side data. does not touch data, size and ref pkt->d = QSharedDataPointer(new PacketPrivate()); pkt->d->initialized = true; AVPacket *p = &pkt->d->avpkt; av_packet_ref(p, (AVPacket*)avpkt); //properties are copied internally // add ref without copy, bytearray does not copy either. bytearray options linke remove() is safe. omit FF_INPUT_BUFFER_PADDING_SIZE pkt->data = QByteArray::fromRawData((const char*)p->data, p->size); // QtAV always use ms (1/1000s) and s. As a result no time_base is required in Packet p->pts = pkt->pts * 1000.0; p->dts = pkt->dts * 1000.0; p->duration = pkt->duration * 1000.0; return true; } Packet::Packet() : hasKeyFrame(false) , isCorrupt(false) , pts(-1) , duration(-1) , dts(-1) , position(-1) { } Packet::Packet(const Packet &other) : hasKeyFrame(other.hasKeyFrame) , isCorrupt(other.isCorrupt) , data(other.data) , pts(other.pts) , duration(other.duration) , dts(other.dts) , position(other.position) , d(other.d) { } Packet& Packet::operator =(const Packet& other) { if (this == &other) return *this; d = other.d; hasKeyFrame = other.hasKeyFrame; isCorrupt = other.isCorrupt; pts = other.pts; duration = other.duration; dts = other.dts; position = other.position; data = other.data; return *this; } Packet::~Packet() { } const AVPacket *Packet::asAVPacket() const { if (d.constData()) { //why d->initialized (ref==1) result in detach? if (d.constData()->initialized) {//d.data() was 0 if d has not been accessed. now only contains avpkt, check d.constData() is engough d->avpkt.data = (uint8_t*)data.constData(); d->avpkt.size = data.size(); return &d->avpkt; } } else { d = QSharedDataPointer(new PacketPrivate()); } d->initialized = true; AVPacket *p = &d->avpkt; p->pts = pts * 1000.0; p->dts = dts * 1000.0; p->duration = duration * 1000.0; p->pos = position; if (isCorrupt) p->flags |= AV_PKT_FLAG_CORRUPT; if (hasKeyFrame) p->flags |= AV_PKT_FLAG_KEY; if (!data.isEmpty()) { p->data = (uint8_t*)data.constData(); p->size = data.size(); } return p; } void Packet::skip(int bytes) { if (!d.constData()) { //not constructed from AVPacket d = QSharedDataPointer(new PacketPrivate()); } d->initialized = false; data = QByteArray::fromRawData(data.constData() + bytes, data.size() - bytes); if (position >= 0) position += bytes; // TODO: if duration is valid, compute pts/dts and no manually update outside? } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const Packet &pkt) { dbg.nospace() << "QtAV::Packet.data " << hex << (qptrdiff)pkt.data.constData() << "+" << dec << pkt.data.size(); dbg.nospace() << ", dts: " << pkt.dts; dbg.nospace() << ", pts: " << pkt.pts; dbg.nospace() << ", duration: " << pkt.duration; dbg.nospace() << ", position: " << pkt.position; dbg.nospace() << ", hasKeyFrame: " << pkt.hasKeyFrame; dbg.nospace() << ", isCorrupt: " << pkt.isCorrupt; dbg.nospace() << ", eof: " << pkt.isEOF(); return dbg.space(); } #endif //QT_NO_DEBUG_STREAM } //namespace QtAV QtAV-1.12.0/src/PacketBuffer.cpp000066400000000000000000000117461312235004300162440ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "PacketBuffer.h" #include namespace QtAV { static const int kAvgSize = 16; PacketBuffer::PacketBuffer() : m_mode(BufferTime) , m_buffering(true) // in buffering state at the beginning , m_max(1.5) , m_buffer(0) , m_value0(0) , m_value1(0) , m_history(kAvgSize) { } PacketBuffer::~PacketBuffer() { } void PacketBuffer::setBufferMode(BufferMode mode) { m_mode = mode; if (queue.isEmpty()) { m_value0 = m_value1 = 0; return; } if (m_mode == BufferTime) { m_value0 = qint64(queue[0].pts*1000.0); } else { m_value0 = 0; } } BufferMode PacketBuffer::bufferMode() const { return m_mode; } void PacketBuffer::setBufferValue(qint64 value) { m_buffer = value; } qint64 PacketBuffer::bufferValue() const { return m_buffer; } void PacketBuffer::setBufferMax(qreal max) { if (max < 1.0) { qWarning("max (%f) must >= 1.0", max); return; } m_max = max; } qreal PacketBuffer::bufferMax() const { return m_max; } qint64 PacketBuffer::buffered() const { Q_ASSERT(m_value1 >= m_value0); return m_value1 - m_value0; } bool PacketBuffer::isBuffering() const { return m_buffering; } qreal PacketBuffer::bufferProgress() const { const qreal p = qreal(buffered())/qreal(bufferValue()); return qMax(qMin(p, 1.0), 0.0); } qreal PacketBuffer::bufferSpeed() const { return calc_speed(false); } qreal PacketBuffer::bufferSpeedInBytes() const { return calc_speed(true); } bool PacketBuffer::checkEnough() const { return buffered() >= bufferValue(); } bool PacketBuffer::checkFull() const { return buffered() >= qint64(qreal(bufferValue())*bufferMax()); } void PacketBuffer::onPut(const Packet &p) { if (m_mode == BufferTime) { m_value1 = qint64(p.pts*1000.0); // FIXME: what if no pts m_value0 = qint64(queue[0].pts*1000.0); // must compute here because it is reset to 0 if take from empty //if (isBuffering()) // qDebug("+buffering progress: %.1f%%=%.1f/%.1f~%.1fs %d-%d", bufferProgress()*100.0, (qreal)buffered()/1000.0, (qreal)bufferValue()/1000.0, qreal(bufferValue())*bufferMax()/1000.0, m_value1, m_value0); } else if (m_mode == BufferBytes) { m_value1 += p.data.size(); } else { m_value1++; } if (!m_buffering) return; if (checkEnough()) { m_buffering = false; } if (!m_buffering) { //buffering=>buffered m_history = ring(kAvgSize); return; } BufferInfo bi; bi.bytes = p.data.size(); if (!m_history.empty()) bi.bytes += m_history.back().bytes; bi.v = m_value1; bi.t = QDateTime::currentMSecsSinceEpoch(); m_history.push_back(bi); } void PacketBuffer::onTake(const Packet &p) { if (checkEmpty()) { m_buffering = true; } if (queue.isEmpty()) { m_value0 = 0; m_value1 = 0; return; } if (m_mode == BufferTime) { m_value0 = qint64(queue[0].pts*1000.0); //if (isBuffering()) // qDebug("-buffering progress: %.1f=%.1f/%.1fs", bufferProgress(), (qreal)buffered()/1000.0, (qreal)bufferValue()/1000.0); } else if (m_mode == BufferBytes) { m_value1 -= p.data.size(); m_value1 = qMax(0LL, m_value1); } else { m_value1--; } } qreal PacketBuffer::calc_speed(bool use_bytes) const { if (m_history.empty()) return 0; const qreal dt = (double)QDateTime::currentMSecsSinceEpoch()/1000.0 - m_history.front().t/1000.0; // dt should be always > 0 because history stores absolute time if (qFuzzyIsNull(dt)) return 0; const qint64 delta = use_bytes ? m_history.back().bytes - m_history.front().bytes : m_history.back().v - m_history.front().v; if (delta < 0) { qWarning("PacketBuffer internal error. delta(bytes %d): %lld", use_bytes, delta); return 0; } return (qreal)delta/dt; } } //namespace QtAV QtAV-1.12.0/src/PacketBuffer.h000066400000000000000000000070201312235004300156770ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_PACKETBUFFER_H #define QTAV_PACKETBUFFER_H #include #include #include "utils/BlockingQueue.h" #include "utils/ring.h" namespace QtAV { /* * take empty: start buffering, block at next take if still empty * take enough: start to put more packets * put enough: end buffering, end take block * put full: stop putting more packets */ class PacketBuffer : public BlockingQueue { public: PacketBuffer(); ~PacketBuffer(); void setBufferMode(BufferMode mode); BufferMode bufferMode() const; /*! * \brief setBufferValue * Ensure the buffered msecs/bytes/packets in queue is at least the given value * \param value BufferBytes: bytes, BufferTime: msecs, BufferPackets: packets count */ void setBufferValue(qint64 value); qint64 bufferValue() const; /*! * \brief setBufferMax * stop buffering if max value reached. Real value is bufferValue()*bufferMax() * \param max the ratio to bufferValue(). always >= 1.0 */ void setBufferMax(qreal max); qreal bufferMax() const; /*! * \brief buffered * Current buffered value in the queue */ qint64 buffered() const; bool isBuffering() const; /*! * \brief bufferProgress * How much of the data buffer is currently filled, from 0.0 (empty) to 1.0 (enough or full). * bufferProgress() can be less than 1 if not isBuffering(); * \return Percent of buffered time, bytes or packets. */ qreal bufferProgress() const; /*! * \brief bufferSpeed * Depending on BufferMode, the result is delta_pts/s, packets/s or bytes/s * \return */ qreal bufferSpeed() const; qreal bufferSpeedInBytes() const; protected: bool checkEnough() const Q_DECL_OVERRIDE; bool checkFull() const Q_DECL_OVERRIDE; void onTake(const Packet &) Q_DECL_OVERRIDE; void onPut(const Packet &) Q_DECL_OVERRIDE; protected: typedef BlockingQueue PQ; using PQ::setCapacity; using PQ::setThreshold; using PQ::capacity; using PQ::threshold; private: qreal calc_speed(bool use_bytes) const; BufferMode m_mode; bool m_buffering; qreal m_max; // bytes or count qint64 m_buffer; qint64 m_value0, m_value1; typedef struct { qint64 v; //pts, total packes or total bytes qint64 bytes; //total bytes qint64 t; } BufferInfo; ring m_history; }; } //namespace QtAV #endif // QTAV_PACKETBUFFER_H QtAV-1.12.0/src/QtAV.icns000066400000000000000000001720001312235004300146570ustar00rootroot00000000000000icnsis32 1};,[m D4 \I)q nG+ OGb Mg Wn Pc ?]F   8)Æ ѻ˷ 廷 㶈ƻ׹ ɰ ٲ ݴ ճ ·طüո ξ Ƽ 1};,[m D4 \I)q nG+ OGb Mg Wn Pc ?]F   8)s8mk%yD#$iA0JCl/}N)Y?E(il32z      .FH4.F  & k  85[\ %  $% |]-9_   I Es?g+^m& ɿθʼżĸ淼ȸⷽ󻻽Ĺù긼ӷͷٷַƷ ŷ      .FH4.F  & k  85[\ %  $% |]-9_   I Es?g+^m&l8mkG_fZ=tWnDD'P @p0KUMu5H^5 W&# e+{ =i_.ih32 SWʧo($H  FU @%L N u0F 4 X?$  oA R ;S 1D )lv FG&S  b>^ >՟$X0r+f 7@j,|ɓG Ƹθ·ͶѶ˷䷼Ǹ㶼˸ٶⶼ緼跼㶼Ƙڶͷ淼˸鸼ùҷٶַɶػμ¾Wʧo($H  FU @%L N u0F 4 X?$  oA R ;S 1D )lv FG&S  b>^ >՟$X0r+f 7@j,|ɓG h8mk #*qزw0]g O\)73C'6 CVG[&Oc x>R ,>"2j~  _n ۈ%-eǶj2it32k%'$*YɫT% SԗKA9aЀT ^ P;. 7(gTzzbLc7 ,\  / a ,!I,@,es%,, )}, I~,od,F, 0/,S}, z4,#o, 9W,], ,# 6, BP,g9g,Lz,+`,=j,@p,-o,iu,k[b, DGH,%3.,,_, <,a, }G',V, 3,e1,r,La, +I ,Ȳu,gZ,Bf ,#M / 8 ,_&cj#H3dNt_xckVQ>, M>  u "  !  \R U ˕ODlȺiAѬϹ ŻϿƎʹһѹǶ޺ŷѶܷ㸆师㷈޶Զȸ㷼η󼇻ٶܶշﺇǸܶ𺇻ĹҷᶼùʸϷշ׶ٶٶ׶ӷηȸ¹븼ݶη븼ַ¹跼η𺉻ҷ񺊻Ϸ긼Ĺֶ鸌ǸϷԶնĮҶ˶·̶ູۻ ¶ѽº »ʻŻ%'$*YɫT% SԗKA9aЀT ^ P;. 7(gTzzbLc7 ,\  / a ,!I,@,es%,, )}, I~,od,F, 0/,S}, z4,#o, 9W,], ,# 6, BP,g9g,Lz,+`,=j,@p,-o,iu,k[b, DGH,%3.,,_, <,a, }G',V, 3,e1,r,La, +I ,Ȳu,gZ,Bf ,#M / 8 ,_&cj#H3dNt_xckVQ>, M>  u "  !  \R U ˕ODlȺiAt8mk@*E_rp]B'>uˡo8 %kdkb7.E;<1p`&bR *5&;+5$( xXD"cN }2 9$(q T=P9|#T<l(<)L9_GhPnWqZkTeMXAE25! oX<)t^2"j0\G v]t] YC*q\C0nXwpbP4'ZL 9.PEOE5.WO F@ =dβ`: %-141,$ ic083\PNG  IHDR\rf IDATx]x]ű^IЌM z yBubBKh( x-ޫ ]of9{%[;|+Ku;3;;+`0 `0 `0 `0 `0 `0 `0 `0 `0 #HZB)Є)_ #@M(,"xmL}_ 5Hk?`0Yv9hY=gX}4Vg z8|30(M{n0xh;V)41!k`0 2(ԄaUHWX4oADk'zݠ/~Z:t!:͠[Aւnnt &5sAǁ }vK@{Yx %J+pH!a}<uXE7ןZJhcJ@ teئ1u- 9W{pQ =F^ʧ(t2Naj ?AG CnZF=C3?>~b-|B{(P`LWB/]c/VBXp~{^fg <z6kTM|8y&Fۙ[$`A?]jZY6L!oa蛠3&YWaBeTeC@7c<%颬D%x?j@{w"M @ҥEy?;k Ðbs,>z<| i`"P6[gDEïT_~]S<@I(y=&ׁ< A[Evo)2E+~Z{-t˜l;W3o@iЉѮuDph^W38)?+ &5mm\QSsÚwA!h+9@_39N2gJ: 5Qͤw7g*Lڇό +>#WWUZ 8%R%Ic);/$I6ULd3SBCspH!#mԷcV5sWT!0VQYeWV}|}E+7dK S\j~z-x8RP1+`o pPi.'IBoZɸѐff& --م?ooYHD46w"(wox4y!s?bImք v2dm^k4VedrFLC4$yƸ3/RN'9Q?xyĈ#>Uw-Zh#/1m$&bL c3B-^İFT}lƄ mD7sROy׬s Lai{n^# ^mj7ȕ[Wʙgʡk}(Zmm.úȖe-2R+O$j#,iLFwjXO't>ZUaU7ʗ{IV˶.kw0\Ibxu췠=:8)& LoG[T +7ۅLys{/aǀg\ɵW_ "v|k[rY ;۶oWtioy-u['{A^>rٹ"! r&9P2!#2SON><ޘ,X)Fq<G\>8A9l0nW+Qq붭W ϼ]y*\rI \|5q8Uji6|0վX'vwj%heMB}ArgZgzH9 j{9A$8y!F@"t= G÷y׷L~srTe8HG ;oʜ9LY9y{O!JmmY>}Ai+;ʿam0& 2|U)We5$rBԇOyqa "b 'GMwwW7W>1 FD[Wf([fܢ3s `߁ӭG_fu]i WV5tԬ U1x[e6v24BZ@/a$cqUM}RǷɾs+md^h)Wff*YHg4>pI R0˕y>4=ߍ\W^kx`Ա갠 ހU'C_8xm꫽dG?޵Wn,(W%0:UvR'`_E2>_?Y|_@U'}p?O{\y;;%>h#p1ORf `p@N)sA*J7M3U"v6\L 21|8T&_=,cD]tDؤ{&`2+$@;۞^4fdSVK7qpꄏE>tN]Y;dc?\0xB%gU ~/RoNC{+G<|3~d߈u#c'̐%o2beC@L](:yBmYOPAr҆Ijbc& |s:Z]Ȑ(T"F{" T0oTK'\]Tg\t@c5x`6nxR=SFV2z,(ПsbD4fX[jlx4f>I Kp*SPN85W--tTډA=}QvAdkb? @?L}*OT"exy#Dh)>9!ؿjp^9U*񣫈}̉È>h,_^}~ B$/A7H_/+>\7wv_$aF{ qeilq~p\9UJ=9I5Alh$нޓ{gJ΄5 L``xxsW`MNMTTg i燗0EH.E"Xa9{wn0p"0'HXq?@ 04 D6L*:s &oeqG>*懁߻bob0!b| =$W@$g;@(m՜ue&DXA}1@!H A T244wͺ+|ǍKHPp#w3k"D # σlV z'f gahkwr|"Z &l[a@ @hy&]ocުR0"!J~ ̪Pw{ܨ* vG̯gAs~ ZB|bЀfEZr}ʝԇ|z\U1@#"8 mR7qXP?h1nljxu _,8!~ZDԷ)R%ÂA$pϬ{LmmW}on$ū~;\5QM%3,+@пaz+kVCUc^nq"ϋTR?Ai?sOCHH,@Yx/~ݾ</`&v{O{Q4~D`]@>/p'eB~IpSO[~&o}:EB:ۆDg' \9Pu"/`-վ}s/O:VF$/ Y$UgmH̱gfR!H$pmC5`wտCE9g5Q] sp/A6 5,U_g $V߆ m@o5>UDdGPۆ@![sН+%%Ì[EY 9yd5Q&o~O!h X6 Vi%{:F̙RkR/5QL"m +Ag^F=tIFX"`;SQ>0bn5j#&3?% | bϤ:}"i/r'=\28A7?$!g"tRGC悶c G~)w_?/}7 { 5?@SUsK%>7i;Oⱄ;#=L=+T˄ D`; @ruRm 9?©?9uiWC& 1ÂA!SX`'jQ#JaK@ۋXf#)a)xj#PAAwKX@sWHd\"b[v #|t;WukUG+anWA!, or云 Ӧ'ioG ݅f5 N$ б(GOQd,Ū08h&˝om=5PqrQ!oXp Q֌€{gߛ0|Aa@!%1?rەK,Q'%  2>O Y=$B蝓í^N;g9O!%|ǎoY*6m4붭:eZ4^8e[n|3Ud{L P\LDH ςlADeېo/K4í@ GJ pwۯ=.O|l>'n?"2v"ť~TRHgAa@,lY.N=N{uj58Q+rYÚťjMwEJa§5,{8q2E{RyfcULӄy8(ߜYwc+}A^A5,s.tB&LijP+ )?X؝3̤@{ ۺ@4yKK>0:n| 3?dd&=D,z%&!8>+bQm@{\\Sƞ qD>h  ^SbűԵxF)WJmn?~?1ÂA!bҙ4d D42́#PS@Fг3,gKeX}0( Lf",t0w3L D;xd!=#?hwf&7 \("}0,fHw|%Y &'@6  (zՙT+(v/I1P9?.p>Z8voX_ г!-6}gU^hTLeg%v`9 5Sѓs|_ B!c;X8$Sm@$?W OA6 @^wDJADO;[ۀ/Z@QD*ŤO;8N&6C H Osʆ)@OwVlMt+PRa*L7փMĕLw5= tL7׃#(ӽ>nӀ$ EM0_58qC[{)taq%H;}yKav@;O58qC\cԫ/LW \1L.!@HvB8b5!`v? (>akVy#2 ̞gEb[Ɨ:TJ%qNFLw%m :k:Z8ahfn"]^`y(jbD| B'q~Cogzȴ^TO@z78!Jw>Pe#H4Q) j"ڀ.(x\'Dt(u˶w3.0G<: @Tʕ1\hUFx-JyCa'1> ߁ 7h}V PihFIrV9q~C^.8`[N ||Q k0iB.~@cp7g(L)"Hp{YʷUb%BÞxqYya"n[An%q=%[2=d?nTDv o*J2V4E"9qjcy/y]-4Y\&"HpmAWM<4!נA7?~(o5xaʑ`7RDt !}Q ymq "1@}NAqmyl%>^U:XT Z\v&1ݿAe[Ofk7>"ǀ냓p&QpSV<_=;,P$SH{v`] ?͞`]h" F&o &(IDATo\4`ÆsAeR߳oq~} +O<;CݬI^`zo-}K bԽl@&oq~C ?cX /t€A7x>^ OEe`6!_@{*?sA*Vz1 !p@P7"dJf h}ٌQ{??Aw0!χ >}cTt :tab)y×jSxWU/uOB VߔߴTGpC;2|>pf(N.7GvW ]Xd ]OCҎsTYtYmƁvo.m%՜)\@s! LG',{70|l\U@m; @a?L87? ~zkcGdTrf tq*Fd'j x +h vL7pr+p€#A +?Xۂ]v+PG",ȝ @2Fɬtmgr\S D  0Dx< =I =vKSƜv_^%q⟆YO|7pW$ ~f]Y9sͳGXo,.47 $@]Wz#8W8TVDe9OJ`U3IJh0u z 7<5 8D˶.SƳpWCJݥ" 0`zpFsP!J#}d r?^z1,ƀQȿR?V"g1Efn<ܫ|/9yd51/sPg*E7YϤÐZJ 㟽i<\&9 0ax>@ Ô+)kR/5Y aYea܏J}1U,"0A$EdIC$pa4m['Osj~\^?o]?Iy665y}d7m?W]>Z&h? vMDXX]x:LM&"Jnh;Sױ:`VRǠP@0qn~=81~Poe&S! LsF T^8:^7:]?};odS3~Z}  yR.Bö\P̍3e4~gJ=@--Ġַ}uIv!u'?)O[_~BJ PBDgM6  `G!l`ycߡ}Y{US?~3!_0jTM,`mRmdIyA̾GW% sY3pzh`UMC&zl'N4t32/c 2~=(h$%ϱj(R*D{o&$Ƣ f A?ˍֺ}/(-37<+Loo3xhT)84 $Pvo !]dZJv]'IiNar+?S a?LFhaR3$hWN[ϕఠ0Vm{gV)`{Xlb` N+qJdЫ 8DYqUA`naN坥CJ ^;]p?9SH礞rFN&zA۩W'=Y>4ﰌH|" |UBw.Ȩ*NX\,AYwɅD'^{ff5/PuRߜVUݢ ;#k";GO 7v vE d)|źag@4[1L踟7/a@RЫt ovam{f|]\^EEvpIiY Fc-Mz9rJ" ZZOrrKaĥ4z":꿻]y~~ -0 a^֠XSNbuxW\Dd0{%+H(oqܷz_e*Q gWO ͵Z{s MÅ*1āHU uc1EVg>g_^j>Va%΂&ཿ ~F܇qIQ4|^ ƏP^&8IA겲h*N$Jzh2hSFz¯UbfPMb0=*Ej;^5X?~yҘJj_ҜzY ^N; d+& 0Lf5م*BXm.T*S6>xcs~kȪ!&]&;Wuތe-FU_~50e+ 9|vCV"`;4lUX n=MQfKN {bg?@ͱ2epJřL0 &4KhYRvbHG!=>qKߖ,@~skOޗo-yK 򑹏;f!rȣF%S6O5{϶oiNC>Ď=N#ؓ!VRKHU~=~WK:1}cSUyzpR mBDVGx?<IKhdHDQwh5'74ۍu @PWCghMޗ; 6JM Jq/R0wth^`VDl5jxQ76~.0UnkXM~:Or}m) ؓ1;EȚuU/i}G5_3XKx)hNɾ!%zq) =?22ˆRPT+'@Wmn59--BQD\йjx-R.u>tao zZk\_ YcDs!]@R>w ¯_OU:>z Zw&%z&kHTnUQ/pbHT " @+2N}}p:y]Oܫx@n.}FpНV–gDziϑvrtQn`d TJKgIVz&8Y=C;ocza(wbg St9JEϠK+n|xdD˹ܟ(mC?ZUcfa kZ_Ƕ:d3kH A.')/ }ޜ&8yLn5YT8%nuy}Fh}ܸvMSƢk@+1|Te5z-_^Ϭ֧,W)b0BKԖn'*@NjshqPLl[n7xV9EϏ}#B \P;288{ֽFMN(wDy[i+>#zyM8 QkΗ6`XQP,hR b"c {/M* |VnՖ2ɦE+x?@8IńR}ۉa1jku3C'e ~+,-6j3U\F;P **_U :?A?"IM5z4ZxBV S;AE*2v8`$.a.`ϝ#à ]bάTfB v虤ĕ;EÅٸÝjτdKs9e3%(tAϵ.`=@1-%gŻURA/ fw`K1\oWZO]{# pyuw]̈́H~uʦ[st+m EpqՙujnRd)˛HنMnt<]x`0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 pB+?bIENDB`ic09@PNG  IHDRx IDATx U' YHXA-Gd@EAaDP@`D(*# (2$$[ $=tzn M:ݪT|^ssU@QO49R g/ɗ+$#4vG% ;6d2HdD9,8irY5y ?6Ye$)[ML7yM~lM+?"%}ɗ6=#?UL4_fP= Zrpo?126h1/c?n +?я2yI.s`MJvH÷7Lrg_UɾaŧT=U etrE l|Z~L5,u&cby=k:]F34|a6fmf i F:76)ߓ'ǟ}l"Erh)*E<nrPDc۴{сED9N{~]Xlif {qMaXTHbx݌/qp-#or@5yikX,Hc'ګ ~ۿ4WyHYoo@\&Ji8wOĕop7D~ҲzR@'dOY Y*jOH9W/_֞q _sQL bnO{2΁HHB22%]&hm3? _N2g t"].I9o_\307ͻ)ua#?;s>>)G} ǵԄ K KOs>YE ]h0l\P4XsazcPo*jv*WsE==@+I=yFq<& ?>`]:54UjkoF?:87*ol5[ etiPa2CJ,'lʄO#++W^˳o<ԋdO )NǾĪ'5[0dwRC&Dݎ!.:|۱=>i s.uQxo0pvE|ځ *ܿL{mB̦n\54Rh JE= ē4So~*|XN{2{g}Ӟ=.#YixzsIPWWsEPD{́n">rʑc5%/@ȥ~cIHqhY5uU˩0#S&`vQLfO5s$ltYbCncڣ 14D.gk!H&>&k4m؇M|σTY.Snm&_:сt״~ j7 ٓ"}'S#v'_f:(EOnۯi8-ع |T{t:`a5-bO[SF{@?9%6#W|j7GsS}О(o+:of\/?|U/\D"G ǓSL:m+WkwMC>o:Wdn@(w*f $͖-s* uu)}"~4i8C{;k\ZǁNW6.8bC< -Lvg2`ҀݷoV@<9dܙ\֥|Gj? )_HFV:0Hς>4[kEٙUs3"dC!?KNipcr oŽ~c $W"a}u(Vva $Do&l&zC}\WV]\8B9d>V $}ׁ֓R|H0e 8&aP{n1fpvK!OI3΁<;<.}߃ޅiRg /#;0as[ʺJ 8oCJ6dȹpWFt`jU 15#>wې3dU$Uo HmW~U}!KdnwxrIS-O>[9j 9M)%:V -&#?n@t2$S.{j SOP-N|v/w}@UaB $2D]ٓ/2ҁ{Ytvʕ._"T*yCm3W90bϭo?QK*M>LO1ݺI!7̽!qbAb7r t'7;0b=޷Jh{bSo!Hn@rS'Q9oy]= @;o.zT{|$Ln63<ƾ^zv/ϯ{>uQ#KFn@9,Xt=K!S::O>'@ I[zvIzOX2I/l3ud1]A޺< K뫃oejxey\}>sKnjߛ5~3ݣv. Gk/HfZ=<=>tTgl fpX0>[^^"f< %#s*]\:hMo Cb Ηh/Hx _-[rJ6zi<D ޳v[2Jj/H' Xr{J)nlDOǔ޷vMKėԱWUǁ3qbTgf ʄ{n@dIcbuw oKrR 5Dᆹ7OAFnS1Qo4dƔx9H{@ʾf v`0GC'lP-qm8S\;l{jS2Z{ AeNAS]viqo>pjo ga6$QÑkđ2 }LjY9"R$ c'괭6Me^j QKGTK ^V0[NP LQQW5(>&#Oj/-H_U1ľ[^YpuTzT)\zkL`.kx5Y1F7`7oj) N,=Q߅o̓[i 9bAe'$)[]L]cj)5ϯ{^5fI_e.WHzAyXvO@3I4v+\a h=c駩0nc|\}pƐcK 뫵{I7ŵ؛7JrxR<Y^=H>kmSY/LFS{K ,AC8' 3n=1s?v^v O<.)-kj)N~z ˿i/;pAeD8~wW6MuLCoL^zOt`0F_-^uTzr >MPj/?4^zP`4l(W}`){S]=1k/Aɵ s»Z05ׅ͑ #22ށAizURx5];)ӯZW6+P ցin{F;h2t). zT&㴗&?"Ϥ͓6ځ @u){0WK }F0Ul ;nq}ԤT{B6e Hc_kEސq3?6@4gN?S{SikTT׵-d/$ђi]txk){} 4@t!|΁yl=w ltꓦJM͹Nl4iS?sFr+{=Vx){z=5L|F{Bg!תs͜k,: ~qV j0t'gW{΢SǙ3=gL?C=: #3 {u9{߫[My0( /=Wlܬ}Ljj)ħ`Szo i/c"k5(sYg+\c ѫzP&/h/e|yosY 5={v5 ar$'90x"d=Vx)DEƜr'=G%lWMo3ܢ=JoGv`Dk\=G%l[MqpUr_4/V41d̊1sY u)GrgSW&4'?p`DfjOd }>}S輛ݤgLC5'cLY3֞6ަq|L6*e m1J/0`:wԭS{mV{iC[r`!iMdtYBlʃoh/mh _nU,17=7El_]MkיB )ožUf.2%d6e% @~Õкm5P(1d]"҄ @vu?Y~Ɨ۵7Mt3 qҞ26[75,o]zm̟8M u`Df]='elr.3Zsz KƗI csYTM}Mz5. 0_~ <>lk 7 Gk/sh'/O{>"P{*{RSY0R{CK@bȬ# R0soy%DAS&n=6TTuwPᅪaẃAKfl=6TuwM%MnRᅪYaw%W=6Tݔ=Vxԇ?;ځAKnמ*jvTRej$pt(= X2tP9AjʵW`Szp IDATHP/BLR2D{""l8*74`7i/7!p@zkL-2TlxE$rO>bI "N6J:Nރ?\BZU{"l1^3f\K<>b̒%`@u0{SUwfBla"}Pn5To|U{'E z`@35T_TrOJ5hCD Zikz7??_i!iCD R2eu޸78,^K!hɏ!"SkzWB-f^X!m`@^*2559(#^8c0H6M%8,ߵ\ap_>bs`HS[L6=ߵ?dyBlS'gN36[Zh -{zc <@9kGdܫi '?x;sF?Z{>">c-S#w?_N^hs7# @:k_M~,2T{/󙙟ў26M6Ua 3|zcܢ>bNI$ @:){|o.]>sz)ݵ?xr!i u7M!F.oK|!+=/El[r@W4.}R~,^`pCy% @ۏ%#oh/}|>r% @kz㱱zzma0^{eİdhå@ uBS<} C@Ě~R{~"K]a{m yX{CO80 bOɖ,af5]ӻg-2D_iOd 2S LAGd6eM<> r;WjOd w$S\ӫϞuch/{hgँ{lkzW-@^$#,Wi@Mw,C_{C=OiSdYӧ_[;~?(Tr󼛵) @u)u֚n^l;Kv7N :T{" S^oW{KÀb|E']e WrZF`+=_IlMa a-&#wj/wh~-=0rۗkWt&Ҋ= _h )ۥ=o lW=MqMor=1JN^МZF`%)=o lSטZl eO8{x9@{Cs\>0s»-: @S)$~OKėA  +=w l:V'L՘B|^Ji/uh' jY> @jSMg2xaiܿ~b{.!ŇVUe-o j9e)s:פ͓{hɗ9$#_S ʙc^]Mjj)[M}MpPA^7Ej(jmdb:LS  {gY%=s3ډ @Ce*ozlZɋE9-O{>r}){|NS-VS j/ohMFF <8|ɑkzWBn66ȹŁ-=8$SmuzlCj/mhv90`Tc/@r`){Mo)+]e҆dF5*WioQ.l-7X?Y^ܬ|Q/B{~Ҿ"S@}^IO)2T{YC[Ɓ )5kup)М}NIG1?v^V 0zzp_6Mkz9-jUyE{IC{y2߁޾L{ ҲfkzњE;%?i/gh/_QOKw;Z %Z_^^ anw|G+8ʪ˂ޅb+| /Cr*\՛Žnkz?z@k/YȦQQW;0ʷ|K?Q\M{Qg}K͓Gcw 4]ӻo/z`?u<9߁\>?}AϘzbց,^@? 02lvyQl^h,Zs Qas:uhP]oš6Up3z=9E{BT|9ǁd~FN){|*SAbX0~^;0МIçvsLi ȊG{Y'2J{B|\7::8Ĕ=kzmIŒwao>0 Qlp^vI4]ӻn@/uzp2҄xs4M>{gp%We_{Ww%œo8s{iӖ L.YW.?zDvJ^O tśJN SO1>;ݳ:紗$͓:0I'uڽ%gi){|K 'N'#^7n5w.SL-0~p}A}S:Sǣip)SuzB A'w90۝>Qv\G{SR)E2P{_AˁtNfXs zNǗ<%@N]>;T=̐QU{@yWz*b bD`SA_~<ԛhj/Hl[>9y>@<`[8'>E9t{&x;ީ>c.ɗ!rE1;Ε ~UzHwoT yH{I@)KO+GR%|F{+dr'w`*N_.v?cMg]ԛ|N{@j'`!u+O5Zm~Kr]FӁ3s~tS69P LT>º-5[+߹R}n*Z4HlHc/ww0[>QӁ\%AYuvT9ӿlrv>ʞAI$ձ<^ <M{˩fVOm;jwhf RSN QoNėb.2rDɑ +H`Qu޳1pZ ]N5&LN6H({6K+yr*|Im3^2vɡ\eڽg<9Y%pTʑt# **8.wΟqr0 ׀&Pi'ӶN@fnZU{8*)381㥧ijH9{=}³x[Q\4"1@UѰ_k'19 o]^x:Mw/;UK}'&؃Ԁr3'0Hg< tƚ5'= ze~/?s`‘f^:o!w.3][}'0ۤPje  W#{ȹod6e$֚/"7Mh%#_n@I?3:0I 9nqc;8k(YS}&:v+t3 vOBT|P@Yue&/+~WqInÀKj_x]˵!^5=l\p `j;R"k_@/>!I2[Z4ɇed0 ×LLc]k_lG{BTU{?n0'_M}\|yMv[c&^lK"1%DŽw]ȎU74sM0`sg&$o{}ǃsw6kG7tOcLkt/Td&1^*sǖ'Fy;慟Q[cHn@eb3*D)zٳ<;o\Ǡxsqfz)-Ȓ%?3ҟWϾ:8hi5Ln@z%`,GL9"˂,Ag-%oI `/q|ȊG[|O/iwx9H]铑g&X8S'W eӷN_g`53  I_9ׅA]e`6 /q`>0go;V:,8oyo1|7߱Y|wwXwy9sQS{eM'hG Z|@q#KdW'j2LC!{|L]M{ɗ  (Wo\4)/6rM$_FY@s 7dnw'@ /8pY 1u͂dd WI0NKiB:ߩۓgh dfgx  ģLh&dY- @gek©e~,@s!")&h27^ҬD7% !čTܨݚ>f:|!zY&rvK7O>gށ&D?6@Kat!$ԉ}=xi4&BHb>_rƓorIByR(õ WP a͊4|?Sk+}a $Y%rvK4\ Y@#7yljIɁ&Wof\8@؆X!ws<Ud4&GiHzi ien6`Mb7rvk+%ρGHfݔ@  $la@o dd48 Is6v4IBҘßIA'e4LBҐSfoYƁJH<$R 3Ml͔B]cγo xāJ7yFp e?d>HaBgkOQVoH:x ,") e:f8x9lFKOڍ((\GIWi?5:X@&$d(34w5: I/^/\6D}|'=~};!-Z{=e }<9_짫CS>!kbf(jOH?_>i_7^qxo:+ fE+| K{A '$_Fh}@ONl y-|<##RbRFfվ/ w#c=Wd|Ƨ7olp`A"e){>]nn))oo++=9KKbbe^ wwo66zZZ..tiiUUgll K__UUw#AA55O wwll^ރLL ##11(@@&&nn>mmDD GG3ZZUVOO~mff''_``FqqIIe((BBUUOO;;5H%5%cj0??(@ @  Mk& [' o0p!(n`w55RRccffYYDD""&99``e hh''+QQ߈((OxVV iiXX&&qppYYnnuu?&f,,ރvvUUBBD߆kYY00(ބ]]5 1**%225544,,dnn=ccLLY(())MM߈nn -\%%DD %%!!dw(( ߆]]^Y ZZHH tt*--j766UZZYss"" GFF0 dd++77ccޅ߈߈߈vvJJ10q>r s> Ju^'?????????(   "/U|xR)"ff"I>0'1+<./" oTCoVCkPwk44ZZ݁||VV00 HHނ@@ly<<߈++[hޅwwP.ރ oo^^JJ99n߈ooS FF33EQQ@@, ww^^offLLRNN77eBB--J [Aޅ__hh**%%ff t%% YZZ%%"";;+%%GGmee%%nnGGQ%%^^%%,,AA3%%LLo%% Uzz%%\\{%%00%%]] TT%% 66-%%xx;%%<<"H%%ff/U%%<bCC%%##&&Jn__%%IIBBVhkk%%nnOOP\xx%%\\COޅ%%--jj6B߈%%DDmm)5||%%DD``oo%%&&SScc%%FFNN%%]]11))%%AA %%fO%% 5%%UUii%%44LL((%%sM%%nn1 %%LLww<<%%))!!E%%-{{%%nn^^%%DD k %%%%tt%% '%%]]qq%%??)\\%%BB%% s%%UU WEEbb//@uuXXG2i ߈ ggPP088$$8 {{(388**&&ZZIIPPAA DD<<[VVLLI"PPxxttJJo>/M85)wfme0qj)Ddozymb@???????????PNG  IHDR\rf# IDATxixUݛ$6Yþ/A 6ӊm2 8ڍC4v"@\؂(["$$ar[!V%V[u'~9=y頻RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)$(m&ԧXJ U3pO8Lҡ+i'i$&@"&́$ @,9@6&l IGF-&t;&=‘c(cу{iut@l'z RfrR4JjM@',Tl=#6A&Ƞ jJ` oQ{Lt@Չ&I$|! +ҁ%gD(h5FQ}~(I&K:p )˩K4]xfEMvK0 %ǝULf3Mv01Xp|L՝Va0A|"ia`)>` ;"MVYH|L;;#O9/ S§ҁx&P'Aҡ(0y?A`@ZM"~far9M &c0](]^UDjI.[|Leo``JF"dLҡ*YOq at n 2VQ?ƟY|D<w[Kn eC"`<(Jq-L^mbгVOciݜ-iݜFQHL WyJBrJr8T|E8P|o!tNmP!fF 4\jj`<ZŴ_|?%).q݉Dž^pV6'>fcFr @&3TȞ$,:JRY34a(C:7ФF+LֱjdIpq_u~e \Ld-ʥtّ4 #G\ }3,=%GpD1d2? ޺b촌XyK:iR cFw#.|&e|/GFvK 'x)D6&4oHQe9˘p>yUl {!Mi0hm(b;H#lV h:㼅neJ ; ^eE`i@:Wsf,n%A_V^v t7}Mj4ajbXp\hQ~3`UcHat䚋1S9<$J_ ?os&L|Dt8p?xM/!/^TA4,K3|b3ZWHIIY0C*1E2SjOӪL(]FP:vNsc]WiA3+߽/%y< Y& `}CSmfaD^^AiS _uJj]]ո`q=xtiyrђv2Ɍ ;dPRP~׉fZ4wHn ;nλn*x WN~Knt|sX,w68uW5ޓngs?u? o|_v_Om9۷E_[[4tB?Iu''4IqIl곉Z[ E%4&c0VzvO36%oӚtc& ( w}qX~ND@o=XtMk&|]rUB/̤<-ݦ U:*$vε㟥[_)䗤aaK/aTC+je6NW[?>wyH#R Is>ӚG7gaV`7N2pt|E]*tl-kI"L$pK_bSZ`2jgvCDЇL2q5;pC/ V"i{@gIC嶆I\ħ|\(F5offiJD0N-L=NViDSeb%t33I!mVboC/`˜r.w&ᕊcSN-w3 =YB{6)IqIӄhl@Fo~1Xf!ro0lU|ߵ]L|#4l Kܵ>zӛьGqs}4QRmpWȠ8`$ǔV:Akt#E1!3C/䗘2{s+2s{FLe*2I#n@VN\X9I3dF$[Mn'}ɗ-vH#ʒ'g0*G6Iͤ[iӋ^Ld"J#bDmj1ǏvO0aɌ.EO:09Q&|lxNWx^hdIqI\tft8dL˅ Ɲv;q߸0.v1W|%##pd$ҏ뜌 `cs.;>]-5 q/'t:ݜ[nyPBIYvܿN+U t~]nusCLd"Ws5 - jU(RY>NV>n3OF1oF:ˍls8l(wp íEeEXr$+E6bmW@ÍNsGˎň#B-"np"V0"HR9v{*2ml'd7տɊbOKicbuMkQ[2 $F3]W:jC,r)#A8"nth_4W־RrdpB ;)N?B|?I+iïNOzp\Rڽ8KЂ,rq2 k-iNiFmheǑFd"[7 jrccù^qB/޾"|8sܽVw*|g J6n,XfWO&&ʋEѱfGǛt Seǖ:tu>.+NcV6)Y̢\sQZ(֥'ƶuJA21\ldh,q8E5 v>t>' 6iPM\˵f49,GbLbH<t[9L%5^-mŖ[r3oB@,8]yXŲntc Ke%-c >Le ݑ:F5 | p]GGwJbXGoz3ck]]{Pf_C.&!JT?VJN3*Ԗz,:x gsԍp|С<"|&1te%+-/^d=+Mh?0߰%y0= c} yE`=}`X<ʈrJa?<'B./gWfOqz8˾UX=(l;gW#.F{y'iNsKʳpY,:et9cYfZi-}[O, zoWY4` ,b8=p|S> FQe Sk4 h;s:8DgxKkԴ8} ,T)U.ԙΤ V8 J *ʖty'[+QrxyVIv܀Eמ/;cc*a~J%jP\u异fк[#n W^s4f v~} hˑN'y<ZY5 ьƐNVSn?uxVũ/V!F7Ig1ix MY2>>T240A\-<0hOq5RU* y̠+b~=}[^VՑ#+nWe9t,ȴ'e: w $WІ6˻d%(%6Z;( v(=gp}fe>Zjus<.vjV[e]a}?֘KY}l5AS7 &btu&B v:K@@ On^r- G{N^{Vi[q>|r-JkIKVj3#7%vba3NF2wCu,ptk 5pJ9dӻuV ^b&3ŢC2]o0-ݼ|cW' ݦw%Շ$ f4QKX~mo#=L+aX>Snpwou Asf_TULa hF|e``s|Slx! s>:ԑGi5 }S[t%4vG\fA&_Zθe@2G"uU,o H~ӧٳx9qG(/|ѓ3;ә4Xr^:1uTbgeyvf"H: u"갵VZFE Z[۽f$Rn{FWaPAivӄccL5щcI= ?33*+=/iI>o"$;𯂻PakY,=T: aE>sq3 khn\EcSMi-rܕnKu;$r3tU'`0_˥8W^ -)9"cN=T7& ]Bg䖭P,ta7sgU|b3 !J@^ ۶Fn ש*ǩe`0(|:cv׃a$?πOc$?I۽ , 0.dE5N7 Nq֛~уg9Bܛʣo{@r)z_NC0XȨNVM/H/E-$B_ϡC KaﭪP۶G .#hv-CqM1<|U\Ai>Ɔz)$ ZS9xEnzKK.aϙ=\U?\?7Hq)k//ߧߦ~=#p6|Rܱ?Cutm@ P.YflɛdαNM9W6AI?7Fك,0*of5p^n>wqTk8߀ҡ\J yrIG.rJrmjNH)|s Pa5;̛ޤ{ҡT+r{>lj+yS[ CXyz'Wr5sՙ8Sv&r[;?n_L$ydbG:`'h*JXħ5N~& +t %JUc8_>H^ G^s(+ In|C޽^'ќnft]G!Zrd wl >B)&1}ҁ\wGx_:P6HeN9$$J) 3)kOl\?#E~oex?dM%YXǨY_:W:op,$'$f A:Pxǭ nf1@0.8XңKӁ?Fp.$t U~WSOðCvLh:1M /K֙,^8z x*#ɝ v&~ `- 1G: z]}wO61P%GnλdfKTLe7O%Eh pt(v>CjB*#tHTjYɊ,Yƞ3{C\IatI`pt(Nj݂5ЯN?">۰(Xħ'>el8'?/Q(uU7vg8=z3'=j71&іW&&u&m6$X"QXMQ]dUwȠ1|xFXZFytsE5"!2Q hՈ8~O{@\r9*>ā|[muYM[%R'4LmQM I341l,\k! eP!xLVbO:)/τp?WTH87Hd{Tp kf/q1A (_ d_ bt`4ԥ0IG+LD9P_w)p_6~_{&}c~ @8 ]lG.$&C ǥRJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)R.x+OwwIENDB`QtAV-1.12.0/src/QtAV.qrc000066400000000000000000000001771312235004300145150ustar00rootroot00000000000000 i18n/QtAV_zh_CN.qm QtAV.svg QtAV-1.12.0/src/QtAV.rc000066400000000000000000000020661312235004300143330ustar00rootroot00000000000000#include "winver.h" #include "QtAV/version.h" IDI_ICON1 ICON DISCARDABLE "QtAV.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 PRODUCTVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEFLAGSMASK 0x3fL FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000004b0" BEGIN VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" VALUE "FileDescription", "QtAV Multimedia framework. http://qtav.org" VALUE "FileVersion", QTAV_VERSION_STR ".0" VALUE "LegalCopyright", "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" VALUE "InternalName", "QtAV" VALUE "OriginalFilename", "QtAV.dll" VALUE "ProductName", "QtAV" VALUE "ProductVersion", QTAV_VERSION_STR ".0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0, 1200 END END QtAV-1.12.0/src/QtAV.svg000066400000000000000000000015501312235004300145230ustar00rootroot00000000000000 QtAV-1.12.0/src/QtAV/000077500000000000000000000000001312235004300140015ustar00rootroot00000000000000QtAV-1.12.0/src/QtAV/AVClock.h000066400000000000000000000147371312235004300154500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVCLOCK_H #define QTAV_AVCLOCK_H #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #include #else #include typedef QTime QElapsedTimer; #endif /* * AVClock is created by AVPlayer. The only way to access AVClock is through AVPlayer::masterClock() * The default clock type is Audio's clock, i.e. vedio synchronizes to audio. If audio stream is not * detected, then the clock will set to External clock automatically. * I name it ExternalClock because the clock can be corrected outside, though it is a clock inside AVClock */ namespace QtAV { static const double kThousandth = 0.001; class Q_AV_EXPORT AVClock : public QObject { Q_OBJECT public: typedef enum { AudioClock, ExternalClock, VideoClock //sync to video timestamp } ClockType; AVClock(ClockType c, QObject* parent = 0); AVClock(QObject* parent = 0); void setClockType(ClockType ct); ClockType clockType() const; bool isActive() const; /*! * \brief setInitialValue * Usually for ExternalClock. For example, media start time is not 0, clock have to set initial value as media start time */ void setInitialValue(double v); double initialValue() const; /* * auto clock: use audio clock if audio stream found, otherwise use external clock */ void setClockAuto(bool a); bool isClockAuto() const; /*in seconds*/ inline double pts() const; /*! * \brief value * the real timestamp in seconds: pts + delay * \return */ inline double value() const; inline void updateValue(double pts); //update the pts /*used when seeking and correcting from external*/ void updateExternalClock(qint64 msecs); /*external clock outside still running, so it's more accurate for syncing multiple clocks serially*/ void updateExternalClock(const AVClock& clock); inline void updateVideoTime(double pts); inline double videoTime() const; inline double delay() const; //playing audio spends some time inline void updateDelay(double delay); inline qreal diff() const; void setSpeed(qreal speed); inline qreal speed() const; bool isPaused() const; /*! * \brief syncStart * For internal use now * Start to sync "count" objects. Call syncEndOnce(id) "count" times to end sync. * \param count Number of objects to sync. Each one should call syncEndOnce(int id) * \return an id */ int syncStart(int count); int syncId() const {return sync_id;} /*! * \brief syncEndOnce * Decrease sync objects count if id is current sync id. * \return true if sync is end for id or id is not current sync id */ bool syncEndOnce(int id); Q_SIGNALS: void paused(bool); void paused(); //equals to paused(true) void resumed();//equals to paused(false) void started(); void resetted(); public Q_SLOTS: //these slots are not frequently used. so not inline /*start the external clock*/ void start(); /*pause external clock*/ void pause(bool p); /*reset clock intial value and external clock parameters (and stop timer). keep speed() and isClockAuto()*/ void reset(); protected: virtual void timerEvent(QTimerEvent *event); private Q_SLOTS: /// make sure QBasic timer start/stop in a right thread void restartCorrectionTimer(); void stopCorrectionTimer(); private: bool auto_clock; int m_state; ClockType clock_type; mutable double pts_; mutable double pts_v; double delay_; mutable QElapsedTimer timer; qreal mSpeed; double value0; /*! * \brief correction_schedule_timer * accumulative error is too large using QElapsedTimer.restart() frequently. * we periodically correct value() to keep the error always less * than the error of calling QElapsedTimer.restart() once * see github issue 46, 307 etc */ QBasicTimer correction_schedule_timer; qint64 t; // absolute time for elapsed timer correction static const int kCorrectionInterval = 1; // 1000ms double last_pts; double avg_err; // average error of restart() mutable int nb_restarted; QAtomicInt nb_sync; int sync_id; }; double AVClock::value() const { if (clock_type == AudioClock) { // TODO: audio clock need a timer too // timestamp from media stream is >= value0 return pts_ == 0 ? value0 : pts_ + delay_; } else if (clock_type == ExternalClock) { if (timer.isValid()) { ++nb_restarted; pts_ += (double(timer.restart()) * kThousandth + avg_err)* speed(); } else {//timer is paused //qDebug("clock is paused. return the last value %f", pts_); } return pts_ + value0; } else { return pts_v; // value0 is 1st video pts_v already } } void AVClock::updateValue(double pts) { if (clock_type == AudioClock) pts_ = pts; } void AVClock::updateVideoTime(double pts) { pts_v = pts; if (clock_type == VideoClock) timer.restart(); } double AVClock::videoTime() const { return pts_v; } double AVClock::delay() const { return delay_; } void AVClock::updateDelay(double delay) { delay_ = delay; } qreal AVClock::diff() const { return value() - videoTime(); } qreal AVClock::speed() const { return mSpeed; } } //namespace QtAV #endif // QTAV_AVCLOCK_H QtAV-1.12.0/src/QtAV/AVDecoder.h000066400000000000000000000075001312235004300157500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_DECODER_H #define QAV_DECODER_H #include #include #include namespace QtAV { class Packet; class AVDecoderPrivate; class Q_AV_EXPORT AVDecoder : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(AVDecoder) //Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) public: virtual ~AVDecoder(); virtual QString name() const; virtual QString description() const; /*! * default is open FFmpeg codec context * codec config must be done before open * NOTE: open() and close() are not thread safe. You'd better call them in the same thread. */ virtual bool open(); virtual bool close(); bool isOpen() const; virtual void flush(); void setCodecContext(void* codecCtx); //protected void* codecContext() const; /*not available if AVCodecContext == 0*/ bool isAvailable() const; virtual bool decode(const Packet& packet) = 0; int undecodedSize() const; //TODO: remove. always decode whole input data completely // avcodec_open2 /*! * \brief setOptions * 1. If has key "avcodec", it's value (suboption, a hash or map) will be used to set AVCodecContext use av_opt_set and av_dict_set. A value of hash type is ignored. * we can ignore the flags used in av_dict_xxx because we can use hash api. * empty value does nothing to current context if it is open, but will clear AVDictionary in the next open. * AVDictionary is used in avcodec_open2() and will not change unless user call setOptions(). * 2. Set QObject properties for AVDecoder. Use AVDecoder::name() or lower case as a key to set properties. If key not found, assume key is "avcodec" * 3. If no ket AVDecoder::name() found in the option, set key-value pairs as QObject property-value pairs. * \param dict * example: * "avcodec": {"vismv":"pf"}, "vaapi":{"display":"DRM"}, "copyMode": "ZeroCopy" * means set avcodec context option vismv=>pf, VA-API display (qt property) to DRM when using VA-API, set copyMode (GPU decoders) property to ZeroCopy */ void setOptions(const QVariantHash &dict); QVariantHash options() const; Q_SIGNALS: void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax void descriptionChanged(); protected: AVDecoder(AVDecoderPrivate& d); DPTR_DECLARE(AVDecoder) // force a codec. only used by avcodec sw decoders. TODO: move to public? profile set? void setCodecName(const QString& name); QString codecName() const; virtual void codecNameChanged() {}//signals can not be decared virtual (winrt) private: Q_DISABLE_COPY(AVDecoder) AVDecoder(); // base class, not direct create. only final class has is enough }; } //namespace QtAV #endif // QAV_DECODER_H QtAV-1.12.0/src/QtAV/AVDemuxer.h000066400000000000000000000176661312235004300160320ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_DEMUXER_H #define QAV_DEMUXER_H #include #include #include #include #include struct AVFormatContext; struct AVCodecContext; QT_BEGIN_NAMESPACE class QIODevice; QT_END_NAMESPACE // TODO: force codec name. clean code namespace QtAV { class AVError; class MediaIO; class Q_AV_EXPORT AVDemuxer : public QObject { Q_OBJECT public: enum StreamType { //TODO: move to common MediaType AudioStream, VideoStream, SubtitleStream, }; static const QStringList& supportedFormats(); static const QStringList& supportedExtensions(); /// Supported ffmpeg/libav input protocols(not complete). A static string list static const QStringList& supportedProtocols(); AVDemuxer(QObject *parent = 0); ~AVDemuxer(); MediaStatus mediaStatus() const; bool atEnd() const; QString fileName() const; QIODevice* ioDevice() const; /// not null for QIODevice, custom protocols MediaIO* mediaIO() const; /*! * \brief setMedia * \return whether the media source is changed */ bool setMedia(const QString& fileName); bool setMedia(QIODevice* dev); bool setMedia(MediaIO* in); /*! * \brief setFormat * Force the input format. Useful if input stream is a raw video stream(fmt="rawvideo). * formatForced() is reset if media changed. So you have to call setFormat() for every media * you want to force the format. * If AVFormatContext.format_whitelist contains only 1 format, then that format will be forced. * For example, setOptions({"format_whitelist": "rawvideo"}) */ void setFormat(const QString& fmt); QString formatForced() const; bool load(); bool unload(); bool isLoaded() const; /*! * \brief readFrame * Read a packet from 1 of the streams. use packet() to get the result packet. packet() returns last valid packet. * So do not use packet() if readFrame() failed. * Call readFrame() and seek() in the same thread. * \return true if no error. false if error occurs, eof reaches, interrupted by user or time out(getInterruptTimeout()) */ bool readFrame(); // TODO: rename int readPacket(), return stream number /*! * \brief packet * return the packet read by demuxer. packet is invalid if readFrame() returns false. */ Packet packet() const; /*! * \brief stream * Current readFrame() readed stream index. */ int stream() const; bool isSeekable() const; // TODO: change in unload? void setSeekUnit(SeekUnit unit); SeekUnit seekUnit() const; void setSeekType(SeekType target); SeekType seekType() const; /*! * \brief seek * seek to a given position. Only support timestamp seek now. * Experiment: if pos is out of range (>duration()), do nothing unless a seekable and variableSize MediaIO is used. * \return false if fail */ bool seek(qint64 pos); //pos: ms /*! * \brief seek * Percentage seek. duration() must be >0LL * \param q [0, 1] * TODO: what if duration() is not valid but size is known? */ bool seek(qreal q); AVFormatContext* formatContext(); QString formatName() const; QString formatLongName() const; // TODO: rename startPosition() qint64 startTime() const; //ms, AVFormatContext::start_time/1000 qint64 duration() const; //ms, AVFormatContext::duration/1000 qint64 startTimeUs() const; //us, AVFormatContext::start_time qint64 durationUs() const; //us, AVFormatContext::duration //total bit rate int bitRate() const; //AVFormatContext::bit_rate qreal frameRate() const; //deprecated AVStream::avg_frame_rate // if stream is -1, return the current video(or audio if no video) stream. // TODO: audio/videoFrames? qint64 frames(int stream = -1) const; //AVFormatContext::nb_frames bool hasAttacedPicture() const; /*! * \brief setStreamIndex * Set stream by index in stream list. call it after loaded. * Stream/index will not change in next load() unless media source changed * index < 0 is invalid */ bool setStreamIndex(StreamType st, int index); // current open stream int currentStream(StreamType st) const; QList streams(StreamType st) const; // TODO: stream(StreamType), streams(StreamType) // current open stream int audioStream() const; QList audioStreams() const; int videoStream() const; QList videoStreams() const; int subtitleStream() const; QList subtitleStreams() const; //codec. stream < 0: the stream going to play (or the stream set by setStreamIndex()) AVCodecContext* audioCodecContext(int stream = -1) const; AVCodecContext* videoCodecContext(int stream = -1) const; AVCodecContext* subtitleCodecContext(int stream = -1) const; /** * @brief getInterruptTimeout return the interrupt timeout */ qint64 getInterruptTimeout() const; /** * @brief setInterruptTimeout set the interrupt timeout * @param timeout in ms */ void setInterruptTimeout(qint64 timeout); bool isInterruptOnTimeout() const; void setInterruptOnTimeout(bool value); /** * @brief getInterruptStatus return the interrupt status. * \return -1: interrupted by user * 0: not interrupted * >0: timeout value of AVError::ErrorCode */ int getInterruptStatus() const; /** * @brief setInterruptStatus set the interrupt status * @param interrupt <0: abort current operation like loading and reading packets. * 0: no interrupt */ void setInterruptStatus(int interrupt); /*! * \brief setOptions * libav's AVDictionary. we can ignore the flags used in av_dict_xxx because we can use hash api. * empty value does nothing to current context if it is open, but will change AVDictionary options to null in next open. * AVDictionary is used in avformat_open_input() and will not change unless user call setOptions() * If an option is not found */ void setOptions(const QVariantHash &dict); QVariantHash options() const; Q_SIGNALS: void unloaded(); void userInterrupted(); //NO direct connection because it's emit before interrupted happens void loaded(); /*emit when the first frame is read*/ void started(); void finished(); //end of file void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax void mediaStatusChanged(QtAV::MediaStatus status); void seekableChanged(); private: void setMediaStatus(MediaStatus status); // error code (errorCode) and message (msg) may be modified internally void handleError(int averr, AVError::ErrorCode* errorCode, QString& msg); class Private; QScopedPointer d; class InterruptHandler; friend class InterruptHandler; }; } //namespace QtAV #endif // QAV_DEMUXER_H QtAV-1.12.0/src/QtAV/AVEncoder.h000066400000000000000000000076021312235004300157650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_ENCODER_H #define QAV_ENCODER_H #include #include #include #include #include namespace QtAV { class AVEncoderPrivate; class Q_AV_EXPORT AVEncoder : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(AVEncoder) Q_PROPERTY(int bitRate READ bitRate WRITE setBitRate NOTIFY bitRateChanged) Q_PROPERTY(QString codecName READ codecName WRITE setCodecName NOTIFY codecNameChanged) Q_PROPERTY(TimestampMode timestampMode READ timestampMode WRITE setTimestampMode NOTIFY timestampModeChanged) Q_ENUMS(TimestampMode) public: enum TimestampMode { TimestampMonotonic, TimestampCopy, }; virtual ~AVEncoder(); virtual QString name() const = 0; virtual QString description() const; /*! * \brief setCodecName * An encoder can support more than 1 codec. */ void setCodecName(const QString& name); QString codecName() const; bool open(); bool close(); bool isOpen() const; virtual void flush(); Packet encoded() const; /*! * used by ff muxer. Be sure all parameters are set. (open?) */ virtual void copyAVCodecContext(void* ctx); void* codecContext() const; // TODO: always have a avctx like decoder? /*! * \brief setBitRate * Higher bit rate result in better quality. * Default for video: 400000, audio: 64000 */ void setBitRate(int value); int bitRate() const; TimestampMode timestampMode() const; void setTimestampMode(TimestampMode value); /*! * \brief setOptions * 1. If has key "avcodec", it's value (suboption, a hash or map) will be used to set AVCodecContext use av_opt_set and av_dict_set. A value of hash type is ignored. * we can ignore the flags used in av_dict_xxx because we can use hash api. * empty value does nothing to current context if it is open, but will clear AVDictionary in the next open. * AVDictionary is used in avcodec_open2() and will not change unless user call setOptions(). * 2. Set QObject properties for AVEncoder. Use AVEncoder::name() or lower case as a key to set properties. If key not found, assume key is "avcodec" * 3. If no ket AVEncoder::name() found in the option, set key-value pairs as QObject property-value pairs. */ void setOptions(const QVariantHash &dict); QVariantHash options() const; Q_SIGNALS: void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax void codecNameChanged(); void bitRateChanged(); void timestampModeChanged(TimestampMode mode); protected: AVEncoder(AVEncoderPrivate& d); DPTR_DECLARE(AVEncoder) private: Q_DISABLE_COPY(AVEncoder) AVEncoder(); // base class, not direct create. only final class has is enough }; } //namespace QtAV #endif // QAV_ENCODER_H QtAV-1.12.0/src/QtAV/AVError.h000066400000000000000000000065251312235004300155020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVERROR_H #define QTAV_AVERROR_H #include #include namespace QtAV { class Q_AV_EXPORT AVError { public: enum ErrorCode { NoError, //open/read/seek network stream error. value must be less then ResourceError because of correct_error_by_ffmpeg NetworkError, // all above and before NoError are NetworkError OpenTimedout, OpenError, ParseStreamTimedOut, FindStreamInfoTimedout = ParseStreamTimedOut, ParseStreamError, FindStreamInfoError = ParseStreamError, StreamNotFound, //a,v,s? ReadTimedout, ReadError, SeekError, ResourceError, // all above and before NetworkError are ResourceError OpenCodecError, CloseCodecError, AudioCodecNotFound, VideoCodecNotFound, SubtitleCodecNotFound, CodecError, // all above and before NoError are CodecError FormatError, // all above and before CodecError are FormatError // decrypt error. Not implemented AccessDenied, // all above and before NetworkError are AccessDenied UnknowError }; AVError(); AVError(ErrorCode code, int ffmpegError = 0); /*! * \brief AVError * string() will be detail. If ffmpeg error not 0, also contains ffmpegErrorString() * \param code ErrorCode value * \param detail ErrorCode string will be overrided by detail. * \param ffmpegError ffmpeg error code. If not 0, string() will contains ffmpeg error string. */ AVError(ErrorCode code, const QString& detail, int ffmpegError = 0); AVError(const AVError& other); AVError &operator=(const AVError &other); bool operator==(const AVError &other) const; inline bool operator!=(const AVError &other) const { return !(*this == other); } void setError(ErrorCode ec); ErrorCode error() const; QString string() const; int ffmpegErrorCode() const; QString ffmpegErrorString() const; private: ErrorCode mError; int mFFmpegError; QString mDetail; }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::AVError) #ifndef QT_NO_DEBUG_STREAM QT_BEGIN_NAMESPACE class QDebug; QT_END_NAMESPACE Q_AV_EXPORT QDebug operator<<(QDebug debug, const QtAV::AVError &error); #endif #endif // QTAV_AVERROR_H QtAV-1.12.0/src/QtAV/AVMuxer.h000066400000000000000000000057371312235004300155150ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AVMUXER_H #define QAV_AVMUXER_H #include #include #include #include #include #include #include namespace QtAV { class MediaIO; class VideoEncoder; class AudioEncoder; class Q_AV_EXPORT AVMuxer : public QObject { Q_OBJECT public: static const QStringList& supportedFormats(); static const QStringList& supportedExtensions(); /// Supported ffmpeg/libav input protocols(not complete). A static string list static const QStringList& supportedProtocols(); AVMuxer(QObject *parent = 0); ~AVMuxer(); QString fileName() const; QIODevice* ioDevice() const; /// not null for QIODevice, custom protocols MediaIO *mediaIO() const; /*! * \brief setMedia * \return whether the media is changed */ bool setMedia(const QString& fileName); bool setMedia(QIODevice* dev); bool setMedia(MediaIO* io); /*! * \brief setFormat * Force the output format. * formatForced() is reset if media changed. So you have to call setFormat() for every media * you want to force the format. * Also useful for custom io */ void setFormat(const QString& fmt); QString formatForced() const; bool open(); bool close(); bool isOpen() const; // TODO: copyAudioContext(void* avctx) for copy encoding without decoding void copyProperties(VideoEncoder* enc); //rename to setEncoder void copyProperties(AudioEncoder* enc); void setOptions(const QVariantHash &dict); QVariantHash options() const; public Q_SLOTS: // TODO: multiple streams. Packet.type,stream bool writeAudio(const QtAV::Packet& packet); bool writeVideo(const QtAV::Packet& packet); //void writeHeader(); //void writeTrailer(); private: class Private; QScopedPointer d; }; } //namespace QtAV #endif //QAV_AVMUXER_H QtAV-1.12.0/src/QtAV/AVOutput.h000066400000000000000000000067301312235004300157070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AVOUTPUT_H #define QAV_AVOUTPUT_H #include #include /*! * TODO: add api id(), name(), detail() */ namespace QtAV { class AVDecoder; class AVOutputPrivate; class Filter; class Statistics; class OutputSet; class Q_AV_EXPORT AVOutput { DPTR_DECLARE_PRIVATE(AVOutput) public: AVOutput(); virtual ~AVOutput(); bool isAvailable() const; //void addSource(AVPlayer* player); //call player.addVideoRenderer(this) //void removeSource(AVPlayer* player); //Demuxer thread automatically paused because packets will be full //only pause the renderering, the thread going on. If all outputs are paused, then pause the thread(OutputSet.tryPause) //TODO: what about audio's pause api? void pause(bool p); //processEvents when waiting? bool isPaused() const; QList& filters(); /*! * \brief installFilter * Insert a filter at position 'index' of current filter list. * If the filter is already installed, it will move to the correct index. * \param index A nagative index == size() + index. If index >= size(), append at last * \return false if already installed */ bool installFilter(Filter *filter, int index = 0x7fffffff); bool uninstallFilter(Filter *filter); protected: AVOutput(AVOutputPrivate& d); /* * If the pause state is true setted by pause(true), then block the thread and wait for pause state changed, i.e. pause(false) * and return true. Otherwise, return false immediatly. */ Q_DECL_DEPRECATED bool tryPause(); //move to OutputSet //TODO: we need an active set void addOutputSet(OutputSet *set); void removeOutputSet(OutputSet *set); void attach(OutputSet *set); //add this to set void detach(OutputSet *set = 0); //detatch from (all, if 0) output set(s) // for thread safe void hanlePendingTasks(); DPTR_DECLARE(AVOutput) private: // for proxy VideoOutput virtual void setStatistics(Statistics* statistics); //called by friend AVPlayer virtual bool onInstallFilter(Filter *filter, int index); virtual bool onUninstallFilter(Filter *filter); // only called in handlePaintEvent. But filters may change. so required by proxy to update it's filters virtual bool onHanlePendingTasks(); //return true: proxy update filters friend class AVPlayer; friend class OutputSet; friend class VideoOutput; }; } //namespace QtAV #endif //QAV_AVOUTPUT_H QtAV-1.12.0/src/QtAV/AVPlayer.h000066400000000000000000000605271312235004300156470ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVPLAYER_H #define QTAV_AVPLAYER_H #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QIODevice; QT_END_NAMESPACE namespace QtAV { class MediaIO; class AudioOutput; class VideoRenderer; class Filter; class AudioFilter; class VideoFilter; class VideoCapture; /*! * \brief The AVPlayer class * Preload: * \code * player->setFile(...); * player->load(); * do some thing... * player->play(); * \endcode * No preload: * \code * player->setFile(...); * player->play(); * \endcode */ class Q_AV_EXPORT AVPlayer : public QObject { Q_OBJECT Q_PROPERTY(bool relativeTimeMode READ relativeTimeMode WRITE setRelativeTimeMode NOTIFY relativeTimeModeChanged) Q_PROPERTY(bool autoLoad READ isAutoLoad WRITE setAutoLoad NOTIFY autoLoadChanged) Q_PROPERTY(bool asyncLoad READ isAsyncLoad WRITE setAsyncLoad NOTIFY asyncLoadChanged) Q_PROPERTY(qreal bufferProgress READ bufferProgress NOTIFY bufferProgressChanged) Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged) Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) Q_PROPERTY(qint64 position READ position WRITE setPosition NOTIFY positionChanged) Q_PROPERTY(qint64 startPosition READ startPosition WRITE setStartPosition NOTIFY startPositionChanged) Q_PROPERTY(qint64 stopPosition READ stopPosition WRITE setStopPosition NOTIFY stopPositionChanged) Q_PROPERTY(qint64 repeat READ repeat WRITE setRepeat NOTIFY repeatChanged) Q_PROPERTY(int currentRepeat READ currentRepeat NOTIFY currentRepeatChanged) Q_PROPERTY(qint64 interruptTimeout READ interruptTimeout WRITE setInterruptTimeout NOTIFY interruptTimeoutChanged) Q_PROPERTY(bool interruptOnTimeout READ isInterruptOnTimeout WRITE setInterruptOnTimeout NOTIFY interruptOnTimeoutChanged) Q_PROPERTY(int notifyInterval READ notifyInterval WRITE setNotifyInterval NOTIFY notifyIntervalChanged) Q_PROPERTY(int brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(int contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(int saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged) Q_PROPERTY(QtAV::MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged) Q_PROPERTY(QtAV::MediaEndAction mediaEndAction READ mediaEndAction WRITE setMediaEndAction NOTIFY mediaEndActionChanged) Q_ENUMS(State) public: /*! * \brief The State enum * The playback state. It's different from MediaStatus. MediaStatus indicates media stream state */ enum State { StoppedState, PlayingState, /// Start to play if it was stopped, or resume if it was paused PausedState }; /// Supported input protocols. A static string list static const QStringList& supportedProtocols(); explicit AVPlayer(QObject *parent = 0); ~AVPlayer(); /*! * \brief masterClock * setClockType() should call when playback started. * \return */ AVClock* masterClock(); // If path is different from previous one, the stream to play will be reset to default. /*! * \brief setFile * TODO: Set current media source if current media is invalid or auto load is enabled. * Otherwise set as the pendding media and it becomes the current media if the next * load(), play() is called * \param path */ void setFile(const QString& path); QString file() const; /*! * \brief setIODevice * Play media stream from QIODevice. AVPlayer does not take the ownership. You have to manage device lifetime. */ void setIODevice(QIODevice* device); /*! * \brief setInput * Play media stream from custom MediaIO. AVPlayer's demuxer takes the ownership. Call it when player is stopped. */ void setInput(MediaIO* in); MediaIO* input() const; bool isLoaded() const; /*! * \brief setAsyncLoad * async load is enabled by default */ void setAsyncLoad(bool value = true); bool isAsyncLoad() const; /*! * \brief setAutoLoad * true: current media source changed immediatly and stop current playback if new media source is set. * status becomes LoadingMedia=>LoadedMedia before play( and BufferedMedia when playing?) * false: * Default is false */ void setAutoLoad(bool value = true); // NOT implemented bool isAutoLoad() const; // NOT implemented MediaStatus mediaStatus() const; // TODO: add hasAudio, hasVideo, isMusic(has pic) /*! * \brief relativeTimeMode * true (default): mediaStartPosition() is always 0. All time related API, for example setPosition(), position() and positionChanged() * use relative time instead of real pts * false: mediaStartPosition() is from media stream itself, same as absoluteMediaStartPosition() * To get real start time, use statistics().start_time. Or setRelativeTimeMode(false) first but may affect playback when playing. */ bool relativeTimeMode() const; /// Media stream property. The first timestamp in the media qint64 absoluteMediaStartPosition() const; qreal durationF() const; //unit: s, This function may be removed in the future. qint64 duration() const; //unit: ms. media duration. network stream may be very small, why? /*! * \brief mediaStartPosition * If relativeTimeMode() is true (default), it's 0. Otherwise is the same as absoluteMediaStartPosition() */ qint64 mediaStartPosition() const; /// mediaStartPosition() + duration(). qint64 mediaStopPosition() const; qreal mediaStartPositionF() const; //unit: s qreal mediaStopPositionF() const; //unit: s // can set by user. may be not the real media start position. qint64 startPosition() const; /*! * \brief stopPosition: the position at which player should stop playing * \return * If media stream is not a local file, stopPosition()==max value of qint64 */ qint64 stopPosition() const; //unit: ms qint64 position() const; //unit: ms //0: play once. N: play N+1 times. <0: infinity int repeat() const; //or repeatMax()? /*! * \brief currentRepeat * \return -1 if not playback is stopped, otherwise (Playback times - 1) */ int currentRepeat() const; /*! * \brief setExternalAudio * set audio track from an external audio stream. this will try to load the external audio and * select the 1st audio stream. If no error happens, the external audio stream will be set to * current audio track. * If external audio stream <0 before play, stream is auto selected * You have to manually empty value to unload the external audio! * \param file external audio file path. Set empty to use internal audio tracks. TODO: reset stream number if switch to internal * \return true if no error happens */ bool setExternalAudio(const QString& file); QString externalAudio() const; /*! * \brief externalAudioTracks * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support. * [ {id: 0, file: abc.dts, language: eng, title: xyz}, ...] * id: used for setAudioStream(id) * \sa externalAudioTracksChanged */ const QVariantList& externalAudioTracks() const; const QVariantList& internalAudioTracks() const; const QVariantList& internalVideoTracks() const; /*! * \brief setAudioStream * set an external audio file and stream number as audio track. It will be reset if setFile()/setIODevice()/setInput() is called * \param file external audio file. set empty to use internal audio tracks * \param n audio stream number n=0, 1, .... n<0: disable audio thread * \return false if fail */ bool setAudioStream(const QString& file, int n = 0); /*! * set audio/video/subtitle stream to n. n=0, 1, 2..., means the 1st, 2nd, 3rd audio/video/subtitle stream * If n < 0, there will be no audio thread and sound/ * If a new file is set(except the first time) then a best stream will be selected. If the file not changed, * e.g. replay, then the stream not change * return: false if stream not changed, not valid * TODO: rename to track instead of stream */ /*! * \brief setAudioStream * Set audio stream number in current media or external audio file */ bool setAudioStream(int n); //TODO: n<0, no video thread. It will be reset if setFile()/setIODevice()/setInput() is called bool setVideoStream(int n); /*! * \brief internalAudioTracks * A list of QVariantMap. Using QVariantMap and QVariantList is mainly for QML support. * [ {id: 0, file: abc.dts, language: eng, title: xyz}, ...] * id: used for setSubtitleStream(id) * \sa internalSubtitleTracksChanged; * Different with external audio tracks, the external subtitle is supported by class Subtitle. */ const QVariantList& internalSubtitleTracks() const; bool setSubtitleStream(int n); int currentAudioStream() const; int currentVideoStream() const; int currentSubtitleStream() const; int audioStreamCount() const; int videoStreamCount() const; int subtitleStreamCount() const; /*! * \brief videoCapture * Capture the current frame using videoCapture()->capture() * \sa VideoCapture */ VideoCapture *videoCapture() const; //TODO: no replay, replay without parsing the stream if it's already loaded. (not implemented). to force reload the stream, unload() then play() /*! * \brief play * If isAsyncLoad() is true (default), play() will return immediately. Signals started() and stateChanged() will be emitted if media is loaded and playback starts. */ void play(const QString& path); bool isPlaying() const; bool isPaused() const; /*! * \brief state * Player's playback state. Default is StoppedState. * setState() is a replacement of play(), stop(), pause(bool) * \return */ State state() const; void setState(State value); // TODO: use id as parameter and return ptr? void addVideoRenderer(VideoRenderer *renderer); void removeVideoRenderer(VideoRenderer *renderer); void clearVideoRenderers(); void setRenderer(VideoRenderer* renderer); VideoRenderer* renderer(); QList videoOutputs(); /*! * \brief audio * AVPlayer always has an AudioOutput instance. You can access or control audio output properties through audio(). * To disable audio output, set audio()->setBackends(QStringList() << "null") before starting playback * \return */ AudioOutput* audio(); /*! * \brief setSpeed * Set playback speed. * \param speed speed > 0. 1.0: normal speed * TODO: playbackRate */ void setSpeed(qreal speed); qreal speed() const; /*! * \brief setInterruptTimeout * Emit error(usually network error) if open/read spends too much time. * If isInterruptOnTimeout() is true, abort current operation and stop playback * \param ms milliseconds. <0: never interrupt. */ /// TODO: rename to timeout void setInterruptTimeout(qint64 ms); qint64 interruptTimeout() const; /*! * \brief setInterruptOnTimeout * \param value */ void setInterruptOnTimeout(bool value); bool isInterruptOnTimeout() const; /*! * \brief setFrameRate * Force the (video) frame rate to a given value. * AVClock::ClockType and autoClock will be changed internally. * \param value <=0: use previous playback speed. * >0: force to a given (video) frame rate */ void setFrameRate(qreal value); qreal forcedFrameRate() const; //Statistics& statistics(); const Statistics& statistics() const; /*! * \brief installFilter * Insert a filter at position 'index' of current filter list. * If the filter is already installed, it will move to the correct index. * \param index A nagative index == size() + index. If index >= size(), append at last * \return false if audio/video thread is not ready. But the filter will be installed when thread is ready. * false if already installed. */ bool installFilter(AudioFilter* filter, int index = 0x7FFFFFFF); bool installFilter(VideoFilter* filter, int index = 0x7FFFFFFF); bool uninstallFilter(AudioFilter* filter); bool uninstallFilter(VideoFilter* filter); QList audioFilters() const; QList videoFilters() const; /*! * \brief setPriority * A suitable decoder will be applied when video is playing. The decoder does not change in current playback if no decoder is found. * If not playing or no decoder found, the decoder will be changed at the next playback * \param ids */ void setPriority(const QVector& ids); /*! * \brief setVideoDecoderPriority * also can set in opt.priority * \param names the video decoder name list in priority order. Name can be "FFmpeg", "CUDA", "DXVA", "D3D11", "VAAPI", "VDA", "VideoToolbox", "MediaCodec", "MMAL", "QSV", "CrystalHD", case insensitive */ void setVideoDecoderPriority(const QStringList& names); QStringList videoDecoderPriority() const; //void setPriority(const QVector& ids); /*! * below APIs are deprecated. * TODO: setValue("key", value) or setOption("key", value) ? * enum OptionKey { Brightness, ... VideoCodec, FilterOptions...} * or use QString as keys? */ int brightness() const; int contrast() const; int hue() const; //not implemented int saturation() const; /*! * \sa AVDemuxer::setOptions() * example: * QVariantHash opt; * opt["rtsp_transport"] = "tcp" * player->setOptionsForFormat(opt); */ // avformat_open_input void setOptionsForFormat(const QVariantHash &dict); QVariantHash optionsForFormat() const; // avcodec_open2. TODO: the same for audio/video codec? /*! * \sa AVDecoder::setOptions() * example: * QVariantHash opt, vaopt, ffopt; * vaopt["display"] = "X11"; * opt["vaapi"] = vaopt; // only apply for va-api decoder * ffopt["vismv"] = "pf"; * opt["ffmpeg"] = ffopt; // only apply for ffmpeg software decoder * player->setOptionsForVideoCodec(opt); */ // QVariantHash deprecated, use QVariantMap to get better js compatibility void setOptionsForAudioCodec(const QVariantHash &dict); QVariantHash optionsForAudioCodec() const; void setOptionsForVideoCodec(const QVariantHash& dict); QVariantHash optionsForVideoCodec() const; /*! * \brief mediaEndAction * The action at the end of media or when playback is stopped. Default is quit threads and clear video renderers. * If the flag MediaEndAction_KeepDisplay is set, the last video frame will keep displaying in video renderers. * If MediaEndAction_Pause is set, you can still seek and resume the playback because no thread exits. */ MediaEndAction mediaEndAction() const; void setMediaEndAction(MediaEndAction value); public Q_SLOTS: /*! * \brief load * Load the current media set by setFile(); Can be used to reload a media and call play() later. If already loaded, does nothing and return true. * If async load, mediaStatus() becomes LoadingMedia and user should connect signal loaded() * or mediaStatusChanged(QtAV::LoadedMedia) to a slot * \return true if success or already loaded. */ bool load(); void togglePause(); void pause(bool p = true); /*! * \brief play * Load media and start playback. If current media is playing and media source is not changed, nothing to do. If media source is not changed, try to load (not in LoadingStatus or LoadedStatus) and start playback. If media source changed, reload and start playback. */ void play(); /*! * \brief stop * Stop playback. It blocks current thread until the playback is stopped. Will emit signal stopped(). startPosition(), stopPosition(), repeat() are reset */ void stop(); /*! * \brief stepForward * Play the next frame and pause */ void stepForward(); /*! * \brief stepBackward * Play the previous frame and pause. Currently only support the previous decoded frames */ void stepBackward(); void setRelativeTimeMode(bool value); /*! * \brief setRepeat * Repeat max times between startPosition() and endPosition(). It's reset if playback is stopped. * max==0: no repeat * max<0: infinity. std::numeric_limits::max(); * \param max */ void setRepeat(int max); /*! * \brief startPosition * Used to repeat from startPosition() to endPosition(). * You can also start to play at a given position * \code * player->setStartPosition(); * player->play("some video"); * \endcode * pos < 0: equals duration()+pos * pos == 0, means start at the beginning of media stream * pos > media end position, or pos > normalized stopPosition(): undefined */ void setStartPosition(qint64 pos); /*! * \brief stopPosition * pos > mediaStopPosition(): mediaStopPosition() * pos < 0: duration() + pos * With the default value, the playback will not stop until the end of media (including dynamically changed media duration, e.g. recording video) */ void setStopPosition(qint64 pos = std::numeric_limits::max()); /*! * \brief setTimeRange * Set startPosition and stopPosition. Make sure start <= stop. */ void setTimeRange(qint64 start, qint64 stop = std::numeric_limits::max()); bool isSeekable() const; /*! * \brief setPosition equals to seek(qreal) * position < 0: 0 * \param position in ms */ void setPosition(qint64 position); void seek(qreal r); // r: [0, 1] void seek(qint64 pos); //ms. same as setPosition(pos) void seekForward(); void seekBackward(); void setSeekType(SeekType type); SeekType seekType() const; /*! * \brief bufferProgress * How much the data buffer is currently filled. From 0.0 to 1.0. * Playback can start or resume only when the buffer is entirely filled. */ qreal bufferProgress() const; /*! * \brief bufferSpeed * Bytes/s * \return 0 if not buffering. >= 0 if buffering */ qreal bufferSpeed() const; /*! * \brief buffered * Current buffered value in msecs, bytes or packet count depending on bufferMode() */ qint64 buffered() const; void setBufferMode(BufferMode mode); BufferMode bufferMode() const; /*! * \brief setBufferValue * Ensure the buffered msecs/bytes/packets in queue is at least the given value before playback starts. * Set before playback starts. * \param value <0: auto; BufferBytes: bytes, BufferTime: msecs, BufferPackets: packets count */ void setBufferValue(qint64 value); int bufferValue() const; /*! * \brief setNotifyInterval * The interval at which progress will update * \param msec <=0: auto and compute internally depending on duration and fps */ void setNotifyInterval(int msec); /// The real notify interval. Always > 0 int notifyInterval() const; void updateClock(qint64 msecs); //update AVClock's external clock // for all renderers. val: [-100, 100]. other value changes nothing void setBrightness(int val); void setContrast(int val); void setHue(int val); //not implemented void setSaturation(int val); Q_SIGNALS: void bufferProgressChanged(qreal); void relativeTimeModeChanged(); void autoLoadChanged(); void asyncLoadChanged(); void muteChanged(); void sourceChanged(); void loaded(); // == mediaStatusChanged(QtAV::LoadedMedia) void mediaStatusChanged(QtAV::MediaStatus status); //explictly use QtAV::MediaStatus void mediaEndActionChanged(QtAV::MediaEndAction action); /*! * \brief durationChanged emit when media is loaded/unloaded */ void durationChanged(qint64); void error(const QtAV::AVError& e); //explictly use QtAV::AVError in connection for Qt4 syntax void paused(bool p); /*! * \brief started * Emitted when playback is started. Some functions that control playback should be called after playback is started, otherwise they won't work, e.g. setPosition(), pause(). stop() can be called at any time. */ void started(); void stopped(); void stoppedAt(qint64 position); void stateChanged(QtAV::AVPlayer::State state); void speedChanged(qreal speed); void repeatChanged(int r); void currentRepeatChanged(int r); void startPositionChanged(qint64 position); void stopPositionChanged(qint64 position); void seekableChanged(); /*! * \brief seekFinished * If there is a video stream currently playing, emitted when video seek is finished. If only an audio stream is playing, emitted when audio seek is finished. The position() is the master clock value, It can be very different from video timestamp at this time. * \param position The video or audio timestamp when seek is finished */ void seekFinished(qint64 position); void positionChanged(qint64 position); void interruptTimeoutChanged(); void interruptOnTimeoutChanged(); void notifyIntervalChanged(); void brightnessChanged(int val); void contrastChanged(int val); void hueChanged(int val); void saturationChanged(int val); void subtitleStreamChanged(int value); /*! * \brief internalAudioTracksChanged * Emitted when media is loaded. \sa internalAudioTracks */ void internalAudioTracksChanged(const QVariantList& tracks); /*! * \brief internalVideoTracksChanged * Emitted when media is loaded. \sa internalVideoTracks */ void internalVideoTracksChanged(const QVariantList& tracks); void externalAudioTracksChanged(const QVariantList& tracks); void internalSubtitleTracksChanged(const QVariantList& tracks); /*! * \brief internalSubtitleHeaderRead * Emitted when internal subtitle is loaded. Empty data if no data. * codec is used by subtitle processors */ void internalSubtitleHeaderRead(const QByteArray& codec, const QByteArray& data); void internalSubtitlePacketRead(int track, const QtAV::Packet& packet); private Q_SLOTS: void loadInternal(); // simply load void playInternal(); // simply play void stopFromDemuxerThread(); void aboutToQuitApp(); // start/stop notify timer in this thread. use QMetaObject::invokeMethod void startNotifyTimer(); void stopNotifyTimer(); void onStarted(); void updateMediaStatus(QtAV::MediaStatus status); void onSeekFinished(qint64 value); void tryClearVideoRenderers(); protected: // TODO: set position check timer interval virtual void timerEvent(QTimerEvent *); private: /*! * \brief unload * If the media is loading or loaded but not playing, unload it. Internall use only. */ void unload(); //TODO: private. call in stop() if not load() by user? or always unload() in stop()? qint64 normalizedPosition(qint64 pos); class Private; QScopedPointer d; }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::AVPlayer::State) #endif // QTAV_AVPLAYER_H QtAV-1.12.0/src/QtAV/AVTranscoder.h000066400000000000000000000116131312235004300165070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) (2012-2016) Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVTRANSCODE_H #define QTAV_AVTRANSCODE_H #include #include #include namespace QtAV { class AVPlayer; class Q_AV_EXPORT AVTranscoder : public QObject { Q_OBJECT public: AVTranscoder(QObject* parent = 0); ~AVTranscoder(); // TODO: other source (more operations needed, e.g. seek)? void setMediaSource(AVPlayer* player); AVPlayer* sourcePlayer() const; QString outputFile() const; QIODevice* outputDevice() const; MediaIO* outputMediaIO() const; /*! * \brief setOutputMedia */ void setOutputMedia(const QString& fileName); void setOutputMedia(QIODevice* dev); void setOutputMedia(MediaIO* io); /*! * \brief setOutputFormat * Force the output format. Useful for custom io */ void setOutputFormat(const QString& fmt); QString outputFormatForced() const; void setOutputOptions(const QVariantHash &dict); QVariantHash outputOptions() const; /*! * \brief setAsync * Enable async encoding. Default is disabled. */ void setAsync(bool value = true); bool isAsync() const; /*! * \brief createEncoder * Destroy old encoder and create a new one in filter chain. Filter has the ownership. You shall not manually open it. Transcoder will set the missing parameters open it. * \param name registered encoder name, for example "FFmpeg" * \return false if failed */ bool createVideoEncoder(const QString& name = QLatin1String("FFmpeg")); /*! * \brief encoder * Use this to set encoder properties and options. * If frameRate is not set, source frame rate will be set if it's valid, otherwise VideoEncoder::defaultFrameRate() will be used internally * Do not call open()/close() manually * \return Encoder instance or null if createVideoEncoder failed */ VideoEncoder* videoEncoder() const; /*! * \brief createEncoder * Destroy old encoder and create a new one in filter chain. Filter has the ownership. You shall not manually open it. Transcoder will set the missing parameters open it. * \param name registered encoder name, for example "FFmpeg" * \return false if failed */ bool createAudioEncoder(const QString& name = QLatin1String("FFmpeg")); /*! * \brief encoder * Use this to set encoder properties and options. * Do not call open()/close() manually * \return Encoder instance or null if createAudioEncoder failed */ AudioEncoder* audioEncoder() const; /*! * \brief isRunning * \return true if encoding started */ bool isRunning() const; bool isPaused() const; qint64 encodedSize() const; qreal startTimestamp() const; qreal encodedDuration() const; /*! * \brief startTime * Start to encode after startTime(). To decode from a given time, call sourcePlayer()->setPosition() */ qint64 startTime() const; void setStartTime(qint64 ms); Q_SIGNALS: void videoFrameEncoded(qreal timestamp); void audioFrameEncoded(qreal timestamp); void started(); void stopped(); void paused(bool value); void startTimeChanged(qint64 ms); void asyncChanged(); public Q_SLOTS: void start(); /*! * \brief stop * Call stop() to encode delayed frames remains in encoder and then stop encoding. * It's called internally when sourcePlayer() is stopped */ void stop(); /*! * \brief pause * pause the encoders * \param value */ void pause(bool value); private Q_SLOTS: void onSourceStarted(); void prepareMuxer(); void writeAudio(const QtAV::Packet& packet); void writeVideo(const QtAV::Packet& packet); void tryFinish(); private: void stopInternal(); class Private; QScopedPointer d; }; } //namespace QtAV #endif // QTAV_AVTRANSCODE_H QtAV-1.12.0/src/QtAV/AudioDecoder.h000066400000000000000000000060731312235004300165070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AUDIODECODER_H #define QAV_AUDIODECODER_H #include #include #include #define USE_AUDIO_FRAME 1 //TODO: decoder.in/outAudioFormat()? namespace QtAV { typedef int AudioDecoderId; // built-in decoders extern Q_AV_EXPORT AudioDecoderId AudioDecoderId_FFmpeg; class AudioResampler; class AudioDecoderPrivate; class Q_AV_EXPORT AudioDecoder : public AVDecoder { Q_DISABLE_COPY(AudioDecoder) DPTR_DECLARE_PRIVATE(AudioDecoder) public: static QStringList supportedCodecs(); static AudioDecoder* create(AudioDecoderId id); /*! * \brief create * create a decoder from registered name. FFmpeg decoder will be created for empty name * \param name can be "FFmpeg" * \return 0 if not registered */ static AudioDecoder* create(const char* name = "FFmpeg"); virtual AudioDecoderId id() const = 0; QString name() const; //name from factory virtual QByteArray data() const; //decoded data virtual AudioFrame frame() = 0; AudioResampler *resampler(); //TODO: remove. can not share the same resampler for multiple frames public: template static bool Register(AudioDecoderId id, const char* name) { return Register(id, create, name);} /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static AudioDecoderId* next(AudioDecoderId* id = 0); static const char* name(AudioDecoderId id); static AudioDecoderId id(const char* name); private: // if QtAV is static linked (ios for example), components may be not automatically registered. Add registerAll() to workaround static void registerAll(); template static AudioDecoder* create() { return new C();} typedef AudioDecoder* (*AudioDecoderCreator)(); static bool Register(AudioDecoderId id, AudioDecoderCreator, const char *name); protected: AudioDecoder(AudioDecoderPrivate& d); private: AudioDecoder(); }; } //namespace QtAV #endif // QAV_AUDIODECODER_H QtAV-1.12.0/src/QtAV/AudioEncoder.h000066400000000000000000000064711312235004300165230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AUDIOENCODER_H #define QTAV_AUDIOENCODER_H #include #include #include namespace QtAV { typedef int AudioEncoderId; class AudioEncoderPrivate; class Q_AV_EXPORT AudioEncoder : public AVEncoder { Q_OBJECT Q_PROPERTY(QtAV::AudioFormat audioFormat READ audioFormat WRITE setAudioFormat NOTIFY audioFormatChanged) Q_DISABLE_COPY(AudioEncoder) DPTR_DECLARE_PRIVATE(AudioEncoder) public: static QStringList supportedCodecs(); static AudioEncoder* create(AudioEncoderId id); /*! * \brief create * create an encoder from registered names * \param name can be "FFmpeg". FFmpeg encoder will be created for empty name * \return 0 if not registered */ static AudioEncoder* create(const char* name = "FFmpeg"); virtual AudioEncoderId id() const = 0; QString name() const Q_DECL_OVERRIDE; //name from factory /*! * \brief encode * encode a audio frame to a Packet * \param frame pass an invalid frame to get delayed frames * \return */ virtual bool encode(const AudioFrame& frame = AudioFrame()) = 0; /// output parameters /*! * \brief audioFormat * If not set or set to an invalid format, a supported format will be used and audioFormat() will be that format after open() * \return * TODO: check supported formats */ const AudioFormat& audioFormat() const; void setAudioFormat(const AudioFormat& format); Q_SIGNALS: void audioFormatChanged(); public: template static bool Register(AudioEncoderId id, const char* name) { return Register(id, create, name);} /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static AudioEncoderId* next(AudioEncoderId* id = 0); static const char* name(AudioEncoderId id); static AudioEncoderId id(const char* name); private: template static AudioEncoder* create() { return new C();} typedef AudioEncoder* (*AudioEncoderCreator)(); static bool Register(AudioEncoderId id, AudioEncoderCreator, const char *name); protected: AudioEncoder(AudioEncoderPrivate& d); private: AudioEncoder(); }; } //namespace QtAV #endif // QTAV_AUDIOENCODER_H QtAV-1.12.0/src/QtAV/AudioFormat.h000066400000000000000000000150431312235004300163670ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AUDIOFORMAT_H #define QTAV_AUDIOFORMAT_H #include #include #include QT_BEGIN_NAMESPACE class QDebug; QT_END_NAMESPACE namespace QtAV { class AudioFormatPrivate; class Q_AV_EXPORT AudioFormat { enum { kSize = 12, kFloat = 1<<(kSize+1), kUnsigned = 1<<(kSize+2), kPlanar = 1<<(kSize+3), kByteOrder = 1<<(kSize+4) }; public: /*! * \brief The SampleFormat enum * s8, u16, u24, s24, u32 are not listed in ffmpeg sample format and have not planar format. * pcm_s24le will be decoded as s32-24bit in ffmpeg, it's encoded as 32 bits, but raw sample has 24 bits */ enum SampleFormat { SampleFormat_Unknown = 0, SampleFormat_Input = SampleFormat_Unknown, SampleFormat_Unsigned8 = 1 | kUnsigned, SampleFormat_Signed8 = 1, SampleFormat_Unigned16 = 2 | kUnsigned, SampleFormat_Signed16 = 2, SampleFormat_Unsigned24 = 3 | kUnsigned, SampleFormat_Signed24 = 3, SampleFormat_Unsigned32 = 4 | kUnsigned, SampleFormat_Signed32 = 4, SampleFormat_Float = 4 | kFloat, SampleFormat_Double = 8 | kFloat, SampleFormat_Unsigned8Planar = SampleFormat_Unsigned8 | kPlanar, SampleFormat_Signed16Planar = SampleFormat_Signed16 | kPlanar, SampleFormat_Signed32Planar = SampleFormat_Signed32 | kPlanar, SampleFormat_FloatPlanar = SampleFormat_Float | kPlanar, SampleFormat_DoublePlanar = SampleFormat_Double | kPlanar }; enum ChannelLayout { ChannelLayout_Left, ChannelLayout_Right, ChannelLayout_Center, ChannelLayout_Mono = ChannelLayout_Center, ChannelLayout_Stereo, ChannelLayout_Unsupported //ok. now it's not complete }; //typedef qint64 ChannelLayout; //currently use latest FFmpeg's // TODO: constexpr friend int RawSampleSize(SampleFormat fmt) { return fmt & ((1<<(kSize+1)) - 1); } friend bool IsFloat(SampleFormat fmt) { return !!(fmt & kFloat);} friend bool IsPlanar(SampleFormat format) { return !!(format & kPlanar);} friend bool IsUnsigned(SampleFormat format) {return !!(format & kUnsigned);} friend SampleFormat ToPlanar(SampleFormat fmt) { return IsPlanar(fmt) ? fmt : SampleFormat((int)fmt|kPlanar);} friend SampleFormat ToPacked(SampleFormat fmt) { return IsPlanar(fmt) ? SampleFormat((int)fmt^kPlanar) : fmt;} static ChannelLayout channelLayoutFromFFmpeg(qint64 clff); static qint64 channelLayoutToFFmpeg(ChannelLayout cl); static SampleFormat sampleFormatFromFFmpeg(int fffmt); static int sampleFormatToFFmpeg(SampleFormat fmt); static SampleFormat make(int bytesPerSample, bool isFloat, bool isUnsigned, bool isPlanar); AudioFormat(); AudioFormat(const AudioFormat &other); ~AudioFormat(); AudioFormat& operator=(const AudioFormat &other); bool operator==(const AudioFormat &other) const; bool operator!=(const AudioFormat &other) const; bool isValid() const; bool isFloat() const; bool isUnsigned() const; bool isPlanar() const; int planeCount() const; void setSampleRate(int sampleRate); int sampleRate() const; /*! * setChannelLayout and setChannelLayoutFFmpeg also sets the correct channels if channels does not match. */ void setChannelLayoutFFmpeg(qint64 layout); qint64 channelLayoutFFmpeg() const; //currently a limitted set of channel layout is supported. call setChannelLayoutFFmpeg is recommended void setChannelLayout(ChannelLayout layout); ChannelLayout channelLayout() const; QString channelLayoutName() const; /*! * setChannels also sets the default layout for this channels if channels does not match. */ void setChannels(int channels); /*! * \brief channels * For planar format, channel count == plane count. For packed format, plane count is 1 * \return */ int channels() const; void setSampleFormat(SampleFormat sampleFormat); SampleFormat sampleFormat() const; void setSampleFormatFFmpeg(int ffSampleFormat); int sampleFormatFFmpeg() const; QString sampleFormatName() const; // Helper functions // in microseconds qint32 bytesForDuration(qint64 duration) const; qint64 durationForBytes(qint32 byteCount) const; qint32 bytesForFrames(qint32 frameCount) const; qint32 framesForBytes(qint32 byteCount) const; // in microseconds qint32 framesForDuration(qint64 duration) const; qint64 durationForFrames(qint32 frameCount) const; // 1 frame = 1 sample with channels /*! Returns the number of bytes required to represent one frame (a sample in each channel) in this format. Returns 0 if this format is invalid. */ int bytesPerFrame() const; /*! Returns the current sample size value, in bytes. \sa bytesPerFrame() */ int bytesPerSample() const; int sampleSize() const; // the same as bytesPerSample() int bitRate() const; //bits per second int bytesPerSecond() const; private: QSharedDataPointer d; }; #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug debug, const AudioFormat &fmt); Q_AV_EXPORT QDebug operator<<(QDebug debug, AudioFormat::SampleFormat sampleFormat); Q_AV_EXPORT QDebug operator<<(QDebug debug, AudioFormat::ChannelLayout channelLayout); #endif } //namespace QtAV Q_DECLARE_METATYPE(QtAV::AudioFormat) Q_DECLARE_METATYPE(QtAV::AudioFormat::SampleFormat) Q_DECLARE_METATYPE(QtAV::AudioFormat::ChannelLayout) #endif // QTAV_AUDIOFORMAT_H QtAV-1.12.0/src/QtAV/AudioFrame.h000066400000000000000000000053251312235004300161730ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AUDIOFRAME_H #define QTAV_AUDIOFRAME_H #include #include namespace QtAV { class AudioResampler; class AudioFramePrivate; class Q_AV_EXPORT AudioFrame : public Frame { Q_DECLARE_PRIVATE(AudioFrame) public: //data must be complete /*! * \brief AudioFrame * construct an audio frame from a given buffer and format */ AudioFrame(const AudioFormat& format = AudioFormat(), const QByteArray& data = QByteArray()); AudioFrame(const AudioFrame &other); virtual ~AudioFrame(); AudioFrame &operator =(const AudioFrame &other); bool isValid() const; operator bool() const { return isValid();} /*! * \brief data * Audio data. clone is called if frame is not constructed with a QByteArray. * \return */ QByteArray data(); virtual int channelCount() const; /*! * Deep copy. If you want to copy data from somewhere, knowing the format, width and height, * then you can use clone(). */ AudioFrame clone() const; AudioFormat format() const; void setSamplesPerChannel(int samples); // may change after resampling int samplesPerChannel() const; AudioFrame to(const AudioFormat& fmt) const; //AudioResamplerId void setAudioResampler(AudioResampler *conv); //TODO: remove /*! Returns the number of microseconds represented by \a bytes in this format. Returns 0 if this format is not valid. Note that some rounding may occur if \a bytes is not an exact multiple of the number of bytes per frame. */ qint64 duration() const; }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::AudioFrame) #endif // QTAV_AUDIOFRAME_H QtAV-1.12.0/src/QtAV/AudioOutput.h000066400000000000000000000211731312235004300164400ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AUDIOOUTPUT_H #define QAV_AUDIOOUTPUT_H #include #include #include #include /*! * AudioOutput ao; * ao.setAudioFormat(fmt); * ao.open(); * while (has_data) { * data = read_data(ao->bufferSize()); * ao->play(data, pts); * } * ao->close(); * See QtAV/tests/ao/main.cpp for detail */ namespace QtAV { class AudioFormat; class AudioOutputPrivate; class Q_AV_EXPORT AudioOutput : public QObject, public AVOutput { Q_OBJECT DPTR_DECLARE_PRIVATE(AudioOutput) Q_ENUMS(DeviceFeature) Q_FLAGS(DeviceFeatures) Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(bool mute READ isMute WRITE setMute NOTIFY muteChanged) Q_PROPERTY(DeviceFeatures deviceFeatures READ deviceFeatures WRITE setDeviceFeatures NOTIFY deviceFeaturesChanged) Q_PROPERTY(QStringList backends READ backends WRITE setBackends NOTIFY backendsChanged) public: /*! * \brief DeviceFeature Feature enum * Features supported by the audio playback api (we call device or backend here) * If a feature is not supported, e.g. SetVolume, then a software implementation is used. */ enum DeviceFeature { NoFeature = 0, SetVolume = 1, /// Use backend volume control api rather than software scale. Ignore if backend does not support. SetMute = 1 << 1, SetSampleRate = 1 << 2, /// NOT IMPLEMENTED SetSpeed = 1 << 3, /// NOT IMPLEMENTED }; Q_DECLARE_FLAGS(DeviceFeatures, DeviceFeature) /*! * \brief backendsAvailable * All registered backends in default priority order * \return */ static QStringList backendsAvailable(); /*! * \brief AudioOutput * Audio format set to preferred sample format and channel layout */ AudioOutput(QObject *parent = 0); ~AudioOutput(); /*! * \brief setBackends * set the given backends. Old backend instance and backend() is updated soon if backendsChanged. * It is called internally with a default backend names when AudioOutput is created. */ void setBackends(const QStringList &backendNames = QStringList()); QStringList backends() const; /*! * \brief backend * backend name currently in use */ QString backend() const; /*! * \brief flush * Play the buffered audio data * \return */ void flush(); /*! * \brief clear * Clear audio buffers and set time to 0. The default behavior is flush and reset time */ void clear(); bool open(); bool close(); bool isOpen() const; /*! * \brief play * Play out the given audio data. It may block current thread until the data can be written to audio device * for async playback backend, or until the data is completely played for blocking playback backend. * \param data Audio data to play * \param pts Timestamp for this data. Useful if need A/V sync. Ignore it if only play audio * \return false if currently isPaused(), no backend is available or backend failed to play */ bool play(const QByteArray& data, qreal pts = 0.0); /*! * \brief pause * Pause audio rendering. play() will fail. */ void pause(bool value); bool isPaused() const; /*! * \brief setAudioFormat * Set/Request to use the given \l format. If it's not supported, an preferred format will be used. * \param format requested format * \return actual format to use. Invalid format if backend is not available * NOTE: Check format support may fail for some backends (OpenAL) if it's closed. */ AudioFormat setAudioFormat(const AudioFormat& format); const AudioFormat& requestedFormat() const; /*! * \brief audioFormat * \return actual format for requested format */ const AudioFormat& audioFormat() const; /*! * \brief setVolume * Set volume level. * If SetVolume feature is not set or not supported, software implementation will be used. * Call this after open(), because it will call backend api if SetVolume feature is enabled * \param volume linear. 1.0: original volume. */ void setVolume(qreal value); qreal volume() const; /*! * \brief setMute * If SetMute feature is not set or not supported, software implementation will be used. * Call this after open(), because it will call backend api if SetMute feature is enabled */ void setMute(bool value = true); bool isMute() const; /*! * \brief setSpeed set audio playing speed * Currently only store the value and does nothing else in audio output. You may change sample rate to get the same effect. * The speed affects the playing only if audio is available and clock type is * audio clock. For example, play a video contains audio without special configurations. * To change the playing speed in other cases, use AVPlayer::setSpeed(qreal) * \param speed linear. > 0 * TODO: resample internally */ void setSpeed(qreal speed); qreal speed() const; /*! * \brief isSupported * check \a isSupported(format.sampleFormat()) and \a isSupported(format.channelLayout()) * \param format * \return true if \a format is supported. default is true * NOTE: may fail for some backends if it's closed, for example OpenAL */ bool isSupported(const AudioFormat& format) const; /*! * \brief bufferSamples * Number of samples that audio output accept in 1 buffer. Feed the audio output this size of data every time. * Smaller buffer samples gives more buffers for a given data to avoid stutter. But if it's too small, the duration of 1 buffer will be too small to play, for example 1ms. Currently the default value is 512. * Some backends(OpenAL) are affected significantly by this property */ int bufferSamples() const; void setBufferSamples(int value); int bufferSize() const; /// bufferSamples()*bytesPerSample /*! * \brief bufferCount * Total buffer count. If it's not large enough, playing high sample rate audio may be poor. * The default value is 16. TODO: depending on audio format(sample rate?) * Some backends(OpenAL) are affected significantly by this property */ int bufferCount() const; void setBufferCount(int value); int bufferSizeTotal() const { return bufferCount() * bufferSize();} /*! * \brief setDeviceFeatures * Unsupported features will not be set. * You can call this in a backend ctor. */ void setDeviceFeatures(DeviceFeatures value); /*! * \brief deviceFeatures * \return features set by setFeatures() excluding unsupported features */ DeviceFeatures deviceFeatures() const; /*! * \brief supportedDeviceFeatures * Supported features of the backend, defined by AudioOutput(DeviceFeatures,AudioOutput&,QObject*) in a backend ctor */ DeviceFeatures supportedDeviceFeatures() const; qreal timestamp() const; // timestamp of current playing data Q_SIGNALS: void volumeChanged(qreal); void muteChanged(bool); void deviceFeaturesChanged(); void backendsChanged(); protected: // Store and fill data to audio buffers bool receiveData(const QByteArray &data, qreal pts = 0.0); /*! * \brief waitForNextBuffer * wait until you can feed more data */ bool waitForNextBuffer(); private Q_SLOTS: void reportVolume(qreal value); void reportMute(bool value); private: void onCallback(); friend class AudioOutputBackend; Q_DISABLE_COPY(AudioOutput) }; } //namespace QtAV #endif // QAV_AUDIOOUTPUT_H QtAV-1.12.0/src/QtAV/AudioResampler.h000066400000000000000000000103641312235004300170720ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AUDIORESAMPLER_H #define QTAV_AUDIORESAMPLER_H #include namespace QtAV { typedef int AudioResamplerId; class AudioFormat; class AudioResamplerPrivate; class Q_AV_EXPORT AudioResampler //export is required for users who want add their own subclass outside QtAV { DPTR_DECLARE_PRIVATE(AudioResampler) public: virtual ~AudioResampler(); // if QtAV is static linked (ios for example), components may be not automatically registered. Add registerAll() to workaround static void registerAll(); template static bool Register(AudioResamplerId id, const char* name) { return Register(id, create, name);} static AudioResampler* create(AudioResamplerId id); /*! * \brief create * Create resampler from name * \param name can be "FFmpeg", "Libav" */ static AudioResampler* create(const char* name); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static AudioResamplerId* next(AudioResamplerId* id = 0); static const char* name(AudioResamplerId id); static AudioResamplerId id(const char* name); QByteArray outData() const; /* check whether the parameters are supported. If not, you should use ff*/ /*! * \brief prepare * Check whether the parameters are supported and setup the resampler * setIn/OutXXX will call prepare() if format is changed */ virtual bool prepare(); virtual bool convert(const quint8** data); //speed: >0, default is 1 void setSpeed(qreal speed); //out_sample_rate = out_sample_rate/speed qreal speed() const; void setInAudioFormat(const AudioFormat& format); AudioFormat& inAudioFormat(); const AudioFormat& inAudioFormat() const; void setOutAudioFormat(const AudioFormat& format); AudioFormat& outAudioFormat(); const AudioFormat& outAudioFormat() const; //decoded frame's samples/channel void setInSampesPerChannel(int samples); // > 0 valid after resample done int outSamplesPerChannel() const; //channel count can be computed by av_get_channel_layout_nb_channels(chl) void setInSampleRate(int isr); void setOutSampleRate(int osr); //default is in //TODO: enum void setInSampleFormat(int isf); //FFmpeg sample format void setOutSampleFormat(int osf); //FFmpeg sample format. set by user. default is in //TODO: enum. layout will be set to the default layout of the channels if not defined void setInChannelLayout(qint64 icl); void setOutChannelLayout(qint64 ocl); //default is in void setInChannels(int channels); void setOutChannels(int channels); //Are getter functions required? private: AudioResampler(); template static AudioResampler* create() { return new C(); } typedef AudioResampler* (*AudioResamplerCreator)(); static bool Register(AudioResamplerId id, AudioResamplerCreator, const char *name); protected: AudioResampler(AudioResamplerPrivate& d); DPTR_DECLARE(AudioResampler) }; extern Q_AV_EXPORT AudioResamplerId AudioResamplerId_FF; extern Q_AV_EXPORT AudioResamplerId AudioResamplerId_Libav; } //namespace QtAV #endif // QTAV_AUDIORESAMPLER_H QtAV-1.12.0/src/QtAV/ConvolutionShader.h000066400000000000000000000045251312235004300176260ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_CONVOLUTIONSHADER_H #define QTAV_CONVOLUTIONSHADER_H #include namespace QtAV { class ConvolutionShaderPrivate; /*! * \brief The ConvolutionShader class * Uniform u_Kernel is used */ class Q_AV_EXPORT ConvolutionShader : public VideoShader { DPTR_DECLARE_PRIVATE(ConvolutionShader) public: ConvolutionShader(); /*! * \brief kernelRadius * Default is 1, i.e. 3x3 kernel * kernelSize is (2*kernelRadius()+1)^2 * \return */ int kernelRadius() const; /// TODO: update shader program if radius is changed. mark dirty program void setKernelRadius(int value); int kernelSize() const; protected: virtual const float* kernel() const = 0; const QByteArray& kernelUniformHeader() const; //can be used in your userFragmentShaderHeader(); const QByteArray& kernelSample() const; //can be in your userSample(); void setKernelUniformValue(); // can be used in your setUserUniformValues() private: /// default implementions const char* userShaderHeader(QOpenGLShader::ShaderType t) const Q_DECL_OVERRIDE; const char* userSample() const Q_DECL_OVERRIDE; bool setUserUniformValues() Q_DECL_OVERRIDE; protected: ConvolutionShader(ConvolutionShaderPrivate &d); }; } //namespace QtAV #endif // QTAV_CONVOLUTIONSHADER_H QtAV-1.12.0/src/QtAV/EncodeFilter.h000066400000000000000000000121761312235004300165240ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_ENCODEFILTER_H #define QTAV_ENCODEFILTER_H #include #include #include #include namespace QtAV { class AudioEncoder; class AudioEncodeFilterPrivate; class Q_AV_EXPORT AudioEncodeFilter : public AudioFilter { Q_OBJECT DPTR_DECLARE_PRIVATE(AudioEncodeFilter) public: AudioEncodeFilter(QObject *parent = 0); /*! * \brief setAsync * Enable async encoding. Default is disabled. */ void setAsync(bool value = true); bool isAsync() const; /*! * \brief createEncoder * Destroy old encoder and create a new one. Filter has the ownership. * Encoder will open when encoding first valid frame, and set width/height as frame's. * \param name registered encoder name, for example "FFmpeg" * \return null if failed */ AudioEncoder* createEncoder(const QString& name = QLatin1String("FFmpeg")); /*! * \brief encoder * Use this to set encoder properties and options * \return Encoder instance or null if createEncoder failed */ AudioEncoder* encoder() const; // TODO: async property /*! * \brief startTime * start to encode after startTime() */ qint64 startTime() const; void setStartTime(qint64 value); public Q_SLOTS: /*! * \brief finish * Tell the encoder no more frames to encode. Signal finished() will be emitted when all frames are encoded */ void finish(); Q_SIGNALS: void finished(); /*! * \brief readyToEncode * Emitted when encoder is open. All parameters are set and muxer can set codec properties now. * close the encoder() to reset and reopen. */ void readyToEncode(); void frameEncoded(const QtAV::Packet& packet); void startTimeChanged(qint64 value); // internal use only void requestToEncode(const AudioFrame& frame); protected Q_SLOTS: void encode(const QtAV::AudioFrame& frame = AudioFrame()); protected: virtual void process(Statistics* statistics, AudioFrame* frame = 0) Q_DECL_OVERRIDE; }; class VideoEncoder; class VideoEncodeFilterPrivate; class Q_AV_EXPORT VideoEncodeFilter : public VideoFilter { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoEncodeFilter) public: VideoEncodeFilter(QObject* parent = 0); /*! * \brief setAsync * Enable async encoding. Default is disabled. */ void setAsync(bool value = true); bool isAsync() const; bool isSupported(VideoFilterContext::Type t) const Q_DECL_OVERRIDE { return t == VideoFilterContext::None;} /*! * \brief createEncoder * Destroy old encoder and create a new one. Filter has the ownership. * Encoder will open when encoding first valid frame, and set width/height as frame's. * \param name registered encoder name, for example "FFmpeg" * \return null if failed */ VideoEncoder* createEncoder(const QString& name = QLatin1String("FFmpeg")); /*! * \brief encoder * Use this to set encoder properties and options * \return Encoder instance or null if createEncoder failed */ VideoEncoder* encoder() const; // TODO: async property /*! * \brief startTime * start to encode after startTime() */ qint64 startTime() const; void setStartTime(qint64 value); public Q_SLOTS: /*! * \brief finish * Tell the encoder no more frames to encode. Signal finished() will be emitted when all frames are encoded */ void finish(); Q_SIGNALS: void finished(); /*! * \brief readyToEncode * Emitted when encoder is open. All parameters are set and muxer can set codec properties now. * close the encoder() to reset and reopen. */ void readyToEncode(); void frameEncoded(const QtAV::Packet& packet); void startTimeChanged(qint64 value); // internal use only void requestToEncode(const QtAV::VideoFrame& frame); protected Q_SLOTS: void encode(const QtAV::VideoFrame& frame = VideoFrame()); protected: virtual void process(Statistics* statistics, VideoFrame* frame = 0) Q_DECL_OVERRIDE; }; } //namespace QtAV #endif // QTAV_ENCODEFILTER_H QtAV-1.12.0/src/QtAV/FactoryDefine.h000066400000000000000000000156211312235004300167010ustar00rootroot00000000000000/****************************************************************************** Some macros to create a factory and register functions Copyright (C) 2012-2015 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef FACTORYDEFINE_H #define FACTORYDEFINE_H #include #include #include /*! * A: Suppose we need a factory for class MyClass. We can query a derived class * of MyClass from factory by an id type MyClassId. * To create the factory, just 2 steps * 1. In MyClass.h: * #include "FactoryDefine.h" * FACTORY_DECLARE(MyClass) * 2. In MyClass.cpp: * #include "QtAV/private/factory.h" * FACTORY_DEFINE(MyClass) * * To create and register a new subclass MyClassSubA with it's id * 0. In MyClassTypes.h (Usually just include this is enough to use the factory. And MyClassXXX.{h,cpp} can NOT include this file), * MyClassSubA's ID: * extern Q_AV_EXPORT MyClassId MyClassId_SubA; * In MyClassTypes.cpp, define the id value: * MyClassId MyClassId_SubA = some_value; * We define the id in MyClassTypes.cpp because MyClassSubA may not be compiled(e.g. platform dependent features), but the id must be defined. * 1. create a source file MyClassSubA.cpp and implement the required members * 2. In MyClassSubA.cpp, add the following lines * #include "QtAV/private/prepost.h" //for PRE_FUNC_ADD() * //we don't want to depend on MyClassTypes.h, so extern * extern MyClassId MyClassId_SubA; * FACTORY_REGISTER_ID_AUTO(MyClass, SubA, "SubA's name") * void RegisterMyClassSubA_Man() * { * FACTORY_REGISTER_ID_MAN(MyClass, SubA, "SubA's name") * } * * 3. In MyClass.cpp, add register function into MyClass_RegisterAll(); * extern void RegisterMyClassSubA_Man(); * void MyClass_RegisterAll() * { * RegisterMyClassSubA_Man(); //add this line * } * ******************************************************************************************************* * B: If MyClass and it's factory already exist in the library and you want to add a new subclass without * changing the library, * just create MyClassSubA.cpp with the content: * * #include "MyClass.h" * #include "QtAV/private/prepost.h" //for PRE_FUNC_ADD() * MyClassId MyClassId_SubA = some_value; //it can be used somewhere else as "extern" * FACTORY_REGISTER_ID_AUTO(MyClass, SubA, "SubA's name") * void RegisterMyClassSubA_Man() //call it when you need as "extern" * { * FACTORY_REGISTER_ID_MAN(MyClass, SubA, "SubA's name") * } * * class MyClassSubA : public MyClass * { * ... * }; */ /* * This should be in header */ #define FACTORY_REGISTER(BASE, _ID, NAME) FACTORY_REGISTER_ID_TYPE(BASE, BASE##Id_##_ID, BASE##_ID, NAME) #define FACTORY_REGISTER_ID_TYPE(BASE, ID, TYPE, NAME) \ FACTORY_REGISTER_ID_TYPE_AUTO(BASE, ID, TYPE, NAME) \ void Register##TYPE##_Man() { \ FACTORY_REGISTER_ID_TYPE_MAN(BASE, ID, TYPE, NAME); \ } #define FACTORY_REGISTER_ID_AUTO(BASE, _ID, NAME) \ FACTORY_REGISTER_ID_TYPE_AUTO(BASE, BASE##Id_##_ID, BASE##_ID, NAME) #define FACTORY_REGISTER_ID_MAN(BASE, _ID, NAME) \ FACTORY_REGISTER_ID_TYPE_MAN(BASE, BASE##Id_##_ID, BASE##_ID, NAME) #define FACTORY_REGISTER_ID_TYPE_MAN(BASE, ID, TYPE, NAME) \ BASE##Factory::register_(ID); \ BASE##Factory::registerIdName(ID, NAME); /* * FIXME: __init_##TYPE (only if static) and xxx_Man() has the same content, and are both defined, construtor functions will not be called for gcc5. * maybe also happens for ios * Remove xxx_Man() is also a workaround */ #define FACTORY_REGISTER_ID_TYPE_AUTO(BASE, ID, TYPE, NAME) \ static int __init_##TYPE() { \ FACTORY_REGISTER_ID_TYPE_MAN(BASE, ID, TYPE, NAME) \ return 0; \ } \ PRE_FUNC_ADD(__init_##TYPE) /* * This should be in header */ #define FACTORY_DECLARE(T) FACTORY_DECLARE_ID(T, T##Id) #define FACTORY_DECLARE_ID(T, ID) \ class Q_AV_EXPORT T##Factory \ { \ public: \ typedef T* (*T##Creator)(); \ static T* create(const ID& id); \ template \ static bool register_(const ID& id) { return registerCreator(id, create); } \ static bool registerCreator(const ID&, const T##Creator&); \ static bool registerIdName(const ID& id, const std::string& name); \ static bool unregisterCreator(const ID& id); \ static ID id(const std::string& name, bool caseSensitive = true); \ static std::string name(const ID &id); \ static std::vector registeredIds(); \ static std::vector registeredNames(); \ static size_t count(); \ static T* getRandom(); \ private: \ template static T* create() { return new C(); } \ }; /* * This should be in cpp */ #define FACTORY_DEFINE(T) FACTORY_DEFINE_ID(T, T##Id) #define FACTORY_DEFINE_ID(T, ID) \ class T##FactoryBridge : public Factory {}; \ T* T##Factory::create(const ID& id) { return T##FactoryBridge::Instance().create(id); } \ bool T##Factory::registerCreator(const ID& id, const T##Creator& callback) { return T##FactoryBridge::Instance().registerCreator(id, callback); } \ bool T##Factory::registerIdName(const ID& id, const std::string& name) { return T##FactoryBridge::Instance().registerIdName(id, name); } \ bool T##Factory::unregisterCreator(const ID& id) { return T##FactoryBridge::Instance().unregisterCreator(id); } \ ID T##Factory::id(const std::string& name, bool caseSensitive) { return T##FactoryBridge::Instance().id(name, caseSensitive); } \ std::string T##Factory::name(const ID &id) { return T##FactoryBridge::Instance().name(id); } \ std::vector T##Factory::registeredIds() { return T##FactoryBridge::Instance().registeredIds(); } \ std::vector T##Factory::registeredNames() { return T##FactoryBridge::Instance().registeredNames(); } \ size_t T##Factory::count() { return T##FactoryBridge::Instance().count(); } \ T* T##Factory::getRandom() { fflush(0);return T##FactoryBridge::Instance().getRandom(); } #endif // FACTORYDEFINE_H QtAV-1.12.0/src/QtAV/Filter.h000066400000000000000000000077451312235004300154140ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FILTER_H #define QTAV_FILTER_H #include #include #include namespace QtAV { class AudioFormat; class AVOutput; class AVPlayer; class FilterPrivate; class Statistics; class Frame; class Q_AV_EXPORT Filter : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(Filter) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) public: virtual ~Filter(); bool isEnabled() const; /*! * \brief setOwnedByTarget * If a filter is owned by target, it's not safe to access the filter after it's installed to a target. * QtAV will delete the installed filter internally if filter is owned by target AND it's parent (QObject) is null. */ void setOwnedByTarget(bool value = true); // default is false bool isOwnedByTarget() const; // setInput/Output: no need to call installTo // bool setInput(Filter*); // bool setOutput(Filter*); /*! * \brief installTo * Install filter to player can process every frame before rendering. * Equals to player->installFilter(this) */ virtual bool installTo(AVPlayer *player) = 0; // called in destructor automatically bool uninstall(); public Q_SLOTS: void setEnabled(bool enabled = true); Q_SIGNALS: void enabledChanged(bool); protected: /* * If the filter is in AVThread, it's safe to operate on ref. */ Filter(FilterPrivate& d, QObject *parent = 0); DPTR_DECLARE(Filter) }; class VideoFilterPrivate; class Q_AV_EXPORT VideoFilter : public Filter { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoFilter) public: VideoFilter(QObject* parent = 0); VideoFilterContext* context(); virtual bool isSupported(VideoFilterContext::Type ct) const; bool installTo(AVPlayer *player); /*! * \brief installTo * The process() function is in rendering thread. Used by * 1. GPU filters * 2. QPainter rendering on widget based renderers. Changing the frame has no effect * \return false if already installed */ bool installTo(AVOutput *output); //only for video. move to video filter installToRenderer void apply(Statistics* statistics, VideoFrame *frame = 0); bool prepareContext(VideoFilterContext*& ctx, Statistics* statistics = 0, VideoFrame* frame = 0); //internal use protected: VideoFilter(VideoFilterPrivate& d, QObject *parent = 0); virtual void process(Statistics* statistics, VideoFrame* frame = 0) = 0; }; class AudioFrame; class AudioFilterPrivate; class Q_AV_EXPORT AudioFilter : public Filter { Q_OBJECT DPTR_DECLARE_PRIVATE(AudioFilter) public: AudioFilter(QObject* parent = 0); bool installTo(AVPlayer *player); void apply(Statistics* statistics, AudioFrame *frame = 0); protected: AudioFilter(AudioFilterPrivate& d, QObject *parent = 0); virtual void process(Statistics* statistics, AudioFrame* frame = 0) = 0; }; } //namespace QtAV #endif // QTAV_FILTER_H QtAV-1.12.0/src/QtAV/FilterContext.h000066400000000000000000000151551312235004300167530ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FILTERCONTEXT_H #define QTAV_FILTERCONTEXT_H #include #include #include #include /* * QPainterFilterContext, D2DFilterContext, ... */ QT_BEGIN_NAMESPACE class QPainter; class QPaintDevice; class QTextDocument; QT_END_NAMESPACE namespace QtAV { class VideoFrame; class Q_AV_EXPORT VideoFilterContext { public: enum Type { ////audio and video... QtPainter, OpenGL, Direct2D, //Not implemeted GdiPlus, //Not implemented X11, None //user defined filters, no context can be used }; static VideoFilterContext* create(Type t); VideoFilterContext(); virtual ~VideoFilterContext(); virtual Type type() const = 0; // map to Qt types //drawSurface? virtual void drawImage(const QPointF& pos, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor); // if target is null, draw image at target.topLeft(). if source is null, draw the whole image virtual void drawImage(const QRectF& target, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor); virtual void drawPlainText(const QPointF& pos, const QString& text); // if rect is null, draw single line text at rect.topLeft(), ignoring flags virtual void drawPlainText(const QRectF& rect, int flags, const QString& text); virtual void drawRichText(const QRectF& rect, const QString& text, bool wordWrap = true); /* * TODO: x, y, width, height: |?|>=1 is in pixel unit, otherwise is ratio of video context rect * filter.x(a).y(b).width(c).height(d) */ QRectF rect; // Fallback to QPainter if no other paint engine implemented QPainter *painter; //TODO: shared_ptr? qreal opacity; QTransform transform; QPainterPath clip_path; QFont font; QPen pen; QBrush brush; /* * for the filters apply on decoded data, paint_device must be initialized once the data changes * can we allocate memory on stack? */ QPaintDevice *paint_device; int video_width, video_height; //original size protected: bool own_painter; bool own_paint_device; protected: virtual bool isReady() const = 0; // save the state then setup new parameters virtual bool prepare() = 0; virtual void initializeOnFrame(VideoFrame *frame); //private? // share qpainter etc. virtual void shareFrom(VideoFilterContext *vctx); friend class VideoFilter; }; class VideoFrameConverter; //TODO: font, pen, brush etc? class Q_AV_EXPORT QPainterFilterContext Q_DECL_FINAL: public VideoFilterContext { public: QPainterFilterContext(); virtual ~QPainterFilterContext(); Type type() const Q_DECL_OVERRIDE { return VideoFilterContext::QtPainter;} // empty source rect equals to the whole source rect void drawImage(const QPointF& pos, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE; void drawImage(const QRectF& target, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE; void drawPlainText(const QPointF& pos, const QString& text) Q_DECL_OVERRIDE; // if rect is null, draw single line text at rect.topLeft(), ignoring flags void drawPlainText(const QRectF& rect, int flags, const QString& text) Q_DECL_OVERRIDE; void drawRichText(const QRectF& rect, const QString& text, bool wordWrap = true) Q_DECL_OVERRIDE; protected: bool isReady() const Q_DECL_OVERRIDE; bool prepare() Q_DECL_OVERRIDE; void initializeOnFrame(VideoFrame *vframe) Q_DECL_OVERRIDE; QTextDocument *doc; VideoFrameConverter *cvt; }; #if QTAV_HAVE(X11) class Q_AV_EXPORT X11FilterContext Q_DECL_FINAL: public VideoFilterContext { public: typedef struct _XDisplay Display; typedef struct _XGC *GC; typedef quintptr Drawable; typedef quintptr Pixmap; struct XImage; X11FilterContext(); virtual ~X11FilterContext(); Type type() const Q_DECL_OVERRIDE { return VideoFilterContext::X11;} void resetX11(Display* dpy = 0, GC g = 0, Drawable d = 0); // empty source rect equals to the whole source rect void drawImage(const QPointF& pos, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE; void drawImage(const QRectF& target, const QImage& image, const QRectF& source = QRectF(), Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE; void drawPlainText(const QPointF& pos, const QString& text) Q_DECL_OVERRIDE; // if rect is null, draw single line text at rect.topLeft(), ignoring flags void drawPlainText(const QRectF& rect, int flags, const QString& text) Q_DECL_OVERRIDE; void drawRichText(const QRectF& rect, const QString& text, bool wordWrap = true) Q_DECL_OVERRIDE; protected: bool isReady() const Q_DECL_OVERRIDE; bool prepare() Q_DECL_OVERRIDE; void initializeOnFrame(VideoFrame *vframe) Q_DECL_OVERRIDE; void shareFrom(VideoFilterContext *vctx) Q_DECL_OVERRIDE; // null image: use the old x11 image/pixmap void renderTextImageX11(QImage* img, const QPointF &pos); void destroyX11Resources(); QTextDocument *doc; VideoFrameConverter *cvt; Display* display; GC gc; Drawable drawable; XImage *text_img; XImage *mask_img; Pixmap mask_pix; QImage text_q; QImage mask_q; bool plain; QString text; QImage test_img; //for computing bounding rect }; #endif //QTAV_HAVE(X11) } //namespace QtAV #endif // QTAV_FILTERCONTEXT_H QtAV-1.12.0/src/QtAV/Frame.h000066400000000000000000000071341312235004300152110ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FRAME_H #define QTAV_FRAME_H #include #include #include // TODO: fromAVFrame() asAVFrame()? namespace QtAV { class FramePrivate; class Q_AV_EXPORT Frame { Q_DECLARE_PRIVATE(Frame) public: Frame(const Frame& other); virtual ~Frame() = 0; Frame& operator =(const Frame &other); /*! * \brief planeCount * a decoded frame can be packed and planar. packed format has only 1 plane, while planar * format has more than 1 plane. For audio, the number plane equals channel count. For * video, rgb is 1 plane, yuv420p is 3 plane, p means planar * \param plane default is the first plane * \return */ int planeCount() const; /*! * \brief channelCount * for audio, channel count equals plane count * for video, channels >= planes * \return */ virtual int channelCount() const; /*! * \brief bytesPerLine * For video, it's size of each picture line. For audio, it's the whole size of plane * \param plane * \return line size of plane */ int bytesPerLine(int plane = 0) const; // the whole frame data. may be empty unless clone() or allocate is called QByteArray frameData() const; // deep copy 1 plane data QByteArray data(int plane = 0) const; uchar* bits(int plane = 0); const uchar *bits(int plane = 0) const { return constBits(plane);} const uchar* constBits(int plane = 0) const; /*! * \brief setBits * does nothing if plane is invalid. if given array size is greater than planeCount(), only planeCount() elements is used * \param b slice * \param plane color/audio channel */ // TODO: const? void setBits(uchar *b, int plane = 0); void setBits(const QVector& b); void setBits(quint8 *slice[]); /*! * \brief setBytesPerLine * does nothing if plane is invalid. if given array size is greater than planeCount(), only planeCount() elements is used */ void setBytesPerLine(int lineSize, int plane = 0); void setBytesPerLine(const QVector& lineSize); void setBytesPerLine(int stride[]); QVariantMap availableMetaData() const; QVariant metaData(const QString& key) const; void setMetaData(const QString &key, const QVariant &value); void setTimestamp(qreal ts); qreal timestamp() const; inline void swap(Frame &other) { qSwap(d_ptr, other.d_ptr); } protected: Frame(FramePrivate *d); QExplicitlySharedDataPointer d_ptr; }; } //namespace QtAV #endif // QTAV_FRAME_H QtAV-1.12.0/src/QtAV/FrameReader.h000066400000000000000000000051441312235004300163330ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FRAMEREADER_H #define QTAV_FRAMEREADER_H #include #include namespace QtAV { /*! * \brief The FrameReader class * while (reader->readMore()) { * while (reader->hasVideoFrame()) { //or hasEnoughVideoFrames() * reader->getVideoFrame(); * ... * } * } * or (faster) * while (reader->readMore()) { * reader->getVideoFrame(); //we can ensure 1 frame is available, but may block here * } * while (r.hasVideoFrame()) { //get buffered frames * reader->getVideoFrame(); * } * TODO: multiple tracks */ class Q_AV_EXPORT FrameReader : public QObject { Q_OBJECT public: // TODO: load and get info // TODO: asnyc option explicit FrameReader(QObject *parent = 0); ~FrameReader(); void setMedia(const QString& url); QString mediaUrl() const; void setVideoDecoders(const QStringList& names); QStringList videoDecoders() const; VideoFrame getVideoFrame(); bool hasVideoFrame() const; bool hasEnoughVideoFrames() const; // return false if eof bool readMore(); // TODO: tryLoad on seek even at eof // TODO: compress seek requests bool seek(qint64 pos); Q_SIGNALS: void frameRead(const QtAV::VideoFrame& frame); void readEnd(); void seekFinished(qint64 pos); // internal void readMoreRequested(); void seekRequested(qint64); private Q_SLOTS: void readMoreInternal(); bool seekInternal(qint64 value); private: class Private; QScopedPointer d; }; } //namespace QtAV #endif // QTAV_FRAMEREADER_H QtAV-1.12.0/src/QtAV/GLSLFilter.h000066400000000000000000000057461312235004300160750ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GLSLFILTER_H #define QTAV_GLSLFILTER_H #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #undef QOpenGLFramebufferObject #define QOpenGLFramebufferObject QGLFramebufferObject #endif QT_BEGIN_NAMESPACE class QOpenGLFramebufferObject; QT_END_NAMESPACE namespace QtAV { class OpenGLVideo; class GLSLFilterPrivate; class Q_AV_EXPORT GLSLFilter : public VideoFilter { Q_OBJECT DPTR_DECLARE_PRIVATE(GLSLFilter) Q_PROPERTY(QSize outputSize READ outputSize WRITE setOutputSize NOTIFY outputSizeChanged) public: GLSLFilter(QObject* parent = 0); bool isSupported(VideoFilterContext::Type ct) const Q_DECL_OVERRIDE { return ct == VideoFilterContext::OpenGL; } /*! * \brief opengl * Currently you can only use it to set custom shader OpenGLVideo.setUserShader() */ OpenGLVideo* opengl() const; QOpenGLFramebufferObject* fbo() const; /*! * \brief outputSize * Output frame size. FBO uses the same size to render. An empty size means using the input frame size * \return */ QSize outputSize() const; void setOutputSize(const QSize& value); void setOutputSize(int width, int height); Q_SIGNALS: void outputSizeChanged(const QSize& size); protected: GLSLFilter(GLSLFilterPrivate& d, QObject *parent = 0); /*! * \brief process * Draw video frame into fbo and apply the user shader from opengl()->userShader(); * \param frame input frame can be a frame holding host memory data, or any other GPU frame can interop with OpenGL texture (including frames from HW decoders in QtAV). * Output frame holds an RGB texture, which can be processed in the next GPU filter, or rendered by OpenGL renderers. * When process() is done, FBO before before process() is bounded. */ void process(Statistics* statistics, VideoFrame* frame = 0) Q_DECL_OVERRIDE; }; } //namespace QtAV #endif // QTAV_GLSLFILTER_H QtAV-1.12.0/src/QtAV/Geometry.h000066400000000000000000000142071312235004300157510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GEOMETRY_H #define QTAV_GEOMETRY_H #include #include #include // TODO: generate vertex/fragment shader code from Geometry.attributes() namespace QtAV { enum DataType { //equals to GL_BYTE etc. TypeS8 = 0x1400, //S8 TypeU8 = 0x1401, //U8 TypeS16 = 0x1402, //S16 TypeU16 = 0x1403, //U16 TypeS32 = 0x1404, //S32 TypeU32 = 0x1405, //U32 TypeF32 = 0x1406 //F32 }; class Q_AV_EXPORT Attribute { bool m_normalize; DataType m_type; int m_tupleSize, m_offset; QByteArray m_name; public: Attribute(DataType type = TypeF32, int tupleSize = 0, int offset = 0, bool normalize = false); Attribute(const QByteArray& name, DataType type = TypeF32, int tupleSize = 0, int offset = 0, bool normalize = false); QByteArray name() const {return m_name;} DataType type() const {return m_type;} int tupleSize() const {return m_tupleSize;} int offset() const {return m_offset;} bool normalize() const {return m_normalize;} bool operator==(const Attribute& other) const { return tupleSize() == other.tupleSize() && offset() == other.offset() && type() == other.type() && normalize() == other.normalize(); } }; #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug debug, const Attribute &a); #endif /*! * \brief The Geometry class * \code * foreach (const Attribute& a, g->attributes()) { * program()->setAttributeBuffer(a.name(), a.type(), a.offset(), a.tupleSize(), g->stride()); * } * \endcode */ class Q_AV_EXPORT Geometry { public: /// Strip or Triangles is preferred by ANGLE. The values are equal to opengl enum Primitive { Triangles = 0x0004, TriangleStrip = 0x0005, //default TriangleFan = 0x0006, // Not recommended }; Geometry(int vertexCount = 0, int indexCount = 0, DataType indexType = TypeU16); virtual ~Geometry() {} Primitive primitive() const {return m_primitive;} void setPrimitive(Primitive value) { m_primitive = value;} int vertexCount() const {return m_vcount;} void setVertexCount(int value) {m_vcount = value;} // TODO: remove, or allocate data here // TODO: setStride and no virtual virtual int stride() const = 0; // TODO: add/set/remove attributes() virtual const QVector& attributes() const = 0; void* vertexData() { return m_vdata.data();} const void* vertexData() const { return m_vdata.constData();} const void* constVertexData() const { return m_vdata.constData();} void dumpVertexData(); void* indexData() { return m_icount > 0 ? m_idata.data() : NULL;} const void* indexData() const { return m_icount > 0 ? m_idata.constData() : NULL;} const void* constIndexData() const { return m_icount > 0 ? m_idata.constData() : NULL;} int indexCount() const { return m_icount;} int indexDataSize() const; // GL_UNSIGNED_BYTE/SHORT/INT DataType indexType() const {return m_itype;} void setIndexType(DataType value) { m_itype = value;} void setIndexValue(int index, int value); void setIndexValue(int index, int v1, int v2, int v3); // a triangle void dumpIndexData(); /*! * \brief allocate * Call allocate() when all parameters are set. vertexData() may change */ void allocate(int nbVertex, int nbIndex = 0); /*! * \brief compare * Compare each attribute and stride that used in glVertexAttribPointer * \return true if equal */ bool compare(const Geometry *other) const; protected: Primitive m_primitive; DataType m_itype; int m_vcount; int m_icount; QByteArray m_vdata; QByteArray m_idata; }; class Q_AV_EXPORT TexturedGeometry : public Geometry { public: TexturedGeometry(); /*! * \brief setTextureCount * sometimes we needs more than 1 texture coordinates, for example we have to set rectangle texture * coordinates for each plane. */ void setTextureCount(int value); int textureCount() const; void setRect(const QRectF& r, const QRectF& tr, int texIndex = 0); void setGeometryRect(const QRectF& r); void setTextureRect(const QRectF& tr, int texIndex = 0); int stride() const Q_DECL_OVERRIDE { return 2*sizeof(float)*(textureCount()+1); } const QVector& attributes() const Q_DECL_OVERRIDE; virtual void create(); private: void setPoint(int index, const QPointF& p, const QPointF& tp, int texIndex = 0); void setGeometryPoint(int index, const QPointF& p); void setTexturePoint(int index, const QPointF& tp, int texIndex = 0); protected: int nb_tex; QRectF geo_rect; QVector texRect; QVector a; }; class Q_AV_EXPORT Sphere : public TexturedGeometry { public: Sphere(); void setResolution(int w, int h); // >= 2x2 void setRadius(float value); float radius() const; void create() Q_DECL_OVERRIDE; int stride() const Q_DECL_OVERRIDE { return 3*sizeof(float)+2*sizeof(float)*textureCount(); } protected: using Geometry::setPrimitive; private: int ru, rv; float r; }; } //namespace QtAV #endif //QTAV_GEOMETRY_H QtAV-1.12.0/src/QtAV/GeometryRenderer.h000066400000000000000000000061301312235004300174340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GEOMETRYRENDERER_H #define QTAV_GEOMETRYRENDERER_H #include #define QT_VAO (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)) #if QT_VAO #include #endif //QT_VAO # if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include typedef QGLBuffer QOpenGLBuffer; #endif QT_BEGIN_NAMESPACE class QOpenGLShaderProgram; QT_END_NAMESPACE namespace QtAV { /*! * \brief The GeometryRenderer class * TODO: support multiple geometry with same primitive (glPrimitiveRestartIndex, glDrawArraysInstanced, glDrawArraysInstanced, glMultiDrawArrays...) */ class Q_AV_EXPORT GeometryRenderer { public: // rendering features. Use all possible features as the default. static const int kVBO = 0x01; static const int kIBO = 0x02; static const int kVAO = 0x04; static const int kMapBuffer = 1<<16; // TODO: VAB, VBUM etc. GeometryRenderer(); virtual ~GeometryRenderer() {} // call updateBuffer internally in bindBuffer if feature is changed void setFeature(int f, bool on); void setFeatures(int value); int features() const; int actualFeatures() const; bool testFeatures(int value) const; /*! * \brief updateGeometry * Update geometry buffer. Rebind VBO, IBO to VAO if geometry attributes is changed. * Assume attributes are bound in the order 0, 1, 2,.... * Make sure the old geometry is not destroyed before this call because it will be used to compare with the new \l geo * \param geo null: release vao/vbo */ void updateGeometry(Geometry* geo = NULL); virtual void render(); protected: void bindBuffers(); void unbindBuffers(); private: Geometry *g; int features_; int vbo_size, ibo_size; // QOpenGLBuffer.size() may get error 0x501 QOpenGLBuffer vbo; //VertexBuffer #if QT_VAO QOpenGLVertexArrayObject vao; #endif //QT_VAO QOpenGLBuffer ibo; // geometry characteristic int stride; QVector attrib; }; } //namespace QtAV #endif //QTAV_GEOMETRYRENDERER_H QtAV-1.12.0/src/QtAV/LibAVFilter.h000066400000000000000000000072141312235004300162610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_LIBAVFILTER_H #define QTAV_LIBAVFILTER_H #include namespace QtAV { class Q_AV_EXPORT LibAVFilter { public: static QString filterDescription(const QString& filterName); static QStringList videoFilters(); static QStringList audioFilters(); /*! * \brief The Status enum * Status of filter graph. * If setOptions() is called, the status is NotConfigured, and will to configure when processing * a new frame. * Filter graph will be reconfigured if options, incoming video frame format or size changed */ enum Status { NotConfigured, ConfigureFailed, ConfigureOk }; LibAVFilter(); virtual ~LibAVFilter(); /*! * \brief setOptions * Set new option. Filter graph will be setup if receives a frame if options changed. * \param options option string for libavfilter. libav and ffmpeg are different */ void setOptions(const QString& options); QString options() const; Status status() const; protected: virtual QString sourceArguments() const = 0; bool pushVideoFrame(Frame* frame, bool changed); bool pushAudioFrame(Frame* frame, bool changed); void* pullFrameHolder(); static QStringList registeredFilters(int type); // filters whose input/output type matches virtual void optionsChanged() {} class Private; Private *priv; }; class Q_AV_EXPORT LibAVFilterVideo : public VideoFilter, public LibAVFilter { Q_OBJECT Q_PROPERTY(QString options READ options WRITE setOptions NOTIFY optionsChanged) Q_PROPERTY(QStringList filters READ filters) public: LibAVFilterVideo(QObject *parent = 0); bool isSupported(VideoFilterContext::Type t) const Q_DECL_OVERRIDE { return t == VideoFilterContext::None;} QStringList filters() const; //the same as LibAVFilter::videoFilters Q_SIGNALS: void optionsChanged() Q_DECL_OVERRIDE; protected: void process(Statistics *statistics, VideoFrame *frame) Q_DECL_OVERRIDE; QString sourceArguments() const Q_DECL_OVERRIDE; }; class Q_AV_EXPORT LibAVFilterAudio : public AudioFilter, public LibAVFilter { Q_OBJECT Q_PROPERTY(QString options READ options WRITE setOptions NOTIFY optionsChanged) Q_PROPERTY(QStringList filters READ filters) public: LibAVFilterAudio(QObject *parent = 0); QStringList filters() const; //the same as LibAVFilter::audioFilters Q_SIGNALS: void optionsChanged() Q_DECL_OVERRIDE; protected: void process(Statistics *statistics, AudioFrame *frame) Q_DECL_OVERRIDE; QString sourceArguments() const Q_DECL_OVERRIDE; }; } //namespace QtAV #endif // QTAV_LIBAVFILTER_H QtAV-1.12.0/src/QtAV/MediaIO.h000066400000000000000000000146711312235004300154320ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_MediaIO_H #define QTAV_MediaIO_H #include //SEEK_SET #include #include #include namespace QtAV { /*! * \brief MediaIO * Built-in io (use MediaIO::create(name), example: MediaIO *qio = MediaIO::create("QIODevice")) * "QIODevice": * properties: * device - read/write. parameter: QIODevice*. example: io->setDevice(mydev) * "QFile" * properties: * device - read only. example: io->device() * protocols: "", "qrc" */ typedef int MediaIOId; class MediaIOPrivate; class Q_AV_EXPORT MediaIO : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(MediaIO) Q_DISABLE_COPY(MediaIO) Q_ENUMS(AccessMode) public: enum AccessMode { Read, // default Write }; /// Registered MediaIO::name(): "QIODevice", "QFile" static QStringList builtInNames(); /*! * \brief createForProtocol * If an MediaIO subclass SomeInput.protocols() contains the protocol, return it's instance. * "QFile" input has protocols "qrc"(and empty "" means "qrc") * \return Null if none of registered MediaIO supports the protocol */ static MediaIO* createForProtocol(const QString& protocol); /*! * \brief createForUrl * Create a MediaIO and setUrl(url) if protocol of url is supported. * Example: MediaIO *qrc = MediaIO::createForUrl("qrc:/icon/test.mkv"); * \return MediaIO instance with url set. Null if protocol is not supported. */ static MediaIO* createForUrl(const QString& url); virtual ~MediaIO(); virtual QString name() const = 0; /*! * \brief setUrl * onUrlChange() will be called if url is different. onUrlChange() will close the old url and open the new url if it's not empty * \param url */ void setUrl(const QString& url = QString()); QString url() const; /*! * \brief setAccessMode * A MediaIO instance can be 1 mode, Read (default) or Write. If !isWritable(), then set to Write will fail and mode does not change * Call it before any function! * \return false if set failed */ bool setAccessMode(AccessMode value); AccessMode accessMode() const; /// supported protocols. default is empty virtual const QStringList& protocols() const; virtual bool isSeekable() const = 0; virtual bool isWritable() const { return false;} /*! * \brief read * read at most maxSize bytes to data, and return the bytes were actually read */ virtual qint64 read(char *data, qint64 maxSize) = 0; /*! * \brief write * write at most maxSize bytes from data, and return the bytes were actually written */ virtual qint64 write(const char* data, qint64 maxSize) { Q_UNUSED(data); Q_UNUSED(maxSize); return 0; } /*! * \brief seek * \param from SEEK_SET, SEEK_CUR and SEEK_END from stdio.h * \return true if success */ virtual bool seek(qint64 offset, int from = SEEK_SET) = 0; /*! * \brief position * MUST implement this. Used in seek * TODO: implement internally by default */ virtual qint64 position() const = 0; /*! * \brief size * \return <=0 if not support */ virtual qint64 size() const = 0; /*! * \brief isVariableSize * Experiment: A hack for size() changes during playback. * If true, containers that estimate duration from pts(or bit rate) will get an invalid duration. Thus no eof get * when the size of playback start reaches. So playback will not stop. * Demuxer seeking should work for this case. */ virtual bool isVariableSize() const { return false;} /*! * \brief setBufferSize * \param value <0: use default value */ void setBufferSize(int value = -1); int bufferSize() const; // The followings are for internal use. used by AVDemuxer, AVMuxer //struct AVIOContext; //anonymous struct in FFmpeg1.0.x void* avioContext(); //const? void release(); //TODO: how to remove it? public: static void registerAll(); template static bool Register(MediaIOId id, const char* name) { return Register(id, create, name);} static MediaIO* create(MediaIOId id); static MediaIO* create(const char* name); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static MediaIOId* next(MediaIOId* id = 0); static const char* name(MediaIOId id); static MediaIOId id(const char* name); private: template static MediaIO* create() { return new C();} typedef MediaIO* (*MediaIOCreator)(); static bool Register(MediaIOId id, MediaIOCreator, const char *name); protected: MediaIO(MediaIOPrivate& d, QObject* parent = 0); /*! * \brief onUrlChanged * Here you can close old url, parse new url() and open it */ virtual void onUrlChanged(); DPTR_DECLARE(MediaIO) //private: // must add QT+=av-private if default ctor is private // base class, not direct create. only final class has public ctor is enough // FIXME: it's required by Q_DECLARE_METATYPE (also copy ctor) MediaIO(QObject* parent = 0); }; Q_DECL_DEPRECATED typedef MediaIO AVInput; // for source compatibility } //namespace QtAV #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include Q_DECLARE_METATYPE(QtAV::MediaIO*) Q_DECLARE_METATYPE(QIODevice*) #endif #endif // QTAV_MediaIO_H QtAV-1.12.0/src/QtAV/OpenGLRendererBase.h000066400000000000000000000056331312235004300175670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLRENDERERBASE_H #define QTAV_OPENGLRENDERERBASE_H #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #elif QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) #include #define QOpenGLFunctions QGLFunctions #endif namespace QtAV { /*! * \brief The OpenGLRendererBase class * Renderering video frames using GLSL. A more generic high level class OpenGLVideo is used internally. * TODO: for Qt5, no QtOpenGL, use QWindow instead. */ class OpenGLRendererBasePrivate; class Q_AV_EXPORT OpenGLRendererBase : public VideoRenderer #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) , public QOpenGLFunctions #endif { DPTR_DECLARE_PRIVATE(OpenGLRendererBase) public: virtual ~OpenGLRendererBase(); bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; OpenGLVideo* opengl() const Q_DECL_OVERRIDE; protected: virtual bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; //called in paintEvent before drawFrame() when required virtual void drawBackground() Q_DECL_OVERRIDE; //draw the current frame using the current paint engine. called by paintEvent() virtual void drawFrame() Q_DECL_OVERRIDE; void onInitializeGL(); void onPaintGL(); void onResizeGL(int w, int h); void onResizeEvent(int w, int h); void onShowEvent(); private: void onSetOutAspectRatioMode(OutAspectRatioMode mode) Q_DECL_OVERRIDE; void onSetOutAspectRatio(qreal ratio) Q_DECL_OVERRIDE; bool onSetOrientation(int value) Q_DECL_OVERRIDE; bool onSetBrightness(qreal b) Q_DECL_OVERRIDE; bool onSetContrast(qreal c) Q_DECL_OVERRIDE; bool onSetHue(qreal h) Q_DECL_OVERRIDE; bool onSetSaturation(qreal s) Q_DECL_OVERRIDE; protected: OpenGLRendererBase(OpenGLRendererBasePrivate &d); }; } //namespace QtAV #endif // QTAV_OPENGLRENDERERBASE_H QtAV-1.12.0/src/QtAV/OpenGLTypes.h000066400000000000000000000131601312235004300163240ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLTYPES_H #define QTAV_OPENGLTYPES_H #include #include namespace QtAV { // TODO: namespace gl/gfx? class Q_AV_EXPORT Uniform { public: enum { V = 16, Vec = 1< * TODO: Qt types */ void set(const QVariant& v); /*! * \brief setGL * Call glUniformXXX to update uniform values that set by set(const T&, int) and mark dirty false. Currently only use OpenGL ES2 supported functions, i.e. uint, double types are not supported. * \return false if location is invalid, or if uniform type is not supported by QOpenGLFunctions * TODO: Sampler */ bool setGL(); bool operator == (const Uniform &other) const { if (type() != other.type()) return false; if (name != other.name) return false; if (data != other.data) return false; return true; } Type type() const {return t;} /*! * \brief tupleSize * 2, 3, 4 for vec2, vec3 and vec4; 2^2, 3^2 and 4^2 for mat2, mat3 and mat4 */ int tupleSize() const {return tuple_size;} /*! * \brief arraySize * If uniform is an array, it's array size; otherwise 1 */ int arraySize() const {return array_size;} /*! * Return an array of given type. the type T must match type(), for example T is float for Float, VecN, MatN and array of them */ template QVector value() const { Q_ASSERT(sizeof(T)*tupleSize()*arraySize() <= data.size()*sizeof(int) && "Bad type or array size"); QVector v(tupleSize()*arraySize()); memcpy((char*)v.data(), (const char*)data.constData(), v.size()*sizeof(T)); return v; } template const T* address() const { Q_ASSERT(sizeof(T)*tupleSize()*arraySize() <= data.size()*sizeof(int) && "Bad type or array size"); return reinterpret_cast(data.constData()); } private: int tuple_size; int array_size; Type t; QVector data; //uniform array }; #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug debug, const Uniform &u); Q_AV_EXPORT QDebug operator<<(QDebug debug, Uniform::Type ut); #endif } //namespace QtAV #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QT_BEGIN_NAMESPACE Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) QT_END_NAMESPACE #endif #endif QtAV-1.12.0/src/QtAV/OpenGLVideo.h000066400000000000000000000106231312235004300162670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLVIDEO_H #define QTAV_OPENGLVIDEO_H #ifndef QT_NO_OPENGL #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include #define QOpenGLContext QGLContext #endif QT_BEGIN_NAMESPACE class QColor; QT_END_NAMESPACE namespace QtAV { class VideoFrame; class VideoShader; class OpenGLVideoPrivate; /*! * \brief The OpenGLVideo class * high level api for renderering a video frame. use VideoShader, VideoMaterial and ShaderManager internally. * By default, VBO is used. Set environment var QTAV_NO_VBO=1 or 0 to disable/enable VBO. * VAO will be enabled if supported. Disabling VAO is the same as VBO. */ class Q_AV_EXPORT OpenGLVideo : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(OpenGLVideo) public: enum MeshType { RectMesh, SphereMesh }; static bool isSupported(VideoFormat::PixelFormat pixfmt); OpenGLVideo(); /*! * \brief setOpenGLContext * a context must be set before renderering. * \param ctx * 0: current context in OpenGL is done. shaders will be released. * QOpenGLContext is QObject in Qt5, and gl resources here will be released automatically if context is destroyed. * But you have to call setOpenGLContext(0) for Qt4 explicitly in the old context. * Viewport is also set here using context surface/paintDevice size and devicePixelRatio. * devicePixelRatio may be wrong for multi-screen with 5.0 * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLWINDOWRENDERER_H #define QTAV_OPENGLWINDOWRENDERER_H #ifndef QT_NO_OPENGL #include #include namespace QtAV { class OpenGLWindowRendererPrivate; class Q_AV_EXPORT OpenGLWindowRenderer : public QOpenGLWindow, public OpenGLRendererBase { Q_OBJECT DPTR_DECLARE_PRIVATE(OpenGLWindowRenderer) Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: explicit OpenGLWindowRenderer(UpdateBehavior updateBehavior = NoPartialUpdate, QWindow *parent = 0); virtual VideoRendererId id() const Q_DECL_OVERRIDE; QWindow* qwindow() Q_DECL_OVERRIDE Q_DECL_FINAL { return this; } Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; protected: virtual void initializeGL() Q_DECL_OVERRIDE; virtual void paintGL() Q_DECL_OVERRIDE; virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; virtual void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef OpenGLWindowRenderer VideoRendererOpenGLWindow; } //namespace QtAV #endif //QT_NO_OPENGL #endif // QTAV_OPENGLWINDOWRENDERER_H QtAV-1.12.0/src/QtAV/Packet.h000066400000000000000000000055571312235004300153750ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_PACKET_H #define QAV_PACKET_H #include #include struct AVPacket; namespace QtAV { class PacketPrivate; class Q_AV_EXPORT Packet { public: static Packet fromAVPacket(const AVPacket* avpkt, double time_base); static bool fromAVPacket(Packet *pkt, const AVPacket *avpkt, double time_base); static Packet createEOF(); Packet(); ~Packet(); // required if no defination of PacketPrivate Packet(const Packet& other); Packet& operator =(const Packet& other); bool isEOF()const; inline bool isValid() const; /*! * \brief asAVPacket * If Packet is constructed from AVPacket, then data and properties are the same as that AVPacket. * Otherwise, Packet's data and properties are used and no side data. * Packet takes the owner ship. time unit is always ms even constructed from AVPacket. */ const AVPacket* asAVPacket() const; /*! * \brief skip * Skip bytes of packet data. User has to update pts, dts etc to new values. * Useful for asAVPakcet(). When asAVPakcet() is called, AVPacket->pts/dts will be updated to new values. */ void skip(int bytes); bool hasKeyFrame; bool isCorrupt; QByteArray data; // time unit is s. qreal pts, duration; qreal dts; qint64 position; // position in source file byte stream private: // we must define default/copy ctor, dtor and operator= so that we can provide only forward declaration of PacketPrivate mutable QSharedDataPointer d; }; bool Packet::isValid() const { return !isCorrupt && !data.isEmpty() && pts >= 0 && duration >= 0; //!data.isEmpty()? } #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug debug, const Packet &pkt); #endif } //namespace QtAV Q_DECLARE_METATYPE(QtAV::Packet) #endif // QAV_PACKET_H QtAV-1.12.0/src/QtAV/QPainterRenderer.h000066400000000000000000000033731312235004300173720ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_QPAINTERRENDERER_H #define QAV_QPAINTERRENDERER_H #include #include //TODO: not abstract. namespace QtAV { class QPainterRendererPrivate; class Q_AV_EXPORT QPainterRenderer : public VideoRenderer { DPTR_DECLARE_PRIVATE(QPainterRenderer) public: QPainterRenderer(); bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; protected: bool preparePixmap(const VideoFrame& frame); void drawBackground() Q_DECL_OVERRIDE; //draw the current frame using the current paint engine. called by paintEvent() void drawFrame() Q_DECL_OVERRIDE; QPainterRenderer(QPainterRendererPrivate& d); }; } //namespace QtAV #endif // QAV_QPAINTERRENDERER_H QtAV-1.12.0/src/QtAV/QtAV000066400000000000000000000000221312235004300145310ustar00rootroot00000000000000#include "QtAV.h" QtAV-1.12.0/src/QtAV/QtAV.h000066400000000000000000000046631312235004300147760ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_H #define QTAV_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0) && !defined(QT_NO_OPENGL)) || defined(QT_OPENGL_LIB) #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include //The following renderer headers can be removed #include #if QT_VERSION >= QT_VERSION_CHECK(5,4,0) #include #endif #include #include #include #endif // QTAV_H QtAV-1.12.0/src/QtAV/QtAV_Global.h000066400000000000000000000141341312235004300162500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GLOBAL_H #define QTAV_GLOBAL_H #include #include #include //QByteArrayLiteral check #include #include #ifdef BUILD_QTAV_STATIC #define Q_AV_EXPORT #else #if defined(BUILD_QTAV_LIB) # undef Q_AV_EXPORT # define Q_AV_EXPORT Q_DECL_EXPORT #else # undef Q_AV_EXPORT # define Q_AV_EXPORT Q_DECL_IMPORT //only for vc? #endif #endif //BUILD_QTAV_STATIC #define Q_AV_PRIVATE_EXPORT Q_AV_EXPORT /* runtime version. used to compare with compile time version */ Q_AV_EXPORT unsigned QtAV_Version(); Q_AV_EXPORT QString QtAV_Version_String(); Q_AV_EXPORT QString QtAV_Version_String_Long(); namespace QtAV { enum LogLevel { LogOff, LogDebug, // log all LogWarning, // log warning, critical, fatal LogCritical, // log critical, fatal LogFatal, // log fatal LogAll }; Q_AV_EXPORT QString aboutFFmpeg_PlainText(); Q_AV_EXPORT QString aboutFFmpeg_HTML(); Q_AV_EXPORT QString aboutQtAV_PlainText(); Q_AV_EXPORT QString aboutQtAV_HTML(); /*! * Default value: LogOff for release build. LogAll for debug build. * The level can also be changed at runtime by setting the QTAV_LOG_LEVEL or QTAV_LOG environment variable; * QTAV_LOG_LEVEL can be: off, debug, warning, critical, fatal, all. Or use their enum values * if both setLogLevel() is called and QTAV_LOG_LEVEL is set, the environment variable takes preceden. */ Q_AV_EXPORT void setLogLevel(LogLevel value); Q_AV_EXPORT LogLevel logLevel(); /// Default handler is qt message logger. Set environment QTAV_FFMPEG_LOG=0 or setFFmpegLogHandler(0) to disable. Q_AV_EXPORT void setFFmpegLogHandler(void(*)(void *, int, const char *, va_list)); /*! * \brief setFFmpegLogLevel * \param level can be: quiet, panic, fatal, error, warn, info, verbose, debug, trace */ Q_AV_EXPORT void setFFmpegLogLevel(const QByteArray& level); /// query the common options of avformat/avcodec that can be used by AVPlayer::setOptionsForXXX. Format/codec specified options are also included Q_AV_EXPORT QString avformatOptions(); Q_AV_EXPORT QString avcodecOptions(); ////////////Types///////////// enum MediaStatus { UnknownMediaStatus, NoMedia, LoadingMedia, // when source is set LoadedMedia, // if auto load and source is set. player is stopped state StalledMedia, // insufficient buffering or other interruptions (timeout, user interrupt) BufferingMedia, // NOT IMPLEMENTED BufferedMedia, // when playing //NOT IMPLEMENTED EndOfMedia, // Playback has reached the end of the current media. The player is in the StoppedState. InvalidMedia // what if loop > 0 or stopPosition() is not mediaStopPosition()? }; enum BufferMode { BufferTime, BufferBytes, BufferPackets }; enum MediaEndActionFlag { MediaEndAction_Default, /// stop playback (if loop end) and clear video renderer MediaEndAction_KeepDisplay = 1, /// stop playback but video renderer keeps the last frame MediaEndAction_Pause = 1 << 1 /// pause playback. Currently AVPlayer repeat mode will not work if this flag is set }; Q_DECLARE_FLAGS(MediaEndAction, MediaEndActionFlag) enum SeekUnit { SeekByTime, // only this is supported now SeekByByte, SeekByFrame }; enum SeekType { AccurateSeek, // slow KeyFrameSeek, // fast AnyFrameSeek }; //http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-5-200204-I!!PDF-E.pdf // TODO: other color spaces (yuv itu.xxxx, XYZ, ...) enum ColorSpace { ColorSpace_Unknown, ColorSpace_RGB, ColorSpace_GBR, // for planar gbr format(e.g. video from x264) used in glsl ColorSpace_BT601, ColorSpace_BT709, ColorSpace_XYZ }; /*! * \brief The ColorRange enum * YUV or RGB color range */ enum ColorRange { ColorRange_Unknown, ColorRange_Limited, // TV, MPEG ColorRange_Full // PC, JPEG }; /*! * \brief The SurfaceType enum * HostMemorySurface: * Map the decoded frame to host memory * GLTextureSurface: * Map the decoded frame as an OpenGL texture * SourceSurface: * get the original surface from decoder, for example VASurfaceID for va-api, CUdeviceptr for CUDA and IDirect3DSurface9* for DXVA. * Zero copy mode is required. * UserSurface: * Do your own magic mapping with it */ enum SurfaceType { HostMemorySurface, GLTextureSurface, SourceSurface, UserSurface = 0xffff }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::MediaStatus) Q_DECLARE_METATYPE(QtAV::MediaEndAction) // TODO: internal use. move to a private header #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #define QStringLiteral(X) QString::fromUtf8(X) #endif //QT_VERSION #ifndef QByteArrayLiteral #define QByteArrayLiteral(str) QByteArray(str, sizeof(str) - 1) #endif /* * msvc sucks! can not deal with (defined(QTAV_HAVE_##FEATURE) && QTAV_HAVE_##FEATURE) */ // TODO: internal use. move to a private header #define QTAV_HAVE(FEATURE) (defined QTAV_HAVE_##FEATURE && QTAV_HAVE_##FEATURE) #ifndef Q_DECL_OVERRIDE #define Q_DECL_OVERRIDE #endif #ifndef Q_DECL_FINAL #define Q_DECL_FINAL #endif #if defined(BUILD_QTAV_LIB) #define QTAV_DEPRECATED #else #define QTAV_DEPRECATED Q_DECL_DEPRECATED #endif #endif // QTAV_GLOBAL_H QtAV-1.12.0/src/QtAV/Statistics.h000066400000000000000000000075201312235004300163100ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_STATISTICS_H #define QTAV_STATISTICS_H #include #include #include #include /*! * values from functions are dynamically calculated */ namespace QtAV { class Q_AV_EXPORT Statistics { public: Statistics(); ~Statistics(); void reset(); QString url; int bit_rate; QString format; QTime start_time, duration; QHash metadata; class Common { public: Common(); //TODO: dynamic bit rate compute bool available; QString codec, codec_long; QString decoder; QString decoder_detail; QTime current_time, total_time, start_time; int bit_rate; qint64 frames; qreal frame_rate; // average fps stored in media stream information //union member with ctor, dtor, copy ctor only works in c++11 /*union { audio_only audio; video_only video; } only;*/ QHash metadata; } audio, video; //init them //from AVCodecContext class Q_AV_EXPORT AudioOnly { public: AudioOnly(); int sample_rate; ///< samples per second int channels; ///< number of audio channels QString channel_layout; QString sample_fmt; ///< sample format /** * Number of samples per channel in an audio frame. * - decoding: may be set by some decoders to indicate constant frame size */ int frame_size; /** * number of bytes per packet if constant and known or 0 * Used by some WAV based audio codecs. */ int block_align; } audio_only; //from AVCodecContext class Q_AV_EXPORT VideoOnly { public: //union member with ctor, dtor, copy ctor only works in c++11 VideoOnly(); VideoOnly(const VideoOnly&); VideoOnly& operator =(const VideoOnly&); ~VideoOnly(); // compute from pts history qreal currentDisplayFPS() const; qreal pts() const; // last pts int width, height; /** * Bitstream width / height, may be different from width/height if lowres enabled. * - decoding: Set by user before init if known. Codec should override / dynamically change if needed. */ int coded_width, coded_height; /** * the number of pictures in a group of pictures, or 0 for intra_only */ int gop_size; QString pix_fmt; /// return current absolute time (seconds since epcho qint64 frameDisplayed(qreal pts); // used to compute currentDisplayFPS() private: class Private; QExplicitlySharedDataPointer d; } video_only; }; } //namespace QtAV #endif // QTAV_STATISTICS_H QtAV-1.12.0/src/QtAV/SubImage.h000066400000000000000000000050071312235004300156500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and LibASS Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBIMAGE_H #define QTAV_SUBIMAGE_H #include #include namespace QtAV { /*! * \brief The SubImage struct * (x, y, w, h) is relative to parent SubImageSet (0, 0, SubImageSet.w, SubImageSet.h) */ struct SubImageSet; struct Q_AV_EXPORT SubImage { SubImage(int x = 0, int y = 0, int w = 0, int h = 0, int stride = 0); bool operator ==(const SubImage& o) const { return x == o.x && y == o.y && w == o.w && h == o.h && stride == o.stride && color == o.color && data == o.data; } int x, y; int w, h; int stride; quint32 color; //ass only QByteArray data; //size = stride*h }; struct Q_AV_EXPORT SubImageSet { enum Format { ASS, RGBA, Unknown }; SubImageSet(int width = 0, int height = 0, Format format = Unknown); int width() const {return w;} int height() const {return h;} Format format() const {return fmt;} bool isValid() const {return !images.isEmpty() && w > 0 && h > 0 && fmt != Unknown;} void reset(int width = 0, int height = 0, Format format = Unknown) { fmt = format; w = width; h = height; ++id; images.clear(); } bool operator ==(const SubImageSet& other) const { return id == other.id && w == other.w && h == other.h && fmt == other.fmt && images == other.images; //TODO: image data } QVector images; private: Format fmt; int w, h; int id; }; } //namespace QtAV #endif //QTAV_SUBIMAGE_H QtAV-1.12.0/src/QtAV/Subtitle.h000066400000000000000000000234541312235004300157550ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBTITLE_H #define QTAV_SUBTITLE_H #include #include #include #include #include /* * to avoid read error, subtitle size > 10*1024*1024 will be ignored. */ namespace QtAV { class Q_AV_EXPORT SubtitleFrame { public: SubtitleFrame() : begin(0) , end(0) {} // valied: begin < end bool isValid() const { return begin < end;} operator bool() const { return isValid();} bool operator !() const { return !isValid();} inline bool operator <(const SubtitleFrame& f) const { return end < f.end;} inline bool operator <(qreal t) const { return end < t;} qreal begin; qreal end; QString text; //plain text. always valid }; class Q_AV_EXPORT Subtitle : public QObject { Q_OBJECT Q_PROPERTY(QByteArray codec READ codec WRITE setCodec NOTIFY codecChanged) // QList Q_PROPERTY(QStringList engines READ engines WRITE setEngines NOTIFY enginesChanged) Q_PROPERTY(QString engine READ engine NOTIFY engineChanged) Q_PROPERTY(bool fuzzyMatch READ fuzzyMatch WRITE setFuzzyMatch NOTIFY fuzzyMatchChanged) Q_PROPERTY(QByteArray rawData READ rawData WRITE setRawData NOTIFY rawDataChanged) Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) Q_PROPERTY(QStringList dirs READ dirs WRITE setDirs NOTIFY dirsChanged) Q_PROPERTY(QStringList suffixes READ suffixes WRITE setSuffixes NOTIFY suffixesChanged) Q_PROPERTY(QStringList supportedSuffixes READ supportedSuffixes NOTIFY supportedSuffixesChanged) Q_PROPERTY(qreal timestamp READ timestamp WRITE setTimestamp) Q_PROPERTY(qreal delay READ delay WRITE setDelay NOTIFY delayChanged) Q_PROPERTY(QString text READ getText) Q_PROPERTY(bool loaded READ isLoaded) Q_PROPERTY(bool canRender READ canRender NOTIFY canRenderChanged) // font properties for libass engine Q_PROPERTY(QString fontFile READ fontFile WRITE setFontFile NOTIFY fontFileChanged) Q_PROPERTY(QString fontsDir READ fontsDir WRITE setFontsDir NOTIFY fontsDirChanged) Q_PROPERTY(bool fontFileForced READ isFontFileForced WRITE setFontFileForced NOTIFY fontFileForcedChanged) public: explicit Subtitle(QObject *parent = 0); virtual ~Subtitle(); /*! * \brief setCodec * set subtitle encoding that supported by QTextCodec. You have to call load() to manually reload the subtitle with given codec * \param value codec name. see QTextCodec.availableCodecs(). Empty value means using the default codec in QTextCodec * If linked with libchardet(https://github.com/cnangel/libchardet) or can dynamically load it, * set value of "AutoDetect" to detect the charset of subtitle */ void setCodec(const QByteArray& value); QByteArray codec() const; /*! * \brief isValid * indicate whether the subtitle can be found and processed * \return */ bool isLoaded() const; /*! * \brief setEngines * Set subtitle processor engine names, in priority order. When loading a subtitle, use the engines * one by one until a usable engine is found. * \param value */ void setEngines(const QStringList& value); QStringList engines() const; /*! * \brief engine * \return The engine in use for current subtitle */ QString engine() const; void setFuzzyMatch(bool value); bool fuzzyMatch() const; void setRawData(const QByteArray& data); QByteArray rawData() const; /*! * \brief setFileName * the given name will be in the 1st place to try to open(if using fuzzy match). then files in suffixes() order * or in processor's supported suffixes order * \param name */ void setFileName(const QString& name); QString fileName() const; /*! * \brief setDirs * Set subtitle search directories. Video's dir will always be added. */ void setDirs(const QStringList& value); QStringList dirs() const; /*! * \brief supportedFormats * the suffix names supported by all engines. for example ["ass", "ssa"] * \return */ QStringList supportedSuffixes() const; /*! * \brief setSuffixes * default is using SubtitleProcessor. Empty equals default value. But suffixes() will return empty. */ void setSuffixes(const QStringList& value); QStringList suffixes() const; qreal timestamp() const; /*! * \brief delay * unit: second * The subtitle from getText() and getImage() is at the time: timestamp() + delay() * \return */ qreal delay() const; void setDelay(qreal value); /*! * \brief canRender * wether current processor supports rendering. Check before getImage() * \return */ bool canRender() const; // call setTimestamp before getText/Image //plain text. separated by '\n' if more more than 1 text rects found QString getText() const; /*! * \brief getImage * Get a subtitle image with given (video) frame size. The result image size usually smaller than * given frame size because subtitle are lines of text. The boundingRect indicates the actual * image position and size relative to given size. * The result image format is QImage::Format_ARGB32 * \return empty image if no image, or subtitle processor does not support renderering */ QImage getImage(int width, int height, QRect* boundingRect = 0); SubImageSet getSubImages(int width, int height, QRect* boundingRect = 0); // used for embedded subtitles. /*! * \brief processHeader * Always called if switch to a new internal subtitle stream. But header data can be empty * Used by libass to set style etc. */ bool processHeader(const QByteArray &codec, const QByteArray& data); // ffmpeg decodes subtitle lines and call processLine. if AVPacket contains plain text, no decoding is ok bool processLine(const QByteArray& data, qreal pts = -1, qreal duration = 0); QString fontFile() const; void setFontFile(const QString& value); /*! * \brief fontsDir * Not tested for dwrite provider. FontConfig can work. */ QString fontsDir() const; void setFontsDir(const QString& value); bool isFontFileForced() const; void setFontFileForced(bool value); public Q_SLOTS: /*! * \brief start * start to process the whole subtitle content in a thread */ void load(); void loadAsync(); void setTimestamp(qreal t); Q_SIGNALS: // TODO: also add to AVPlayer? /// empty path if load from raw data void loaded(const QString& path = QString()); void canRenderChanged(); void codecChanged(); void enginesChanged(); void fuzzyMatchChanged(); /*! * \brief contentChanged * emitted when text content changed. */ void contentChanged(); void rawDataChanged(); void fileNameChanged(); void dirsChanged(); void suffixesChanged(); void supportedSuffixesChanged(); void engineChanged(); void delayChanged(); void fontFileChanged(); void fontsDirChanged(); void fontFileForcedChanged(); private: void checkCapability(); class Private; Private *priv; }; // internal use class Q_AV_EXPORT SubtitleAPIProxy { public: SubtitleAPIProxy(QObject* obj); void setSubtitle(Subtitle *sub); // API from Subtitle /*! * \brief setCodec * set subtitle encoding that supported by QTextCodec. subtitle will be reloaded * \param value codec name. see QTextCodec.availableCodecs(). Empty value means using the default codec in QTextCodec */ void setCodec(const QByteArray& value); QByteArray codec() const; bool isLoaded() const; void setEngines(const QStringList& value); QStringList engines() const; QString engine() const; void setFuzzyMatch(bool value); bool fuzzyMatch() const; //always use exact file path by setFile(). file name is used internally //void setFileName(const QString& name); //QString fileName() const; void setDirs(const QStringList& value); QStringList dirs() const; QStringList supportedSuffixes() const; void setSuffixes(const QStringList& value); QStringList suffixes() const; bool canRender() const; // TODO: rename to capability() qreal delay() const; void setDelay(qreal value); QString fontFile() const; void setFontFile(const QString& value); QString fontsDir() const; void setFontsDir(const QString& value); bool isFontFileForced() const; void setFontFileForced(bool value); // API from PlayerSubtitle /* void setFile(const QString& file); QString file() const; void setAutoLoad(bool value); bool autoLoad() const; */ private: QObject *m_obj; Subtitle *m_s; }; } //namespace QtAV #endif // QTAV_SUBTITLE_H QtAV-1.12.0/src/QtAV/SubtitleFilter.h000066400000000000000000000114511312235004300171150ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBTITLEFILTER_H #define QTAV_SUBTITLEFILTER_H #include #include //final class namespace QtAV { class AVPlayer; class SubtitleFilterPrivate; /*! * \brief The SubtitleFilter class * draw text and image subtitles * Subtitle load priority: user specified file (setFile(...)) > auto load external (autoLoad() must be true) > embedded subtitle */ class Q_AV_EXPORT SubtitleFilter : public VideoFilter, public SubtitleAPIProxy { Q_OBJECT DPTR_DECLARE_PRIVATE(SubtitleFilter) Q_PROPERTY(QByteArray codec READ codec WRITE setCodec NOTIFY codecChanged) Q_PROPERTY(QStringList engines READ engines WRITE setEngines NOTIFY enginesChanged) Q_PROPERTY(QString engine READ engine NOTIFY engineChanged) Q_PROPERTY(bool fuzzyMatch READ fuzzyMatch WRITE setFuzzyMatch NOTIFY fuzzyMatchChanged) //Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) Q_PROPERTY(QStringList dirs READ dirs WRITE setDirs NOTIFY dirsChanged) Q_PROPERTY(QStringList suffixes READ suffixes WRITE setSuffixes NOTIFY suffixesChanged) Q_PROPERTY(QStringList supportedSuffixes READ supportedSuffixes NOTIFY supportedSuffixesChanged) Q_PROPERTY(bool canRender READ canRender NOTIFY canRenderChanged) Q_PROPERTY(qreal delay READ delay WRITE setDelay NOTIFY delayChanged) Q_PROPERTY(bool autoLoad READ autoLoad WRITE setAutoLoad NOTIFY autoLoadChanged) Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged) Q_PROPERTY(QRectF rect READ rect WRITE setRect NOTIFY rectChanged) Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) // font properties for libass engine Q_PROPERTY(QString fontFile READ fontFile WRITE setFontFile NOTIFY fontFileChanged) Q_PROPERTY(QString fontsDir READ fontsDir WRITE setFontsDir NOTIFY fontsDirChanged) Q_PROPERTY(bool fontFileForced READ isFontFileForced WRITE setFontFileForced NOTIFY fontFileForcedChanged) public: explicit SubtitleFilter(QObject *parent = 0); void setPlayer(AVPlayer* player); bool isSupported(VideoFilterContext::Type ct) const Q_DECL_OVERRIDE { return ct == VideoFilterContext::QtPainter || ct == VideoFilterContext::X11; } /*! * \brief setFile * Load user selected subtitle. The subtitle will not change unless you manually setFile(QString()). */ void setFile(const QString& file); QString file() const; /*! * \brief autoLoad * Auto find and load a suitable external subtitle if file() is not empty. */ bool autoLoad() const; // <1 means normalized. not valid means the whole target rect. default is (0, 0, 1, 0.9) and align bottom void setRect(const QRectF& r); QRectF rect() const; void setFont(const QFont& f); QFont font() const; void setColor(const QColor& c); QColor color() const; QString subtitleText(qreal t) const; public Q_SLOTS: // TODO: enable changed & autoload=> load void setAutoLoad(bool value); Q_SIGNALS: void rectChanged(); void fontChanged(); void colorChanged(); void autoLoadChanged(bool value); Q_SIGNALS: void fileChanged(); void canRenderChanged(); void loaded(const QString& path); void codecChanged(); void enginesChanged(); void fuzzyMatchChanged(); void contentChanged(); //void fileNameChanged(); void dirsChanged(); void suffixesChanged(); void supportedSuffixesChanged(); void engineChanged(); void delayChanged(); void fontFileChanged(); void fontsDirChanged(); void fontFileForcedChanged(); protected: void process(Statistics* statistics, VideoFrame* frame) Q_DECL_OVERRIDE; }; } //namespace QtAV #endif // QTAV_SUBTITLEFILTER_H QtAV-1.12.0/src/QtAV/SurfaceInterop.h000066400000000000000000000062261312235004300171110ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROP_H #define QTAV_SURFACEINTEROP_H #include #include namespace QtAV { class Q_AV_EXPORT VideoSurfaceInterop { public: virtual ~VideoSurfaceInterop() {} /*! * \brief map * currently is used to map a frame from hardware decoder to opengl texture, host memory. * \param type currently only support GLTextureSurface and HostMemorySurface for some decoders * \param fmt * HostMemorySurface: must be a packed rgb format * \param handle address of real handle. handle value can be modified in map() and the caller (VideoShader for example) should manage the changes. * GLTextureSurface: usually opengl texture. maybe other objects for some decoders in the feature * HostMemorySurface: a VideoFrame ptr * \param plane * \return Null if not supported or failed. handle if success. */ virtual void* map(SurfaceType type, const VideoFormat& fmt, void* handle = 0, int plane = 0) { Q_UNUSED(type); Q_UNUSED(fmt); Q_UNUSED(handle); Q_UNUSED(plane); return 0; } // TODO: SurfaceType. unmap is currenty used by opengl rendering virtual void unmap(void* handle) { Q_UNUSED(handle);} //virtual void unmap(void* handle, SurfaceType type) { Q_UNUSED(handle);} //for SourceSurfaceType /*! * \brief createHandle * It is used by opengl renderer to create a texture when rendering frame from VDA/VideoToolbox decoder * VideoSurfaceInterop does not have the ownership. VideoShader does * \return NULL if not used for opengl rendering. handle if create here */ virtual void* createHandle(void* handle, SurfaceType type, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) { Q_UNUSED(handle); Q_UNUSED(type); Q_UNUSED(fmt); Q_UNUSED(plane); Q_UNUSED(planeWidth); Q_UNUSED(planeHeight); return 0; } }; typedef QSharedPointer VideoSurfaceInteropPtr; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::VideoSurfaceInteropPtr) #endif // QTAV_SURFACEINTEROP_H QtAV-1.12.0/src/QtAV/VideoCapture.h000066400000000000000000000125031312235004300165450ustar00rootroot00000000000000/****************************************************************************** VideoCapture.h: description Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOCAPTURE_H #define QTAV_VIDEOCAPTURE_H #include #include #include #include namespace QtAV { //on capture per thread or all in one thread? class Q_AV_EXPORT VideoCapture : public QObject { Q_OBJECT Q_PROPERTY(bool async READ isAsync WRITE setAsync NOTIFY asyncChanged) Q_PROPERTY(bool autoSave READ autoSave WRITE setAutoSave NOTIFY autoSaveChanged) Q_PROPERTY(bool originalFormat READ isOriginalFormat WRITE setOriginalFormat NOTIFY originalFormatChanged) Q_PROPERTY(QString saveFormat READ saveFormat WRITE setSaveFormat NOTIFY saveFormatChanged) Q_PROPERTY(int quality READ quality WRITE setQuality NOTIFY qualityChanged) Q_PROPERTY(QString captureName READ captureName WRITE setCaptureName NOTIFY captureNameChanged) Q_PROPERTY(QString captureDir READ captureDir WRITE setCaptureDir NOTIFY captureDirChanged) public: explicit VideoCapture(QObject *parent = 0); // TODO: if async is true, the cloned hw frame shares the same interop object with original frame, so interop obj may do 2 map() at the same time. It's not safe void setAsync(bool value = true); bool isAsync() const; /*! * \brief setAutoSave * If auto save is true, then the captured video frame will be saved as a file when frame is available. * Otherwise, signal ready() and finished() will be emitted without doing other things. */ void setAutoSave(bool value = true); bool autoSave() const; /*! * \brief setOriginalFormat * Save the original frame, can be YUV, NV12 etc. No format converting. default is false * The file name suffix is video frame's format name in lower case, e.g. yuv420p, nv12. * quality property will not take effect. */ void setOriginalFormat(bool value = true); bool isOriginalFormat() const; /*! * \brief setFormat * Set saved format. can be "PNG", "jpg" etc. Not be used if save raw frame data. * \param format image format string like "png", "jpg" */ void setSaveFormat(const QString& format); QString saveFormat() const; /*! * \brief setQuality * Set saved image quality. Not be used if save original frame data. * \param value 0-100, larger is better quality. -1: default quality */ void setQuality(int value); int quality() const; /*! * \brief name * suffix is auto add * empty name: filename_timestamp.format(suffix is videoframe.format.name() if save as raw data) * If autoSave() is true, saved file name will add a timestamp string. */ void setCaptureName(const QString& value); QString captureName() const; void setCaptureDir(const QString& value); QString captureDir() const; public Q_SLOTS: void capture(); Q_SIGNALS: void requested(); /*use it to popup a dialog for selecting dir, name etc. TODO: block avthread if not async*/ /*! * \brief frameAvailable * Emitted when requested frame is available. */ void frameAvailable(const QtAV::VideoFrame& frame); /*! * \brief imageCaptured * Emitted when captured video frame is converted to a QImage. * \param image */ void imageCaptured(const QImage& image); //TODO: emit only if not original format is set? void failed(); /*! * \brief saved * Only for autoSave is true. Emitted when captured frame is saved. * \param path the saved captured frame path. */ void saved(const QString& path); void asyncChanged(); void autoSaveChanged(); void originalFormatChanged(); void saveFormatChanged(); void qualityChanged(); void captureNameChanged(); void captureDirChanged(); private Q_SLOTS: void handleAppQuit(); private: void setVideoFrame(const VideoFrame& frame); // It's called by VideoThread after immediatly setVideoFrame(). Will emit ready() void start(); friend class CaptureTask; friend class VideoThread; bool async; bool auto_save; bool original_fmt; //TODO: use blocking queue? If not, the parameters will change when thre previous is not finished //or use a capture event that wrapper all these parameters int qual; QImage::Format qfmt; QString fmt; QString name, dir; VideoFrame frame; }; } //namespace QtAV #endif // QTAV_VIDEOCAPTURE_H QtAV-1.12.0/src/QtAV/VideoDecoder.h000066400000000000000000000104161312235004300165100ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEODECODER_H #define QTAV_VIDEODECODER_H #include #include #include namespace QtAV { typedef int VideoDecoderId; /*! Useful properties. A key is a string, a value can be int, bool or string. Both int and string are valid for enumerate properties. Flag properties must use int if more than 1 value is used. e.g. decoder->setProperty("display", 1) equals decoder->setProperty("display", "GLX") setOptions() also applies the properties. avcodec (also for VA-API, DXVA, VDA) Use AVCodecContext options CUDA surfaces: 0 is auto deinterlace: 0 "Weave", 1 "Bob", 2 "Adaptive" VA-API display: 0 "X11", 1 "GLX", 2 "DRM" DXVA, VA-API surfaces: 0 default DXVA, VA-API, VDA: sse4: bool CedarV neon: bool FFmpeg skip_loop_filter, skip_idct, skip_frame: -16 "None", 0: "Default", 8 "NoRef", 16 "Bidir", 32 "NoKey", 64 "All" threads: int, 0 is auto vismv(motion vector visualization): flag, 0 "NO", 1 "PF", 2 "BF", 4 "BB" */ class VideoDecoderPrivate; class Q_AV_EXPORT VideoDecoder : public AVDecoder { Q_DISABLE_COPY(VideoDecoder) DPTR_DECLARE_PRIVATE(VideoDecoder) public: static QStringList supportedCodecs(); static VideoDecoder* create(VideoDecoderId id); /*! * \brief create * create a decoder from registered name. FFmpeg decoder will be created for empty name * \param name can be "FFmpeg", "CUDA", "VDA", "VAAPI", "DXVA", "Cedarv" * \return 0 if not registered */ static VideoDecoder* create(const char* name = "FFmpeg"); virtual VideoDecoderId id() const = 0; QString name() const; //name from factory virtual VideoFrame frame() = 0; public: typedef int Id; static QVector registered(); template static bool Register(VideoDecoderId id, const char* name) { return Register(id, create, name);} /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static VideoDecoderId* next(VideoDecoderId* id = 0); static const char* name(VideoDecoderId id); static VideoDecoderId id(const char* name); private: template static VideoDecoder* create() { return new C();} typedef VideoDecoder* (*VideoDecoderCreator)(); protected: static bool Register(VideoDecoderId id, VideoDecoderCreator, const char *name); protected: VideoDecoder(VideoDecoderPrivate& d); private: VideoDecoder(); }; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_FFmpeg; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_CUDA; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_DXVA; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_D3D11; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_VAAPI; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_Cedarv; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_VDA; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_VideoToolbox; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_MediaCodec; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_MMAL; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_QSV; extern Q_AV_EXPORT VideoDecoderId VideoDecoderId_CrystalHD; } //namespace QtAV #endif // QTAV_VIDEODECODER_H QtAV-1.12.0/src/QtAV/VideoEncoder.h000066400000000000000000000105301312235004300165170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOENCODER_H #define QTAV_VIDEOENCODER_H #include #include #include namespace QtAV { typedef int VideoEncoderId; class VideoEncoderPrivate; class Q_AV_EXPORT VideoEncoder : public AVEncoder { Q_OBJECT Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) Q_PROPERTY(qreal frameRate READ frameRate WRITE setFrameRate NOTIFY frameRateChanged) Q_PROPERTY(QtAV::VideoFormat::PixelFormat pixelFormat READ pixelFormat WRITE setPixelFormat NOTIFY pixelFormatChanged) Q_DISABLE_COPY(VideoEncoder) DPTR_DECLARE_PRIVATE(VideoEncoder) public: static QStringList supportedCodecs(); static VideoEncoder* create(VideoEncoderId id); /*! * \brief create * create an encoder from registered names * \param name can be "FFmpeg". FFmpeg encoder will be created for empty name * \return 0 if not registered */ static VideoEncoder* create(const char* name = "FFmpeg"); virtual VideoEncoderId id() const = 0; QString name() const Q_DECL_OVERRIDE; //name from factory /*! * \brief encode * encode a video frame to a Packet * \param frame pass an invalid frame to get delayed frames * \return */ virtual bool encode(const VideoFrame& frame = VideoFrame()) = 0; /// output parameters /*! * \brief setWidth * set the encoded video width. The same as input frame size if value <= 0 */ void setWidth(int value); int width() const; void setHeight(int value); int height() const; /// TODO: check avctx->supported_framerates. use frame_rate_used /*! * \brief setFrameRate * If frame rate is not set, frameRate() returns -1, but internally the default frame rate 25 will be used * \param value */ void setFrameRate(qreal value); qreal frameRate() const; static qreal defaultFrameRate() { return 25;} /*! * \brief setPixelFormat * If not set or set to an unsupported format, a supported format will be used and pixelFormat() will be that format after open() */ void setPixelFormat(const VideoFormat::PixelFormat format); /*! * \brief pixelFormat * \return user requested format. may be different with actually used format */ VideoFormat::PixelFormat pixelFormat() const; // TODO: supportedPixelFormats() const; Q_SIGNALS: void widthChanged(); void heightChanged(); void frameRateChanged(); void pixelFormatChanged(); public: template static bool Register(VideoEncoderId id, const char* name) { return Register(id, create, name);} /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static VideoEncoderId* next(VideoEncoderId* id = 0); static const char* name(VideoEncoderId id); static VideoEncoderId id(const char* name); private: template static VideoEncoder* create() { return new C();} typedef VideoEncoder* (*VideoEncoderCreator)(); static bool Register(VideoEncoderId id, VideoEncoderCreator, const char *name); protected: VideoEncoder(VideoEncoderPrivate& d); private: VideoEncoder(); }; } //namespace QtAV #endif // QTAV_VIDEOENCODER_H QtAV-1.12.0/src/QtAV/VideoFormat.h000066400000000000000000000226741312235004300164040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOFORMAT_H #define QTAV_VIDEOFORMAT_H #include #include #include #include QT_BEGIN_NAMESPACE class QDebug; QT_END_NAMESPACE namespace QtAV { class VideoFormatPrivate; /*! * \brief The VideoFormat class * Describes the layout of video data. Some properties like display aspect ratio, color space and color range, which describes how to display the video frame, should be in VideoFrame class. + */ class Q_AV_EXPORT VideoFormat { public: /*! * \brief The PixelFormat enum * 32 bit rgba format enum name indicates it's channel layout. For example, * Format_ARGB32 byte layout is AARRGGBB, it's integer value is 0xAARRGGBB on big endian platforms * and 0xBBGGRRAA on little endian platforms * Format_RGB32 and QImage::Format_ARGB32 are the same. * TODO: 0RGB, XRGB, not native endia use R8 or R16. ffmpeg does not have native endian format * currently 0rgb xrgb use rgba formats and check hasAlpha() is required */ enum PixelFormat { Format_Invalid = -1, Format_ARGB32, // AARRGGBB or 00RRGGBB, check hasAlpha is required Format_BGRA32, //BBGGRRAA Format_ABGR32, // QImage.RGBA8888 le Format_RGBA32, // QImage. no Format_RGB32, // 0xAARRGGBB native endian. same as QImage::Format_ARGB32. be: ARGB32, le: BGRA32 Format_BGR32, // 0xAABBGGRR native endian Format_RGB24, Format_BGR24, Format_RGB565, Format_BGR565, Format_RGB555, Format_BGR555, //http://www.fourcc.org/yuv.php Format_AYUV444, Format_YUV444P, Format_YUV422P, Format_YUV420P, Format_YUV411P, Format_YUV410P, Format_YV12, Format_UYVY, //422 Format_VYUY, //not in ffmpeg. OMX_COLOR_FormatCrYCbY Format_YUYV, //422, aka yuy2 Format_YVYU, //422 Format_NV12, Format_NV21, Format_IMC1, Format_IMC2, Format_IMC3, //same as IMC1, swap U V Format_IMC4, //same as IMC2, swap U V Format_Y8, //GREY. single 8 bit Y plane Format_Y16, //single 16 bit Y plane. LE Format_Jpeg, //yuvj //Format_CameraRaw, //Format_AdobeDng, Format_YUV420P9LE, Format_YUV422P9LE, Format_YUV444P9LE, Format_YUV420P10LE, Format_YUV422P10LE, Format_YUV444P10LE, Format_YUV420P12LE, Format_YUV422P12LE, Format_YUV444P12LE, Format_YUV420P14LE, Format_YUV422P14LE, Format_YUV444P14LE, Format_YUV420P16LE, Format_YUV422P16LE, Format_YUV444P16LE, Format_YUV420P9BE, Format_YUV422P9BE, Format_YUV444P9BE, Format_YUV420P10BE, Format_YUV422P10BE, Format_YUV444P10BE, Format_YUV420P12BE, Format_YUV422P12BE, Format_YUV444P12BE, Format_YUV420P14BE, Format_YUV422P14BE, Format_YUV444P14BE, Format_YUV420P16BE, Format_YUV422P16BE, Format_YUV444P16BE, Format_RGB48, // native endian Format_RGB48LE, Format_RGB48BE, Format_BGR48, Format_BGR48LE, Format_BGR48BE, Format_RGBA64, //native endian Format_RGBA64LE, Format_RGBA64BE, Format_BGRA64, //native endian Format_BGRA64LE, Format_BGRA64BE, Format_VYU, // for rgb422_apple texture, the layout is like rgb24: (v, y, u, ) Format_XYZ12, Format_XYZ12LE, Format_XYZ12BE, Format_User }; static PixelFormat pixelFormatFromImageFormat(QImage::Format format); /*! * \brief imageFormatFromPixelFormat * If returns a negative value, the QImage format is the positive one but R/G components are swapped because no direct support by QImage. QImage can swap R/G very fast. */ static QImage::Format imageFormatFromPixelFormat(PixelFormat format); static PixelFormat pixelFormatFromFFmpeg(int ff); //AVPixelFormat static int pixelFormatToFFmpeg(PixelFormat fmt); static QVector pixelFormatsFFmpeg(); VideoFormat(PixelFormat format = Format_Invalid); VideoFormat(int formatFF); VideoFormat(QImage::Format fmt); VideoFormat(const QString& name); VideoFormat(const VideoFormat &other); ~VideoFormat(); VideoFormat& operator=(const VideoFormat &other); VideoFormat& operator=(VideoFormat::PixelFormat pixfmt); VideoFormat& operator=(QImage::Format qpixfmt); VideoFormat& operator=(int ffpixfmt); bool operator==(const VideoFormat &other) const; bool operator==(VideoFormat::PixelFormat pixfmt) const; bool operator==(QImage::Format qpixfmt) const; bool operator==(int ffpixfmt) const; bool operator!=(const VideoFormat &other) const; bool operator!=(VideoFormat::PixelFormat pixfmt) const; bool operator!=(QImage::Format qpixfmt) const; bool operator!=(int ffpixfmt) const; bool isValid() const; PixelFormat pixelFormat() const; int pixelFormatFFmpeg() const; QImage::Format imageFormat() const; QString name() const; /*! * \brief setPixelFormat set pixel format to format. other information like bpp will be updated * \param format */ void setPixelFormat(PixelFormat format); void setPixelFormatFFmpeg(int format); /*! * \brief channels * \return number of channels(components) the the format. e.g. RGBA has 4 channels, NV12 is 3 */ int channels() const; /*! * \brief channels * \param plane * \return number of channels in a plane */ int channels(int plane) const; /*! * \brief planeCount * \return -1 if not a valid format */ int planeCount() const; /*! * https://wiki.videolan.org/YUV * bytesPerPixel() * YUV420P: 1pix = 4Y+U+V, (4*8+8+8)/4 = 12 * bytesPerPixel(plane) is different, for example * uyvy422 bytesPerPixel(0) = 8+8+8 = 24, while bytesPerPixel() = (2*8+8+8)/2 = 16 */ int bitsPerPixel() const; /// nv12: 16 for uv plane int bitsPerPixel(int plane) const; /// bgr24 is 24 not 32 int bitsPerPixelPadded() const; int bytesPerPixel() const; int bytesPerPixel(int plane) const; /*! * \brief bitsPerComponent * \return number of bits per component (0 if uneven) */ int bitsPerComponent() const; // return line size with given width int bytesPerLine(int width, int plane) const; /*! * \brief chromaWidth * \param lumaWidth * \return U, V component (or channel) width for the given luma width. */ int chromaWidth(int lumaWidth) const; int chromaHeight(int lumaHeight) const; /*! * \brief width * plane width for given lumaWidth in current format * \return lumaWidth if plane <= 0. otherwise chromaWidth */ int width(int lumaWidth, int plane) const; int height(int lumaHeight, int plane) const; /*! * \brief normalizedWidth * \return 1.0 for plane <= 0. otherwise chroma width */ qreal normalizedWidth(int plane) const; qreal normalizedHeight(int plane) const; //TODO: add planeWidth()/planeHeight() // test AV_PIX_FMT_FLAG_XXX bool isBigEndian() const; bool hasPalette() const; bool isPseudoPaletted() const; /** * All values of a component are bit-wise packed end to end. */ bool isBitStream() const; /** * Pixel format is an HW accelerated format. */ bool isHWAccelerated() const; /*! * \brief isPlanar * \return true if is planar or semi planar * * Semi-planar: 2 planes instead of 3, one plane for luminance, and one plane for both chrominance components. * They are also sometimes referred to as biplanar formats also * Packed: 1 plane * Planar: 1 plane for each component (channel) */ bool isPlanar() const; bool isRGB() const; bool isXYZ() const; bool hasAlpha() const; static bool isPlanar(PixelFormat pixfmt); static bool isRGB(PixelFormat pixfmt); static bool hasAlpha(PixelFormat pixfmt); private: QSharedDataPointer d; }; #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug debug, const VideoFormat &fmt); Q_AV_EXPORT QDebug operator<<(QDebug debug, VideoFormat::PixelFormat pixFmt); #endif } //namespace QtAV Q_DECLARE_METATYPE(QtAV::VideoFormat) Q_DECLARE_METATYPE(QtAV::VideoFormat::PixelFormat) #endif // QTAV_VIDEOFORMAT_H QtAV-1.12.0/src/QtAV/VideoFrame.h000066400000000000000000000152521312235004300162000ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOFRAME_H #define QTAV_VIDEOFRAME_H #include #include #include /// TODO: fromAVFrame(const AVFrame* f); namespace QtAV { /// metadata: pallete for pal8 class VideoFramePrivate; class Q_AV_EXPORT VideoFrame : public Frame { Q_DECLARE_PRIVATE(VideoFrame) public: /*! * \brief fromGPU * Make a VideoFrame with data on host memory from GPU resource * \param fmt video format of GPU resource * \param width frame width * \param height frame height * \param surface_h surface height. Can be greater than visual frame height because of alignment * \param src CPU accessible address of frame planes on GPU. src[0] must be valid. src[i>0] will be filled depending on pixel format, pitch and surface_h if it's NULL. * \param pitch plane pitch on GPU. pitch[0] must be valid. pitch[i>0] will be filled depending on pixel format, pitch[0] and surface_h if it's NULL. * \param optimized try to use SIMD to copy from GPU. otherwise use memcpy * \param swapUV it's required if u/v src are null */ static VideoFrame fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized = true, bool swapUV = false); static void copyPlane(quint8 *dst, size_t dst_stride, const quint8 *src, size_t src_stride, unsigned byteWidth, unsigned height); VideoFrame(); //must set planes and linesize manually if data is empty // must set planes and linesize manually VideoFrame(int width, int height, const VideoFormat& format, const QByteArray& data = QByteArray()); VideoFrame(const QImage& image); VideoFrame(const VideoFrame &other); ~VideoFrame(); VideoFrame &operator =(const VideoFrame &other); int channelCount() const Q_DECL_OVERRIDE; /*! * Deep copy. Given the format, width and height, plane addresses and line sizes. */ VideoFrame clone() const; VideoFormat format() const; VideoFormat::PixelFormat pixelFormat() const; QImage::Format imageFormat() const; int pixelFormatFFmpeg() const; bool isValid() const; operator bool() const { return isValid();} QSize size() const; //int width(int plane = 0) const? int width() const; int height() const; /*! * \brief effectiveBytesPerLine * The plane bytes contains valid image data without padded data for alignment reason */ int effectiveBytesPerLine(int plane) const; // plane width with padded bytes for alignment. int planeWidth(int plane) const; int planeHeight(int plane) const; // display attributes float displayAspectRatio() const; void setDisplayAspectRatio(float displayAspectRatio); // TODO: pixel aspect ratio ColorSpace colorSpace() const; void setColorSpace(ColorSpace value); ColorRange colorRange() const; void setColorRange(ColorRange value); /*! * \brief toImage * Return a QImage of current video frame, with given format, image size and region of interest. * If VideoFrame is constructed from an QImage, the target format, size and roi are the same, then no data copy. * \param dstSize result image size * \param roi NOT implemented! */ QImage toImage(QImage::Format fmt = QImage::Format_ARGB32, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; /*! * \brief to * The result frame data is always on host memory. If video frame data is already in host memory, and the target parameters are the same, then return the current frame. * \param pixfmt target pixel format * \param dstSize target frame size * \param roi interested region of source frame */ VideoFrame to(VideoFormat::PixelFormat pixfmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; VideoFrame to(const VideoFormat& fmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; bool to(VideoFormat::PixelFormat pixfmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; bool to(const VideoFormat& fmt, quint8 *const dst[], const int dstStride[], const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; /*! * map a gpu frame to opengl texture or d3d texture or other handle. * handle: given handle. can be gl texture (& GLuint), d3d texture, or 0 if create a new handle * return the result handle or 0 if not supported */ void* map(SurfaceType type, void* handle, int plane = 0); void* map(SurfaceType type, void* handle, const VideoFormat& fmt, int plane = 0); void unmap(void* handle); /*! * \brief createInteropHandle * \param handle input/output handle * \return null on error. otherwise return the input handle */ void* createInteropHandle(void* handle, SurfaceType type, int plane); }; class ImageConverter; class Q_AV_EXPORT VideoFrameConverter { public: VideoFrameConverter(); ~VideoFrameConverter(); /// value out of [-100, 100] will be ignored void setEq(int brightness, int contrast, int saturation); /*! * \brief convert * return a frame with a given format from a given source frame. The result frame data is always on host memory. */ VideoFrame convert(const VideoFrame& frame, const VideoFormat& fmt) const; VideoFrame convert(const VideoFrame& frame, VideoFormat::PixelFormat fmt) const; VideoFrame convert(const VideoFrame& frame, QImage::Format fmt) const; VideoFrame convert(const VideoFrame& frame, int fffmt) const; private: mutable ImageConverter *m_cvt; int m_eq[3]; }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::VideoFrame) #endif // QTAV_VIDEOFRAME_H QtAV-1.12.0/src/QtAV/VideoFrameExtractor.h000066400000000000000000000075171312235004300201010ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOFRAMEEXTRACTOR_H #define QTAV_VIDEOFRAMEEXTRACTOR_H #include #include //TODO: extract all streams namespace QtAV { class VideoFrameExtractorPrivate; class Q_AV_EXPORT VideoFrameExtractor : public QObject { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoFrameExtractor) Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(bool autoExtract READ autoExtract WRITE setAutoExtract NOTIFY autoExtractChanged) Q_PROPERTY(bool async READ async WRITE setAsync NOTIFY asyncChanged) Q_PROPERTY(int precision READ precision WRITE setPrecision NOTIFY precisionChanged) Q_PROPERTY(qint64 position READ position WRITE setPosition NOTIFY positionChanged) public: explicit VideoFrameExtractor(QObject *parent = 0); /*! * \brief setSource * Set the video file. If video changes, current loaded video will be unloaded. */ void setSource(const QString url); QString source() const; /*! * \brief setAsync * Extract video frames in another thread. Default is true. * In async mode, if current extraction is not finished, new * setPosition() will be ignored. */ void setAsync(bool value); bool async() const; void setAutoExtract(bool value); bool autoExtract() const; /*! * \brief setPrecision * if the difference between the next requested position is less than the value, previous * one is used and no positionChanged() and frameExtracted() signals to emit. * \param value < 0: auto. Real value depends on video duration and fps, but always 20 <= value <=500 * Default is auto. */ void setPrecision(int value); int precision() const; void setPosition(qint64 value); qint64 position() const; virtual bool event(QEvent *e); Q_SIGNALS: void frameExtracted(const QtAV::VideoFrame& frame); // parameter: VideoFrame, bool changed? void sourceChanged(); void asyncChanged(); void error(); // clear preview image in a slot void autoExtractChanged(); /*! * \brief positionChanged * If not autoExtract, positionChanged() => extract() in a slot */ void positionChanged(); void precisionChanged(); void aboutToExtract(qint64 pos); public Q_SLOTS: /*! * \brief extract * If last extracted frame can be use, use it. * If there is a key frame in [position, position+precision], the nearest key frame * before position+precision will be extracted. Otherwise, the given position frame will be extracted. */ void extract(); private Q_SLOTS: void extractInternal(qint64 pos); protected: //VideoFrameExtractor(VideoFrameExtractorPrivate &d, QObject* parent = 0); DPTR_DECLARE(VideoFrameExtractor) }; } //namespace QtAV #endif // QTAV_VIDEOFRAMEEXTRACTOR_H QtAV-1.12.0/src/QtAV/VideoOutput.h000066400000000000000000000140021312235004300164360ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOOUTPUT_H #define QTAV_VIDEOOUTPUT_H #include #include namespace QtAV { class VideoOutputPrivate; /*! * \brief The VideoOutput class * A VideoRenderer wrapper with QObject features. If create VideoOutput without a given renderer id, QtAV will try to create a widget based renderer, and dynamically load QtAVWidgets module if it's not loaded. */ class Q_AV_EXPORT VideoOutput : public QObject, public VideoRenderer { DPTR_DECLARE_PRIVATE(VideoOutput) Q_OBJECT Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: /*! * \brief VideoOutput * Create a QWidget based renderer. Result can be a QOpenGLWidget or QGLWidget based renderer if possible. Otherwise fallback to a software renderer */ VideoOutput(QObject *parent = 0); /*! * \brief VideoOutput * Create a renderer with given rendererId. MUST check VideoOutput::isAvailable() later! */ VideoOutput(VideoRendererId rendererId, QObject *parent = 0); ~VideoOutput(); VideoRendererId id() const Q_DECL_OVERRIDE; VideoFormat::PixelFormat preferredPixelFormat() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; QWindow* qwindow() Q_DECL_OVERRIDE Q_DECL_FINAL; QWidget* widget() Q_DECL_OVERRIDE Q_DECL_FINAL; QGraphicsItem* graphicsItem() Q_DECL_OVERRIDE Q_DECL_FINAL; OpenGLVideo* opengl() const Q_DECL_OVERRIDE; Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; protected: bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; void drawBackground() Q_DECL_OVERRIDE; void drawFrame() Q_DECL_OVERRIDE; void handlePaintEvent() Q_DECL_OVERRIDE; private: virtual bool onSetPreferredPixelFormat(VideoFormat::PixelFormat pixfmt) Q_DECL_OVERRIDE; virtual bool onForcePreferredPixelFormat(bool force = true) Q_DECL_OVERRIDE; virtual void onSetOutAspectRatioMode(OutAspectRatioMode mode) Q_DECL_OVERRIDE; virtual void onSetOutAspectRatio(qreal ratio) Q_DECL_OVERRIDE; virtual bool onSetQuality(Quality q) Q_DECL_OVERRIDE; virtual bool onSetOrientation(int value) Q_DECL_OVERRIDE; virtual void onResizeRenderer(int width, int height) Q_DECL_OVERRIDE; virtual bool onSetRegionOfInterest(const QRectF& roi) Q_DECL_OVERRIDE; virtual QPointF onMapToFrame(const QPointF& p) const Q_DECL_OVERRIDE; virtual QPointF onMapFromFrame(const QPointF& p) const Q_DECL_OVERRIDE; virtual bool onSetBrightness(qreal brightness) Q_DECL_OVERRIDE; virtual bool onSetContrast(qreal contrast) Q_DECL_OVERRIDE; virtual bool onSetHue(qreal hue) Q_DECL_OVERRIDE; virtual bool onSetSaturation(qreal saturation) Q_DECL_OVERRIDE; virtual void onSetBackgroundColor(const QColor& color) Q_DECL_OVERRIDE; // from AVOutput virtual void setStatistics(Statistics* statistics) Q_DECL_OVERRIDE; //called by friend AVPlayer virtual bool onInstallFilter(Filter *filter, int index) Q_DECL_OVERRIDE; virtual bool onUninstallFilter(Filter *filter) Q_DECL_OVERRIDE; virtual bool onHanlePendingTasks() Q_DECL_OVERRIDE; }; } //namespace QtAV #endif // QTAV_VIDEOOUTPUT_H QtAV-1.12.0/src/QtAV/VideoRenderer.h000066400000000000000000000260031312235004300167100ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_VIDEORENDERER_H #define QAV_VIDEORENDERER_H #include #include #include #include #include #include /*! * A bridge for VideoOutput(QObject based) and video renderer backend classes * Every public setter call it's virtual onSetXXX(...) which has default behavior. * While VideoOutput.onSetXXX(...) simply calls backend's setXXX(...) and return whether the result is desired. */ QT_BEGIN_NAMESPACE class QWidget; class QWindow; class QGraphicsItem; QT_END_NAMESPACE namespace QtAV { typedef int VideoRendererId; extern Q_AV_EXPORT VideoRendererId VideoRendererId_OpenGLWindow; class Filter; class OpenGLVideo; class VideoFormat; class VideoRendererPrivate; class Q_AV_EXPORT VideoRenderer : public AVOutput { DPTR_DECLARE_PRIVATE(VideoRenderer) public: //TODO: original video size mode // fillmode: keepsize enum OutAspectRatioMode { RendererAspectRatio //Use renderer's aspect ratio, i.e. stretch to fit the renderer rect , VideoAspectRatio //Use video's aspect ratio and align center in renderer. , CustomAspectRation //Use the ratio set by setOutAspectRatio(qreal). Mode will be set to this if that function is called //, AspectRatio4_3, AspectRatio16_9 }; enum Quality { //TODO: deprecated. simpily use int 0~100 QualityDefault, //good QualityBest, QualityFastest }; template static bool Register(VideoRendererId id, const char* name) { return Register(id, create, name);} static VideoRenderer* create(VideoRendererId id); static VideoRenderer* create(const char* name); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static VideoRendererId* next(VideoRendererId* id = 0); static const char* name(VideoRendererId id); static VideoRendererId id(const char* name); VideoRenderer(); virtual ~VideoRenderer(); virtual VideoRendererId id() const = 0; bool receive(const VideoFrame& frame); /*! * \brief setPreferredPixelFormat * \param pixfmt * pixfmt will be used if decoded format is not supported by this renderer. otherwise, use decoded format. * return false if \a pixfmt is not supported and not changed. */ bool setPreferredPixelFormat(VideoFormat::PixelFormat pixfmt); /*! * \brief preferredPixelFormat * \return preferred pixel format. e.g. WidgetRenderer is rgb formats. */ virtual VideoFormat::PixelFormat preferredPixelFormat() const; //virtual? /*! * \brief forcePreferredPixelFormat * force to use preferredPixelFormat() even if incoming format is supported * \param force */ void forcePreferredPixelFormat(bool force = true); bool isPreferredPixelFormatForced() const; virtual bool isSupported(VideoFormat::PixelFormat pixfmt) const = 0; /*! * \brief sourceAspectRatio * The display aspect ratio of received video frame. 0 for an invalid frame. * sourceAspectRatioChanged() (a signal for QObject renderers) will be called if the new frame has a different DAR. */ qreal sourceAspectRatio() const; void setOutAspectRatioMode(OutAspectRatioMode mode); OutAspectRatioMode outAspectRatioMode() const; //If setOutAspectRatio(qreal) is used, then OutAspectRatioMode is CustomAspectRation void setOutAspectRatio(qreal ratio); qreal outAspectRatio() const;// void setQuality(Quality q); Quality quality() const; void resizeRenderer(const QSize& size); void resizeRenderer(int width, int height); QSize rendererSize() const; int rendererWidth() const; int rendererHeight() const; //geometry size of current video frame. can not use frameSize because qwidget use it QSize videoFrameSize() const; /*! * \brief orientation * 0, 90, 180, 270. other values are ignored * outAspectRatio() corresponds with orientation == 0. displayed aspect ratio may change if orientation is not 0 */ int orientation() const; void setOrientation(int value); //The video frame rect in renderer you shoud paint to. e.g. in RendererAspectRatio mode, the rect equals to renderer's QRect videoRect() const; /* * region of interest, ROI * invalid rect means the whole source rect * null rect is the whole available source rect. e.g. (0, 0, 0, 0) equals whole source rect * (20, 30, 0, 0) equals (20, 30, sourceWidth - 20, sourceHeight - 30) * if |x|<1, |y|<1, |width|<1, |height|<1 means the ratio of source rect(normalized value) * |width| == 1 or |height| == 1 is a normalized value iff x or y is normalized * call realROI() to get the frame rect actually to be render * TODO: nagtive width or height means invert direction. is nagtive necessary? */ QRectF regionOfInterest() const; // TODO: reset aspect ratio to roi.width/roi/heghit void setRegionOfInterest(qreal x, qreal y, qreal width, qreal height); void setRegionOfInterest(const QRectF& roi); // compute the real ROI QRect realROI() const; // |w| <= 1, |x| < 1 QRectF normalizedROI() const; // TODO: map normalized /*! * \brief mapToFrame * map point in VideoRenderer coordinate to VideoFrame, with current ROI */ QPointF mapToFrame(const QPointF& p) const; /*! * \brief mapFromFrame * map point in VideoFrame coordinate to VideoRenderer, with current ROI */ QPointF mapFromFrame(const QPointF& p) const; // to avoid conflicting width QWidget::window() virtual QWindow* qwindow() { return 0;} /*! * \brief widget * \return default is 0. A QWidget subclass can return \a this */ virtual QWidget* widget() { return 0; } /*! * \brief graphicsItem * \return default is 0. A QGraphicsItem subclass can return \a this */ virtual QGraphicsItem* graphicsItem() { return 0; } /*! * \brief brightness, contrast, hue, saturation * values range between -1.0 and 1.0, the default is 0. * value is not changed if does not implementd and onChangingXXX() returns false. * video widget/item will update after if onChangingXXX/setXXX returns true * \return \a false if failed to set (may be onChangingXXX not implemented or return false) */ qreal brightness() const; bool setBrightness(qreal brightness); qreal contrast() const; bool setContrast(qreal contrast); qreal hue() const; bool setHue(qreal hue); qreal saturation() const; bool setSaturation(qreal saturation); QColor backgroundColor() const; void setBackgroundColor(const QColor& c); /*! * \brief opengl * Currently you can only use it to set custom shader OpenGLVideo.setUserShader() */ virtual OpenGLVideo* opengl() const { return NULL;} protected: VideoRenderer(VideoRendererPrivate &d); //TODO: batch drawBackground(color, region)=>loop drawBackground(color,rect) virtual bool receiveFrame(const VideoFrame& frame) = 0; QRegion backgroundRegion() const; virtual void drawBackground(); //draw the current frame using the current paint engine. called by paintEvent() // TODO: parameter VideoFrame virtual void drawFrame() = 0; //You MUST reimplement this to display a frame. Other draw functions are not essential virtual void handlePaintEvent(); //has default. User don't have to implement it virtual void updateUi(); // by default post an UpdateRequest event for window and UpdateLater event for widget to ensure ui update private: // property change. used as signals in subclasses. implemented by moc virtual void sourceAspectRatioChanged(qreal) {} virtual void outAspectRatioChanged() {} virtual void outAspectRatioModeChanged() {} virtual void orientationChanged() {} virtual void videoRectChanged() {} virtual void contentRectChanged() {} virtual void regionOfInterestChanged() {} virtual void videoFrameSizeChanged() {} virtual void rendererSizeChanged() {} virtual void brightnessChanged(qreal) {} virtual void contrastChanged(qreal) {} virtual void hueChanged(qreal) {} virtual void saturationChanged(qreal) {} virtual void backgroundColorChanged() {} private: // mainly used by VideoOutput class /*! * return false if value not changed. default is true */ virtual bool onSetPreferredPixelFormat(VideoFormat::PixelFormat pixfmt); virtual bool onForcePreferredPixelFormat(bool force = true); virtual void onSetOutAspectRatioMode(OutAspectRatioMode mode); virtual void onSetOutAspectRatio(qreal ratio); virtual bool onSetQuality(Quality q); virtual void onResizeRenderer(int width, int height); virtual bool onSetOrientation(int value); virtual bool onSetRegionOfInterest(const QRectF& roi); virtual QPointF onMapToFrame(const QPointF& p) const; virtual QPointF onMapFromFrame(const QPointF& p) const; /*! * \brief onSetXX * It's called when user call setXXX() with a new value. You should implement how to actually change the value, e.g. change brightness with shader. * \return * false: It's default. means not implemented. \a brightness() does not change. * true: Implement this and return true. \a brightness() will change to new value */ virtual bool onSetBrightness(qreal brightness); virtual bool onSetContrast(qreal contrast); virtual bool onSetHue(qreal hue); virtual bool onSetSaturation(qreal saturation); virtual void onSetBackgroundColor(const QColor& color); private: template static VideoRenderer* create() { return new C(); } typedef VideoRenderer* (*VideoRendererCreator)(); static bool Register(VideoRendererId id, VideoRendererCreator, const char *name); friend class VideoOutput; //the size of decoded frame. get called in receiveFrame(). internal use only void setInSize(const QSize& s); void setInSize(int width, int height); }; } //namespace QtAV #endif // QAV_VIDEORENDERER_H QtAV-1.12.0/src/QtAV/VideoShader.h000066400000000000000000000236451312235004300163610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOSHADER_H #define QTAV_VIDEOSHADER_H #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #else #include #include #undef QOpenGLShaderProgram #undef QOpenGLShader #define QOpenGLShaderProgram QGLShaderProgram #define QOpenGLShader QGLShader #endif QT_BEGIN_NAMESPACE class QOpenGLShaderProgram; QT_END_NAMESPACE namespace QtAV { class VideoMaterial; class VideoShaderPrivate; /*! * \brief The VideoShader class * Represents a shader for rendering a video frame. * Low-level api. Used by OpenGLVideo and Scene Graph. * You can also create your own shader. Usually only sampling function and rgb post processing are enough. * Transforming color to rgb is done internally. * TODO: vertex shader (fully controlled by user?). Mesh */ class Q_AV_EXPORT VideoShader { DPTR_DECLARE_PRIVATE(VideoShader) public: VideoShader(); virtual ~VideoShader(); /*! * \brief attributeNames * Array must end with null. { position, texcoord, ..., 0}, location is bound to 0, 1, ... * \return */ virtual char const *const *attributeNames() const; /*! * \brief vertexShader * mvp uniform: u_Matrix * Vertex shader in: a_Position, a_TexCoordsN (see attributeNames()) * Vertex shader out: v_TexCoordsN */ virtual const char *vertexShader() const; virtual const char *fragmentShader() const; /*! * \brief initialize * \param shaderProgram: 0 means create a shader program internally. if not linked, vertex/fragment shader will be added and linked */ virtual void initialize(QOpenGLShaderProgram* shaderProgram = 0); int uniformLocation(const char* name) const; /*! * \brief textureLocationCount * number of texture locations is * 1: packed RGB * number of channels: yuv or plannar RGB */ int textureLocationCount() const; int textureLocation(int index) const; int matrixLocation() const; int colorMatrixLocation() const; int opacityLocation() const; int channelMapLocation() const; int texelSizeLocation() const; int textureSizeLocation() const; VideoFormat videoFormat() const; // defalut is GL_TEXTURE_2D int textureTarget() const; QOpenGLShaderProgram* program(); /*! * \brief update * Upload textures, setup uniforms before rendering. * If material type changed, build a new shader program. */ bool update(VideoMaterial* material); protected: /// rebuild shader program before next rendering. call this if shader code is updated void rebuildLater(); private: /*! * \brief programReady * Called when program is linked and all uniforms are resolved */ virtual void programReady() {} /// User configurable shader APIs BEGIN /*! * Keywords will be replaced in user shader code: * %planes% => plane count * Uniforms can be used: (N: 0 ~ planes-1) * u_Matrix (vertex shader), * u_TextureN, v_TexCoordsN, u_texelSize(array of vec2, normalized), u_textureSize(array of vec2), u_opacity, u_c(channel map), u_colorMatrix, u_to8(vec2, computing 16bit value with 8bit components) * Vertex shader in: a_Position, a_TexCoordsN (see attributeNames()) * Vertex shader out: v_TexCoordsN */ /*! * \brief userShaderHeader * Must add additional uniform declarations here */ virtual const char* userShaderHeader(QOpenGLShader::ShaderType) const {return 0;} /*! * \brief setUserUniformValues * Call program()->setUniformValue(...) here * You can upload a texture for blending in userPostProcess(), * or LUT texture used by userSample() or userPostProcess() etc. * \return false if use use setUserUniformValue(Uniform& u), true if call program()->setUniformValue() here */ virtual bool setUserUniformValues() {return false;} /*! * \brief setUserUniformValue * Update value of uniform u. Call Uniform.set(const T& value, int count); VideoShader will call Uniform.setGL() later if value is changed */ virtual void setUserUniformValue(Uniform&) {} /*! * \brief userSample * Fragment shader only. The custom sampling function to replace texture2D()/texture() (replace %1 in shader). * \code * vec4 sample2d(sampler2D tex, vec2 pos, int plane) { .... } * \endcode * The 3rd parameter can be used to get texel/texture size of a given plane u_texelSize[plane]/textureSize[plane]; * Convolution of result rgb and kernel has the same effect as convolution of input yuv and kernel, ensured by * Σ_i c_i* Σ_j k_j*x_j=Σ_i k_i* Σ_j c_j*x_j * Because because the input yuv is from a real rgb color, so no clamp() is required for the transformed color. */ virtual const char* userSample() const { return 0;} /*! * \brief userPostProcess * Fragment shader only. Process rgb color * TODO: parameter ShaderType? */ virtual const char* userPostProcess() const {return 0;} /// User configurable shader APIs END QByteArray shaderSourceFromFile(const QString& fileName) const; bool build(QOpenGLShaderProgram* shaderProgram); void setVideoFormat(const VideoFormat& format); void setTextureTarget(int type); void setMaterialType(qint32 value); friend class VideoMaterial; protected: VideoShader(VideoShaderPrivate &d); DPTR_DECLARE(VideoShader) }; class VideoMaterialPrivate; /*! * \brief The VideoMaterial class * Encapsulates rendering state for a video shader program. * Low-level api. Used by OpenGLVideo and Scene Graph */ class Q_AV_EXPORT VideoMaterial { DPTR_DECLARE_PRIVATE(VideoMaterial) public: VideoMaterial(); virtual ~VideoMaterial() {} void setCurrentFrame(const VideoFrame& frame); VideoFormat currentFormat() const; VideoShader* createShader() const; virtual qint32 type() const; static QString typeName(qint32 value); bool bind(); // TODO: roi void unbind(); int compare(const VideoMaterial* other) const; int textureTarget() const; /*! * \brief isDirty * \return true if material type changed, or other properties changed, e.g. 8bit=>10bit (the same material type) and eq */ bool isDirty() const; /*! * \brief setDirty * Call it after frame is rendered, i.e. after VideoShader::update(VideoMaterial*) */ void setDirty(bool value); const QMatrix4x4 &colorMatrix() const; const QMatrix4x4& channelMap() const; int bitsPerComponent() const; //0 if the value of components are different QVector2D vectorTo8bit() const; int planeCount() const; /*! * \brief validTextureWidth * Value is (0, 1]. Normalized valid width of a plane. * A plane may has padding invalid data at the end for aligment. * Use this value to reduce texture coordinate computation. * --------------------- * | | | * | valid width | | * | | | * ---------------------- * | <- aligned width ->| * \return valid width ratio */ qreal validTextureWidth() const; QSize frameSize() const; /*! * \brief texelSize * The size of texture unit * \return (1.0/textureWidth, 1.0/textureHeight) */ QSizeF texelSize(int plane) const; //vec2? /*! * \brief texelSize * For GLSL. 1 for rectangle texture, 1/(width, height) for 2d texture */ QVector texelSize() const; /*! * \brief textureSize * It can be used with a uniform to emulate GLSL textureSize() which exists in new versions. */ QSize textureSize(int plane) const; /*! * \brief textureSize * For GLSL. Not normalized */ QVector textureSize() const; /*! * \brief normalizedROI * \param roi logical roi of a video frame. * the same as mapToTexture(roi, 1) */ QRectF normalizedROI(const QRectF& roi) const; /*! * \brief mapToTexture * map a point p or a rect r to video texture in a given plane and scaled to valid width. * p or r is in video frame's rect coordinates, no matter which plane is * \param normalize -1: auto(do not normalize for rectangle texture). 0: no. 1: yes * \return * point or rect in current texture valid coordinates. \sa validTextureWidth() */ QPointF mapToTexture(int plane, const QPointF& p, int normalize = -1) const; QRectF mapToTexture(int plane, const QRectF& r, int normalize = -1) const; qreal brightness() const; void setBrightness(qreal value); qreal contrast() const; void setContrast(qreal value); qreal hue() const; void setHue(qreal value); qreal saturation() const; void setSaturation(qreal value); protected: VideoMaterial(VideoMaterialPrivate &d); DPTR_DECLARE(VideoMaterial) }; } //namespace QtAV #endif // QTAV_VIDEOSHADER_H QtAV-1.12.0/src/QtAV/VideoShaderObject.h000066400000000000000000000062171312235004300175040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOSHADEROBJECT_H #define QTAV_VIDEOSHADEROBJECT_H #include #include #include namespace QtAV { // check and auto update properties in shader class VideoShaderObjectPrivate; /*! * \brief The VideoShaderObject class * User defined uniform names are bound to class meta properties (property signals are required) * and object dynamic properties. * Property value type T is limited to float, int, unsigned(ES3.0) and QVector */ class Q_AV_EXPORT VideoShaderObject : public QObject, public VideoShader { DPTR_DECLARE_PRIVATE(VideoShaderObject) Q_OBJECT public: VideoShaderObject(QObject* parent = 0); protected: VideoShaderObject(VideoShaderObjectPrivate &d, QObject* parent = 0); bool event(QEvent *event) Q_DECL_OVERRIDE; private Q_SLOTS: void propertyChanged(int id); private: void programReady() Q_DECL_OVERRIDE Q_DECL_FINAL; }; class DynamicShaderObjectPrivate; /*! * \brief The DynamicShaderObject class * Able to set custom shader code */ class Q_AV_EXPORT DynamicShaderObject : public VideoShaderObject { Q_OBJECT DPTR_DECLARE_PRIVATE(DynamicShaderObject) Q_PROPERTY(QString header READ header WRITE setHeader NOTIFY headerChanged) Q_PROPERTY(QString sample READ sample WRITE setSample NOTIFY sampleChanged) Q_PROPERTY(QString postProcess READ postProcess WRITE setPostProcess NOTIFY postProcessChanged) public: DynamicShaderObject(QObject* parent = 0); QString header() const; void setHeader(const QString& text); QString sample() const; void setSample(const QString& text); QString postProcess() const; void setPostProcess(const QString& text); Q_SIGNALS: void headerChanged(); void sampleChanged(); void postProcessChanged(); protected: DynamicShaderObject(DynamicShaderObjectPrivate &d, QObject* parent = 0); private: const char* userShaderHeader(QOpenGLShader::ShaderType st) const Q_DECL_OVERRIDE; const char* userSample() const Q_DECL_OVERRIDE; const char* userPostProcess() const Q_DECL_OVERRIDE; }; } //namespace QtAV #endif //QTAV_VIDEOSHADEROBJECT_H QtAV-1.12.0/src/QtAV/dptr.h000066400000000000000000000143001312235004300151210ustar00rootroot00000000000000/****************************************************************************** dptr.h: An improved d-pointer interface from Qxt Improved by Wang Bin , 2012 ******************************************************************************/ /**************************************************************************** ** Copyright (c) 2006 - 2011, the LibQxt project. ** See the Qxt AUTHORS file for a list of authors and copyright holders. ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** * Neither the name of the LibQxt project nor the ** names of its contributors may be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** *****************************************************************************/ /**************************************************************************** ** This file is derived from code bearing the following notice: ** The sole author of this file, Adam Higerd, has explicitly disclaimed all ** copyright interest and protection for the content within. This file has ** been placed in the public domain according to United States copyright ** statute and case law. In jurisdictions where this public domain dedication ** is not legally recognized, anyone who receives a copy of this file is ** permitted to use, modify, duplicate, and redistribute this file, in whole ** or in part, with no restrictions or conditions. In these jurisdictions, ** this file shall be copyright (C) 2006-2008 by Adam Higerd. ****************************************************************************/ #ifndef DPTR_H #define DPTR_H /*! requrement: Base class must have default ctor. example: //Base.h class BasePrivate class Base { DPTR_DECLARE_PRIVATE(Base) public: Base(); virtual ~Base(); protected: Base(BasePrivate& d); DPTR_DECLARE(Base) }; //Base.cpp: Base::Base(){} Base::Base(BasePrivate& d):DPTR_INIT(&d){} ... //Base_p.h: class Base; class BasePrivate : public DPtrPrivate { public: int data; }; //Derived.h: class DerivedPrivate; class Derived : public Base { DPTR_DECLARE_PRIVATE(Derived) public: Derived(); virtual ~Derived(); protected: Derived(DerivedPrivate& d); }; //Derived.cpp Derived::Derived():Base(*new DerivedPrivate()){} Derived::Derived(DerivedPrivate& d):Base(d){} //Derived_p.h class DerivedPrivate : public BasePrivate { public: int more_data; }; */ /* * Initialize the dptr when calling Base(BasePrivate& d) ctor. * The derived class using this ctor will reduce memory allocation * p is a DerivedPrivate* */ #define DPTR_INIT(p) dptr(p) //put in protected #define DPTR_DECLARE(Class) DPtrInterface dptr; //put in private #define DPTR_DECLARE_PRIVATE(Class) \ inline Class##Private& d_func() { return dptr.pri(); } \ inline const Class##Private& d_func() const { return dptr.pri(); } \ friend class Class##Private; #define DPTR_DECLARE_PUBLIC(Class) \ inline Class& q_func() { return *static_cast(dptr_ptr()); } \ inline const Class& q_func() const { return *static_cast(dptr_ptr()); } \ friend class Class; #define DPTR_INIT_PRIVATE(Class) dptr.setPublic(this); #define DPTR_D(Class) Class##Private& d = dptr.pri() #define DPTR_P(Class) Class& p = *static_cast(dptr_ptr()) //interface template class DPtrPrivate { public: virtual ~DPtrPrivate() {} inline void DPTR_setPublic(PUB* pub) { dptr_p_ptr = pub; } protected: inline PUB& dptr_p() { return *dptr_p_ptr; } inline const PUB& dptr_p() const { return *dptr_p_ptr; } inline PUB* dptr_ptr() { return dptr_p_ptr; } inline const PUB* dptr_ptr() const { return dptr_p_ptr; } private: PUB* dptr_p_ptr; }; //interface template class DPtrInterface { friend class DPtrPrivate; public: DPtrInterface(PVT* d):pvt(d) {} DPtrInterface():pvt(new PVT()) {} ~DPtrInterface() { if (pvt) { delete pvt; pvt = 0; } } inline void setPublic(PUB* pub) { pvt->DPTR_setPublic(pub); } template inline T& pri() { return *reinterpret_cast(pvt); } template inline const T& pri() const { return *reinterpret_cast(pvt); } //static cast requires defination of T inline PVT& operator()() { return *static_cast(pvt); } inline const PVT& operator()() const { return *static_cast(pvt); } inline PVT * operator->() { return static_cast(pvt); } inline const PVT * operator->() const { return static_cast(pvt); } private: DPtrInterface(const DPtrInterface&); DPtrInterface& operator=(const DPtrInterface&); DPtrPrivate* pvt; }; #endif // DPTR_H QtAV-1.12.0/src/QtAV/private/000077500000000000000000000000001312235004300154535ustar00rootroot00000000000000QtAV-1.12.0/src/QtAV/private/AVCompat.h000066400000000000000000000445331312235004300173070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg solve the version problem and diffirent api in FFmpeg and libav Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_COMPAT_H #define QTAV_COMPAT_H /*! NOTE: include this at last */ #define QTAV_USE_FFMPEG(MODULE) (MODULE##_VERSION_MICRO >= 100) #define QTAV_USE_LIBAV(MODULE) !QTAV_USE_FFMPEG(MODULE) #define FFMPEG_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) \ (QTAV_USE_FFMPEG(MODULE) && MODULE##_VERSION_INT >= AV_VERSION_INT(MAJOR, MINOR, MICRO)) #define LIBAV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) \ (QTAV_USE_LIBAV(MODULE) && MODULE##_VERSION_INT >= AV_VERSION_INT(MAJOR, MINOR, MICRO)) #define AV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO, MINOR2, MICRO2) \ (LIBAV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) || FFMPEG_MODULE_CHECK(MODULE, MAJOR, MINOR2, MICRO2)) /// example: AV_ENSURE(avcodec_close(avctx), false) will print error and return false if failed. AV_WARN just prints error. #define AV_ENSURE_OK(FUNC, ...) AV_RUN_CHECK(FUNC, return, __VA_ARGS__) #define AV_ENSURE(FUNC, ...) AV_RUN_CHECK(FUNC, return, __VA_ARGS__) #define AV_WARN(FUNC) AV_RUN_CHECK(FUNC, void) #include "QtAV/QtAV_Global.h" #ifdef __cplusplus extern "C" { /*UINT64_C: C99 math features, need -D__STDC_CONSTANT_MACROS in CXXFLAGS*/ #endif /*__cplusplus*/ #include #include #include #include #include #include #include #include #include //AV_ROUND_UP, av_rescale_rnd for libav #include #include #include #include #include #include #if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 51, 73, 101) #include #endif /* TODO: how to check whether we have swresample or not? how to check avresample?*/ #include #if QTAV_HAVE(SWRESAMPLE) #include #ifndef LIBSWRESAMPLE_VERSION_INT //ffmpeg 0.9, swr 0.5 #define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, LIBSWRESAMPLE_VERSION_MINOR, LIBSWRESAMPLE_VERSION_MICRO) #endif //LIBSWRESAMPLE_VERSION_INT //ffmpeg >= 0.11.x. swr0.6.100: ffmpeg-0.10.x #define HAVE_SWR_GET_DELAY (LIBSWRESAMPLE_VERSION_INT > AV_VERSION_INT(0, 6, 100)) #endif //QTAV_HAVE(SWRESAMPLE) #if QTAV_HAVE(AVRESAMPLE) #include #endif //QTAV_HAVE(AVRESAMPLE) #if QTAV_HAVE(AVFILTER) #include /*code is here for old version*/ #include #include #include #endif //QTAV_HAVE(AVFILTER) #if QTAV_HAVE(AVDEVICE) #include #endif #ifdef __cplusplus } #endif /*__cplusplus*/ /*! * Guide to uniform the api for different FFmpeg version(or other libraries) * We use the existing old api to simulater . * 1. The old version does not have this api: Just add it. * 2. The old version has similar api: Try using macro. * e.g. the old is bool my_play(char* data, size_t size) * the new is bool my_play2(const ByteArray& data) * change: * #define my_play2(data) my_play(data.data(), data.size()); * * 3. The old version api is conflicted with the latest's. We can redefine the api * e.g. the old is bool my_play(char* data, size_t size) * the new is bool my_play(const ByteArray& data) * change: * typedef bool (*my_play_t)(const ByteArray&); * static my_play_t my_play_ptr = my_play; //using the existing my_play(char*, size_t) * #define my_play my_play_compat * inline bool my_play_compat(const ByteArray& data) * { * return my_play_ptr(data.data(), data.size()); * } * 4. conflict macros * see av_err2str */ #ifndef AV_VERSION_INT #define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c) #endif /*AV_VERSION_INT*/ void ffmpeg_version_print(); #if !FFMPEG_MODULE_CHECK(LIBAVFORMAT, 56, 4, 101) int avio_feof(AVIOContext *s); #endif #if QTAV_USE_LIBAV(LIBAVFORMAT) int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename); #endif //TODO: always inline /* --gnu option of the RVCT compiler also defines __GNUC__ */ #if defined(__GNUC__) && !(defined(__ARMCC__) || defined(__CC_ARM)) #define GCC_VERSION_AT_LEAST(major, minor, patch) \ (__GNUC__ > major || (__GNUC__ == major && (__GNUC_MINOR__ > minor \ || (__GNUC_MINOR__ == minor && __GNUC_PATCHLEVEL__ >= patch)))) #else /* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ #define GCC_VERSION_AT_LEAST(major, minor, patch) 0 #endif //FFmpeg2.0, Libav10 2013-03-08 - Reference counted buffers - lavu 52.19.100/52.8.0, lavc 55.0.100 / 55.0.0, lavf 55.0.100 / 55.0.0, lavd 54.4.100 / 54.0.0, lavfi 3.5.0 #define QTAV_HAVE_AVBUFREF AV_MODULE_CHECK(LIBAVUTIL, 52, 8, 0, 19, 100) #if defined(_MSC_VER) || !defined(av_err2str) || (GCC_VERSION_AT_LEAST(4, 7, 0) && __cplusplus) #ifdef av_err2str #undef av_err2str /*#define av_make_error_string qtav_make_error_string*/ #else /** * Fill the provided buffer with a string containing an error string * corresponding to the AVERROR code errnum. * * @param errbuf a buffer * @param errbuf_size size in bytes of errbuf * @param errnum error code to describe * @return the buffer in input, filled with the error description * @see av_strerror() */ static av_always_inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum) { av_strerror(errnum, errbuf, errbuf_size); return errbuf; } #endif /*av_err2str*/ #define AV_ERROR_MAX_STRING_SIZE 64 #ifdef QT_CORE_LIB #include #define av_err2str(e) av_err2str_qsp(e).data() av_always_inline QSharedPointer av_err2str_qsp(int errnum) { QSharedPointer str((char*)calloc(AV_ERROR_MAX_STRING_SIZE, 1), ::free); av_strerror(errnum, str.data(), AV_ERROR_MAX_STRING_SIZE); return str; } #else av_always_inline char* av_err2str(int errnum) { static char str[AV_ERROR_MAX_STRING_SIZE]; memset(str, 0, sizeof(str)); return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum); } #endif /* QT_CORE_LIB */ #endif /*!defined(av_err2str) || GCC_VERSION_AT_LEAST(4, 7, 2)*/ #if (LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(52,23,0)) #define avcodec_decode_audio3(avctx, samples, frame_size_ptr, avpkt) \ avcodec_decode_audio2(avctx, samples, frame_size_ptr, (*avpkt).data, (*avpkt).size); #endif /*AV_VERSION_INT(52,23,0)*/ #if (LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(52,101,0)) #define av_dump_format(...) dump_format(__VA_ARGS__) #endif /*AV_VERSION_INT(52,101,0)*/ #if QTAV_HAVE(SWRESAMPLE) && (LIBSWRESAMPLE_VERSION_INT <= AV_VERSION_INT(0, 5, 0)) #define swresample_version() LIBSWRESAMPLE_VERSION_INT //we can not know the runtime version, so just use build time version #define swresample_configuration() "Not available." #define swresample_license() "Not available." #endif #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0) int64_t av_get_default_channel_layout(int nb_channels); #endif /* * mapping avresample to swresample * https://github.com/xbmc/xbmc/commit/274679d */ #if (QTAV_HAVE(SWR_AVR_MAP) || !QTAV_HAVE(SWRESAMPLE)) && QTAV_HAVE(AVRESAMPLE) #ifndef SWR_CH_MAX #ifdef AVRESAMPLE_MAX_CHANNELS #define SWR_CH_MAX AVRESAMPLE_MAX_CHANNELS #else #define SWR_CH_MAX 64 #endif //AVRESAMPLE_MAX_CHANNELS #endif //SWR_CH_MAX #define SwrContext AVAudioResampleContext #define swr_init(ctx) avresample_open(ctx) //free context and set pointer to null. see swresample #define swr_free(ctx) \ if (ctx && *ctx) { \ avresample_close(*ctx); \ *ctx = 0; \ } #define swr_get_class() avresample_get_class() #define swr_alloc() avresample_alloc_context() //#define swr_next_pts() #define swr_set_compensation() avresample_set_compensation() #define swr_set_channel_mapping(ctx, map) avresample_set_channel_mapping(ctx, map) #define swr_set_matrix(ctx, matrix, stride) avresample_set_matrix(ctx, matrix, stride) //#define swr_drop_output(ctx, count) //#define swr_inject_silence(ctx, count) #define swr_get_delay(ctx, ...) avresample_get_delay(ctx) #if LIBAVRESAMPLE_VERSION_INT >= AV_VERSION_INT(1, 0, 0) //ffmpeg >= 1.1 #define swr_convert(ctx, out, out_count, in, in_count) \ avresample_convert(ctx, out, 0, out_count, const_cast(in), 0, in_count) #else #define swr_convert(ctx, out, out_count, in, in_count) \ avresample_convert(ctx, (void**)out, 0, out_count, (void**)in, 0, in_count) #define HAVE_SWR_GET_DELAY 1 #define swr_get_delay(ctx, ...) avresample_get_delay(ctx) #endif struct SwrContext *swr_alloc_set_opts(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, int log_offset, void *log_ctx); #define swresample_version() avresample_version() #define swresample_configuration() avresample_configuration() #define swresample_license() avresample_license() #endif //MAP_SWR_AVR /* For FFmpeg < 2.0 * FF_API_PIX_FMT macro? * 51.42.0: PIX_FMT_* -> AV_PIX_FMT_*, PixelFormat -> AVPixelFormat * so I introduce QTAV_PIX_FMT_C(X) for internal use * FFmpeg n1.1 AVPixelFormat */ #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 13, 100) //(51, 42, 0) typedef enum PixelFormat AVPixelFormat; // so we must avoid using enum AVPixelFormat #define QTAV_PIX_FMT_C(X) PIX_FMT_##X #else //FFmpeg >= 2.0 typedef enum AVPixelFormat AVPixelFormat; #define QTAV_PIX_FMT_C(X) AV_PIX_FMT_##X #endif //AV_VERSION_INT(51, 42, 0) // FF_API_PIX_FMT #ifdef PixelFormat #undef PixelFormat #endif // AV_PIX_FMT_FLAG_XXX was PIX_FMT_XXX before FFmpeg 2.0 // AV_PIX_FMT_FLAG_ALPHA was added at 52.2.0. but version.h not changed #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 2, 1) //git cbe5a60c9d495df0fb4775b064f06719b70b9952 #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 22, 1) //git 38d553322891c8e47182f05199d19888422167dc #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 19, 0) //git 6b0768e2021b90215a2ab55ed427bce91d148148 #define PIX_FMT_PLANAR 16 ///< At least one pixel component is not in the first data plane #define PIX_FMT_RGB 32 ///< The pixel format contains RGB-like data (as opposed to YUV/grayscale) #endif //AV_VERSION_INT(51, 19, 0) #define PIX_FMT_PSEUDOPAL 64 //why not defined in FFmpeg 0.9 lavu51.32.0 but git log says 51.22.1 defined it? #endif //AV_VERSION_INT(51, 22, 1) #define PIX_FMT_ALPHA 128 ///< The pixel format has an alpha channel #endif //AV_VERSION_INT(52, 2, 1) #ifndef PIX_FMT_PLANAR #define PIX_FMT_PLANAR 16 #endif //PIX_FMT_PLANAR #ifndef PIX_FMT_RGB #define PIX_FMT_RGB 32 #endif //PIX_FMT_RGB #ifndef PIX_FMT_PSEUDOPAL #define PIX_FMT_PSEUDOPAL 64 #endif //PIX_FMT_PSEUDOPAL #ifndef PIX_FMT_ALPHA #define PIX_FMT_ALPHA 128 #endif //PIX_FMT_ALPHA /* * rename PIX_FMT_* flags to AV_PIX_FMT_FLAG_*. git e6c4ac7b5f038be56dfbb0171f5dd0cb850d9b28 */ //#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 11, 0) #ifndef AV_PIX_FMT_FLAG_BE #define AV_PIX_FMT_FLAG_BE PIX_FMT_BE #define AV_PIX_FMT_FLAG_PAL PIX_FMT_PAL #define AV_PIX_FMT_FLAG_BITSTREAM PIX_FMT_BITSTREAM #define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL // FFmpeg >= 0.9, libav >= 0.8.8(51,22,1) #define AV_PIX_FMT_FLAG_PLANAR PIX_FMT_PLANAR #define AV_PIX_FMT_FLAG_RGB PIX_FMT_RGB // FFmpeg >= 1.0, libav >= 9.7 #define AV_PIX_FMT_FLAG_PSEUDOPAL PIX_FMT_PSEUDOPAL // FFmpeg >= 1.1, libav >= 9.7 #define AV_PIX_FMT_FLAG_ALPHA PIX_FMT_ALPHA #endif //AV_PIX_FMT_FLAG_BE //#endif //AV_VERSION_INT(52, 11, 0) // FFmpeg >= 1.1, but use internal av_pix_fmt_descriptors. FFmpeg < 1.1 has extern av_pix_fmt_descriptors // used by av_pix_fmt_count_planes #if !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100) const AVPixFmtDescriptor *av_pix_fmt_desc_get(AVPixelFormat pix_fmt); const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev); AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc); #endif // !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100) #if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 48, 101) // since ffmpeg2.1, libavutil53.16.0 (FF_API_AVFRAME_COLORSPACE), git 8c02adc enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame); enum AVColorRange av_frame_get_color_range(const AVFrame *frame); #endif /* * lavu 52.9.0 git 2c328a907978b61949fd20f7c991803174337855 * FFmpeg >= 2.0. */ #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100) int av_pix_fmt_count_planes(AVPixelFormat pix_fmt); #endif //AV_VERSION_INT(52, 38, 100) // FFmpeg < 1.0 has no av_samples_copy #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 73, 101) /** * Copy samples from src to dst. * * @param dst destination array of pointers to data planes * @param src source array of pointers to data planes * @param dst_offset offset in samples at which the data will be written to dst * @param src_offset offset in samples at which the data will be read from src * @param nb_samples number of samples to be copied * @param nb_channels number of audio channels * @param sample_fmt audio sample format */ int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset, int src_offset, int nb_samples, int nb_channels, enum AVSampleFormat sample_fmt); #endif //AV_VERSION_INT(51, 73, 101) // < ffmpeg 1.0 //#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100) #if AV_MODULE_CHECK(LIBAVCODEC, 54, 25, 0, 51, 100) #define QTAV_CODEC_ID(X) AV_CODEC_ID_##X #else typedef enum CodecID AVCodecID; #define QTAV_CODEC_ID(X) CODEC_ID_##X #endif /* av_frame_alloc * since FFmpeg2.0: 2.0.4 avcodec-55.18.102, avutil-52.38.100 (1.2.7 avcodec-54.92.100,avutil-52.18.100) * since libav10.0: 10.2 avcodec55.34.1, avutil-53.3.0 * the same as avcodec_alloc_frame() (deprecated since 2.2). AVFrame was in avcodec.h, now in avutil/frame.h */ #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 0, 18, 100) #define av_frame_alloc() avcodec_alloc_frame() #if QTAV_USE_LIBAV(LIBAVCODEC) || FFMPEG_MODULE_CHECK(LIBAVCODEC, 54, 59, 100) #define av_frame_free(f) avcodec_free_frame(f) #else #define av_frame_free(f) av_free(f) #endif #endif #if QTAV_USE_LIBAV(LIBAVCODEC) const char *avcodec_get_name(enum AVCodecID id); #endif #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 55, 0, 68, 100) void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb); #endif // since libav-11, ffmpeg-2.1 #if !LIBAV_MODULE_CHECK(LIBAVCODEC, 56, 1, 0) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100) int av_packet_copy_props(AVPacket *dst, const AVPacket *src); #endif // since libav-10, ffmpeg-2.1 #if !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100) void av_packet_free_side_data(AVPacket *pkt); #endif //ffmpeg2.1 libav10 #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1, 39, 101) int av_packet_ref(AVPacket *dst, const AVPacket *src); #define av_packet_unref(pkt) av_free_packet(pkt) #endif #if !AV_MODULE_CHECK(LIBAVCODEC, 55, 52, 0, 63, 100) void avcodec_free_context(AVCodecContext **pavctx); #endif #if QTAV_HAVE(AVFILTER) // ffmpeg2.0 2013-07-03 - 838bd73 - lavfi 3.78.100 - avfilter.h #if QTAV_USE_LIBAV(LIBAVFILTER) #define avfilter_graph_parse_ptr(pGraph, pFilters, ppInputs, ppOutputs, pLog) avfilter_graph_parse(pGraph, pFilters, *ppInputs, *ppOutputs, pLog) #elif !FFMPEG_MODULE_CHECK(LIBAVFILTER, 3, 78, 100) #define avfilter_graph_parse_ptr(pGraph, pFilters, ppInputs, ppOutputs, pLog) avfilter_graph_parse(pGraph, pFilters, ppInputs, ppOutputs, pLog) #endif //QTAV_USE_LIBAV(LIBAVFILTER) //ffmpeg1.0 2012-06-12 - c7b9eab / 84b9fbe - lavfi 2.79.100 / 2.22.0 - avfilter.h #if !AV_MODULE_CHECK(LIBAVFILTER, 2, 22, 0, 79, 100) //FF_API_AVFILTERPAD_PUBLIC const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx); enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx); #endif ///ffmpeg1.0 lavfi 2.74.100 / 2.17.0. was in ffmpeg in old ffmpeg and now are in avfilter.h and deprecated. declare here to avoid version check #if QTAV_USE_FFMPEG(LIBAVFILTER) #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ struct AVFilterBufferRef; int avfilter_copy_buf_props(AVFrame *dst, const AVFilterBufferRef *src); #ifdef __cplusplus } #endif /* __cplusplus */ #endif #endif //QTAV_HAVE(AVFILTER) /* helper functions */ const char *get_codec_long_name(AVCodecID id); // AV_CODEC_ID_H265 is a macro defined as AV_CODEC_ID_HEVC in ffmpeg but not in libav. so we can use FF_PROFILE_HEVC_MAIN to avoid libavcodec version check. (from ffmpeg 2.1) #ifndef FF_PROFILE_HEVC_MAIN //libav does not define it #define AV_CODEC_ID_HEVC ((AVCodecID)0) //QTAV_CODEC_ID(NONE) #define CODEC_ID_HEVC ((AVCodecID)0) //QTAV_CODEC_ID(NONE) #define FF_PROFILE_HEVC_MAIN -1 #define FF_PROFILE_HEVC_MAIN_10 -1 #endif #if !FFMPEG_MODULE_CHECK(LIBAVCODEC, 54, 92, 100) && !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) //ffmpeg1.2 libav10 #define AV_CODEC_ID_VP9 ((AVCodecID)0) //QTAV_CODEC_ID(NONE) #define CODEC_ID_VP9 ((AVCodecID)0) //QTAV_CODEC_ID(NONE) #endif #ifndef FF_PROFILE_VP9_0 #define FF_PROFILE_VP9_0 0 #define FF_PROFILE_VP9_1 1 #define FF_PROFILE_VP9_2 2 #define FF_PROFILE_VP9_3 3 #endif #define AV_RUN_CHECK(FUNC, RETURN, ...) do { \ int ret = FUNC; \ if (ret < 0) { \ char str[AV_ERROR_MAX_STRING_SIZE]; \ memset(str, 0, sizeof(str)); \ av_strerror(ret, str, sizeof(str)); \ av_log(NULL, AV_LOG_WARNING, "Error " #FUNC " @%d " __FILE__ ": (%#x) %s\n", __LINE__, ret, str); \ RETURN __VA_ARGS__; \ } } while(0) #endif //QTAV_COMPAT_H QtAV-1.12.0/src/QtAV/private/AVDecoder_p.h000066400000000000000000000107101312235004300177360ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVDECODER_P_H #define QTAV_AVDECODER_P_H #include #include #include #include "QtAV/QtAV_Global.h" #include "QtAV/private/AVCompat.h" namespace QtAV { // always define the class to avoid macro check when using it class AVFrameBuffers { #if QTAV_HAVE(AVBUFREF) QVector buf; #endif public: AVFrameBuffers(AVFrame* frame) { Q_UNUSED(frame); #if QTAV_HAVE(AVBUFREF) if (!frame->buf[0]) { //not ref counted. duplicate data? return; } buf.reserve(frame->nb_extended_buf + FF_ARRAY_ELEMS(frame->buf)); buf.resize(frame->nb_extended_buf + FF_ARRAY_ELEMS(frame->buf)); for (int i = 0; i < (int)FF_ARRAY_ELEMS(frame->buf); ++i) { if (!frame->buf[i]) //so not use planes + nb_extended_buf! continue; buf[i] = av_buffer_ref(frame->buf[i]); if (!buf[i]) { qWarning("av_buffer_ref(frame->buf[%d]) error", i); } } if (!frame->extended_buf) return; for (int i = 0; i < frame->nb_extended_buf; ++i) { const int k = buf.size() + i - frame->nb_extended_buf; buf[k] = av_buffer_ref(frame->extended_buf[i]); if (!buf[k]) { qWarning("av_buffer_ref(frame->extended_buf[%d]) error", i); } } #endif //QTAV_HAVE(AVBUFREF) } ~AVFrameBuffers() { #if QTAV_HAVE(AVBUFREF) foreach (AVBufferRef* b, buf) { av_buffer_unref(&b); } #endif //QTAV_HAVE(AVBUFREF) } }; typedef QSharedPointer AVFrameBuffersRef; class Q_AV_PRIVATE_EXPORT AVDecoderPrivate : public DPtrPrivate { public: static const char* getProfileName(AVCodecID id, int profile) { AVCodec *c = avcodec_find_decoder(id); if (!c) return "Unknow"; return av_get_profile_name(c, profile); } static const char* getProfileName(const AVCodecContext* ctx) { if (ctx->codec) return av_get_profile_name(ctx->codec, ctx->profile); return getProfileName(ctx->codec_id, ctx->profile); } AVDecoderPrivate(): codec_ctx(0) , available(true) , is_open(false) , undecoded_size(0) , dict(0) { codec_ctx = avcodec_alloc_context3(NULL); } virtual ~AVDecoderPrivate() { if (dict) { av_dict_free(&dict); } if (codec_ctx) { avcodec_free_context(&codec_ctx); } } virtual bool open() {return true;} virtual void close() {} virtual bool enableFrameRef() const { return true;} void applyOptionsForDict(); void applyOptionsForContext(); AVCodecContext *codec_ctx; //set once and not change bool available; //TODO: true only when context(and hw ctx) is ready bool is_open; int undecoded_size; QString codec_name; QVariantHash options; AVDictionary *dict; }; class AudioResampler; class AudioDecoderPrivate : public AVDecoderPrivate { public: AudioDecoderPrivate(); virtual ~AudioDecoderPrivate(); AudioResampler *resampler; QByteArray decoded; }; class Q_AV_PRIVATE_EXPORT VideoDecoderPrivate : public AVDecoderPrivate { public: VideoDecoderPrivate(): AVDecoderPrivate() {} virtual ~VideoDecoderPrivate() {} }; } //namespace QtAV Q_DECLARE_METATYPE(QtAV::AVFrameBuffersRef) #endif // QTAV_AVDECODER_P_H QtAV-1.12.0/src/QtAV/private/AVEncoder_p.h000066400000000000000000000055571312235004300177650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVENCODER_P_H #define QTAV_AVENCODER_P_H #include #include "QtAV/AudioFormat.h" #include "QtAV/Packet.h" #include "QtAV/VideoFormat.h" #include "QtAV/private/AVCompat.h" namespace QtAV { class Q_AV_PRIVATE_EXPORT AVEncoderPrivate : public DPtrPrivate { public: AVEncoderPrivate(): avctx(0) , is_open(false) , bit_rate(0) , timestamp_mode(0) , dict(0) { } virtual ~AVEncoderPrivate() { if (dict) { av_dict_free(&dict); } if (avctx) { avcodec_free_context(&avctx); } } virtual bool open() {return true;} virtual bool close() {return true;} // used iff avctx != null void applyOptionsForDict(); void applyOptionsForContext(); AVCodecContext *avctx; // null if not avcodec. allocated in ffmpeg based encoders bool is_open; int bit_rate; int timestamp_mode; QString codec_name; QVariantHash options; AVDictionary *dict; // null if not avcodec Packet packet; }; class AudioResampler; class AudioEncoderPrivate : public AVEncoderPrivate { public: AudioEncoderPrivate() : AVEncoderPrivate() { bit_rate = 64000; } virtual ~AudioEncoderPrivate() {} AudioResampler *resampler; AudioFormat format, format_used; }; class Q_AV_PRIVATE_EXPORT VideoEncoderPrivate : public AVEncoderPrivate { public: VideoEncoderPrivate(): AVEncoderPrivate() , width(0) , height(0) , frame_rate(-1) , format_used(VideoFormat::Format_Invalid) , format(format_used) { bit_rate = 400000; } virtual ~VideoEncoderPrivate() {} int width, height; qreal frame_rate; VideoFormat::PixelFormat format_used; VideoFormat format; }; } //namespace QtAV #endif // QTAV_AVENCODER_P_H QtAV-1.12.0/src/QtAV/private/AVOutput_p.h000066400000000000000000000041571312235004300177010ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2013 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVOUTPUT_P_H #define QTAV_AVOUTPUT_P_H #include #include #include #include namespace QtAV { class AVOutput; class AVDecoder; class Filter; class VideoFilterContext; class Statistics; class OutputSet; class Q_AV_PRIVATE_EXPORT AVOutputPrivate : public DPtrPrivate { public: AVOutputPrivate(): paused(false) , available(true) , statistics(0) , filter_context(0) {} virtual ~AVOutputPrivate(); bool paused; bool available; QMutex mutex; //pause QWaitCondition cond; //pause //paintEvent is in main thread, copy it(only dynamic information) is better. //the static data are copied from AVPlayer when open Statistics *statistics; //do not own the ptr. just use AVPlayer's statistics ptr VideoFilterContext *filter_context; //create internally by the renderer with correct type QList filters; QList pending_uninstall_filters; QList output_sets; }; } //namespace QtAV #endif // QTAV_AVOUTPUT_P_H QtAV-1.12.0/src/QtAV/private/AudioOutputBackend.h000066400000000000000000000134711312235004300213640ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_AUDIOOUTPUTBACKEND_H #define QAV_AUDIOOUTPUTBACKEND_H #include #include #include namespace QtAV { typedef int AudioOutputBackendId; class Q_AV_PRIVATE_EXPORT AudioOutputBackend : public QObject { Q_OBJECT public: AudioOutput *audio; bool available; // default is true. set to false when failed to create backend int buffer_size; int buffer_count; AudioFormat format; static QStringList defaultPriority(); /*! * \brief AudioOutputBackend * Specify supported features by the backend. Use this for new backends. */ AudioOutputBackend(AudioOutput::DeviceFeatures f, QObject *parent); virtual ~AudioOutputBackend() {} virtual QString name() const = 0; virtual bool open() = 0; virtual bool close() = 0; virtual bool write(const QByteArray& data) = 0; //MUST virtual bool play() = 0; //MUST virtual bool flush() { return false;} virtual bool clear() { return false;} virtual bool isSupported(const AudioFormat& format) const { return isSupported(format.sampleFormat()) && isSupported(format.channelLayout());} // FIXME: workaround. planar convertion crash now! virtual bool isSupported(AudioFormat::SampleFormat f) const { return !IsPlanar(f);} // 5, 6, 7 channels may not play virtual bool isSupported(AudioFormat::ChannelLayout cl) const { return int(cl) < int(AudioFormat::ChannelLayout_Unsupported);} /*! * \brief The BufferControl enum * Used to adapt to different audio playback backend. Usually you don't need this in application level development. */ enum BufferControl { User = 0, // You have to reimplement waitForNextBuffer() Blocking = 1, BytesCallback = 1 << 1, CountCallback = 1 << 2, PlayedCount = 1 << 3, //number of buffers played since last buffer dequeued PlayedBytes = 1 << 4, OffsetIndex = 1 << 5, //current playing offset OffsetBytes = 1 << 6, //current playing offset by bytes WritableBytes = 1 << 7, }; virtual BufferControl bufferControl() const = 0; // called by callback with Callback control virtual void onCallback(); virtual void acquireNextBuffer() {} //default return -1. means not the control virtual int getPlayedCount() {return -1;} //PlayedCount /*! * \brief getPlayedBytes * reimplement this if bufferControl() is PlayedBytes. * \return the bytes played since last dequeue the buffer queue */ virtual int getPlayedBytes() {return -1;} // PlayedBytes virtual int getOffset() {return -1;} // OffsetIndex virtual int getOffsetByBytes() {return -1;}// OffsetBytes virtual int getWritableBytes() {return -1;} //WritableBytes // not virtual. called in ctor AudioOutput::DeviceFeatures supportedFeatures() { return m_features;} /*! * \brief setVolume * Set volume by backend api. If backend can not set the given volume, or SetVolume feature is not set, software implemention will be used. * \param value >=0 * \return true if success */ virtual bool setVolume(qreal value) { Q_UNUSED(value); return false;} virtual qreal getVolume() const { return 1.0;} virtual bool setMute(bool value = true) { Q_UNUSED(value); return false;} virtual bool getMute() const { return false;} Q_SIGNALS: /* * \brief reportVolume * Volume can be changed by per-app volume control from system outside this library. Useful for synchronizing ui to system. * Volume control from QtAV may invoke it too. And it may be invoked even if volume is not changed. * If volume changed, signal volumeChanged() will be emitted and volume() will be updated. * Only supported by some backends, e.g. pulseaudio */ void volumeReported(qreal value); void muteReported(bool value); public: template static bool Register(AudioOutputBackendId id, const char* name) { return Register(id, create, name);} static AudioOutputBackend* create(AudioOutputBackendId id); static AudioOutputBackend* create(const char* name); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static AudioOutputBackendId* next(AudioOutputBackendId* id = 0); static const char* name(AudioOutputBackendId id); static AudioOutputBackendId id(const char* name); private: template static AudioOutputBackend* create() { return new C();} typedef AudioOutputBackend* (*AudioOutputBackendCreator)(); static bool Register(AudioOutputBackendId id, AudioOutputBackendCreator, const char *name); private: AudioOutput::DeviceFeatures m_features; Q_DISABLE_COPY(AudioOutputBackend) }; } //namespace QtAV #endif //QAV_AUDIOOUTPUTBACKEND_H QtAV-1.12.0/src/QtAV/private/AudioResampler_p.h000066400000000000000000000034411312235004300210610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AUDIORESAMPLER_P_H #define QTAV_AUDIORESAMPLER_P_H #include "QtAV/AudioFormat.h" #include "QtAV/private/AVCompat.h" #include namespace QtAV { class AudioResampler; class Q_AV_PRIVATE_EXPORT AudioResamplerPrivate : public DPtrPrivate { public: AudioResamplerPrivate(): in_samples_per_channel(0) , out_samples_per_channel(0) , speed(1.0) { in_format.setSampleFormat(AudioFormat::SampleFormat_Unknown); out_format.setSampleFormat(AudioFormat::SampleFormat_Float); } int in_samples_per_channel, out_samples_per_channel; qreal speed; AudioFormat in_format, out_format; QByteArray data_out; }; } //namespace QtAV #endif // QTAV_AUDIORESAMPLER_P_H QtAV-1.12.0/src/QtAV/private/Filter_p.h000066400000000000000000000033641312235004300173760ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FILTER_P_H #define QTAV_FILTER_P_H #include namespace QtAV { class Filter; class VideoFilterContext; class Statistics; class Q_AV_PRIVATE_EXPORT FilterPrivate : public DPtrPrivate { public: FilterPrivate(): enabled(true) , owned_by_target(false) {} virtual ~FilterPrivate() {} bool enabled; bool owned_by_target; }; class Q_AV_PRIVATE_EXPORT VideoFilterPrivate : public FilterPrivate { public: VideoFilterPrivate() : context(0) {} VideoFilterContext *context; //used only when is necessary }; class Q_AV_PRIVATE_EXPORT AudioFilterPrivate : public FilterPrivate { }; } //namespace QtAV #endif // QTAV_FILTER_P_H QtAV-1.12.0/src/QtAV/private/Frame_p.h000066400000000000000000000031141312235004300171740ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FRAME_P_H #define QTAV_FRAME_P_H #include #include #include #include namespace QtAV { class Frame; class FramePrivate : public QSharedData { Q_DISABLE_COPY(FramePrivate) public: FramePrivate() : timestamp(0) {} virtual ~FramePrivate() {} QVector planes; //slice QVector line_sizes; //stride QVariantMap metadata; QByteArray data; qreal timestamp; }; } //namespace QtAV #endif // QTAV_Frame_P_H QtAV-1.12.0/src/QtAV/private/MediaIO_p.h000066400000000000000000000030701312235004300174120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_MediaIO_P_H #define QTAV_MediaIO_P_H #include "QtAV/QtAV_Global.h" #include "QtAV/private/AVCompat.h" #include #include "QtAV/MediaIO.h" namespace QtAV { class MediaIO; class Q_AV_PRIVATE_EXPORT MediaIOPrivate : public DPtrPrivate { public: MediaIOPrivate() : ctx(NULL) , buffer_size(-1) , mode(MediaIO::Read) {} AVIOContext *ctx; int buffer_size; MediaIO::AccessMode mode; QString url; }; } //namespace QtAV #endif // QTAV_MediaIO_P_H QtAV-1.12.0/src/QtAV/private/OpenGLRendererBase_p.h000066400000000000000000000030571312235004300215560ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2014-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLRENDERERBASE_P_H #define QTAV_OPENGLRENDERERBASE_P_H #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/OpenGLVideo.h" namespace QtAV { class Q_AV_PRIVATE_EXPORT OpenGLRendererBasePrivate : public VideoRendererPrivate { public: OpenGLRendererBasePrivate(QPaintDevice *pd); virtual ~OpenGLRendererBasePrivate(); void setupAspectRatio(); QPainter *painter; OpenGLVideo glv; QMatrix4x4 matrix; bool frame_changed; }; } //namespace QtAV #endif // QTAV_OpenGLRendererBase_P_H QtAV-1.12.0/src/QtAV/private/PlayerSubtitle.h000066400000000000000000000057611312235004300206050ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_PLAYERSUBTITLE_H #define QTAV_PLAYERSUBTITLE_H #include #include #include #include #include namespace QtAV { class AVPlayer; class Subtitle; /*! * \brief The PlayerSubtitle class * Bind Subtitle to AVPlayer. Used by SubtitleFilter and QuickSubtitle. * Subtitle load priority: user specified file (setFile(...)) > auto load external (autoLoad() must be true) > embedded subtitle */ class Q_AV_PRIVATE_EXPORT PlayerSubtitle : public QObject { Q_OBJECT public: PlayerSubtitle(QObject *parent = 0); void setPlayer(AVPlayer* player); Subtitle* subtitle(); /*! * \brief setFile * Load user selected subtitle. The subtitle will not change unless you manually setFile(QString()). */ void setFile(const QString& file); QString file() const; /*! * \brief autoLoad * Auto find and load a suitable external subtitle if file() is not empty. */ void setAutoLoad(bool value); bool autoLoad() const; Q_SIGNALS: void autoLoadChanged(bool value); void fileChanged(); public Q_SLOTS: void onEnabledChanged(bool value); private Q_SLOTS: void onPlayerSourceChanged(); void onPlayerPositionChanged(); void onPlayerStart(); void tryReload(); void tryReloadInternalSub(); void updateInternalSubtitleTracks(const QVariantList& tracks); void processInternalSubtitlePacket(int track, const QtAV::Packet& packet); void processInternalSubtitleHeader(const QByteArray &codec, const QByteArray& data); //TODO: remove private: void connectSignals(); void disconnectSignals(); void tryReload(int flag); //1: internal, 2: external, 3: internal+external private: bool m_auto; bool m_enabled; // TODO: m_enable_external AVPlayer *m_player; Subtitle *m_sub; QString m_file; QVariantList m_tracks; QVector m_current_pkt; }; } #endif // QTAV_PLAYERSUBTITLE_H QtAV-1.12.0/src/QtAV/private/QPainterRenderer_p.h000066400000000000000000000046331312235004300213630ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QPAINTERRENDERER_P_H #define QTAV_QPAINTERRENDERER_P_H #include #include #include namespace QtAV { class Q_AV_PRIVATE_EXPORT QPainterRendererPrivate : public VideoRendererPrivate { public: QPainterRendererPrivate(): painter(0) {} virtual ~QPainterRendererPrivate(){ if (painter) { delete painter; painter = 0; } } void setupQuality() { switch (quality) { case VideoRenderer::QualityFastest: painter->setRenderHint(QPainter::Antialiasing, false); painter->setRenderHint(QPainter::TextAntialiasing, false); painter->setRenderHint(QPainter::SmoothPixmapTransform, false); painter->setRenderHint(QPainter::HighQualityAntialiasing, false); break; case VideoRenderer::QualityBest: default: painter->setRenderHint(QPainter::Antialiasing, true); painter->setRenderHint(QPainter::TextAntialiasing, true); painter->setRenderHint(QPainter::SmoothPixmapTransform, true); painter->setRenderHint(QPainter::HighQualityAntialiasing, true); break; } } // drawPixmap() is faster for on screen painting QPixmap pixmap; QPainter *painter; }; } //namespace QtAV #endif // QTAV_QPAINTERRENDERER_P_H QtAV-1.12.0/src/QtAV/private/SubtitleProcessor.h000066400000000000000000000106021312235004300213160ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBTITLEPROCESSOR_H #define QTAV_SUBTITLEPROCESSOR_H #include #include #include #include namespace QtAV { typedef QString SubtitleProcessorId; class Q_AV_PRIVATE_EXPORT SubtitleProcessor { public: SubtitleProcessor(); virtual ~SubtitleProcessor() {} virtual SubtitleProcessorId id() const = 0; virtual QString name() const = 0; /*! * \brief supportedTypes * \return a list of supported suffixes. e.g. [ "ass", "ssa", "srt" ] * used to find subtitle files with given suffixes */ virtual QStringList supportedTypes() const = 0; /*! * \brief process * process subtitle from QIODevice. * \param dev dev is open and you don't have to close it * \return false if failed or does not supports iodevice, e.g. does not support sequential device */ virtual bool process(QIODevice* dev) = 0; /*! * \brief process * default behavior is calling process(QFile*) * \param path * \return false if failed or does not support file */ virtual bool process(const QString& path); /*! * \brief timestamps * call this after process(). SubtitleFrame.text must be set * \return */ virtual QList frames() const = 0; virtual bool canRender() const { return false;} // return false if not supported virtual bool processHeader(const QByteArray& codec, const QByteArray& data) { Q_UNUSED(codec); Q_UNUSED(data); return false; } // return timestamp, insert it to Subtitle's internal linkedlist. can be invalid if only support renderering virtual SubtitleFrame processLine(const QByteArray& data, qreal pts = -1, qreal duration = 0) = 0; virtual QString getText(qreal pts) const = 0; // default null image virtual QImage getImage(qreal pts, QRect* boundingRect = 0); virtual SubImageSet getSubImages(qreal pts, QRect* boundingRect = 0); void setFrameSize(int width, int height); QSize frameSize() const; int frameWidth() const; int frameHeight() const; // font properties: libass only now virtual void setFontFile(const QString& file) {Q_UNUSED(file);} virtual void setFontsDir(const QString& dir) {Q_UNUSED(dir);} virtual void setFontFileForced(bool force) {Q_UNUSED(force);} public: static void registerAll(); template static bool Register(SubtitleProcessorId id, const char* name) { return Register(id, create, name);} static SubtitleProcessor* create(SubtitleProcessorId id); static SubtitleProcessor* create(const char* name = "FFmpeg"); /*! * \brief next * \param id NULL to get the first id address * \return address of id or NULL if not found/end */ static SubtitleProcessorId* next(SubtitleProcessorId* id = 0); static const char* name(SubtitleProcessorId id); static SubtitleProcessorId id(const char* name); private: template static SubtitleProcessor* create() { return new C();} typedef SubtitleProcessor* (*SubtitleProcessorCreator)(); static bool Register(SubtitleProcessorId id, SubtitleProcessorCreator, const char *name); protected: // default do nothing virtual void onFrameSizeChanged(int width, int height); private: int m_width, m_height; }; } //namespace QtAV #endif // QTAV_SUBTITLEPROCESSOR_H QtAV-1.12.0/src/QtAV/private/VideoRenderer_p.h000066400000000000000000000115271312235004300207060ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_VIDEORENDERER_P_H #define QAV_VIDEORENDERER_P_H #include #include #include #include #include #include /*TODO: * Region of Interest(ROI) * use matrix to compute out rect, mapped point etc */ QT_BEGIN_NAMESPACE class QObject; class QWidget; QT_END_NAMESPACE namespace QtAV { class Filter; class Q_AV_PRIVATE_EXPORT VideoRendererPrivate : public AVOutputPrivate { public: VideoRendererPrivate(): AVOutputPrivate() , update_background(true) , renderer_width(480) , renderer_height(320) , source_aspect_ratio(0) , src_width(0) , src_height(0) , aspect_ratio_changed(true) //to set the initial parameters , out_aspect_ratio_mode(VideoRenderer::VideoAspectRatio) , out_aspect_ratio(0) , quality(VideoRenderer::QualityBest) , orientation(0) , preferred_format(VideoFormat::Format_RGB32) , force_preferred(false) , brightness(0) , contrast(0) , hue(0) , saturation(0) , bg_color(0, 0, 0) { //conv.setInFormat(PIX_FMT_YUV420P); //conv.setOutFormat(PIX_FMT_BGR32); //TODO: why not RGB32? } virtual ~VideoRendererPrivate(){ } // return true if video rect changed bool computeOutParameters(qreal outAspectRatio) { qreal rendererAspectRatio = qreal(renderer_width)/qreal(renderer_height); const QRect out_rect0(out_rect); if (out_aspect_ratio_mode == VideoRenderer::RendererAspectRatio) { out_aspect_ratio = rendererAspectRatio; out_rect = QRect(0, 0, renderer_width, renderer_height); return out_rect0 != out_rect; } // dar: displayed aspect ratio in video renderer orientation const qreal dar = (orientation % 180) ? 1.0/outAspectRatio : outAspectRatio; //qDebug("out rect: %f %dx%d ==>", out_aspect_ratio, out_rect.width(), out_rect.height()); if (rendererAspectRatio >= dar) { //equals to original video aspect ratio here, also equals to out ratio //renderer is too wide, use renderer's height, horizonal align center const int h = renderer_height; const int w = qRound(dar * qreal(h)); out_rect = QRect((renderer_width - w)/2, 0, w, h); } else if (rendererAspectRatio < dar) { //renderer is too high, use renderer's width const int w = renderer_width; const int h = qRound(qreal(w)/dar); out_rect = QRect(0, (renderer_height - h)/2, w, h); } out_aspect_ratio = outAspectRatio; //qDebug("%f %dx%d <<<<<<<<", out_aspect_ratio, out_rect.width(), out_rect.height()); return out_rect0 != out_rect; } virtual void setupQuality() {} //draw background when necessary, for example, renderer is resized. Then set to false bool update_background; // width, height: the renderer's size. i.e. size of video frame with the value with borders //TODO: rename to renderer_width/height int renderer_width, renderer_height; qreal source_aspect_ratio; int src_width, src_height; //TODO: in_xxx QMutex img_mutex; //for both source, out aspect ratio. because source change may result in out change if mode is VideoAspectRatio bool aspect_ratio_changed; VideoRenderer::OutAspectRatioMode out_aspect_ratio_mode; qreal out_aspect_ratio; VideoRenderer::Quality quality; //out_rect: the displayed video frame out_rect in the renderer QRect out_rect; //TODO: out_out_rect QRectF roi; int orientation; VideoFrame video_frame; VideoFormat::PixelFormat preferred_format; bool force_preferred; qreal brightness, contrast, hue, saturation; QColor bg_color; }; } //namespace QtAV #endif // QAV_VIDEORENDERER_P_H QtAV-1.12.0/src/QtAV/private/VideoShader_p.h000066400000000000000000000140501312235004300203400ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOSHADER_P_H #define QTAV_VIDEOSHADER_P_H #include "QtAV/OpenGLTypes.h" #include "QtAV/VideoFrame.h" #include "ColorTransform.h" #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #include #else #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) #include #endif #include #include typedef QGLBuffer QOpenGLBuffer; #define QOpenGLShaderProgram QGLShaderProgram #define QOpenGLShader QGLShader #define QOpenGLFunctions QGLFunctions #define QOpenGLContext QGLContext #endif namespace QtAV { // can not move to OpenGLHelper.h because that's not public/private header enum ShaderType { VertexShader, FragmentShader, ShaderTypeCount }; class VideoShader; class Q_AV_PRIVATE_EXPORT VideoShaderPrivate : public DPtrPrivate { public: VideoShaderPrivate() : owns_program(false) , rebuild_program(false) , update_builtin_uniforms(true) , program(0) , u_Matrix(-1) , u_colorMatrix(-1) , u_to8(-1) , u_opacity(-1) , u_c(-1) , material_type(0) , texture_target(GL_TEXTURE_2D) {} virtual ~VideoShaderPrivate() { if (owns_program && program) { if (QOpenGLContext::currentContext()) { // FIXME: may be not called from renderering thread. so we still have to detach shaders program->removeAllShaders(); } delete program; } program = 0; } bool owns_program; // shader program is not created by this. e.g. scene graph create it's own program and we store it here bool rebuild_program; bool update_builtin_uniforms; //builtin uniforms are static, set the values once is enough if no change QOpenGLShaderProgram *program; int u_Matrix; int u_colorMatrix; int u_to8; int u_opacity; int u_c; int u_texelSize; int u_textureSize; qint32 material_type; QVector u_Texture; GLenum texture_target; VideoFormat video_format; mutable QByteArray planar_frag, packed_frag; mutable QByteArray vert; QVector user_uniforms[ShaderTypeCount]; }; class VideoMaterial; class VideoMaterialPrivate : public DPtrPrivate { public: VideoMaterialPrivate() : update_texure(true) , init_textures_required(true) , bpc(0) , width(0) , height(0) , video_format(VideoFormat::Format_Invalid) , plane1_linesize(0) , effective_tex_width_ratio(1.0) , target(GL_TEXTURE_2D) , dirty(true) , try_pbo(true) { v_texel_size.reserve(4); textures.reserve(4); texture_size.reserve(4); effective_tex_width.reserve(4); internal_format.reserve(4); data_format.reserve(4); data_type.reserve(4); static bool enable_pbo = qgetenv("QTAV_PBO").toInt() > 0; if (try_pbo) try_pbo = enable_pbo; pbo.reserve(4); colorTransform.setOutputColorSpace(ColorSpace_RGB); } ~VideoMaterialPrivate(); bool initPBO(int plane, int size); bool initTexture(GLuint tex, GLint internal_format, GLenum format, GLenum dataType, int width, int height); bool updateTextureParameters(const VideoFormat& fmt); void uploadPlane(int p, bool updateTexture = true); bool ensureResources(); bool ensureTextures(); void setupQuality(); bool update_texure; // reduce upload/map times. true: new frame not bound. false: current frame is bound bool init_textures_required; // e.g. target changed int bpc; int width, height; //avoid accessing frame(need lock) VideoFrame frame; /* * old format. used to check whether we have to update textures. set to current frame's format after textures are updated. * TODO: only VideoMaterial.type() is enough to check and update shader. so remove it */ VideoFormat video_format; QSize plane0Size; // width is in bytes. different alignments may result in different plane 1 linesize even if plane 0 are the same int plane1_linesize; // textures.d in updateTextureParameters() changed. happens in qml. why? quint8 workaround_vector_crash_on_linux[8]; //TODO: remove QVector textures; //texture ids. size is plane count QHash owns_texture; QVector texture_size; QVector effective_tex_width; //without additional width for alignment qreal effective_tex_width_ratio; GLenum target; QVector internal_format; QVector data_format; QVector data_type; bool dirty; ColorTransform colorTransform; bool try_pbo; QVector pbo; QVector2D vec_to8; //TODO: vec3 to support both RG and LA (.rga, vec_to8) QMatrix4x4 channel_map; QVector v_texel_size; QVector v_texture_size; }; } //namespace QtAV #endif // QTAV_VideoShader_P_H QtAV-1.12.0/src/QtAV/private/factory.h000066400000000000000000000200541312235004300172740ustar00rootroot00000000000000/****************************************************************************** Factory: factory template Copyright (C) 2012-2016 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef FACTORY_H #define FACTORY_H /* * NOTE: this file can not be included in public headers! It must be used * inside the library, i.e., only be included in cpp or internal header. * Using it outside results in initializing static singleton member twice. */ #include //tolower #include #include #include #include #include //std::remove #include "singleton.h" #if 0 #include template Class& Loki::Singleton::Instance() { return Loki::SingletonHolder::Instance(); } #endif #define FACTORY_REGISTER(BASE, _ID, NAME) FACTORY_REGISTER_ID_TYPE(BASE, BASE##Id_##_ID, BASE##_ID, NAME) #define FACTORY_REGISTER_ID_TYPE(BASE, ID, TYPE, NAME) \ FACTORY_REGISTER_ID_TYPE_AUTO(BASE, ID, TYPE, NAME) \ bool Register##TYPE##_Man() { \ return BASE::Register(ID, NAME); \ } #define FACTORY_REGISTER_ID_TYPE_AUTO(BASE, ID, TYPE, NAME) \ namespace { \ static const struct factory_register_##TYPE { \ inline factory_register_##TYPE() { \ BASE::Register(ID, NAME); \ } \ } sInit_##TYPE; \ } #define FACTORY_DEFINE(T) \ class T##Factory : public Factory {}; \ bool T::Register(T##Id id, T##Creator c, const char *name) { \ DBG(#T "::Register(..., %s)\n", name); \ return T##Factory::Instance().registerCreator(id, c) && T##Factory::Instance().registerIdName(id, name); \ } \ T* T::create(T##Id id) {return T##Factory::Instance().create(id);} \ T* T::create(const char* name) { return T::create(T::id(name));} \ T##Id* T::next(T##Id *id) { \ const std::vector& ids = T##Factory::Instance().registeredIds(); \ if (!id) return (T##Id*)&ids[0]; \ T##Id *id0 = (T##Id*)&ids[0], *id1 = (T##Id*)&ids[ids.size() - 1]; \ if (id >= id0 && id < id1) return id + 1; \ if (id == id1) return NULL; \ std::vector::const_iterator it = std::find(ids.begin(), ids.end(), *id); \ if (it == ids.end()) return NULL; \ return (T##Id*)&*(it++); \ } \ T##Id T::id(const char* name) { DBG(#T "::id(\"%s\")\n", name); return T##Factory::Instance().id(name, false);} \ const char* T::name(T##Id id) {return T##Factory::Instance().name(id);} /* * Used in library, can not be used both in library and outside. so we don't need export it */ template class Factory : public Singleton { DISABLE_COPY(Factory) typedef Id ID; typedef T Type; typedef Type* (*Creator)(); public: Type* create(const ID& id); template bool register_(const ID& id) { // register_(id, name) std::pair result = creators.insert(std::make_pair(id, create)); return result.second; } //template bool registerCreator(const ID& id, const Creator& callback); bool registerIdName(const ID& id, const char* name); bool unregisterCreator(const ID& id); //bool unregisterAll(); ID id(const char* name, bool caseSensitive = true) const; const char* name(const ID &id) const; size_t count() const; const std::vector ®isteredIds() const; std::vector registeredNames() const; Type* getRandom(); //remove // Type* at(int index); // ID idAt(int index); protected: Factory() {} virtual ~Factory() {} private: template static Type* create() { return new C(); } typedef std::map CreatorMap; CreatorMap creators; std::vector ids; typedef std::map NameMap; NameMap name_map; //static? }; #if 0 template typename Factory::CreatorMap Factory::creators; template typename Factory::NameMap Factory::name_map; #endif template typename Factory::Type *Factory::create(const ID& id) { typename CreatorMap::const_iterator it = creators.find(id); if (it == creators.end()) { DBG("Unknown id "); return 0; //throw std::runtime_error(err_msg.arg(id).toStdString()); } return (it->second)(); } template bool Factory::registerCreator(const ID& id, const Creator& callback) { //DBG("%p id [%d] registered. size=%d\n", &Factory::Instance(), id, ids.size()); ids.insert(ids.end(), id); return creators.insert(typename CreatorMap::value_type(id, callback)).second; } template bool Factory::registerIdName(const ID& id, const char* name) { return name_map.insert(typename NameMap::value_type(id, name/*.toLower()*/)).second; } template bool Factory::unregisterCreator(const ID& id) { //DBG("Id [%d] unregistered\n", id); ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end()); name_map.erase(id); return creators.erase(id) == 1; } template typename Factory::ID Factory::id(const char* name, bool caseSensitive) const { #ifdef _MSC_VER #define strcasecmp(s1, s2) _strcmpi(s1, s2) #endif //need 'typename' because 'Factory::NameMap' is a dependent scope for (typename NameMap::const_iterator it = name_map.begin(); it!=name_map.end(); ++it) { if (caseSensitive) { if (it->second == name || !strcmp(it->second, name)) return it->first; } else { if (!strcasecmp(it->second, name)) { return it->first; } } } DBG("Not found\n"); return ID(); //can not return ref. TODO: Use a ID wrapper class } template const char* Factory::name(const ID &id) const { typename NameMap::const_iterator it = name_map.find(id); if (it == name_map.end()) return NULL; return it->second; } template const std::vector& Factory::registeredIds() const { return ids; } template std::vector Factory::registeredNames() const { std::vector names; for (typename NameMap::const_iterator it = name_map.begin(); it != name_map.end(); ++it) { names.push_back((*it).second); } return names; } template size_t Factory::count() const { //DBG("%p size = %d", &Factory::Instance(), ids.size()); return ids.size(); } template typename Factory::Type* Factory::getRandom() { srand(time(0)); int index = rand() % ids.size(); //DBG("random %d/%d", index, ids.size()); ID new_eid = ids.at(index); //DBG("id %d", new_eid); return create(new_eid); } #endif // FACTORY_H QtAV-1.12.0/src/QtAV/private/mkid.h000066400000000000000000000151751312235004300165610ustar00rootroot00000000000000/****************************************************************************** mkid: map chars to int at build time. Template based. A replacement of FourCC Copyright (C) 2012-2014 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef MKID_H #define MKID_H /*! * Example: * int id1 = mkid::fourcc<'H', 'E', 'V', 'C'>::value; * int id2 = mkid::id32base64_5<'H', 'e', 'l', 'l', 'o'>::value; * int id3 = mkid::id32base36_6<'M', 'r', 'W', 'a', 'n', 'g'>::value; * For (u)int32 result, base 64 accepts at most 5 characters, while base 36 accepts at most 6 characters. */ namespace mkid { namespace detail { template struct map_base; } // namespace internal template struct id32_1 { enum { value = detail::map_base::value};}; template struct id32_2 { enum { value = id32_1::value*base + detail::map_base::value};}; template struct id32_3 { enum { value = id32_2::value*base + detail::map_base::value};}; template struct id32_4 { enum { value = id32_3::value*base + detail::map_base::value};}; template struct id32_5 { enum { value = id32_4::value*base + detail::map_base::value};}; template struct id32_6 { enum { value = id32_5::value*base + detail::map_base::value};}; // template based FourCC template struct fourcc : public id32_4<256, a0, a1, a2, a3>{}; // a0~a4: '0'~'9', 'A'~'Z', 'a'~'z', '_', '.' ==>> [0,63] template struct id32base64_1 : public id32_1<64, a0>{}; template struct id32base64_2 : public id32_2<64, a0, a1>{}; template struct id32base64_3 : public id32_3<64, a0, a1, a2>{}; template struct id32base64_4 : public id32_4<64, a0, a1, a2, a3>{}; template struct id32base64_5 : public id32_5<64, a0, a1, a2, a3, a4>{}; // a0~a5: '0'~'9', 'A'~'Z', 'a'~'z' ==>> [0,63], upper and lower letters are equal template struct id32base36_1 : public id32_1<36, a0>{}; template struct id32base36_2 : public id32_2<36, a0, a1>{}; template struct id32base36_3 : public id32_3<36, a0, a1, a2>{}; template struct id32base36_4 : public id32_4<36, a0, a1, a2, a3>{}; template struct id32base36_5 : public id32_5<36, a0, a1, a2, a3, a4>{}; template struct id32base36_6 : public id32_6<36, a0, a1, a2, a3, a4, a5>{}; namespace detail { ////////////////////////////Details//////////////////////////// template struct if_then_else; template struct if_then_else { typedef T1 Type;}; template struct if_then_else { typedef T2 Type;}; template struct is_lower_letter { enum { value = c >= 97 && c <= 122 };}; template struct is_upper_letter { enum { value = c >= 65 && c <= 90 };}; template struct is_num { enum { value = c >= 48 && c <= 57 };}; template struct is_underline { enum { value = c == 95 }; }; template struct is_dot { enum { value = c == 46 };}; struct lower_letter_t; struct upper_letter_t; struct number_t; struct underline_t; struct dot_t; template struct map64_helper; template struct map64_helper { enum { value = x-48}; }; template struct map64_helper { enum { value = x-65+10}; }; template struct map64_helper { enum { value = x-97+10+26}; }; template struct map64_helper { enum { value = 62}; }; template struct map64_helper { enum { value = 63}; }; struct invalid_char_must_be_number_26letters_underline_dot; template struct map_base<64, x> { enum { value = map64_helper::value, number_t, typename if_then_else::value, upper_letter_t, typename if_then_else::value, lower_letter_t, typename if_then_else::value, underline_t, typename if_then_else::value, dot_t, invalid_char_must_be_number_26letters_underline_dot>::Type>::Type>::Type>::Type>::Type>::value }; }; template struct map36_helper; template struct map36_helper { enum { value = x-48}; }; template struct map36_helper { enum { value = x-65+10}; }; template struct map36_helper { enum { value = x-97+10}; }; struct invalid_char_must_be_number_26letters; template struct map_base<36, x> { enum { value = map36_helper::value, number_t, typename if_then_else::value, upper_letter_t, typename if_then_else::value, lower_letter_t, invalid_char_must_be_number_26letters>::Type>::Type>::Type>::value }; }; // for FourCC template struct out_of_lower_bound {}; template struct out_of_upper_bound {}; struct map_identical_t; template struct map_identical_helper; template struct map_identical_helper{enum{value=x};}; template struct map_base<256, x> { enum { value = map_identical_helper, typename if_then_else<(x>255), out_of_upper_bound<255>, map_identical_t>::Type>::Type>::value }; }; } // namespace detail } // namespace mkid #endif // MKID_H QtAV-1.12.0/src/QtAV/private/prepost.h000066400000000000000000000072531312235004300173270ustar00rootroot00000000000000/****************************************************************************** prepost.h: Add function calls before/after main() Copyright (C) 2012-2013 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef PREPOST_H #define PREPOST_H /*Avoid a compiler warning when the arguments is empty, *e.g. PRE_FUNC_ADD(foo). The right one is PRE_FUNC_ADD(f,)*/ /* * TODO: * boost::call_once * http://stackoverflow.com/questions/4173384/how-to-make-sure-a-function-is-only-called-once * http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fco.htm */ #ifdef __cplusplus /* for C++, we use non-local static object to call the functions automatically before main(). * anonymous namespace: avoid name confliction('static' keyword is not necessary) * */ #define PRE_FUNC_ADD(f, .../*args*/) \ namespace { \ static const struct initializer_for_##f { \ inline initializer_for_##f() { \ f(__VA_ARGS__); \ } \ } __sInit_##f; \ } #define POST_FUNC_ADD(f, .../*args*/) \ namespace { \ static const struct deinitializer_for_##f { \ inline deinitializer_for_##f() {} \ inline ~deinitializer_for_##f() { f(__VA_ARGS__); } \ } __sDeinit_##f; \ } #else /*for C. ! defined __cplusplus*/ /* *http://buliedian.iteye.com/blog/1069072 *http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/_crt.c.htm */ #if defined(_MSC_VER) #pragma section(".CRT$XIU", long, read) #pragma section(".CRT$XPU", long, read) #define _CRTALLOC(x) __declspec(allocate(x)) /*TODO: Auto unique naming*/ typedef int (__cdecl *_PF)(); /* why not void? */ /*static to avoid multiple defination*/ #define PRE_FUNC_ADD(f, .../*args*/) \ static int init_##f() { f(__VA_ARGS__); return 0;} \ _CRTALLOC(".CRT$XIU") static _PF pinit_##f [] = { init_##f }; /*static void (*pinit_##f)() = init_##f //__cdecl */ #define POST_FUNC_ADD(f, .../*args*/) \ static int deinit_##f() { f(__VA_ARGS__); return 0;} \ _CRTALLOC(".CRT$XPU") static _PF pdeinit_##f [] = { deinit_##f }; #elif defined(__GNUC__) #define PRE_FUNC_ADD(f, ...) \ __attribute__((constructor)) static void init_##f() { f(__VA_ARGS__); } #define POST_FUNC_ADD(f, ...) \ __attribute__((destructor)) static void deinit_##f() { f(__VA_ARGS__); } #else #ifndef __cplusplus #error Not supported for C: PRE_FUNC_ADD, POST_FUNC_ADD #endif #include /*static var init, atexit*/ #define PRE_FUNC_ADD(f, ...) \ static int init_##f() { f(__VA_ARGS__); return 0; } \ static int v_init_##f = init_##f(); /*Works for C++. For C, gcc will throw an error: *initializer element is not constant */ /*atexit do not support arguments*/ #define POST_FUNC_ADD(f, ...) \ static void atexit_##f() { atexit(f); } \ PRE_FUNC_ADD(atexit_##f) #endif #endif //__cplusplus #endif // PREPOST_H QtAV-1.12.0/src/QtAV/private/singleton.h000066400000000000000000000057271312235004300176410ustar00rootroot00000000000000/****************************************************************************** singleton.h: singleton template Copyright (C) 2012-2016 Wang Bin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef SINGLETON_H #define SINGLETON_H #include #include //harmattan: atexit #include #define USE_EXCEPTION 0 #if USE_EXCEPTION #include // std::string breaks abi #endif #ifdef DEBUG #define DBG(fmt, ...) \ fprintf(stderr, fmt, ##__VA_ARGS__); \ fflush(0); #else #define DBG(...) #endif //DEBUG #define DISABLE_COPY(Class) \ Class(const Class &); \ Class &operator=(const Class &); /* * Used in library, can not be used both in library and outside. so we don't need export it */ template class Singleton { DISABLE_COPY(Singleton) public: typedef T ObjectType; static T& Instance(); protected: Singleton() {} virtual ~Singleton() {} private: static void MakeInstance(); static void DestroySingleton(); static T* pInstance_; static bool destroyed_; }; /*if it is used as dll, template will instanced in dll and exe *, and pInstance_ are 0 for both*/ //TODO: use static Singleton inst; return inst; template T* Singleton::pInstance_ = 0; //Why it will be initialized twice? The order? template bool Singleton::destroyed_ = false; template T &Singleton::Instance() { //DBG("instance = %p\n", pInstance_); if (!pInstance_) { MakeInstance(); } return *pInstance_; } template void Singleton::MakeInstance() { if (!pInstance_) { if (destroyed_) { destroyed_ = false; #if USE_EXCEPTION throw std::logic_error("Dead Reference Detected"); #else DBG("Dead Reference Detected"); exit(1); #endif //QT_NO_EXCEPTIONS } pInstance_ = new T(); DBG("Singleton %p created...\n", pInstance_); std::atexit(&DestroySingleton); } } template void Singleton::DestroySingleton() { DBG("DestroySingleton...\n"); assert(!destroyed_); delete pInstance_; pInstance_ = 0; destroyed_ = true; } #endif // SINGLETON_H QtAV-1.12.0/src/QtAV/version.h000066400000000000000000000036201312235004300156400ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VERSION_H #define QTAV_VERSION_H #define QTAV_MAJOR 1 //((QTAV_VERSION&0xff0000)>>16) #define QTAV_MINOR 12 //((QTAV_VERSION&0xff00)>>8) #define QTAV_PATCH 0 //(QTAV_VERSION&0xff) #define QTAV_VERSION_MAJOR(V) ((V & 0xff0000) >> 16) #define QTAV_VERSION_MINOR(V) ((V & 0xff00) >> 8) #define QTAV_VERSION_PATCH(V) (V & 0xff) #define QTAV_VERSION_CHK(major, minor, patch) \ (((major&0xff)<<16) | ((minor&0xff)<<8) | (patch&0xff)) #define QTAV_VERSION QTAV_VERSION_CHK(QTAV_MAJOR, QTAV_MINOR, QTAV_PATCH) /*! Stringify \a x. */ #define _TOSTR(x) #x /*! Stringify \a x, perform macro expansion. */ #define TOSTR(x) _TOSTR(x) /* the following are compile time version */ /* C++11 requires a space between literal and identifier */ #define QTAV_VERSION_STR TOSTR(QTAV_MAJOR) "." TOSTR(QTAV_MINOR) "." TOSTR(QTAV_PATCH) #endif // QTAV_VERSION_H QtAV-1.12.0/src/QtAV_Global.cpp000066400000000000000000000351371312235004300157760ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/QtAV_Global.h" #include #include #include #include "QtAV/version.h" #include "QtAV/private/AVCompat.h" #include "utils/internal.h" #include "utils/Logger.h" unsigned QtAV_Version() { return QTAV_VERSION; } QString QtAV_Version_String() { // vs<2015: C2308: concatenating mismatched strings for QStringLiteral("a" "b") return QString::fromLatin1(QTAV_VERSION_STR); } #define QTAV_VERSION_STR_LONG QTAV_VERSION_STR "(" __DATE__ ", " __TIME__ ")" QString QtAV_Version_String_Long() { return QString::fromLatin1(QTAV_VERSION_STR_LONG); } namespace QtAV { namespace Internal { // disable logging for release. you can manually enable it. #if defined(QT_NO_DEBUG)// && !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_WINRT) static QtAV::LogLevel gLogLevel = QtAV::LogOff; #else static QtAV::LogLevel gLogLevel = QtAV::LogAll; #endif static bool gLogLevelSet = false; bool isLogLevelSet() { return gLogLevelSet;} static int gAVLogLevel = AV_LOG_INFO; } //namespace Internal //TODO: auto add new depend libraries information QString aboutFFmpeg_PlainText() { return aboutFFmpeg_HTML().remove(QRegExp(QStringLiteral("<[^>]*>"))); } namespace Internal { typedef struct depend_component { const char* lib; unsigned build_version; unsigned rt_version; const char *config; const char *license; } depend_component; static unsigned get_qt_version() { int major = 0, minor = 0, patch = 0; if (sscanf(qVersion(), "%d.%d.%d", &major, &minor, &patch) != 3) qWarning("Can not recognize Qt runtime version"); return QT_VERSION_CHECK(major, minor, patch); } static const depend_component* get_depend_component(const depend_component* info = 0) { // DO NOT use QStringLiteral here because the install script use strings to search "Qt-" in the library. QStringLiteral will place it in .ro and strings can not find it static const QByteArray qt_license(QLibraryInfo::licensee().prepend(QLatin1String("Qt-" QT_VERSION_STR " licensee: ")).toUtf8()); #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) static const char* qt_build_info = get_qt_version() >= QT_VERSION_CHECK(5, 3, 0) ? QLibraryInfo::build() : ""; #else static const char* qt_build_info = ""; #endif static const depend_component components[] = { { "Qt", QT_VERSION, get_qt_version(), qt_build_info, qt_license.constData() }, //TODO: auto check loaded libraries #define FF_COMPONENT(name, NAME) #name, LIB##NAME##_VERSION_INT, name##_version(), name##_configuration(), name##_license() { FF_COMPONENT(avutil, AVUTIL) }, { FF_COMPONENT(avcodec, AVCODEC) }, { FF_COMPONENT(avformat, AVFORMAT) }, #if QTAV_HAVE(AVFILTER) { FF_COMPONENT(avfilter, AVFILTER) }, #endif //QTAV_HAVE(AVFILTER) #if QTAV_HAVE(AVDEVICE) { FF_COMPONENT(avdevice, AVDEVICE) }, #endif //QTAV_HAVE(AVDEVICE) #if QTAV_HAVE(AVRESAMPLE) { FF_COMPONENT(avresample, AVRESAMPLE) }, #endif //QTAV_HAVE(AVRESAMPLE) #if QTAV_HAVE(SWRESAMPLE) { FF_COMPONENT(swresample, SWRESAMPLE) }, #endif //QTAV_HAVE(SWRESAMPLE) { FF_COMPONENT(swscale, SWSCALE) }, #undef FF_COMPONENT { 0, 0, 0, 0, 0 } }; if (!info) return &components[0]; // invalid input ptr if (((ptrdiff_t)info - (ptrdiff_t)(&components[0]))%sizeof(depend_component)) return 0; const depend_component *next = info; next++; if (!next->lib) return 0; return next; } void print_library_info() { qDebug() << aboutQtAV_PlainText().toUtf8().constData(); const depend_component* info = Internal::get_depend_component(0); while (info) { if (!qstrcmp(info->lib, "avutil")) qDebug("FFmpeg/Libav configuration: %s", info->config); qDebug("Build with %s-%u.%u.%u" , info->lib , QTAV_VERSION_MAJOR(info->build_version) , QTAV_VERSION_MINOR(info->build_version) , QTAV_VERSION_PATCH(info->build_version) ); unsigned rt_version = info->rt_version; if (info->build_version != rt_version) { qWarning("Warning: %s runtime version %u.%u.%u mismatch!" , info->lib , QTAV_VERSION_MAJOR(rt_version) , QTAV_VERSION_MINOR(rt_version) , QTAV_VERSION_PATCH(rt_version) ); } info = Internal::get_depend_component(info); } } } //namespace Internal QString aboutFFmpeg_HTML() { QString text = QStringLiteral("

FFmpeg/Libav

\n"); const Internal::depend_component* info = Internal::get_depend_component(0); while (info) { text += QStringLiteral("

%1: %2-%3.%4.%5

\n") .arg(QObject::tr("Build version")) .arg(QLatin1String(info->lib)) .arg(QTAV_VERSION_MAJOR(info->build_version)) .arg(QTAV_VERSION_MINOR(info->build_version)) .arg(QTAV_VERSION_PATCH(info->build_version)) ; unsigned rt_version = info->rt_version; if (info->build_version != rt_version) { text += QStringLiteral("

%1: %2.%3.%4

\n") .arg(QObject::tr("Runtime version")) .arg(QTAV_VERSION_MAJOR(rt_version)) .arg(QTAV_VERSION_MINOR(rt_version)) .arg(QTAV_VERSION_PATCH(rt_version)) ; } text += QStringLiteral("

%1

\n

%2

\n").arg(QString::fromUtf8(info->config)).arg(QString::fromUtf8(info->license)); info = Internal::get_depend_component(info); } return text; } QString aboutQtAV_PlainText() { return aboutQtAV_HTML().remove(QRegExp(QStringLiteral("<[^>]*>"))); } QString aboutQtAV_HTML() { static QString about = QString::fromLatin1("

QtAV " QTAV_VERSION_STR_LONG "

\n" "

%1

%2

%3

" "

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

\n" "

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

\n" "

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

\n" "

%6: http://qtav.org

" ).arg(QObject::tr("Multimedia framework base on Qt and FFmpeg.\n")) .arg(QObject::tr("Distributed under the terms of LGPLv2.1 or later.\n")) .arg(QObject::tr("Shanghai University->S3 Graphics->Deepin->PPTV, Shanghai, China")) .arg(QObject::tr("Donate")) .arg(QObject::tr("Source")) .arg(QObject::tr("Home page")); return about; } void setLogLevel(LogLevel value) { Internal::gLogLevelSet = true; Internal::gLogLevel = value; } LogLevel logLevel() { return (LogLevel)Internal::gLogLevel; } void setFFmpegLogHandler(void (*callback)(void *, int, const char *, va_list)) { // libav does not check null callback if (!callback) callback = av_log_default_callback; av_log_set_callback(callback); } void setFFmpegLogLevel(const QByteArray &level) { if (level.isEmpty()) return; bool ok = false; const int value = level.toInt(&ok); if ((ok && value == 0) || level == "off" || level == "quiet") Internal::gAVLogLevel = AV_LOG_QUIET; else if (level == "panic") Internal::gAVLogLevel = AV_LOG_PANIC; else if (level == "fatal") Internal::gAVLogLevel = AV_LOG_FATAL; else if (level == "error") Internal::gAVLogLevel = AV_LOG_ERROR; else if (level.startsWith("warn")) Internal::gAVLogLevel = AV_LOG_WARNING; else if (level == "info") Internal::gAVLogLevel = AV_LOG_INFO; else if (level == "verbose") Internal::gAVLogLevel = AV_LOG_VERBOSE; else if (level == "debug") Internal::gAVLogLevel = AV_LOG_DEBUG; #ifdef AV_LOG_TRACE else if (level == "trace") Internal::gAVLogLevel = AV_LOG_TRACE; #endif else Internal::gAVLogLevel = AV_LOG_INFO; av_log_set_level(Internal::gAVLogLevel); } static void qtav_ffmpeg_log_callback(void* ctx, int level,const char* fmt, va_list vl) { // AV_LOG_DEBUG is used by ffmpeg developers if (level > Internal::gAVLogLevel) return; AVClass *c = ctx ? *(AVClass**)ctx : 0; QString qmsg = QString().sprintf("[FFmpeg:%s] ", c ? c->item_name(ctx) : "?") + QString().vsprintf(fmt, vl); qmsg = qmsg.trimmed(); if (level > AV_LOG_WARNING) qDebug() << qPrintable(qmsg); else if (level > AV_LOG_PANIC) qWarning() << qPrintable(qmsg); } QString avformatOptions() { static QString opts; if (!opts.isEmpty()) return opts; void* obj = const_cast(reinterpret_cast(avformat_get_class())); opts = Internal::optionsToString((void*)&obj); opts.append(ushort('\n')); av_register_all(); AVInputFormat *i = NULL; while ((i = av_iformat_next(i))) { QString opt(Internal::optionsToString((void*)&i->priv_class).trimmed()); if (opt.isEmpty()) continue; opts.append(QStringLiteral("options for input format %1:\n%2\n\n") .arg(QLatin1String(i->name)) .arg(opt)); } AVOutputFormat *o = NULL; while ((o = av_oformat_next(o))) { QString opt(Internal::optionsToString((void*)&o->priv_class).trimmed()); if (opt.isEmpty()) continue; opts.append(QStringLiteral("options for output format %1:\n%2\n\n") .arg(QLatin1String(o->name)) .arg(opt)); } return opts; } QString avcodecOptions() { static QString opts; if (!opts.isEmpty()) return opts; void* obj = const_cast(reinterpret_cast(avcodec_get_class())); opts = Internal::optionsToString((void*)&obj); opts.append(ushort('\n')); avcodec_register_all(); AVCodec* c = NULL; while ((c=av_codec_next(c))) { QString opt(Internal::optionsToString((void*)&c->priv_class).trimmed()); if (opt.isEmpty()) continue; opts.append(QStringLiteral("Options for codec %1:\n%2\n\n").arg(QLatin1String(c->name)).arg(opt)); } return opts; } #if 0 const QStringList& supportedInputMimeTypes() { static QStringList mimes; if (!mimes.isEmpty()) return mimes; av_register_all(); // MUST register all input/output formats AVOutputFormat *i = av_oformat_next(NULL); QStringList list; while (i) { list << QString(i->mime_type).split(QLatin1Char(','), QString::SkipEmptyParts); i = av_oformat_next(i); } foreach (const QString& v, list) { mimes.append(v.trimmed()); } mimes.removeDuplicates(); return mimes; } static QStringList s_audio_mimes, s_video_mimes, s_subtitle_mimes; static void init_supported_codec_info() { const AVCodecDescriptor* cd = avcodec_descriptor_next(NULL); while (cd) { QStringList list; if (cd->mime_types) { for (int i = 0; cd->mime_types[i]; ++i) { list.append(QString(cd->mime_types[i]).trimmed()); } } switch (cd->type) { case AVMEDIA_TYPE_AUDIO: s_audio_mimes << list; break; case AVMEDIA_TYPE_VIDEO: s_video_mimes << list; case AVMEDIA_TYPE_SUBTITLE: s_subtitle_mimes << list; default: break; } cd = avcodec_descriptor_next(cd); } s_audio_mimes.removeDuplicates(); s_video_mimes.removeDuplicates(); s_subtitle_mimes.removeDuplicates(); } const QStringList& supportedAudioMimeTypes() { if (s_audio_mimes.isEmpty()) init_supported_codec_info(); return s_audio_mimes; } const QStringList& supportedVideoMimeTypes() { if (s_video_mimes.isEmpty()) init_supported_codec_info(); return s_video_mimes; } // TODO: subtitleprocessor support const QStringList& supportedSubtitleMimeTypes() { if (s_subtitle_mimes.isEmpty()) init_supported_codec_info(); return s_subtitle_mimes; } #endif /* * AVColorSpace: * libav11 libavutil54.3.0 pixfmt.h, ffmpeg2.1*libavutil52.48.101 frame.h * ffmpeg2.5 pixfmt.h. AVFrame.colorspace * earlier versions: avcodec.h, avctx.colorspace */ ColorSpace colorSpaceFromFFmpeg(AVColorSpace cs) { switch (cs) { // from ffmpeg: order of coefficients is actually GBR case AVCOL_SPC_RGB: return ColorSpace_GBR; case AVCOL_SPC_BT709: return ColorSpace_BT709; case AVCOL_SPC_BT470BG: return ColorSpace_BT601; case AVCOL_SPC_SMPTE170M: return ColorSpace_BT601; default: return ColorSpace_Unknown; } } ColorRange colorRangeFromFFmpeg(AVColorRange cr) { switch (cr) { case AVCOL_RANGE_MPEG: return ColorRange_Limited; case AVCOL_RANGE_JPEG: return ColorRange_Full; default: return ColorRange_Unknown; } } namespace { static const struct RegisterMetaTypes { RegisterMetaTypes() { qRegisterMetaType("QtAV::MediaStatus"); } } _registerMetaTypes; } // TODO: static link. move all into 1 namespace { class InitFFmpegLog { public: InitFFmpegLog() { setFFmpegLogHandler(qtav_ffmpeg_log_callback); QtAV::setFFmpegLogLevel(qgetenv("QTAV_FFMPEG_LOG").toLower()); } }; InitFFmpegLog fflog; } } // Initialize Qt Resource System when the library is built // statically static void initResources() { Q_INIT_RESOURCE(shaders); Q_INIT_RESOURCE(QtAV); } namespace { class ResourceLoader { public: ResourceLoader() { initResources(); } }; ResourceLoader QtAV_QRCLoader; } QtAV-1.12.0/src/Statistics.cpp000066400000000000000000000061171312235004300160310ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Statistics.h" #include "utils/ring.h" namespace QtAV { Statistics::Common::Common(): available(false) , bit_rate(0) , frames(0) , frame_rate(0) { } Statistics::AudioOnly::AudioOnly(): sample_rate(0) , channels(0) , frame_size(0) , block_align(0) { } class Statistics::VideoOnly::Private : public QSharedData { public: Private() : pts(0) , history(ring(30)) {} qreal pts; ring history; }; Statistics::VideoOnly::VideoOnly(): width(0) , height(0) , coded_width(0) , coded_height(0) , gop_size(0) , d(new Private()) { } Statistics::VideoOnly::VideoOnly(const VideoOnly& v) : width(v.width) , height(v.height) , coded_width(v.coded_width) , coded_height(v.coded_height) , gop_size(v.gop_size) , d(v.d) { } Statistics::VideoOnly& Statistics::VideoOnly::operator =(const VideoOnly& v) { width = v.width; height = v.height; coded_width = v.coded_width; coded_height = v.coded_height; gop_size = v.gop_size; d = v.d; return *this; } Statistics::VideoOnly::~VideoOnly() { } qreal Statistics::VideoOnly::pts() const { return d->pts; } qint64 Statistics::VideoOnly::frameDisplayed(qreal pts) { d->pts = pts; const qint64 msecs = QDateTime::currentMSecsSinceEpoch(); const qreal t = (double)msecs/1000.0; d->history.push_back(t); return msecs; } // d->history is not thread safe! qreal Statistics::VideoOnly::currentDisplayFPS() const { if (d->history.empty()) return 0; // DO NOT use d->history.last-first const qreal dt = (double)QDateTime::currentMSecsSinceEpoch()/1000.0 - d->history.front(); // dt should be always > 0 because history stores absolute time if (qFuzzyIsNull(dt)) return 0; return (qreal)d->history.size()/dt; } Statistics::Statistics() { } Statistics::~Statistics() { } void Statistics::reset() { url = QString(); audio = Common(); video = Common(); audio_only = AudioOnly(); video_only = VideoOnly(); metadata.clear(); } } //namespace QtAV QtAV-1.12.0/src/VideoCapture.cpp000066400000000000000000000177061312235004300162770ustar00rootroot00000000000000/****************************************************************************** VideoCapture.cpp: description Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoCapture.h" #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif #include "utils/Logger.h" namespace QtAV { Q_GLOBAL_STATIC(QThreadPool, videoCaptureThreadPool) static bool app_is_dieing = false; // TODO: cancel if qapp is quit class CaptureTask : public QRunnable { public: CaptureTask(VideoCapture* c) : cap(c) , save(true) , original_fmt(false) , quality(-1) , format(QStringLiteral("PNG")) , qfmt(QImage::Format_ARGB32) { setAutoDelete(true); } virtual void run() { if (app_is_dieing) { qDebug("app is dieing. cancel capture task %p", this); return; } QImage image(frame.toImage()); if (image.isNull()) { qWarning("Failed to convert to QImage"); return; } QMetaObject::invokeMethod(cap, "imageCaptured", Q_ARG(QImage, image)); if (!save) return; bool main_thread = QThread::currentThread() == qApp->thread(); qDebug("capture task running in thread %p [main thread=%d]", QThread::currentThreadId(), main_thread); if (!QDir(dir).exists()) { if (!QDir().mkpath(dir)) { qWarning("Failed to create capture dir [%s]", qPrintable(dir)); QMetaObject::invokeMethod(cap, "failed"); return; } } name += QString::number(frame.timestamp(), 'f', 3); QString path(dir + QStringLiteral("/") + name + QStringLiteral(".")); if (original_fmt) { if (!frame.constBits(0)) { frame = frame.to(frame.format()); } path.append(frame.format().name()); qDebug("Saving capture to %s", qPrintable(path)); QFile file(path); if (!file.open(QIODevice::WriteOnly)) { qWarning("VideoCapture is failed to open file %s", qPrintable(path)); QMetaObject::invokeMethod(cap, "failed"); return; } if (file.write(frame.frameData()) <= 0) { qWarning("VideoCapture is failed to write captured frame with original format"); QMetaObject::invokeMethod(cap, "failed"); file.close(); return; } file.close(); QMetaObject::invokeMethod(cap, "saved", Q_ARG(QString, path)); return; } if (image.isNull()) return; path.append(format.toLower()); qDebug("Saving capture to %s", qPrintable(path)); bool ok = image.save(path, format.toLatin1().constData(), quality); if (!ok) { qWarning("Failed to save capture"); QMetaObject::invokeMethod(cap, "failed"); } QMetaObject::invokeMethod(cap, "saved", Q_ARG(QString, path)); } VideoCapture *cap; bool save; bool original_fmt; int quality; QString format, dir, name; QImage::Format qfmt; VideoFrame frame; }; VideoCapture::VideoCapture(QObject *parent) : QObject(parent) , async(true) , auto_save(true) , original_fmt(false) , qfmt(QImage::Format_ARGB32) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) dir = QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); #else dir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); #endif if (dir.isEmpty()) dir = qApp->applicationDirPath() + QStringLiteral("/capture"); fmt = QStringLiteral("PNG"); qual = -1; // seems no direct connection is fine too connect(qApp, SIGNAL(aboutToQuit()), SLOT(handleAppQuit()), Qt::DirectConnection); } void VideoCapture::setAsync(bool value) { if (async == value) return; async = value; Q_EMIT asyncChanged(); } bool VideoCapture::isAsync() const { return async; } void VideoCapture::setAutoSave(bool value) { if (auto_save == value) return; auto_save = value; Q_EMIT autoSaveChanged(); } bool VideoCapture::autoSave() const { return auto_save; } void VideoCapture::setOriginalFormat(bool value) { if (original_fmt == value) return; original_fmt = value; Q_EMIT originalFormatChanged(); } bool VideoCapture::isOriginalFormat() const { return original_fmt; } void VideoCapture::handleAppQuit() { app_is_dieing = true; // TODO: how to cancel? since qt5.2, we can use clear() #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) videoCaptureThreadPool()->clear(); #endif videoCaptureThreadPool()->setExpiryTimeout(0); videoCaptureThreadPool()->waitForDone(); } void VideoCapture::capture() { Q_EMIT requested(); } void VideoCapture::start() { Q_EMIT frameAvailable(frame); if (!frame.isValid() || !frame.constBits(0)) { // if frame is always cloned, then size is at least width*height qDebug("Captured frame from hardware decoder surface."); } CaptureTask *task = new CaptureTask(this); // copy properties so the task will not be affect even if VideoCapture properties changed task->save = autoSave(); task->original_fmt = original_fmt; task->quality = qual; task->dir = dir; task->name = name; task->format = fmt; task->qfmt = qfmt; task->frame = frame; //copy here and it's safe in capture thread because start() is called immediatly after setVideoFrame if (isAsync()) { videoCaptureThreadPool()->start(task); } else { task->run(); delete task; } } void VideoCapture::setSaveFormat(const QString &format) { if (format.toLower() == fmt.toLower()) return; fmt = format; Q_EMIT saveFormatChanged(); } QString VideoCapture::saveFormat() const { return fmt; } void VideoCapture::setQuality(int value) { if (qual == value) return; qual = value; Q_EMIT qualityChanged(); } int VideoCapture::quality() const { return qual; } void VideoCapture::setCaptureName(const QString &value) { if (name == value) return; name = value; Q_EMIT captureNameChanged(); } QString VideoCapture::captureName() const { return name; } void VideoCapture::setCaptureDir(const QString &value) { if (dir == value) return; dir = value; Q_EMIT captureDirChanged(); } QString VideoCapture::captureDir() const { return dir; } /* * If the frame is not created for direct rendering, then the frame data is already deep copied, so detach is enough. * TODO: map frame from texture etc. */ void VideoCapture::setVideoFrame(const VideoFrame &frame) { // parameter in ready(QtAV::VideoFrame) ensure we can access the frame without lock /* * clone here may block VideoThread. But if not clone here, the frame may be * modified outside and is not safe. */ this->frame = frame.clone(); // TODO: no clone, use detach() } } //namespace QtAV QtAV-1.12.0/src/VideoFormat.cpp000066400000000000000000001101761312235004300161170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoFormat.h" #include #include #ifndef QT_NO_DEBUG_STREAM #include #endif #include "QtAV/private/AVCompat.h" extern "C" { #include } #define FF_HAS_YUV12BITS FFMPEG_MODULE_CHECK(LIBAVUTIL, 51, 73, 101) #if (Q_BYTE_ORDER == Q_BIG_ENDIAN) #define PIXFMT_NE(B, L) VideoFormat::Format_##B #else #define PIXFMT_NE(B, L) VideoFormat::Format_##L #endif // ffmpeg3.0 151aa2e/ libav>11 2268db2 #if AV_MODULE_CHECK(LIBAVUTIL, 55, 0, 0, 0, 100) #define DESC_VAL(X) (X) #else #define DESC_VAL(X) (X##_minus1 + 1) #endif namespace QtAV { class VideoFormatPrivate : public QSharedData { public: VideoFormatPrivate(VideoFormat::PixelFormat fmt) : pixfmt(fmt) , pixfmt_ff(QTAV_PIX_FMT_C(NONE)) , qpixfmt(QImage::Format_Invalid) , planes(0) , bpp(0) , bpp_pad(0) , bpc(0) , pixdesc(0) { if (fmt == VideoFormat::Format_Invalid) { pixfmt_ff = QTAV_PIX_FMT_C(NONE); qpixfmt = QImage::Format_Invalid; return; } init(fmt); } VideoFormatPrivate(AVPixelFormat fmt) : pixfmt(VideoFormat::Format_Invalid) , pixfmt_ff(fmt) , qpixfmt(QImage::Format_Invalid) , planes(0) , bpp(0) , bpp_pad(0) , bpc(0) , pixdesc(0) { init(fmt); } VideoFormatPrivate(QImage::Format fmt) : pixfmt(VideoFormat::Format_Invalid) , pixfmt_ff(QTAV_PIX_FMT_C(NONE)) , qpixfmt(fmt) , planes(0) , bpp(0) , bpp_pad(0) , bpc(0) , pixdesc(0) { init(fmt); } void init(VideoFormat::PixelFormat fmt) { pixfmt = fmt; pixfmt_ff = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(pixfmt); qpixfmt = VideoFormat::imageFormatFromPixelFormat(pixfmt); init(); } void init(QImage::Format fmt) { qpixfmt = fmt; pixfmt = VideoFormat::pixelFormatFromImageFormat(fmt); pixfmt_ff = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(pixfmt); init(); } void init(AVPixelFormat fffmt) { pixfmt_ff = fffmt; pixfmt = VideoFormat::pixelFormatFromFFmpeg(pixfmt_ff); qpixfmt = VideoFormat::imageFormatFromPixelFormat(pixfmt); init(); } void init() { // TODO: what if other formats not supported by ffmpeg? give attributes in QtAV? if (pixfmt_ff == QTAV_PIX_FMT_C(NONE)) { qWarning("Invalid pixel format"); return; } planes = qMax(av_pix_fmt_count_planes(pixfmt_ff), 0); bpps.reserve(planes); channels.reserve(planes); bpps.resize(planes); channels.resize(planes); pixdesc = const_cast(av_pix_fmt_desc_get(pixfmt_ff)); if (!pixdesc) return; initBpp(); } QString name() const { return QLatin1String(av_get_pix_fmt_name(pixfmt_ff)); } int flags() const { if (!pixdesc) return 0; return pixdesc->flags; } int bytesPerLine(int width, int plane) const { return av_image_get_linesize(pixfmt_ff, width, plane); } VideoFormat::PixelFormat pixfmt; AVPixelFormat pixfmt_ff; QImage::Format qpixfmt; quint8 planes; quint8 bpp; quint8 bpp_pad; quint8 bpc; QVector bpps; QVector channels; AVPixFmtDescriptor *pixdesc; private: // from libavutil/pixdesc.c void initBpp() { //TODO: call later when bpp need bpp = 0; bpp_pad = 0; //libavutil55: depth, step, offset bpc = DESC_VAL(pixdesc->comp[0].depth); const int log2_pixels = pixdesc->log2_chroma_w + pixdesc->log2_chroma_h; int steps[4]; memset(steps, 0, sizeof(steps)); for (int c = 0; c < pixdesc->nb_components; c++) { const AVComponentDescriptor *comp = &pixdesc->comp[c]; int s = c == 1 || c == 2 ? 0 : log2_pixels; //? bpps[comp->plane] += DESC_VAL(comp->depth); steps[comp->plane] = DESC_VAL(comp->step) << s; channels[comp->plane] += 1; bpp += DESC_VAL(comp->depth) << s; if (DESC_VAL(comp->depth) != bpc) bpc = 0; } for (int i = 0; i < planes; ++i) { bpp_pad += steps[i]; } if (!(pixdesc->flags & AV_PIX_FMT_FLAG_BITSTREAM)) bpp_pad *= 8; bpp >>= log2_pixels; bpp_pad >>= log2_pixels; } }; // TODO: use FFmpeg macros to get right endian static const struct { VideoFormat::PixelFormat fmt; AVPixelFormat ff; //int } pixfmt_map[] = { { VideoFormat::Format_YUV420P, QTAV_PIX_FMT_C(YUV420P) }, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) { VideoFormat::Format_YV12, QTAV_PIX_FMT_C(YUV420P) }, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) { VideoFormat::Format_YUYV, QTAV_PIX_FMT_C(YUYV422) }, //?? ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr { VideoFormat::Format_RGB24, QTAV_PIX_FMT_C(RGB24) }, ///< packed RGB 8:8:8, 24bpp, RGBRGB... { VideoFormat::Format_BGR24, QTAV_PIX_FMT_C(BGR24) }, ///< packed RGB 8:8:8, 24bpp, BGRBGR... { VideoFormat::Format_YUV422P, QTAV_PIX_FMT_C(YUV422P)}, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) { VideoFormat::Format_YUV444P, QTAV_PIX_FMT_C(YUV444P) }, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) { VideoFormat::Format_YUV410P, QTAV_PIX_FMT_C(YUV410P) }, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) { VideoFormat::Format_YUV411P, QTAV_PIX_FMT_C(YUV411P) }, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) { VideoFormat::Format_Y8, QTAV_PIX_FMT_C(GRAY8) }, ///< Y , 8bpp //QTAV_PIX_FMT_C(MONOWHITE), ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb //QTAV_PIX_FMT_C(MONOBLACK), ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb //QTAV_PIX_FMT_C(PAL8), ///< 8 bit with PIX_FMT_RGB32 palette { VideoFormat::Format_YUV420P, QTAV_PIX_FMT_C(YUVJ420P) }, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of PIX_FMT_YUV420P and setting color_range //QTAV_PIX_FMT_C(YUVJ422P), ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of PIX_FMT_YUV422P and setting color_range //QTAV_PIX_FMT_C(YUVJ444P), ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of PIX_FMT_YUV444P and setting color_range //QTAV_PIX_FMT_C(XVMC_MPEG2_MC),///< XVideo Motion Acceleration via common packet passing //QTAV_PIX_FMT_C(XVMC_MPEG2_IDCT), { VideoFormat::Format_UYVY, QTAV_PIX_FMT_C(UYVY422) }, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 //QTAV_PIX_FMT_C(UYYVYY411), ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 //QTAV_PIX_FMT_C(BGR8), ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) //QTAV_PIX_FMT_C(BGR4), ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits //QTAV_PIX_FMT_C(BGR4_BYTE), ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) //QTAV_PIX_FMT_C(RGB8), ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) //QTAV_PIX_FMT_C(RGB4), ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits //QTAV_PIX_FMT_C(RGB4_BYTE), ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) { VideoFormat::Format_NV12, QTAV_PIX_FMT_C(NV12) }, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) { VideoFormat::Format_NV21, QTAV_PIX_FMT_C(NV21) }, ///< as above, but U and V bytes are swapped { VideoFormat::Format_ARGB32, QTAV_PIX_FMT_C(ARGB) }, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB... { VideoFormat::Format_RGBA32, QTAV_PIX_FMT_C(RGBA) }, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA... { VideoFormat::Format_ABGR32, QTAV_PIX_FMT_C(ABGR) }, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR... { VideoFormat::Format_BGRA32, QTAV_PIX_FMT_C(BGRA) }, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA... //QTAV_PIX_FMT_C(GRAY16BE), ///< Y , 16bpp, big-endian { VideoFormat::Format_Y16, QTAV_PIX_FMT_C(GRAY16LE) }, ///< Y , 16bpp, little-endian //QTAV_PIX_FMT_C(YUV440P), ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) //QTAV_PIX_FMT_C(YUVJ440P), ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of PIX_FMT_YUV440P and setting color_range //QTAV_PIX_FMT_C(YUVA420P), ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) /* QTAV_PIX_FMT_C(VDPAU_H264,///< H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers QTAV_PIX_FMT_C(VDPAU_MPEG1,///< MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers QTAV_PIX_FMT_C(VDPAU_MPEG2,///< MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers QTAV_PIX_FMT_C(VDPAU_WMV3,///< WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers QTAV_PIX_FMT_C(VDPAU_VC1, ///< VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ { VideoFormat::Format_RGB48BE, QTAV_PIX_FMT_C(RGB48BE) }, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian { VideoFormat::Format_RGB48LE, QTAV_PIX_FMT_C(RGB48LE) }, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian { VideoFormat::Format_RGB565, QTAV_PIX_FMT_C(RGB565) }, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), native-endian { VideoFormat::Format_RGB555, QTAV_PIX_FMT_C(RGB555) }, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), native-endian, be: most significant bit to 1 { VideoFormat::Format_BGR565, QTAV_PIX_FMT_C(BGR565) }, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), native-endian { VideoFormat::Format_BGR555, QTAV_PIX_FMT_C(BGR555) }, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), native-endian, be: most significant bit to 1 /* QTAV_PIX_FMT_C(VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers QTAV_PIX_FMT_C(VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers QTAV_PIX_FMT_C(VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ #if FF_HAS_YUV12BITS { VideoFormat::Format_YUV420P16LE, QTAV_PIX_FMT_C(YUV420P16LE) }, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian { VideoFormat::Format_YUV420P16BE, QTAV_PIX_FMT_C(YUV420P16BE) }, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian { VideoFormat::Format_YUV422P16LE, QTAV_PIX_FMT_C(YUV422P16LE) }, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian { VideoFormat::Format_YUV422P16BE, QTAV_PIX_FMT_C(YUV422P16BE) }, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian { VideoFormat::Format_YUV444P16LE, QTAV_PIX_FMT_C(YUV444P16LE) }, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian { VideoFormat::Format_YUV444P16BE, QTAV_PIX_FMT_C(YUV444P16BE) }, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian #endif //FF_HAS_YUV12BITS /* QTAV_PIX_FMT_C(VDPAU_MPEG4, ///< MPEG4 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers QTAV_PIX_FMT_C(DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer QTAV_PIX_FMT_C(RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), little-endian, most significant bits to 0 QTAV_PIX_FMT_C(RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), big-endian, most significant bits to 0 QTAV_PIX_FMT_C(BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), little-endian, most significant bits to 1 QTAV_PIX_FMT_C(BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), big-endian, most significant bits to 1 QTAV_PIX_FMT_C(GRAY8A, ///< 8bit gray, 8bit alpha */ { VideoFormat::Format_BGR48BE, QTAV_PIX_FMT_C(BGR48BE) }, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian { VideoFormat::Format_BGR48LE, QTAV_PIX_FMT_C(BGR48LE) }, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian //the following 10 formats have the disadvantage of needing 1 format for each bit depth, thus //If you want to support multiple bit depths, then using QTAV_PIX_FMT_C(YUV420P16* with the bpp stored separately //is better // the ffmpeg QtAV can build against( >= 0.9) supports 9,10 bits { VideoFormat::Format_YUV420P9BE, QTAV_PIX_FMT_C(YUV420P9BE) }, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian { VideoFormat::Format_YUV420P9LE, QTAV_PIX_FMT_C(YUV420P9LE) }, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian { VideoFormat::Format_YUV420P10BE, QTAV_PIX_FMT_C(YUV420P10BE) },///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian { VideoFormat::Format_YUV420P10LE, QTAV_PIX_FMT_C(YUV420P10LE) },///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian { VideoFormat::Format_YUV422P10BE, QTAV_PIX_FMT_C(YUV422P10BE) },///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian { VideoFormat::Format_YUV422P10LE, QTAV_PIX_FMT_C(YUV422P10LE) },///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian { VideoFormat::Format_YUV444P9BE, QTAV_PIX_FMT_C(YUV444P9BE) }, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian { VideoFormat::Format_YUV444P9LE, QTAV_PIX_FMT_C(YUV444P9LE) }, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian { VideoFormat::Format_YUV444P10BE, QTAV_PIX_FMT_C(YUV444P10BE) },///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian { VideoFormat::Format_YUV444P10LE, QTAV_PIX_FMT_C(YUV444P10LE) },///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian { VideoFormat::Format_YUV422P9BE, QTAV_PIX_FMT_C(YUV422P9BE) }, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian { VideoFormat::Format_YUV422P9LE, QTAV_PIX_FMT_C(YUV422P9LE) }, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian //QTAV_PIX_FMT_C(VDA_VLD, ///< hardware decoding through VDA /* #ifdef AV_PIX_FMT_ABI_GIT_MASTER QTAV_PIX_FMT_C(RGBA64BE) }, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian QTAV_PIX_FMT_C(RGBA64LE) }, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian QTAV_PIX_FMT_C(BGRA64BE) }, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian QTAV_PIX_FMT_C(BGRA64LE) }, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian #endif QTAV_PIX_FMT_C(GBRP, ///< planar GBR 4:4:4 24bpp QTAV_PIX_FMT_C(GBRP9BE, ///< planar GBR 4:4:4 27bpp, big-endian QTAV_PIX_FMT_C(GBRP9LE, ///< planar GBR 4:4:4 27bpp, little-endian QTAV_PIX_FMT_C(GBRP10BE, ///< planar GBR 4:4:4 30bpp, big-endian QTAV_PIX_FMT_C(GBRP10LE, ///< planar GBR 4:4:4 30bpp, little-endian QTAV_PIX_FMT_C(GBRP16BE, ///< planar GBR 4:4:4 48bpp, big-endian QTAV_PIX_FMT_C(GBRP16LE, ///< planar GBR 4:4:4 48bpp, little-endian */ /** * duplicated pixel formats for compatibility with libav. * FFmpeg supports these formats since May 8 2012 and Jan 28 2012 (commits f9ca1ac7 and 143a5c55) * Libav added them Oct 12 2012 with incompatible values (commit 6d5600e85) */ /* QTAV_PIX_FMT_C(YUVA422P_LIBAV, ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples) QTAV_PIX_FMT_C(YUVA444P_LIBAV, ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples) QTAV_PIX_FMT_C(YUVA420P9BE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian QTAV_PIX_FMT_C(YUVA420P9LE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian QTAV_PIX_FMT_C(YUVA422P9BE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian QTAV_PIX_FMT_C(YUVA422P9LE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian QTAV_PIX_FMT_C(YUVA444P9BE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian QTAV_PIX_FMT_C(YUVA444P9LE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian // Y: 2bytes/pix U: 1, V: 1 QTAV_PIX_FMT_C(YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) QTAV_PIX_FMT_C(YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) QTAV_PIX_FMT_C(YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) QTAV_PIX_FMT_C(YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) QTAV_PIX_FMT_C(YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) QTAV_PIX_FMT_C(YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) QTAV_PIX_FMT_C(YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) */ //QTAV_PIX_FMT_C(VDPAU, ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface // doc/APIChanges: 2014-04-07 - 0a1cc04 / 8b17243 - lavu 52.75.100 / 53.11.0 - pixfmt.h //Add AV_PIX_FMT_YVYU422 pixel format. #if (FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 75, 100) || LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 11, 0)) { VideoFormat::Format_YVYU, QTAV_PIX_FMT_C(YVYU422) }, #endif // 2014-03-16 - 6b1ca17 / 1481d24 - lavu 52.67.100 / 53.6.0 before ffmpeg2.2 libav11 RGBA64_LIBAV #if (QTAV_USE_FFMPEG(LIBAVUTIL) || LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 6, 0)) { VideoFormat::Format_RGBA64BE, QTAV_PIX_FMT_C(RGBA64BE)}, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian { VideoFormat::Format_RGBA64LE, QTAV_PIX_FMT_C(RGBA64LE)}, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian { VideoFormat::Format_BGRA64BE, QTAV_PIX_FMT_C(BGRA64BE)}, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian { VideoFormat::Format_BGRA64LE, QTAV_PIX_FMT_C(BGRA64LE)}, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian #endif #if QTAV_USE_FFMPEG(LIBAVUTIL) //still use rgba formats but check hasAplha is required { VideoFormat::Format_ARGB32, QTAV_PIX_FMT_C(0RGB)}, ///< packed RGB 8:8:8, 32bpp, 0RGB0RGB... { VideoFormat::Format_RGBA32, QTAV_PIX_FMT_C(RGB0)}, ///< packed RGB 8:8:8, 32bpp, RGB0RGB0... { VideoFormat::Format_ABGR32, QTAV_PIX_FMT_C(0BGR)}, ///< packed BGR 8:8:8, 32bpp, 0BGR0BGR... { VideoFormat::Format_BGRA32, QTAV_PIX_FMT_C(BGR0)}, ///< packed BGR 8:8:8, 32bpp, BGR0BGR0... #endif // //QTAV_PIX_FMT_C(YUVA444P, ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples) //QTAV_PIX_FMT_C(YUVA422P, ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples) #if FF_HAS_YUV12BITS { VideoFormat::Format_YUV420P12BE, QTAV_PIX_FMT_C(YUV420P12BE) }, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian { VideoFormat::Format_YUV420P12LE, QTAV_PIX_FMT_C(YUV420P12LE) }, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian { VideoFormat::Format_YUV420P14BE, QTAV_PIX_FMT_C(YUV420P14BE) }, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian { VideoFormat::Format_YUV420P14LE, QTAV_PIX_FMT_C(YUV420P14LE) }, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian { VideoFormat::Format_YUV422P12BE, QTAV_PIX_FMT_C(YUV422P12BE) }, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian { VideoFormat::Format_YUV422P12LE, QTAV_PIX_FMT_C(YUV422P12LE) }, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian { VideoFormat::Format_YUV422P14BE, QTAV_PIX_FMT_C(YUV422P14BE) }, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian { VideoFormat::Format_YUV422P14LE, QTAV_PIX_FMT_C(YUV422P14LE) }, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian { VideoFormat::Format_YUV444P12BE, QTAV_PIX_FMT_C(YUV444P12BE) }, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian { VideoFormat::Format_YUV444P12LE, QTAV_PIX_FMT_C(YUV444P12LE) }, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian { VideoFormat::Format_YUV444P14BE, QTAV_PIX_FMT_C(YUV444P14BE) }, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian { VideoFormat::Format_YUV444P14LE, QTAV_PIX_FMT_C(YUV444P14LE) }, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian #endif //FF_HAS_YUV12BITS /* QTAV_PIX_FMT_C(GBRP12BE, ///< planar GBR 4:4:4 36bpp, big-endian QTAV_PIX_FMT_C(GBRP12LE, ///< planar GBR 4:4:4 36bpp, little-endian QTAV_PIX_FMT_C(GBRP14BE, ///< planar GBR 4:4:4 42bpp, big-endian QTAV_PIX_FMT_C(GBRP14LE, ///< planar GBR 4:4:4 42bpp, little-endian */ // native endian formats // QTAV_PIX_FMT_C(RGB32) is depends on byte order, ARGB for BE, BGRA for LE { VideoFormat::Format_RGB32, QTAV_PIX_FMT_C(RGB32) }, //auto endian // AV_PIX_FMT_BGR32_1: bgra, argb { VideoFormat::Format_BGR32, QTAV_PIX_FMT_C(BGR32) }, //auto endian { VideoFormat::Format_RGB48, QTAV_PIX_FMT_C(RGB48) }, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian { VideoFormat::Format_BGR48, QTAV_PIX_FMT_C(BGR48) }, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian #if QTAV_USE_FFMPEG(LIBAVUTIL) { VideoFormat::Format_RGBA64, QTAV_PIX_FMT_C(RGBA64) }, { VideoFormat::Format_BGRA64, QTAV_PIX_FMT_C(BGRA64) }, #endif //QTAV_USE_FFMPEG(LIBAVUTIL) { VideoFormat::Format_VYUY, QTAV_PIX_FMT_C(UYVY422) }, // FIXME: hack for invalid ffmpeg formats { VideoFormat::Format_VYU, QTAV_PIX_FMT_C(RGB32) }, #ifdef AV_PIX_FMT_XYZ12 { VideoFormat::Format_XYZ12, QTAV_PIX_FMT_C(XYZ12) }, { VideoFormat::Format_XYZ12LE, QTAV_PIX_FMT_C(XYZ12LE) }, { VideoFormat::Format_XYZ12BE, QTAV_PIX_FMT_C(XYZ12BE) }, #endif { VideoFormat::Format_Invalid, QTAV_PIX_FMT_C(NONE) }, }; VideoFormat::PixelFormat VideoFormat::pixelFormatFromFFmpeg(int ff) { for (unsigned int i = 0; i < sizeof(pixfmt_map)/sizeof(pixfmt_map[0]); ++i) { if (pixfmt_map[i].ff == ff) return pixfmt_map[i].fmt; } return VideoFormat::Format_Invalid; } int VideoFormat::pixelFormatToFFmpeg(VideoFormat::PixelFormat fmt) { for (unsigned int i = 0; i < sizeof(pixfmt_map)/sizeof(pixfmt_map[0]); ++i) { if (pixfmt_map[i].fmt == fmt) return pixfmt_map[i].ff; } return QTAV_PIX_FMT_C(NONE); } QVector VideoFormat::pixelFormatsFFmpeg() { static QVector sFmts; if (sFmts.isEmpty()) { const AVPixFmtDescriptor *desc = NULL; while ((desc = av_pix_fmt_desc_next(desc))) { if ((desc->flags & AV_PIX_FMT_FLAG_HWACCEL) == AV_PIX_FMT_FLAG_HWACCEL) continue; sFmts.append(av_pix_fmt_desc_get_id(desc)); } } return sFmts; } /*! Returns a video pixel format equivalent to an image \a format. If there is no equivalent format VideoFormat::InvalidType is returned instead. \note In general \l QImage does not handle YUV formats. */ static const struct { VideoFormat::PixelFormat fmt; QImage::Format qfmt; } qpixfmt_map[] = { // QImage::Format_ARGB32: 0xAARRGGBB, VideoFormat::Format_BGRA32: layout is BBGGRRAA { PIXFMT_NE(ARGB32, BGRA32), QImage::Format_ARGB32 }, { VideoFormat::Format_RGB32, QImage::Format_ARGB32 }, { VideoFormat::Format_RGB32, QImage::Format_RGB32 }, #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) { VideoFormat::Format_RGBA32, QImage::Format_RGBA8888 }, //be 0xRRGGBBAA, le 0xAABBGGRR #endif { VideoFormat::Format_RGB565, QImage::Format_RGB16 }, { VideoFormat::Format_BGR565, (QImage::Format)-QImage::Format_RGB16 }, { VideoFormat::Format_RGB555, QImage::Format_RGB555 }, { VideoFormat::Format_BGR555, (QImage::Format)-QImage::Format_RGB555 }, { VideoFormat::Format_RGB24, QImage::Format_RGB888 }, { VideoFormat::Format_BGR24, (QImage::Format)-QImage::Format_RGB888 }, { VideoFormat::Format_Invalid, QImage::Format_Invalid } }; VideoFormat::PixelFormat VideoFormat::pixelFormatFromImageFormat(QImage::Format format) { for (int i = 0; qpixfmt_map[i].fmt != Format_Invalid; ++i) { if (qpixfmt_map[i].qfmt == format) return qpixfmt_map[i].fmt; } return Format_Invalid; } QImage::Format VideoFormat::imageFormatFromPixelFormat(PixelFormat format) { for (int i = 0; qpixfmt_map[i].fmt != Format_Invalid; ++i) { if (qpixfmt_map[i].fmt == format) return qpixfmt_map[i].qfmt; } return QImage::Format_Invalid; } VideoFormat::VideoFormat(PixelFormat format) :d(new VideoFormatPrivate(format)) { } VideoFormat::VideoFormat(int formatFF) :d(new VideoFormatPrivate((AVPixelFormat)formatFF)) { } VideoFormat::VideoFormat(QImage::Format fmt) :d(new VideoFormatPrivate(fmt)) { } VideoFormat::VideoFormat(const QString &name) :d(new VideoFormatPrivate(av_get_pix_fmt(name.toUtf8().constData()))) { } VideoFormat::VideoFormat(const VideoFormat &other) :d(other.d) { } VideoFormat::~VideoFormat() { } /*! Assigns \a other to this VideoFormat implementation. */ VideoFormat& VideoFormat::operator=(const VideoFormat &other) { d = other.d; return *this; } VideoFormat& VideoFormat::operator =(VideoFormat::PixelFormat fmt) { d = new VideoFormatPrivate(fmt); return *this; } VideoFormat& VideoFormat::operator =(QImage::Format qpixfmt) { d = new VideoFormatPrivate(qpixfmt); return *this; } VideoFormat& VideoFormat::operator =(int fffmt) { d = new VideoFormatPrivate((AVPixelFormat)fffmt); return *this; } bool VideoFormat::operator==(const VideoFormat &other) const { return d->pixfmt_ff == other.d->pixfmt_ff; } bool VideoFormat::operator==(VideoFormat::PixelFormat fmt) const { return d->pixfmt == fmt; } bool VideoFormat::operator==(QImage::Format qpixfmt) const { return d->qpixfmt == qpixfmt; } bool VideoFormat::operator==(int fffmt) const { return d->pixfmt_ff == fffmt; } bool VideoFormat::operator!=(const VideoFormat& other) const { return !(*this == other); } bool VideoFormat::operator!=(VideoFormat::PixelFormat fmt) const { return d->pixfmt != fmt; } bool VideoFormat::operator!=(QImage::Format qpixfmt) const { return d->qpixfmt != qpixfmt; } bool VideoFormat::operator!=(int fffmt) const { return d->pixfmt_ff != fffmt; } bool VideoFormat::isValid() const { return d->pixfmt_ff != QTAV_PIX_FMT_C(NONE) || d->pixfmt != Format_Invalid; } VideoFormat::PixelFormat VideoFormat::pixelFormat() const { return d->pixfmt; } int VideoFormat::pixelFormatFFmpeg() const { return d->pixfmt_ff; } QImage::Format VideoFormat::imageFormat() const { return d->qpixfmt; } QString VideoFormat::name() const { return d->name(); } void VideoFormat::setPixelFormat(PixelFormat format) { d->pixfmt = format; d->init(format); } void VideoFormat::setPixelFormatFFmpeg(int format) { d->pixfmt_ff = (AVPixelFormat)format; d->init((AVPixelFormat)format); } int VideoFormat::channels() const { if (!d->pixdesc) return 0; return d->pixdesc->nb_components; } int VideoFormat::channels(int plane) const { if (plane > d->channels.size()) return 0; return d->channels[plane]; } int VideoFormat::planeCount() const { return d->planes; } int VideoFormat::bitsPerPixel() const { return d->bpp; } int VideoFormat::bitsPerPixelPadded() const { return d->bpp_pad; } int VideoFormat::bitsPerPixel(int plane) const { //must be a valid index position in the vector if (plane >= d->bpps.size()) return 0; return d->bpps[plane]; } int VideoFormat::bytesPerPixel() const { return (bitsPerPixel() + 7) >> 3; } int VideoFormat::bytesPerPixel(int plane) const { return (bitsPerPixel(plane) + 7) >> 3; } int VideoFormat::bitsPerComponent() const { return d->bpc; } int VideoFormat::bytesPerLine(int width, int plane) const { return d->bytesPerLine(width, plane); } int VideoFormat::chromaWidth(int lumaWidth) const { return -((-lumaWidth) >> d->pixdesc->log2_chroma_w); } int VideoFormat::chromaHeight(int lumaHeight) const { return -((-lumaHeight) >> d->pixdesc->log2_chroma_h); } int VideoFormat::width(int lumaWidth, int plane) const { if (plane <= 0) return lumaWidth; return chromaWidth(lumaWidth); } int VideoFormat::height(int lumaHeight, int plane) const { if (plane <= 0) return lumaHeight; return chromaHeight(lumaHeight); } qreal VideoFormat::normalizedWidth(int plane) const { if (plane <= 0) return 1.0; return 1.0/std::pow(2.0, qreal(d->pixdesc->log2_chroma_w)); } qreal VideoFormat::normalizedHeight(int plane) const { if (plane <= 0) return 1.0; return 1.0/std::pow(2.0, qreal(d->pixdesc->log2_chroma_h)); } // test AV_PIX_FMT_FLAG_XXX bool VideoFormat::isBigEndian() const { return (d->flags() & AV_PIX_FMT_FLAG_BE) == AV_PIX_FMT_FLAG_BE; } bool VideoFormat::hasPalette() const { return (d->flags() & AV_PIX_FMT_FLAG_PAL) == AV_PIX_FMT_FLAG_PAL; } bool VideoFormat::isPseudoPaletted() const { return (d->flags() & AV_PIX_FMT_FLAG_PSEUDOPAL) == AV_PIX_FMT_FLAG_PSEUDOPAL; } bool VideoFormat::isBitStream() const { return (d->flags() & AV_PIX_FMT_FLAG_BITSTREAM) == AV_PIX_FMT_FLAG_BITSTREAM; } bool VideoFormat::isHWAccelerated() const { return (d->flags() & AV_PIX_FMT_FLAG_HWACCEL) == AV_PIX_FMT_FLAG_HWACCEL; } bool VideoFormat::isPlanar() const { return (d->flags() & AV_PIX_FMT_FLAG_PLANAR) == AV_PIX_FMT_FLAG_PLANAR; } bool VideoFormat::isRGB() const { return (d->flags() & AV_PIX_FMT_FLAG_RGB) == AV_PIX_FMT_FLAG_RGB && d->pixfmt != Format_VYU; } bool VideoFormat::isXYZ() const { return d->pixfmt == Format_XYZ12 || d->pixfmt == Format_XYZ12LE || d->pixfmt == Format_XYZ12BE; } bool VideoFormat::hasAlpha() const { return (d->flags() & AV_PIX_FMT_FLAG_ALPHA) == AV_PIX_FMT_FLAG_ALPHA; } bool VideoFormat::isPlanar(PixelFormat pixfmt) { return pixfmt == Format_YUV420P || pixfmt == Format_NV12 || pixfmt == Format_NV21 || pixfmt == Format_YV12 || pixfmt == Format_YUV410P || pixfmt == Format_YUV411P || pixfmt == Format_YUV422P || pixfmt == Format_YUV444P || pixfmt == Format_AYUV444 || pixfmt == Format_IMC1 || pixfmt == Format_IMC2 || pixfmt == Format_IMC3 || pixfmt == Format_IMC4 ; } bool VideoFormat::isRGB(PixelFormat pixfmt) { return pixfmt == Format_RGB32 || pixfmt == Format_ARGB32 || pixfmt == Format_RGB24 || pixfmt == Format_BGRA32 || pixfmt == Format_ABGR32 || pixfmt == Format_RGBA32 || pixfmt == Format_BGR565 || pixfmt == Format_RGB555 || pixfmt == Format_RGB565 || pixfmt == Format_BGR24 || pixfmt == Format_BGR32 || pixfmt == Format_BGR555 || pixfmt == Format_RGB48 || pixfmt == Format_RGB48LE || pixfmt == Format_RGB48BE || pixfmt == Format_BGR48 || pixfmt == Format_BGR48LE || pixfmt == Format_BGR48BE || pixfmt == Format_RGBA64 || pixfmt == Format_RGBA64LE || pixfmt == Format_RGBA64BE || pixfmt == Format_BGRA64 || pixfmt == Format_BGRA64LE || pixfmt == Format_BGRA64BE ; } bool VideoFormat::hasAlpha(PixelFormat pixfmt) { return pixfmt == Format_ARGB32 || pixfmt == Format_BGRA32 || pixfmt == Format_AYUV444// || pixfmt == Format_RGB555 || pixfmt == Format_BGR555 ; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const VideoFormat &fmt) { dbg.nospace() << "QtAV::VideoFormat(pixelFormat: " << (int)fmt.pixelFormat() << " " << fmt.name() << " alpha: " << fmt.hasAlpha(); dbg.nospace() << ", channels: " << fmt.channels(); dbg.nospace() << ", planes: " << fmt.planeCount(); dbg.nospace() << ", bpc: " << fmt.bitsPerComponent(); dbg.nospace() << ", bpp: " << fmt.bitsPerPixel() << "/" << fmt.bitsPerPixelPadded() << " "; for (int i = 0; i < fmt.planeCount(); ++i) { dbg.nospace() << "-" << fmt.bitsPerPixel(i); } dbg.nospace() << ")"; return dbg.space(); } QDebug operator<<(QDebug dbg, VideoFormat::PixelFormat pixFmt) { dbg.nospace() << (int)pixFmt << " " << av_get_pix_fmt_name((AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(pixFmt)); return dbg.space(); } #endif namespace { class VideoFormatPrivateRegisterMetaTypes { public: VideoFormatPrivateRegisterMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); } } _registerMetaTypes; } } //namespace QtAV QtAV-1.12.0/src/VideoFrame.cpp000066400000000000000000000407321312235004300157210ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoFrame.h" #include "QtAV/private/Frame_p.h" #include "QtAV/SurfaceInterop.h" #include "ImageConverter.h" #include #include #include "QtAV/private/AVCompat.h" #include "utils/GPUMemCopy.h" #include "utils/Logger.h" // TODO: VideoFrame.copyPropertyies(VideoFrame) to avoid missing property copy namespace QtAV { namespace{ static const struct RegisterMetaTypes { inline RegisterMetaTypes() { qRegisterMetaType("QtAV::VideoFrame"); } } _registerMetaTypes; } VideoFrame VideoFrame::fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized, bool swapUV) { Q_ASSERT(src[0] && pitch[0] > 0 && "VideoFrame::fromGPU: src[0] and pitch[0] must be set"); const int nb_planes = fmt.planeCount(); const int chroma_pitch = nb_planes > 1 ? fmt.bytesPerLine(pitch[0], 1) : 0; const int chroma_h = fmt.chromaHeight(surface_h); int h[] = { surface_h, 0, 0}; for (int i = 1; i < nb_planes; ++i) { h[i] = chroma_h; // set chroma address and pitch if not set if (pitch[i] <= 0) pitch[i] = chroma_pitch; if (!src[i]) src[i] = src[i-1] + pitch[i-1]*h[i-1]; } if (swapUV) { std::swap(src[1], src[2]); std::swap(pitch[1], pitch[2]); } VideoFrame frame; if (optimized) { int yuv_size = 0; for (int i = 0; i < nb_planes; ++i) { yuv_size += pitch[i]*h[i]; } // additional 15 bytes to ensure 16 bytes aligned QByteArray buf(15 + yuv_size, 0); const int offset_16 = (16 - ((uintptr_t)buf.constData() & 0x0f)) & 0x0f; // plane 1, 2... is aligned? uchar* plane_ptr = (uchar*)buf.constData() + offset_16; QVector dst(nb_planes, 0); for (int i = 0; i < nb_planes; ++i) { dst[i] = plane_ptr; // TODO: add VideoFormat::planeWidth/Height() ? // pitch instead of surface_width plane_ptr += pitch[i] * h[i]; gpu_memcpy(dst[i], src[i], pitch[i]*h[i]); } frame = VideoFrame(width, height, fmt, buf); frame.setBits(dst); frame.setBytesPerLine(pitch); } else { frame = VideoFrame(width, height, fmt); frame.setBits(src); frame.setBytesPerLine(pitch); // TODO: why clone is faster()? // TODO: buffer pool and create VideoFrame when needed to avoid copy? also for other va frame = frame.clone(); } return frame; } void VideoFrame::copyPlane(quint8 *dst, size_t dst_stride, const quint8 *src, size_t src_stride, unsigned byteWidth, unsigned height) { if (!dst || !src) return; if (dst_stride == src_stride && src_stride == byteWidth && height) { memcpy(dst, src, byteWidth*height); return; } for (; height > 0; --height) { memcpy(dst, src, byteWidth); src += src_stride; dst += dst_stride; } } class VideoFramePrivate : public FramePrivate { Q_DISABLE_COPY(VideoFramePrivate) public: VideoFramePrivate() : FramePrivate() , width(0) , height(0) , color_space(ColorSpace_Unknown) , color_range(ColorRange_Unknown) , displayAspectRatio(0) , format(VideoFormat::Format_Invalid) {} VideoFramePrivate(int w, int h, const VideoFormat& fmt) : FramePrivate() , width(w) , height(h) , color_space(ColorSpace_Unknown) , color_range(ColorRange_Unknown) , displayAspectRatio(0) , format(fmt) { if (!format.isValid()) return; planes.resize(format.planeCount()); line_sizes.resize(format.planeCount()); planes.reserve(format.planeCount()); line_sizes.reserve(format.planeCount()); } ~VideoFramePrivate() {} int width, height; ColorSpace color_space; ColorRange color_range; float displayAspectRatio; VideoFormat format; QScopedPointer qt_image; VideoSurfaceInteropPtr surface_interop; }; VideoFrame::VideoFrame() : Frame(new VideoFramePrivate()) { } VideoFrame::VideoFrame(int width, int height, const VideoFormat &format, const QByteArray& data) : Frame(new VideoFramePrivate(width, height, format)) { Q_D(VideoFrame); d->data = data; } VideoFrame::VideoFrame(const QImage& image) : Frame(new VideoFramePrivate(image.width(), image.height(), VideoFormat(image.format()))) { setBits((uchar*)image.constBits(), 0); setBytesPerLine(image.bytesPerLine(), 0); d_func()->qt_image.reset(new QImage(image)); } /*! Constructs a shallow copy of \a other. Since VideoFrame is explicitly shared, these two instances will reflect the same frame. */ VideoFrame::VideoFrame(const VideoFrame &other) : Frame(other) { } /*! Assigns the contents of \a other to this video frame. Since VideoFrame is explicitly shared, these two instances will reflect the same frame. */ VideoFrame &VideoFrame::operator =(const VideoFrame &other) { d_ptr = other.d_ptr; return *this; } VideoFrame::~VideoFrame() { } int VideoFrame::channelCount() const { Q_D(const VideoFrame); if (!d->format.isValid()) return 0; return d->format.channels(); } VideoFrame VideoFrame::clone() const { Q_D(const VideoFrame); if (!d->format.isValid()) return VideoFrame(); // data may be not set (ff decoder) if (d->planes.isEmpty() || !d->planes.at(0)) {//d->data.size() < width()*height()) { // at least width*height // maybe in gpu memory, then bits() is not set qDebug("frame data not valid. size: %d", d->data.size()); VideoFrame f(width(), height(), d->format); f.d_ptr->metadata = d->metadata; // need metadata? f.setTimestamp(d->timestamp); f.setDisplayAspectRatio(d->displayAspectRatio); return f; } int bytes = 0; for (int i = 0; i < d->format.planeCount(); ++i) { bytes += bytesPerLine(i)*planeHeight(i); } QByteArray buf(bytes, 0); char *dst = buf.data(); //must before buf is shared, otherwise data will be detached. VideoFrame f(width(), height(), d->format, buf); const int nb_planes = d->format.planeCount(); for (int i = 0; i < nb_planes; ++i) { f.setBits((quint8*)dst, i); f.setBytesPerLine(bytesPerLine(i), i); const int plane_size = bytesPerLine(i)*planeHeight(i); memcpy(dst, constBits(i), plane_size); dst += plane_size; } f.d_ptr->metadata = d->metadata; // need metadata? f.setTimestamp(d->timestamp); f.setDisplayAspectRatio(d->displayAspectRatio); f.setColorSpace(d->color_space); f.setColorRange(d->color_range); return f; } VideoFormat VideoFrame::format() const { return d_func()->format; } VideoFormat::PixelFormat VideoFrame::pixelFormat() const { return d_func()->format.pixelFormat(); } QImage::Format VideoFrame::imageFormat() const { return d_func()->format.imageFormat(); } int VideoFrame::pixelFormatFFmpeg() const { return d_func()->format.pixelFormatFFmpeg(); } bool VideoFrame::isValid() const { Q_D(const VideoFrame); return d->width > 0 && d->height > 0 && d->format.isValid(); //data not empty? } QSize VideoFrame::size() const { Q_D(const VideoFrame); return QSize(d->width, d->height); } int VideoFrame::width() const { return d_func()->width; } int VideoFrame::height() const { return d_func()->height; } int VideoFrame::planeWidth(int plane) const { Q_D(const VideoFrame); return d->format.width(width(), plane); } int VideoFrame::planeHeight(int plane) const { Q_D(const VideoFrame); if (plane == 0) return d->height; return d->format.chromaHeight(d->height); } float VideoFrame::displayAspectRatio() const { Q_D(const VideoFrame); if (d->displayAspectRatio > 0) return d->displayAspectRatio; if (d->width > 0 && d->height > 0) return (float)d->width / (float)d->height; return 0; } void VideoFrame::setDisplayAspectRatio(float displayAspectRatio) { d_func()->displayAspectRatio = displayAspectRatio; } ColorSpace VideoFrame::colorSpace() const { return d_func()->color_space; } void VideoFrame::setColorSpace(ColorSpace value) { d_func()->color_space = value; } ColorRange VideoFrame::colorRange() const { return d_func()->color_range; } void VideoFrame::setColorRange(ColorRange value) { d_func()->color_range = value; } int VideoFrame::effectiveBytesPerLine(int plane) const { Q_D(const VideoFrame); return d->format.bytesPerLine(width(), plane); } QImage VideoFrame::toImage(QImage::Format fmt, const QSize& dstSize, const QRectF &roi) const { Q_D(const VideoFrame); if (!d->qt_image.isNull() && fmt == d->qt_image->format() && dstSize == d->qt_image->size() && (!roi.isValid() || roi == d->qt_image->rect())) { return *d->qt_image.data(); } VideoFrame f(to(VideoFormat(VideoFormat::pixelFormatFromImageFormat(fmt)), dstSize, roi)); if (!f) return QImage(); QImage image((const uchar*)f.frameData().constData(), f.width(), f.height(), f.bytesPerLine(0), fmt); return image.copy(); } VideoFrame VideoFrame::to(const VideoFormat &fmt, const QSize& dstSize, const QRectF& roi) const { if (!isValid() || !constBits(0)) {// hw surface. map to host. only supports rgb packed formats now Q_D(const VideoFrame); const QVariant v = d->metadata.value(QStringLiteral("surface_interop")); if (!v.isValid()) return VideoFrame(); VideoSurfaceInteropPtr si = v.value(); if (!si) return VideoFrame(); VideoFrame f; f.setDisplayAspectRatio(displayAspectRatio()); f.setTimestamp(timestamp()); if (si->map(HostMemorySurface, fmt, &f)) { if ((!dstSize.isValid() ||dstSize == QSize(width(), height())) && (!roi.isValid() || roi == QRectF(0, 0, width(), height()))) //roi is not supported now return f; return f.to(fmt, dstSize, roi); } return VideoFrame(); } const int w = dstSize.width() > 0 ? dstSize.width() : width(); const int h = dstSize.height() > 0 ? dstSize.height() : height(); if (fmt.pixelFormatFFmpeg() == pixelFormatFFmpeg() && w == width() && h == height() // TODO: roi check. ) return *this; Q_D(const VideoFrame); ImageConverterSWS conv; conv.setInFormat(pixelFormatFFmpeg()); conv.setOutFormat(fmt.pixelFormatFFmpeg()); conv.setInSize(width(), height()); conv.setOutSize(w, h); conv.setInRange(colorRange()); if (!conv.convert(d->planes.constData(), d->line_sizes.constData())) { qWarning() << "VideoFrame::to error: " << format() << "=>" << fmt; return VideoFrame(); } VideoFrame f(w, h, fmt, conv.outData()); f.setBits(conv.outPlanes()); f.setBytesPerLine(conv.outLineSizes()); if (fmt.isRGB()) { f.setColorSpace(fmt.isPlanar() ? ColorSpace_GBR : ColorSpace_RGB); } else { f.setColorSpace(ColorSpace_Unknown); } // TODO: color range f.setTimestamp(timestamp()); f.setDisplayAspectRatio(displayAspectRatio()); f.d_ptr->metadata = d->metadata; // need metadata? return f; } VideoFrame VideoFrame::to(VideoFormat::PixelFormat pixfmt, const QSize& dstSize, const QRectF &roi) const { return to(VideoFormat(pixfmt), dstSize, roi); } void *VideoFrame::map(SurfaceType type, void *handle, int plane) { return map(type, handle, format(), plane); } void *VideoFrame::map(SurfaceType type, void *handle, const VideoFormat& fmt, int plane) { Q_D(VideoFrame); const QVariant v = d->metadata.value(QStringLiteral("surface_interop")); if (!v.isValid()) return 0; d->surface_interop = v.value(); if (!d->surface_interop) return 0; if (plane > planeCount()) return 0; return d->surface_interop->map(type, fmt, handle, plane); } void VideoFrame::unmap(void *handle) { Q_D(VideoFrame); if (!d->surface_interop) return; d->surface_interop->unmap(handle); } void* VideoFrame::createInteropHandle(void* handle, SurfaceType type, int plane) { Q_D(VideoFrame); const QVariant v = d->metadata.value(QStringLiteral("surface_interop")); if (!v.isValid()) return 0; d->surface_interop = v.value(); if (!d->surface_interop) return 0; if (plane > planeCount()) return 0; return d->surface_interop->createHandle(handle, type, format(), plane, planeWidth(plane), planeHeight(plane)); } VideoFrameConverter::VideoFrameConverter() : m_cvt(0) { memset(m_eq, 0, sizeof(m_eq)); } VideoFrameConverter::~VideoFrameConverter() { if (m_cvt) { delete m_cvt; m_cvt = 0; } } void VideoFrameConverter::setEq(int brightness, int contrast, int saturation) { if (brightness >= -100 && brightness <= 100) m_eq[0] = brightness; if (contrast >= -100 && contrast <= 100) m_eq[1] = contrast; if (saturation >= -100 && saturation <= 100) m_eq[2] = saturation; } VideoFrame VideoFrameConverter::convert(const VideoFrame& frame, const VideoFormat &fmt) const { return convert(frame, fmt.pixelFormatFFmpeg()); } VideoFrame VideoFrameConverter::convert(const VideoFrame &frame, VideoFormat::PixelFormat fmt) const { return convert(frame, VideoFormat::pixelFormatToFFmpeg(fmt)); } VideoFrame VideoFrameConverter::convert(const VideoFrame& frame, QImage::Format fmt) const { return convert(frame, VideoFormat::pixelFormatFromImageFormat(fmt)); } VideoFrame VideoFrameConverter::convert(const VideoFrame &frame, int fffmt) const { if (!frame.isValid() || fffmt == QTAV_PIX_FMT_C(NONE)) return VideoFrame(); if (!frame.constBits(0)) // hw surface return frame.to(VideoFormat::pixelFormatFromFFmpeg(fffmt)); const VideoFormat format(frame.format()); //if (fffmt == format.pixelFormatFFmpeg()) // return *this; if (!m_cvt) { m_cvt = new ImageConverterSWS(); } m_cvt->setBrightness(m_eq[0]); m_cvt->setContrast(m_eq[1]); m_cvt->setSaturation(m_eq[2]); m_cvt->setInFormat(format.pixelFormatFFmpeg()); m_cvt->setOutFormat(fffmt); m_cvt->setInSize(frame.width(), frame.height()); m_cvt->setOutSize(frame.width(), frame.height()); m_cvt->setInRange(frame.colorRange()); const int pal = format.hasPalette(); QVector pitch(format.planeCount() + pal); QVector stride(format.planeCount() + pal); for (int i = 0; i < format.planeCount(); ++i) { pitch[i] = frame.constBits(i); stride[i] = frame.bytesPerLine(i); } const QByteArray paldata(frame.metaData(QStringLiteral("pallete")).toByteArray()); if (pal > 0) { pitch[1] = (const uchar*)paldata.constData(); stride[1] = paldata.size(); } if (!m_cvt->convert(pitch.constData(), stride.constData())) { return VideoFrame(); } const VideoFormat fmt(fffmt); VideoFrame f(frame.width(), frame.height(), fmt, m_cvt->outData()); f.setBits(m_cvt->outPlanes()); f.setBytesPerLine(m_cvt->outLineSizes()); f.setTimestamp(frame.timestamp()); f.setDisplayAspectRatio(frame.displayAspectRatio()); // metadata? if (fmt.isRGB()) { f.setColorSpace(fmt.isPlanar() ? ColorSpace_GBR : ColorSpace_RGB); } else { f.setColorSpace(ColorSpace_Unknown); } // TODO: color range return f; } } //namespace QtAV QtAV-1.12.0/src/VideoFrameExtractor.cpp000066400000000000000000000405131312235004300176120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoFrameExtractor.h" #include #include #include #include #include #include #include "QtAV/VideoCapture.h" #include "QtAV/VideoDecoder.h" #include "QtAV/AVDemuxer.h" #include "QtAV/Packet.h" #include "utils/BlockingQueue.h" #include "utils/Logger.h" // TODO: event and signal do not work #define ASYNC_SIGNAL 0 #define ASYNC_EVENT 0 #define ASYNC_TASK 1 namespace QtAV { class ExtractThread : public QThread { public: ExtractThread(QObject *parent = 0) : QThread(parent) , stop(false) { tasks.setCapacity(1); // avoid too frequent } ~ExtractThread() { waitStop(); } void waitStop() { if (!isRunning()) return; scheduleStop(); wait(); } void addTask(QRunnable* t) { if (tasks.size() >= tasks.capacity()) { QRunnable *task = tasks.take(); //clear only for seek task if (task->autoDelete()) delete task; } tasks.put(t); } void scheduleStop() { class StopTask : public QRunnable { public: StopTask(ExtractThread* t) : thread(t) {} void run() { thread->stop = true;} private: ExtractThread *thread; }; addTask(new StopTask(this)); } protected: virtual void run() { #if ASYNC_TASK while (!stop) { QRunnable *task = tasks.take(); if (!task) return; task->run(); if (task->autoDelete()) delete task; } #else exec(); #endif //ASYNC_TASK } public: volatile bool stop; private: BlockingQueue tasks; }; // FIXME: avcodec_close() crash const int kDefaultPrecision = 500; class VideoFrameExtractorPrivate : public DPtrPrivate { public: VideoFrameExtractorPrivate() : extracted(false) , abort_seek(false) , async(true) , has_video(true) , auto_extract(true) , auto_precision(true) , seek_count(0) , position(-2*kDefaultPrecision) , precision(kDefaultPrecision) , decoder(0) { QVariantHash opt; opt[QString::fromLatin1("skip_frame")] = 8; // 8 for "avcodec", "NoRef" for "FFmpeg". see AVDiscard opt[QString::fromLatin1("skip_loop_filter")] = 8; //skip all? //skip_dict is slower dec_opt_framedrop[QString::fromLatin1("avcodec")] = opt; opt[QString::fromLatin1("skip_frame")] = 0; // 0 for "avcodec", "Default" for "FFmpeg". see AVDiscard opt[QString::fromLatin1("skip_loop_filter")] = 0; dec_opt_normal[QString::fromLatin1("avcodec")] = opt; // avcodec need correct string or value in libavcodec codecs #if QTAV_HAVE(DXVA) // << QStringLiteral("DXVA") #endif //QTAV_HAVE(DXVA) #if QTAV_HAVE(VAAPI) // << QStringLiteral("VAAPI") #endif //QTAV_HAVE(VAAPI) #if QTAV_HAVE(CEDARV) //<< QStringLiteral("Cedarv") #endif //QTAV_HAVE(CEDARV) #if QTAV_HAVE(VDA) // << QStringLiteral("VDA") // only 1 app can use VDA at a given time #endif //QTAV_HAVE(VDA) << QStringLiteral("FFmpeg"); } ~VideoFrameExtractorPrivate() { // stop first before demuxer and decoder close to avoid running new seek task after demuxer is closed. thread.waitStop(); releaseResourceInternal(); } bool checkAndOpen() { const bool loaded = demuxer.fileName() == source && demuxer.isLoaded(); if (loaded && decoder)// && !demuxer.atEnd()) //we may seek back later when eof got. TODO: remove demuxer.atEnd() return true; seek_count = 0; decoder.reset(0); if (!loaded || demuxer.atEnd()) { demuxer.unload(); demuxer.setMedia(source); if (!demuxer.load()) { return false; } } has_video = demuxer.videoStreams().size() > 0; if (!has_video) { demuxer.unload(); return false; } if (codecs.isEmpty()) return false; if (auto_precision) { if (demuxer.duration() < 10*1000) precision = kDefaultPrecision/2; else precision = kDefaultPrecision; } foreach (const QString& c, codecs) { VideoDecoder *vd = VideoDecoder::create(c.toUtf8().constData()); if (!vd) continue; decoder.reset(vd); decoder->setCodecContext(demuxer.videoCodecContext()); if (!decoder->open()) { decoder.reset(0); continue; } QVariantHash opt, va; // FIXME: why QStringLiteral can't be used as key for vs<2015 but somewhere else it can? error C2958: the left bracket '[' found at qstringliteral va[QString::fromLatin1("display")] = QString::fromLatin1("X11"); // to support swscale opt[QString::fromLatin1("vaapi")] = va; decoder->setOptions(opt); break; } return !!decoder; } // return the key frame position bool extractInPrecision(qint64 value, int range) { abort_seek = false; frame = VideoFrame(); if (value < demuxer.startTime()) value += demuxer.startTime(); demuxer.seek(value); const int vstream = demuxer.videoStream(); Packet pkt; qint64 pts0 = -1; bool warn_bad_seek = true; bool warn_out_of_range = true; while (!demuxer.atEnd()) { if (abort_seek) { qDebug("VideoFrameExtractor abort seek before read"); return false; } if (!demuxer.readFrame()) continue; if (demuxer.stream() != vstream) continue; pkt = demuxer.packet(); if (pts0 < 0LL) pts0 = (qint64)(pkt.pts*1000.0); if ((qint64)(pkt.pts*1000.0) - value > (qint64)range) { if (warn_out_of_range) qDebug("read packet out of range"); warn_out_of_range = false; // No return because decoder needs more packets before the desired frame is decoded //return false; } //qDebug("video packet: %f", pkt.pts); // TODO: always key frame? if (pkt.hasKeyFrame) break; if (warn_bad_seek) qWarning("Not seek to key frame!!!"); warn_bad_seek = false; } // enlarge range if seek to key-frame failed const qint64 key_pts = (qint64)(pkt.pts*1000.0); const bool enlarge_range = pts0 >= 0LL && key_pts - pts0 > 0LL; if (enlarge_range) { range = qMax(key_pts - value, range); qDebug() << "enlarge range ==>>>> " << range; } if (!pkt.isValid()) { qWarning("VideoFrameExtractor failed to get a packet at %lld", value); return false; } decoder->flush(); //must flush otherwise old frames will be decoded at the beginning decoder->setOptions(dec_opt_normal); // must decode key frame int k = 0; while (k < 2 && !frame.isValid()) { if (abort_seek) { qDebug("VideoFrameExtractor abort seek before decoding key frames"); return false; } //qWarning("invalid key frame!!!!! undecoded: %d", decoder->undecodedSize()); if (decoder->decode(pkt)) { frame = decoder->frame(); } ++k; } // if seek backward correctly to key frame, diff0 = t - value <= 0 // but sometimes seek to no-key frame(and range is enlarged), diff0 >= 0 // decode key frame const int diff0 = qint64(frame.timestamp()*1000.0) - value; if (qAbs(diff0) <= range) { //TODO: flag forward: result pts must >= value if (frame.isValid()) { qDebug() << "VideoFrameExtractor: key frame found @" << frame.timestamp() <<" diff=" << diff0 << ". format: " << frame.format(); return true; } } QVariantHash* dec_opt = &dec_opt_normal; // 0: default, 1: framedrop // decode at the given position while (!demuxer.atEnd()) { if (abort_seek) { qDebug("VideoFrameExtractor abort seek after key frame before read"); return false; } if (!demuxer.readFrame()) continue; if (demuxer.stream() != vstream) continue; pkt = demuxer.packet(); const qreal t = pkt.pts; //qDebug("video packet: %f, delta=%lld", t, value - qint64(t*1000.0)); if (!pkt.isValid()) { qWarning("invalid packet. no decode"); continue; } if (pkt.hasKeyFrame) { // FIXME: //qCritical("Internal error. Can not be a key frame!!!!"); //return false; //?? } qint64 diff = qint64(t*1000.0) - value; QVariantHash *dec_opt_old = dec_opt; if (seek_count == 0 || diff >= 0) dec_opt = &dec_opt_normal; else dec_opt = &dec_opt_framedrop; if (dec_opt != dec_opt_old) decoder->setOptions(*dec_opt); // invalid packet? if (!decoder->decode(pkt)) { qWarning("!!!!!!!!!decode failed!!!!"); frame = VideoFrame(); return false; } // store the last decoded frame because next frame may be out of range const VideoFrame f = decoder->frame(); if (!f.isValid()) { //qDebug("VideoFrameExtractor: invalid frame!!!"); continue; } frame = f; const qreal pts = frame.timestamp(); const qint64 pts_ms = pts*1000.0; if (pts_ms < value) continue; // diff = pts_ms - value; if (qAbs(diff) <= (qint64)range) { qDebug("got frame at %fs, diff=%lld", pts, diff); break; } // if decoder was not flushed, we may get old frame which is acceptable if (diff > range && t > pts) { qWarning("out pts out of range. diff=%lld, range=%d", diff, range); frame = VideoFrame(); return false; } } ++seek_count; // now we get the final frame if (demuxer.atEnd()) releaseResourceInternal(); return true; } void releaseResourceInternal() { seek_count = 0; // close codec context first. decoder.reset(0); demuxer.unload(); } void safeReleaseResource() { class Cleaner : public QRunnable { VideoFrameExtractorPrivate *p; public: Cleaner(VideoFrameExtractorPrivate* pri) : p(pri) {} void run() { p->releaseResourceInternal(); } }; thread.addTask(new Cleaner(this)); } bool extracted; volatile bool abort_seek; bool async; bool has_video; bool loading; bool auto_extract; bool auto_precision; int seek_count; qint64 position; int precision; QString source; AVDemuxer demuxer; QScopedPointer decoder; VideoFrame frame; QStringList codecs; ExtractThread thread; static QVariantHash dec_opt_framedrop, dec_opt_normal; }; QVariantHash VideoFrameExtractorPrivate::dec_opt_framedrop; QVariantHash VideoFrameExtractorPrivate::dec_opt_normal; VideoFrameExtractor::VideoFrameExtractor(QObject *parent) : QObject(parent) { DPTR_D(VideoFrameExtractor); moveToThread(&d.thread); d.thread.start(); connect(this, SIGNAL(aboutToExtract(qint64)), SLOT(extractInternal(qint64))); } void VideoFrameExtractor::setSource(const QString url) { DPTR_D(VideoFrameExtractor); if (url == d.source) return; d.source = url; d.has_video = true; Q_EMIT sourceChanged(); d.frame = VideoFrame(); d.safeReleaseResource(); } QString VideoFrameExtractor::source() const { return d_func().source; } void VideoFrameExtractor::setAsync(bool value) { DPTR_D(VideoFrameExtractor); if (d.async == value) return; d.async = value; Q_EMIT asyncChanged(); } bool VideoFrameExtractor::async() const { return d_func().async; } void VideoFrameExtractor::setAutoExtract(bool value) { DPTR_D(VideoFrameExtractor); if (d.auto_extract == value) return; d.auto_extract = value; Q_EMIT autoExtractChanged(); } bool VideoFrameExtractor::autoExtract() const { return d_func().auto_extract; } void VideoFrameExtractor::setPosition(qint64 value) { DPTR_D(VideoFrameExtractor); if (!d.has_video) return; if (qAbs(value - d.position) < precision()) { return; } d.frame = VideoFrame(); d.extracted = false; d.position = value; Q_EMIT positionChanged(); if (!autoExtract()) return; extract(); } qint64 VideoFrameExtractor::position() const { return d_func().position; } void VideoFrameExtractor::setPrecision(int value) { DPTR_D(VideoFrameExtractor); if (d.precision == value) return; d.auto_precision = value < 0; // explain why value (p0) is used but not the actual decoded position (p) // it's key frame finding rule if (value >= 0) d.precision = value; Q_EMIT precisionChanged(); } int VideoFrameExtractor::precision() const { return d_func().precision; } bool VideoFrameExtractor::event(QEvent *e) { //qDebug("event: %d", e->type()); if (e->type() != QEvent::User) return QObject::event(e); extractInternal(position()); // FIXME: wrong position return true; } void VideoFrameExtractor::extract() { DPTR_D(VideoFrameExtractor); if (!d.async) { extractInternal(position()); return; } #if ASYNC_SIGNAL else { Q_EMIT aboutToExtract(position()); return; } #endif #if ASYNC_TASK class ExtractTask : public QRunnable { public: ExtractTask(VideoFrameExtractor *e, qint64 t) : extractor(e) , position(t) {} void run() { extractor->extractInternal(position); } private: VideoFrameExtractor *extractor; qint64 position; }; d.abort_seek = true; d.thread.addTask(new ExtractTask(this, position())); return; #endif #if ASYNC_EVENT qApp->postEvent(this, new QEvent(QEvent::User)); #endif //ASYNC_EVENT } void VideoFrameExtractor::extractInternal(qint64 pos) { DPTR_D(VideoFrameExtractor); int precision_old = precision(); if (!d.checkAndOpen()) { Q_EMIT error(); //qWarning("can not open decoder...."); return; // error handling } if (precision_old != precision()) { Q_EMIT precisionChanged(); } d.extracted = d.extractInPrecision(pos, precision()); if (!d.extracted) { Q_EMIT error(); return; } Q_EMIT frameExtracted(d.frame); } } //namespace QtAV QtAV-1.12.0/src/VideoThread.cpp000066400000000000000000000572061312235004300161020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoThread.h" #include "AVThread_p.h" #include "QtAV/Packet.h" #include "QtAV/AVClock.h" #include "QtAV/VideoCapture.h" #include "QtAV/VideoDecoder.h" #include "QtAV/VideoRenderer.h" #include "QtAV/Statistics.h" #include "QtAV/Filter.h" #include "QtAV/FilterContext.h" #include "output/OutputSet.h" #include "QtAV/private/AVCompat.h" #include #include "utils/Logger.h" namespace QtAV { class VideoThreadPrivate : public AVThreadPrivate { public: VideoThreadPrivate(): AVThreadPrivate() , force_fps(0) , force_dt(0) , capture(0) , filter_context(0) { } ~VideoThreadPrivate() { //not neccesary context is managed by filters. if (filter_context) { delete filter_context; filter_context = 0; } } VideoFrameConverter conv; qreal force_fps; // <=0: try to use pts. if no pts in stream(guessed by 5 packets), use |force_fps| // not const. int force_dt; //unit: ms. force_fps = 1/force_dt. double pts; //current decoded pts. for capture. TODO: remove VideoCapture *capture; VideoFilterContext *filter_context;//TODO: use own smart ptr. QSharedPointer "=" is ugly VideoFrame displayed_frame; }; VideoThread::VideoThread(QObject *parent) : AVThread(*new VideoThreadPrivate(), parent) { } //it is called in main thread usually, but is being used in video thread, VideoCapture* VideoThread::setVideoCapture(VideoCapture *cap) { qDebug("setCapture %p", cap); DPTR_D(VideoThread); QMutexLocker locker(&d.mutex); VideoCapture *old = d.capture; d.capture = cap; if (old) disconnect(old, SIGNAL(requested()), this, SLOT(addCaptureTask())); if (cap) connect(cap, SIGNAL(requested()), this, SLOT(addCaptureTask())); if (cap->autoSave() && cap->name.isEmpty()) { // statistics is already set by AVPlayer cap->setCaptureName(QFileInfo(d.statistics->url).completeBaseName()); } return old; } VideoCapture* VideoThread::videoCapture() const { return d_func().capture; } void VideoThread::addCaptureTask() { if (!isRunning()) return; class CaptureTask : public QRunnable { public: CaptureTask(VideoThread *vt) : vthread(vt) {} void run() { VideoCapture *vc = vthread->videoCapture(); if (!vc) return; VideoFrame frame(vthread->displayedFrame()); //vthread->applyFilters(frame); vc->setVideoFrame(frame); vc->start(); } private: VideoThread *vthread; }; scheduleTask(new CaptureTask(this)); } void VideoThread::clearRenderers() { d_func().outputSet->sendVideoFrame(VideoFrame()); } VideoFrame VideoThread::displayedFrame() const { return d_func().displayed_frame; } void VideoThread::setFrameRate(qreal value) { DPTR_D(VideoThread); d.force_fps = value; if (d.force_fps != 0.0) { d.force_dt = int(1000.0/d.force_fps); } else { d.force_dt = 0; } } void VideoThread::setBrightness(int val) { setEQ(val, 101, 101); } void VideoThread::setContrast(int val) { setEQ(101, val, 101); } void VideoThread::setSaturation(int val) { setEQ(101, 101, val); } void VideoThread::setEQ(int b, int c, int s) { class EQTask : public QRunnable { public: EQTask(VideoFrameConverter *c) : brightness(0) , contrast(0) , saturation(0) , conv(c) { //qDebug("EQTask tid=%p", QThread::currentThread()); } void run() { conv->setEq(brightness, contrast, saturation); } int brightness, contrast, saturation; private: VideoFrameConverter *conv; }; DPTR_D(VideoThread); EQTask *task = new EQTask(&d.conv); task->brightness = b; task->contrast = c; task->saturation = s; if (isRunning()) { scheduleTask(task); } else { task->run(); delete task; } } void VideoThread::applyFilters(VideoFrame &frame) { DPTR_D(VideoThread); QMutexLocker locker(&d.mutex); Q_UNUSED(locker); if (!d.filters.isEmpty()) { //sort filters by format. vo->defaultFormat() is the last foreach (Filter *filter, d.filters) { VideoFilter *vf = static_cast(filter); if (!vf->isEnabled()) continue; if (vf->prepareContext(d.filter_context, d.statistics, &frame)) vf->apply(d.statistics, &frame); } } } // filters on vo will not change video frame, so it's safe to protect frame only in every individual vo bool VideoThread::deliverVideoFrame(VideoFrame &frame) { DPTR_D(VideoThread); /* * TODO: video renderers sorted by preferredPixelFormat() and convert in AVOutputSet. * Convert only once for the renderers has the same preferredPixelFormat(). */ d.outputSet->lock(); QList outputs = d.outputSet->outputs(); VideoRenderer *vo = 0; if (!outputs.isEmpty()) vo = static_cast(outputs.first()); if (vo && (!vo->isSupported(frame.pixelFormat()) || (vo->isPreferredPixelFormatForced() && vo->preferredPixelFormat() != frame.pixelFormat()) )) { VideoFormat fmt(frame.format()); if (fmt.hasPalette() || fmt.isRGB()) fmt = VideoFormat::Format_RGB32; else fmt = vo->preferredPixelFormat(); VideoFrame outFrame(d.conv.convert(frame, fmt)); if (!outFrame.isValid()) { d.outputSet->unlock(); return false; } frame = outFrame; } d.outputSet->sendVideoFrame(frame); //TODO: group by format, convert group by group d.outputSet->unlock(); Q_EMIT frameDelivered(); return true; } //TODO: if output is null or dummy, the use duration to wait void VideoThread::run() { DPTR_D(VideoThread); if (!d.dec || !d.dec->isAvailable() || !d.outputSet) return; resetState(); if (d.capture->autoSave()) { d.capture->setCaptureName(QFileInfo(d.statistics->url).completeBaseName()); } //not neccesary context is managed by filters. d.filter_context = VideoFilterContext::create(VideoFilterContext::QtPainter); VideoDecoder *dec = static_cast(d.dec); Packet pkt; QVariantHash *dec_opt = &d.dec_opt_normal; //TODO: restore old framedrop option after seek /*! * if we skip some frames(e.g. seek, drop frames to speed up), then then first frame to decode must * be a key frame for hardware decoding. otherwise may crash */ bool wait_key_frame = false; int nb_dec_slow = 0; int nb_dec_fast = 0; qint32 seek_count = 0; // wm4 says: 1st seek can not use frame drop for decoder // TODO: kNbSlowSkip depends on video fps, ensure slow time <= 2s /* kNbSlowSkip: if video frame slow count >= kNbSlowSkip, skip decoding all frames until next keyframe reaches. * if slow count > kNbSlowSkip/2, skip rendering every 3 or 6 frames */ const int kNbSlowSkip = 20; // kNbSlowFrameDrop: if video frame slow count > kNbSlowFrameDrop, skip decoding nonref frames. only some of ffmpeg based decoders support it. const int kNbSlowFrameDrop = 10; bool sync_audio = d.clock->clockType() == AVClock::AudioClock; bool sync_video = d.clock->clockType() == AVClock::VideoClock; // no frame drop const qint64 start_time = QDateTime::currentMSecsSinceEpoch(); qreal v_a = 0; int nb_no_pts = 0; //bool wait_audio_drain const char* pkt_data = NULL; // workaround for libav9 decode fail but error code >= 0 qint64 last_deliver_time = 0; int sync_id = 0; while (!d.stop) { processNextTask(); //TODO: why put it at the end of loop then stepForward() not work? //processNextTask tryPause(timeout) and and continue outter loop if (d.render_pts0 < 0) { // no pause when seeking if (tryPause()) { //DO NOT continue, or stepForward() will fail } else { if (isPaused()) continue; //timeout. process pending tasks } } if (d.seek_requested) { d.seek_requested = false; qDebug("request seek video thread"); pkt = Packet(); // last decode failed and pkt is valid, reset pkt to force take the next packet if seek is requested msleep(1); } else { // d.render_pts0 < 0 means seek finished here if (d.clock->syncId() > 0) { qDebug("video thread wait to sync end for sync id: %d", d.clock->syncId()); if (d.render_pts0 < 0 && sync_id > 0) { msleep(10); v_a = 0; continue; } } else { sync_id = 0; } } if(!pkt.isValid() && !pkt.isEOF()) { // can't seek back if eof packet is read pkt = d.packets.take(); //wait to dequeue // TODO: push pts history here and reorder } if (pkt.isEOF()) { wait_key_frame = false; qDebug("video thread gets an eof packet."); } else { //qDebug() << pkt.position << " pts:" <flush(); //d.dec instead of dec because d.dec maybe changed in processNextTask() but dec is not d.render_pts0 = pkt.pts; sync_id = pkt.position; if (pkt.pts >= 0) qDebug("video seek: %.3f, id: %d", d.render_pts0, sync_id); d.pts_history = ring(d.pts_history.capacity()); v_a = 0; continue; } } if (pkt.pts <= 0 && !pkt.isEOF() && pkt.data.size() > 0) { nb_no_pts++; } else { nb_no_pts = 0; } if (nb_no_pts > 5) { qDebug("the stream may have no pts. force fps to: %f/%f", d.force_fps < 0 ? -d.force_fps : 24, d.force_fps); d.clock->setClockAuto(false); d.clock->setClockType(AVClock::VideoClock); if (d.force_fps < 0) setFrameRate(-d.force_fps); else if (d.force_fps == 0) setFrameRate(24); } if (d.clock->clockType() == AVClock::AudioClock) { sync_audio = true; sync_video = false; } else if (d.clock->clockType() == AVClock::VideoClock) { sync_audio = false; sync_video = true; } else { sync_audio = false; sync_video = false; } const qreal dts = pkt.dts; //FIXME: pts and dts // TODO: delta ref time // if dts is invalid, diff can be very small (<0) and video will be decoded and rendered(display_wait is disabled for now) immediately qreal diff = dts > 0 ? dts - d.clock->value() + v_a : v_a; if (pkt.isEOF()) diff = qMin(1.0, qMax(d.delay, 1.0/d.statistics->video_only.currentDisplayFPS())); if (diff < 0 && sync_video) diff = 0; // this ensures no frame drop if (diff > kSyncThreshold) { nb_dec_fast++; } else { nb_dec_fast /= 2; } bool seeking = d.render_pts0 >= 0.0; if (seeking) { nb_dec_slow = 0; nb_dec_fast = 0; } //qDebug("nb_fast/slow: %d/%d. diff: %f, delay: %f, dts: %f, clock: %f", nb_dec_fast, nb_dec_slow, diff, d.delay, dts, clock()->value()); if (d.delay < -0.5 && d.delay > diff) { if (!seeking) { // ensure video will not later than 2s if (diff < -2 || (nb_dec_slow > kNbSlowSkip && diff < -1.0 && !pkt.hasKeyFrame)) { qDebug("video is too slow. skip decoding until next key frame."); // TODO: when to reset so frame drop flag can reset? nb_dec_slow = 0; wait_key_frame = true; pkt = Packet(); v_a = 0; // TODO: use discard flag continue; } else { nb_dec_slow++; qDebug("frame slow count: %d. v-a: %.3f", nb_dec_slow, diff); } } } else { if (nb_dec_slow >= kNbSlowFrameDrop) { qDebug("decrease 1 slow frame: %d", nb_dec_slow); nb_dec_slow = qMax(0, nb_dec_slow-1); // nb_dec_slow < kNbSlowFrameDrop will reset decoder frame drop flag } } // can not change d.delay after! we need it to comapre to next loop d.delay = diff; /* *after seeking forward, a packet may be the old, v packet may be *the new packet, then the d.delay is very large, omit it. */ if (seeking) diff = 0; // TODO: here? if (!sync_audio && diff > 0) { // wait to dts reaches // d.force_fps>0: wait after decoded before deliver if (d.force_fps <= 0)// || !qFuzzyCompare(d.clock->speed(), 1.0)) waitAndCheck(diff*1000UL, dts); // TODO: count decoding and filter time, or decode immediately but wait for display diff = 0; // TODO: can not change delay! } // update here after wait. TODO: use decoded timestamp/guessed next pts? d.clock->updateVideoTime(dts); // FIXME: dts or pts? bool skip_render = false; if (qAbs(diff) < 0.5) { if (diff < -kSyncThreshold) { //Speed up. drop frame? //continue; } } else if (!seeking) { //when to drop off? qDebug("delay %fs @%.3fs pts:%.3f", diff, d.clock->value(), pkt.pts); if (diff < 0) { if (nb_dec_slow > kNbSlowSkip) { skip_render = !pkt.hasKeyFrame && (nb_dec_slow %2); } } else { const double s = qMin(0.01*(nb_dec_fast>>1), diff); qWarning("video too fast!!! sleep %.2f s, nb fast: %d, v_a: %.4f", s, nb_dec_fast, v_a); waitAndCheck(s*1000UL, dts); diff = 0; skip_render = false; } } //audio packet not cleaned up? if (diff > 0 && diff < 1.0 && !seeking) { // can not change d.delay here! we need it to comapre to next loop waitAndCheck(diff*1000UL, dts); } if (wait_key_frame) { if (!pkt.hasKeyFrame) { qDebug("waiting for key frame. queue size: %d. pkt.size: %d", d.packets.size(), pkt.data.size()); pkt = Packet(); v_a = 0; continue; } wait_key_frame = false; } QVariantHash *dec_opt_old = dec_opt; if (!seeking || pkt.pts - d.render_pts0 >= -0.05) { // MAYBE not seeking. We should not drop the frames near the seek target. FIXME: use packet pts distance instead of -0.05 (20fps) if (seeking) qDebug("seeking... pkt.pts - d.render_pts0: %.3f", pkt.pts - d.render_pts0); if (nb_dec_slow < kNbSlowFrameDrop) { if (dec_opt == &d.dec_opt_framedrop) { qDebug("frame drop=>normal. nb_dec_slow: %d", nb_dec_slow); dec_opt = &d.dec_opt_normal; } } else { if (dec_opt == &d.dec_opt_normal) { qDebug("frame drop=>noref. nb_dec_slow: %d too slow", nb_dec_slow); dec_opt = &d.dec_opt_framedrop; } } } else { // seeking if (seek_count > 0 && d.drop_frame_seek) { if (dec_opt == &d.dec_opt_normal) { qDebug("seeking... pkt.pts - d.render_pts0: %.3f, frame drop=>noref. nb_dec_slow: %d", pkt.pts - d.render_pts0, nb_dec_slow); dec_opt = &d.dec_opt_framedrop; } } else { seek_count = -1; } } // decoder maybe changed in processNextTask(). code above MUST use d.dec but not dec if (dec != static_cast(d.dec)) { dec = static_cast(d.dec); if (!pkt.hasKeyFrame) { wait_key_frame = true; v_a = 0; continue; } qDebug("decoder changed. decoding key frame"); } if (dec_opt != dec_opt_old) dec->setOptions(*dec_opt); if (!dec->decode(pkt)) { d.pts_history.push_back(d.pts_history.back()); qWarning("Decode video failed. undecoded: %d/%d", dec->undecodedSize(), pkt.data.size()); if (pkt.isEOF()) { Q_EMIT eofDecoded(); qDebug("video decode eof done. d.render_pts0: %.3f", d.render_pts0); if (d.render_pts0 >= 0) { qDebug("video seek done at eof pts: %.3f. id: %d", d.pts_history.back(), sync_id); d.render_pts0 = -1; d.clock->syncEndOnce(sync_id); Q_EMIT seekFinished(qint64(d.pts_history.back()*1000.0)); if (seek_count == -1) seek_count = 1; else if (seek_count > 0) seek_count++; } if (!pkt.position) break; } pkt = Packet(); v_a = 0; //? continue; } // reduce here to ensure to decode the rest data in the next loop if (!pkt.isEOF()) pkt.skip(pkt.data.size() - dec->undecodedSize()); VideoFrame frame = dec->frame(); if (!frame.isValid()) { qWarning("invalid video frame from decoder. undecoded data size: %d", pkt.data.size()); if (pkt_data == pkt.data.constData()) //FIXME: for libav9. what about other versions? pkt = Packet(); else pkt_data = pkt.data.constData(); v_a = 0; //? continue; } pkt_data = pkt.data.constData(); if (frame.timestamp() <= 0) frame.setTimestamp(pkt.pts); // pkt.pts is wrong. >= real timestamp const qreal pts = frame.timestamp(); d.pts_history.push_back(pts); // seek finished because we can ensure no packet before seek decoded when render_pts0 is set //qDebug("pts0: %f, pts: %f, clock: %d", d.render_pts0, pts, d.clock->clockType()); if (d.render_pts0 >= 0.0) { if (pts < d.render_pts0) { if (!pkt.isEOF()) pkt = Packet(); v_a = 0; continue; } d.render_pts0 = -1; qDebug("video seek finished @%f. id: %d", pts, sync_id); d.clock->syncEndOnce(sync_id); Q_EMIT seekFinished(qint64(pts*1000.0)); if (seek_count == -1) seek_count = 1; else if (seek_count > 0) seek_count++; } if (skip_render) { qDebug("skip rendering @%.3f", pts); pkt = Packet(); v_a = 0; continue; } Q_ASSERT(d.statistics); d.statistics->video.current_time = QTime(0, 0, 0).addMSecs(int(pts * 1000.0)); //TODO: is it expensive? applyFilters(frame); //while can pause, processNextTask, not call outset.puase which is deperecated while (d.outputSet->canPauseThread()) { d.outputSet->pauseThread(100); //tryPause(100); processNextTask(); } //qDebug("force fps: %f dt: %d", d.force_fps, d.force_dt); if (d.force_dt > 0) {// && qFuzzyCompare(d.clock->speed(), 1.0)) { const qint64 now = QDateTime::currentMSecsSinceEpoch(); const qint64 delta = qint64(d.force_dt) - (now - last_deliver_time); if (frame.timestamp() <= 0) { // TODO: what if seek happens during playback? const int msecs_started(now + qMax(0LL, delta) - start_time); frame.setTimestamp(qreal(msecs_started)/1000.0); clock()->updateValue(frame.timestamp()); //external clock? } if (delta > 0LL) { // limit up bound? waitAndCheck((ulong)delta, -1); // wait and not compare pts-clock } } else if (false) { //FIXME: may block a while when seeking const qreal display_wait = pts - clock()->value(); if (!seeking && display_wait > 0.0) { // wait to pts reaches. TODO: count rendering time //qDebug("wait %f to display for pts %f-%f", display_wait, pts, clock()->value()); if (display_wait < 1.0) waitAndCheck(display_wait*1000UL, pts); // TODO: count decoding and filter time } } // no return even if d.stop is true. ensure frame is displayed. otherwise playing an image may be failed to display if (!deliverVideoFrame(frame)) continue; //qDebug("clock.diff: %.3f", d.clock->diff()); if (d.force_dt > 0) last_deliver_time = QDateTime::currentMSecsSinceEpoch(); // TODO: store original frame. now the frame is filtered and maybe converted to renderer perferred format d.displayed_frame = frame; if (d.clock->clockType() == AVClock::AudioClock) { const qreal v_a_ = frame.timestamp() - d.clock->value(); if (!qFuzzyIsNull(v_a_)) { if (v_a_ < -0.1) { if (v_a <= v_a_) v_a += -0.01; else v_a = (v_a_ +v_a)*0.5; } else if (v_a_ < -0.002) { v_a += -0.001; } else if (v_a_ < 0.002) { } else if (v_a_ < 0.1) { v_a += 0.001; } else { if (v_a >= v_a_) v_a += 0.01; else v_a = (v_a_ +v_a)*0.5; } if (v_a < -2 || v_a > 2) v_a /= 2.0; } //qDebug("v_a:%.4f, v_a_: %.4f", v_a, v_a_); } } #if 0 if (d.stop) {// user stop // decode eof? qDebug("decoding eof..."); while (d.dec && d.dec->decode(Packet::createEOF())) {d.dec->flush();} } #endif d.packets.clear(); qDebug("Video thread stops running..."); } } //namespace QtAV QtAV-1.12.0/src/VideoThread.h000066400000000000000000000042271312235004300155420ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOTHREAD_H #define QTAV_VIDEOTHREAD_H #include "AVThread.h" #include namespace QtAV { class VideoCapture; class VideoFrame; class VideoThreadPrivate; class VideoThread : public AVThread { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoThread) public: explicit VideoThread(QObject *parent = 0); VideoCapture *setVideoCapture(VideoCapture* cap); //ensure thread safe VideoCapture *videoCapture() const; VideoFrame displayedFrame() const; void setFrameRate(qreal value); //virtual bool event(QEvent *event); void setBrightness(int val); void setContrast(int val); void setSaturation(int val); void setEQ(int b, int c, int s); public Q_SLOTS: void addCaptureTask(); void clearRenderers(); protected: void applyFilters(VideoFrame& frame); // deliver video frame to video renderers. frame may be converted to a suitable format for renderer bool deliverVideoFrame(VideoFrame &frame); virtual void run(); // wait for value msec. every usleep is a small time, then process next task and get new delay }; } //namespace QtAV #endif // QTAV_VIDEOTHREAD_H QtAV-1.12.0/src/capi/000077500000000000000000000000001312235004300141025ustar00rootroot00000000000000QtAV-1.12.0/src/capi/ass_api.cpp000066400000000000000000000221721312235004300162310ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2014-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ //%DEFS% #define ASS_CAPI_BUILD //#define CAPI_IS_LAZY_RESOLVE 0 #ifndef CAPI_LINK_ASS #include "capi.h" #endif //CAPI_LINK_ASS #include "ass_api.h" //include last to avoid covering types later namespace ass { #ifdef CAPI_LINK_ASS api::api(){dll=0;} api::~api(){} bool api::loaded() const{return true;} #else static const char* names[] = { "ass", //%LIBS% #ifdef CAPI_TARGET_OS_WIN "libass", #endif NULL }; typedef ::capi::dso user_dso; //%DSO% #if 1 static const int versions[] = { ::capi::NoVersion, // the following line will be replaced by the content of config/ass/version if exists 5, 4, ::capi::EndVersion }; CAPI_BEGIN_DLL_VER(names, versions, user_dso) # else CAPI_BEGIN_DLL(names, user_dso) # endif //1 // CAPI_DEFINE_RESOLVER(argc, return_type, name, argv_no_name) // mkapi code generation BEGIN //CAPI_DEFINE_ENTRY(int, ass_library_version, CAPI_ARG0()) CAPI_DEFINE_ENTRY(ASS_Library *, ass_library_init, CAPI_ARG0()) CAPI_DEFINE_ENTRY(void, ass_library_done, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE_ENTRY(void, ass_set_fonts_dir, CAPI_ARG2(ASS_Library *, const char *)) CAPI_DEFINE_ENTRY(void, ass_set_extract_fonts, CAPI_ARG2(ASS_Library *, int)) CAPI_DEFINE_ENTRY(void, ass_set_style_overrides, CAPI_ARG2(ASS_Library *, char **)) //CAPI_DEFINE_ENTRY(void, ass_process_force_style, CAPI_ARG1(ASS_Track *)) CAPI_DEFINE_ENTRY(void, ass_set_message_cb, CAPI_ARG3(ASS_Library *, void (*msg_cb) (int level, const char *fmt, va_list args, void *data), void *)) CAPI_DEFINE_ENTRY(ASS_Renderer *, ass_renderer_init, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE_ENTRY(void, ass_renderer_done, CAPI_ARG1(ASS_Renderer *)) CAPI_DEFINE_ENTRY(void, ass_set_frame_size, CAPI_ARG3(ASS_Renderer *, int, int)) //CAPI_DEFINE_ENTRY(void, ass_set_storage_size, CAPI_ARG3(ASS_Renderer *, int, int)) CAPI_DEFINE_ENTRY(void, ass_set_shaper, CAPI_ARG2(ASS_Renderer *, ASS_ShapingLevel)) CAPI_DEFINE_ENTRY(void, ass_set_margins, CAPI_ARG5(ASS_Renderer *, int, int, int, int)) CAPI_DEFINE_ENTRY(void, ass_set_use_margins, CAPI_ARG2(ASS_Renderer *, int)) //CAPI_DEFINE_ENTRY(void, ass_set_pixel_aspect, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE_ENTRY(void, ass_set_aspect_ratio, CAPI_ARG3(ASS_Renderer *, double, double)) CAPI_DEFINE_ENTRY(void, ass_set_font_scale, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE_ENTRY(void, ass_set_hinting, CAPI_ARG2(ASS_Renderer *, ASS_Hinting)) //CAPI_DEFINE_ENTRY(void, ass_set_line_spacing, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE_ENTRY(void, ass_set_line_position, CAPI_ARG2(ASS_Renderer *, double)) CAPI_DEFINE_ENTRY(void, ass_set_fonts, CAPI_ARG6(ASS_Renderer *, const char *, const char *, int, const char *, int)) //CAPI_DEFINE_ENTRY(void, ass_set_selective_style_override_enabled, CAPI_ARG2(ASS_Renderer *, int)) //CAPI_DEFINE_ENTRY(void, ass_set_selective_style_override, CAPI_ARG2(ASS_Renderer *, ASS_Style *)) CAPI_DEFINE_ENTRY(int, ass_fonts_update, CAPI_ARG1(ASS_Renderer *)) //CAPI_DEFINE_ENTRY(void, ass_set_cache_limits, CAPI_ARG3(ASS_Renderer *, int, int)) CAPI_DEFINE_ENTRY(ASS_Image *, ass_render_frame, CAPI_ARG4(ASS_Renderer *, ASS_Track *, long long, int *)) CAPI_DEFINE_ENTRY(ASS_Track *, ass_new_track, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE_ENTRY(void, ass_free_track, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE_ENTRY(int, ass_alloc_style, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE_ENTRY(int, ass_alloc_event, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE_ENTRY(void, ass_free_style, CAPI_ARG2(ASS_Track *, int)) //CAPI_DEFINE_ENTRY(void, ass_free_event, CAPI_ARG2(ASS_Track *, int)) CAPI_DEFINE_ENTRY(void, ass_process_data, CAPI_ARG3(ASS_Track *, char *, int)) CAPI_DEFINE_ENTRY(void, ass_process_codec_private, CAPI_ARG3(ASS_Track *, char *, int)) CAPI_DEFINE_ENTRY(void, ass_process_chunk, CAPI_ARG5(ASS_Track *, char *, int, long long, long long)) CAPI_DEFINE_ENTRY(void, ass_set_check_readorder, CAPI_ARG2(ASS_Track *, int)) CAPI_DEFINE_ENTRY(void, ass_flush_events, CAPI_ARG1(ASS_Track *)) CAPI_DEFINE_ENTRY(ASS_Track *, ass_read_file, CAPI_ARG3(ASS_Library *, char *, char *)) CAPI_DEFINE_ENTRY(ASS_Track *, ass_read_memory, CAPI_ARG4(ASS_Library *, char *, size_t, char *)) //CAPI_DEFINE_ENTRY(int, ass_read_styles, CAPI_ARG3(ASS_Track *, char *, char *)) //CAPI_DEFINE_ENTRY(void, ass_add_font, CAPI_ARG4(ASS_Library *, char *, char *, int)) //CAPI_DEFINE_ENTRY(void, ass_clear_fonts, CAPI_ARG1(ASS_Library *)) //CAPI_DEFINE_ENTRY(long long, ass_step_sub, CAPI_ARG3(ASS_Track *, long long, int)) // mkapi code generation END CAPI_END_DLL() CAPI_DEFINE_DLL // CAPI_DEFINE(argc, return_type, name, argv_no_name) typedef void (*ass_set_message_cb_msg_cb1) (int level, const char *fmt, va_list args, void *data); // mkapi code generation BEGIN //CAPI_DEFINE(int, ass_library_version, CAPI_ARG0()) CAPI_DEFINE(ASS_Library *, ass_library_init, CAPI_ARG0()) CAPI_DEFINE(void, ass_library_done, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE(void, ass_set_fonts_dir, CAPI_ARG2(ASS_Library *, const char *)) CAPI_DEFINE(void, ass_set_extract_fonts, CAPI_ARG2(ASS_Library *, int)) CAPI_DEFINE(void, ass_set_style_overrides, CAPI_ARG2(ASS_Library *, char **)) //CAPI_DEFINE(void, ass_process_force_style, CAPI_ARG1(ASS_Track *)) CAPI_DEFINE(void, ass_set_message_cb, CAPI_ARG3(ASS_Library *, ass_set_message_cb_msg_cb1, void *)) CAPI_DEFINE(ASS_Renderer *, ass_renderer_init, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE(void, ass_renderer_done, CAPI_ARG1(ASS_Renderer *)) CAPI_DEFINE(void, ass_set_frame_size, CAPI_ARG3(ASS_Renderer *, int, int)) //CAPI_DEFINE(void, ass_set_storage_size, CAPI_ARG3(ASS_Renderer *, int, int)) CAPI_DEFINE(void, ass_set_shaper, CAPI_ARG2(ASS_Renderer *, ASS_ShapingLevel)) CAPI_DEFINE(void, ass_set_margins, CAPI_ARG5(ASS_Renderer *, int, int, int, int)) CAPI_DEFINE(void, ass_set_use_margins, CAPI_ARG2(ASS_Renderer *, int)) //CAPI_DEFINE(void, ass_set_pixel_aspect, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE(void, ass_set_aspect_ratio, CAPI_ARG3(ASS_Renderer *, double, double)) CAPI_DEFINE(void, ass_set_font_scale, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE(void, ass_set_hinting, CAPI_ARG2(ASS_Renderer *, ASS_Hinting)) //CAPI_DEFINE(void, ass_set_line_spacing, CAPI_ARG2(ASS_Renderer *, double)) //CAPI_DEFINE(void, ass_set_line_position, CAPI_ARG2(ASS_Renderer *, double)) CAPI_DEFINE(void, ass_set_fonts, CAPI_ARG6(ASS_Renderer *, const char *, const char *, int, const char *, int)) //CAPI_DEFINE(void, ass_set_selective_style_override_enabled, CAPI_ARG2(ASS_Renderer *, int)) //CAPI_DEFINE(void, ass_set_selective_style_override, CAPI_ARG2(ASS_Renderer *, ASS_Style *)) CAPI_DEFINE(int, ass_fonts_update, CAPI_ARG1(ASS_Renderer *)) //CAPI_DEFINE(void, ass_set_cache_limits, CAPI_ARG3(ASS_Renderer *, int, int)) CAPI_DEFINE(ASS_Image *, ass_render_frame, CAPI_ARG4(ASS_Renderer *, ASS_Track *, long long, int *)) CAPI_DEFINE(ASS_Track *, ass_new_track, CAPI_ARG1(ASS_Library *)) CAPI_DEFINE(void, ass_free_track, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE(int, ass_alloc_style, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE(int, ass_alloc_event, CAPI_ARG1(ASS_Track *)) //CAPI_DEFINE(void, ass_free_style, CAPI_ARG2(ASS_Track *, int)) //CAPI_DEFINE(void, ass_free_event, CAPI_ARG2(ASS_Track *, int)) CAPI_DEFINE(void, ass_process_data, CAPI_ARG3(ASS_Track *, char *, int)) CAPI_DEFINE(void, ass_process_codec_private, CAPI_ARG3(ASS_Track *, char *, int)) CAPI_DEFINE(void, ass_process_chunk, CAPI_ARG5(ASS_Track *, char *, int, long long, long long)) CAPI_DEFINE(void, ass_set_check_readorder, CAPI_ARG2(ASS_Track *, int)) CAPI_DEFINE(void, ass_flush_events, CAPI_ARG1(ASS_Track *)) CAPI_DEFINE(ASS_Track *, ass_read_file, CAPI_ARG3(ASS_Library *, char *, char *)) CAPI_DEFINE(ASS_Track *, ass_read_memory, CAPI_ARG4(ASS_Library *, char *, size_t, char *)) //CAPI_DEFINE(int, ass_read_styles, CAPI_ARG3(ASS_Track *, char *, char *)) //CAPI_DEFINE(void, ass_add_font, CAPI_ARG4(ASS_Library *, char *, char *, int)) //CAPI_DEFINE(void, ass_clear_fonts, CAPI_ARG1(ASS_Library *)) //CAPI_DEFINE(long long, ass_step_sub, CAPI_ARG3(ASS_Track *, long long, int)) // mkapi code generation END #endif //CAPI_LINK_ASS } //namespace ass //this file is generated by "mkapi.sh -name ass /usr/local/include/ass/ass.h" QtAV-1.12.0/src/capi/ass_api.h000066400000000000000000000125321312235004300156750ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2014-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef ASS_API_H #define ASS_API_H // no need to include the C header if only functions declared there #ifndef CAPI_LINK_ASS namespace ass { namespace capi { #else extern "C" { #endif // the following line will be replaced by the content of config/ASS/include if exists #include "ass/ass.h" #ifndef CAPI_LINK_ASS } #endif } namespace ass { #ifndef CAPI_LINK_ASS using namespace capi; // original header is in namespace capi, types are changed #endif // CAPI_LINK_ASS namespace capi { bool loaded();} // For link or NS style. Or load test for class style. api.loaded for class style. class api_dll; class api { api_dll *dll; public: api(); virtual ~api(); virtual bool loaded() const; // user may inherits multiple api classes: final::loaded() { return base1::loaded() && base2::loaded();} #if !defined(CAPI_LINK_ASS) && !defined(ASS_CAPI_NS) // mkapi code generation BEGIN int ass_library_version(); ASS_Library * ass_library_init(); void ass_library_done(ASS_Library * priv); void ass_set_fonts_dir(ASS_Library * priv, const char * fonts_dir); void ass_set_extract_fonts(ASS_Library * priv, int extract); void ass_set_style_overrides(ASS_Library * priv, char ** list); void ass_process_force_style(ASS_Track * track); void ass_set_message_cb(ASS_Library * priv, void (*msg_cb) (int level, const char *fmt, va_list args, void *data), void * data); ASS_Renderer * ass_renderer_init(ASS_Library *); void ass_renderer_done(ASS_Renderer * priv); void ass_set_frame_size(ASS_Renderer * priv, int w, int h); void ass_set_storage_size(ASS_Renderer * priv, int w, int h); void ass_set_shaper(ASS_Renderer * priv, ASS_ShapingLevel level); void ass_set_margins(ASS_Renderer * priv, int t, int b, int l, int r); void ass_set_use_margins(ASS_Renderer * priv, int use); void ass_set_pixel_aspect(ASS_Renderer * priv, double par); void ass_set_aspect_ratio(ASS_Renderer * priv, double dar, double sar); void ass_set_font_scale(ASS_Renderer * priv, double font_scale); void ass_set_hinting(ASS_Renderer * priv, ASS_Hinting ht); void ass_set_line_spacing(ASS_Renderer * priv, double line_spacing); void ass_set_line_position(ASS_Renderer * priv, double line_position); void ass_set_fonts(ASS_Renderer * priv, const char * default_font, const char * default_family, int dfp, const char * config, int update); void ass_set_selective_style_override_enabled(ASS_Renderer * priv, int bits); void ass_set_selective_style_override(ASS_Renderer * priv, ASS_Style * style); int ass_fonts_update(ASS_Renderer * priv); void ass_set_cache_limits(ASS_Renderer * priv, int glyph_max, int bitmap_max_size); ASS_Image * ass_render_frame(ASS_Renderer * priv, ASS_Track * track, long long now, int * detect_change); ASS_Track * ass_new_track(ASS_Library *); void ass_free_track(ASS_Track * track); int ass_alloc_style(ASS_Track * track); int ass_alloc_event(ASS_Track * track); void ass_free_style(ASS_Track * track, int sid); void ass_free_event(ASS_Track * track, int eid); void ass_process_data(ASS_Track * track, char * data, int size); void ass_process_codec_private(ASS_Track * track, char * data, int size); void ass_process_chunk(ASS_Track * track, char * data, int size, long long timecode, long long duration); void ass_set_check_readorder(ASS_Track *track, int check_readorder); void ass_flush_events(ASS_Track * track); ASS_Track * ass_read_file(ASS_Library * library, char * fname, char * codepage); ASS_Track * ass_read_memory(ASS_Library * library, char * buf, size_t bufsize, char * codepage); int ass_read_styles(ASS_Track * track, char * fname, char * codepage); void ass_add_font(ASS_Library * library, char * name, char * data, int data_size); void ass_clear_fonts(ASS_Library * library); long long ass_step_sub(ASS_Track * track, long long now, int movement); // mkapi code generation END #endif // !defined(CAPI_LINK_ASS) && !defined(ASS_CAPI_NS) }; } //namespace ass #ifndef ASS_CAPI_BUILD // avoid ambiguous in ass_api.cpp #if defined(ASS_CAPI_NS) && !defined(CAPI_LINK_ASS) using namespace ass::capi; #else using namespace ass; #endif #endif //ASS_CAPI_BUILD #endif // ASS_API_H //this file is generated by "mkapi.sh -name ass /usr/local/include/ass/ass.h" QtAV-1.12.0/src/capi/egl_api.cpp000066400000000000000000000301521312235004300162070ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2015-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #define CAPI_LINKAGE EGLAPIENTRY // for functions defined in namespace egl::capi #define EGL_CAPI_BUILD //#define CAPI_IS_LAZY_RESOLVE 0 #ifndef CAPI_LINK_EGL #include "capi.h" #endif //CAPI_LINK_EGL #include "egl_api.h" //include last to avoid covering types later // TODO: winrt resolver use GetModuleHandle first? namespace egl { #ifdef CAPI_LINK_EGL api::api(){dll=0;} api::~api(){} bool api::loaded() const{return true;} #else static const char* names[] = { #ifdef CAPI_TARGET_OS_WIN #if defined(QT_CORE_LIB) && !defined(QT_NO_DEBUG) "libEGLd", "libEGL", #else "libEGL", "libEGLd", #endif //defined(QT_CORE_LIB) && !defined(QT_NO_DEBUG) #else "EGL", #endif NULL }; class EGLLib : public ::capi::dso { public: virtual void* resolve(const char *symbol) { #ifndef __MINGW32__ return (void*)dso::resolve(symbol); #endif // from qwindowseglcontext.cpp. Play nice with 32-bit mingw: Try func first, then func@0, func@4, ..., func@64. // The def file does not provide any aliases in libEGL and libGLESv2 in these builds which results in exporting function names like eglInitialize@12. // This cannot be fixed without breaking binary compatibility. So be flexible here instead. void *proc = 0; for (int n = -4; !proc && n <= 64; n += 4) { if (n < 0) { proc = (void*)dso::resolve(symbol); continue; } char name[512]; CAPI_SNPRINTF(name, sizeof(name), "%s@%d", symbol, n); proc = (void*)dso::resolve(name); if (proc) fprintf(stderr, "%s=>%s\n", symbol, name); } return proc; } }; typedef EGLLib user_dso; #if 1 static const int versions[] = { ::capi::NoVersion, // the following line will be replaced by the content of config/egl/version if exists 1, ::capi::EndVersion }; CAPI_BEGIN_DLL_VER(names, versions, user_dso) # else CAPI_BEGIN_DLL(names, user_dso) # endif //CAPI_HAS_EGL_VERSION // CAPI_DEFINE_RESOLVER(argc, return_type, name, argv_no_name) // mkapi code generation BEGIN #ifdef EGL_VERSION_1_0 CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglChooseConfig, CAPI_ARG5(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglCopyBuffers, CAPI_ARG3(EGLDisplay, EGLSurface, EGLNativePixmapType)) CAPI_DEFINE_M_ENTRY(EGLContext, EGLAPIENTRY, eglCreateContext, CAPI_ARG4(EGLDisplay, EGLConfig, EGLContext, const EGLint *)) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreatePbufferSurface, CAPI_ARG3(EGLDisplay, EGLConfig, const EGLint *)) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreatePixmapSurface, CAPI_ARG4(EGLDisplay, EGLConfig, EGLNativePixmapType, const EGLint *)) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreateWindowSurface, CAPI_ARG4(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglDestroyContext, CAPI_ARG2(EGLDisplay, EGLContext)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglDestroySurface, CAPI_ARG2(EGLDisplay, EGLSurface)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglGetConfigAttrib, CAPI_ARG4(EGLDisplay, EGLConfig, EGLint, EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglGetConfigs, CAPI_ARG4(EGLDisplay, EGLConfig *, EGLint, EGLint *)) CAPI_DEFINE_M_ENTRY(EGLDisplay, EGLAPIENTRY, eglGetCurrentDisplay, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglGetCurrentSurface, CAPI_ARG1(EGLint)) CAPI_DEFINE_M_ENTRY(EGLDisplay, EGLAPIENTRY, eglGetDisplay, CAPI_ARG1(EGLNativeDisplayType)) CAPI_DEFINE_M_ENTRY(EGLint, EGLAPIENTRY, eglGetError, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(__eglMustCastToProperFunctionPointerType, EGLAPIENTRY, eglGetProcAddress, CAPI_ARG1(const char *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglInitialize, CAPI_ARG3(EGLDisplay, EGLint *, EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglMakeCurrent, CAPI_ARG4(EGLDisplay, EGLSurface, EGLSurface, EGLContext)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglQueryContext, CAPI_ARG4(EGLDisplay, EGLContext, EGLint, EGLint *)) CAPI_DEFINE_M_ENTRY(const char *, EGLAPIENTRY, eglQueryString, CAPI_ARG2(EGLDisplay, EGLint)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglQuerySurface, CAPI_ARG4(EGLDisplay, EGLSurface, EGLint, EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglSwapBuffers, CAPI_ARG2(EGLDisplay, EGLSurface)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglTerminate, CAPI_ARG1(EGLDisplay)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglWaitGL, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglWaitNative, CAPI_ARG1(EGLint)) #endif //EGL_VERSION_1_0 #ifdef EGL_VERSION_1_1 CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglBindTexImage, CAPI_ARG3(EGLDisplay, EGLSurface, EGLint)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglReleaseTexImage, CAPI_ARG3(EGLDisplay, EGLSurface, EGLint)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglSurfaceAttrib, CAPI_ARG4(EGLDisplay, EGLSurface, EGLint, EGLint)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglSwapInterval, CAPI_ARG2(EGLDisplay, EGLint)) #endif //EGL_VERSION_1_1 #ifdef EGL_VERSION_1_2 CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglBindAPI, CAPI_ARG1(EGLenum)) CAPI_DEFINE_M_ENTRY(EGLenum, EGLAPIENTRY, eglQueryAPI, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreatePbufferFromClientBuffer, CAPI_ARG5(EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglReleaseThread, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglWaitClient, CAPI_ARG0()) #endif //EGL_VERSION_1_2 #ifdef EGL_VERSION_1_4 CAPI_DEFINE_M_ENTRY(EGLContext, EGLAPIENTRY, eglGetCurrentContext, CAPI_ARG0()) #endif //EGL_VERSION_1_4 #ifdef EGL_VERSION_1_5 CAPI_DEFINE_M_ENTRY(EGLSync, EGLAPIENTRY, eglCreateSync, CAPI_ARG3(EGLDisplay, EGLenum, const EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglDestroySync, CAPI_ARG2(EGLDisplay, EGLSync)) CAPI_DEFINE_M_ENTRY(EGLint, EGLAPIENTRY, eglClientWaitSync, CAPI_ARG4(EGLDisplay, EGLSync, EGLint, EGLTime)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglGetSyncAttrib, CAPI_ARG4(EGLDisplay, EGLSync, EGLint, EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLImage, EGLAPIENTRY, eglCreateImage, CAPI_ARG5(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglDestroyImage, CAPI_ARG2(EGLDisplay, EGLImage)) CAPI_DEFINE_M_ENTRY(EGLDisplay, EGLAPIENTRY, eglGetPlatformDisplay, CAPI_ARG3(EGLenum, void *, const EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreatePlatformWindowSurface, CAPI_ARG4(EGLDisplay, EGLConfig, void *, const EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLSurface, EGLAPIENTRY, eglCreatePlatformPixmapSurface, CAPI_ARG4(EGLDisplay, EGLConfig, void *, const EGLAttrib *)) CAPI_DEFINE_M_ENTRY(EGLBoolean, EGLAPIENTRY, eglWaitSync, CAPI_ARG3(EGLDisplay, EGLSync, EGLint)) #endif //EGL_VERSION_1_5 // mkapi code generation END CAPI_END_DLL() CAPI_DEFINE_DLL // CAPI_DEFINE(argc, return_type, name, argv_no_name) // mkapi code generation BEGIN #ifdef EGL_VERSION_1_0 CAPI_DEFINE(EGLBoolean, eglChooseConfig, CAPI_ARG5(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)) CAPI_DEFINE(EGLBoolean, eglCopyBuffers, CAPI_ARG3(EGLDisplay, EGLSurface, EGLNativePixmapType)) CAPI_DEFINE(EGLContext, eglCreateContext, CAPI_ARG4(EGLDisplay, EGLConfig, EGLContext, const EGLint *)) CAPI_DEFINE(EGLSurface, eglCreatePbufferSurface, CAPI_ARG3(EGLDisplay, EGLConfig, const EGLint *)) CAPI_DEFINE(EGLSurface, eglCreatePixmapSurface, CAPI_ARG4(EGLDisplay, EGLConfig, EGLNativePixmapType, const EGLint *)) CAPI_DEFINE(EGLSurface, eglCreateWindowSurface, CAPI_ARG4(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *)) CAPI_DEFINE(EGLBoolean, eglDestroyContext, CAPI_ARG2(EGLDisplay, EGLContext)) CAPI_DEFINE(EGLBoolean, eglDestroySurface, CAPI_ARG2(EGLDisplay, EGLSurface)) CAPI_DEFINE(EGLBoolean, eglGetConfigAttrib, CAPI_ARG4(EGLDisplay, EGLConfig, EGLint, EGLint *)) CAPI_DEFINE(EGLBoolean, eglGetConfigs, CAPI_ARG4(EGLDisplay, EGLConfig *, EGLint, EGLint *)) CAPI_DEFINE(EGLDisplay, eglGetCurrentDisplay, CAPI_ARG0()) CAPI_DEFINE(EGLSurface, eglGetCurrentSurface, CAPI_ARG1(EGLint)) CAPI_DEFINE(EGLDisplay, eglGetDisplay, CAPI_ARG1(EGLNativeDisplayType)) CAPI_DEFINE(EGLint, eglGetError, CAPI_ARG0()) CAPI_DEFINE(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, CAPI_ARG1(const char *)) CAPI_DEFINE(EGLBoolean, eglInitialize, CAPI_ARG3(EGLDisplay, EGLint *, EGLint *)) CAPI_DEFINE(EGLBoolean, eglMakeCurrent, CAPI_ARG4(EGLDisplay, EGLSurface, EGLSurface, EGLContext)) CAPI_DEFINE(EGLBoolean, eglQueryContext, CAPI_ARG4(EGLDisplay, EGLContext, EGLint, EGLint *)) CAPI_DEFINE(const char *, eglQueryString, CAPI_ARG2(EGLDisplay, EGLint)) CAPI_DEFINE(EGLBoolean, eglQuerySurface, CAPI_ARG4(EGLDisplay, EGLSurface, EGLint, EGLint *)) CAPI_DEFINE(EGLBoolean, eglSwapBuffers, CAPI_ARG2(EGLDisplay, EGLSurface)) CAPI_DEFINE(EGLBoolean, eglTerminate, CAPI_ARG1(EGLDisplay)) CAPI_DEFINE(EGLBoolean, eglWaitGL, CAPI_ARG0()) CAPI_DEFINE(EGLBoolean, eglWaitNative, CAPI_ARG1(EGLint)) #endif //EGL_VERSION_1_0 #ifdef EGL_VERSION_1_1 CAPI_DEFINE(EGLBoolean, eglBindTexImage, CAPI_ARG3(EGLDisplay, EGLSurface, EGLint)) CAPI_DEFINE(EGLBoolean, eglReleaseTexImage, CAPI_ARG3(EGLDisplay, EGLSurface, EGLint)) CAPI_DEFINE(EGLBoolean, eglSurfaceAttrib, CAPI_ARG4(EGLDisplay, EGLSurface, EGLint, EGLint)) CAPI_DEFINE(EGLBoolean, eglSwapInterval, CAPI_ARG2(EGLDisplay, EGLint)) #endif //EGL_VERSION_1_1 #ifdef EGL_VERSION_1_2 CAPI_DEFINE(EGLBoolean, eglBindAPI, CAPI_ARG1(EGLenum)) CAPI_DEFINE(EGLenum, eglQueryAPI, CAPI_ARG0()) CAPI_DEFINE(EGLSurface, eglCreatePbufferFromClientBuffer, CAPI_ARG5(EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *)) CAPI_DEFINE(EGLBoolean, eglReleaseThread, CAPI_ARG0()) CAPI_DEFINE(EGLBoolean, eglWaitClient, CAPI_ARG0()) #endif //EGL_VERSION_1_2 #ifdef EGL_VERSION_1_4 CAPI_DEFINE(EGLContext, eglGetCurrentContext, CAPI_ARG0()) #endif //EGL_VERSION_1_4 #ifdef EGL_VERSION_1_5 CAPI_DEFINE(EGLSync, eglCreateSync, CAPI_ARG3(EGLDisplay, EGLenum, const EGLAttrib *)) CAPI_DEFINE(EGLBoolean, eglDestroySync, CAPI_ARG2(EGLDisplay, EGLSync)) CAPI_DEFINE(EGLint, eglClientWaitSync, CAPI_ARG4(EGLDisplay, EGLSync, EGLint, EGLTime)) CAPI_DEFINE(EGLBoolean, eglGetSyncAttrib, CAPI_ARG4(EGLDisplay, EGLSync, EGLint, EGLAttrib *)) CAPI_DEFINE(EGLImage, eglCreateImage, CAPI_ARG5(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)) CAPI_DEFINE(EGLBoolean, eglDestroyImage, CAPI_ARG2(EGLDisplay, EGLImage)) CAPI_DEFINE(EGLDisplay, eglGetPlatformDisplay, CAPI_ARG3(EGLenum, void *, const EGLAttrib *)) CAPI_DEFINE(EGLSurface, eglCreatePlatformWindowSurface, CAPI_ARG4(EGLDisplay, EGLConfig, void *, const EGLAttrib *)) CAPI_DEFINE(EGLSurface, eglCreatePlatformPixmapSurface, CAPI_ARG4(EGLDisplay, EGLConfig, void *, const EGLAttrib *)) CAPI_DEFINE(EGLBoolean, eglWaitSync, CAPI_ARG3(EGLDisplay, EGLSync, EGLint)) #endif //EGL_VERSION_1_5 // mkapi code generation END #endif //CAPI_LINK_EGL } //namespace egl //this file is generated by "mkapi.sh -I /Users/wangbin/dev/qtbase/src/3rdparty/angle/include -name egl -template capi /Users/wangbin/dev/qtbase/src/3rdparty/angle/include/EGL/egl.h" QtAV-1.12.0/src/capi/egl_api.h000066400000000000000000000144211312235004300156550ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2015-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef EGL_API_H #define EGL_API_H // winrt: must define CAPI_LINK_EGL // no need to include the C header if only functions declared there #ifndef CAPI_LINK_EGL namespace egl { namespace capi { #define EGLAPI // avoid warning and link error #else extern "C" { #endif // the following line will be replaced by the content of config/EGL/include if exists #include "EGL/egl.h" #ifndef CAPI_LINK_EGL } #endif } namespace egl { #ifndef CAPI_LINK_EGL using namespace capi; // original header is in namespace capi, types are changed #endif // CAPI_LINK_EGL namespace capi { bool loaded();} // For link or NS style. Or load test for class style. api.loaded for class style. class api_dll; class api { api_dll *dll; public: api(); virtual ~api(); virtual bool loaded() const; // user may inherits multiple api classes: final::loaded() { return base1::loaded() && base2::loaded();} #if !defined(CAPI_LINK_EGL) && !defined(EGL_CAPI_NS) // mkapi code generation BEGIN #ifdef EGL_VERSION_1_0 EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint * attrib_list, EGLConfig * configs, EGLint config_size, EGLint * num_config); EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target); EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint * attrib_list); EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint * attrib_list); EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint * attrib_list); EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint * attrib_list); EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx); EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface); EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint * value); EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig * configs, EGLint config_size, EGLint * num_config); EGLDisplay eglGetCurrentDisplay(); EGLSurface eglGetCurrentSurface(EGLint readdraw); EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); EGLint eglGetError(); __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char * procname); EGLBoolean eglInitialize(EGLDisplay dpy, EGLint * major, EGLint * minor); EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint * value); const char * eglQueryString(EGLDisplay dpy, EGLint name); EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint * value); EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); EGLBoolean eglTerminate(EGLDisplay dpy); EGLBoolean eglWaitGL(); EGLBoolean eglWaitNative(EGLint engine); #endif //EGL_VERSION_1_0 #ifdef EGL_VERSION_1_1 EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval); #endif //EGL_VERSION_1_1 #ifdef EGL_VERSION_1_2 EGLBoolean eglBindAPI(EGLenum api); EGLenum eglQueryAPI(); EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint * attrib_list); EGLBoolean eglReleaseThread(); EGLBoolean eglWaitClient(); #endif //EGL_VERSION_1_2 #ifdef EGL_VERSION_1_4 EGLContext eglGetCurrentContext(); #endif //EGL_VERSION_1_4 #ifdef EGL_VERSION_1_5 EGLSync eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib * attrib_list); EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSync sync); EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib * value); EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list); EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImage image); EGLDisplay eglGetPlatformDisplay(EGLenum platform, void * native_display, const EGLAttrib * attrib_list); EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void * native_window, const EGLAttrib * attrib_list); EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void * native_pixmap, const EGLAttrib * attrib_list); EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags); #endif //EGL_VERSION_1_5 // mkapi code generation END #endif // !defined(CAPI_LINK_EGL) && !defined(EGL_CAPI_NS) }; } //namespace egl #ifndef EGL_CAPI_BUILD // avoid ambiguous in egl_api.cpp #if defined(EGL_CAPI_NS) && !defined(CAPI_LINK_EGL) using namespace egl::capi; #else using namespace egl; #endif #endif //EGL_CAPI_BUILD #endif // EGL_API_H //this file is generated by "mkapi.sh -I /Users/wangbin/dev/qtbase/src/3rdparty/angle/include -name egl -template capi /Users/wangbin/dev/qtbase/src/3rdparty/angle/include/EGL/egl.h" QtAV-1.12.0/src/capi/openal_api.cpp000066400000000000000000000413341312235004300167220ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2014-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ //%DEFS% #define OPENAL_CAPI_BUILD //#define DEBUG_RESOLVE //#define DEBUG_LOAD // why affects other xxx_api.cpp? //#define CAPI_IS_LAZY_RESOLVE 0 #ifndef CAPI_LINK_OPENAL #include "capi.h" #endif //CAPI_LINK_OPENAL #include "openal_api.h" //include last to avoid covering types later namespace openal { #ifdef CAPI_LINK_OPENAL api::api(){dll=0;} api::~api(){} bool api::loaded() const{return true;} #else static const char* names[] = { "openal", #ifdef CAPI_TARGET_OS_WIN "OpenAL32", #endif #ifdef __APPLE__ "/System/Library/Frameworks/OpenAL.framework/OpenAL", // iOS and macOS #endif "OpenAL", //blackberry NULL }; typedef ::capi::dso user_dso; //%DSO% #if 1 static const int versions[] = { ::capi::NoVersion, // the following line will be replaced by the content of config/openal/version if exists 1, ::capi::EndVersion }; CAPI_BEGIN_DLL_VER(names, versions, user_dso) # else CAPI_BEGIN_DLL(names, user_dso) # endif //1 // CAPI_DEFINE_RESOLVER(argc, return_type, name, argv_no_name) // mkapi code generation BEGIN CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDopplerFactor, CAPI_ARG1(ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDopplerVelocity, CAPI_ARG1(ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSpeedOfSound, CAPI_ARG1(ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDistanceModel, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alEnable, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDisable, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(ALboolean, AL_APIENTRY, alIsEnabled, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(const ALchar *, AL_APIENTRY, alGetString, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBooleanv, CAPI_ARG2(ALenum, ALboolean *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetIntegerv, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetFloatv, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetDoublev, CAPI_ARG2(ALenum, ALdouble *)) CAPI_DEFINE_M_ENTRY(ALboolean, AL_APIENTRY, alGetBoolean, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(ALint, AL_APIENTRY, alGetInteger, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(ALfloat, AL_APIENTRY, alGetFloat, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(ALdouble, AL_APIENTRY, alGetDouble, CAPI_ARG1(ALenum)) CAPI_DEFINE_M_ENTRY(ALenum, AL_APIENTRY, alGetError, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(ALboolean, AL_APIENTRY, alIsExtensionPresent, CAPI_ARG1(const ALchar *)) CAPI_DEFINE_M_ENTRY(void *, AL_APIENTRY, alGetProcAddress, CAPI_ARG1(const ALchar *)) CAPI_DEFINE_M_ENTRY(ALenum, AL_APIENTRY, alGetEnumValue, CAPI_ARG1(const ALchar *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListenerf, CAPI_ARG2(ALenum, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListener3f, CAPI_ARG4(ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListenerfv, CAPI_ARG2(ALenum, const ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListeneri, CAPI_ARG2(ALenum, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListener3i, CAPI_ARG4(ALenum, ALint, ALint, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alListeneriv, CAPI_ARG2(ALenum, const ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListenerf, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListener3f, CAPI_ARG4(ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListenerfv, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListeneri, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListener3i, CAPI_ARG4(ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetListeneriv, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGenSources, CAPI_ARG2(ALsizei, ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDeleteSources, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(ALboolean, AL_APIENTRY, alIsSource, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcef, CAPI_ARG3(ALuint, ALenum, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSource3f, CAPI_ARG5(ALuint, ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcefv, CAPI_ARG3(ALuint, ALenum, const ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcei, CAPI_ARG3(ALuint, ALenum, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSource3i, CAPI_ARG5(ALuint, ALenum, ALint, ALint, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceiv, CAPI_ARG3(ALuint, ALenum, const ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSourcef, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSource3f, CAPI_ARG5(ALuint, ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSourcefv, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSourcei, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSource3i, CAPI_ARG5(ALuint, ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetSourceiv, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcePlayv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceStopv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceRewindv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcePausev, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcePlay, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceStop, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceRewind, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourcePause, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceQueueBuffers, CAPI_ARG3(ALuint, ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alSourceUnqueueBuffers, CAPI_ARG3(ALuint, ALsizei, ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGenBuffers, CAPI_ARG2(ALsizei, ALuint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alDeleteBuffers, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE_M_ENTRY(ALboolean, AL_APIENTRY, alIsBuffer, CAPI_ARG1(ALuint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBufferData, CAPI_ARG5(ALuint, ALenum, const ALvoid *, ALsizei, ALsizei)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBufferf, CAPI_ARG3(ALuint, ALenum, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBuffer3f, CAPI_ARG5(ALuint, ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBufferfv, CAPI_ARG3(ALuint, ALenum, const ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBufferi, CAPI_ARG3(ALuint, ALenum, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBuffer3i, CAPI_ARG5(ALuint, ALenum, ALint, ALint, ALint)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alBufferiv, CAPI_ARG3(ALuint, ALenum, const ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBufferf, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBuffer3f, CAPI_ARG5(ALuint, ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBufferfv, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBufferi, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBuffer3i, CAPI_ARG5(ALuint, ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE_M_ENTRY(void, AL_APIENTRY, alGetBufferiv, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE_M_ENTRY(ALCcontext *, ALC_APIENTRY, alcCreateContext, CAPI_ARG2(ALCdevice *, const ALCint*)) CAPI_DEFINE_M_ENTRY(ALCboolean, ALC_APIENTRY, alcMakeContextCurrent, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcProcessContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcSuspendContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcDestroyContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE_M_ENTRY(ALCcontext *, ALC_APIENTRY, alcGetCurrentContext, CAPI_ARG0()) CAPI_DEFINE_M_ENTRY(ALCdevice *, ALC_APIENTRY, alcGetContextsDevice, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE_M_ENTRY(ALCdevice *, ALC_APIENTRY, alcOpenDevice, CAPI_ARG1(const ALCchar *)) CAPI_DEFINE_M_ENTRY(ALCboolean, ALC_APIENTRY, alcCloseDevice, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE_M_ENTRY(ALCenum, ALC_APIENTRY, alcGetError, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE_M_ENTRY(ALCboolean, ALC_APIENTRY, alcIsExtensionPresent, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE_M_ENTRY(void *, ALC_APIENTRY, alcGetProcAddress, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE_M_ENTRY(ALCenum, ALC_APIENTRY, alcGetEnumValue, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE_M_ENTRY(const ALCchar *, ALC_APIENTRY, alcGetString, CAPI_ARG2(ALCdevice *, ALCenum)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcGetIntegerv, CAPI_ARG4(ALCdevice *, ALCenum, ALCsizei, ALCint *)) CAPI_DEFINE_M_ENTRY(ALCdevice *, ALC_APIENTRY, alcCaptureOpenDevice, CAPI_ARG4(const ALCchar *, ALCuint, ALCenum, ALCsizei)) CAPI_DEFINE_M_ENTRY(ALCboolean, ALC_APIENTRY, alcCaptureCloseDevice, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcCaptureStart, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcCaptureStop, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE_M_ENTRY(void, ALC_APIENTRY, alcCaptureSamples, CAPI_ARG3(ALCdevice *, ALCvoid *, ALCsizei)) // mkapi code generation END CAPI_END_DLL() CAPI_DEFINE_DLL // CAPI_DEFINE(argc, return_type, name, argv_no_name) // mkapi code generation BEGIN CAPI_DEFINE(void, alDopplerFactor, CAPI_ARG1(ALfloat)) CAPI_DEFINE(void, alDopplerVelocity, CAPI_ARG1(ALfloat)) CAPI_DEFINE(void, alSpeedOfSound, CAPI_ARG1(ALfloat)) CAPI_DEFINE(void, alDistanceModel, CAPI_ARG1(ALenum)) CAPI_DEFINE(void, alEnable, CAPI_ARG1(ALenum)) CAPI_DEFINE(void, alDisable, CAPI_ARG1(ALenum)) CAPI_DEFINE(ALboolean, alIsEnabled, CAPI_ARG1(ALenum)) CAPI_DEFINE(const ALchar *, alGetString, CAPI_ARG1(ALenum)) CAPI_DEFINE(void, alGetBooleanv, CAPI_ARG2(ALenum, ALboolean *)) CAPI_DEFINE(void, alGetIntegerv, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE(void, alGetFloatv, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE(void, alGetDoublev, CAPI_ARG2(ALenum, ALdouble *)) CAPI_DEFINE(ALboolean, alGetBoolean, CAPI_ARG1(ALenum)) CAPI_DEFINE(ALint, alGetInteger, CAPI_ARG1(ALenum)) CAPI_DEFINE(ALfloat, alGetFloat, CAPI_ARG1(ALenum)) CAPI_DEFINE(ALdouble, alGetDouble, CAPI_ARG1(ALenum)) CAPI_DEFINE(ALenum, alGetError, CAPI_ARG0()) CAPI_DEFINE(ALboolean, alIsExtensionPresent, CAPI_ARG1(const ALchar *)) CAPI_DEFINE(void *, alGetProcAddress, CAPI_ARG1(const ALchar *)) CAPI_DEFINE(ALenum, alGetEnumValue, CAPI_ARG1(const ALchar *)) CAPI_DEFINE(void, alListenerf, CAPI_ARG2(ALenum, ALfloat)) CAPI_DEFINE(void, alListener3f, CAPI_ARG4(ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE(void, alListenerfv, CAPI_ARG2(ALenum, const ALfloat *)) CAPI_DEFINE(void, alListeneri, CAPI_ARG2(ALenum, ALint)) CAPI_DEFINE(void, alListener3i, CAPI_ARG4(ALenum, ALint, ALint, ALint)) CAPI_DEFINE(void, alListeneriv, CAPI_ARG2(ALenum, const ALint *)) CAPI_DEFINE(void, alGetListenerf, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE(void, alGetListener3f, CAPI_ARG4(ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE(void, alGetListenerfv, CAPI_ARG2(ALenum, ALfloat *)) CAPI_DEFINE(void, alGetListeneri, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE(void, alGetListener3i, CAPI_ARG4(ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE(void, alGetListeneriv, CAPI_ARG2(ALenum, ALint *)) CAPI_DEFINE(void, alGenSources, CAPI_ARG2(ALsizei, ALuint *)) CAPI_DEFINE(void, alDeleteSources, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(ALboolean, alIsSource, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alSourcef, CAPI_ARG3(ALuint, ALenum, ALfloat)) CAPI_DEFINE(void, alSource3f, CAPI_ARG5(ALuint, ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE(void, alSourcefv, CAPI_ARG3(ALuint, ALenum, const ALfloat *)) CAPI_DEFINE(void, alSourcei, CAPI_ARG3(ALuint, ALenum, ALint)) CAPI_DEFINE(void, alSource3i, CAPI_ARG5(ALuint, ALenum, ALint, ALint, ALint)) CAPI_DEFINE(void, alSourceiv, CAPI_ARG3(ALuint, ALenum, const ALint *)) CAPI_DEFINE(void, alGetSourcef, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE(void, alGetSource3f, CAPI_ARG5(ALuint, ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE(void, alGetSourcefv, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE(void, alGetSourcei, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE(void, alGetSource3i, CAPI_ARG5(ALuint, ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE(void, alGetSourceiv, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE(void, alSourcePlayv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(void, alSourceStopv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(void, alSourceRewindv, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(void, alSourcePausev, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(void, alSourcePlay, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alSourceStop, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alSourceRewind, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alSourcePause, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alSourceQueueBuffers, CAPI_ARG3(ALuint, ALsizei, const ALuint *)) CAPI_DEFINE(void, alSourceUnqueueBuffers, CAPI_ARG3(ALuint, ALsizei, ALuint *)) CAPI_DEFINE(void, alGenBuffers, CAPI_ARG2(ALsizei, ALuint *)) CAPI_DEFINE(void, alDeleteBuffers, CAPI_ARG2(ALsizei, const ALuint *)) CAPI_DEFINE(ALboolean, alIsBuffer, CAPI_ARG1(ALuint)) CAPI_DEFINE(void, alBufferData, CAPI_ARG5(ALuint, ALenum, const ALvoid *, ALsizei, ALsizei)) CAPI_DEFINE(void, alBufferf, CAPI_ARG3(ALuint, ALenum, ALfloat)) CAPI_DEFINE(void, alBuffer3f, CAPI_ARG5(ALuint, ALenum, ALfloat, ALfloat, ALfloat)) CAPI_DEFINE(void, alBufferfv, CAPI_ARG3(ALuint, ALenum, const ALfloat *)) CAPI_DEFINE(void, alBufferi, CAPI_ARG3(ALuint, ALenum, ALint)) CAPI_DEFINE(void, alBuffer3i, CAPI_ARG5(ALuint, ALenum, ALint, ALint, ALint)) CAPI_DEFINE(void, alBufferiv, CAPI_ARG3(ALuint, ALenum, const ALint *)) CAPI_DEFINE(void, alGetBufferf, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE(void, alGetBuffer3f, CAPI_ARG5(ALuint, ALenum, ALfloat *, ALfloat *, ALfloat *)) CAPI_DEFINE(void, alGetBufferfv, CAPI_ARG3(ALuint, ALenum, ALfloat *)) CAPI_DEFINE(void, alGetBufferi, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE(void, alGetBuffer3i, CAPI_ARG5(ALuint, ALenum, ALint *, ALint *, ALint *)) CAPI_DEFINE(void, alGetBufferiv, CAPI_ARG3(ALuint, ALenum, ALint *)) CAPI_DEFINE(ALCcontext *, alcCreateContext, CAPI_ARG2(ALCdevice *, const ALCint*)) CAPI_DEFINE(ALCboolean, alcMakeContextCurrent, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE(void, alcProcessContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE(void, alcSuspendContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE(void, alcDestroyContext, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE(ALCcontext *, alcGetCurrentContext, CAPI_ARG0()) CAPI_DEFINE(ALCdevice *, alcGetContextsDevice, CAPI_ARG1(ALCcontext *)) CAPI_DEFINE(ALCdevice *, alcOpenDevice, CAPI_ARG1(const ALCchar *)) CAPI_DEFINE(ALCboolean, alcCloseDevice, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE(ALCenum, alcGetError, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE(ALCboolean, alcIsExtensionPresent, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE(void *, alcGetProcAddress, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE(ALCenum, alcGetEnumValue, CAPI_ARG2(ALCdevice *, const ALCchar *)) CAPI_DEFINE(const ALCchar *, alcGetString, CAPI_ARG2(ALCdevice *, ALCenum)) CAPI_DEFINE(void, alcGetIntegerv, CAPI_ARG4(ALCdevice *, ALCenum, ALCsizei, ALCint *)) CAPI_DEFINE(ALCdevice *, alcCaptureOpenDevice, CAPI_ARG4(const ALCchar *, ALCuint, ALCenum, ALCsizei)) CAPI_DEFINE(ALCboolean, alcCaptureCloseDevice, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE(void, alcCaptureStart, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE(void, alcCaptureStop, CAPI_ARG1(ALCdevice *)) CAPI_DEFINE(void, alcCaptureSamples, CAPI_ARG3(ALCdevice *, ALCvoid *, ALCsizei)) // mkapi code generation END #endif //CAPI_LINK_OPENAL } //namespace openal //this file is generated by "mkapi.sh -name openal /Users/wangbin/dev/openal-soft/include/AL/al.h /Users/wangbin/dev/openal-soft/include/AL/alc.h" QtAV-1.12.0/src/capi/openal_api.h000066400000000000000000000205671312235004300163740ustar00rootroot00000000000000/****************************************************************************** mkapi dynamic load code generation for capi template Copyright (C) 2014-2016 Wang Bin https://github.com/wang-bin/mkapi https://github.com/wang-bin/capi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef OPENAL_API_H #define OPENAL_API_H // no need to include the C header if only functions declared there #ifndef CAPI_LINK_OPENAL namespace openal { namespace capi { #define AL_LIBTYPE_STATIC // openal-soft AL_API dllimport error. mac's macro is AL_BUILD_LIBRARY #else extern "C" { #endif // the following line will be replaced by the content of config/OPENAL/include if exists #ifdef __APPLE__ #include #include #else #include #include #endif #ifndef CAPI_LINK_OPENAL } #endif } namespace openal { #ifndef CAPI_LINK_OPENAL using namespace capi; // original header is in namespace capi, types are changed #endif // CAPI_LINK_OPENAL namespace capi { bool loaded();} // For link or NS style. Or load test for class style. api.loaded for class style. class api_dll; class api { api_dll *dll; public: api(); virtual ~api(); virtual bool loaded() const; // user may inherits multiple api classes: final::loaded() { return base1::loaded() && base2::loaded();} #if !defined(CAPI_LINK_OPENAL) && !defined(OPENAL_CAPI_NS) // mkapi code generation BEGIN void alDopplerFactor(ALfloat value); void alDopplerVelocity(ALfloat value); void alSpeedOfSound(ALfloat value); void alDistanceModel(ALenum distanceModel); void alEnable(ALenum capability); void alDisable(ALenum capability); ALboolean alIsEnabled(ALenum capability); const ALchar * alGetString(ALenum param); void alGetBooleanv(ALenum param, ALboolean * values); void alGetIntegerv(ALenum param, ALint * values); void alGetFloatv(ALenum param, ALfloat * values); void alGetDoublev(ALenum param, ALdouble * values); ALboolean alGetBoolean(ALenum param); ALint alGetInteger(ALenum param); ALfloat alGetFloat(ALenum param); ALdouble alGetDouble(ALenum param); ALenum alGetError(); ALboolean alIsExtensionPresent(const ALchar * extname); void * alGetProcAddress(const ALchar * fname); ALenum alGetEnumValue(const ALchar * ename); void alListenerf(ALenum param, ALfloat value); void alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); void alListenerfv(ALenum param, const ALfloat * values); void alListeneri(ALenum param, ALint value); void alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); void alListeneriv(ALenum param, const ALint * values); void alGetListenerf(ALenum param, ALfloat * value); void alGetListener3f(ALenum param, ALfloat * value1, ALfloat * value2, ALfloat * value3); void alGetListenerfv(ALenum param, ALfloat * values); void alGetListeneri(ALenum param, ALint * value); void alGetListener3i(ALenum param, ALint * value1, ALint * value2, ALint * value3); void alGetListeneriv(ALenum param, ALint * values); void alGenSources(ALsizei n, ALuint * sources); void alDeleteSources(ALsizei n, const ALuint * sources); ALboolean alIsSource(ALuint source); void alSourcef(ALuint source, ALenum param, ALfloat value); void alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); void alSourcefv(ALuint source, ALenum param, const ALfloat * values); void alSourcei(ALuint source, ALenum param, ALint value); void alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); void alSourceiv(ALuint source, ALenum param, const ALint * values); void alGetSourcef(ALuint source, ALenum param, ALfloat * value); void alGetSource3f(ALuint source, ALenum param, ALfloat * value1, ALfloat * value2, ALfloat * value3); void alGetSourcefv(ALuint source, ALenum param, ALfloat * values); void alGetSourcei(ALuint source, ALenum param, ALint * value); void alGetSource3i(ALuint source, ALenum param, ALint * value1, ALint * value2, ALint * value3); void alGetSourceiv(ALuint source, ALenum param, ALint * values); void alSourcePlayv(ALsizei n, const ALuint * sources); void alSourceStopv(ALsizei n, const ALuint * sources); void alSourceRewindv(ALsizei n, const ALuint * sources); void alSourcePausev(ALsizei n, const ALuint * sources); void alSourcePlay(ALuint source); void alSourceStop(ALuint source); void alSourceRewind(ALuint source); void alSourcePause(ALuint source); void alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint * buffers); void alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint * buffers); void alGenBuffers(ALsizei n, ALuint * buffers); void alDeleteBuffers(ALsizei n, const ALuint * buffers); ALboolean alIsBuffer(ALuint buffer); void alBufferData(ALuint buffer, ALenum format, const ALvoid * data, ALsizei size, ALsizei freq); void alBufferf(ALuint buffer, ALenum param, ALfloat value); void alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); void alBufferfv(ALuint buffer, ALenum param, const ALfloat * values); void alBufferi(ALuint buffer, ALenum param, ALint value); void alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); void alBufferiv(ALuint buffer, ALenum param, const ALint * values); void alGetBufferf(ALuint buffer, ALenum param, ALfloat * value); void alGetBuffer3f(ALuint buffer, ALenum param, ALfloat * value1, ALfloat * value2, ALfloat * value3); void alGetBufferfv(ALuint buffer, ALenum param, ALfloat * values); void alGetBufferi(ALuint buffer, ALenum param, ALint * value); void alGetBuffer3i(ALuint buffer, ALenum param, ALint * value1, ALint * value2, ALint * value3); void alGetBufferiv(ALuint buffer, ALenum param, ALint * values); ALCcontext * alcCreateContext(ALCdevice * device, const ALCint* attrlist); ALCboolean alcMakeContextCurrent(ALCcontext * context); void alcProcessContext(ALCcontext * context); void alcSuspendContext(ALCcontext * context); void alcDestroyContext(ALCcontext * context); ALCcontext * alcGetCurrentContext(); ALCdevice * alcGetContextsDevice(ALCcontext * context); ALCdevice * alcOpenDevice(const ALCchar * devicename); ALCboolean alcCloseDevice(ALCdevice * device); ALCenum alcGetError(ALCdevice * device); ALCboolean alcIsExtensionPresent(ALCdevice * device, const ALCchar * extname); void * alcGetProcAddress(ALCdevice * device, const ALCchar * funcname); ALCenum alcGetEnumValue(ALCdevice * device, const ALCchar * enumname); const ALCchar * alcGetString(ALCdevice * device, ALCenum param); void alcGetIntegerv(ALCdevice * device, ALCenum param, ALCsizei size, ALCint * values); ALCdevice * alcCaptureOpenDevice(const ALCchar * devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); ALCboolean alcCaptureCloseDevice(ALCdevice * device); void alcCaptureStart(ALCdevice * device); void alcCaptureStop(ALCdevice * device); void alcCaptureSamples(ALCdevice * device, ALCvoid * buffer, ALCsizei samples); // mkapi code generation END #endif // !defined(CAPI_LINK_OPENAL) && !defined(OPENAL_CAPI_NS) }; } //namespace openal #ifndef OPENAL_CAPI_BUILD // avoid ambiguous in openal_api.cpp #if defined(OPENAL_CAPI_NS) && !defined(CAPI_LINK_OPENAL) using namespace openal::capi; #else using namespace openal; #endif #endif //OPENAL_CAPI_BUILD #endif // OPENAL_API_H //this file is generated by "mkapi.sh -name openal /Users/wangbin/dev/openal-soft/include/AL/al.h /Users/wangbin/dev/openal-soft/include/AL/alc.h" QtAV-1.12.0/src/codec/000077500000000000000000000000001312235004300142435ustar00rootroot00000000000000QtAV-1.12.0/src/codec/AVDecoder.cpp000066400000000000000000000201771312235004300165520ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include #include "utils/internal.h" #include "utils/Logger.h" namespace QtAV { static AVCodec* get_codec(const QString &name, const QString& hwa, AVCodecID cid) { QString fullname(name); if (name.isEmpty()) { if (hwa.isEmpty()) return avcodec_find_decoder(cid); fullname = QString("%1_%2").arg(avcodec_get_name(cid)).arg(hwa); } AVCodec *codec = avcodec_find_decoder_by_name(fullname.toUtf8().constData()); if (codec) return codec; const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(fullname.toUtf8().constData()); if (cd) return avcodec_find_decoder(cd->id); return NULL; } AVDecoder::AVDecoder(AVDecoderPrivate &d) :DPTR_INIT(&d) { avcodec_register_all(); // avcodec_find_decoder will always be used } AVDecoder::~AVDecoder() { setCodecContext(0); // FIXME: will call virtual } QString AVDecoder::name() const { return QString(); } QString AVDecoder::description() const { return QString(); } bool AVDecoder::open() { DPTR_D(AVDecoder); // codec_ctx can't be null for none-ffmpeg based decoders because we may use it's properties in those decoders if (!d.codec_ctx) { qWarning("FFmpeg codec context not ready"); return false; } const QString hwa = property("hwaccel").toString(); AVCodec* codec = get_codec(codecName(), hwa, d.codec_ctx->codec_id); if (!codec) { // TODO: can be null for none-ffmpeg based decoders QString es(tr("No codec could be found for '%1'")); if (d.codec_name.isEmpty()) { es = es.arg(QLatin1String(avcodec_get_name(d.codec_ctx->codec_id))); if (!hwa.isEmpty()) es.append('_').append(hwa); } else { es = es.arg(d.codec_name); } qWarning() << es; AVError::ErrorCode ec(AVError::CodecError); switch (d.codec_ctx->codec_type) { case AVMEDIA_TYPE_VIDEO: ec = AVError::VideoCodecNotFound; break; case AVMEDIA_TYPE_AUDIO: ec = AVError::AudioCodecNotFound; break; case AVMEDIA_TYPE_SUBTITLE: ec = AVError::SubtitleCodecNotFound; default: break; } Q_EMIT error(AVError(ec, es)); return false; } // hwa extra init can be here if (!d.open()) { d.close(); return false; } // CODEC_FLAG_OUTPUT_CORRUPT, CODEC_FLAG2_SHOW_ALL? // TODO: skip for none-ffmpeg based decoders d.applyOptionsForDict(); av_opt_set_int(d.codec_ctx, "refcounted_frames", d.enableFrameRef(), 0); // why dict may have no effect? // TODO: only open for ff decoders //av_dict_set(&d.dict, "lowres", "1", 0); // dict is used for a specified AVCodec options (priv_class), av_opt_set_xxx(avctx) is only for avctx AV_ENSURE_OK(avcodec_open2(d.codec_ctx, codec, d.options.isEmpty() ? NULL : &d.dict), false); d.is_open = true; static const char* thread_name[] = { "Single", "Frame", "Slice"}; qDebug("%s thread type: %s, count: %d", metaObject()->className(), thread_name[d.codec_ctx->active_thread_type], d.codec_ctx->thread_count); return true; } bool AVDecoder::close() { if (!isOpen()) { return true; } DPTR_D(AVDecoder); d.is_open = false; // hwa extra finalize can be here flush(); d.close(); // TODO: reset config? if (d.codec_ctx) { AV_ENSURE_OK(avcodec_close(d.codec_ctx), false); } return true; } bool AVDecoder::isOpen() const { return d_func().is_open; } void AVDecoder::flush() { if (!isAvailable()) return; if (!isOpen()) return; avcodec_flush_buffers(d_func().codec_ctx); } /* * do nothing if equal * close the old one. the codec context can not be shared in more than 1 decoder. */ void AVDecoder::setCodecContext(void *codecCtx) { DPTR_D(AVDecoder); AVCodecContext *ctx = (AVCodecContext*)codecCtx; if (d.codec_ctx == ctx) return; if (isOpen()) { qWarning("Can not copy codec properties when it's open"); close(); // } d.is_open = false; if (!ctx) { avcodec_free_context(&d.codec_ctx); d.codec_ctx = 0; return; } if (!d.codec_ctx) d.codec_ctx = avcodec_alloc_context3(NULL); // avcodec_alloc_context3(codec) equals to avcodec_alloc_context3(NULL) + avcodec_get_context_defaults3(codec), codec specified private data is initialized if (!d.codec_ctx) { qWarning("avcodec_alloc_context3 failed"); return; } AV_ENSURE_OK(avcodec_copy_context(d.codec_ctx, ctx)); } //TODO: reset other parameters? void* AVDecoder::codecContext() const { return d_func().codec_ctx; } void AVDecoder::setCodecName(const QString &name) { DPTR_D(AVDecoder); if (d.codec_name == name) return; d.codec_name = name; Q_EMIT codecNameChanged(); } QString AVDecoder::codecName() const { DPTR_D(const AVDecoder); return d.codec_name; } bool AVDecoder::isAvailable() const { return d_func().codec_ctx != 0; } int AVDecoder::undecodedSize() const { return d_func().undecoded_size; } void AVDecoder::setOptions(const QVariantHash &dict) { DPTR_D(AVDecoder); d.options = dict; // if dict is empty, can not return here, default options will be set for AVCodecContext // apply to AVCodecContext d.applyOptionsForContext(); /* set AVDecoder meta properties. * we do not check whether the property exists thus we can set dynamic properties. */ if (dict.isEmpty()) return; if (name() == QLatin1String("avcodec")) return; QVariant opt(dict); if (dict.contains(name())) opt = dict.value(name()); else if (dict.contains(name().toLower())) opt = dict.value(name().toLower()); Internal::setOptionsForQObject(opt, this); } QVariantHash AVDecoder::options() const { return d_func().options; } void AVDecoderPrivate::applyOptionsForDict() { if (dict) { av_dict_free(&dict); dict = 0; //aready 0 in av_free } // enable ref if possible av_dict_set(&dict, "refcounted_frames", enableFrameRef() ? "1" : "0", 0); if (options.isEmpty()) return; // TODO: use QVariantMap only if (!options.contains(QStringLiteral("avcodec"))) return; qDebug("set AVCodecContext dict:"); // workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict // TODO: wrong if opt is empty Internal::setOptionsToDict(options.value(QStringLiteral("avcodec")), &dict); } void AVDecoderPrivate::applyOptionsForContext() { if (!codec_ctx) return; if (options.isEmpty()) { // av_opt_set_defaults(codec_ctx); //can't set default values! result maybe unexpected return; } if (!options.contains(QStringLiteral("avcodec"))) return; // workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict // TODO: wrong if opt is empty Internal::setOptionsToFFmpegObj(options.value(QStringLiteral("avcodec")), codec_ctx); } } //namespace QtAV QtAV-1.12.0/src/codec/AVEncoder.cpp000066400000000000000000000123731312235004300165630ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include #include "utils/internal.h" #include "utils/Logger.h" namespace QtAV { AVEncoder::AVEncoder(AVEncoderPrivate &d) :DPTR_INIT(&d) { } AVEncoder::~AVEncoder() { close(); } QString AVEncoder::description() const { return QString(); } void AVEncoder::setCodecName(const QString &name) { DPTR_D(AVEncoder); if (d.codec_name == name) return; d.codec_name = name; Q_EMIT codecNameChanged(); } QString AVEncoder::codecName() const { DPTR_D(const AVEncoder); if (!d.codec_name.isEmpty()) return d.codec_name; if (d.avctx) return QLatin1String(avcodec_get_name(d.avctx->codec_id)); return QString(); } void AVEncoder::setBitRate(int value) { DPTR_D(AVEncoder); if (d.bit_rate == value) return; d.bit_rate = value; emit bitRateChanged(); } int AVEncoder::bitRate() const { return d_func().bit_rate; } AVEncoder::TimestampMode AVEncoder::timestampMode() const { return TimestampMode(d_func().timestamp_mode); } void AVEncoder::setTimestampMode(TimestampMode value) { DPTR_D(AVEncoder); if (d.timestamp_mode == (int)value) return; d.timestamp_mode = (int)value; Q_EMIT timestampModeChanged(value); } bool AVEncoder::open() { DPTR_D(AVEncoder); if (d.avctx) { d.applyOptionsForDict(); } if (!d.open()) { d.close(); return false; } d.is_open = true; return true; } bool AVEncoder::close() { if (!isOpen()) { return true; } DPTR_D(AVEncoder); d.is_open = false; // hwa extra finalize can be here d.close(); return true; } bool AVEncoder::isOpen() const { return d_func().is_open; } void AVEncoder::flush() { if (!isOpen()) return; if (d_func().avctx) avcodec_flush_buffers(d_func().avctx); } Packet AVEncoder::encoded() const { return d_func().packet; } void* AVEncoder::codecContext() const { return d_func().avctx; } void AVEncoder::copyAVCodecContext(void* ctx) { if (!ctx) return; DPTR_D(AVEncoder); AVCodecContext* c = static_cast(ctx); if (d.avctx) { // dest should be avcodec_alloc_context3(NULL) AV_ENSURE_OK(avcodec_copy_context(d.avctx, c)); d.is_open = false; return; } } void AVEncoder::setOptions(const QVariantHash &dict) { DPTR_D(AVEncoder); d.options = dict; // if dict is empty, can not return here, default options will be set for AVCodecContext // apply to AVCodecContext d.applyOptionsForContext(); /* set AVEncoder meta properties. * we do not check whether the property exists thus we can set dynamic properties. */ if (dict.isEmpty()) return; if (name() == QLatin1String("avcodec")) return; QVariant opt(dict); if (dict.contains(name())) opt = dict.value(name()); else if (dict.contains(name().toLower())) opt = dict.value(name().toLower()); Internal::setOptionsForQObject(opt, this); } QVariantHash AVEncoder::options() const { return d_func().options; } void AVEncoderPrivate::applyOptionsForDict() { if (dict) { av_dict_free(&dict); dict = 0; //aready 0 in av_free } if (options.isEmpty()) return; qDebug("set AVCodecContext dict:"); // TODO: use QVariantMap only if (!options.contains(QStringLiteral("avcodec"))) return; // workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict // TODO: wrong if opt is empty Internal::setOptionsToDict(options.value(QStringLiteral("avcodec")), &dict); } void AVEncoderPrivate::applyOptionsForContext() { if (!avctx) return; if (options.isEmpty()) { // av_opt_set_defaults(avctx); //can't set default values! result maybe unexpected return; } // TODO: use QVariantMap only if (!options.contains(QStringLiteral("avcodec"))) return; // workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict // TODO: wrong if opt is empty Internal::setOptionsToFFmpegObj(options.value(QStringLiteral("avcodec")), avctx); } } //namespace QtAV QtAV-1.12.0/src/codec/audio/000077500000000000000000000000001312235004300153445ustar00rootroot00000000000000QtAV-1.12.0/src/codec/audio/AudioDecoder.cpp000066400000000000000000000052741312235004300204070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioDecoder.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/AudioResampler.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(AudioDecoder) // TODO: why vc can not declare extern func in a class member? resolved as &func@@YAXXZ extern bool RegisterAudioDecoderFFmpeg_Man(); void AudioDecoder::registerAll() { static bool done = false; if (done) return; done = true; RegisterAudioDecoderFFmpeg_Man(); } QStringList AudioDecoder::supportedCodecs() { static QStringList codecs; if (!codecs.isEmpty()) return codecs; avcodec_register_all(); AVCodec* c = NULL; while ((c=av_codec_next(c))) { if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_AUDIO) continue; codecs.append(QString::fromLatin1(c->name)); } return codecs; } AudioDecoderPrivate::AudioDecoderPrivate() : AVDecoderPrivate() , resampler(0) { resampler = AudioResampler::create(AudioResamplerId_FF); if (!resampler) resampler = AudioResampler::create(AudioResamplerId_Libav); if (resampler) resampler->setOutSampleFormat(AV_SAMPLE_FMT_FLT); } AudioDecoderPrivate::~AudioDecoderPrivate() { if (resampler) { delete resampler; resampler = 0; } } AudioDecoder::AudioDecoder(AudioDecoderPrivate &d): AVDecoder(d) { } QString AudioDecoder::name() const { return QLatin1String(AudioDecoder::name(id())); } QByteArray AudioDecoder::data() const { return d_func().decoded; } AudioResampler* AudioDecoder::resampler() { return d_func().resampler; } } //namespace QtAV QtAV-1.12.0/src/codec/audio/AudioDecoderFFmpeg.cpp000066400000000000000000000122421312235004300214650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioDecoder.h" #include "QtAV/AudioResampler.h" #include "QtAV/Packet.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "QtAV/version.h" #include "utils/Logger.h" namespace QtAV { class AudioDecoderFFmpegPrivate; class AudioDecoderFFmpeg : public AudioDecoder { Q_OBJECT Q_DISABLE_COPY(AudioDecoderFFmpeg) DPTR_DECLARE_PRIVATE(AudioDecoderFFmpeg) Q_PROPERTY(QString codecName READ codecName WRITE setCodecName NOTIFY codecNameChanged) public: AudioDecoderFFmpeg(); AudioDecoderId id() const Q_DECL_OVERRIDE Q_DECL_FINAL; virtual QString description() const Q_DECL_OVERRIDE Q_DECL_FINAL { const int patch = QTAV_VERSION_PATCH(avcodec_version()); return QStringLiteral("%1 avcodec %2.%3.%4") .arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav")) .arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch); } bool decode(const Packet& packet) Q_DECL_OVERRIDE Q_DECL_FINAL; AudioFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL; Q_SIGNALS: void codecNameChanged() Q_DECL_OVERRIDE Q_DECL_FINAL; }; AudioDecoderId AudioDecoderId_FFmpeg = mkid::id32base36_6<'F','F','m','p','e','g'>::value; FACTORY_REGISTER(AudioDecoder, FFmpeg, "FFmpeg") class AudioDecoderFFmpegPrivate Q_DECL_FINAL: public AudioDecoderPrivate { public: AudioDecoderFFmpegPrivate() : AudioDecoderPrivate() , frame(av_frame_alloc()) { avcodec_register_all(); } ~AudioDecoderFFmpegPrivate() { if (frame) { av_frame_free(&frame); frame = 0; } } AVFrame *frame; //set once and not change }; AudioDecoderId AudioDecoderFFmpeg::id() const { return AudioDecoderId_FFmpeg; } AudioDecoderFFmpeg::AudioDecoderFFmpeg() : AudioDecoder(*new AudioDecoderFFmpegPrivate()) { } bool AudioDecoderFFmpeg::decode(const Packet &packet) { if (!isAvailable()) return false; DPTR_D(AudioDecoderFFmpeg); d.decoded.clear(); int got_frame_ptr = 0; int ret = 0; if (packet.isEOF()) { AVPacket eofpkt; av_init_packet(&eofpkt); eofpkt.data = NULL; eofpkt.size = 0; ret = avcodec_decode_audio4(d.codec_ctx, d.frame, &got_frame_ptr, &eofpkt); } else { // const AVPacket*: ffmpeg >= 1.0. no libav ret = avcodec_decode_audio4(d.codec_ctx, d.frame, &got_frame_ptr, (AVPacket*)packet.asAVPacket()); } d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size()); if (ret == AVERROR(EAGAIN)) { return false; } if (ret < 0) { qWarning("[AudioDecoder] %s", av_err2str(ret)); return false; } if (!got_frame_ptr) { qWarning("[AudioDecoder] got_frame_ptr=false. decoded: %d, un: %d %s", ret, d.undecoded_size, av_err2str(ret)); return !packet.isEOF(); } #if USE_AUDIO_FRAME return true; #endif d.resampler->setInSampesPerChannel(d.frame->nb_samples); if (!d.resampler->convert((const quint8**)d.frame->extended_data)) { return false; } d.decoded = d.resampler->outData(); return true; return !d.decoded.isEmpty(); } AudioFrame AudioDecoderFFmpeg::frame() { DPTR_D(AudioDecoderFFmpeg); AudioFormat fmt; fmt.setSampleFormatFFmpeg(d.frame->format); fmt.setChannelLayoutFFmpeg(d.frame->channel_layout); fmt.setSampleRate(d.frame->sample_rate); if (!fmt.isValid()) {// need more data to decode to get a frame return AudioFrame(); } AudioFrame f(fmt); //av_frame_get_pkt_duration ffmpeg f.setBits(d.frame->extended_data); // TODO: ref f.setBytesPerLine(d.frame->linesize[0], 0); // for correct alignment f.setSamplesPerChannel(d.frame->nb_samples); // TODO: ffplay check AVFrame.pts, pkt_pts, last_pts+nb_samples. move to AudioFrame::from(AVFrame*) f.setTimestamp((double)d.frame->pkt_pts/1000.0); f.setAudioResampler(d.resampler); // TODO: remove. it's not safe if frame is shared. use a pool or detach if ref >1 return f; } } //namespace QtAV #include "AudioDecoderFFmpeg.moc" QtAV-1.12.0/src/codec/audio/AudioEncoder.cpp000066400000000000000000000046411312235004300204160ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioEncoder.h" #include "QtAV/private/AVEncoder_p.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(AudioEncoder) void AudioEncoder_RegisterAll() { static bool called = false; if (called) return; called = true; // factory.h does not check whether an id is registered if (AudioEncoder::id("FFmpeg")) //registered on load return; extern bool RegisterAudioEncoderFFmpeg_Man(); RegisterAudioEncoderFFmpeg_Man(); } QStringList AudioEncoder::supportedCodecs() { static QStringList codecs; if (!codecs.isEmpty()) return codecs; avcodec_register_all(); AVCodec* c = NULL; while ((c=av_codec_next(c))) { if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_AUDIO) continue; codecs.append(QString::fromLatin1(c->name)); } return codecs; } AudioEncoder::AudioEncoder(AudioEncoderPrivate &d): AVEncoder(d) { } QString AudioEncoder::name() const { return QLatin1String(AudioEncoder::name(id())); } void AudioEncoder::setAudioFormat(const AudioFormat& format) { DPTR_D(AudioEncoder); if (d.format == format) return; d.format = format; d.format_used = format; Q_EMIT audioFormatChanged(); } const AudioFormat& AudioEncoder::audioFormat() const { return d_func().format_used; } } //namespace QtAV QtAV-1.12.0/src/codec/audio/AudioEncoderFFmpeg.cpp000066400000000000000000000213031312235004300214750ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioEncoder.h" #include "QtAV/private/AVEncoder_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "QtAV/version.h" #include "utils/Logger.h" /*! * options (properties) are from libavcodec/options_table.h * enum name here must convert to lower case to fit the names in avcodec. done in AVEncoder.setOptions() * Don't use lower case here because the value name may be "default" in avcodec which is a keyword of C++ */ namespace QtAV { class AudioEncoderFFmpegPrivate; class AudioEncoderFFmpeg Q_DECL_FINAL: public AudioEncoder { DPTR_DECLARE_PRIVATE(AudioEncoderFFmpeg) public: AudioEncoderFFmpeg(); AudioEncoderId id() const Q_DECL_OVERRIDE; bool encode(const AudioFrame &frame = AudioFrame()) Q_DECL_OVERRIDE; }; static const AudioEncoderId AudioEncoderId_FFmpeg = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value; FACTORY_REGISTER(AudioEncoder, FFmpeg, "FFmpeg") class AudioEncoderFFmpegPrivate Q_DECL_FINAL: public AudioEncoderPrivate { public: AudioEncoderFFmpegPrivate() : AudioEncoderPrivate() , frame_size(0) { avcodec_register_all(); // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264). avctx = avcodec_alloc_context3(NULL); } bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; int frame_size; // used if avctx->frame_size == 0 QByteArray buffer; }; bool AudioEncoderFFmpegPrivate::open() { if (codec_name.isEmpty()) { // copy ctx from muxer by copyAVCodecContext AVCodec *codec = avcodec_find_encoder(avctx->codec_id); AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false); return true; } AVCodec *codec = avcodec_find_encoder_by_name(codec_name.toUtf8().constData()); if (!codec) { const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(codec_name.toUtf8().constData()); if (cd) { codec = avcodec_find_encoder(cd->id); } } if (!codec) { qWarning() << "Can not find encoder for codec " << codec_name; return false; } if (avctx) { avcodec_free_context(&avctx); avctx = 0; } avctx = avcodec_alloc_context3(codec); // reset format_used to user defined format. important to update default format if format is invalid format_used = format; if (format.sampleRate() <= 0) { if (codec->supported_samplerates) { qDebug("use first supported sample rate: %d", codec->supported_samplerates[0]); format_used.setSampleRate(codec->supported_samplerates[0]); } else { qWarning("sample rate and supported sample rate are not set. use 44100"); format_used.setSampleRate(44100); } } if (format.sampleFormat() == AudioFormat::SampleFormat_Unknown) { if (codec->sample_fmts) { qDebug("use first supported sample format: %d", codec->sample_fmts[0]); format_used.setSampleFormatFFmpeg((int)codec->sample_fmts[0]); } else { qWarning("sample format and supported sample format are not set. use s16"); format_used.setSampleFormat(AudioFormat::SampleFormat_Signed16); } } if (format.channelLayout() == AudioFormat::ChannelLayout_Unsupported) { if (codec->channel_layouts) { char cl[128]; av_get_channel_layout_string(cl, sizeof(cl), -1, codec->channel_layouts[0]); //TODO: ff version qDebug("use first supported channel layout: %s", cl); format_used.setChannelLayoutFFmpeg((qint64)codec->channel_layouts[0]); } else { qWarning("channel layout and supported channel layout are not set. use stereo"); format_used.setChannelLayout(AudioFormat::ChannelLayout_Stereo); } } avctx->sample_fmt = (AVSampleFormat)format_used.sampleFormatFFmpeg(); avctx->channel_layout = format_used.channelLayoutFFmpeg(); avctx->channels = format_used.channels(); avctx->sample_rate = format_used.sampleRate(); avctx->bits_per_raw_sample = format_used.bytesPerSample()*8; /// set the time base. TODO avctx->time_base.num = 1; avctx->time_base.den = format_used.sampleRate(); avctx->bit_rate = bit_rate; qDebug() << format_used; /** Allow the use of the experimental AAC encoder */ avctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; av_dict_set(&dict, "strict", "-2", 0); //aac, vorbis applyOptionsForContext(); // avctx->frame_size will be set in avcodec_open2 AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false); // from mpv ao_lavc int pcm_hack = 0; int buffer_size = 0; frame_size = avctx->frame_size; if (frame_size <= 1) pcm_hack = av_get_bits_per_sample(avctx->codec_id)/8; if (pcm_hack) { frame_size = 16384; // "enough" buffer_size = frame_size*pcm_hack*format_used.channels()*2+200; } else { buffer_size = frame_size*format_used.bytesPerSample()*format_used.channels()*2+200; } if (buffer_size < FF_MIN_BUFFER_SIZE) buffer_size = FF_MIN_BUFFER_SIZE; buffer.resize(buffer_size); return true; } bool AudioEncoderFFmpegPrivate::close() { AV_ENSURE_OK(avcodec_close(avctx), false); return true; } AudioEncoderFFmpeg::AudioEncoderFFmpeg() : AudioEncoder(*new AudioEncoderFFmpegPrivate()) { } AudioEncoderId AudioEncoderFFmpeg::id() const { return AudioEncoderId_FFmpeg; } bool AudioEncoderFFmpeg::encode(const AudioFrame &frame) { DPTR_D(AudioEncoderFFmpeg); AVFrame *f = NULL; if (frame.isValid()) { f = av_frame_alloc(); const AudioFormat fmt(frame.format()); f->format = fmt.sampleFormatFFmpeg(); f->channel_layout = fmt.channelLayoutFFmpeg(); // f->channels = fmt.channels(); //remove? not availale in libav9 // must be (not the last frame) exactly frame_size unless CODEC_CAP_VARIABLE_FRAME_SIZE is set (frame_size==0) // TODO: mpv use pcmhack for avctx.frame_size==0. can we use input frame.samplesPerChannel? f->nb_samples = d.frame_size; /// f->quality = d.avctx->global_quality; //TODO // TODO: record last pts. mpv compute pts internally and also use playback time f->pts = int64_t(frame.timestamp()*fmt.sampleRate()); // TODO // pts is set in muxer const int nb_planes = frame.planeCount(); // bytes between 2 samples on a plane. TODO: add to AudioFormat? what about bytesPerFrame? const int sample_stride = fmt.isPlanar() ? fmt.bytesPerSample() : fmt.bytesPerSample()*fmt.channels(); for (int i = 0; i < nb_planes; ++i) { f->linesize[i] = f->nb_samples * sample_stride;// frame.bytesPerLine(i); // f->extended_data[i] = (uint8_t*)frame.constBits(i); } } AVPacket pkt; av_init_packet(&pkt); pkt.data = (uint8_t*)d.buffer.constData(); //NULL pkt.size = d.buffer.size(); //0 int got_packet = 0; int ret = avcodec_encode_audio2(d.avctx, &pkt, f, &got_packet); av_frame_free(&f); if (ret < 0) { //qWarning("error avcodec_encode_audio2: %s" ,av_err2str(ret)); //av_packet_unref(&pkt); //FIXME return false; //false } if (!got_packet) { qWarning("no packet got"); d.packet = Packet(); // invalid frame means eof return frame.isValid(); } // qDebug("enc avpkt.pts: %lld, dts: %lld.", pkt.pts, pkt.dts); d.packet = Packet::fromAVPacket(&pkt, av_q2d(d.avctx->time_base)); // qDebug("enc packet.pts: %.3f, dts: %.3f.", d.packet.pts, d.packet.dts); return true; } } //namespace QtAV QtAV-1.12.0/src/codec/video/000077500000000000000000000000001312235004300153515ustar00rootroot00000000000000QtAV-1.12.0/src/codec/video/SurfaceInteropCUDA.cpp000066400000000000000000000670431312235004300214550ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropCUDA.h" #include "QtAV/VideoFrame.h" #include "utils/Logger.h" #include "helper_cuda.h" #define WORKAROUND_UNMAP_CONTEXT_SWITCH 1 #define USE_STREAM 1 namespace QtAV { namespace cuda { InteropResource::InteropResource() : cuda_api() , dev(0) , ctx(0) , dec(0) , lock(0) { memset(res, 0, sizeof(res)); } InteropResource::~InteropResource() { //CUDA_WARN(cuCtxPushCurrent(ctx)); //error invalid value if (res[0].cuRes) CUDA_WARN(cuGraphicsUnregisterResource(res[0].cuRes)); if (res[1].cuRes) CUDA_WARN(cuGraphicsUnregisterResource(res[1].cuRes)); if (res[0].stream) CUDA_WARN(cuStreamDestroy(res[0].stream)); if (res[1].stream) CUDA_WARN(cuStreamDestroy(res[1].stream)); // FIXME: we own the context. But why crash to destroy ctx? CUDA_ERROR_INVALID_VALUE if (!share_ctx && ctx) CUDA_ENSURE(cuCtxDestroy(ctx)); } void* InteropResource::mapToHost(const VideoFormat &format, void *handle, int picIndex, const CUVIDPROCPARAMS ¶m, int width, int height, int coded_height) { AutoCtxLock locker((cuda_api*)this, lock); Q_UNUSED(locker); CUdeviceptr devptr; unsigned int pitch; CUDA_ENSURE(cuvidMapVideoFrame(dec, picIndex, &devptr, &pitch, const_cast(¶m)), NULL); CUVIDAutoUnmapper unmapper(this, dec, devptr); Q_UNUSED(unmapper); uchar* host_data = NULL; const unsigned int host_size = pitch*coded_height*3/2; CUDA_ENSURE(cuMemAllocHost((void**)&host_data, host_size), NULL); // copy to the memory not allocated by cuda is possible but much slower CUDA_ENSURE(cuMemcpyDtoH(host_data, devptr, host_size), NULL); VideoFrame frame(width, height, VideoFormat::Format_NV12); uchar *planes[] = { host_data, host_data + pitch * coded_height }; frame.setBits(planes); int pitches[] = { (int)pitch, (int)pitch }; frame.setBytesPerLine(pitches); VideoFrame *f = reinterpret_cast(handle); frame.setTimestamp(f->timestamp()); frame.setDisplayAspectRatio(f->displayAspectRatio()); if (format == frame.format()) *f = frame.clone(); else *f = frame.to(format); CUDA_ENSURE(cuMemFreeHost(host_data), f); return f; } #ifndef QT_NO_OPENGL HostInteropResource::HostInteropResource() : InteropResource() { memset(&host_mem, 0, sizeof(host_mem)); host_mem.index = -1; } HostInteropResource::~HostInteropResource() { if (ctx) { //cuMemFreeHost need the context of mem allocated, it's shared context, or own context CUDA_WARN(cuCtxPushCurrent(ctx)); } if (host_mem.data) { //FIXME: CUDA_ERROR_INVALID_VALUE CUDA_ENSURE(cuMemFreeHost(host_mem.data)); host_mem.data = NULL; } if (ctx) { CUDA_WARN(cuCtxPopCurrent(NULL)); } } bool HostInteropResource::map(int picIndex, const CUVIDPROCPARAMS ¶m, GLuint tex, int w, int h, int H, int plane) { Q_UNUSED(w); if (host_mem.index != picIndex || !host_mem.data) { AutoCtxLock locker((cuda_api*)this, lock); Q_UNUSED(locker); CUdeviceptr devptr; unsigned int pitch; //qDebug("index: %d=>%d, plane: %d", host_mem.index, picIndex, plane); CUDA_ENSURE(cuvidMapVideoFrame(dec, picIndex, &devptr, &pitch, const_cast(¶m)), false); CUVIDAutoUnmapper unmapper(this, dec, devptr); Q_UNUSED(unmapper); if (!ensureResource(pitch, H)) //copy height is coded height return false; // the same thread (context) as cuMemAllocHost, so no ccontext switch is needed CUDA_ENSURE(cuMemcpyDtoH(host_mem.data, devptr, pitch*H*3/2), NULL); host_mem.index = picIndex; } // map to texture //qDebug("map plane %d @%d", plane, picIndex); GLint iformat[2]; GLenum format[2], dtype[2]; OpenGLHelper::videoFormatToGL(VideoFormat::Format_NV12, iformat, format, dtype); DYGL(glBindTexture(GL_TEXTURE_2D, tex)); const int chroma = plane != 0; // chroma pitch for gl is 1/2 (gl_rg) // texture height is not coded height! DYGL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, host_mem.pitch>>chroma, h>>chroma, format[plane], dtype[plane], host_mem.data + chroma*host_mem.pitch*host_mem.height)); //DYGL(glTexImage2D(GL_TEXTURE_2D, 0, iformat[plane], host_mem.pitch>>chroma, h>>chroma, 0, format[plane], dtype[plane], host_mem.data + chroma*host_mem.pitch*host_mem.height)); return true; } bool HostInteropResource::unmap(GLuint) { return true; } bool HostInteropResource::ensureResource(int pitch, int height) { if (host_mem.data && host_mem.pitch == pitch && host_mem.height == height) return true; if (host_mem.data) { CUDA_ENSURE(cuMemFreeHost(host_mem.data), false); host_mem.data = NULL; } qDebug("allocate cuda host mem. %dx%d=>%dx%d", host_mem.pitch, host_mem.height, pitch, height); host_mem.pitch = pitch; host_mem.height = height; if (!ctx) { CUDA_ENSURE(cuCtxCreate(&ctx, CU_CTX_SCHED_BLOCKING_SYNC, dev), false); CUDA_WARN(cuCtxPopCurrent(&ctx)); share_ctx = false; } if (!share_ctx) // cuMemFreeHost will be called in dtor which is not the current thread. CUDA_WARN(cuCtxPushCurrent(ctx)); // NV12 CUDA_ENSURE(cuMemAllocHost((void**)&host_mem.data, pitch*height*3/2), NULL); if (!share_ctx) CUDA_WARN(cuCtxPopCurrent(NULL)); //can be null or &ctx return true; } #endif //QT_NO_OPENGL void SurfaceInteropCUDA::setSurface(int picIndex, CUVIDPROCPARAMS param, int width, int height, int surface_height) { m_index = picIndex; m_param = param; w = width; h = height; H = surface_height; } void* SurfaceInteropCUDA::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane) { Q_UNUSED(fmt); if (m_resource.isNull()) return NULL; if (!handle) return NULL; if (m_index < 0) return 0; if (type == GLTextureSurface) { #ifndef QT_NO_OPENGL // FIXME: to strong ref may delay the delete and cuda resource maybe already destoryed after strong ref is finished if (m_resource.toStrongRef()->map(m_index, m_param, *((GLuint*)handle), w, h, H, plane)) return handle; #endif //QT_NO_OPENGL } else if (type == HostMemorySurface) { return m_resource.toStrongRef()->mapToHost(fmt, handle, m_index, m_param, w, h, H); } return NULL; } void SurfaceInteropCUDA::unmap(void *handle) { if (m_resource.isNull()) return; #ifndef QT_NO_OPENGL // FIXME: to strong ref may delay the delete and cuda resource maybe already destoryed after strong ref is finished m_resource.toStrongRef()->unmap(*((GLuint*)handle)); #endif } } //namespace cuda } //namespace QtAV #if QTAV_HAVE(CUDA_EGL) #ifdef QT_OPENGL_ES_2_ANGLE_STATIC #define CAPI_LINK_EGL #else #define EGL_CAPI_NS #endif //QT_OPENGL_ES_2_ANGLE_STATIC #include "capi/egl_api.h" #include //include after egl_capi.h to match types #define DX_LOG_COMPONENT "CUDA.D3D" #include "utils/DirectXHelper.h" namespace QtAV { namespace cuda { class EGL { public: EGL() : dpy(EGL_NO_DISPLAY), surface(EGL_NO_SURFACE) {} EGLDisplay dpy; EGLSurface surface; //only support rgb. then we must use CUDA kernel #ifdef EGL_VERSION_1_5 // eglCreateImageKHR does not support EGL_NATIVE_PIXMAP_KHR, only 2d, 3d, render buffer //EGLImageKHR image[2]; //EGLImage image[2]; //not implemented yet #endif //EGL_VERSION_1_5 }; EGLInteropResource::EGLInteropResource() : InteropResource() , egl(new EGL()) , dll9(NULL) , d3d9(NULL) , device9(NULL) , texture9(NULL) , surface9(NULL) , texture9_nv12(NULL) , surface9_nv12(NULL) , query9(NULL) { ctx = NULL; //need a context created with d3d (TODO: check it?) share_ctx = false; } EGLInteropResource::~EGLInteropResource() { releaseEGL(); if (egl) { delete egl; egl = NULL; } SafeRelease(&query9); SafeRelease(&surface9_nv12); SafeRelease(&texture9_nv12); SafeRelease(&surface9); SafeRelease(&texture9); SafeRelease(&device9); SafeRelease(&d3d9); if (dll9) FreeLibrary(dll9); } bool EGLInteropResource::ensureD3DDevice() { if (device9) return true; if (!dll9) dll9 = LoadLibrary(TEXT("D3D9.DLL")); if (!dll9) { qWarning("cuda::EGLInteropResource cannot load d3d9.dll"); return false; } D3DADAPTER_IDENTIFIER9 ai9; ZeroMemory(&ai9, sizeof(ai9)); device9 = DXHelper::CreateDevice9Ex(dll9, (IDirect3D9Ex**)(&d3d9), &ai9); if (!device9) { qWarning("Failed to create d3d9 device ex, fallback to d3d9 device"); device9 = DXHelper::CreateDevice9(dll9, &d3d9, &ai9); } if (!device9) return false; qDebug() << QString().sprintf("CUDA.D3D9 (%.*s, vendor %lu, device %lu, revision %lu)", sizeof(ai9.Description), ai9.Description, ai9.VendorId, ai9.DeviceId, ai9.Revision); // move to ensureResouce DX_ENSURE(device9->CreateQuery(D3DQUERYTYPE_EVENT, &query9), false); query9->Issue(D3DISSUE_END); return !!device9; } void EGLInteropResource::releaseEGL() { if (egl->surface != EGL_NO_SURFACE) { eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); eglDestroySurface(egl->dpy, egl->surface); egl->surface = EGL_NO_SURFACE; } } bool EGLInteropResource::ensureResource(int w, int h, int W, int H, GLuint tex) { TexRes &r = res[0];// 1 NV12 texture if (ensureD3D9CUDA(w, h, W, H) && ensureD3D9EGL(w, h)) { r.texture = tex; r.w = w; r.h = h; r.W = W; r.H = H; return true; } releaseEGL(); //releaseDX(); SafeRelease(&query9); SafeRelease(&surface9); SafeRelease(&texture9); SafeRelease(&surface9_nv12); SafeRelease(&texture9_nv12); return false; } bool EGLInteropResource::ensureD3D9CUDA(int w, int h, int W, int H) { TexRes &r = res[0];// 1 NV12 texture if (r.w == w && r.h == h && r.W == W && r.H == H && r.cuRes) return true; if (share_ctx) { share_ctx = false; ctx = NULL; } if (!ctx) { // TODO: how to use pop/push decoder's context without the context in opengl context if (!ensureD3DDevice()) return false; // CUdevice is different from decoder's CUDA_ENSURE(cuD3D9CtxCreate(&ctx, &dev, CU_CTX_SCHED_BLOCKING_SYNC, device9), false); #if USE_STREAM CUDA_WARN(cuStreamCreate(&res[0].stream, CU_STREAM_DEFAULT)); CUDA_WARN(cuStreamCreate(&res[1].stream, CU_STREAM_DEFAULT)); #endif //USE_STREAM qDebug("cuda contex on gl thread: %p", ctx); CUDA_ENSURE(cuCtxPopCurrent(&ctx), false); // TODO: why cuMemcpy2D need this } if (r.cuRes) { CUDA_ENSURE(cuGraphicsUnregisterResource(r.cuRes), false); r.cuRes = NULL; } // create d3d resource for interop if (!surface9_nv12) { // TODO: need pitch from cuvid to ensure cuMemcpy2D can copy the whole pitch DX_ENSURE(device9->CreateTexture(W //, H , H*3/2 , 1 , D3DUSAGE_DYNAMIC //D3DUSAGE_DYNAMIC is lockable // 0 is from NV example. cudaD3D9.h says The primary rendertarget may not be registered with CUDA. So can not be D3DUSAGE_RENDERTARGET? //, D3DUSAGE_RENDERTARGET , D3DFMT_L8 //, (D3DFORMAT)MAKEFOURCC('N','V','1','2') // can not create nv12. use 2 textures L8+A8L8? , D3DPOOL_DEFAULT // must be D3DPOOL_DEFAULT for cuda? , &texture9_nv12 , NULL) // - Resources allocated as shared may not be registered with CUDA. , false); DX_ENSURE(device9->CreateOffscreenPlainSurface(W, H, (D3DFORMAT)MAKEFOURCC('N','V','1','2'), D3DPOOL_DEFAULT, &surface9_nv12, NULL), false); //TODO: createrendertarget } // TODO: cudaD3D9.h says NV12 is not supported // CUDA_ERROR_INVALID_HANDLE if register D3D9 surface // TODO: why flag CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD is invalid while it's fine for opengl CUDA_ENSURE(cuGraphicsD3D9RegisterResource(&r.cuRes, texture9_nv12, CU_GRAPHICS_REGISTER_FLAGS_NONE), false); return true; } bool EGLInteropResource::ensureD3D9EGL(int w, int h) { if (egl->surface && res[0].w == w && res[0].h == h) return true; releaseEGL(); egl->dpy = eglGetCurrentDisplay(); qDebug("EGL version: %s, client api: %s", eglQueryString(egl->dpy, EGL_VERSION), eglQueryString(egl->dpy, EGL_CLIENT_APIS)); EGLint cfg_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, // EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, //remove? EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_NONE }; EGLint nb_cfgs; EGLConfig egl_cfg; if (!eglChooseConfig(egl->dpy, cfg_attribs, &egl_cfg, 1, &nb_cfgs)) { qWarning("Failed to create EGL configuration"); return false; } // check extensions QList extensions = QByteArray(eglQueryString(egl->dpy, EGL_EXTENSIONS)).split(' '); // ANGLE_d3d_share_handle_client_buffer will be used if possible const bool kEGL_ANGLE_d3d_share_handle_client_buffer = extensions.contains("EGL_ANGLE_d3d_share_handle_client_buffer"); const bool kEGL_ANGLE_query_surface_pointer = extensions.contains("EGL_ANGLE_query_surface_pointer"); if (!kEGL_ANGLE_d3d_share_handle_client_buffer && !kEGL_ANGLE_query_surface_pointer) { qWarning("EGL extension 'kEGL_ANGLE_query_surface_pointer' or 'ANGLE_d3d_share_handle_client_buffer' is required!"); return false; } GLint has_alpha = 1; //QOpenGLContext::currentContext()->format().hasAlpha() eglGetConfigAttrib(egl->dpy, egl_cfg, EGL_BIND_TO_TEXTURE_RGBA, &has_alpha); //EGL_ALPHA_SIZE qDebug("choose egl display:%p config: %p/%d, has alpha: %d", egl->dpy, egl_cfg, nb_cfgs, has_alpha); EGLint attribs[] = { EGL_WIDTH, w, EGL_HEIGHT, h, EGL_TEXTURE_FORMAT, has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE }; HANDLE share_handle = NULL; if (!kEGL_ANGLE_d3d_share_handle_client_buffer && kEGL_ANGLE_query_surface_pointer) { EGL_ENSURE((egl->surface = eglCreatePbufferSurface(egl->dpy, egl_cfg, attribs)) != EGL_NO_SURFACE, false); qDebug("pbuffer surface: %p", egl->surface); PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE = reinterpret_cast(eglGetProcAddress("eglQuerySurfacePointerANGLE")); if (!eglQuerySurfacePointerANGLE) { qWarning("EGL_ANGLE_query_surface_pointer is not supported"); return false; } EGL_ENSURE(eglQuerySurfacePointerANGLE(egl->dpy, egl->surface, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle), false); } SafeRelease(&surface9); SafeRelease(&texture9); // _A8 for a yuv plane /* * d3d resource share requires windows >= vista: https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800(v=vs.85).aspx * from extension files: * d3d9: level must be 1, dimensions must match EGL surface's * d3d9ex or d3d10: */ DX_ENSURE(device9->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &texture9, &share_handle) , false); DX_ENSURE(texture9->GetSurfaceLevel(0, &surface9), false); if (kEGL_ANGLE_d3d_share_handle_client_buffer) { // requires extension EGL_ANGLE_d3d_share_handle_client_buffer // egl surface size must match d3d texture's // d3d9ex or d3d10 is required EGL_ENSURE((egl->surface = eglCreatePbufferFromClientBuffer(egl->dpy, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, share_handle, egl_cfg, attribs)), false); qDebug("pbuffer surface from client buffer: %p", egl->surface); } return true; } bool EGLInteropResource::map(int picIndex, const CUVIDPROCPARAMS ¶m, GLuint tex, int w, int h, int H, int plane) { // plane is always 0 because frame is rgb AutoCtxLock locker((cuda_api*)this, lock); Q_UNUSED(locker); if (!ensureResource(w, h, param.Reserved[0], H, tex)) // TODO surface size instead of frame size because we copy the device data return false; //CUDA_ENSURE(cuCtxPushCurrent(ctx), false); CUdeviceptr devptr; unsigned int pitch; CUDA_ENSURE(cuvidMapVideoFrame(dec, picIndex, &devptr, &pitch, const_cast(¶m)), false); CUVIDAutoUnmapper unmapper(this, dec, devptr); Q_UNUSED(unmapper); // TODO: why can not use res[plane].stream? CUDA_ERROR_INVALID_HANDLE CUDA_ENSURE(cuGraphicsMapResources(1, &res[plane].cuRes, 0), false); CUarray array; CUDA_ENSURE(cuGraphicsSubResourceGetMappedArray(&array, res[plane].cuRes, 0, 0), false); CUDA_ENSURE(cuGraphicsUnmapResources(1, &res[plane].cuRes, 0), false); // mapped array still accessible! CUDA_MEMCPY2D cu2d; memset(&cu2d, 0, sizeof(cu2d)); // Y plane cu2d.srcDevice = devptr; cu2d.srcMemoryType = CU_MEMORYTYPE_DEVICE; cu2d.srcPitch = pitch; cu2d.dstArray = array; cu2d.dstMemoryType = CU_MEMORYTYPE_ARRAY; cu2d.dstPitch = pitch; // the whole size or copy size? cu2d.WidthInBytes = res[plane].W; // the same value as texture9_nv12 cu2d.Height = H*3/2; if (res[plane].stream) CUDA_ENSURE(cuMemcpy2DAsync(&cu2d, res[plane].stream), false); else CUDA_ENSURE(cuMemcpy2D(&cu2d), false); //TODO: delay cuCtxSynchronize && unmap. do it in unmap(tex)? // map to an already mapped resource will crash. sometimes I can not unmap the resource in unmap(tex) because if context switch error // so I simply unmap the resource here if (WORKAROUND_UNMAP_CONTEXT_SWITCH) { if (res[plane].stream) { //CUDA_WARN(cuCtxSynchronize(), false); //wait too long time? use cuStreamQuery? CUDA_WARN(cuStreamSynchronize(res[plane].stream)); //slower than CtxSynchronize } /* * This function provides the synchronization guarantee that any CUDA work issued * in \p stream before ::cuGraphicsUnmapResources() will complete before any * subsequently issued graphics work begins. * The graphics API from which \p resources were registered * should not access any resources while they are mapped by CUDA. If an * application does so, the results are undefined. */ // CUDA_ENSURE(cuGraphicsUnmapResources(1, &res[plane].cuRes, 0), false); } D3DLOCKED_RECT rect_src, rect_dst; DX_ENSURE(texture9_nv12->LockRect(0, &rect_src, NULL, D3DLOCK_READONLY), false); DX_ENSURE(surface9_nv12->LockRect(&rect_dst, NULL, D3DLOCK_DISCARD), false); memcpy(rect_dst.pBits, rect_src.pBits, res[plane].W*H*3/2); // exactly w and h DX_ENSURE(surface9_nv12->UnlockRect(), false); DX_ENSURE(texture9_nv12->UnlockRect(0), false); #if 0 //IDirect3DSurface9 *raw_surface = NULL; //DX_ENSURE(texture9_nv12->GetSurfaceLevel(0, &raw_surface), false); const RECT src = { 0, 0, (~0-1)&w, (~0-1)&(h*3/2)}; DX_ENSURE(device9->StretchRect(raw_surface, &src, surface9_nv12, NULL, D3DTEXF_NONE), false); #endif if (!map(surface9_nv12, tex, w, h, H)) return false; return true; } bool EGLInteropResource::map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int H) { Q_UNUSED(H); D3DSURFACE_DESC dxvaDesc; surface->GetDesc(&dxvaDesc); const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h}; //StretchRect does not supports odd values DX_ENSURE(device9->StretchRect(surface, &src, surface9, NULL, D3DTEXF_NONE), false); if (query9) { // Flush the draw command now. Ideally, this should be done immediately before the draw call that uses the texture. Flush it once here though. query9->Issue(D3DISSUE_END); // ensure data is copied to egl surface. Solution and comment is from chromium // The DXVA decoder has its own device which it uses for decoding. ANGLE has its own device which we don't have access to. // The above code attempts to copy the decoded picture into a surface which is owned by ANGLE. // As there are multiple devices involved in this, the StretchRect call above is not synchronous. // We attempt to flush the batched operations to ensure that the picture is copied to the surface owned by ANGLE. // We need to do this in a loop and call flush multiple times. // We have seen the GetData call for flushing the command buffer fail to return success occassionally on multi core machines, leading to an infinite loop. // Workaround is to have an upper limit of 10 on the number of iterations to wait for the Flush to finish. int k = 0; // skip at decoder.close() while (/*!skip_dx.load() && */(query9->GetData(NULL, 0, D3DGETDATA_FLUSH) == FALSE) && ++k < 10) { Sleep(1); } } DYGL(glBindTexture(GL_TEXTURE_2D, tex)); eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); return true; } } //namespace cuda } //namespace QtAV #endif //QTAV_HAVE(CUDA_EGL) #if QTAV_HAVE(CUDA_GL) namespace QtAV { namespace cuda { //TODO: cuGLMapBufferObject: get cudeviceptr from pbo, then memcpy2d bool GLInteropResource::map(int picIndex, const CUVIDPROCPARAMS ¶m, GLuint tex, int w, int h, int H, int plane) { AutoCtxLock locker((cuda_api*)this, lock); Q_UNUSED(locker); if (!ensureResource(w, h, H, tex, plane)) // TODO surface size instead of frame size because we copy the device data return false; //CUDA_ENSURE(cuCtxPushCurrent(ctx), false); CUdeviceptr devptr; unsigned int pitch; CUDA_ENSURE(cuvidMapVideoFrame(dec, picIndex, &devptr, &pitch, const_cast(¶m)), false); CUVIDAutoUnmapper unmapper(this, dec, devptr); Q_UNUSED(unmapper); // TODO: why can not use res[plane].stream? CUDA_ERROR_INVALID_HANDLE CUDA_ENSURE(cuGraphicsMapResources(1, &res[plane].cuRes, 0), false); CUarray array; CUDA_ENSURE(cuGraphicsSubResourceGetMappedArray(&array, res[plane].cuRes, 0, 0), false); CUDA_MEMCPY2D cu2d; memset(&cu2d, 0, sizeof(cu2d)); cu2d.srcDevice = devptr; cu2d.srcMemoryType = CU_MEMORYTYPE_DEVICE; cu2d.srcPitch = pitch; cu2d.dstArray = array; cu2d.dstMemoryType = CU_MEMORYTYPE_ARRAY; cu2d.dstPitch = pitch; // the whole size or copy size? cu2d.WidthInBytes = pitch; cu2d.Height = h; if (plane == 1) { cu2d.srcXInBytes = 0;// +srcY*srcPitch + srcXInBytes cu2d.srcY = H; // skip the padding height cu2d.Height /= 2; } if (res[plane].stream) CUDA_ENSURE(cuMemcpy2DAsync(&cu2d, res[plane].stream), false); else CUDA_ENSURE(cuMemcpy2D(&cu2d), false); //TODO: delay cuCtxSynchronize && unmap. do it in unmap(tex)? // map to an already mapped resource will crash. sometimes I can not unmap the resource in unmap(tex) because if context switch error // so I simply unmap the resource here if (WORKAROUND_UNMAP_CONTEXT_SWITCH) { if (res[plane].stream) { //CUDA_WARN(cuCtxSynchronize(), false); //wait too long time? use cuStreamQuery? CUDA_WARN(cuStreamSynchronize(res[plane].stream)); //slower than CtxSynchronize } /* * This function provides the synchronization guarantee that any CUDA work issued * in \p stream before ::cuGraphicsUnmapResources() will complete before any * subsequently issued graphics work begins. * The graphics API from which \p resources were registered * should not access any resources while they are mapped by CUDA. If an * application does so, the results are undefined. */ CUDA_ENSURE(cuGraphicsUnmapResources(1, &res[plane].cuRes, 0), false); } else { // call it at last. current context will be used by other cuda calls (unmap() for example) CUDA_ENSURE(cuCtxPopCurrent(&ctx), false); // not required } return true; } bool GLInteropResource::unmap(GLuint tex) { Q_UNUSED(tex); if (WORKAROUND_UNMAP_CONTEXT_SWITCH) return true; int plane = -1; if (res[0].texture == tex) plane = 0; else if (res[1].texture == tex) plane = 1; else return false; // FIXME: why cuCtxPushCurrent gives CUDA_ERROR_INVALID_CONTEXT if opengl viewport changed? CUDA_WARN(cuCtxPushCurrent(ctx)); CUDA_WARN(cuStreamSynchronize(res[plane].stream)); // FIXME: need a correct context. But why we have to push context even though map/unmap are called in the same thread // Because the decoder switch the context in another thread so we have to switch the context back? // to workaround the context issue, we must pop the context that valid in map() and push it here CUDA_ENSURE(cuGraphicsUnmapResources(1, &res[plane].cuRes, 0), false); CUDA_ENSURE(cuCtxPopCurrent(&ctx), false); return true; } bool GLInteropResource::ensureResource(int w, int h, int H, GLuint tex, int plane) { Q_ASSERT(plane < 2 && "plane number must be 0 or 1 for NV12"); TexRes &r = res[plane]; if (r.texture == tex && r.w == w && r.h == h && r.H == H && r.cuRes) return true; if (!ctx) { // TODO: how to use pop/push decoder's context without the context in opengl context CUDA_ENSURE(cuCtxCreate(&ctx, CU_CTX_SCHED_BLOCKING_SYNC, dev), false); if (USE_STREAM) { CUDA_WARN(cuStreamCreate(&res[0].stream, CU_STREAM_DEFAULT)); CUDA_WARN(cuStreamCreate(&res[1].stream, CU_STREAM_DEFAULT)); } qDebug("cuda contex on gl thread: %p", ctx); CUDA_ENSURE(cuCtxPopCurrent(&ctx), false); // TODO: why cuMemcpy2D need this } if (r.cuRes) { CUDA_ENSURE(cuGraphicsUnregisterResource(r.cuRes), false); r.cuRes = NULL; } // CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD works too for opengl, but not d3d CUDA_ENSURE(cuGraphicsGLRegisterImage(&r.cuRes, tex, GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_NONE), false); r.texture = tex; r.w = w; r.h = h; r.H = H; return true; } } //namespace cuda } //namespace QtAV #endif //QTAV_HAVE(CUDA_GL) QtAV-1.12.0/src/codec/video/SurfaceInteropCUDA.h000066400000000000000000000165211312235004300211150ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROPCUDA_H #define QTAV_SURFACEINTEROPCUDA_H #include "cuda_api.h" #include #include "QtAV/SurfaceInterop.h" #include "opengl/OpenGLHelper.h" #ifndef QT_NO_OPENGL #ifdef Q_OS_WIN // no need to check qt4 because no ANGLE there #if QTAV_HAVE(EGL_CAPI) // always use dynamic load #if (defined(QT_OPENGL_DYNAMIC) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_2_ANGLE)) && !defined(Q_OS_WINRT) #define QTAV_HAVE_CUDA_EGL 1 #include #endif #endif //QTAV_HAVE(EGL_CAPI) #endif //Q_OS_WIN #if defined(QT_OPENGL_DYNAMIC) || !defined(QT_OPENGL_ES_2) #define QTAV_HAVE_CUDA_GL 1 #endif #endif //QT_NO_OPENGL namespace QtAV { namespace cuda { class InteropResource : protected cuda_api { public: InteropResource(); void setDevice(CUdevice d) { dev = d;} void setShareContext(CUcontext c) { ctx = c; share_ctx = !!c; } void setDecoder(CUvideodecoder d) { dec = d;} void setLock(CUvideoctxlock l) { lock = l;} virtual ~InteropResource(); /// copy from gpu (optimized if possible) and convert to target format if necessary // mapToHost /*! * \brief map * \param surface cuda decoded surface * \param tex opengl texture * \param h frame height(visual height) without alignment, <= cuda decoded surface(picture) height * \param H cuda decoded surface(picture) height * \param plane useless now * \return true if success */ virtual bool map(int picIndex, const CUVIDPROCPARAMS& param, GLuint tex, int w, int h, int H, int plane) = 0; virtual bool unmap(GLuint tex) { Q_UNUSED(tex); return true;} /// copy from gpu and convert to target format if necessary. used by VideoCapture void* mapToHost(const VideoFormat &format, void *handle, int picIndex, const CUVIDPROCPARAMS ¶m, int width, int height, int surface_height); protected: bool share_ctx; CUdevice dev; CUcontext ctx; CUvideodecoder dec; CUvideoctxlock lock; typedef struct { GLuint texture; int w, h, W, H; CUgraphicsResource cuRes; CUstream stream; // for async works } TexRes; TexRes res[2]; }; typedef QSharedPointer InteropResourcePtr; // avoid inheriting cuda_api because SurfaceInteropCUDA is frequently created and cuda functions are only used in mapToHost() class SurfaceInteropCUDA Q_DECL_FINAL: public VideoSurfaceInterop { public: SurfaceInteropCUDA(const QWeakPointer& res) : m_index(-1), m_resource(res), h(0), H(0) {} ~SurfaceInteropCUDA() {} /*! * \brief setSurface * \param picIndex cuda decoded picture index in cuda decoder. * If use CUdeviceptr, we must cuMemAllocPitch() and cuMemcpyDtoD for each plane every time. But we can not know whether * the decoded picture the index pointed to is changed when rendering, so the displayed images may out of order. * CUdeviceptr is associated with context * \param width frame width(visual width) without alignment, <= cuda decoded surface(picture) width * \param height frame height(visual height) without alignment, <= cuda decoded surface(picture) height * \param surface_height cuda decoded surface(picture) height */ void setSurface(int picIndex, CUVIDPROCPARAMS param, int width, int height, int surface_height); /// GLTextureSurface only supports rgb32 void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE; void unmap(void *handle) Q_DECL_OVERRIDE; private: //CUdeviceptr m_surface; // invalid in a different context int m_index; CUVIDPROCPARAMS m_param; // decoder is deleted but interop is still alive QWeakPointer m_resource; int w, h, H; }; #ifndef QT_NO_OPENGL class HostInteropResource Q_DECL_FINAL: public InteropResource { public: HostInteropResource(); ~HostInteropResource(); bool map(int picIndex, const CUVIDPROCPARAMS& param, GLuint tex, int w, int h, int H, int plane) Q_DECL_OVERRIDE; bool unmap(GLuint) Q_DECL_OVERRIDE; private: bool ensureResource(int pitch, int height); struct { int index; uchar* data; int height; int pitch; } host_mem; }; #endif //QT_NO_OPENGL #if QTAV_HAVE(CUDA_EGL) class EGL; /*! * \brief The EGLInteropResource class * Interop with NV12 texture, then convert NV12 to RGB texture like DXVA+EGL does. * TODO: use pixel shader to convert L8+A8L8 textures to a NV12 texture, or an rgb texture directly on pbuffer surface * The VideoFrame from CUDA decoder is RGB format */ class EGLInteropResource Q_DECL_FINAL: public InteropResource { public: EGLInteropResource(); ~EGLInteropResource(); bool map(int picIndex, const CUVIDPROCPARAMS& param, GLuint tex, int w, int h, int H, int plane) Q_DECL_OVERRIDE; private: bool map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int H); bool ensureD3DDevice(); void releaseEGL(); bool ensureResource(int w, int h, int W, int H, GLuint tex); bool ensureD3D9CUDA(int w, int h, int W, int H); // check/update nv12 texture and register for cuda interoperation bool ensureD3D9EGL(int w, int h); EGL* egl; HINSTANCE dll9; IDirect3D9 *d3d9; IDirect3DDevice9 *device9; // for CUDA IDirect3DTexture9 *texture9; // for EGL. IDirect3DSurface9 *surface9; // for EGL. size is frame size(visual size) for display IDirect3DTexture9 *texture9_nv12; // If size is coded size, crop when StretchRect. If frame size, crop in cuMemcpy2D for each plane IDirect3DSurface9 *surface9_nv12; IDirect3DQuery9 *query9; }; #endif //QTAV_HAVE(CUDA_EGL) #if QTAV_HAVE(CUDA_GL) class GLInteropResource Q_DECL_FINAL: public InteropResource { public: bool map(int picIndex, const CUVIDPROCPARAMS& param, GLuint tex, int w, int h, int H, int plane) Q_DECL_OVERRIDE; bool unmap(GLuint tex) Q_DECL_OVERRIDE; private: /* * TODO: do we need to check h, H etc? interop is created by decoder and frame size does not change in the playback. * playing a new stream will recreate the decoder and interop * All we need to ensure is register when texture changed. But there's no way to check the texture change. */ bool ensureResource(int w, int h, int H, GLuint tex, int plane); }; #endif //QTAV_HAVE(CUDA_GL) } //namespace cuda } //namespace QtAV #endif // QTAV_SURFACEINTEROPCUDA_H QtAV-1.12.0/src/codec/video/SurfaceInteropCV.cpp000066400000000000000000000211531312235004300212410ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropCV.h" #include "QtAV/VideoFrame.h" #include "opengl/OpenGLHelper.h" namespace QtAV { typedef struct { int cv_pixfmt; VideoFormat::PixelFormat pixfmt; } cv_format; //https://developer.apple.com/library/Mac/releasenotes/General/MacOSXLionAPIDiffs/CoreVideo.html /* use fourcc '420v', 'yuvs' for NV12 and yuyv to avoid build time version check * qt4 targets 10.6, so those enum values is not valid in build time, while runtime is supported. */ static const cv_format cv_formats[] = { { 'y420', VideoFormat::Format_YUV420P }, //kCVPixelFormatType_420YpCbCr8Planar { '2vuy', VideoFormat::Format_UYVY }, //kCVPixelFormatType_422YpCbCr8 //#ifdef OSX_TARGET_MIN_LION { '420f' , VideoFormat::Format_NV12 }, // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange { '420v', VideoFormat::Format_NV12 }, //kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange { 'yuvs', VideoFormat::Format_YUYV }, //kCVPixelFormatType_422YpCbCr8_yuvs //#endif { 'BGRA', VideoFormat::Format_BGRA32 }, { kCVPixelFormatType_24RGB, VideoFormat::Format_RGB24 }, { 0, VideoFormat::Format_Invalid } }; namespace cv { VideoFormat::PixelFormat format_from_cv(int cv) { for (int i = 0; cv_formats[i].cv_pixfmt; ++i) { if (cv_formats[i].cv_pixfmt == cv) return cv_formats[i].pixfmt; } return VideoFormat::Format_Invalid; } extern InteropResource* CreateInteropCVPixelbuffer(); extern InteropResource* CreateInteropIOSurface(); extern InteropResource* CreateInteropCVOpenGL(); extern InteropResource* CreateInteropCVOpenGLES(); InteropResource* InteropResource::create(InteropType type) { if (type == InteropAuto) { #ifdef Q_OS_MACX type = InteropIOSurface; #else type = InteropCVPixelBuffer; type = InteropCVOpenGLES; #endif } switch (type) { case InteropCVPixelBuffer: return CreateInteropCVPixelbuffer(); #ifdef Q_OS_MACX case InteropIOSurface: return CreateInteropIOSurface(); //case InteropCVOpenGL: return CreateInteropCVOpenGL(); #else case InteropCVOpenGLES: return CreateInteropCVOpenGLES(); #endif default: return NULL; } return NULL; } InteropResource::InteropResource() : m_cvfmt(0) { memset(m_iformat, 0, sizeof(m_iformat)); memset(m_format, 0, sizeof(m_format)); memset(m_dtype, 0, sizeof(m_dtype)); } bool InteropResource::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat *outFmt) { *outFmt = format_from_cv(cvfmt); switch (cvfmt) { case '2vuy': case 'yuvs': { if (strides[0] <= 0) strides[0] = 2*width; } break; case '420v': case '420f': { if (strides[1] <= 0) strides[1] = width; } break; case 'y420': { if (strides[1] <= 0) strides[1] = strides[2] = width/2; } break; default: return false; } if (strides[0] <= 0) strides[0] = width; return true; } void InteropResource::getParametersGL(OSType cvpixfmt, GLint *internalFormat, GLenum *format, GLenum *dataType, int plane) { if (cvpixfmt != m_cvfmt) { const VideoFormat fmt(format_from_cv(cvpixfmt)); OpenGLHelper::videoFormatToGL(fmt, m_iformat, m_format, m_dtype); m_cvfmt = cvpixfmt; } *internalFormat = m_iformat[plane]; *format = m_format[plane]; *dataType = m_dtype[plane]; } void SurfaceInteropCV::setSurface(CVPixelBufferRef buf, int w, int h) { m_surface = buf; CVPixelBufferRetain(buf); // videotoolbox need it for map and CVPixelBufferRelease frame_width = w; frame_height = h; } SurfaceInteropCV::~SurfaceInteropCV() { if (m_surface) CVPixelBufferRelease(m_surface); } void* SurfaceInteropCV::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane) { if (!handle) return NULL; if (!m_surface) return 0; if (type == GLTextureSurface) { if (m_resource->map(m_surface, (GLuint*)handle, frame_width, frame_height, plane)) return handle; } else if (type == HostMemorySurface) { return mapToHost(fmt, handle, plane); } else if (type == SourceSurface) { CVPixelBufferRef *b = reinterpret_cast(handle); *b = m_surface; return handle; } return NULL; } void SurfaceInteropCV::unmap(void *handle) { // TODO: surface type m_resource->unmap(m_surface, *((GLuint*)handle)); } void* SurfaceInteropCV::createHandle(void *handle, SurfaceType type, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) { if (type != GLTextureSurface) return NULL; GLuint tex = m_resource->createTexture(m_surface, fmt, plane, planeWidth, planeHeight); if (tex == 0) return NULL; *((GLuint*)handle) = tex; return handle; } void* SurfaceInteropCV::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); CVPixelBufferLockBaseAddress(m_surface, kCVPixelBufferLock_ReadOnly); const VideoFormat fmt(format_from_cv(CVPixelBufferGetPixelFormatType(m_surface))); if (!fmt.isValid()) { CVPixelBufferUnlockBaseAddress(m_surface, kCVPixelBufferLock_ReadOnly); return NULL; } const int w = CVPixelBufferGetWidth(m_surface); const int h = CVPixelBufferGetHeight(m_surface); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { // get address results in internal copy src[i] = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(m_surface, i); pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(m_surface, i); } CVPixelBufferUnlockBaseAddress(m_surface, kCVPixelBufferLock_ReadOnly); //CVPixelBufferRelease(cv_buffer); // release when video frame is destroyed VideoFrame frame(VideoFrame::fromGPU(fmt, w, h, h, src, pitch)); if (fmt != format) frame = frame.to(format); VideoFrame *f = reinterpret_cast(handle); frame.setTimestamp(f->timestamp()); frame.setDisplayAspectRatio(f->displayAspectRatio()); *f = frame; return f; } /*! * \brief The InteropResourceCVPixelBuffer class * The mapping is not 0-copy. Use CVPixelBufferGetBaseAddressOfPlane to upload video frame to opengl. */ class InteropResourceCVPixelBuffer Q_DECL_FINAL : public InteropResource { public: bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE; }; InteropResource* CreateInteropCVPixelbuffer() { return new InteropResourceCVPixelBuffer(); } bool InteropResourceCVPixelBuffer::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) { Q_UNUSED(h); Q_UNUSED(w); CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); GLint iformat; GLenum format, dtype; getParametersGL(CVPixelBufferGetPixelFormatType(buf), &iformat, &format, &dtype, plane); //TODO: call once when format changed const int texture_w = CVPixelBufferGetBytesPerRowOfPlane(buf, plane)/OpenGLHelper::bytesOfGLFormat(format, dtype); //qDebug("cv plane%d width: %d, stride: %d, tex width: %d", plane, CVPixelBufferGetWidthOfPlane(buf, plane), CVPixelBufferGetBytesPerRowOfPlane(buf, plane), texture_w); // get address results in internal copy DYGL(glBindTexture(GL_TEXTURE_2D, *tex)); DYGL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0 , texture_w , CVPixelBufferGetHeightOfPlane(buf, plane) , format , dtype , (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(buf, plane))); CVPixelBufferUnlockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); return true; } } // namespace cv } // namespace QtAV QtAV-1.12.0/src/codec/video/SurfaceInteropCV.h000066400000000000000000000106301312235004300207040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROPCV_H #define QTAV_SURFACEINTEROPCV_H #include #include #include namespace QtAV { namespace cv { VideoFormat::PixelFormat format_from_cv(int cv); typedef uint32_t GLuint; // define here to avoid including gl headers which are not required by decoder typedef uint32_t GLenum; typedef int32_t GLint; // FIXME: not texture if change from InteropCVOpenGLES to InteropCVPixelBuffer enum InteropType { InteropCVPixelBuffer, // macOS+ios InteropIOSurface, // macOS InteropCVOpenGL, // macOS, not implemented InteropCVOpenGLES, // ios InteropAuto }; class InteropResource { public: InteropResource(); // Must have CreateInteropXXX in each implemention static InteropResource* create(InteropType type); virtual ~InteropResource() {} /*! * \brief stridesForWidth * The stride used by opengl can be different in some interop because frame display format can change (outFmt), for example we can use rgb for uyvy422. The default value use the origial pixel format to comupte the strides. * If the input strides has a valid value, use this value to compute the output strides. Otherwise, use width to compute the output strides without taking care of padded data size. */ virtual bool stridesForWidth(int cvfmt, int width, int* strides/*in_out*/, VideoFormat::PixelFormat* outFmt); virtual bool mapToTexture2D() const { return true;} // egl supports yuv extension /*! * \brief map * \param buf vt decoded buffer * \param texInOut opengl texture. You can modify it * \param w frame width(visual width) without alignment, <= dxva surface width * \param h frame height(visual height) * \param plane useless now * \return true if success */ virtual bool map(CVPixelBufferRef buf, GLuint *texInOut, int w, int h, int plane) = 0; virtual bool unmap(CVPixelBufferRef buf, GLuint tex) { Q_UNUSED(buf); Q_UNUSED(tex); return true; } virtual GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) { Q_UNUSED(fmt); Q_UNUSED(plane); Q_UNUSED(planeWidth); Q_UNUSED(planeHeight); return 0; } void getParametersGL(OSType cvpixfmt, GLint* internalFormat, GLenum* format, GLenum* dataType, int plane = 0); private: OSType m_cvfmt; GLint m_iformat[4]; GLenum m_format[4]; GLenum m_dtype[4]; }; typedef QSharedPointer InteropResourcePtr; class SurfaceInteropCV Q_DECL_FINAL: public VideoSurfaceInterop { public: SurfaceInteropCV(const InteropResourcePtr& res) : frame_width(0), frame_height(0), m_resource(res) {} ~SurfaceInteropCV(); void setSurface(CVPixelBufferRef buf, int w, int h); void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE; void unmap(void *handle) Q_DECL_OVERRIDE; virtual void* createHandle(void* handle, SurfaceType type, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE; protected: void* mapToHost(const VideoFormat &format, void *handle, int plane); private: int frame_width, frame_height; InteropResourcePtr m_resource; CVPixelBufferRef m_surface; }; } // namespace cv } // namespace QtAV #endif //QTAV_SURFACEINTEROPCV_H QtAV-1.12.0/src/codec/video/SurfaceInteropCVOpenGLES.mm000066400000000000000000000131451312235004300223670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropCV.h" #include #include #import #include "QtAV/VideoFrame.h" #include "opengl/OpenGLHelper.h" namespace QtAV { namespace cv { VideoFormat::PixelFormat format_from_cv(int cv); // https://www.opengl.org/registry/specs/APPLE/rgb_422.txt class InteropResourceCVOpenGLES Q_DECL_FINAL : public InteropResource { public: InteropResourceCVOpenGLES() : InteropResource() , texture_cache(NULL) { memset(textures, 0, sizeof(textures)); } ~InteropResourceCVOpenGLES() { for (int i = 0; i < 4; ++i) { if (textures[i]) { CFRelease(textures[i]); textures[i] = NULL; } } if (texture_cache) { CFRelease(texture_cache); texture_cache = NULL; } } bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE; bool map(CVPixelBufferRef buf, GLuint *texInOut, int w, int h, int plane) Q_DECL_OVERRIDE; private: bool ensureResource(); CVOpenGLESTextureCacheRef texture_cache; CVOpenGLESTextureRef textures[4]; }; InteropResource* CreateInteropCVOpenGLES() { return new InteropResourceCVOpenGLES(); } bool InteropResourceCVOpenGLES::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt) { switch (cvfmt) { case '2vuy': case 'yuvs': { *outFmt = VideoFormat::Format_VYU; if (strides[0] <= 0) strides[0] = 4*width; //RGB layout: BRGX else strides[0] *= 2; } break; default: return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt); } return true; } bool InteropResourceCVOpenGLES::map(CVPixelBufferRef buf, GLuint *texInOut, int w, int h, int plane) { Q_UNUSED(w); Q_UNUSED(h); if (!ensureResource()) return false; const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf); GLint iformat; GLenum format, dtype; getParametersGL(pixfmt, &iformat, &format, &dtype, plane); switch (pixfmt) { case '2vuy': case 'yuvs': iformat = GL_RGB_422_APPLE; // ES2 requires internal format and format are the same. desktop can use internal format GL_RGB or sized GL_RGB8 format = GL_RGB_422_APPLE; dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE; break; case 'BGRA': // GL_BGRA error? iformat = GL_RGBA; format = GL_RGBA; break; default: break; } CVOpenGLESTextureCacheFlush(texture_cache, NULL); CVOpenGLESTextureRef &tex = textures[plane]; if (tex) { CFRelease(tex); tex = NULL; } const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane); const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane); //const int texture_w = CVPixelBufferGetBytesPerRowOfPlane(buf, plane)/OpenGLHelper::bytesOfGLFormat(format, dtype); //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype); CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage( kCFAllocatorDefault , texture_cache , buf , NULL , GL_TEXTURE_2D , iformat , planeW //Why not texture width? , planeH , format, dtype , plane , &tex); if (err != kCVReturnSuccess) { qDebug("Failed to create OpenGL texture(%d)", err); return false; } //CVOpenGLESTextureGetCleanTexCoords *texInOut = CVOpenGLESTextureGetName(tex); //TODO: remove below? DYGL(glBindTexture(GL_TEXTURE_2D, *texInOut)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); // This is necessary for non-power-of-two textures DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); //TODO: remove. only call once return true; } bool InteropResourceCVOpenGLES::ensureResource() { if (texture_cache) return true; CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [EAGLContext currentContext], NULL, &texture_cache); if (err != kCVReturnSuccess) { qWarning("Failed to create OpenGL ES texture Cache(%d)", err); return false; } return true; } } // namespace cv } // namespace QtAV QtAV-1.12.0/src/codec/video/SurfaceInteropIOSurface.cpp000066400000000000000000000106501312235004300225510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropCV.h" #include #include "QtAV/VideoFrame.h" #include "opengl/OpenGLHelper.h" namespace QtAV { namespace cv { VideoFormat::PixelFormat format_from_cv(int cv); // https://www.opengl.org/registry/specs/APPLE/rgb_422.txt // https://www.opengl.org/registry/specs/APPLE/ycbcr_422.txt uyvy: UNSIGNED_SHORT_8_8_REV_APPLE, yuy2: GL_UNSIGNED_SHORT_8_8_APPLE // check extension GL_APPLE_rgb_422 and rectangle? class InteropResourceIOSurface Q_DECL_FINAL : public InteropResource { public: bool stridesForWidth(int cvfmt, int width, int* strides, VideoFormat::PixelFormat* outFmt) Q_DECL_OVERRIDE; bool mapToTexture2D() const Q_DECL_OVERRIDE { return false;} bool map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) Q_DECL_OVERRIDE; GLuint createTexture(CVPixelBufferRef, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE { Q_UNUSED(fmt); Q_UNUSED(plane); Q_UNUSED(planeWidth); Q_UNUSED(planeHeight); GLuint tex = 0; DYGL(glGenTextures(1, &tex)); return tex; } }; InteropResource* CreateInteropIOSurface() { return new InteropResourceIOSurface(); } bool InteropResourceIOSurface::stridesForWidth(int cvfmt, int width, int *strides, VideoFormat::PixelFormat* outFmt) { switch (cvfmt) { case '2vuy': case 'yuvs': { *outFmt = VideoFormat::Format_VYU; if (strides[0] <= 0) strides[0] = 4*width; //RGB layout: BRGX else strides[0] *= 2; } break; default: return InteropResource::stridesForWidth(cvfmt, width, strides, outFmt); } return true; } bool InteropResourceIOSurface::map(CVPixelBufferRef buf, GLuint *tex, int w, int h, int plane) { Q_UNUSED(w); Q_UNUSED(h); const OSType pixfmt = CVPixelBufferGetPixelFormatType(buf); GLint iformat; GLenum format, dtype; getParametersGL(pixfmt, &iformat, &format, &dtype, plane); switch (pixfmt) { case '2vuy': case 'yuvs': iformat = GL_RGB8; // ES2 requires internal format and format are the same. OSX can use internal format GL_RGB or sized GL_RGB8 format = GL_RGB_422_APPLE; dtype = pixfmt == '2vuy' ? GL_UNSIGNED_SHORT_8_8_APPLE : GL_UNSIGNED_SHORT_8_8_REV_APPLE; break; // macOS: GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV // GL_YCBCR_422_APPLE: convert to rgb texture internally (bt601). only supports OSX // GL_RGB_422_APPLE: raw yuv422 texture case 'BGRA': iformat = GL_RGBA8; format = GL_BGRA; dtype = GL_UNSIGNED_INT_8_8_8_8_REV; break; default: break; } const GLenum target = GL_TEXTURE_RECTANGLE; DYGL(glBindTexture(target, *tex)); const int planeW = CVPixelBufferGetWidthOfPlane(buf, plane); const int planeH = CVPixelBufferGetHeightOfPlane(buf, plane); //qDebug("map plane%d. %dx%d, gl %d %d %d", plane, planeW, planeH, iformat, format, dtype); const IOSurfaceRef surface = CVPixelBufferGetIOSurface(buf); CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, planeW, planeH, format, dtype, surface, plane); if (err != kCGLNoError) { qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err)); } DYGL(glBindTexture(target, 0)); return true; } } // namespace cv } // namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoder.cpp000066400000000000000000000105531312235004300204150ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoDecoder.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(VideoDecoder) VideoDecoderId VideoDecoderId_FFmpeg = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value; VideoDecoderId VideoDecoderId_CUDA = mkid::id32base36_4<'C', 'U', 'D', 'A'>::value; VideoDecoderId VideoDecoderId_DXVA = mkid::id32base36_4<'D', 'X', 'V', 'A'>::value; VideoDecoderId VideoDecoderId_D3D11 = mkid::id32base36_5<'D','3','D','1','1'>::value; VideoDecoderId VideoDecoderId_VAAPI = mkid::id32base36_5<'V', 'A', 'A', 'P', 'I'>::value; VideoDecoderId VideoDecoderId_Cedarv = mkid::id32base36_6<'C', 'e', 'd', 'a', 'r', 'V'>::value; VideoDecoderId VideoDecoderId_VDA = mkid::id32base36_3<'V', 'D', 'A'>::value; VideoDecoderId VideoDecoderId_VideoToolbox = mkid::id32base36_5<'V', 'T', 'B', 'o', 'x'>::value; VideoDecoderId VideoDecoderId_MediaCodec = mkid::id32base36_4<'F','F','M','C'>::value; VideoDecoderId VideoDecoderId_MMAL = mkid::id32base36_6<'F','F','M', 'M','A', 'L'>::value; VideoDecoderId VideoDecoderId_QSV = mkid::id32base36_5<'F','F','Q','S', 'V'>::value; VideoDecoderId VideoDecoderId_CrystalHD = mkid::id32base36_5<'F','F','C','H', 'D'>::value; static void VideoDecoder_RegisterAll() { static bool called = false; if (called) return; called = true; // factory.h does not check whether an id is registered if (VideoDecoder::name(VideoDecoderId_FFmpeg)) //registered on load return; extern bool RegisterVideoDecoderFFmpeg_Man(); RegisterVideoDecoderFFmpeg_Man(); #if QTAV_HAVE(DXVA) extern bool RegisterVideoDecoderDXVA_Man(); RegisterVideoDecoderDXVA_Man(); #endif //QTAV_HAVE(DXVA) #if QTAV_HAVE(D3D11VA) extern bool RegisterVideoDecoderD3D11_Man(); RegisterVideoDecoderD3D11_Man(); #endif //QTAV_HAVE(DXVA) #if QTAV_HAVE(CUDA) extern bool RegisterVideoDecoderCUDA_Man(); RegisterVideoDecoderCUDA_Man(); #endif //QTAV_HAVE(CUDA) #if QTAV_HAVE(VAAPI) extern bool RegisterVideoDecoderVAAPI_Man(); RegisterVideoDecoderVAAPI_Man(); #endif //QTAV_HAVE(VAAPI) #if QTAV_HAVE(VIDEOTOOLBOX) extern bool RegisterVideoDecoderVideoToolbox_Man(); RegisterVideoDecoderVideoToolbox_Man(); #endif //QTAV_HAVE(VIDEOTOOLBOX) #if QTAV_HAVE(VDA) extern bool RegisterVideoDecoderVDA_Man(); RegisterVideoDecoderVDA_Man(); #endif //QTAV_HAVE(VDA) #if QTAV_HAVE(CEDARV) extern bool RegisterVideoDecoderCedarv_Man(); RegisterVideoDecoderCedarv_Man(); #endif //QTAV_HAVE(CEDARV) } // TODO: called in ::create()/next() etc. to ensure registered? QVector VideoDecoder::registered() { VideoDecoder_RegisterAll(); return QVector::fromStdVector(VideoDecoderFactory::Instance().registeredIds()); } QStringList VideoDecoder::supportedCodecs() { static QStringList codecs; if (!codecs.isEmpty()) return codecs; avcodec_register_all(); AVCodec* c = NULL; while ((c=av_codec_next(c))) { if (!av_codec_is_decoder(c) || c->type != AVMEDIA_TYPE_VIDEO) continue; codecs.append(QString::fromLatin1(c->name)); } return codecs; } VideoDecoder::VideoDecoder(VideoDecoderPrivate &d): AVDecoder(d) { } QString VideoDecoder::name() const { return QLatin1String(VideoDecoder::name(id())); } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderCUDA.cpp000066400000000000000000000736241312235004300210620ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoDecoder.h" #include "QtAV/Packet.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/private/factory.h" #include #if QTAV_HAVE(DLLAPI_CUDA) #include "dllapi.h" #endif //QTAV_HAVE(DLLAPI_CUDA) #include "QtAV/private/AVCompat.h" #include "utils/BlockingQueue.h" /* * TODO: VC1, HEVC bsf */ #define COPY_ON_DECODE 1 /* * avc1, ccv1 => h264 + sps, pps, nal. use filter or lavcudiv * http://blog.csdn.net/gavinr/article/details/7183499 * https://www.ffmpeg.org/ffmpeg-bitstream-filters.html * http://hi.baidu.com/freenaut/item/49ca125112587314aaf6d733 * CUDA_ERROR_INVALID_VALUE "cuvidDecodePicture(p->dec, cuvidpic)" */ #include "cuda/helper_cuda.h" #include "cuda/cuda_api.h" #include "utils/Logger.h" #include "SurfaceInteropCUDA.h" //decode error if not floating context namespace QtAV { static const unsigned int kMaxDecodeSurfaces = 20; class VideoDecoderCUDAPrivate; class VideoDecoderCUDA : public VideoDecoder { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderCUDA) Q_PROPERTY(CopyMode copyMode READ copyMode WRITE setCopyMode NOTIFY copyModeChanged) Q_PROPERTY(int surfaces READ surfaces WRITE setSurfaces) Q_PROPERTY(Flags flags READ flags WRITE setFlags) Q_PROPERTY(Deinterlace deinterlace READ deinterlace WRITE setDeinterlace) Q_FLAGS(Flags) Q_ENUMS(Flags) Q_ENUMS(Deinterlace) Q_ENUMS(CopyMode) public: enum Flags { Default = cudaVideoCreate_Default, // Default operation mode: use dedicated video engines CUDA = cudaVideoCreate_PreferCUDA, // Use a CUDA-based decoder if faster than dedicated engines (requires a valid vidLock object for multi-threading) DXVA = cudaVideoCreate_PreferDXVA, // Go through DXVA internally if possible (requires D3D9 interop) CUVID = cudaVideoCreate_PreferCUVID // Use dedicated video engines directly }; enum Deinterlace { Bob = cudaVideoDeinterlaceMode_Bob, // Drop one field Weave = cudaVideoDeinterlaceMode_Weave, // Weave both fields (no deinterlacing) Adaptive = cudaVideoDeinterlaceMode_Adaptive // Adaptive deinterlacing }; enum CopyMode { ZeroCopy, // default for linux DirectCopy, // use the same host address without additional copy to frame. If address does not change, it should be safe GenericCopy }; VideoDecoderCUDA(); ~VideoDecoderCUDA(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; void flush() Q_DECL_OVERRIDE; bool decode(const Packet &packet) Q_DECL_OVERRIDE; virtual VideoFrame frame() Q_DECL_OVERRIDE; // properties int surfaces() const; void setSurfaces(int n); Flags flags() const; void setFlags(Flags f); Deinterlace deinterlace() const; void setDeinterlace(Deinterlace di); CopyMode copyMode() const; void setCopyMode(CopyMode value); Q_SIGNALS: void copyModeChanged(CopyMode value); }; extern VideoDecoderId VideoDecoderId_CUDA; FACTORY_REGISTER(VideoDecoder, CUDA, "CUDA") static struct { AVCodecID ffCodec; cudaVideoCodec cudaCodec; } const ff_cuda_codecs[] = { { QTAV_CODEC_ID(MPEG1VIDEO), cudaVideoCodec_MPEG1 }, { QTAV_CODEC_ID(MPEG2VIDEO), cudaVideoCodec_MPEG2 }, { QTAV_CODEC_ID(MPEG4), cudaVideoCodec_MPEG4 }, { QTAV_CODEC_ID(VC1), cudaVideoCodec_VC1 }, { QTAV_CODEC_ID(H264), cudaVideoCodec_H264 }, { QTAV_CODEC_ID(H264), cudaVideoCodec_H264_SVC}, { QTAV_CODEC_ID(H264), cudaVideoCodec_H264_MVC}, { QTAV_CODEC_ID(HEVC), cudaVideoCodec_HEVC }, { QTAV_CODEC_ID(VP8), cudaVideoCodec_VP8 }, { QTAV_CODEC_ID(VP9), cudaVideoCodec_VP9 }, { QTAV_CODEC_ID(NONE), cudaVideoCodec_NumCodecs} }; static cudaVideoCodec mapCodecFromFFmpeg(AVCodecID codec) { for (int i = 0; ff_cuda_codecs[i].ffCodec != QTAV_CODEC_ID(NONE); ++i) { if (ff_cuda_codecs[i].ffCodec == codec) { return ff_cuda_codecs[i].cudaCodec; } } return cudaVideoCodec_NumCodecs; } static AVCodecID mapCodecToFFmpeg(cudaVideoCodec cudaCodec) { for (int i = 0; ff_cuda_codecs[i].ffCodec != QTAV_CODEC_ID(NONE); ++i) { if (ff_cuda_codecs[i].cudaCodec == cudaCodec) { return ff_cuda_codecs[i].ffCodec; } } return QTAV_CODEC_ID(NONE); } class VideoDecoderCUDAPrivate Q_DECL_FINAL: public VideoDecoderPrivate , protected cuda_api { public: VideoDecoderCUDAPrivate(): VideoDecoderPrivate() , can_load(true) , host_data(0) , host_data_size(0) , create_flags(cudaVideoCreate_Default) , deinterlace(cudaVideoDeinterlaceMode_Adaptive) , yuv_range(ColorRange_Limited) , nb_dec_surface(kMaxDecodeSurfaces) , copy_mode(VideoDecoderCUDA::DirectCopy) { #ifndef Q_OS_WIN // CUDA+GL/D3D interop requires NV GPU is used for rendering. Windows can use CUDA with intel GPU, I don't know how to detect this, so 0-copy interop can fail in this case. But linux always requires nvidia GPU to use CUDA, so interop works. copy_mode = VideoDecoderCUDA::ZeroCopy; #endif #if QTAV_HAVE(DLLAPI_CUDA) can_load = dllapi::testLoad("nvcuvid"); #endif //QTAV_HAVE(DLLAPI_CUDA) available = false; bsf = 0; cuctx = 0; cudev = 0; dec = 0; vid_ctx_lock = 0; parser = 0; stream = 0; force_sequence_update = false; frame_queue.setCapacity(20); frame_queue.setThreshold(10); surface_in_use.resize(nb_dec_surface); surface_in_use.fill(false); if (!can_load) return; if (!isLoaded()) //cuda_api return; interop_res = cuda::InteropResourcePtr(); } ~VideoDecoderCUDAPrivate() { if (bsf) av_bitstream_filter_close(bsf); if (!can_load) return; if (!isLoaded()) //cuda_api return; // if not reset here, CUDA_ERROR_CONTEXT_IS_DESTROYED in ~cuda::InteropResource() // QSharedPointer.reset() is in qt5 interop_res = cuda::InteropResourcePtr(); // in interop object it's weak ptr. It's safe to reset here before cuda resouce is released. releaseCuda(); } bool open() Q_DECL_OVERRIDE; bool initCuda(); bool releaseCuda(); bool createCUVIDDecoder(cudaVideoCodec cudaCodec, int cw, int ch); void createInterop() { if (copy_mode == VideoDecoderCUDA::ZeroCopy) { #if QTAV_HAVE(CUDA_GL) if (!OpenGLHelper::isOpenGLES()) interop_res = cuda::InteropResourcePtr(new cuda::GLInteropResource()); #endif //QTAV_HAVE(CUDA_GL) #if QTAV_HAVE(CUDA_EGL) if (OpenGLHelper::isOpenGLES()) interop_res = cuda::InteropResourcePtr(new cuda::EGLInteropResource()); #endif //QTAV_HAVE(CUDA_EGL) } #ifndef QT_NO_OPENGL else if (copy_mode == VideoDecoderCUDA::DirectCopy) { interop_res = cuda::InteropResourcePtr(new cuda::HostInteropResource()); } #endif //QT_NO_OPENGL if (!interop_res) return; interop_res->setDevice(cudev); interop_res->setShareContext(cuctx); //it not share the context, interop res will create it's own context, context switch is slow interop_res->setDecoder(dec); interop_res->setLock(vid_ctx_lock); } bool createCUVIDParser(); bool flushParser(); bool processDecodedData(CUVIDPARSERDISPINFO *cuviddisp, VideoFrame* outFrame = 0); bool doParseVideoData(CUVIDSOURCEDATAPACKET* pPkt) { //CUDA_ENSURE(cuCtxPushCurrent(cuctx), false); AutoCtxLock lock(this, vid_ctx_lock); Q_UNUSED(lock); CUDA_ENSURE(cuvidParseVideoData(parser, pPkt), false); return true; } bool doDecodePicture(CUVIDPICPARAMS *cuvidpic) { AutoCtxLock lock(this, vid_ctx_lock); Q_UNUSED(lock); CUDA_ENSURE(cuvidDecodePicture(dec, cuvidpic), false); return true; } VideoFrame getNextFrame() { #if COPY_ON_DECODE return frame_queue.take(); #else VideoFrame vf; CUVIDPARSERDISPINFO *cuviddisp = frame_queue.take(); processDecodedData(cuviddisp, &vf); return vf; #endif //COPY_ON_DECODE } static int CUDAAPI HandleVideoSequence(void *obj, CUVIDEOFORMAT *cuvidfmt) { VideoDecoderCUDAPrivate *p = reinterpret_cast(obj); CUVIDDECODECREATEINFO *dci = &p->dec_create_info; if ((cuvidfmt->codec != dci->CodecType) || (cuvidfmt->coded_width != dci->ulWidth) || (cuvidfmt->coded_height != dci->ulHeight) || (cuvidfmt->chroma_format != dci->ChromaFormat) || p->force_sequence_update) { qDebug("recreate cuvid parser"); p->force_sequence_update = false; p->yuv_range = cuvidfmt->video_signal_description.video_full_range_flag ? ColorRange_Full : ColorRange_Limited; //coded_width or width? p->createCUVIDDecoder(cuvidfmt->codec, cuvidfmt->coded_width, cuvidfmt->coded_height); // how about parser.ulMaxNumDecodeSurfaces? recreate? AVCodecID codec = mapCodecToFFmpeg(cuvidfmt->codec); p->setBSF(codec); p->createInterop(); } //TODO: lavfilter return 1; } static int CUDAAPI HandlePictureDecode(void *obj, CUVIDPICPARAMS *cuvidpic) { VideoDecoderCUDAPrivate *p = reinterpret_cast(obj); //qDebug("%s @%d tid=%p dec=%p idx=%d inUse=%d", __FUNCTION__, __LINE__, QThread::currentThread(), p->dec, cuvidpic->CurrPicIdx, p->surface_in_use[cuvidpic->CurrPicIdx]); p->doDecodePicture(cuvidpic); return 1; } static int CUDAAPI HandlePictureDisplay(void *obj, CUVIDPARSERDISPINFO *cuviddisp) { VideoDecoderCUDAPrivate *p = reinterpret_cast(obj); p->surface_in_use[cuviddisp->picture_index] = true; //qDebug("mark in use pic_index: %d", cuviddisp->picture_index); #if COPY_ON_DECODE return p->processDecodedData(cuviddisp, 0); #else p->frame_queue.put(cuviddisp); return 1; #endif } void setBSF(AVCodecID codec); bool can_load; //if linked to cuvid, it's true. otherwise(use dllapi) equals to whether cuvid can be loaded uchar *host_data; int host_data_size; CUcontext cuctx; CUdevice cudev; cudaVideoCreateFlags create_flags; cudaVideoDeinterlaceMode deinterlace; CUvideodecoder dec; CUVIDDECODECREATEINFO dec_create_info; CUvideoctxlock vid_ctx_lock; //NULL CUVIDPICPARAMS pic_params; CUVIDEOFORMATEX extra_parser_info; CUvideoparser parser; CUstream stream; bool force_sequence_update; ColorRange yuv_range; /* * callbacks are in the same thread as cuvidParseVideoData. so video thread may be blocked * so create another thread? */ #if COPY_ON_DECODE BlockingQueue frame_queue; #else BlockingQueue frame_queue; #endif QVector surface_in_use; int nb_dec_surface; QString description; AVBitStreamFilterContext *bsf; //TODO: rename bsf_ctx VideoDecoderCUDA::CopyMode copy_mode; cuda::InteropResourcePtr interop_res; //may be still used in video frames when decoder is destroyed }; VideoDecoderCUDA::VideoDecoderCUDA(): VideoDecoder(*new VideoDecoderCUDAPrivate()) { // dynamic properties about static property details. used by UI // format: detail_property setProperty("detail_surfaces", tr("Decoding surfaces")); setProperty("detail_flags", tr("Decoder flags")); setProperty("detail_copyMode", QString("%1\n%2\n%3\n%4") .arg(tr("Performace: ZeroCopy > DirectCopy > GenericCopy")) .arg(tr("ZeroCopy: no copy back from GPU to System memory. Directly render the decoded data on GPU")) .arg(tr("DirectCopy: copy back to host memory but video frames and map to GL texture")) .arg(tr("GenericCopy: copy back to host memory and each video frame")) ); Q_UNUSED(QObject::tr("ZeroCopy")); Q_UNUSED(QObject::tr("DirectCopy")); Q_UNUSED(QObject::tr("GenericCopy")); Q_UNUSED(QObject::tr("copyMode")); } VideoDecoderCUDA::~VideoDecoderCUDA() { } VideoDecoderId VideoDecoderCUDA::id() const { return VideoDecoderId_CUDA; } QString VideoDecoderCUDA::description() const { DPTR_D(const VideoDecoderCUDA); if (!d.description.isEmpty()) return d.description; return QStringLiteral("NVIDIA CUVID"); } void VideoDecoderCUDA::flush() { DPTR_D(VideoDecoderCUDA); d.frame_queue.clear(); d.surface_in_use.fill(false); } bool VideoDecoderCUDA::decode(const Packet &packet) { if (!isAvailable()) return false; DPTR_D(VideoDecoderCUDA); if (!d.parser) { qWarning("CUVID parser not ready"); return false; } if (packet.isEOF()) { if (!d.flushParser()) { qDebug("Error decode EOS"); // when? return false; } return !d.frame_queue.isEmpty(); } uint8_t *outBuf = 0; int outBufSize = 0; int filtered = 0; if (d.bsf) { // h264_mp4toannexb_filter does not use last parameter 'keyFrame', so just set 0 //return: 0: not changed, no outBuf allocated. >0: ok. <0: fail filtered = av_bitstream_filter_filter(d.bsf, d.codec_ctx, NULL, &outBuf, &outBufSize , (const uint8_t*)packet.data.constData(), packet.data.size() , 0);//d.is_keyframe); //qDebug("%s @%d filtered=%d outBuf=%p, outBufSize=%d", __FUNCTION__, __LINE__, filtered, outBuf, outBufSize); if (filtered < 0) { qDebug("failed to filter: %s", av_err2str(filtered)); } } else { outBuf = (uint8_t*)packet.data.constData(); outBufSize = packet.data.size(); } CUVIDSOURCEDATAPACKET cuvid_pkt; memset(&cuvid_pkt, 0, sizeof(CUVIDSOURCEDATAPACKET)); cuvid_pkt.payload = outBuf;// (unsigned char *)packet.data.constData(); cuvid_pkt.payload_size = outBufSize; //packet.data.size(); // TODO: other flags if (packet.pts >= 0.0) { cuvid_pkt.flags = CUVID_PKT_TIMESTAMP; cuvid_pkt.timestamp = packet.pts * 1000.0; // TODO: 10MHz? } //TODO: fill NALU header for h264? https://devtalk.nvidia.com/default/topic/515571/what-the-data-format-34-cuvidparsevideodata-34-can-accept-/ d.doParseVideoData(&cuvid_pkt); if (filtered > 0) { av_freep(&outBuf); } // callbacks are in the same thread as this. so no queue is required? //qDebug("frame queue size on decode: %d", d.frame_queue.size()); return !d.frame_queue.isEmpty(); // video thread: if dec.hasFrame() keep pkt for the next loop and not decode, direct display the frame } VideoFrame VideoDecoderCUDA::frame() { DPTR_D(VideoDecoderCUDA); if (d.frame_queue.isEmpty()) return VideoFrame(); #if COPY_ON_DECODE return d.frame_queue.take(); #else return d.getNextFrame(); #endif } int VideoDecoderCUDA::surfaces() const { return d_func().nb_dec_surface; } void VideoDecoderCUDA::setSurfaces(int n) { if (n <= 0) n = kMaxDecodeSurfaces; DPTR_D(VideoDecoderCUDA); d.nb_dec_surface = n; d.surface_in_use.resize(n); d.surface_in_use.fill(false); } VideoDecoderCUDA::Flags VideoDecoderCUDA::flags() const { return (Flags)d_func().create_flags; } void VideoDecoderCUDA::setFlags(Flags f) { d_func().create_flags = (cudaVideoCreateFlags)f; } VideoDecoderCUDA::Deinterlace VideoDecoderCUDA::deinterlace() const { return (Deinterlace)d_func().deinterlace; } void VideoDecoderCUDA::setDeinterlace(Deinterlace di) { d_func().deinterlace = (cudaVideoDeinterlaceMode)di; } VideoDecoderCUDA::CopyMode VideoDecoderCUDA::copyMode() const { return d_func().copy_mode; } void VideoDecoderCUDA::setCopyMode(CopyMode value) { DPTR_D(VideoDecoderCUDA); if (d.copy_mode == value) return; d.copy_mode = value; Q_EMIT copyModeChanged(value); } bool VideoDecoderCUDAPrivate::open() { //TODO: destroy decoder // d.available is true if cuda decoder is ready if (!can_load) { qWarning("VideoDecoderCUDAPrivate::open(): CUVID library not available"); return false; } if (!isLoaded()) //cuda_api return false; if (!cuctx) initCuda(); setBSF(codec_ctx->codec_id); // max decoder surfaces is computed in createCUVIDDecoder. createCUVIDParser use the value if (!createCUVIDDecoder(mapCodecFromFFmpeg(codec_ctx->codec_id), codec_ctx->coded_width, codec_ctx->coded_height)) return false; if (!createCUVIDParser()) return false; available = true; return true; } bool VideoDecoderCUDAPrivate::initCuda() { CUDA_ENSURE(cuInit(0), false); cudev = GetMaxGflopsGraphicsDeviceId(); int clockRate; cuDeviceGetAttribute(&clockRate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, cudev); int major, minor; CUDA_WARN(cuDeviceComputeCapability(&major, &minor, cudev)); char devname[256]; CUDA_WARN(cuDeviceGetName(devname, 256, cudev)); description = QStringLiteral("CUDA device: %1 %2.%3 %4 MHz @%5").arg(QLatin1String((const char*)devname)).arg(major).arg(minor).arg(clockRate/1000).arg(cudev); // cuD3DCtxCreate > cuGLCtxCreate(deprecated) > cuCtxCreate (fallback if d3d and gl return status is failed) CUDA_ENSURE(cuCtxCreate(&cuctx, CU_CTX_SCHED_BLOCKING_SYNC, cudev), false); //CU_CTX_SCHED_AUTO: slower in my test #if 0 //FIXME: why mingw crash? unsigned api_ver = 0; CUDA_ENSURE(cuCtxGetApiVersion(cuctx, &api_ver), false); qDebug("cuCtxGetApiVersion: %u", api_ver); #endif CUDA_ENSURE(cuCtxPopCurrent(&cuctx), false); CUDA_ENSURE(cuvidCtxLockCreate(&vid_ctx_lock, cuctx), 0); { AutoCtxLock lock(this, vid_ctx_lock); Q_UNUSED(lock); //Flags- Parameters for stream creation (must be 0 (CU_STREAM_DEFAULT=0 in cuda5) in cuda 4.2, no CU_STREAM_NON_BLOCKING) CUDA_ENSURE(cuStreamCreate(&stream, CU_STREAM_DEFAULT), false); //require compute capability >= 1.1 //flag: Reserved for future use, must be 0 //cuStreamAddCallback(stream, CUstreamCallback, this, 0); } return true; } bool VideoDecoderCUDAPrivate::releaseCuda() { available = false; if (cuctx) CUDA_WARN(cuCtxPushCurrent(cuctx)); //cuMemFreeHost need the context of cuMemAllocHost which was called in VideoThread, while releaseCuda() in dtor can be called in any thread if (!can_load) return true; if (dec) { CUDA_WARN(cuvidDestroyDecoder(dec)); dec = 0; } if (parser) { CUDA_WARN(cuvidDestroyVideoParser(parser)); parser = 0; } if (stream) { CUDA_WARN(cuStreamDestroy(stream)); stream = 0; } if (host_data) { CUDA_WARN(cuMemFreeHost(host_data)); //CUDA_ERROR_INVALID_CONTEXT host_data = 0; host_data_size = 0; } if (vid_ctx_lock) { CUDA_WARN(cuvidCtxLockDestroy(vid_ctx_lock)); vid_ctx_lock = 0; } if (cuctx) { CUDA_ENSURE(cuCtxDestroy(cuctx), false); cuctx = 0; } return true; } bool VideoDecoderCUDAPrivate::createCUVIDDecoder(cudaVideoCodec cudaCodec, int cw, int ch) { if (cudaCodec == cudaVideoCodec_NumCodecs) { return false; } AutoCtxLock lock(this, vid_ctx_lock); Q_UNUSED(lock); if (dec) { CUDA_ENSURE(cuvidDestroyDecoder(dec), false); } memset(&dec_create_info, 0, sizeof(CUVIDDECODECREATEINFO)); dec_create_info.ulWidth = cw; // Coded Sequence Width dec_create_info.ulHeight = ch; dec_create_info.ulNumDecodeSurfaces = nb_dec_surface; //same as ulMaxNumDecodeSurfaces dec_create_info.CodecType = cudaCodec; dec_create_info.ChromaFormat = cudaVideoChromaFormat_420; // cudaVideoChromaFormat_XXX (only 4:2:0 is currently supported) //cudaVideoCreate_PreferCUVID is slow in example. DXVA may failed to create (CUDA_ERROR_NO_DEVICE) dec_create_info.ulCreationFlags = create_flags; // TODO: lav yv12 dec_create_info.OutputFormat = cudaVideoSurfaceFormat_NV12; // NV12 (currently the only supported output format) dec_create_info.DeinterlaceMode = deinterlace; // No scaling dec_create_info.ulTargetWidth = cw; dec_create_info.ulTargetHeight = ch; //TODO: dec_create_info.display_area. dec_create_info.ulNumOutputSurfaces = 2; // We won't simultaneously map more than 8 surfaces dec_create_info.vidLock = vid_ctx_lock;//vidCtxLock; //FIXME // Limit decode memory to 24MB (16M pixels at 4:2:0 = 24M bytes) // otherwise CUDA_ERROR_OUT_OF_MEMORY on cuMemcpyDtoH // if ulNumDecodeSurfaces < ulMaxNumDecodeSurfaces, CurrPicIdx may be > ulNumDecodeSurfaces /* * TODO: check video memory, e.g. runtime api extern __host__ cudaError_t CUDARTAPI cudaMemGetInfo(size_t *free, size_t *total); * 24MB is too small for 4k video, only n2 surfaces can be use so decoding will be too slow */ #if 0 while (dec_create_info.ulNumDecodeSurfaces * codec_ctx->coded_width * codec_ctx->coded_height > 16*1024*1024) { dec_create_info.ulNumDecodeSurfaces--; } #endif // create the decoder available = false; CUDA_ENSURE(cuvidCreateDecoder(&dec, &dec_create_info), false); available = true; return true; } bool VideoDecoderCUDAPrivate::createCUVIDParser() { cudaVideoCodec cudaCodec = mapCodecFromFFmpeg(codec_ctx->codec_id); if (cudaCodec == cudaVideoCodec_NumCodecs) { QString es(QObject::tr("Codec %1 is not supported by CUDA").arg(QLatin1String(avcodec_get_name(codec_ctx->codec_id)))); //emit error(AVError::CodecError, es); qWarning() << es; available = false; return false; } if (parser) { CUDA_WARN(cuvidDestroyVideoParser(parser)); parser = 0; } //lavfilter check level C CUVIDPARSERPARAMS parser_params; memset(&parser_params, 0, sizeof(CUVIDPARSERPARAMS)); parser_params.CodecType = cudaCodec; /*! * CUVIDPICPARAMS.CurrPicIdx <= kMaxDecodeSurfaces. * CUVIDPARSERDISPINFO.picture_index <= kMaxDecodeSurfaces * HandlePictureDecode must check whether CUVIDPICPARAMS.CurrPicIdx is in use * HandlePictureDisplay must mark CUVIDPARSERDISPINFO.picture_index is in use * If a frame is unmapped, mark the index not in use * */ parser_params.ulMaxNumDecodeSurfaces = nb_dec_surface; //parser_params.ulMaxDisplayDelay = 4; //? parser_params.pUserData = this; // Parser callbacks // The parser will call these synchronously from within cuvidParseVideoData(), whenever a picture is ready to // be decoded and/or displayed. parser_params.pfnSequenceCallback = VideoDecoderCUDAPrivate::HandleVideoSequence; parser_params.pfnDecodePicture = VideoDecoderCUDAPrivate::HandlePictureDecode; parser_params.pfnDisplayPicture = VideoDecoderCUDAPrivate::HandlePictureDisplay; parser_params.ulErrorThreshold = 0;//!wait for key frame //parser_params.pExtVideoInfo qDebug("~~~~~~~~~~~~~~~~extradata: %p %d", codec_ctx->extradata, codec_ctx->extradata_size); memset(&extra_parser_info, 0, sizeof(CUVIDEOFORMATEX)); // nalu extra_parser_info.format.seqhdr_data_length = 0; if (codec_ctx->codec_id != QTAV_CODEC_ID(H264) && codec_ctx->codec_id != QTAV_CODEC_ID(HEVC)) { if (codec_ctx->extradata_size > 0) { extra_parser_info.format.seqhdr_data_length = codec_ctx->extradata_size; memcpy(extra_parser_info.raw_seqhdr_data, codec_ctx->extradata, FFMIN(sizeof(extra_parser_info.raw_seqhdr_data), codec_ctx->extradata_size)); } } parser_params.pExtVideoInfo = &extra_parser_info; CUDA_ENSURE(cuvidCreateVideoParser(&parser, &parser_params), false); CUVIDSOURCEDATAPACKET seq_pkt; seq_pkt.payload = extra_parser_info.raw_seqhdr_data; seq_pkt.payload_size = extra_parser_info.format.seqhdr_data_length; if (seq_pkt.payload_size > 0) { CUDA_ENSURE(cuvidParseVideoData(parser, &seq_pkt), false); } //lavfilter: cuStreamCreate force_sequence_update = true; //DecodeSequenceData() return true; } bool VideoDecoderCUDAPrivate::flushParser() { CUVIDSOURCEDATAPACKET flush_packet; memset(&flush_packet, 0, sizeof(CUVIDSOURCEDATAPACKET)); flush_packet.flags |= CUVID_PKT_ENDOFSTREAM; return doParseVideoData(&flush_packet); } bool VideoDecoderCUDAPrivate::processDecodedData(CUVIDPARSERDISPINFO *cuviddisp, VideoFrame* outFrame) { int num_fields = cuviddisp->progressive_frame ? 1 : 2+cuviddisp->repeat_first_field; for (int active_field = 0; active_field < num_fields; ++active_field) { CUVIDPROCPARAMS proc_params; memset(&proc_params, 0, sizeof(CUVIDPROCPARAMS)); proc_params.progressive_frame = cuviddisp->progressive_frame; //check user config proc_params.second_field = active_field == 1; //check user config proc_params.top_field_first = cuviddisp->top_field_first; proc_params.unpaired_field = cuviddisp->progressive_frame == 1; //const uint cw = dec_create_info.ulWidth;//PAD_ALIGN(dec_create_info.ulWidth, 0x3F); const uint ch = dec_create_info.ulHeight;//PAD_ALIGN(dec_create_info.ulHeight, 0x0F); //? CUdeviceptr devptr; unsigned int pitch; { AutoCtxLock lock(this, vid_ctx_lock); Q_UNUSED(lock); //CUDA_ENSURE(cuCtxPushCurrent(cuctx), false); CUDA_ENSURE(cuvidMapVideoFrame(dec, cuviddisp->picture_index, &devptr, &pitch, &proc_params), false); CUVIDAutoUnmapper unmapper(this, dec, devptr); Q_UNUSED(unmapper); if (copy_mode != VideoDecoderCUDA::ZeroCopy) { const int size = pitch*ch*3/2; if (size > host_data_size && host_data) { cuMemFreeHost(host_data); host_data = 0; host_data_size = 0; } if (!host_data) { CUDA_ENSURE(cuMemAllocHost((void**)&host_data, size), false); host_data_size = size; } // copy to the memory not allocated by cuda is possible but much slower // TODO: cuMemcpy2D? CUDA_ENSURE(cuMemcpyDtoHAsync(host_data, devptr, size, stream), false); CUDA_WARN(cuStreamSynchronize(stream)); } } // lock end //CUDA_ENSURE(cuCtxPopCurrent(&cuctx), false); //qDebug("cuCtxPopCurrent %p", cuctx); VideoFrame frame; if (copy_mode != VideoDecoderCUDA::GenericCopy && interop_res) { if (OpenGLHelper::isOpenGLES() && copy_mode == VideoDecoderCUDA::ZeroCopy) { proc_params.Reserved[0] = pitch; // TODO: pass pitch to setSurface() frame = VideoFrame(codec_ctx->width, codec_ctx->height, VideoFormat::Format_RGB32); frame.setBytesPerLine(codec_ctx->width * 4); //used by gl to compute texture size } else { frame = VideoFrame(codec_ctx->width, codec_ctx->height, VideoFormat::Format_NV12); } cuda::SurfaceInteropCUDA *interop = new cuda::SurfaceInteropCUDA(interop_res); interop->setSurface(cuviddisp->picture_index, proc_params, codec_ctx->width, codec_ctx->height, ch); //TODO: both surface size(for copy 2d) and frame size(for map host) frame.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop))); } else { uchar *planes[] = { host_data, host_data + pitch * ch }; frame = VideoFrame(codec_ctx->width, codec_ctx->height, VideoFormat::Format_NV12); frame.setBits(planes); } int pitches[] = { (int)pitch, (int)pitch }; if (!frame.format().isRGB()) { frame.setBytesPerLine(pitches); frame.setColorRange(yuv_range); } frame.setTimestamp((double)cuviddisp->timestamp/1000.0); if (codec_ctx && codec_ctx->sample_aspect_ratio.num > 1) //skip 1/1 because is the default value frame.setDisplayAspectRatio(frame.displayAspectRatio()*av_q2d(codec_ctx->sample_aspect_ratio)); if (copy_mode == VideoDecoderCUDA::GenericCopy) frame = frame.clone(); if (outFrame) { *outFrame = frame; } #if COPY_ON_DECODE frame_queue.put(frame); #endif //qDebug("frame queue size: %d", frame_queue.size()); surface_in_use[cuviddisp->picture_index] = false; // FIXME: 0-copy still use the index } return true; } void VideoDecoderCUDAPrivate::setBSF(AVCodecID codec) { if (codec == QTAV_CODEC_ID(H264)) { if (!bsf) bsf = av_bitstream_filter_init("h264_mp4toannexb"); Q_ASSERT(bsf && "h264_mp4toannexb bsf not found"); } else if (codec == QTAV_CODEC_ID(HEVC)) { if (!bsf) bsf = av_bitstream_filter_init("hevc_mp4toannexb"); Q_ASSERT(bsf && "hevc_mp4toannexb bsf not found"); } else { if (bsf) { av_bitstream_filter_close(bsf); bsf = 0; } } } } //namespace QtAV #include "VideoDecoderCUDA.moc" QtAV-1.12.0/src/codec/video/VideoDecoderCedarv.cpp000066400000000000000000000432561312235004300215500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin Miroslav Bendik * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoDecoder.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/Packet.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include extern "C" { #include } #include "utils/Logger.h" // TODO: neon+nv12+opengl crash #ifndef NO_NEON_OPT //Don't HAVE_NEON extern "C" { // from libvdpau-sunxi void tiled_to_planar(void *src, void *dst, unsigned int dst_pitch, unsigned int width, unsigned int height); void tiled_deinterleave_to_planar(void *src, void *dst1, void *dst2, unsigned int dst_pitch, unsigned int width, unsigned int height); } #endif //NO_NEON_OPT namespace QtAV { class VideoDecoderCedarvPrivate; class VideoDecoderCedarv : public VideoDecoder { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderCedarv) #ifndef NO_NEON_OPT //Don't HAVE_NEON Q_PROPERTY(bool neon READ neon WRITE setNeon NOTIFY neonChanged) #endif Q_PROPERTY(PixFmt outputPixelFormat READ outputPixelFormat WRITE setOutputPixelFormat NOTIFY outputPixelFormatChanged) Q_ENUMS(PixFmt) public: enum PixFmt { YUV420p, NV12 }; VideoDecoderCedarv(); VideoDecoderId id() const Q_DECL_OVERRIDE Q_DECL_FINAL; QString description() const Q_DECL_OVERRIDE Q_DECL_FINAL{ return QStringLiteral("Allwinner A10 CedarX video hardware acceleration"); } bool decode(const Packet& packet) Q_DECL_OVERRIDE Q_DECL_FINAL; VideoFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL; //properties void setNeon(bool value); bool neon() const; void setOutputPixelFormat(PixFmt value); PixFmt outputPixelFormat() const; Q_SIGNALS: void neonChanged(); void outputPixelFormatChanged(); }; extern VideoDecoderId VideoDecoderId_Cedarv; FACTORY_REGISTER(VideoDecoder, Cedarv, "Cedarv") // source data is colum major. every block is 32x32 static void map32x32_to_yuv_Y(void* srcY, void* tarY, unsigned int dst_pitch, unsigned int coded_width, unsigned int coded_height) { unsigned long offset; unsigned char *ptr = (unsigned char *)srcY; const unsigned int mb_width = (coded_width+15) >> 4; const unsigned int mb_height = (coded_height+15) >> 4; const unsigned int twomb_line = (mb_height+1) >> 1; const unsigned int recon_width = (mb_width+1) & 0xfffffffe; for (unsigned int i = 0; i < twomb_line; i++) { const unsigned int M = 32*i; for (unsigned int j = 0; j < recon_width; j+=2) { const unsigned int n = j*16; offset = M*dst_pitch + n; for (unsigned int l = 0; l < 32; l++) { if (M+l < coded_height) { if (n+16 < coded_width) { //1st & 2nd mb memcpy((unsigned char *)tarY+offset, ptr, 32); } else if (n> 3; const unsigned int mb_height = (coded_height+7) >> 3; const unsigned int fourmb_line = (mb_height+3) >> 2; const unsigned int recon_width = (mb_width+1) & 0xfffffffe; for (unsigned int i = 0; i < fourmb_line; i++) { const int M = i*32; for (unsigned int j = 0; j < recon_width; j+=2) { const unsigned int n = j*8; offset = M*dst_pitch + n; for (unsigned int l = 0; l < 32; l++) { if (M+l < coded_height) { if (n+8 < coded_width) { // 1st & 2nd mb memcpy(line, ptr, 32); //unsigned char *line = ptr; for (int k = 0; k < 16; k++) { *((unsigned char *)tarCb + offset + k) = line[2*k]; *((unsigned char *)tarCr + offset + k) = line[2*k+1]; } } else if (n < coded_width) { // 1st mb memcpy(line, ptr, 16); //unsigned char *line = ptr; for (int k = 0; k < 8; k++) { *((unsigned char *)tarCb + offset + k) = line[2*k]; *((unsigned char *)tarCr + offset + k) = line[2*k+1]; } } offset += dst_pitch; } ptr += 32; } } } } #if 0 static void map32x32_to_nv12_UV(void* srcC, void* tarUV, unsigned int dst_pitch, unsigned int coded_width, unsigned int coded_height) { coded_width /= 2; // libvdpau-sunxi compatible unsigned long offset; unsigned char *ptr = (unsigned char *)srcC; const unsigned int mb_width = (coded_width+7) >> 3; const unsigned int mb_height = (coded_height+7) >> 3; const unsigned int fourmb_line = (mb_height+3) >> 2; const unsigned int recon_width = (mb_width+1) & 0xfffffffe; for (unsigned int i = 0; i < fourmb_line; i++) { const int M = i*32; for (unsigned int j = 0; j < recon_width; j+=2) { const unsigned int n = j*16; // 1 logical pixel 2 channel offset = M*dst_pitch + n; for (unsigned int l = 0; l < 32; l++) { // copy 16 logical pixels every time. 16/2*recon_width ~ coded_width if (M+l < coded_height) { if (n+8 < coded_width) { // 1st & 2nd mb memcpy((unsigned char *)tarUV + offset, ptr, 32); } else if (n < coded_width) { // 1st mb memcpy((unsigned char *)tarUV + offset, ptr, 16); } offset += dst_pitch; } ptr += 32; } } } } #endif typedef struct { enum AVCodecID id; cedarv_stream_format_e format; cedarv_sub_format_e sub_format; } avcodec_cedarv; static const avcodec_cedarv avcodec_cedarv_map[] = { { QTAV_CODEC_ID(MPEG1VIDEO), CEDARV_STREAM_FORMAT_MPEG2, CEDARV_MPEG2_SUB_FORMAT_MPEG1 }, { QTAV_CODEC_ID(MPEG2VIDEO), CEDARV_STREAM_FORMAT_MPEG2, CEDARV_MPEG2_SUB_FORMAT_MPEG2 }, { QTAV_CODEC_ID(H263), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_H263 }, { QTAV_CODEC_ID(H264), CEDARV_STREAM_FORMAT_H264, CEDARV_SUB_FORMAT_UNKNOW }, //CEDARV_CONTAINER_FORMAT_TS { QTAV_CODEC_ID(VP6F), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_VP6 }, { QTAV_CODEC_ID(WMV1), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_WMV1 }, { QTAV_CODEC_ID(WMV2), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_WMV2 }, { QTAV_CODEC_ID(WMV3), CEDARV_STREAM_FORMAT_VC1, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(VC1), CEDARV_STREAM_FORMAT_VC1, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(MJPEG), CEDARV_STREAM_FORMAT_MJPEG, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(VP8), CEDARV_STREAM_FORMAT_VP8, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(MSMPEG4V1), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_DIVX1 }, { QTAV_CODEC_ID(MSMPEG4V2), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_DIVX2 }, { QTAV_CODEC_ID(MSMPEG4V3), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_DIVX3 }, { QTAV_CODEC_ID(FLV1), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_SORENSSON_H263 }, { QTAV_CODEC_ID(MPEG4), CEDARV_STREAM_FORMAT_MPEG4, CEDARV_MPEG4_SUB_FORMAT_XVID }, { QTAV_CODEC_ID(RV10), CEDARV_STREAM_FORMAT_REALVIDEO, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(RV20), CEDARV_STREAM_FORMAT_REALVIDEO, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(RV30), CEDARV_STREAM_FORMAT_REALVIDEO, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(RV40), CEDARV_STREAM_FORMAT_REALVIDEO, CEDARV_SUB_FORMAT_UNKNOW }, { QTAV_CODEC_ID(NONE), CEDARV_STREAM_FORMAT_UNKNOW, CEDARV_SUB_FORMAT_UNKNOW } }; typedef struct { enum AVPixelFormat fpf; cedarv_pixel_format_e cpf; } ff_cedarv_pixfmt; static const ff_cedarv_pixfmt pixel_format_map[] = { { QTAV_PIX_FMT_C(RGB565), CEDARV_PIXEL_FORMAT_RGB565 }, { QTAV_PIX_FMT_C(RGB24), CEDARV_PIXEL_FORMAT_RGB888 }, { QTAV_PIX_FMT_C(RGB32), CEDARV_PIXEL_FORMAT_ARGB8888 }, { QTAV_PIX_FMT_C(YUV444P), CEDARV_PIXEL_FORMAT_YUV444 }, { QTAV_PIX_FMT_C(YUV422P), CEDARV_PIXEL_FORMAT_YUV422 }, { QTAV_PIX_FMT_C(YUV420P), CEDARV_PIXEL_FORMAT_YUV420 }, { QTAV_PIX_FMT_C(YUV411P), CEDARV_PIXEL_FORMAT_YUV411 }, //CEDARV_PIXEL_FORMAT_CSIRGB = 0xf, { QTAV_PIX_FMT_C(NV12), CEDARV_PIXEL_FORMAT_AW_YUV420 }, //{ QTAV_PIX_FMT_C(YUV422P), CEDARV_PIXEL_FORMAT_AW_YUV422 }, //{ QTAV_PIX_FMT_C(YUV411P), CEDARV_PIXEL_FORMAT_AW_YUV411 }, }; void format_from_avcodec(enum AVCodecID id, cedarv_stream_format_e* format, cedarv_sub_format_e *sub_format) { size_t i = 0; while (avcodec_cedarv_map[i].id != QTAV_CODEC_ID(NONE)) { if (avcodec_cedarv_map[i].id == id) { *format = avcodec_cedarv_map[i].format; *sub_format = avcodec_cedarv_map[i].sub_format; return; } ++i; } *format = CEDARV_STREAM_FORMAT_UNKNOW; *sub_format = CEDARV_SUB_FORMAT_UNKNOW; } enum AVPixelFormat pixel_format_from_cedarv(cedarv_pixel_format_e cpf) { size_t i = 0; while (i < sizeof(pixel_format_map)/sizeof(pixel_format_map[0])) { if (pixel_format_map[i].cpf == cpf) return pixel_format_map[i].fpf; ++i; } return QTAV_PIX_FMT_C(NONE); } class VideoDecoderCedarvPrivate Q_DECL_FINAL: public VideoDecoderPrivate { public: VideoDecoderCedarvPrivate() : VideoDecoderPrivate() , cedarv(0) , map_y(map32x32_to_yuv_Y) , map_c(map32x32_to_yuv_C) , pixfmt(VideoDecoderCedarv::NV12) {} ~VideoDecoderCedarvPrivate() { if (!cedarv) return; cedarv->ioctrl(cedarv, CEDARV_COMMAND_STOP, 0); // FIXME: why crash? //cedarv->close(cedarv); //libcedarv_exit(cedarv); cedarv = NULL; } bool open() Q_DECL_OVERRIDE; CEDARV_DECODER *cedarv; cedarv_picture_t cedarPicture; typedef void (*map_y_t)(void* src, void* dst, unsigned int dst_pitch, unsigned int w, unsigned int h); map_y_t map_y; typedef void (*map_c_t)(void* src, void* dst1, void* dst2, unsigned int dst_pitch, unsigned int w, unsigned int h); map_c_t map_c; VideoDecoderCedarv::PixFmt pixfmt; }; VideoDecoderCedarv::VideoDecoderCedarv() : VideoDecoder(*new VideoDecoderCedarvPrivate()) { } VideoDecoderId VideoDecoderCedarv::id() const { return VideoDecoderId_Cedarv; } void VideoDecoderCedarv::setNeon(bool value) { if (value == neon()) return; DPTR_D(VideoDecoderCedarv); if (value) { #ifndef NO_NEON_OPT //Don't HAVE_NEON d.map_y = tiled_to_planar; d.map_c = tiled_deinterleave_to_planar; #endif } else { d.map_y = map32x32_to_yuv_Y; d.map_c = map32x32_to_yuv_C; } emit neonChanged(); } void VideoDecoderCedarv::setOutputPixelFormat(PixFmt value) { if (outputPixelFormat() == value) return; d_func().pixfmt = value; emit outputPixelFormatChanged(); } VideoDecoderCedarv::PixFmt VideoDecoderCedarv::outputPixelFormat() const { return d_func().pixfmt; } bool VideoDecoderCedarv::neon() const { return d_func().map_y != map32x32_to_yuv_Y; } bool VideoDecoderCedarvPrivate::open() { cedarv_stream_format_e format; cedarv_sub_format_e sub_format; /* format check */ format_from_avcodec(codec_ctx->codec_id, &format, &sub_format); if (format == CEDARV_STREAM_FORMAT_UNKNOW) { qWarning("CedarV: codec not supported '%s'", avcodec_get_name(codec_ctx->codec_id)); return false; } if (!cedarv) { int ret; cedarv = libcedarv_init(&ret); if (ret < 0 || cedarv == NULL) return false; } cedarv_stream_info_t cedarStreamInfo; memset(&cedarStreamInfo, 0, sizeof cedarStreamInfo); cedarStreamInfo.format = format; cedarStreamInfo.sub_format = sub_format; cedarStreamInfo.video_width = codec_ctx->coded_width; //coded_width? cedarStreamInfo.video_height = codec_ctx->coded_height; if (codec_ctx->extradata_size) { cedarStreamInfo.init_data = codec_ctx->extradata; cedarStreamInfo.init_data_len = codec_ctx->extradata_size; } int cedarvRet; cedarvRet = cedarv->set_vstream_info(cedarv, &cedarStreamInfo); if (cedarvRet < 0) { qWarning("CedarV: set_vstream_info error: %d", cedarvRet); return false; } cedarvRet = cedarv->open(cedarv); if (cedarvRet < 0) { qWarning("CedarV: set_vstream_info error: %d", cedarvRet); return false; } cedarv->ioctrl(cedarv, CEDARV_COMMAND_RESET, 0); cedarv->ioctrl(cedarv, CEDARV_COMMAND_PLAY, 0); return true; } bool VideoDecoderCedarv::decode(const Packet &packet) { DPTR_D(VideoDecoderCedarv); if (packet.data.isEmpty()) return true; //d.cedarv->ioctrl(d.cedarv, CEDARV_COMMAND_JUMP, 0); u32 bufsize0, bufsize1; u8 *buf0, *buf1; int ret = d.cedarv->request_write(d.cedarv, packet.data.size(), &buf0, &bufsize0, &buf1, &bufsize1); if (ret < 0) { qWarning("CedarV: request_write failed (%d)", ret); return false; } memcpy(buf0, packet.data.constData(), bufsize0); if ((u32)packet.data.size() > bufsize0) memcpy(buf1, packet.data.constData() + bufsize0, bufsize1); cedarv_stream_data_info_t stream_data_info; stream_data_info.type = 0; // TODO stream_data_info.lengh = packet.data.size(); stream_data_info.pts = packet.pts * 1000.0; stream_data_info.flags = CEDARV_FLAG_FIRST_PART | CEDARV_FLAG_LAST_PART | CEDARV_FLAG_PTS_VALID; if ((ret = d.cedarv->update_data(d.cedarv, &stream_data_info)) < 0) { qWarning("CedarV: update_data failed (%d)", ret); return false; } if ((ret = d.cedarv->decode(d.cedarv)) < 0) { qWarning("CedarV: decode failed (%d)", ret); return false; } ret = d.cedarv->display_request(d.cedarv, &d.cedarPicture); if (ret > 3 || ret < 0) { qWarning("CedarV: display_request failed (%d)", ret); if (d.cedarPicture.id) { d.cedarv->display_release(d.cedarv, d.cedarPicture.id); d.cedarPicture.id = 0; } return false; } return true; } VideoFrame VideoDecoderCedarv::frame() { DPTR_D(VideoDecoderCedarv); if (!d.cedarPicture.id) return VideoFrame(); d.cedarPicture.display_height = FFALIGN(d.cedarPicture.display_height, 8); const int display_h_align = FFALIGN(d.cedarPicture.display_height, 2); // already aligned to 8! const int display_w_align = FFALIGN(d.cedarPicture.display_width, 16); const int dst_y_stride = display_w_align; const int dst_y_size = dst_y_stride * display_h_align; const int dst_c_stride = FFALIGN(d.cedarPicture.display_width/2, 16); const int dst_c_size = dst_c_stride * (display_h_align/2); const int alloc_size = dst_y_size + dst_c_size * 2; const unsigned int offset_y = 0; const unsigned int offset_u = offset_y + dst_y_size; const unsigned int offset_v = offset_u + dst_c_size; const bool nv12 = outputPixelFormat() == NV12; QByteArray buf(alloc_size, 0); buf.reserve(alloc_size); quint8 *dst = reinterpret_cast(buf.data()); quint8 *plane[] = { dst + offset_y, dst + offset_u, dst + offset_v }; int pitch[] = { dst_y_stride, dst_c_stride, dst_c_stride }; if (nv12) pitch[1] = dst_y_stride; d.map_y(d.cedarPicture.y, plane[0], display_w_align, pitch[0], display_h_align); if (nv12) d.map_y(d.cedarPicture.u, plane[1], pitch[1], display_w_align, display_h_align/2); else d.map_c(d.cedarPicture.u, plane[1], plane[2], pitch[1], display_w_align, display_h_align/2); //vdpau use w, h/2 const VideoFormat fmt(nv12 ? VideoFormat::Format_NV12 : VideoFormat::Format_YUV420P); VideoFrame frame(display_w_align, display_h_align, fmt, buf); frame.setBits(plane); frame.setBytesPerLine(pitch); frame.setTimestamp(qreal(d.cedarPicture.pts)/1000.0); d.cedarv->display_release(d.cedarv, d.cedarPicture.id); d.cedarPicture.id = 0; return frame; } } // namespace QtAV #include "VideoDecoderCedarv.moc" QtAV-1.12.0/src/codec/video/VideoDecoderD3D.cpp000066400000000000000000000671051312235004300207150ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderD3D.h" #include /* must be last included to not redefine existing GUIDs */ #if (FF_PROFILE_HEVC_MAIN == -1) //libav does not define it #ifdef _MSC_VER #pragma message("HEVC will not be supported. Update your FFmpeg") #else #warning "HEVC will not be supported. Update your FFmpeg" #endif #endif namespace QtAV { static bool check_ffmpeg_hevc_dxva2() { avcodec_register_all(); AVHWAccel *hwa = av_hwaccel_next(0); while (hwa) { if (strncmp("hevc_dxva2", hwa->name, 10) == 0) return true; if (strncmp("hevc_d3d11va", hwa->name, 12) == 0) return true; hwa = av_hwaccel_next(hwa); } return false; } bool isHEVCSupported() { static const bool support_hevc = check_ffmpeg_hevc_dxva2(); return support_hevc; } // some MS_GUID are defined in mingw but some are not. move to namespace and define all is ok #define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID name = { l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} MS_GUID (DXVA_NoEncrypt, 0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); /* Codec capabilities GUID, sorted by codec */ MS_GUID (DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b, 0x61b0, 0x4563, 0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66); MS_GUID (DXVA2_ModeMPEG2_IDCT, 0xbf22ad00, 0x03ea, 0x4690, 0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e); MS_GUID (DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9); DEFINE_GUID(DXVA_ModeMPEG1_A, 0x1b81be09, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeMPEG2_A, 0x1b81be0A, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeMPEG2_B, 0x1b81be0B, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeMPEG2_C, 0x1b81be0C, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeMPEG2_D, 0x1b81be0D, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60); DEFINE_GUID(DXVA2_ModeMPEG1_VLD, 0x6f3ec719, 0x3735, 0x42cc, 0x80, 0x63, 0x65, 0xcc, 0x3c, 0xb3, 0x66, 0x16); MS_GUID (DXVA2_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeH264_B, 0x1b81be65, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeH264_C, 0x1b81be66, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeH264_D, 0x1b81be67, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH264_VLD_Multiview, 0x9901CCD3, 0xca12, 0x4b7e, 0x86, 0x7a, 0xe2, 0x22, 0x3d, 0x92, 0x55, 0xc3); // MVC DEFINE_GUID(DXVA_ModeH264_VLD_WithFMOASO_NoFGT, 0xd5f04ff9, 0x3418, 0x45d8, 0x95, 0x61, 0x32, 0xa7, 0x6a, 0xae, 0x2d, 0xdd); DEFINE_GUID(DXVADDI_Intel_ModeH264_A, 0x604F8E64, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); DEFINE_GUID(DXVADDI_Intel_ModeH264_C, 0x604F8E66, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); DEFINE_GUID(DXVA_Intel_H264_NoFGT_ClearVideo, 0x604F8E68, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); DEFINE_GUID(DXVA_ModeH264_VLD_NoFGT_Flash, 0x4245F676, 0x2BBC, 0x4166, 0xa0, 0xBB, 0x54, 0xE7, 0xB8, 0x49, 0xC3, 0x80); MS_GUID (DXVA2_ModeWMV8_A, 0x1b81be80, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeWMV8_B, 0x1b81be81, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeWMV9_A, 0x1b81be90, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeWMV9_B, 0x1b81be91, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeWMV9_C, 0x1b81be94, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeVC1_A, 0x1b81beA0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeVC1_B, 0x1b81beA1, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeVC1_C, 0x1b81beA2, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); MS_GUID (DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); // August 2010 update DEFINE_GUID(DXVA_Intel_VC1_ClearVideo, 0xBCC5DB6D, 0xA2B6, 0x4AF0, 0xAC, 0xE4, 0xAD, 0xB1, 0xF7, 0x87, 0xBC, 0x89); DEFINE_GUID(DXVA_Intel_VC1_ClearVideo_2, 0xE07EC519, 0xE651, 0x4CD6, 0xAC, 0x84, 0x13, 0x70, 0xCC, 0xEE, 0xC8, 0x51); DEFINE_GUID(DXVA_nVidia_MPEG4_ASP, 0x9947EC6F, 0x689B, 0x11DC, 0xA3, 0x20, 0x00, 0x19, 0xDB, 0xBC, 0x41, 0x84); DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_Simple, 0xefd64d74, 0xc9e8, 0x41d7, 0xa5, 0xe9, 0xe9, 0xb0, 0xe3, 0x9f, 0xa3, 0x19); DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0xed418a9f, 0x010d, 0x4eda, 0x9a, 0xe3, 0x9a, 0x65, 0x35, 0x8d, 0x8d, 0x2e); DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0xab998b5b, 0x4258, 0x44a9, 0x9f, 0xeb, 0x94, 0xe5, 0x97, 0xa6, 0xba, 0xae); DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0x7C74ADC6, 0xe2ba, 0x4ade, 0x86, 0xde, 0x30, 0xbe, 0xab, 0xb4, 0x0c, 0xc1); DEFINE_GUID(DXVA_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c, 0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0); DEFINE_GUID(DXVA_ModeHEVC_VLD_Main10, 0x107af0e0, 0xef1a, 0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13); DEFINE_GUID(DXVA_ModeH264_VLD_Stereo_Progressive_NoFGT, 0xd79be8da, 0x0cf1, 0x4c81,0xb8,0x2a,0x69,0xa4,0xe2,0x36,0xf4,0x3d); DEFINE_GUID(DXVA_ModeH264_VLD_Stereo_NoFGT, 0xf9aaccbb, 0xc2b6, 0x4cfc,0x87,0x79,0x57,0x07,0xb1,0x76,0x05,0x52); DEFINE_GUID(DXVA_ModeH264_VLD_Multiview_NoFGT, 0x705b9d82, 0x76cf, 0x49d6,0xb7,0xe6,0xac,0x88,0x72,0xdb,0x01,0x3c); DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Scalable_Baseline, 0xc30700c4, 0xe384, 0x43e0, 0xb9, 0x82, 0x2d, 0x89, 0xee, 0x7f, 0x77, 0xc4); DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Restricted_Scalable_Baseline, 0x9b8175d4, 0xd670, 0x4cf2, 0xa9, 0xf0, 0xfa, 0x56, 0xdf, 0x71, 0xa1, 0xae); DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Scalable_High, 0x728012c9, 0x66a8, 0x422f, 0x97, 0xe9, 0xb5, 0xe3, 0x9b, 0x51, 0xc0, 0x53); DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Restricted_Scalable_High_Progressive, 0x8efa5926, 0xbd9e, 0x4b04, 0x8b, 0x72, 0x8f, 0x97, 0x7d, 0xc4, 0x4c, 0x36); DEFINE_GUID(DXVA_ModeH261_A, 0x1b81be01, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH261_B, 0x1b81be02, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_A, 0x1b81be03, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_B, 0x1b81be04, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_C, 0x1b81be05, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_D, 0x1b81be06, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_E, 0x1b81be07, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA_ModeH263_F, 0x1b81be08, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); static const int PROF_MPEG2_SIMPLE[] = { FF_PROFILE_MPEG2_SIMPLE, 0 }; static const int PROF_MPEG2_MAIN[] = { FF_PROFILE_MPEG2_SIMPLE, FF_PROFILE_MPEG2_MAIN, 0 }; static const int PROF_H264_HIGH[] = { FF_PROFILE_H264_CONSTRAINED_BASELINE, FF_PROFILE_H264_MAIN, FF_PROFILE_H264_HIGH, 0 }; static const int PROF_HEVC_MAIN[] = { FF_PROFILE_HEVC_MAIN, 0 }; static const int PROF_HEVC_MAIN10[] = { FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10, 0 }; // guids are from VLC struct dxva2_mode_t { const char *name; const GUID *guid; int codec; const int *profiles; }; /* XXX Prefered modes must come first */ static const dxva2_mode_t dxva2_modes[] = { /* MPEG-1/2 */ { "MPEG-1 decoder, restricted profile A", &DXVA_ModeMPEG1_A, 0, NULL }, { "MPEG-2 decoder, restricted profile A", &DXVA_ModeMPEG2_A, 0, NULL }, { "MPEG-2 decoder, restricted profile B", &DXVA_ModeMPEG2_B, 0, NULL }, { "MPEG-2 decoder, restricted profile C", &DXVA_ModeMPEG2_C, 0, NULL }, { "MPEG-2 decoder, restricted profile D", &DXVA_ModeMPEG2_D, 0, NULL }, { "MPEG-2 variable-length decoder", &DXVA2_ModeMPEG2_VLD, QTAV_CODEC_ID(MPEG2VIDEO), PROF_MPEG2_SIMPLE }, { "MPEG-2 & MPEG-1 variable-length decoder", &DXVA2_ModeMPEG2and1_VLD, QTAV_CODEC_ID(MPEG2VIDEO), PROF_MPEG2_MAIN }, { "MPEG-2 & MPEG-1 variable-length decoder", &DXVA2_ModeMPEG2and1_VLD, QTAV_CODEC_ID(MPEG1VIDEO), NULL }, { "MPEG-2 motion compensation", &DXVA2_ModeMPEG2_MoComp, 0, NULL }, { "MPEG-2 inverse discrete cosine transform", &DXVA2_ModeMPEG2_IDCT, 0, NULL }, /* MPEG-1 http://download.microsoft.com/download/B/1/7/B172A3C8-56F2-4210-80F1-A97BEA9182ED/DXVA_MPEG1_VLD.pdf */ { "MPEG-1 variable-length decoder, no D pictures", &DXVA2_ModeMPEG1_VLD, 0, NULL }, /* H.264 http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=3d1c290b-310b-4ea2-bf76-714063a6d7a6 */ { "H.264 variable-length decoder, film grain technology", &DXVA2_ModeH264_F, QTAV_CODEC_ID(H264), PROF_H264_HIGH }, { "H.264 variable-length decoder, no film grain technology (Intel ClearVideo)", &DXVA_Intel_H264_NoFGT_ClearVideo, QTAV_CODEC_ID(H264), PROF_H264_HIGH }, { "H.264 variable-length decoder, no film grain technology", &DXVA2_ModeH264_E, QTAV_CODEC_ID(H264), PROF_H264_HIGH }, { "H.264 variable-length decoder, no film grain technology, FMO/ASO", &DXVA_ModeH264_VLD_WithFMOASO_NoFGT, QTAV_CODEC_ID(H264), PROF_H264_HIGH }, { "H.264 variable-length decoder, no film grain technology, Flash", &DXVA_ModeH264_VLD_NoFGT_Flash, QTAV_CODEC_ID(H264), PROF_H264_HIGH }, { "H.264 inverse discrete cosine transform, film grain technology", &DXVA2_ModeH264_D, 0, NULL }, { "H.264 inverse discrete cosine transform, no film grain technology", &DXVA2_ModeH264_C, 0, NULL }, { "H.264 inverse discrete cosine transform, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_C, 0, NULL }, { "H.264 motion compensation, film grain technology", &DXVA2_ModeH264_B, 0, NULL }, { "H.264 motion compensation, no film grain technology", &DXVA2_ModeH264_A, 0, NULL }, { "H.264 motion compensation, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_A, 0, NULL }, /* http://download.microsoft.com/download/2/D/0/2D02E72E-7890-430F-BA91-4A363F72F8C8/DXVA_H264_MVC.pdf */ { "H.264 stereo high profile, mbs flag set", &DXVA_ModeH264_VLD_Stereo_Progressive_NoFGT, 0, NULL }, { "H.264 stereo high profile", &DXVA_ModeH264_VLD_Stereo_NoFGT, 0, NULL }, { "H.264 multiview high profile", &DXVA_ModeH264_VLD_Multiview_NoFGT, 0, NULL }, /* SVC http://download.microsoft.com/download/C/8/A/C8AD9F1B-57D1-4C10-85A0-09E3EAC50322/DXVA_SVC_2012_06.pdf */ { "H.264 scalable video coding, Scalable Baseline Profile", &DXVA_ModeH264_VLD_SVC_Scalable_Baseline, 0, NULL }, { "H.264 scalable video coding, Scalable Constrained Baseline Profile", &DXVA_ModeH264_VLD_SVC_Restricted_Scalable_Baseline, 0, NULL }, { "H.264 scalable video coding, Scalable High Profile", &DXVA_ModeH264_VLD_SVC_Scalable_High, 0, NULL }, { "H.264 scalable video coding, Scalable Constrained High Profile", &DXVA_ModeH264_VLD_SVC_Restricted_Scalable_High_Progressive, 0, NULL }, /* WMV */ { "Windows Media Video 8 motion compensation", &DXVA2_ModeWMV8_B, 0, NULL }, { "Windows Media Video 8 post processing", &DXVA2_ModeWMV8_A, 0, NULL }, { "Windows Media Video 9 IDCT", &DXVA2_ModeWMV9_C, 0, NULL }, { "Windows Media Video 9 motion compensation", &DXVA2_ModeWMV9_B, 0, NULL }, { "Windows Media Video 9 post processing", &DXVA2_ModeWMV9_A, 0, NULL }, /* VC-1 */ { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, QTAV_CODEC_ID(VC1), NULL }, { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, QTAV_CODEC_ID(WMV3), NULL }, { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, QTAV_CODEC_ID(VC1), NULL }, { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, QTAV_CODEC_ID(WMV3), NULL }, { "VC-1 variable-length decoder 2 (Intel)", &DXVA_Intel_VC1_ClearVideo_2, 0, NULL }, { "VC-1 variable-length decoder (Intel)", &DXVA_Intel_VC1_ClearVideo, 0, NULL }, { "VC-1 inverse discrete cosine transform", &DXVA2_ModeVC1_C, 0, NULL }, { "VC-1 motion compensation", &DXVA2_ModeVC1_B, 0, NULL }, { "VC-1 post processing", &DXVA2_ModeVC1_A, 0, NULL }, /* Xvid/Divx: TODO */ { "MPEG-4 Part 2 nVidia bitstream decoder", &DXVA_nVidia_MPEG4_ASP, 0, NULL }, { "MPEG-4 Part 2 variable-length decoder, Simple Profile", &DXVA_ModeMPEG4pt2_VLD_Simple, 0, NULL }, { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, no GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0, NULL }, { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0, NULL }, { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, Avivo", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0, NULL }, /* HEVC */ { "HEVC Main profile", &DXVA_ModeHEVC_VLD_Main, QTAV_CODEC_ID(HEVC), PROF_HEVC_MAIN }, { "HEVC Main 10 profile", &DXVA_ModeHEVC_VLD_Main10, QTAV_CODEC_ID(HEVC), PROF_HEVC_MAIN10 }, /* H.261 */ { "H.261 decoder, restricted profile A", &DXVA_ModeH261_A, 0, NULL }, { "H.261 decoder, restricted profile B", &DXVA_ModeH261_B, 0, NULL }, /* H.263 */ { "H.263 decoder, restricted profile A", &DXVA_ModeH263_A, 0, NULL }, { "H.263 decoder, restricted profile B", &DXVA_ModeH263_B, 0, NULL }, { "H.263 decoder, restricted profile C", &DXVA_ModeH263_C, 0, NULL }, { "H.263 decoder, restricted profile D", &DXVA_ModeH263_D, 0, NULL }, { "H.263 decoder, restricted profile E", &DXVA_ModeH263_E, 0, NULL }, { "H.263 decoder, restricted profile F", &DXVA_ModeH263_F, 0, NULL }, { NULL, NULL, 0, NULL } }; static const dxva2_mode_t *Dxva2FindMode(const GUID *guid) { for (unsigned i = 0; dxva2_modes[i].name; i++) { if (IsEqualGUID(*dxva2_modes[i].guid, *guid)) return &dxva2_modes[i]; } return NULL; } bool isIntelClearVideo(const GUID *guid) { return IsEqualGUID(*guid, DXVA_Intel_H264_NoFGT_ClearVideo); } bool isNoEncrypt(const GUID *guid) { return IsEqualGUID(*guid, DXVA_NoEncrypt); } bool checkProfile(const dxva2_mode_t *mode, int profile) { if (!mode->profiles || !mode->profiles[0] || profile <= 0) return true; for (const int *p = &mode->profiles[0]; *p; ++p) { if (*p == profile) return true; } return false; } /* XXX Prefered format must come first */ //16-bit: https://msdn.microsoft.com/en-us/library/windows/desktop/bb970578(v=vs.85).aspx static const d3d_format_t d3d_formats[] = { { "NV12", MAKEFOURCC('N','V','1','2'), VideoFormat::Format_NV12 }, { "YV12", MAKEFOURCC('Y','V','1','2'), VideoFormat::Format_YUV420P }, { "IMC3", MAKEFOURCC('I','M','C','3'), VideoFormat::Format_YUV420P }, { "P010", MAKEFOURCC('P','0','1','0'), VideoFormat::Format_YUV420P10LE }, { "P016", MAKEFOURCC('P','0','1','6'), VideoFormat::Format_YUV420P16LE }, //FIXME: { NULL, 0, VideoFormat::Format_Invalid } }; static const d3d_format_t *D3dFindFormat(int fourcc) { for (unsigned i = 0; d3d_formats[i].name; i++) { if (d3d_formats[i].fourcc == fourcc) return &d3d_formats[i]; } return NULL; } VideoFormat::PixelFormat pixelFormatFromFourcc(int format) { const d3d_format_t *fmt = D3dFindFormat(format); if (fmt) return fmt->pixfmt; return VideoFormat::Format_Invalid; } int getSupportedFourcc(int *formats, UINT nb_formats) { for (const int *f = formats; f < &formats[nb_formats]; ++f) { const d3d_format_t *format = D3dFindFormat(*f); if (format) { qDebug("%s is supported for output", format->name); } else { qDebug("%d is supported for output (%4.4s)", *f, (const char*)f); } } for (const d3d_format_t *format = d3d_formats; format->name; ++format) { bool is_supported = false; for (unsigned k = 0; !is_supported && k < nb_formats; k++) { if (format->fourcc == formats[k]) return format->fourcc; } } return 0; } VideoDecoderD3D::VideoDecoderD3D(VideoDecoderD3DPrivate &d) : VideoDecoderFFmpegHW(d) { // dynamic properties about static property details. used by UI // format: detail_property setProperty("detail_surfaces", tr("Decoding surfaces") + QStringLiteral(" ") + tr("0: auto")); setProperty("threads", 1); //FIXME: mt crash on close } void VideoDecoderD3D::setSurfaces(int num) { DPTR_D(VideoDecoderD3D); if (d.surface_count == num) return; d.surface_count = num; d.surface_auto = num <= 0; Q_EMIT surfacesChanged(); } int VideoDecoderD3D::surfaces() const { return d_func().surface_count; } VideoDecoderD3DPrivate::VideoDecoderD3DPrivate() : VideoDecoderFFmpegHWPrivate() , surface_auto(true) , surface_count(0) , surface_width(0) , surface_height(0) , surface_order(0) { } bool VideoDecoderD3DPrivate::open() { if (!prepare()) return false; if (codec_ctx->codec_id == QTAV_CODEC_ID(HEVC)) { // runtime hevc check if (!isHEVCSupported()) { qWarning("HEVC DXVA2/D3D11VA is not supported by current FFmpeg runtime."); return false; } } if (!createDevice()) return false; format_fcc = 0; QVector codecs = getSupportedCodecs(); const d3d_format_t *fmt = getFormat(codec_ctx, codecs, &codec_guid); if (!fmt) return false; format_fcc = fmt->fourcc; if (!setupSurfaceInterop()) return false; return true; } void VideoDecoderD3DPrivate::close() { qDeleteAll(surfaces); surfaces.clear(); restore(); releaseUSWC(); destroyDecoder(); destroyDevice(); } void* VideoDecoderD3DPrivate::setup(AVCodecContext *avctx) { const int w = codedWidth(avctx); const int h = codedHeight(avctx); width = avctx->width; // not necessary. set in decode() height = avctx->height; releaseUSWC(); destroyDecoder(); /* Allocates all surfaces needed for the decoder */ if (surface_auto) { switch (codec_ctx->codec_id) { case QTAV_CODEC_ID(HEVC): case QTAV_CODEC_ID(H264): surface_count = 16 + 4; break; case QTAV_CODEC_ID(MPEG1VIDEO): case QTAV_CODEC_ID(MPEG2VIDEO): surface_count = 2 + 4; default: surface_count = 2 + 4; break; } if (avctx->active_thread_type & FF_THREAD_FRAME) surface_count += avctx->thread_count; } qDebug(">>>>>>>>>>>>>>>>>>>>>surfaces: %d, active_thread_type: %d, threads: %d, refs: %d", surface_count, avctx->active_thread_type, avctx->thread_count, avctx->refs); if (surface_count == 0) { qWarning("internal error: wrong surface count. %u auto=%d", surface_count, surface_auto); surface_count = 16 + 4; } qDeleteAll(surfaces); surfaces.clear(); hw_surfaces.clear(); surfaces.resize(surface_count); if (!createDecoder(codec_ctx->codec_id, w, h, surfaces)) return NULL; hw_surfaces.resize(surface_count); for (int i = 0; i < surfaces.size(); ++i) { hw_surfaces[i] = surfaces[i]->getSurface(); } surface_order = 0; surface_width = aligned(w); surface_height = aligned(h); initUSWC(surface_width); return setupAVVAContext(); //can not use codec_ctx for threaded mode! } /* FIXME it is nearly common with VAAPI */ bool VideoDecoderD3DPrivate::getBuffer(void **opaque, uint8_t **data)//vlc_va_t *external, AVFrame *ff) { if (!checkDevice()) return false; /* Grab an unused surface, in case none are, try the oldest * XXX using the oldest is a workaround in case a problem happens with libavcodec */ int i, old; for (i = 0, old = 0; i < surfaces.size(); i++) { const va_surface_t *s = surfaces[i]; if (!s->ref) break; if (s->order < surfaces[old]->order) old = i; } if (i >= surfaces.size()) i = old; va_surface_t *s = surfaces[i]; s->ref = 1; s->order = surface_order++; *data = (uint8_t*)s->getSurface(); *opaque = s; return true; } void VideoDecoderD3DPrivate::releaseBuffer(void *opaque, uint8_t *data) { Q_UNUSED(data); va_surface_t *surface = (va_surface_t*)opaque; surface->ref--; } int VideoDecoderD3DPrivate::aligned(int x) { // from lavfilters int align = 16; // MPEG-2 needs higher alignment on Intel cards, and it doesn't seem to harm anything to do it for all cards. if (codec_ctx->codec_id == QTAV_CODEC_ID(MPEG2VIDEO)) align <<= 1; else if (codec_ctx->codec_id == QTAV_CODEC_ID(HEVC)) align = 128; return FFALIGN(x, align); } const d3d_format_t* VideoDecoderD3DPrivate::getFormat(const AVCodecContext *avctx, const QVector &guids, GUID* selected) const { foreach (const GUID& g, guids) { const dxva2_mode_t *mode = Dxva2FindMode(&g); if (mode) { qDebug("- '%s' is supported by hardware", mode->name); } else { qDebug("- Unknown GUID = %08X-%04x-%04x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x", (unsigned)g.Data1, g.Data2, g.Data3 , g.Data4[0], g.Data4[1] , g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); } } /* Try all supported mode by our priority */ const dxva2_mode_t *mode = dxva2_modes; for (; mode->name; ++mode) { if (!mode->codec || mode->codec != avctx->codec_id) { qDebug("codec does not match to %s: %s", avcodec_get_name(avctx->codec_id), avcodec_get_name((AVCodecID)mode->codec)); continue; } qDebug("D3D found codec: %s. Check runtime support for the codec.", mode->name); bool is_supported = false; //TODO: find_if foreach (const GUID& g, guids) { if (IsEqualGUID(*mode->guid, g)) { is_supported = true; break; } } if (is_supported) { qDebug("Check profile support: %s", AVDecoderPrivate::getProfileName(avctx)); is_supported = checkProfile(mode, avctx->profile); } if (!is_supported) continue; int dxfmt = fourccFor(mode->guid); if (!dxfmt) continue; if (selected) *selected = *mode->guid; return D3dFindFormat(dxfmt); } return NULL; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderD3D.h000066400000000000000000000113521312235004300203530ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEODECODERD3D_H #define QTAV_VIDEODECODERD3D_H #include #include #include "VideoDecoderFFmpegHW.h" #include "VideoDecoderFFmpegHW_p.h" namespace QtAV { struct d3d_format_t { const char *name; int fourcc; VideoFormat::PixelFormat pixfmt; }; bool isIntelClearVideo(const GUID *guid); bool isNoEncrypt(const GUID* guid); int getSupportedFourcc(int *formats, UINT nb_formats); VideoFormat::PixelFormat pixelFormatFromFourcc(int format); class VideoDecoderD3DPrivate; class VideoDecoderD3D : public VideoDecoderFFmpegHW { DPTR_DECLARE_PRIVATE(VideoDecoderD3D) Q_OBJECT Q_PROPERTY(int surfaces READ surfaces WRITE setSurfaces NOTIFY surfacesChanged) public: // properties void setSurfaces(int num); int surfaces() const; Q_SIGNALS: void surfacesChanged(); protected: VideoDecoderD3D(VideoDecoderD3DPrivate& d); }; struct va_surface_t { va_surface_t() : ref(0), order(0) {} virtual ~va_surface_t() {} virtual void setSurface(IUnknown* s) = 0; virtual IUnknown* getSurface() const = 0; int ref; int order; }; class VideoDecoderD3DPrivate : public VideoDecoderFFmpegHWPrivate { public: VideoDecoderD3DPrivate(); bool open() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE; void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE; bool getBuffer(void **opaque, uint8_t **data) Q_DECL_OVERRIDE; void releaseBuffer(void *opaque, uint8_t *data) Q_DECL_OVERRIDE; int aligned(int x); private: virtual bool setupSurfaceInterop() {return true;} virtual bool createDevice() = 0; //d3d device, video context etc. virtual void destroyDevice() = 0; virtual bool checkDevice() {return true;} virtual QVector getSupportedCodecs() const = 0; virtual void* setupAVVAContext() = 0; /// create surfaces and decoder. width and height are coded value, maybe not aligned for d3d surface /// surfaces count is given, but not allocated virtual bool createDecoder(AVCodecID codec, int width, int height, QVector& surf) = 0; virtual void destroyDecoder() = 0; virtual int fourccFor(const GUID *guid) const = 0; const d3d_format_t *getFormat(const AVCodecContext* avctx, const QVector& guids, GUID *selected) const; public: // set by user. don't reset in when call destroy bool surface_auto; int surface_count; QVector hw_surfaces; int format_fcc; GUID codec_guid; private: int surface_width; int surface_height; unsigned surface_order; QVector surfaces; //TODO: delete on close() }; template int SelectConfig(AVCodecID codec_id, const T* cfgs, int nb_cfgs, T* cfg) { qDebug("Decoder configurations: %d", nb_cfgs); /* Select the best decoder configuration */ int cfg_score = 0; for (int i = 0; i < nb_cfgs; i++) { const T &c = cfgs[i]; qDebug("configuration[%d] ConfigBitstreamRaw %d", i, c.ConfigBitstreamRaw); /* */ int score = 0; if (c.ConfigBitstreamRaw == 1) score = 1; else if (codec_id == QTAV_CODEC_ID(H264) && c.ConfigBitstreamRaw == 2) score = 2; else continue; if (isNoEncrypt(&c.guidConfigBitstreamEncryption)) score += 16; if (cfg_score < score) { *cfg = c; cfg_score = score; } } if (cfg_score <= 0) qWarning("Failed to find a supported decoder configuration"); return cfg_score; } #ifndef MAKEFOURCC //winrt #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ ((DWORD)(BYTE)(ch0)|((DWORD)(BYTE)(ch1)<<8)|((DWORD)(BYTE)(ch2)<<16)|((DWORD)(BYTE)(ch3)<<24)) #endif //MAKEFOURCC } //namespace QtAV #endif //QTAV_VIDEODECODERD3D_H QtAV-1.12.0/src/codec/video/VideoDecoderD3D11.cpp000066400000000000000000000341341312235004300210530ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include //IID_ID3D11VideoContext #include "VideoDecoderD3D.h" #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #define DX_LOG_COMPONENT "D3D11VA" #include "utils/DirectXHelper.h" #include "directx/dxcompat.h" #include //include before because d3d11va.h also includes d3d11.h but as a c header (for msvc) #include extern "C" { #include } using namespace Microsoft::WRL; //ComPtr #include "directx/SurfaceInteropD3D11.h" #include "utils/Logger.h" // define __mingw_uuidof #ifdef __CRT_UUID_DECL __CRT_UUID_DECL(ID3D11VideoContext,0x61F21C45,0x3C0E,0x4a74,0x9C,0xEA,0x67,0x10,0x0D,0x9A,0xD5,0xE4) __CRT_UUID_DECL(ID3D11VideoDevice,0x10EC4D5B,0x975A,0x4689,0xB9,0xE4,0xD0,0xAA,0xC3,0x0F,0xE3,0x33) #endif //__CRT_UUID_DECL namespace QtAV { static QString sD3D11Description; struct dxgi_fcc { int fourcc; DXGI_FORMAT dxgi; } dxgi_formats[] = { {MAKEFOURCC('N','V','1','2'), DXGI_FORMAT_NV12}, {MAKEFOURCC('P','0','1','0'), DXGI_FORMAT_P010}, {MAKEFOURCC('P','0','1','6'), DXGI_FORMAT_P016}, }; DXGI_FORMAT fourccToDXGI(int fourcc) { for (size_t i = 0; i < sizeof(dxgi_formats)/sizeof(dxgi_formats[0]); ++i) { if (dxgi_formats[i].fourcc == fourcc) return dxgi_formats[i].dxgi; } return DXGI_FORMAT_UNKNOWN; } int fourccFromDXGI(DXGI_FORMAT fmt) { for (const dxgi_fcc* f = dxgi_formats; f < dxgi_formats + sizeof(dxgi_formats)/sizeof(dxgi_formats[0]); ++f) { if (f->dxgi == fmt) return f->fourcc; } return 0; } class VideoDecoderD3D11Private; class VideoDecoderD3D11 : public VideoDecoderD3D { DPTR_DECLARE_PRIVATE(VideoDecoderD3D11) public: VideoDecoderD3D11(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; }; extern VideoDecoderId VideoDecoderId_D3D11; FACTORY_REGISTER(VideoDecoder, D3D11, "D3D11") VideoDecoderId VideoDecoderD3D11::id() const { return VideoDecoderId_D3D11; } QString VideoDecoderD3D11::description() const { if (!sD3D11Description.isEmpty()) return sD3D11Description; return QStringLiteral("D3D11 Video Acceleration"); } struct d3d11_surface_t : public va_surface_t { d3d11_surface_t(): va_surface_t(), view(0) {} void setSurface(IUnknown* s) { view = (ID3D11VideoDecoderOutputView*)s; } IUnknown* getSurface() const { return view.Get();} private: ComPtr view; }; class VideoDecoderD3D11Private Q_DECL_FINAL : public VideoDecoderD3DPrivate { public: VideoDecoderD3D11Private() : dll(0) { //GetModuleHandle() #ifndef Q_OS_WINRT dll = LoadLibrary(TEXT("d3d11.dll")); available = !!dll; #endif if (d3d11::InteropResource::isSupported(d3d11::InteropEGL)) copy_mode = VideoDecoderFFmpegHW::ZeroCopy; } ~VideoDecoderD3D11Private() { #ifndef Q_OS_WINRT if (dll) { FreeLibrary(dll); } #endif } AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { return QTAV_PIX_FMT_C(D3D11VA_VLD);} private: bool createDevice() Q_DECL_OVERRIDE; void destroyDevice() Q_DECL_OVERRIDE; //d3d device and it's resources bool createDecoder(AVCodecID codec_id, int w, int h, QVector& surf) Q_DECL_OVERRIDE; void destroyDecoder() Q_DECL_OVERRIDE; bool setupSurfaceInterop() Q_DECL_OVERRIDE; void* setupAVVAContext() Q_DECL_OVERRIDE; QVector getSupportedCodecs() const Q_DECL_OVERRIDE; int fourccFor(const GUID *guid) const Q_DECL_OVERRIDE; HMODULE dll; ComPtr d3ddev; ComPtr d3dviddev; ComPtr d3ddec; ComPtr d3dvidctx; D3D11_VIDEO_DECODER_CONFIG cfg; struct AVD3D11VAContext hw; public: ComPtr d3dctx; ComPtr texture_cpu; // used by copy mode. d3d11 texture can not be accessed by both gpu and cpu d3d11::InteropResourcePtr interop_res; //may be still used in video frames when decoder is destroyed }; VideoFrame VideoDecoderD3D11::frame() { DPTR_D(VideoDecoderD3D11); //qDebug("frame size: %dx%d", d.frame->width, d.frame->height); if (!d.frame->opaque || !d.frame->data[0]) return VideoFrame(); if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) return VideoFrame(); ID3D11VideoDecoderOutputView *surface = (ID3D11VideoDecoderOutputView*)(uintptr_t)d.frame->data[3]; if (!surface) { qWarning("Get D3D11 surface error"); return VideoFrame(); } ComPtr texture;// = ((d3d11_surface_t*)(uintptr_t)d.frame->opaque)->texture; surface->GetResource((ID3D11Resource**)texture.GetAddressOf()); if (!texture) { qWarning("Get D3D11 texture error"); return VideoFrame(); } D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc; surface->GetDesc(&view_desc); D3D11_TEXTURE2D_DESC tex_desc; texture->GetDesc(&tex_desc); if (copyMode() == VideoDecoderFFmpegHW::ZeroCopy && d.interop_res) { d3d11::SurfaceInterop *interop = new d3d11::SurfaceInterop(d.interop_res); interop->setSurface(texture, view_desc.Texture2D.ArraySlice, d.width, d.height); VideoFormat fmt(d.interop_res->format(tex_desc.Format)); VideoFrame f(d.width, d.height, fmt); for (int i = 0; i < fmt.planeCount(); ++i) { f.setBytesPerLine(fmt.bytesPerLine(d.width, i), i); //used by gl to compute texture size } f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop))); f.setTimestamp(d.frame->pkt_pts/1000.0); f.setDisplayAspectRatio(d.getDAR(d.frame)); return f; } // qDebug("process for view: %p, texture: %p", surface, texture.Get()); d.d3dctx->CopySubresourceRegion(d.texture_cpu.Get(), 0, 0, 0, 0, texture.Get() , view_desc.Texture2D.ArraySlice , NULL); struct ScopedMap { ScopedMap(ComPtr ctx, ComPtr res, D3D11_MAPPED_SUBRESOURCE *mapped): c(ctx), r(res) { DX_ENSURE(c->Map(r.Get(), 0, D3D11_MAP_READ, 0, mapped)); //TODO: check error } ~ScopedMap() { c->Unmap(r.Get(), 0);} ComPtr c; ComPtr r; }; D3D11_MAPPED_SUBRESOURCE mapped; ScopedMap sm(d.d3dctx, d.texture_cpu, &mapped); //mingw error if ComPtr constructs from ComPtr [T=ID3D11Resource, U=ID3D11Texture2D] Q_UNUSED(sm); int pitch[3] = { (int)mapped.RowPitch, 0, 0}; //compute chroma later uint8_t *src[] = { (uint8_t*)mapped.pData, 0, 0}; //compute chroma later const VideoFormat format = pixelFormatFromFourcc(d.format_fcc); //tex_desc return copyToFrame(format, tex_desc.Height, src, pitch, false); } VideoDecoderD3D11::VideoDecoderD3D11() : VideoDecoderD3D(*new VideoDecoderD3D11Private()) { } bool VideoDecoderD3D11Private::createDevice() { // if (d3dviddev) return true; PFN_D3D11_CREATE_DEVICE fCreateDevice = NULL; #if defined(Q_OS_WINRT) fCreateDevice = ::D3D11CreateDevice; #else fCreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(dll, "D3D11CreateDevice"); if (!fCreateDevice) { qWarning("Can not resolve symbol D3D11CreateDevice"); } #endif // TODO: feature levels DX_ENSURE(fCreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, NULL, 0, D3D11_SDK_VERSION, d3ddev.ReleaseAndGetAddressOf(), NULL, d3dctx.ReleaseAndGetAddressOf()), false); ComPtr mt; DX_ENSURE(d3ddev.As(&mt), false); mt->SetMultithreadProtected(true); //TODO: check threads DX_ENSURE(d3dctx.As(&d3dvidctx), false); DX_ENSURE(d3ddev.As(&d3dviddev), false); ComPtr dxgi_dev; DX_ENSURE(d3ddev.As(&dxgi_dev), false); ComPtr dxgi_adapter; DX_ENSURE(dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()), false); DXGI_ADAPTER_DESC desc; DX_ENSURE(dxgi_adapter->GetDesc(&desc), false); sD3D11Description = QStringLiteral("D3D11 Video Acceleration (%1, vendor %2(%3), device %4, revision %5)") .arg(QString::fromWCharArray(desc.Description)) .arg(desc.VendorId) .arg(QString::fromUtf8(DXHelper::vendorName(desc.VendorId))) .arg(desc.DeviceId) .arg(desc.Revision) ; qDebug() << sD3D11Description; description = sD3D11Description; return true; } void VideoDecoderD3D11Private::destroyDevice() { d3dviddev.Reset(); d3dvidctx.Reset(); d3dctx.Reset(); d3ddev.Reset(); } int VideoDecoderD3D11Private::fourccFor(const GUID *guid) const { BOOL is_supported = FALSE; for (size_t i = 0; i < sizeof(dxgi_formats)/sizeof(dxgi_formats[i]); ++i) { const dxgi_fcc& f = dxgi_formats[i]; DX_ENSURE(d3dviddev->CheckVideoDecoderFormat(guid, f.dxgi, &is_supported), 0); if (is_supported) return f.fourcc; } return 0; } QVector VideoDecoderD3D11Private::getSupportedCodecs() const { UINT nb_profiles = d3dviddev->GetVideoDecoderProfileCount(); QVector guids(nb_profiles); for (UINT i = 0; i < nb_profiles; ++i) { DX_ENSURE(d3dviddev->GetVideoDecoderProfile(i, &guids[i]), QVector()); } return guids; } bool VideoDecoderD3D11Private::createDecoder(AVCodecID codec_id, int w, int h, QVector &surf) { const int nb_surfaces = surf.size(); D3D11_TEXTURE2D_DESC texDesc; ZeroMemory(&texDesc, sizeof(texDesc)); texDesc.Width = aligned(w); texDesc.Height = aligned(h); texDesc.MipLevels = 1; texDesc.Format = fourccToDXGI(format_fcc); texDesc.SampleDesc.Count = 1; texDesc.MiscFlags = 0; texDesc.ArraySize = nb_surfaces; texDesc.Usage = D3D11_USAGE_DEFAULT; texDesc.BindFlags = D3D11_BIND_DECODER; texDesc.CPUAccessFlags = 0; ComPtr tex; DX_ENSURE(d3ddev->CreateTexture2D(&texDesc, NULL, tex.GetAddressOf()), false); if (copy_mode != VideoDecoderFFmpegHW::ZeroCopy || !interop_res) { //copy tex->GetDesc(&texDesc); texDesc.MipLevels = 1; texDesc.MiscFlags = 0; texDesc.ArraySize = 1; texDesc.Usage = D3D11_USAGE_STAGING; texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; texDesc.BindFlags = 0; //? DX_ENSURE(d3ddev->CreateTexture2D(&texDesc, NULL, &texture_cpu), false); } D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc; ZeroMemory(&viewDesc, sizeof(viewDesc)); viewDesc.DecodeProfile = codec_guid; viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D; for (int i = 0; i < nb_surfaces; ++i) { viewDesc.Texture2D.ArraySlice = i; ComPtr view; DX_ENSURE(d3dviddev->CreateVideoDecoderOutputView(tex.Get(), &viewDesc, &view), false); d3d11_surface_t *s = new d3d11_surface_t(); s->setSurface(view.Get()); surf[i] = s; } qDebug("ID3D11VideoDecoderOutputView %d surfaces (%dx%d)", nb_surfaces, aligned(w), aligned(h)); D3D11_VIDEO_DECODER_DESC decoderDesc; ZeroMemory(&decoderDesc, sizeof(decoderDesc)); decoderDesc.Guid = codec_guid; decoderDesc.SampleWidth = w; decoderDesc.SampleHeight = h; decoderDesc.OutputFormat = fourccToDXGI(format_fcc); UINT cfg_count; DX_ENSURE(d3dviddev->GetVideoDecoderConfigCount(&decoderDesc, &cfg_count), false); /* List all configurations available for the decoder */ QVector cfg_list(cfg_count); for (unsigned i = 0; i < cfg_count; i++) { DX_ENSURE(d3dviddev->GetVideoDecoderConfig(&decoderDesc, i, &cfg_list[i]), false); } if (SelectConfig(codec_id, cfg_list.constData(), cfg_count, &cfg) <= 0) return false; // not associated with surfaces, so surfaces can be dynamicall added? DX_ENSURE(d3dviddev->CreateVideoDecoder(&decoderDesc, &cfg, &d3ddec), false); return true; } void VideoDecoderD3D11Private::destroyDecoder() { texture_cpu.Reset(); d3ddec.Reset(); } bool VideoDecoderD3D11Private::setupSurfaceInterop() { if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy) interop_res = d3d11::InteropResourcePtr(d3d11::InteropResource::create()); if (interop_res) interop_res->setDevice(d3ddev); return true; } void* VideoDecoderD3D11Private::setupAVVAContext() { // TODO: FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG if (isIntelClearVideo(&codec_guid)) { #ifdef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO //2014-03-07 - 8b2a130 - lavc 55.50.0 / 55.53.100 - dxva2.h qDebug("FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO"); hw.workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; #endif } else { hw.workaround = 0; } hw.video_context = d3dvidctx.Get(); hw.decoder = d3ddec.Get(); hw.cfg = &cfg; hw.surface_count = hw_surfaces.size(); hw.surface = (ID3D11VideoDecoderOutputView**)hw_surfaces.constData(); return &hw; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderDXVA.cpp000066400000000000000000000406171312235004300211040ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ /// egl support is added by: Andy Bell #ifdef _MSC_VER #pragma comment(lib, "ole32.lib") //CoTaskMemFree. why link failed? #endif #include "VideoDecoderD3D.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" //#include "QtAV/private/mkid.h" #include "utils/Logger.h" #include "directx/SurfaceInteropD3D9.h" #include #define DX_LOG_COMPONENT "DXVA2" #include "utils/DirectXHelper.h" // d3d9ex: http://dxr.mozilla.org/mozilla-central/source/dom/media/wmf/DXVA2Manager.cpp // to use c api, add define COBJMACROS and CINTERFACE #define DXVA2API_USE_BITFIELDS extern "C" { #include //will include d3d9.h, dxva2api.h } #include #include #include /* must be last included to not redefine existing GUIDs */ /* dxva2api.h GUIDs: http://msdn.microsoft.com/en-us/library/windows/desktop/ms697067(v=vs100).aspx * assume that they are declared in dxva2api.h */ //#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) ///TODO: ??? #define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID name = { l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} #ifdef __MINGW32__ # include <_mingw.h> # if !defined(__MINGW64_VERSION_MAJOR) # undef MS_GUID # define MS_GUID DEFINE_GUID /* dxva2api.h fails to declare those, redefine as static */ # else # include # endif #endif /* __MINGW32__ */ namespace QtAV { MS_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); MS_GUID(IID_IDirectXVideoAccelerationService, 0xfc51a550, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); class VideoDecoderDXVAPrivate; class VideoDecoderDXVA : public VideoDecoderD3D { DPTR_DECLARE_PRIVATE(VideoDecoderDXVA) public: VideoDecoderDXVA(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; }; extern VideoDecoderId VideoDecoderId_DXVA; FACTORY_REGISTER(VideoDecoder, DXVA, "DXVA") struct d3d9_surface_t : public va_surface_t { d3d9_surface_t() : va_surface_t(), d3d(0) {} ~d3d9_surface_t() { SafeRelease(&d3d);} void setSurface(IUnknown* s) Q_DECL_OVERRIDE { d3d = (IDirect3DSurface9*)s; } IUnknown* getSurface() const {return d3d;} private: IDirect3DSurface9 *d3d; }; /* */ //https://technet.microsoft.com/zh-cn/aa965266(v=vs.98).aspx class VideoDecoderDXVAPrivate Q_DECL_FINAL: public VideoDecoderD3DPrivate { public: VideoDecoderDXVAPrivate(): VideoDecoderD3DPrivate() { // d3d9+gl interop may not work on optimus moble platforms, 0-copy is enabled only for egl interop if (d3d9::InteropResource::isSupported(d3d9::InteropEGL) && QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) copy_mode = VideoDecoderFFmpegHW::ZeroCopy; hd3d9_dll = 0; hdxva2_dll = 0; d3dobj = 0; d3ddev = 0; token = 0; devmng = 0; device = 0; vs = 0; decoder = 0; available = loadDll(); } virtual ~VideoDecoderDXVAPrivate() // can not unload dlls because dx resource will be released in VideoDecoderD3DPrivate::close { unloadDll(); } AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { return QTAV_PIX_FMT_C(DXVA2_VLD);} private: bool loadDll(); bool unloadDll(); bool createDevice() Q_DECL_OVERRIDE; void destroyDevice() Q_DECL_OVERRIDE; //d3d device and it's resources, device manager, video device and decoder service QVector getSupportedCodecs() const Q_DECL_OVERRIDE; bool checkDevice() Q_DECL_OVERRIDE; bool createDecoder(AVCodecID codec_id, int w, int h, QVector& surf) Q_DECL_OVERRIDE; void destroyDecoder() Q_DECL_OVERRIDE; bool setupSurfaceInterop() Q_DECL_OVERRIDE; void* setupAVVAContext() Q_DECL_OVERRIDE; int fourccFor(const GUID *guid) const Q_DECL_OVERRIDE; /* DLL */ HINSTANCE hd3d9_dll; HINSTANCE hdxva2_dll; IDirect3D9 *d3dobj; IDirect3DDevice9 *d3ddev; // can be Ex /* Device manager */ UINT token; IDirect3DDeviceManager9 *devmng; HANDLE device; /* Video service */ IDirectXVideoDecoderService *vs; /* Video decoder */ DXVA2_ConfigPictureDecode cfg; IDirectXVideoDecoder *decoder; struct dxva_context hw_ctx; QString vendor; public: d3d9::InteropResourcePtr interop_res; //may be still used in video frames when decoder is destroyed }; static D3DFORMAT fourccToD3D(int fcc) { return (D3DFORMAT)fcc; } VideoDecoderDXVA::VideoDecoderDXVA() : VideoDecoderD3D(*new VideoDecoderDXVAPrivate()) { } VideoDecoderId VideoDecoderDXVA::id() const { return VideoDecoderId_DXVA; } QString VideoDecoderDXVA::description() const { DPTR_D(const VideoDecoderDXVA); if (!d.description.isEmpty()) return d.description; return QStringLiteral("DirectX Video Acceleration"); } VideoFrame VideoDecoderDXVA::frame() { DPTR_D(VideoDecoderDXVA); //qDebug("frame size: %dx%d", d.frame->width, d.frame->height); if (!d.frame->opaque || !d.frame->data[0]) return VideoFrame(); if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) return VideoFrame(); IDirect3DSurface9 *d3d = (IDirect3DSurface9*)(uintptr_t)d.frame->data[3]; if (copyMode() == ZeroCopy && d.interop_res) { d3d9::SurfaceInterop *interop = new d3d9::SurfaceInterop(d.interop_res); interop->setSurface(d3d, d.width, d.height); VideoFrame f(d.width, d.height, VideoFormat::Format_RGB32); f.setBytesPerLine(d.width * 4); //used by gl to compute texture size f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop))); f.setTimestamp(d.frame->pkt_pts/1000.0); f.setDisplayAspectRatio(d.getDAR(d.frame)); return f; } class ScopedD3DLock { IDirect3DSurface9 *mpD3D; public: ScopedD3DLock(IDirect3DSurface9* d3d, D3DLOCKED_RECT *rect) : mpD3D(d3d) { if (FAILED(mpD3D->LockRect(rect, NULL, D3DLOCK_READONLY))) { qWarning("Failed to lock surface"); mpD3D = 0; } } ~ScopedD3DLock() { if (mpD3D) mpD3D->UnlockRect(); } }; D3DLOCKED_RECT lock; ScopedD3DLock(d3d, &lock); if (lock.Pitch == 0) { return VideoFrame(); } //picth >= desc.Width D3DSURFACE_DESC desc; d3d->GetDesc(&desc); const VideoFormat fmt = VideoFormat(pixelFormatFromFourcc(desc.Format)); if (!fmt.isValid()) { qWarning("unsupported dxva pixel format: %#x", desc.Format); return VideoFrame(); } //YV12 need swap, not imc3? // imc3 U V pitch == Y pitch, but half of the U/V plane is space. we convert to yuv420p here // nv12 bpp(1)==1 // 3rd plane is not used for nv12 int pitch[3] = { lock.Pitch, 0, 0}; //compute chroma later uint8_t *src[] = { (uint8_t*)lock.pBits, 0, 0}; //compute chroma later const bool swap_uv = desc.Format == MAKEFOURCC('I','M','C','3'); return copyToFrame(fmt, desc.Height, src, pitch, swap_uv); } bool VideoDecoderDXVAPrivate::loadDll() { hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL")); if (!hd3d9_dll) { qWarning("cannot load d3d9.dll"); return false; } hdxva2_dll = LoadLibrary(TEXT("DXVA2.DLL")); if (!hdxva2_dll) { qWarning("cannot load dxva2.dll"); FreeLibrary(hd3d9_dll); return false; } return true; } bool VideoDecoderDXVAPrivate::unloadDll() { if (hdxva2_dll) FreeLibrary(hdxva2_dll); if (hd3d9_dll) FreeLibrary(hd3d9_dll); //TODO: don't unload. maybe it's used by others return true; } bool VideoDecoderDXVAPrivate::createDevice() { // check whether they are created? D3DADAPTER_IDENTIFIER9 d3dai; ZeroMemory(&d3dai, sizeof(d3dai)); d3ddev = DXHelper::CreateDevice9Ex(hd3d9_dll, (IDirect3D9Ex**)(&d3dobj), &d3dai); if (!d3ddev) { qWarning("Failed to create d3d9 device ex, fallback to d3d9 device"); d3ddev = DXHelper::CreateDevice9(hd3d9_dll, &d3dobj, &d3dai); } if (!d3ddev) { qWarning("Failed to create d3d9 device"); return false; } vendor = QString::fromLatin1(DXHelper::vendorName(d3dai.VendorId)); description = QString().sprintf("DXVA2 (%.*s, vendor %lu(%s), device %lu, revision %lu)", sizeof(d3dai.Description), d3dai.Description, d3dai.VendorId, qPrintable(vendor), d3dai.DeviceId, d3dai.Revision); //if (copy_uswc) // copy_uswc = vendor.toLower() == "intel"; qDebug("DXVA2 description: %s", description.toUtf8().constData()); if (!d3ddev) return false; typedef HRESULT (WINAPI *CreateDeviceManager9Func)(UINT *pResetToken, IDirect3DDeviceManager9 **); CreateDeviceManager9Func CreateDeviceManager9 = (CreateDeviceManager9Func)GetProcAddress(hdxva2_dll, "DXVA2CreateDirect3DDeviceManager9"); if (!CreateDeviceManager9) { qWarning("cannot load function DXVA2CreateDirect3DDeviceManager9"); return false; } qDebug("OurDirect3DCreateDeviceManager9 Success!"); DX_ENSURE_OK(CreateDeviceManager9(&token, &devmng), false); qDebug("obtained IDirect3DDeviceManager9"); //http://msdn.microsoft.com/en-us/library/windows/desktop/ms693525%28v=vs.85%29.aspx DX_ENSURE_OK(devmng->ResetDevice(d3ddev, token), false); DX_ENSURE_OK(devmng->OpenDeviceHandle(&device), false); DX_ENSURE_OK(devmng->GetVideoService(device, IID_IDirectXVideoDecoderService, (void**)&vs), false); return true; } void VideoDecoderDXVAPrivate::destroyDevice() { SafeRelease(&vs); if (devmng && device && device != INVALID_HANDLE_VALUE) { devmng->CloseDeviceHandle(device); device = 0; } SafeRelease(&devmng); SafeRelease(&d3ddev); SafeRelease(&d3dobj); } QVector VideoDecoderDXVAPrivate::getSupportedCodecs() const { /* Retreive supported modes from the decoder service */ UINT input_count = 0; GUID *input_list = NULL; QVector guids; DX_ENSURE_OK(vs->GetDecoderDeviceGuids(&input_count, &input_list), guids); guids.resize(input_count); memcpy(guids.data(), input_list, input_count*sizeof(GUID)); CoTaskMemFree(input_list); return guids; } int VideoDecoderDXVAPrivate::fourccFor(const GUID *guid) const { UINT output_count = 0; D3DFORMAT *output_list = NULL; if (FAILED(vs->GetDecoderRenderTargets(*guid, &output_count, &output_list))) { qWarning("IDirectXVideoDecoderService_GetDecoderRenderTargets failed"); return 0; } int fmt = getSupportedFourcc((int*)output_list, output_count); CoTaskMemFree(output_list); return fmt; } bool VideoDecoderDXVAPrivate::checkDevice() { // check pix fmt DXVA2_VLD and h264 profile? HRESULT hr = devmng->TestDevice(device); if (hr == DXVA2_E_NEW_VIDEO_DEVICE) { //https://technet.microsoft.com/zh-cn/aa965266(v=vs.98).aspx // CloseDeviceHandle,Release service&decoder, open a new device handle, create a new decoder qWarning("DXVA2_E_NEW_VIDEO_DEVICE. Video decoder reset is not implemeted"); return false; } else if (FAILED(hr)) { qWarning() << "IDirect3DDeviceManager9.TestDevice (" << hr << "): " << qt_error_string(hr); return false; } return true; } bool VideoDecoderDXVAPrivate::createDecoder(AVCodecID codec_id, int w, int h, QVector& surf) { if (!vs || !d3ddev) { qWarning("dx video surface is not ready. IDirectXVideoService: %p, IDirect3DDevice9: %p", vs, d3ddev); return false; } const int nb_surfaces = surf.size(); static const int kMaxSurfaceCount = 64; IDirect3DSurface9* surface_list[kMaxSurfaceCount]; qDebug("IDirectXVideoDecoderService=%p nb_surfaces=%d surface %dx%d", vs, nb_surfaces, aligned(w), aligned(h)); DX_ENSURE_OK(vs->CreateSurface(aligned(w), aligned(h), nb_surfaces - 1, //The number of back buffers. The method creates BackBuffers + 1 surfaces. fourccToD3D(format_fcc), D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, surface_list, NULL) , false); for (int i = 0; i < nb_surfaces; i++) { d3d9_surface_t* s = new d3d9_surface_t(); s->setSurface(surface_list[i]); surf[i] = s; } qDebug("IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)", nb_surfaces, w, h); /* */ DXVA2_VideoDesc dsc; ZeroMemory(&dsc, sizeof(dsc)); dsc.SampleWidth = w; //coded_width dsc.SampleHeight = h; //coded_height dsc.Format = fourccToD3D(format_fcc); dsc.InputSampleFreq.Numerator = 0; dsc.InputSampleFreq.Denominator = 0; dsc.OutputFrameFreq = dsc.InputSampleFreq; dsc.UABProtectionLevel = FALSE; dsc.Reserved = 0; // see xbmc /* FIXME I am unsure we can let unknown everywhere */ DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; ext->SampleFormat = 0;//DXVA2_SampleProgressiveFrame;//xbmc. DXVA2_SampleUnknown; ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown; ext->NominalRange = 0;//DXVA2_NominalRange_Unknown; ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown; ext->VideoLighting = 0;//DXVA2_VideoLighting_dim;//xbmc. DXVA2_VideoLighting_Unknown; ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown; ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown; /* List all configurations available for the decoder */ UINT cfg_count = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; DX_ENSURE_OK(vs->GetDecoderConfigurations(codec_guid, &dsc, NULL, &cfg_count, &cfg_list) , false); const int score = SelectConfig(codec_id, cfg_list, cfg_count, &cfg); CoTaskMemFree(cfg_list); if (score <= 0) return false; /* Create the decoder */ DX_ENSURE_OK(vs->CreateVideoDecoder(codec_guid, &dsc, &cfg, surface_list, nb_surfaces, &decoder), false); qDebug("IDirectXVideoDecoderService.CreateVideoDecoder succeed. decoder=%p", decoder); return true; } void VideoDecoderDXVAPrivate::destroyDecoder() { SafeRelease(&decoder); } void* VideoDecoderDXVAPrivate::setupAVVAContext() { // TODO: FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG if (isIntelClearVideo(&codec_guid)) { #ifdef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO //2014-03-07 - 8b2a130 - lavc 55.50.0 / 55.53.100 - dxva2.h qDebug("FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO"); hw_ctx.workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; #endif } else { hw_ctx.workaround = 0; } hw_ctx.decoder = decoder; hw_ctx.cfg = &cfg; hw_ctx.surface_count = hw_surfaces.size(); hw_ctx.surface = (IDirect3DSurface9**)hw_surfaces.constData(); return &hw_ctx; } bool VideoDecoderDXVAPrivate::setupSurfaceInterop() { if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy) interop_res = d3d9::InteropResourcePtr(d3d9::InteropResource::create(d3ddev)); return true; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderFFmpeg.cpp000066400000000000000000000356021312235004300215040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegBase.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "QtAV/version.h" #include "utils/Logger.h" /*! * options (properties) are from libavcodec/options_table.h * enum name here must convert to lower case to fit the names in avcodec. done in AVDecoder.setOptions() * Don't use lower case here because the value name may be "default" in avcodec which is a keyword of C++ */ namespace QtAV { class VideoDecoderFFmpegPrivate; class VideoDecoderFFmpeg : public VideoDecoderFFmpegBase { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderFFmpeg) Q_PROPERTY(QString codecName READ codecName WRITE setCodecName NOTIFY codecNameChanged) Q_PROPERTY(QString hwaccel READ hwaccel WRITE setHwaccel NOTIFY hwaccelChanged) Q_PROPERTY(DiscardType skip_loop_filter READ skipLoopFilter WRITE setSkipLoopFilter) Q_PROPERTY(DiscardType skip_idct READ skipIDCT WRITE setSkipIDCT) // Force a strict standard compliance when encoding (accepted values: -2 to 2) //Q_PROPERTY(StrictType strict READ strict WRITE setStrict) Q_PROPERTY(DiscardType skip_frame READ skipFrame WRITE setSkipFrame) Q_PROPERTY(int threads READ threads WRITE setThreads) // 0 is auto Q_PROPERTY(ThreadFlags thread_type READ threadFlags WRITE setThreadFlags) Q_PROPERTY(MotionVectorVisFlags vismv READ motionVectorVisFlags WRITE setMotionVectorVisFlags) //Q_PROPERTY(BugFlags bug READ bugFlags WRITE setBugFlags) Q_ENUMS(StrictType) Q_ENUMS(DiscardType) Q_ENUMS(ThreadFlag) Q_FLAGS(ThreadFlags) Q_ENUMS(MotionVectorVisFlag) Q_FLAGS(MotionVectorVisFlags) Q_ENUMS(BugFlag) Q_FLAGS(BugFlags) public: enum StrictType { Very = FF_COMPLIANCE_VERY_STRICT, Strict = FF_COMPLIANCE_STRICT, Normal = FF_COMPLIANCE_NORMAL, //default Unofficial = FF_COMPLIANCE_UNOFFICIAL, Experimental = FF_COMPLIANCE_EXPERIMENTAL }; enum DiscardType { // TODO: discard_type None = AVDISCARD_NONE, Default = AVDISCARD_DEFAULT, //default NoRef = AVDISCARD_NONREF, Bidir = AVDISCARD_BIDIR, NoKey = AVDISCARD_NONKEY, All = AVDISCARD_ALL }; enum ThreadFlag { DefaultType = FF_THREAD_SLICE | FF_THREAD_FRAME,//default Slice = FF_THREAD_SLICE, Frame = FF_THREAD_FRAME }; Q_DECLARE_FLAGS(ThreadFlags, ThreadFlag) // flags. visualize motion vectors (MVs) enum MotionVectorVisFlag { No = 0, //default PF = FF_DEBUG_VIS_MV_P_FOR, BF = FF_DEBUG_VIS_MV_B_FOR, BB = FF_DEBUG_VIS_MV_B_BACK }; Q_DECLARE_FLAGS(MotionVectorVisFlags, MotionVectorVisFlag) enum BugFlag { autodetect = FF_BUG_AUTODETECT, //default #if FF_API_OLD_MSMPEG4 //old_msmpeg4 = FF_BUG_OLD_MSMPEG4, //moc does not support PP? #endif xvid_ilace = FF_BUG_XVID_ILACE, ump4 = FF_BUG_UMP4, no_padding = FF_BUG_NO_PADDING, amv = FF_BUG_AMV, #if FF_API_AC_VLC //ac_vlc = FF_BUG_AC_VLC, //moc does not support PP? #endif qpel_chroma = FF_BUG_QPEL_CHROMA, std_qpel = FF_BUG_QPEL_CHROMA2, direct_blocksize = FF_BUG_DIRECT_BLOCKSIZE, edge = FF_BUG_EDGE, hpel_chroma = FF_BUG_HPEL_CHROMA, dc_clip = FF_BUG_DC_CLIP, ms = FF_BUG_MS, trunc = FF_BUG_TRUNCATED }; Q_DECLARE_FLAGS(BugFlags, BugFlag) static VideoDecoder* createMMAL() { VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg(); vd->setProperty("hwaccel", "mmal"); return vd; } static VideoDecoder* createQSV() { VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg(); vd->setProperty("hwaccel", "qsv"); return vd; } static VideoDecoder* createCrystalHD() { VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg(); vd->setProperty("hwaccel", "crystalhd"); return vd; } static void registerHWA() { #if defined(Q_OS_WIN32) || (defined(Q_OS_LINUX) && !defined(Q_PROCESSOR_ARM) && !defined(QT_ARCH_ARM)) VideoDecoder::Register(VideoDecoderId_QSV, createQSV, "QSV"); #endif #ifdef Q_OS_LINUX #if defined(Q_PROCESSOR_ARM)/*qt5*/ || defined(QT_ARCH_ARM) /*qt4*/ VideoDecoder::Register(VideoDecoderId_MMAL, createMMAL, "MMAL"); #else VideoDecoder::Register(VideoDecoderId_CrystalHD, createCrystalHD, "CrystalHD"); #endif #endif } VideoDecoderFFmpeg(); VideoDecoderId id() const Q_DECL_OVERRIDE Q_DECL_FINAL; QString description() const Q_DECL_OVERRIDE Q_DECL_FINAL { const int patch = QTAV_VERSION_PATCH(avcodec_version()); return QStringLiteral("%1 avcodec %2.%3.%4") .arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav")) .arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch); } virtual VideoFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL; // TODO: av_opt_set in setter void setSkipLoopFilter(DiscardType value); DiscardType skipLoopFilter() const; void setSkipIDCT(DiscardType value); DiscardType skipIDCT() const; void setStrict(StrictType value); StrictType strict() const; void setSkipFrame(DiscardType value); DiscardType skipFrame() const; void setThreads(int value); int threads() const; void setThreadFlags(ThreadFlags value); ThreadFlags threadFlags() const; void setMotionVectorVisFlags(MotionVectorVisFlags value); MotionVectorVisFlags motionVectorVisFlags() const; void setBugFlags(BugFlags value); BugFlags bugFlags() const; void setHwaccel(const QString& value); QString hwaccel() const; Q_SIGNALS: void codecNameChanged() Q_DECL_OVERRIDE; void hwaccelChanged(); }; extern VideoDecoderId VideoDecoderId_FFmpeg; FACTORY_REGISTER(VideoDecoder, FFmpeg, "FFmpeg") void RegisterFFmpegHWA_Man() { VideoDecoderFFmpeg::registerHWA(); } namespace { static const struct factory_register_FFmpegHWA { inline factory_register_FFmpegHWA() { VideoDecoderFFmpeg::registerHWA(); } } sInit_FFmpegHWA; } class VideoDecoderFFmpegPrivate Q_DECL_FINAL: public VideoDecoderFFmpegBasePrivate { public: VideoDecoderFFmpegPrivate(): VideoDecoderFFmpegBasePrivate() , skip_loop_filter(VideoDecoderFFmpeg::Default) , skip_idct(VideoDecoderFFmpeg::Default) , strict(VideoDecoderFFmpeg::Normal) , skip_frame(VideoDecoderFFmpeg::Default) , thread_type(VideoDecoderFFmpeg::DefaultType) , threads(0) , debug_mv(VideoDecoderFFmpeg::No) , bug(VideoDecoderFFmpeg::autodetect) {} bool open() Q_DECL_OVERRIDE { av_opt_set_int(codec_ctx, "skip_loop_filter", (int64_t)skip_loop_filter, 0); av_opt_set_int(codec_ctx, "skip_idct", (int64_t)skip_idct, 0); av_opt_set_int(codec_ctx, "strict", (int64_t)strict, 0); av_opt_set_int(codec_ctx, "skip_frame", (int64_t)skip_frame, 0); av_opt_set_int(codec_ctx, "threads", (int64_t)threads, 0); av_opt_set_int(codec_ctx, "thread_type", (int64_t)thread_type, 0); av_opt_set_int(codec_ctx, "vismv", (int64_t)debug_mv, 0); av_opt_set_int(codec_ctx, "bug", (int64_t)bug, 0); //CODEC_FLAG_EMU_EDGE: deprecated in ffmpeg >=? & libav>=10. always set by ffmpeg #if 0 if (fast) { codec_ctx->flags2 |= CODEC_FLAG2_FAST; // TODO: } else { //codec_ctx->flags2 &= ~CODEC_FLAG2_FAST; //ffplay has no this } // lavfilter //codec_ctx->slice_flags |= SLICE_FLAG_ALLOW_FIELD; //lavfilter //codec_ctx->strict_std_compliance = FF_COMPLIANCE_STRICT; codec_ctx->thread_safe_callbacks = true; switch (codec_ctx->codec_id) { case QTAV_CODEC_ID(MPEG4): case QTAV_CODEC_ID(H263): codec_ctx->thread_type = 0; break; case QTAV_CODEC_ID(MPEG1VIDEO): case QTAV_CODEC_ID(MPEG2VIDEO): codec_ctx->thread_type &= ~FF_THREAD_SLICE; /* fall through */ # if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)) case QTAV_CODEC_ID(H264): case QTAV_CODEC_ID(VC1): case QTAV_CODEC_ID(WMV3): codec_ctx->thread_type &= ~FF_THREAD_FRAME; # endif default: break; } #endif return true; } int skip_loop_filter; int skip_idct; int strict; int skip_frame; int thread_type; int threads; int debug_mv; int bug; QString hwa; }; VideoDecoderFFmpeg::VideoDecoderFFmpeg(): VideoDecoderFFmpegBase(*new VideoDecoderFFmpegPrivate()) { // dynamic properties about static property details. used by UI // format: detail_property setProperty("detail_skip_loop_filter", tr("Skipping the loop filter (aka deblocking) usually has determinal effect on quality. However it provides a big speedup for hi definition streams")); // like skip_frame setProperty("detail_skip_idct", tr("Force skipping of idct to speed up decoding for frame types (-1=None, " "0=Default, 1=B-frames, 2=P-frames, 3=B+P frames, 4=all frames)")); setProperty("detail_skip_frame", tr("Force skipping frames for speed up decoding.")); setProperty("detail_threads", QString("%1\n%2\n%3") .arg(tr("Number of decoding threads. Set before open. Maybe no effect for some decoders")) .arg(tr("0: auto")) .arg(tr("1: single thread decoding"))); } VideoDecoderId VideoDecoderFFmpeg::id() const { DPTR_D(const VideoDecoderFFmpeg); if (d.hwa == QLatin1String("mmal")) return VideoDecoderId_MMAL; if (d.hwa == QLatin1String("qsv")) return VideoDecoderId_QSV; if (d.hwa == QLatin1String("crystalhd")) return VideoDecoderId_CrystalHD; return VideoDecoderId_FFmpeg; } VideoFrame VideoDecoderFFmpeg::frame() { DPTR_D(VideoDecoderFFmpeg); if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) return VideoFrame(); // it's safe if width, height, pixfmt will not change, only data change VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); frame.setDisplayAspectRatio(d.getDAR(d.frame)); frame.setBits(d.frame->data); frame.setBytesPerLine(d.frame->linesize); // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) frame.setTimestamp((double)d.frame->pkt_pts/1000.0); frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); d.updateColorDetails(&frame); if (frame.format().hasPalette()) { frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4)); } return frame; } void VideoDecoderFFmpeg::setSkipLoopFilter(DiscardType value) { DPTR_D(VideoDecoderFFmpeg); d.skip_loop_filter = value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "skip_loop_filter", (int64_t)value, 0); } VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipLoopFilter() const { return (DiscardType)d_func().skip_loop_filter; } void VideoDecoderFFmpeg::setSkipIDCT(DiscardType value) { DPTR_D(VideoDecoderFFmpeg); d.skip_idct = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "skip_idct", (int64_t)value, 0); } VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipIDCT() const { return (DiscardType)d_func().skip_idct; } void VideoDecoderFFmpeg::setStrict(StrictType value) { DPTR_D(VideoDecoderFFmpeg); d.strict = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "strict", int64_t(value), 0); } VideoDecoderFFmpeg::StrictType VideoDecoderFFmpeg::strict() const { return (StrictType)d_func().strict; } void VideoDecoderFFmpeg::setSkipFrame(DiscardType value) { DPTR_D(VideoDecoderFFmpeg); d.skip_frame = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "skip_frame", (int64_t)value, 0); } VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipFrame() const { return (DiscardType)d_func().skip_frame; } void VideoDecoderFFmpeg::setThreads(int value) { DPTR_D(VideoDecoderFFmpeg); d.threads = value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "threads", (int64_t)value, 0); } int VideoDecoderFFmpeg::threads() const { return d_func().threads; } void VideoDecoderFFmpeg::setThreadFlags(ThreadFlags value) { DPTR_D(VideoDecoderFFmpeg); d.thread_type = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "thread_type", (int64_t)value, 0); } VideoDecoderFFmpeg::ThreadFlags VideoDecoderFFmpeg::threadFlags() const { return (ThreadFlags)d_func().thread_type; } void VideoDecoderFFmpeg::setMotionVectorVisFlags(MotionVectorVisFlags value) { DPTR_D(VideoDecoderFFmpeg); d.debug_mv = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "vismv", (int64_t)value, 0); } VideoDecoderFFmpeg::MotionVectorVisFlags VideoDecoderFFmpeg::motionVectorVisFlags() const { return (MotionVectorVisFlags)d_func().debug_mv; } void VideoDecoderFFmpeg::setBugFlags(BugFlags value) { DPTR_D(VideoDecoderFFmpeg); d.bug = (int)value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "bug", (int64_t)value, 0); } VideoDecoderFFmpeg::BugFlags VideoDecoderFFmpeg::bugFlags() const { return (BugFlags)d_func().bug; } void VideoDecoderFFmpeg::setHwaccel(const QString &value) { DPTR_D(VideoDecoderFFmpeg); if (d.hwa == value) return; d.hwa = value.toLower(); Q_EMIT hwaccelChanged(); } QString VideoDecoderFFmpeg::hwaccel() const { return d_func().hwa; } //namespace { void i18n() { QObject::tr("codecName"); QObject::tr("skip_loop_filter"); QObject::tr("skip_idct"); QObject::tr("strict"); QObject::tr("skip_frame"); QObject::tr("threads"); QObject::tr("thread_type"); QObject::tr("vismv"); QObject::tr("bug"); } //} } //namespace QtAV #include "VideoDecoderFFmpeg.moc" QtAV-1.12.0/src/codec/video/VideoDecoderFFmpegBase.cpp000066400000000000000000000133071312235004300222750ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegBase.h" #include "QtAV/Packet.h" #include "utils/Logger.h" namespace QtAV { extern ColorSpace colorSpaceFromFFmpeg(AVColorSpace cs); extern ColorRange colorRangeFromFFmpeg(AVColorRange cr); static void SetColorDetailsByFFmpeg(VideoFrame *f, AVFrame* frame, AVCodecContext* codec_ctx) { ColorSpace cs = colorSpaceFromFFmpeg(av_frame_get_colorspace(frame)); if (cs == ColorSpace_Unknown) cs = colorSpaceFromFFmpeg(codec_ctx->colorspace); f->setColorSpace(cs); ColorRange cr = colorRangeFromFFmpeg(av_frame_get_color_range(frame)); if (cr == ColorRange_Unknown) { // check yuvj format. TODO: deprecated, check only for old ffmpeg? const AVPixelFormat pixfmt = (AVPixelFormat)frame->format; switch (pixfmt) { //case QTAV_PIX_FMT_C(YUVJ411P): //not in ffmpeg<2 and libav case QTAV_PIX_FMT_C(YUVJ420P): case QTAV_PIX_FMT_C(YUVJ422P): case QTAV_PIX_FMT_C(YUVJ440P): case QTAV_PIX_FMT_C(YUVJ444P): cr = ColorRange_Full; break; default: break; } } if (cr == ColorRange_Unknown) { cr = colorRangeFromFFmpeg(codec_ctx->color_range); if (cr == ColorRange_Unknown) { if (f->format().isXYZ()){ cr = ColorRange_Full; cs = ColorSpace_XYZ; // not here } else if (!f->format().isRGB()) { //qDebug("prefer limited yuv range"); cr = ColorRange_Limited; } } } f->setColorRange(cr); } void VideoDecoderFFmpegBasePrivate::updateColorDetails(VideoFrame *f) { if (f->format().pixelFormatFFmpeg() == frame->format) { SetColorDetailsByFFmpeg(f, frame, codec_ctx); return; } // hw decoder output frame may have a different format, e.g. gl interop frame may have rgb format for rendering(stored as yuv) const bool rgb_frame = f->format().isRGB(); if (rgb_frame) { //qDebug("rgb output frame (yuv coded)"); f->setColorSpace(f->format().isPlanar() ? ColorSpace_GBR : ColorSpace_RGB); f->setColorRange(ColorRange_Full); return; } // yuv frame. When happens? const bool rgb_coded = (av_pix_fmt_desc_get(codec_ctx->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB) == AV_PIX_FMT_FLAG_RGB; if (rgb_coded) { if (f->width() >= 1280 && f->height() >= 576) f->setColorSpace(ColorSpace_BT709); else f->setColorSpace(ColorSpace_BT601); f->setColorRange(ColorRange_Limited); } else { SetColorDetailsByFFmpeg(f, frame, codec_ctx); } } qreal VideoDecoderFFmpegBasePrivate::getDAR(AVFrame *f) { // lavf 54.5.100 av_guess_sample_aspect_ratio: stream.sar > frame.sar qreal dar = 0; if (f->height > 0) dar = (qreal)f->width/(qreal)f->height; // prefer sar from AVFrame if sar != 1/1 if (f->sample_aspect_ratio.num > 1) dar *= av_q2d(f->sample_aspect_ratio); else if (codec_ctx && codec_ctx->sample_aspect_ratio.num > 1) // skip 1/1 dar *= av_q2d(codec_ctx->sample_aspect_ratio); return dar; } VideoDecoderFFmpegBase::VideoDecoderFFmpegBase(VideoDecoderFFmpegBasePrivate &d): VideoDecoder(d) { } bool VideoDecoderFFmpegBase::decode(const Packet &packet) { if (!isAvailable()) return false; DPTR_D(VideoDecoderFFmpegBase); // some decoders might need other fields like flags&AV_PKT_FLAG_KEY // const AVPacket*: ffmpeg >= 1.0. no libav int got_frame_ptr = 0; int ret = 0; if (packet.isEOF()) { AVPacket eofpkt; av_init_packet(&eofpkt); eofpkt.data = NULL; eofpkt.size = 0; ret = avcodec_decode_video2(d.codec_ctx, d.frame, &got_frame_ptr, &eofpkt); } else { ret = avcodec_decode_video2(d.codec_ctx, d.frame, &got_frame_ptr, (AVPacket*)packet.asAVPacket()); } //qDebug("pic_type=%c", av_get_picture_type_char(d.frame->pict_type)); d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size()); if (ret < 0) { qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret)); return false; } if (!got_frame_ptr) { qWarning("no frame could be decompressed: %s %d/%d", av_err2str(ret), d.undecoded_size, packet.data.size()); return !packet.isEOF(); } if (!d.codec_ctx->width || !d.codec_ctx->height) return false; //qDebug("codec %dx%d, frame %dx%d", d.codec_ctx->width, d.codec_ctx->height, d.frame->width, d.frame->height); d.width = d.frame->width; // TODO: remove? used in hwdec d.height = d.frame->height; //avcodec_align_dimensions2(d.codec_ctx, &d.width_align, &d.height_align, aligns); return true; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderFFmpegBase.h000066400000000000000000000044141312235004300217410ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEODECODERFFMPEGBASE_H #define QTAV_VIDEODECODERFFMPEGBASE_H #include "QtAV/VideoDecoder.h" #include "QtAV/private/AVDecoder_p.h" #include "QtAV/private/AVCompat.h" namespace QtAV { class VideoDecoderFFmpegBasePrivate; class VideoDecoderFFmpegBase : public VideoDecoder { Q_DISABLE_COPY(VideoDecoderFFmpegBase) DPTR_DECLARE_PRIVATE(VideoDecoderFFmpegBase) public: virtual bool decode(const Packet& packet) Q_DECL_OVERRIDE; protected: VideoDecoderFFmpegBase(VideoDecoderFFmpegBasePrivate &d); private: VideoDecoderFFmpegBase(); //it's a base class }; class VideoDecoderFFmpegBasePrivate : public VideoDecoderPrivate { public: VideoDecoderFFmpegBasePrivate() : VideoDecoderPrivate() , frame(0) , width(0) , height(0) { avcodec_register_all(); frame = av_frame_alloc(); } virtual ~VideoDecoderFFmpegBasePrivate() { if (frame) { av_frame_free(&frame); frame = 0; } } void updateColorDetails(VideoFrame* f); qreal getDAR(AVFrame *f); AVFrame *frame; //set once and not change int width, height; //The current decoded frame size }; } //namespace QtAV #endif // QTAV_VIDEODECODERFFMPEGBASE_H QtAV-1.12.0/src/codec/video/VideoDecoderFFmpegHW.cpp000066400000000000000000000271121312235004300217400ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegHW.h" #include "VideoDecoderFFmpegHW_p.h" #include #include "utils/Logger.h" #ifndef Q_UNLIKELY #define Q_UNLIKELY(x) (!!(x)) #endif namespace QtAV { static AVPixelFormat ffmpeg_get_va_format(struct AVCodecContext *c, const AVPixelFormat * ff) { VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque; return va->getFormat(c, ff); } #if QTAV_HAVE(AVBUFREF) typedef struct ffmpeg_va_ref_t { VideoDecoderFFmpegHWPrivate *va; void *opaque; //va surface from AVFrame.opaque } ffmpeg_va_ref_t; static void ffmpeg_release_va_buffer2(void *opaque, uint8_t *data) { ffmpeg_va_ref_t *ref = (ffmpeg_va_ref_t*)opaque; ref->va->releaseBuffer(ref->opaque, data); delete ref; } static int ffmpeg_get_va_buffer2(struct AVCodecContext *ctx, AVFrame *frame, int flags) { Q_UNUSED(flags); for (unsigned i = 0; i < AV_NUM_DATA_POINTERS; i++) { frame->data[i] = NULL; frame->linesize[i] = 0; frame->buf[i] = NULL; } //frame->reordered_opaque = ctx->reordered_opaque; //?? xbmc // va must be available here VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)ctx->opaque; if (!va->getBuffer(&frame->opaque, &frame->data[0])) { qWarning("va->getBuffer failed"); return -1; } ffmpeg_va_ref_t *ref = new ffmpeg_va_ref_t; ref->va = va; ref->opaque = frame->opaque; /* data[0] must be non-NULL for libavcodec internal checks. data[3] actually contains the format-specific surface handle. */ frame->data[3] = frame->data[0]; frame->buf[0] = av_buffer_create(frame->data[0], 0, ffmpeg_release_va_buffer2, ref, 0); if (Q_UNLIKELY(!frame->buf[0])) { ffmpeg_release_va_buffer2(ref, frame->data[0]); return -1; } Q_ASSERT(frame->data[0] != NULL); return 0; } #else static int ffmpeg_get_va_buffer(struct AVCodecContext *c, AVFrame *ff)//vlc_va_t *external, AVFrame *ff) { VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque; //ff->reordered_opaque = c->reordered_opaque; //TODO: dxva? ff->opaque = 0; #if !AV_MODULE_CHECK(LIBAVCODEC, 54, 34, 0, 79, 101) ff->pkt_pts = c->pkt ? c->pkt->pts : AV_NOPTS_VALUE; #endif #if LIBAVCODEC_VERSION_MAJOR < 54 ff->age = 256*256*256*64; #endif /* hwaccel_context is not present in old ffmpeg version */ // va must be available here if (!va->getBuffer(&ff->opaque, &ff->data[0])) return -1; //ffmpeg_va_GetFrameBuf ff->data[3] = ff->data[0]; ff->type = FF_BUFFER_TYPE_USER; return 0; } static void ffmpeg_release_va_buffer(struct AVCodecContext *c, AVFrame *ff) { VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque; va->releaseBuffer(ff->opaque, ff->data[0]); memset(ff->data, 0, sizeof(ff->data)); memset(ff->linesize, 0, sizeof(ff->linesize)); } #endif //QTAV_HAVE(AVBUFREF) bool VideoDecoderFFmpegHWPrivate::prepare() { //// From vlc begin codec_ctx->thread_safe_callbacks = true; //? codec_ctx->thread_count = threads; #ifdef _MSC_VER #pragma warning(disable:4065) //vc: switch has default but no case #endif //_MSC_VER switch (codec_ctx->codec_id) { # if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)) /// tested libav-9.x + va-api. If remove this code: Bug detected, please report the issue. Context scratch buffers could not be allocated due to unknown size case QTAV_CODEC_ID(H264): case QTAV_CODEC_ID(VC1): case QTAV_CODEC_ID(WMV3): codec_ctx->thread_type &= ~FF_THREAD_FRAME; # endif default: break; } //// From vlc end codec_ctx->opaque = this; pixfmt = codec_ctx->pix_fmt; get_format = codec_ctx->get_format; #if QTAV_HAVE(AVBUFREF) get_buffer2 = codec_ctx->get_buffer2; #else get_buffer = codec_ctx->get_buffer; reget_buffer = codec_ctx->reget_buffer; release_buffer = codec_ctx->release_buffer; #endif //QTAV_HAVE(AVBUFREF) codec_ctx->get_format = ffmpeg_get_va_format; #if QTAV_HAVE(AVBUFREF) codec_ctx->get_buffer2 = ffmpeg_get_va_buffer2; #else // TODO: FF_API_GET_BUFFER codec_ctx->get_buffer = ffmpeg_get_va_buffer;//ffmpeg_GetFrameBuf; codec_ctx->reget_buffer = avcodec_default_reget_buffer; codec_ctx->release_buffer = ffmpeg_release_va_buffer;//ffmpeg_ReleaseFrameBuf; #endif //QTAV_HAVE(AVBUFREF) return true; } AVPixelFormat VideoDecoderFFmpegHWPrivate::getFormat(struct AVCodecContext *avctx, const AVPixelFormat *pi_fmt) { bool can_hwaccel = false; for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) { const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]); if (dsc == NULL) continue; bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0; qDebug("available %sware decoder output format %d (%s)", hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name); if (hwaccel) can_hwaccel = true; } if (!can_hwaccel) goto end; for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) { if (vaPixelFormat() != pi_fmt[i]) continue; if (hw_w == codedWidth((avctx)) && hw_h == codedHeight(avctx) && hw_profile == avctx->profile // update decoder if profile changed. but now only surfaces are updated && avctx->hwaccel_context) return pi_fmt[i]; // TODO: manage uswc here for x86 (surface size is decoder dependent) avctx->hwaccel_context = setup(avctx); if (!avctx->hwaccel_context) { qWarning("acceleration setup failure"); break; } hw_w = codedWidth((avctx)); hw_h = codedHeight(avctx); hw_profile = avctx->profile; qDebug("Using %s for hardware decoding.", qPrintable(description)); return pi_fmt[i]; } close(); end: qWarning("hardware acceleration is not available" ); /* Fallback to default behaviour */ return avcodec_default_get_format(avctx, pi_fmt); } int VideoDecoderFFmpegHWPrivate::codedWidth(AVCodecContext *avctx) const { if (avctx->coded_width > 0) return avctx->coded_width; return avctx->width; } int VideoDecoderFFmpegHWPrivate::codedHeight(AVCodecContext *avctx) const { if (avctx->coded_height > 0) return avctx->coded_height; return avctx->height; } bool VideoDecoderFFmpegHWPrivate::initUSWC(int lineSize) { if (copy_mode != VideoDecoderFFmpegHW::OptimizedCopy) return false; return gpu_mem.initCache(lineSize); } void VideoDecoderFFmpegHWPrivate::releaseUSWC() { if (copy_mode == VideoDecoderFFmpegHW::OptimizedCopy) gpu_mem.cleanCache(); } VideoDecoderFFmpegHW::VideoDecoderFFmpegHW(VideoDecoderFFmpegHWPrivate &d): VideoDecoderFFmpegBase(d) { setProperty("detail_copyMode", QStringLiteral("%1. %2\n%3. %4\n%5\n%6") .arg(tr("ZeroCopy: fastest. Direct rendering without data copy between CPU and GPU")) .arg(tr("Not implemented for all codecs")) .arg(tr("Not implemented for all codecs")) .arg(tr("OptimizedCopy: copy from USWC memory optimized by SSE4.1")) .arg(tr("GenericCopy: slowest. Generic cpu copy"))); setProperty("detail_threads", QString("%1\n%2\n%3\n%4") .arg(tr("Number of decoding threads. Set before open. Maybe no effect for some decoders")) .arg(tr("Multithread decoding may crash")) .arg(tr("0: auto")) .arg(tr("1: single thread decoding"))); Q_UNUSED(QObject::tr("ZeroCopy")); Q_UNUSED(QObject::tr("OptimizedCopy")); Q_UNUSED(QObject::tr("GenericCopy")); Q_UNUSED(QObject::tr("copyMode")); } void VideoDecoderFFmpegHW::setThreads(int value) { DPTR_D(VideoDecoderFFmpegHW); if (d.threads == value) return; d.threads = value; if (d.codec_ctx) av_opt_set_int(d.codec_ctx, "threads", (int64_t)value, 0); Q_EMIT threadsChanged(); } int VideoDecoderFFmpegHW::threads() const { return d_func().threads; } void VideoDecoderFFmpegHW::setCopyMode(CopyMode value) { DPTR_D(VideoDecoderFFmpegHW); if (d.copy_mode == value) return; d_func().copy_mode = value; emit copyModeChanged(); } VideoDecoderFFmpegHW::CopyMode VideoDecoderFFmpegHW::copyMode() const { return d_func().copy_mode; } VideoFrame VideoDecoderFFmpegHW::copyToFrame(const VideoFormat& fmt, int surface_h, quint8 *src[], int pitch[], bool swapUV) { DPTR_D(VideoDecoderFFmpegHW); Q_ASSERT_X(src[0] && pitch[0] > 0, "VideoDecoderFFmpegHW::copyToFrame", "src[0] and pitch[0] must be set"); const int nb_planes = fmt.planeCount(); const int chroma_pitch = nb_planes > 1 ? fmt.bytesPerLine(pitch[0], 1) : 0; const int chroma_h = fmt.chromaHeight(surface_h); int h[] = { surface_h, 0, 0}; for (int i = 1; i < nb_planes; ++i) { h[i] = chroma_h; // set chroma address and pitch if not set if (pitch[i] <= 0) pitch[i] = chroma_pitch; if (!src[i]) src[i] = src[i-1] + pitch[i-1]*h[i-1]; } if (swapUV && nb_planes > 2) { std::swap(src[1], src[2]); std::swap(pitch[1], pitch[2]); } VideoFrame frame; if (copyMode() == VideoDecoderFFmpegHW::OptimizedCopy && d.gpu_mem.isReady()) { int yuv_size = 0; for (int i = 0; i < nb_planes; ++i) { yuv_size += pitch[i]*h[i]; } // additional 15 bytes to ensure 16 bytes aligned QByteArray buf(15 + yuv_size, 0); const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f; // plane 1, 2... is aligned? uchar* plane_ptr = (uchar*)buf.data() + offset_16; QVector dst(nb_planes, 0); for (int i = 0; i < nb_planes; ++i) { dst[i] = plane_ptr; // TODO: add VideoFormat::planeWidth/Height() ? // pitch instead of surface_width plane_ptr += pitch[i] * h[i]; d.gpu_mem.copyFrame(src[i], dst[i], pitch[i], h[i], pitch[i]); } frame = VideoFrame(d.width, d.height, fmt, buf); frame.setBits(dst); frame.setBytesPerLine(pitch); } else { frame = VideoFrame(d.width, d.height, fmt); frame.setBits(src); frame.setBytesPerLine(pitch); // TODO: why clone is faster()? // TODO: buffer pool and create VideoFrame when needed to avoid copy? also for other va frame = frame.clone(); } frame.setTimestamp(double(d.frame->pkt_pts)/1000.0); frame.setDisplayAspectRatio(d.getDAR(d.frame)); d.updateColorDetails(&frame); return frame; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoDecoderFFmpegHW.h000066400000000000000000000042021312235004300214000ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEODECODERFFMPEGHW_H #define QTAV_VIDEODECODERFFMPEGHW_H #include "VideoDecoderFFmpegBase.h" namespace QtAV { class VideoDecoderFFmpegHWPrivate; class VideoDecoderFFmpegHW : public VideoDecoderFFmpegBase { Q_OBJECT Q_DISABLE_COPY(VideoDecoderFFmpegHW) DPTR_DECLARE_PRIVATE(VideoDecoderFFmpegHW) Q_PROPERTY(int threads READ threads WRITE setThreads NOTIFY threadsChanged) // <=0 is auto Q_PROPERTY(CopyMode copyMode READ copyMode WRITE setCopyMode NOTIFY copyModeChanged) Q_ENUMS(CopyMode) public: enum CopyMode { ZeroCopy, OptimizedCopy, GenericCopy }; VideoFrame copyToFrame(const VideoFormat& fmt, int surface_h, quint8* src[], int pitch[], bool swapUV); // properties int threads() const; void setThreads(int value); void setCopyMode(CopyMode value); CopyMode copyMode() const; Q_SIGNALS: void copyModeChanged(); void threadsChanged(); protected: VideoDecoderFFmpegHW(VideoDecoderFFmpegHWPrivate &d); private: VideoDecoderFFmpegHW(); }; } //namespace QtAV #endif // QTAV_VIDEODECODERFFMPEGHW_H QtAV-1.12.0/src/codec/video/VideoDecoderFFmpegHW_p.h000066400000000000000000000076541312235004300217350ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEODECODERFFMPEGHW_P_H #define QTAV_VIDEODECODERFFMPEGHW_P_H #include "VideoDecoderFFmpegHW.h" #include "utils/GPUMemCopy.h" /*! QTAV_HAVE(AVBUFREF): use AVCodecContext.get_buffer2 instead of old callbacks. In order to avoid compile warnings, now disable old callbacks if possible. maybe we can also do a runtime check and enable old callbacks */ namespace QtAV { class VideoDecoderFFmpegHWPrivate : public VideoDecoderFFmpegBasePrivate { public: VideoDecoderFFmpegHWPrivate() : VideoDecoderFFmpegBasePrivate() , get_format(NULL) , get_buffer(NULL) , release_buffer(NULL) , reget_buffer(NULL) , get_buffer2(NULL) , threads(0) , copy_mode(VideoDecoderFFmpegHW::OptimizedCopy) , hw_w(0) , hw_h(0) , hw_profile(0) {} virtual ~VideoDecoderFFmpegHWPrivate() {} //ctx is 0 now bool enableFrameRef() const Q_DECL_OVERRIDE { return false;} //because of ffmpeg_get_va_buffer2? bool prepare(); void restore() { codec_ctx->pix_fmt = pixfmt; codec_ctx->opaque = 0; codec_ctx->get_format = get_format; #if QTAV_HAVE(AVBUFREF) codec_ctx->get_buffer2 = get_buffer2; #else codec_ctx->get_buffer = get_buffer; codec_ctx->release_buffer = release_buffer; codec_ctx->reget_buffer = reget_buffer; #endif //QTAV_HAVE(AVBUFREF) } // return hwaccel_context or null virtual void* setup(AVCodecContext* avctx) = 0; AVPixelFormat getFormat(struct AVCodecContext *p_context, const AVPixelFormat *pi_fmt); //TODO: remove opaque virtual bool getBuffer(void **opaque, uint8_t **data) = 0; virtual void releaseBuffer(void *opaque, uint8_t *data) = 0; virtual AVPixelFormat vaPixelFormat() const = 0; int codedWidth(AVCodecContext *avctx) const; //TODO: virtual int surfaceWidth(AVCodecContext*) const; int codedHeight(AVCodecContext *avctx) const; bool initUSWC(int lineSize); void releaseUSWC(); AVPixelFormat pixfmt; //store old one //store old values because it does not own AVCodecContext AVPixelFormat (*get_format)(struct AVCodecContext *s, const AVPixelFormat * fmt); int (*get_buffer)(struct AVCodecContext *c, AVFrame *pic); void (*release_buffer)(struct AVCodecContext *c, AVFrame *pic); int (*reget_buffer)(struct AVCodecContext *c, AVFrame *pic); int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags); QString description; int threads; // multithread decoding may crash for some decoders (dxva, videotoolbox) // false for not intel gpu. my test result is intel gpu is supper fast and lower cpu usage if use optimized uswc copy. but nv is worse. // TODO: flag enable, disable, auto VideoDecoderFFmpegHW::CopyMode copy_mode; GPUMemCopy gpu_mem; private: int hw_w, hw_h, hw_profile; }; } //namespace QtAV #endif // QTAV_VideoDecoderFFmpegHW_P_H QtAV-1.12.0/src/codec/video/VideoDecoderMediaCodec.cpp000066400000000000000000000061641312235004300223160ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegBase.h" #if FFMPEG_MODULE_CHECK(LIBAVCODEC, 57, 28, 100) #define QTAV_HAVE_MEDIACODEC 1 #endif #if QTAV_HAVE(MEDIACODEC) #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #include extern "C" { #include } namespace QtAV { class VideoDecoderMediaCodecPrivate; class VideoDecoderMediaCodec : public VideoDecoderFFmpegBase { DPTR_DECLARE_PRIVATE(VideoDecoderMediaCodec) public: VideoDecoderMediaCodec(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; }; extern VideoDecoderId VideoDecoderId_MediaCodec; FACTORY_REGISTER(VideoDecoder, MediaCodec, "MediaCodec") class VideoDecoderMediaCodecPrivate Q_DECL_FINAL : public VideoDecoderFFmpegBasePrivate { public: }; VideoDecoderMediaCodec::VideoDecoderMediaCodec() : VideoDecoderFFmpegBase(*new VideoDecoderMediaCodecPrivate()) { setCodecName("h264_mediacodec"); av_jni_set_java_vm(QAndroidJniEnvironment::javaVM(), NULL); } VideoDecoderId VideoDecoderMediaCodec::id() const { return VideoDecoderId_MediaCodec; } QString VideoDecoderMediaCodec::description() const { return QStringLiteral("MediaCodec"); } VideoFrame VideoDecoderMediaCodec::frame() { DPTR_D(VideoDecoderMediaCodec); if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx) return VideoFrame(); // it's safe if width, height, pixfmt will not change, only data change VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt)); frame.setDisplayAspectRatio(d.getDAR(d.frame)); frame.setBits(d.frame->data); frame.setBytesPerLine(d.frame->linesize); // in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*) frame.setTimestamp((double)d.frame->pkt_pts/1000.0); frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame)))); d.updateColorDetails(&frame); return frame; } } //namespace QtAV #endif //QTAV_HAVE(MEDIACODEC) QtAV-1.12.0/src/codec/video/VideoDecoderVAAPI.cpp000066400000000000000000000733551312235004300212070ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegHW.h" #include "VideoDecoderFFmpegHW_p.h" #include #include #include #include #include #include extern "C" { #include } #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "vaapi/SurfaceInteropVAAPI.h" #include "utils/Logger.h" #define VERSION_CHK(major, minor, patch) \ (((major&0xff)<<16) | ((minor&0xff)<<8) | (patch&0xff)) //ffmpeg_vaapi patch: http://lists.libav.org/pipermail/libav-devel/2013-November/053515.html namespace QtAV { using namespace vaapi; namespace OpenGLHelper { bool isEGL(); #ifdef QT_NO_OPENGL bool isEGL() { return false;} #endif } class VideoDecoderVAAPIPrivate; class VideoDecoderVAAPI : public VideoDecoderFFmpegHW { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderVAAPI) Q_PROPERTY(bool derive READ derive WRITE setDerive) Q_PROPERTY(int surfaces READ surfaces WRITE setSurfaces) //Q_PROPERTY(QStringList displayPriority READ displayPriority WRITE setDisplayPriority) Q_PROPERTY(DisplayType display READ display WRITE setDisplay) Q_ENUMS(DisplayType) public: enum DisplayType { X11, GLX, DRM }; VideoDecoderVAAPI(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; // QObject properties void setDerive(bool y); bool derive() const; void setSurfaces(int num); int surfaces() const; void setDisplayPriority(const QStringList& priority); QStringList displayPriority() const; DisplayType display() const; void setDisplay(DisplayType disp); }; extern VideoDecoderId VideoDecoderId_VAAPI; FACTORY_REGISTER(VideoDecoder, VAAPI, "VAAPI") const char* getProfileName(AVCodecID id, int profile) { AVCodec *c = avcodec_find_decoder(id); if (!c) return "Unknow"; return av_get_profile_name(c, profile); } typedef struct { AVCodecID codec; int profile; VAProfile va_profile; //TODO: use an array like dxva does } codec_profile_t; #define VAProfileNone ((VAProfile)-1) //maybe not defined for old va static const codec_profile_t va_profiles[] = { { QTAV_CODEC_ID(MPEG1VIDEO), FF_PROFILE_UNKNOWN, VAProfileMPEG2Main }, //vlc { QTAV_CODEC_ID(MPEG2VIDEO), FF_PROFILE_MPEG2_MAIN, VAProfileMPEG2Main }, { QTAV_CODEC_ID(MPEG2VIDEO), FF_PROFILE_MPEG2_SIMPLE, VAProfileMPEG2Simple }, { QTAV_CODEC_ID(H263), FF_PROFILE_UNKNOWN, VAProfileH263Baseline }, //xbmc use mpeg4 { QTAV_CODEC_ID(MPEG4), FF_PROFILE_MPEG4_ADVANCED_SIMPLE, VAProfileMPEG4AdvancedSimple }, { QTAV_CODEC_ID(MPEG4), FF_PROFILE_MPEG4_MAIN, VAProfileMPEG4Main }, { QTAV_CODEC_ID(MPEG4), FF_PROFILE_MPEG4_SIMPLE, VAProfileMPEG4Simple }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_HIGH, VAProfileH264High }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_MAIN, VAProfileH264Main }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_BASELINE, VAProfileH264Baseline }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_BASELINE, VAProfileH264ConstrainedBaseline }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_BASELINE, VAProfileH264Main }, { QTAV_CODEC_ID(H264), FF_PROFILE_H264_CONSTRAINED_BASELINE, VAProfileH264ConstrainedBaseline }, //mpv force main { QTAV_CODEC_ID(H264), FF_PROFILE_H264_CONSTRAINED_BASELINE, VAProfileH264Baseline }, //yami { QTAV_CODEC_ID(H264), FF_PROFILE_H264_CONSTRAINED_BASELINE, VAProfileH264Main }, { QTAV_CODEC_ID(VC1), FF_PROFILE_VC1_ADVANCED, VAProfileVC1Advanced }, { QTAV_CODEC_ID(VC1), FF_PROFILE_VC1_MAIN, VAProfileVC1Main }, { QTAV_CODEC_ID(VC1), FF_PROFILE_VC1_SIMPLE, VAProfileVC1Simple }, { QTAV_CODEC_ID(WMV3), FF_PROFILE_VC1_ADVANCED, VAProfileVC1Advanced }, { QTAV_CODEC_ID(WMV3), FF_PROFILE_VC1_MAIN, VAProfileVC1Main }, { QTAV_CODEC_ID(WMV3), FF_PROFILE_VC1_SIMPLE, VAProfileVC1Simple }, #if VA_CHECK_VERSION(0, 38, 0) { QTAV_CODEC_ID(VP8), FF_PROFILE_UNKNOWN, VAProfileVP8Version0_3}, //defined in 0.37 { QTAV_CODEC_ID(VP9), FF_PROFILE_UNKNOWN, VAProfileVP9Profile0}, { QTAV_CODEC_ID(HEVC), FF_PROFILE_HEVC_MAIN, VAProfileHEVCMain}, { QTAV_CODEC_ID(HEVC), FF_PROFILE_HEVC_MAIN_10, VAProfileHEVCMain10}, #endif //VA_CHECK_VERSION(0, 38, 0) { QTAV_CODEC_ID(NONE), FF_PROFILE_UNKNOWN, VAProfileNone } }; static bool isProfileSupportedByRuntime(const VAProfile *profiles, int count, VAProfile p) { for (int i = 0; i < count; ++i) { if (profiles[i] == p) return true; } return false; } const codec_profile_t* findProfileEntry(AVCodecID codec, int profile, const codec_profile_t* p0 = NULL) { if (codec == QTAV_CODEC_ID(NONE)) return 0; if (p0 && p0->codec == QTAV_CODEC_ID(NONE)) //search from the end return 0; const codec_profile_t* pe0 = p0 ? ++p0 : va_profiles; for (const codec_profile_t* p = pe0; p < va_profiles+sizeof(va_profiles)/sizeof(va_profiles[0]); ++p) { if (codec != p->codec || profile != p->profile) continue; // return the first profile entry if given profile is unknow if (profile == FF_PROFILE_UNKNOWN || p->profile == FF_PROFILE_UNKNOWN // force the profile || profile == p->profile) return p; } return 0; } struct fmtentry { uint32_t va; VideoFormat::PixelFormat fmt; }; VideoFormat::PixelFormat pixelFormatFromVA(uint32_t fourcc) { switch (fourcc) { case VA_FOURCC_NV12: return VideoFormat::Format_NV12; case VA_FOURCC_YV12: case VA_FOURCC_IYUV: return VideoFormat::Format_YUV420P; case VA_FOURCC_UYVY: return VideoFormat::Format_UYVY; default: return VideoFormat::Format_Invalid; } return VideoFormat::Format_Invalid; } class VideoDecoderVAAPIPrivate Q_DECL_FINAL: public VideoDecoderFFmpegHWPrivate, protected VAAPI_DRM, protected VAAPI_X11 #ifndef QT_NO_OPENGL , protected VAAPI_GLX #endif //QT_NO_OPENGL { DPTR_DECLARE_PUBLIC(VideoDecoderVAAPI) public: VideoDecoderVAAPIPrivate() : support_4k(true) { if (VAAPI_DRM::isLoaded()) display_type = VideoDecoderVAAPI::DRM; if (VAAPI_X11::isLoaded()) display_type = VideoDecoderVAAPI::X11; #ifndef QT_NO_OPENGL if (display_type == VideoDecoderVAAPI::X11) { #if VA_X11_INTEROP copy_mode = VideoDecoderFFmpegHW::ZeroCopy; //TFP if va<0.38 #endif //VA_X11_INTEROP } #if QTAV_HAVE(EGL_CAPI) if (OpenGLHelper::isEGL()) { if (vaapi::checkEGL_DMA() && va_0_38::isValid()) copy_mode = VideoDecoderFFmpegHW::ZeroCopy; //for x11, drm, glx else if (vaapi::checkEGL_Pixmap() && display_type == VideoDecoderVAAPI::X11) copy_mode = VideoDecoderFFmpegHW::ZeroCopy; else copy_mode = VideoDecoderFFmpegHW::OptimizedCopy; } #endif //QTAV_HAVE(EGL_CAPI) #endif //QT_NO_OPENGL config_id = VA_INVALID_ID; context_id = VA_INVALID_ID; version_major = 0; version_minor = 0; surface_width = 0; surface_height = 0; image.image_id = VA_INVALID_ID; supports_derive = false; // set by user. don't reset in when call destroy nb_surfaces = 0; disable_derive = true; image_fmt = VideoFormat::Format_Invalid; } bool open() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE; bool ensureSurfaces(int count, int w, int h, bool discard_old = false); bool prepareVAImage(int w, int h); void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE; bool getBuffer(void **opaque, uint8_t **data) Q_DECL_OVERRIDE; void releaseBuffer(void *opaque, uint8_t *data) Q_DECL_OVERRIDE; AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { return QTAV_PIX_FMT_C(VAAPI_VLD); } bool support_4k; VideoDecoderVAAPI::DisplayType display_type; QList display_priority; display_ptr display; VAConfigID config_id; VAContextID context_id; struct vaapi_context hw_ctx; int version_major; int version_minor; int surface_width; int surface_height; int nb_surfaces; QVector surfaces; std::list surfaces_free, surfaces_used; VAImage image; bool disable_derive; bool supports_derive; VideoFormat::PixelFormat image_fmt; QString vendor; #ifndef QT_NO_OPENGL InteropResourcePtr interop_res; //may be still used in video frames when decoder is destroyed #endif }; VideoDecoderVAAPI::VideoDecoderVAAPI() : VideoDecoderFFmpegHW(*new VideoDecoderVAAPIPrivate()) { setDisplayPriority(QStringList() << QStringLiteral("X11") << QStringLiteral("DRM") << QStringLiteral("GLX")); // dynamic properties about static property details. used by UI // format: detail_property setProperty("detail_surfaces", tr("Decoding surfaces") + QStringLiteral(" ") + tr("0: auto")); setProperty("detail_derive", tr("Maybe faster")); setProperty("detail_display", QString("%1\n%2\n%3") .arg("X11: libva-x11.so is required") .arg("GLX: libva-glx.so is required") .arg("DRM: Support 0-copy only with EGL. May work without X11. libva-drm.so is required") ); } VideoDecoderId VideoDecoderVAAPI::id() const { return VideoDecoderId_VAAPI; } QString VideoDecoderVAAPI::description() const { if (!d_func().description.isEmpty()) return d_func().description; return QStringLiteral("Video Acceleration API"); } void VideoDecoderVAAPI::setDerive(bool y) { d_func().disable_derive = !y; } bool VideoDecoderVAAPI::derive() const { return !d_func().disable_derive; } void VideoDecoderVAAPI::setSurfaces(int num) { DPTR_D(VideoDecoderVAAPI); d.nb_surfaces = num; const int kMaxSurfaces = 32; if (num > kMaxSurfaces) { qWarning("VAAPI- Too many surfaces. requested: %d, maximun: %d", num, kMaxSurfaces); } } int VideoDecoderVAAPI::surfaces() const { return d_func().nb_surfaces; } VideoDecoderVAAPI::DisplayType VideoDecoderVAAPI::display() const { return d_func().display_type; } void VideoDecoderVAAPI::setDisplay(DisplayType disp) { DPTR_D(VideoDecoderVAAPI); d.display_priority.clear(); d.display_priority.append(disp); d.display_type = disp; } extern ColorSpace colorSpaceFromFFmpeg(AVColorSpace cs); VideoFrame VideoDecoderVAAPI::frame() { DPTR_D(VideoDecoderVAAPI); if (!d.frame->opaque || !d.frame->data[0]) return VideoFrame(); VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3]; VAStatus status = VA_STATUS_SUCCESS; #ifndef QT_NO_OPENGL if (copyMode() == ZeroCopy) { surface_ptr p; std::list::iterator it = d.surfaces_used.begin(); for (; it != d.surfaces_used.end() && !p; ++it) { if((*it)->get() == surface_id) { p = *it; break; } } if (!p) { for (it = d.surfaces_free.begin(); it != d.surfaces_free.end() && !p; ++it) { if((*it)->get() == surface_id) { p = *it; break; } } } if (!p) { qWarning("VAAPI - Unable to find surface"); return VideoFrame(); } SurfaceInteropVAAPI *interop = new SurfaceInteropVAAPI(d.interop_res); interop->setSurface(p, d.width, d.height); VideoFormat fmt(VideoFormat::Format_RGB32); //TODO: nv12 VAImage img; // TODO: derive/get image only once and pass to interop object const bool test_format = OpenGLHelper::isEGL() && vaapi::checkEGL_DMA() && va_0_38::isValid(); if (test_format) { vaDeriveImage(d.display->get(), p->get(), &img); fmt = pixelFormatFromVA(img.format.fourcc); //qDebug() << fmt;//pixelFormatFromVA(img.format.fourcc); } VideoFrame f(d.width, d.height, fmt); // img.pitches[i] is 16 aligned f.setBytesPerLine(d.width*fmt.bytesPerPixel(0), 0); //used by gl to compute texture size if (test_format) { for (int i = 1; i < fmt.planeCount(); ++i) { // img.pitchs[] are 16 aligned f.setBytesPerLine(f.bytesPerLine(0)/(img.pitches[i-1]/(img.pitches[i]-15)), i); } // if not destroyed, error 'surface is in use' VAWARN(vaDestroyImage(d.display->get(), img.image_id)); } f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop))); f.setTimestamp(double(d.frame->pkt_pts)/1000.0); f.setDisplayAspectRatio(d.getDAR(d.frame)); d.updateColorDetails(&f); const ColorSpace cs = f.colorSpace(); if (cs == ColorSpace_BT601) p->setColorSpace(VA_SRC_BT601); else p->setColorSpace(VA_SRC_BT709); return f; } #endif //QT_NO_OPENGL #if VA_CHECK_VERSION(0,31,0) if ((status = vaSyncSurface(d.display->get(), surface_id)) != VA_STATUS_SUCCESS) { qWarning("vaSyncSurface(VADisplay:%p, VASurfaceID:%#x) == %#x", d.display->get(), surface_id, status); #else if (vaSyncSurface(d.display->get(), d.context_id, surface_id)) { qWarning("vaSyncSurface(VADisplay:%#x, VAContextID:%#x, VASurfaceID:%#x) == %#x", d.display, d.context_id, surface_id, status); #endif return VideoFrame(); } if (!d.disable_derive && d.supports_derive) { /* * http://web.archiveorange.com/archive/v/OAywENyq88L319OcRnHI * vaDeriveImage is faster than vaGetImage. But VAImage is uncached memory and copying from it would be terribly slow * TODO: copy from USWC, see vlc and https://github.com/OpenELEC/OpenELEC.tv/pull/2937.diff * https://software.intel.com/en-us/articles/increasing-memory-throughput-with-intel-streaming-simd-extensions-4-intel-sse4-streaming-load */ VA_ENSURE_TRUE(vaDeriveImage(d.display->get(), surface_id, &d.image), VideoFrame()); } else { VA_ENSURE_TRUE(vaGetImage(d.display->get(), surface_id, 0, 0, d.surface_width, d.surface_height, d.image.image_id), VideoFrame()); } void *p_base; VA_ENSURE_TRUE(vaMapBuffer(d.display->get(), d.image.buf, &p_base), VideoFrame()); VideoFormat::PixelFormat pixfmt = pixelFormatFromVA(d.image.format.fourcc); bool swap_uv = (d.disable_derive || !d.supports_derive || d.image.format.fourcc == VA_FOURCC_IYUV) && d.image.format.fourcc != VA_FOURCC_NV12; if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", d.image.format.fourcc); return VideoFrame(); } const VideoFormat fmt(pixfmt); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { src[i] = (uint8_t*)p_base + d.image.offsets[i]; pitch[i] = d.image.pitches[i]; } VideoFrame frame(copyToFrame(fmt, d.surface_height, src, pitch, swap_uv)); VAWARN(vaUnmapBuffer(d.display->get(), d.image.buf)); if (!d.disable_derive && d.supports_derive) { VAWARN(vaDestroyImage(d.display->get(), d.image.image_id)); d.image.image_id = VA_INVALID_ID; } return frame; } void VideoDecoderVAAPI::setDisplayPriority(const QStringList &priority) { DPTR_D(VideoDecoderVAAPI); d.display_priority.clear(); int idx = staticMetaObject.indexOfEnumerator("DisplayType"); const QMetaEnum me = staticMetaObject.enumerator(idx); foreach (const QString& disp, priority) { d.display_priority.push_back((DisplayType)me.keyToValue(disp.toUtf8().constData())); } } QStringList VideoDecoderVAAPI::displayPriority() const { QStringList names; int idx = staticMetaObject.indexOfEnumerator("DisplayType"); const QMetaEnum me = staticMetaObject.enumerator(idx); foreach (DisplayType disp, d_func().display_priority) { names.append(QString::fromLatin1(me.valueToKey(disp))); } return names; } bool VideoDecoderVAAPIPrivate::open() { if (!prepare()) return false; const codec_profile_t* pe = findProfileEntry(codec_ctx->codec_id, codec_ctx->profile); // TODO: allow wrong profile // FIXME: sometimes get wrong profile (switch copyMode) if (!pe) { qWarning("codec(%s) or profile(%s) is not supported", avcodec_get_name(codec_ctx->codec_id), getProfileName(codec_ctx->codec_id, codec_ctx->profile)); return false; } /* Create a VA display */ foreach (VideoDecoderVAAPI::DisplayType dt, display_priority) { NativeDisplay nd; if (dt == VideoDecoderVAAPI::DRM) { nd.type = NativeDisplay::DRM; } else if (dt == VideoDecoderVAAPI::X11) { nd.type = NativeDisplay::X11; } else if (dt == VideoDecoderVAAPI::GLX) { nd.type = NativeDisplay::GLX; } display = display_t::create(nd); if (display) { display_type = dt; break; } } if (!display/* || vaDisplayIsValid(display->get()) != 0*/) { qWarning("Could not get a VAAPI device"); return false; } display->getVersion(&version_major, &version_minor); vendor = QString::fromLatin1(vaQueryVendorString(display->get())); //if (!vendor.toLower().contains(QLatin1Strin("intel"))) // copy_uswc = false; //disable_derive = !copy_uswc; description = QObject::tr("VA API version %1.%2; Vendor: %3;").arg(version_major).arg(version_minor).arg(vendor); DPTR_P(VideoDecoderVAAPI); int idx = p.staticMetaObject.indexOfEnumerator("DisplayType"); const QMetaEnum me = p.staticMetaObject.enumerator(idx); description += QStringLiteral(" Display: ") + QString::fromLatin1(me.valueToKey(display_type)); // check 4k support. from xbmc int major, minor, micro; if (sscanf(vendor.toUtf8().constData(), "Intel i965 driver - %d.%d.%d", &major, &minor, µ) == 3) { /* older version will crash and burn */ if (VERSION_CHK(major, minor, micro) < VERSION_CHK(1, 0, 17)) { qDebug("VAAPI - deinterlace not support on this intel driver version"); } // do the same check for 4K decoding: version < 1.2.0 (stable) and 1.0.21 (staging) // cannot decode 4K and will crash the GPU if (VERSION_CHK(major, minor, micro) < VERSION_CHK(1, 2, 0) && VERSION_CHK(major, minor, micro) < VERSION_CHK(1, 0, 21)) { support_4k = false; } } if (!support_4k && (codec_ctx->width > 1920 || codec_ctx->height > 1088)) { qWarning("VAAPI: frame size (%dx%d) is too large", codec_ctx->width, codec_ctx->height); return false; } /* Check if the selected profile is supported */ qDebug("checking profile: %s, %s", avcodec_get_name(codec_ctx->codec_id), getProfileName(pe->codec, pe->profile)); int nb_profiles = vaMaxNumProfiles(display->get()); if (nb_profiles <= 0) { qWarning("No profile supported"); return false; } QVector supported_profiles(nb_profiles, VAProfileNone); VA_ENSURE_TRUE(vaQueryConfigProfiles(display->get(), supported_profiles.data(), &nb_profiles), false); while (pe && !isProfileSupportedByRuntime(supported_profiles.constData(), nb_profiles, pe->va_profile)) { qDebug("Codec or profile %s %d is not directly supported by the hardware. Checking alternative profiles", vaapi::profileName(pe->va_profile), pe->va_profile); pe = findProfileEntry(codec_ctx->codec_id, codec_ctx->profile, pe); } if (!pe) { qDebug("Codec or profile is not supported by the hardware."); return false; } qDebug("using profile %s (%d)", vaapi::profileName(pe->va_profile), pe->va_profile); /* Create a VA configuration */ VAConfigAttrib attrib; memset(&attrib, 0, sizeof(attrib)); attrib.type = VAConfigAttribRTFormat; VA_ENSURE_TRUE(vaGetConfigAttributes(display->get(), pe->va_profile, VAEntrypointVLD, &attrib, 1), false); /* Not sure what to do if not, I don't have a way to test */ if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) return false; config_id = VA_INVALID_ID; VA_ENSURE_TRUE(vaCreateConfig(display->get(), pe->va_profile, VAEntrypointVLD, &attrib, 1, &config_id), false); supports_derive = false; width = codec_ctx->width; height = codec_ctx->height; surface_width = codedWidth(codec_ctx); surface_height = codedHeight(codec_ctx); qDebug("checking surface resolution support"); VASurfaceID test_surface = VA_INVALID_ID; VA_ENSURE_TRUE(vaCreateSurfaces(display->get(), VA_RT_FORMAT_YUV420, surface_width, surface_height, &test_surface, 1, NULL, 0), false); // context create fail but surface create ok (tested 6k for intel) context_id = VA_INVALID_ID; VA_ENSURE_TRUE(vaCreateContext(display->get(), config_id, surface_width, surface_height, VA_PROGRESSIVE, surfaces.data(), surfaces.size(), &context_id), false); VAWARN(vaDestroyContext(display->get(), context_id)); VAWARN(vaDestroySurfaces(display->get(), &test_surface, 1)); context_id = VA_INVALID_ID; #ifndef QT_NO_OPENGL if (display_type == VideoDecoderVAAPI::GLX) interop_res = InteropResourcePtr(new GLXInteropResource()); #if VA_X11_INTEROP if (display_type == VideoDecoderVAAPI::X11) //if egl and !tfp(va>=0.38), use EGLInteropResource interop_res = InteropResourcePtr(new X11InteropResource()); #endif //VA_X11_INTEROP #if QTAV_HAVE(EGL_CAPI) if (OpenGLHelper::isEGL()) { if (va_0_38::isValid() && vaapi::checkEGL_DMA()) interop_res = InteropResourcePtr(new EGLInteropResource()); } #endif //QTAV_HAVE(EGL_CAPI) #endif //QT_NO_OPENGL return true; } bool VideoDecoderVAAPIPrivate::ensureSurfaces(int count, int w, int h, bool discard_old) { if (!display) { qWarning("no va display"); return false; } qDebug("ensureSurfaces %d->%d %dx%d. discard old surfaces: %d", surfaces.size(), count, w, h, discard_old); Q_ASSERT(w > 0 && h > 0); const int old_size = discard_old ? 0 : surfaces.size(); if (count <= old_size) return true; surfaces.resize(old_size); // clear the old surfaces if discard_old. when initializing va-api, we must discard old surfaces (vdpau_video.c:595: vdpau_CreateContext: Assertion `obj_surface->va_context == 0xffffffff' failed.) surfaces.resize(count); VA_ENSURE_TRUE(vaCreateSurfaces(display->get(), VA_RT_FORMAT_YUV420, w, h, surfaces.data() + old_size, count - old_size, NULL, 0), false); for (int i = old_size; i < surfaces.size(); ++i) { //qDebug("surface id: %p %dx%d", surfaces.at(i), w, height); surfaces_free.push_back(surface_ptr(new surface_t(w, h, surfaces[i], display))); } return true; } bool VideoDecoderVAAPIPrivate::prepareVAImage(int w, int h) { image.image_id = VA_INVALID_ID; static const unsigned int fcc[] = { VA_FOURCC_NV12, VA_FOURCC_YV12, VA_FOURCC_IYUV, 0}; va_new_image(display->get(), fcc, &image, w, h, surfaces[0]); if (image.image_id == VA_INVALID_ID) return false; image_fmt = pixelFormatFromVA(image.format.fourcc); VAImage test_image; if (!disable_derive || copy_mode == VideoDecoderVAAPI::ZeroCopy) { if (vaDeriveImage(display->get(), surfaces[0], &test_image) == VA_STATUS_SUCCESS) { qDebug("vaDeriveImage supported"); supports_derive = true; image_fmt = pixelFormatFromVA(image.format.fourcc); /* from vlc: Use vaDerive() iif it supports the best selected format */ if (image.format.fourcc == test_image.format.fourcc) { qDebug("vaDerive is ok"); // supports_derive = true; } VAWARN(vaDestroyImage(display->get(), test_image.image_id)); } if (supports_derive) { VAWARN(vaDestroyImage(display->get(), image.image_id)); image.image_id = VA_INVALID_ID; } } return true; } void* VideoDecoderVAAPIPrivate::setup(AVCodecContext *avctx) { Q_UNUSED(avctx); if (!display || config_id == VA_INVALID_ID) { qWarning("va-api is not initialized. display: %p, config_id: %#x", display->get(), config_id); return NULL; } int surface_count = nb_surfaces; if (surface_count <= 0) { surface_count = 2+1; qDebug("guess surface count"); if (codec_ctx->codec_id == QTAV_CODEC_ID(H264)) surface_count = 16+2; #ifdef FF_PROFILE_HEVC_MAIN if (codec_ctx->codec_id == QTAV_CODEC_ID(HEVC)) surface_count = 16+2; #endif // TODO: vp8,9 if (codec_ctx->active_thread_type & FF_THREAD_FRAME) surface_count += codec_ctx->thread_count; } releaseUSWC(); if (image.image_id != VA_INVALID_ID) { VAWARN(vaDestroyImage(display->get(), image.image_id)); image.image_id = VA_INVALID_ID; } if (context_id != VA_INVALID_ID) { VAWARN(vaDestroyContext(display->get(), context_id)); context_id = VA_INVALID_ID; } // TODO: config_id reset? if (!ensureSurfaces(surface_count, surface_width, surface_height, true)) return NULL; if (copy_mode != VideoDecoderFFmpegHW::ZeroCopy || OpenGLHelper::isEGL()) { //egl_dma && va_0_38 // egl needs VAImage too if (!prepareVAImage(surface_width, surface_height)) return NULL; } initUSWC(surface_width); VA_ENSURE_TRUE(vaCreateContext(display->get(), config_id, surface_width, surface_height, VA_PROGRESSIVE, surfaces.data(), surfaces.size(), &context_id), NULL); /* Setup the ffmpeg hardware context */ memset(&hw_ctx, 0, sizeof(hw_ctx)); hw_ctx.display = display->get(); hw_ctx.config_id = config_id; hw_ctx.context_id = context_id; return &hw_ctx; } void VideoDecoderVAAPIPrivate::close() { restore(); // do not call vaDestroySurfaces here because they are destroyed by surface_t if (image.image_id != VA_INVALID_ID) { VAWARN(vaDestroyImage(display->get(), image.image_id)); image.image_id = VA_INVALID_ID; } if (context_id != VA_INVALID_ID) { VAWARN(vaDestroyContext(display->get(), context_id)); context_id = VA_INVALID_ID; } if (config_id != VA_INVALID_ID) { VAWARN(vaDestroyConfig(display->get(), config_id)); config_id = VA_INVALID_ID; } display.clear(); releaseUSWC(); nb_surfaces = 0; surfaces.clear(); surfaces_free.clear(); surfaces_used.clear(); surface_width = 0; surface_height = 0; } bool VideoDecoderVAAPIPrivate::getBuffer(void **opaque, uint8_t **data) { VASurfaceID id = (VASurfaceID)(quintptr)*data; // id is always 0? std::list::iterator it = surfaces_free.begin(); if (id && id != VA_INVALID_ID) { bool found = false; for (; it != surfaces_free.end(); ++it) { //? if ((*it)->get() == id) { found = true; break; } } if (!found) { qWarning("surface not found!!!!!!!!!!!!!"); return false; } } else { for (; it != surfaces_free.end() && it->count() > 1; ++it) {} if (it == surfaces_free.end()) { if (!surfaces_free.empty()) qWarning("VAAPI - renderer still using all freed up surfaces by decoder. unable to find free surface, trying to allocate a new one"); const int kMaxSurfaces = 32; if (surfaces.size() + 1 > kMaxSurfaces) { qWarning("VAAPI- Too many surfaces. requested: %d, maximun: %d", surfaces.size() + 1, kMaxSurfaces); } // Set itarator position to the newly allocated surface (end-1) const int old_size = surfaces.size(); if (!ensureSurfaces(old_size + 1, surface_width, surface_height, false)) { // destroy the new created surface. Surfaces can only be destroyed after the context associated has been destroyed? VAWARN(vaDestroySurfaces(display->get(), surfaces.data() + old_size, 1)); surfaces.resize(old_size); } it = surfaces_free.end(); --it; } } id =(*it)->get(); surface_t *s = it->get(); // !erase later. otherwise it may be destroyed surfaces_used.push_back(*it); // TODO: why QList may erase an invalid iterator(first iterator)at the same position? surfaces_free.erase(it); //ref not increased, but can not be used. *data = (uint8_t*)(quintptr)id; *opaque = s; return true; } void VideoDecoderVAAPIPrivate::releaseBuffer(void *opaque, uint8_t *data) { Q_UNUSED(opaque); VASurfaceID id = (VASurfaceID)(uintptr_t)data; for (std::list::iterator it = surfaces_used.begin(); it != surfaces_used.end(); ++it) { if((*it)->get() == id) { surfaces_free.push_back(*it); surfaces_used.erase(it); break; } } } } // namespace QtAV // used by .moc QMetaType::Bool #ifdef Bool #undef Bool #endif #include "VideoDecoderVAAPI.moc" QtAV-1.12.0/src/codec/video/VideoDecoderVDA.cpp000077500000000000000000000403171312235004300207540ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegHW.h" #include "VideoDecoderFFmpegHW_p.h" #include "utils/GPUMemCopy.h" #include "QtAV/SurfaceInterop.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "opengl/OpenGLHelper.h" #include #ifdef __cplusplus extern "C" { #endif //__cplusplus #include #ifdef __cplusplus } #endif //__cplusplus #include #include "utils/Logger.h" // av_vda_default_init2: 2015-05-13 - cc48409 / e7c5e17 - lavc 56.39.100 / 56.23.0 // I(wang bin) found the nv12 is the best format on mac. Then mpv developers confirmed it and pigoz added it to ffmpeg #if AV_MODULE_CHECK(LIBAVCODEC, 56, 23, 0, 39, 100) #define AV_VDA_NEW #endif #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 //MAC_OS_X_VERSION_10_7 #define OSX_TARGET_MIN_LION #endif // 1070 #endif //MAC_OS_X_VERSION_MIN_REQUIRED #ifndef kCFCoreFoundationVersionNumber10_7 #define kCFCoreFoundationVersionNumber10_7 635.00 #endif // hwaccel 1.2: av_vda_xxx namespace QtAV { namespace cv { VideoFormat::PixelFormat format_from_cv(int cv); } class VideoDecoderVDAPrivate; // qt4 moc can not correctly process Q_DECL_FINAL here class VideoDecoderVDA : public VideoDecoderFFmpegHW { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderVDA) Q_PROPERTY(PixelFormat format READ format WRITE setFormat NOTIFY formatChanged) // TODO: try async property Q_ENUMS(PixelFormat) public: enum PixelFormat { NV12 = '420f', UYVY = '2vuy', YUV420P = 'y420', YUYV = 'yuvs' }; VideoDecoderVDA(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; // QObject properties void setFormat(PixelFormat fmt); PixelFormat format() const; Q_SIGNALS: void formatChanged(); }; extern VideoDecoderId VideoDecoderId_VDA; FACTORY_REGISTER(VideoDecoder, VDA, "VDA") class VideoDecoderVDAPrivate Q_DECL_FINAL: public VideoDecoderFFmpegHWPrivate { public: VideoDecoderVDAPrivate() : VideoDecoderFFmpegHWPrivate() , out_fmt(VideoDecoderVDA::NV12) { if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7) out_fmt = VideoDecoderVDA::UYVY; copy_mode = VideoDecoderFFmpegHW::ZeroCopy; description = QStringLiteral("VDA"); #ifndef AV_VDA_NEW memset(&hw_ctx, 0, sizeof(hw_ctx)); #endif } ~VideoDecoderVDAPrivate() {qDebug("~VideoDecoderVDAPrivate");} bool open() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE; void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE; bool getBuffer(void **opaque, uint8_t **data) Q_DECL_OVERRIDE; void releaseBuffer(void *opaque, uint8_t *data) Q_DECL_OVERRIDE; AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { #ifdef AV_VDA_NEW return AV_PIX_FMT_VDA; #else return QTAV_PIX_FMT_C(VDA_VLD); #endif } VideoDecoderVDA::PixelFormat out_fmt; #ifndef AV_VDA_NEW struct vda_context hw_ctx; #endif }; typedef struct { int code; const char *str; } vda_error; static const vda_error vda_errors[] = { { kVDADecoderHardwareNotSupportedErr, "Hardware doesn't support accelerated decoding" }, { kVDADecoderFormatNotSupportedErr, "Hardware doesn't support requested output format" }, { kVDADecoderConfigurationError, "Invalid configuration provided to VDADecoderCreate" }, { kVDADecoderDecoderFailedErr, "Generic error returned by the decoder layer. The cause can range from" " VDADecoder finding errors in the bitstream to another application" " using VDA at the moment. Only one application can use VDA at a" " givent time." }, { 0, NULL }, }; static const char* vda_err_str(int err) { for (int i = 0; vda_errors[i].code; ++i) { if (vda_errors[i].code != err) continue; return vda_errors[i].str; } return 0; } VideoDecoderVDA::VideoDecoderVDA() : VideoDecoderFFmpegHW(*new VideoDecoderVDAPrivate()) { // dynamic properties about static property details. used by UI setProperty("detail_format", tr("Output pixel format from decoder. Performance NV12 > UYVY > YUV420P > YUYV.\nOSX < 10.7 only supports UYVY and YUV420p")); } VideoDecoderId VideoDecoderVDA::id() const { return VideoDecoderId_VDA; } QString VideoDecoderVDA::description() const { return QStringLiteral("Video Decode Acceleration"); } VideoFrame VideoDecoderVDA::frame() { DPTR_D(VideoDecoderVDA); CVPixelBufferRef cv_buffer = (CVPixelBufferRef)d.frame->data[3]; if (!cv_buffer) { qDebug("Frame buffer is empty."); return VideoFrame(); } if (CVPixelBufferGetDataSize(cv_buffer) <= 0) { qDebug("Empty frame buffer"); return VideoFrame(); } VideoFormat::PixelFormat pixfmt = cv::format_from_cv(CVPixelBufferGetPixelFormatType(cv_buffer)); if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vda pixel format: %#x", CVPixelBufferGetPixelFormatType(cv_buffer)); return VideoFrame(); } // we can map the cv buffer addresses to video frame in SurfaceInteropCVBuffer. (may need VideoSurfaceInterop::mapToTexture() class SurfaceInteropCVBuffer Q_DECL_FINAL: public VideoSurfaceInterop { bool glinterop; CVPixelBufferRef cvbuf; // keep ref until video frame is destroyed public: SurfaceInteropCVBuffer(CVPixelBufferRef cv, bool gl) : glinterop(gl), cvbuf(cv) { #ifdef AV_VDA_NEW CVPixelBufferRetain(cvbuf); #endif } ~SurfaceInteropCVBuffer() { CVPixelBufferRelease(cvbuf); } void* mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); CVPixelBufferLockBaseAddress(cvbuf, kCVPixelBufferLock_ReadOnly); const VideoFormat fmt(cv::format_from_cv(CVPixelBufferGetPixelFormatType(cvbuf))); if (!fmt.isValid()) { CVPixelBufferUnlockBaseAddress(cvbuf, kCVPixelBufferLock_ReadOnly); return NULL; } const int w = CVPixelBufferGetWidth(cvbuf); const int h = CVPixelBufferGetHeight(cvbuf); uint8_t *src[3]; int pitch[3]; for (int i = 0; i (handle); frame.setTimestamp(f->timestamp()); frame.setDisplayAspectRatio(f->displayAspectRatio()); *f = frame; return f; } virtual void* map(SurfaceType type, const VideoFormat& fmt, void* handle = 0, int plane = 0) Q_DECL_OVERRIDE { Q_UNUSED(fmt); if (!glinterop) return 0; if (type == HostMemorySurface) { return mapToHost(fmt, handle, plane); } if (type == SourceSurface) { CVPixelBufferRef *b = reinterpret_cast(handle); *b = cvbuf; return handle; } if (type != GLTextureSurface) return 0; // https://www.opengl.org/registry/specs/APPLE/rgb_422.txt // TODO: check extension GL_APPLE_rgb_422 and rectangle? IOSurfaceRef surface = CVPixelBufferGetIOSurface(cvbuf); int w = IOSurfaceGetWidthOfPlane(surface, plane); int h = IOSurfaceGetHeightOfPlane(surface, plane); //qDebug("plane:%d, iosurface %dx%d, ctx: %p", plane, w, h, CGLGetCurrentContext()); OSType pixfmt = IOSurfaceGetPixelFormat(surface); //CVPixelBufferGetPixelFormatType(cvbuf); GLenum iformat = GL_RGBA8; GLenum format = GL_BGRA; GLenum dtype = GL_UNSIGNED_INT_8_8_8_8_REV; const GLenum target = GL_TEXTURE_RECTANGLE; // TODO: GL_RED, GL_RG is better for gl >= 3.0 if (pixfmt == NV12) { dtype = GL_UNSIGNED_BYTE; if (plane == 0) { iformat = format = OpenGLHelper::useDeprecatedFormats() ? GL_LUMINANCE : GL_RED; } else { iformat = format = OpenGLHelper::useDeprecatedFormats() ? GL_LUMINANCE_ALPHA : GL_RG; } } else if (pixfmt == UYVY || pixfmt == YUYV) { w /= 2; //rgba texture } else if (pixfmt == YUV420P) { dtype = GL_UNSIGNED_BYTE; iformat = format = OpenGLHelper::useDeprecatedFormats() ? GL_LUMINANCE : GL_RED; if (plane > 1 && format == GL_LUMINANCE) iformat = format = GL_ALPHA; } DYGL(glBindTexture(target, *((GLuint*)handle))); CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), target, iformat, w, h, format, dtype, surface, plane); if (err != kCGLNoError) { qWarning("error creating IOSurface texture at plane %d: %s", plane, CGLErrorString(err)); } DYGL(glBindTexture(target, 0)); return handle; } void* createHandle(void* handle, SurfaceType type, const VideoFormat &fmt, int plane, int planeWidth, int planeHeight) Q_DECL_OVERRIDE { Q_UNUSED(type); Q_UNUSED(fmt); Q_UNUSED(plane); Q_UNUSED(planeWidth); Q_UNUSED(planeHeight); if (!glinterop) return 0; GLuint *tex = (GLuint*)handle; DYGL(glGenTextures(1, tex)); // no init required return handle; } }; uint8_t *src[3]; int pitch[3]; const bool zero_copy = copyMode() == VideoDecoderFFmpegHW::ZeroCopy; if (zero_copy) { // make sure VideoMaterial can correctly setup parameters switch (format()) { case UYVY: pitch[0] = 2*d.width; pixfmt = VideoFormat::Format_VYUY; //FIXME: VideoShader assume uyvy is uploaded as rgba, but apple limits the result to bgra break; case NV12: pitch[0] = d.width; pitch[1] = d.width; break; case YUV420P: pitch[0] = d.width; pitch[1] = pitch[2] = d.width/2; break; case YUYV: pitch[0] = 2*d.width; // //pixfmt = VideoFormat::Format_YVYU; // break; default: break; } } const VideoFormat fmt(pixfmt); if (!zero_copy) { CVPixelBufferLockBaseAddress(cv_buffer, 0); for (int i = 0; i pkt_pts)/1000.0); f.setDisplayAspectRatio(d.getDAR(d.frame)); if (zero_copy) { f.setMetaData(QStringLiteral("target"), QByteArrayLiteral("rect")); } else { f.setBits(src); // only set for copy back mode } d.updateColorDetails(&f); } else { f = copyToFrame(fmt, d.height, src, pitch, false); } f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(new SurfaceInteropCVBuffer(cv_buffer, zero_copy)))); return f; } void VideoDecoderVDA::setFormat(PixelFormat fmt) { DPTR_D(VideoDecoderVDA); if (d.out_fmt == fmt) return; d.out_fmt = fmt; emit formatChanged(); if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_7) return; if (fmt != YUV420P && fmt != UYVY) qWarning("format is not supported on OSX < 10.7"); } VideoDecoderVDA::PixelFormat VideoDecoderVDA::format() const { return d_func().out_fmt; } void* VideoDecoderVDAPrivate::setup(AVCodecContext *avctx) { releaseUSWC(); #ifdef AV_VDA_NEW av_vda_default_free(avctx); AVVDAContext* hw_ctx = av_vda_alloc_context(); hw_ctx->cv_pix_fmt_type = out_fmt; int status = av_vda_default_init2(avctx, hw_ctx); #else const int w = codedWidth(avctx); const int h = codedHeight(avctx); if (hw_ctx.decoder) { ff_vda_destroy_decoder(&hw_ctx); } else { memset(&hw_ctx, 0, sizeof(hw_ctx)); hw_ctx.format = 'avc1'; //fourcc hw_ctx.cv_pix_fmt_type = out_fmt; // has the same value as cv pixel format } /* Setup the libavcodec hardware context */ hw_ctx.width = w; hw_ctx.height = h; width = avctx->width; // not necessary. set in decode() height = avctx->height; /* create the decoder */ int status = ff_vda_create_decoder(&hw_ctx, codec_ctx->extradata, codec_ctx->extradata_size); #endif if (status) { qWarning("Failed to init VDA decoder (%#x %s): %s", status, av_err2str(status), vda_err_str(status)); return NULL; } initUSWC(codedWidth(avctx)); qDebug() << "VDA decoder created. format: " << cv::format_from_cv(out_fmt); #ifdef AV_VDA_NEW return hw_ctx; #else return &hw_ctx; #endif } bool VideoDecoderVDAPrivate::getBuffer(void **opaque, uint8_t **data) { *data = (uint8_t *)1; // dummy. it's AVFrame.data[0], must be non null required by ffmpeg Q_UNUSED(opaque); return true; } void VideoDecoderVDAPrivate::releaseBuffer(void *opaque, uint8_t *data) { Q_UNUSED(opaque); Q_UNUSED(data) } bool VideoDecoderVDAPrivate::open() { if (!prepare()) return false; qDebug("opening VDA module"); if (codec_ctx->codec_id != AV_CODEC_ID_H264) { qWarning("input codec (%s) isn't H264, canceling VDA decoding", avcodec_get_name(codec_ctx->codec_id)); return false; } switch (codec_ctx->profile) { //profile check code is from xbmc case FF_PROFILE_H264_HIGH_10: case FF_PROFILE_H264_HIGH_10_INTRA: case FF_PROFILE_H264_HIGH_422: case FF_PROFILE_H264_HIGH_422_INTRA: case FF_PROFILE_H264_HIGH_444_PREDICTIVE: case FF_PROFILE_H264_HIGH_444_INTRA: case FF_PROFILE_H264_CAVLC_444: return false; default: break; } return true; //return setup(codec_ctx); // must be in get_format() in hwa 1.2 } void VideoDecoderVDAPrivate::close() { restore(); //IMPORTANT. can not call restore in dtor because ctx is 0 there qDebug("destroying VDA decoder"); #ifdef AV_VDA_NEW if (codec_ctx) av_vda_default_free(codec_ctx); #else ff_vda_destroy_decoder(&hw_ctx); #endif releaseUSWC(); } } //namespace QtAV #include "VideoDecoderVDA.moc" QtAV-1.12.0/src/codec/video/VideoDecoderVideoToolbox.cpp000077500000000000000000000306101312235004300227520ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "VideoDecoderFFmpegHW.h" #include "VideoDecoderFFmpegHW_p.h" #include "utils/GPUMemCopy.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/factory.h" #include "SurfaceInteropCV.h" //#include #include #ifdef __cplusplus extern "C" { #endif //__cplusplus #include #ifdef __cplusplus } #endif //__cplusplus #include "utils/Logger.h" #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 //MAC_OS_X_VERSION_10_7 #define OSX_TARGET_MIN_LION #endif // 1070 #endif //MAC_OS_X_VERSION_MIN_REQUIRED #ifndef kCFCoreFoundationVersionNumber10_7 #define kCFCoreFoundationVersionNumber10_7 635.00 #endif //kCVPixelBufferOpenGLESCompatibilityKey //ios6 namespace QtAV { class VideoDecoderVideoToolboxPrivate; // qt4 moc can not correctly process Q_DECL_FINAL here class VideoDecoderVideoToolbox : public VideoDecoderFFmpegHW { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoDecoderVideoToolbox) Q_PROPERTY(PixelFormat format READ format WRITE setFormat NOTIFY formatChanged) Q_PROPERTY(Interop interop READ interop WRITE setInterop NOTIFY interopChanged) // TODO: try async property Q_ENUMS(PixelFormat) Q_ENUMS(Interop) public: enum PixelFormat { NV12 = '420v', UYVY = '2vuy', BGRA = 'BGRA', //kCVPixelFormatType_32BGRA YUV420P = 'y420', YUYV = 'yuvs', }; // no ios macro check in Interop because moc is an idiot enum Interop { CVPixelBuffer = cv::InteropCVPixelBuffer, CVOpenGLES = cv::InteropCVOpenGLES, IOSurface = cv::InteropIOSurface, Auto = cv::InteropAuto }; VideoDecoderVideoToolbox(); VideoDecoderId id() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; VideoFrame frame() Q_DECL_OVERRIDE; // QObject properties void setFormat(PixelFormat fmt); PixelFormat format() const; void setInterop(Interop value); Interop interop() const; Q_SIGNALS: void formatChanged(); void interopChanged(); }; extern VideoDecoderId VideoDecoderId_VideoToolbox; FACTORY_REGISTER(VideoDecoder, VideoToolbox, "VideoToolbox") class VideoDecoderVideoToolboxPrivate Q_DECL_FINAL: public VideoDecoderFFmpegHWPrivate { public: VideoDecoderVideoToolboxPrivate() : VideoDecoderFFmpegHWPrivate() , out_fmt(VideoDecoderVideoToolbox::NV12) , interop_type(cv::InteropAuto) { if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7) out_fmt = VideoDecoderVideoToolbox::UYVY; copy_mode = VideoDecoderFFmpegHW::ZeroCopy; description = QStringLiteral("VideoToolbox"); } ~VideoDecoderVideoToolboxPrivate() {} bool open() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE; void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE; bool getBuffer(void **opaque, uint8_t **data) Q_DECL_OVERRIDE; void releaseBuffer(void *opaque, uint8_t *data) Q_DECL_OVERRIDE; AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { return AV_PIX_FMT_VIDEOTOOLBOX;} VideoDecoderVideoToolbox::PixelFormat out_fmt; cv::InteropType interop_type; cv::InteropResourcePtr interop_res; }; typedef struct { int code; const char *str; } cv_error; static const cv_error cv_errors[] = { { AVERROR(ENOSYS), "Hardware doesn't support accelerated decoding for this stream" " or Videotoolbox decoder is not available at the moment (another" " application is using it)." }, { AVERROR(EINVAL), "Invalid configuration provided to VTDecompressionSessionCreate" }, { AVERROR_INVALIDDATA, "Generic error returned by the decoder layer. The cause can be Videotoolbox" " found errors in the bitstream." }, { 0, NULL }, }; static const char* cv_err_str(int err) { for (int i = 0; cv_errors[i].code; ++i) { if (cv_errors[i].code != err) continue; return cv_errors[i].str; } return "Unknown error"; } VideoDecoderVideoToolbox::VideoDecoderVideoToolbox() : VideoDecoderFFmpegHW(*new VideoDecoderVideoToolboxPrivate()) { setProperty("threads", 1); // to avoid crash at av_videotoolbox_alloc_context/av_videotoolbox_default_free. I have no idea how the are called // dynamic properties about static property details. used by UI setProperty("detail_format", tr("Output pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV.\nOSX < 10.7 only supports UYVY, BGRA and YUV420p")); setProperty("detail_interop" , tr("Interop with OpenGL") + QStringLiteral("\n") + tr("CVPixelBuffer: macOS+iOS") + QStringLiteral("\n") + tr("CVOpenGLES: iOS, no copy, fast") + QStringLiteral("\n") + tr("IOSurface: macOS, no copy, fast") + QStringLiteral("\n") + tr("Auto: choose the fastest")); Q_UNUSED(QObject::tr("interop")); Q_UNUSED(QObject::tr("format")); } VideoDecoderId VideoDecoderVideoToolbox::id() const { return VideoDecoderId_VideoToolbox; } QString VideoDecoderVideoToolbox::description() const { return QStringLiteral("Apple VideoToolbox"); } VideoFrame VideoDecoderVideoToolbox::frame() { DPTR_D(VideoDecoderVideoToolbox); CVPixelBufferRef cv_buffer = (CVPixelBufferRef)d.frame->data[3]; if (!cv_buffer) { qDebug("Frame buffer is empty."); return VideoFrame(); } if (CVPixelBufferGetDataSize(cv_buffer) <= 0) { qDebug("Empty frame buffer"); return VideoFrame(); } const VideoFormat::PixelFormat pixfmt = cv::format_from_cv(CVPixelBufferGetPixelFormatType(cv_buffer)); if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported cv pixel format: %#x", (quint32)CVPixelBufferGetPixelFormatType(cv_buffer)); return VideoFrame(); } uint8_t *src[3]; int pitch[3]; // must get the value from cvbuffer to compute opengl text size in VideoShader VideoFormat fmt(pixfmt); const bool zero_copy = copyMode() == ZeroCopy; CVPixelBufferLockBaseAddress(cv_buffer, kCVPixelBufferLock_ReadOnly); for (int i = 0; i < fmt.planeCount(); ++i) { pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_buffer, i); // get address results in internal copy if (!zero_copy) src[i] = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_buffer, i); } CVPixelBufferUnlockBaseAddress(cv_buffer, kCVPixelBufferLock_ReadOnly); //CVPixelBufferRelease(cv_buffer); // release when video frame is destroyed VideoFrame f; if (zero_copy) { if (d.interop_res) { // make sure VideoMaterial can correctly setup parameters VideoFormat::PixelFormat opixfmt = pixfmt; d.interop_res->stridesForWidth(format(), d.width, pitch, &opixfmt); if (pixfmt != opixfmt) fmt = VideoFormat(opixfmt); } f = VideoFrame(d.width, d.height, fmt); f.setBytesPerLine(pitch); // TODO: move to updateFrameInfo f.setTimestamp(double(d.frame->pkt_pts)/1000.0); f.setDisplayAspectRatio(d.getDAR(d.frame)); d.updateColorDetails(&f); if (d.interop_res) { // zero_copy cv::SurfaceInteropCV *interop = new cv::SurfaceInteropCV(d.interop_res); interop->setSurface(cv_buffer, d.width, d.height); f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop))); if (!d.interop_res->mapToTexture2D()) f.setMetaData(QStringLiteral("target"), QByteArrayLiteral("rect")); } else { f.setBits(src); // only set for copy back mode } } else { f = copyToFrame(fmt, d.height, src, pitch, false); } return f; } void VideoDecoderVideoToolbox::setFormat(PixelFormat fmt) { DPTR_D(VideoDecoderVideoToolbox); if (d.out_fmt == fmt) return; d.out_fmt = fmt; Q_EMIT formatChanged(); if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_7) return; if (fmt != YUV420P && fmt != UYVY) qWarning("format is not supported on OSX < 10.7"); } VideoDecoderVideoToolbox::PixelFormat VideoDecoderVideoToolbox::format() const { return d_func().out_fmt; } void VideoDecoderVideoToolbox::setInterop(Interop value) { DPTR_D(VideoDecoderVideoToolbox); if (value == (Interop)d.interop_type) return; d.interop_type = (cv::InteropType)value; Q_EMIT interopChanged(); } VideoDecoderVideoToolbox::Interop VideoDecoderVideoToolbox::interop() const { return (Interop)d_func().interop_type; } void* VideoDecoderVideoToolboxPrivate::setup(AVCodecContext *avctx) { releaseUSWC(); av_videotoolbox_default_free(avctx); AVVideotoolboxContext *vtctx = av_videotoolbox_alloc_context(); vtctx->cv_pix_fmt_type = out_fmt; qDebug("AVVideotoolboxContext: %p", vtctx); int err = av_videotoolbox_default_init2(avctx, vtctx); //ios h264 crashes when processing extra data. null H264Context if (err < 0) { qWarning("Failed to init videotoolbox decoder (%#x %s): %s", err, av_err2str(err), cv_err_str(err)); return NULL; } const CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(vtctx->cm_fmt_desc); initUSWC(codedWidth(avctx)); // TODO: use stride qDebug() << "VideoToolbox decoder created. format: " << cv::format_from_cv(out_fmt); qDebug("AVVideotoolboxContext ready: %dx%d", dim.width, dim.height); return vtctx; //the same as avctx->hwaccel_context; } bool VideoDecoderVideoToolboxPrivate::getBuffer(void **opaque, uint8_t **data) { *data = (uint8_t *)1; // dummy. it's AVFrame.data[0], must be non null required by ffmpeg Q_UNUSED(opaque); return true; } void VideoDecoderVideoToolboxPrivate::releaseBuffer(void *opaque, uint8_t *data) { Q_UNUSED(opaque); Q_UNUSED(data); } bool VideoDecoderVideoToolboxPrivate::open() { if (!prepare()) return false; switch (codec_ctx->profile) { //profile check code is from xbmc case FF_PROFILE_H264_HIGH_10: //Apple A7 SoC case FF_PROFILE_H264_HIGH_10_INTRA: case FF_PROFILE_H264_HIGH_422: case FF_PROFILE_H264_HIGH_422_INTRA: case FF_PROFILE_H264_HIGH_444_PREDICTIVE: case FF_PROFILE_H264_HIGH_444_INTRA: case FF_PROFILE_H264_CAVLC_444: return false; default: break; } switch (codec_ctx->codec_id) { case AV_CODEC_ID_H264: case AV_CODEC_ID_H263: case AV_CODEC_ID_MPEG4: case AV_CODEC_ID_MPEG2VIDEO: case AV_CODEC_ID_MPEG1VIDEO: break; default: qWarning("VideoToolbox unsupported codec: %s", avcodec_get_name(codec_ctx->codec_id)); return false; } qDebug("opening VideoToolbox module"); // setup() must be called in getFormat() from avcodec callback, otherwise in ffmpeg3.0 avctx->priv_data is null and crash // TODO: block AVDecoder.open() until hw callback is done //if (!setup(codec_ctx)) // return false; if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy) { interop_res = cv::InteropResourcePtr(cv::InteropResource::create(interop_type)); } else { interop_res.clear(); } return true; } void VideoDecoderVideoToolboxPrivate::close() { restore(); //IMPORTANT. can not call restore in dtor because ctx is 0 there qDebug("destroying VideoToolbox decoder"); if (codec_ctx) { av_videotoolbox_default_free(codec_ctx); } releaseUSWC(); } } //namespace QtAV #include "VideoDecoderVideoToolbox.moc" QtAV-1.12.0/src/codec/video/VideoEncoder.cpp000066400000000000000000000063321312235004300204270ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoEncoder.h" #include "QtAV/private/AVEncoder_p.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(VideoEncoder) void VideoEncoder_RegisterAll() { static bool called = false; if (called) return; called = true; // factory.h does not check whether an id is registered if (!VideoEncoderFactory::Instance().registeredIds().empty()) //registered on load return; extern bool RegisterVideoEncoderFFmpeg_Man(); RegisterVideoEncoderFFmpeg_Man(); } QStringList VideoEncoder::supportedCodecs() { // should check every registered encoders static QStringList codecs; if (!codecs.isEmpty()) return codecs; avcodec_register_all(); AVCodec* c = NULL; while ((c=av_codec_next(c))) { if (!av_codec_is_encoder(c) || c->type != AVMEDIA_TYPE_VIDEO) continue; codecs.append(QString::fromLatin1(c->name)); } return codecs; } VideoEncoder::VideoEncoder(VideoEncoderPrivate &d): AVEncoder(d) { } QString VideoEncoder::name() const { return QLatin1String(VideoEncoder::name(id())); } void VideoEncoder::setWidth(int value) { DPTR_D(VideoEncoder); if (d.width == value) return; d.width = value; emit widthChanged(); } int VideoEncoder::width() const { return d_func().width; } void VideoEncoder::setHeight(int value) { DPTR_D(VideoEncoder); if (d.height == value) return; d.height = value; emit heightChanged(); } int VideoEncoder::height() const { return d_func().height; } void VideoEncoder::setFrameRate(qreal value) { DPTR_D(VideoEncoder); if (d.frame_rate == value) return; d.frame_rate = value; emit frameRateChanged(); } qreal VideoEncoder::frameRate() const { return d_func().frame_rate; } void VideoEncoder::setPixelFormat(const VideoFormat::PixelFormat format) { DPTR_D(VideoEncoder); if (d.format.pixelFormat() == format) return; d.format.setPixelFormat(format); d.format_used = format; Q_EMIT pixelFormatChanged(); } VideoFormat::PixelFormat VideoEncoder::pixelFormat() const { return d_func().format_used; } } //namespace QtAV QtAV-1.12.0/src/codec/video/VideoEncoderFFmpeg.cpp000066400000000000000000000353001312235004300215110ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoEncoder.h" #include "QtAV/private/AVEncoder_p.h" #include "QtAV/private/AVCompat.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "QtAV/version.h" #include "utils/Logger.h" #if AV_MODULE_CHECK(LIBAVUTIL, 55, 13, 0, 27, 100) #define HAVE_AVHWCTX extern "C" { #include } #endif /*! * options (properties) are from libavcodec/options_table.h * enum name here must convert to lower case to fit the names in avcodec. done in AVEncoder.setOptions() * Don't use lower case here because the value name may be "default" in avcodec which is a keyword of C++ */ namespace QtAV { #ifdef HAVE_AVHWCTX struct { AVHWDeviceType type; const char* name; } hwdevs[] = { {AV_HWDEVICE_TYPE_VDPAU, "vdpau"}, {AV_HWDEVICE_TYPE_CUDA, "cuda"}, {AV_HWDEVICE_TYPE_VAAPI, "vaapi"}, {AV_HWDEVICE_TYPE_DXVA2, "dxva2"}, }; AVHWDeviceType fromHWAName(const char* name) { for (size_t i = 0; i < sizeof(hwdevs)/sizeof(hwdevs[0]); ++i) { if (qstrcmp(name, hwdevs[i].name) == 0) return hwdevs[i].type; } return AVHWDeviceType(-1); } #endif // HAVE_AVHWCTX class VideoEncoderFFmpegPrivate; class VideoEncoderFFmpeg : public VideoEncoder { Q_OBJECT DPTR_DECLARE_PRIVATE(VideoEncoderFFmpeg) Q_PROPERTY(QString hwdevice READ hwDevice WRITE setHWDevice NOTIFY hwDeviceChanged) public: VideoEncoderFFmpeg(); VideoEncoderId id() const Q_DECL_OVERRIDE; bool encode(const VideoFrame &frame = VideoFrame()) Q_DECL_OVERRIDE; void setHWDevice(const QString& name); QString hwDevice() const; Q_SIGNALS: void hwDeviceChanged(); }; static const VideoEncoderId VideoEncoderId_FFmpeg = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value; FACTORY_REGISTER(VideoEncoder, FFmpeg, "FFmpeg") class VideoEncoderFFmpegPrivate Q_DECL_FINAL: public VideoEncoderPrivate { public: VideoEncoderFFmpegPrivate() : VideoEncoderPrivate() , nb_encoded(0) { avcodec_register_all(); // NULL: codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264). avctx = avcodec_alloc_context3(NULL); } bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; qint64 nb_encoded; QByteArray buffer; QString hwdev; #ifdef HAVE_AVHWCTX AVBufferRef *hw_device_ctx; AVBufferRef *hwframes_ref; AVHWFramesContext *hwframes; QVector sw_fmts; #endif }; bool VideoEncoderFFmpegPrivate::open() { nb_encoded = 0LL; if (codec_name.isEmpty()) { // copy ctx from muxer by copyAVCodecContext AVCodec *codec = avcodec_find_encoder(avctx->codec_id); AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false); return true; } AVCodec *codec = avcodec_find_encoder_by_name(codec_name.toUtf8().constData()); if (!codec) { const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(codec_name.toUtf8().constData()); if (cd) { codec = avcodec_find_encoder(cd->id); } } if (!codec) { qWarning() << "Can not find encoder for codec " << codec_name; return false; } if (avctx) { avcodec_free_context(&avctx); avctx = 0; } avctx = avcodec_alloc_context3(codec); avctx->width = width; // coded_width works, why? avctx->height = height; // reset format_used to user defined format. important to update default format if format is invalid format_used = VideoFormat::Format_Invalid; AVPixelFormat fffmt = (AVPixelFormat)format.pixelFormatFFmpeg(); if (codec->pix_fmts && format.isValid()) { for (int i = 0; codec->pix_fmts[i] != AVPixelFormat(-1); ++i) { if (fffmt == codec->pix_fmts[i]) { format_used = format.pixelFormat(); break; } } } //avctx->sample_aspect_ratio = AVPixelFormat hwfmt = AVPixelFormat(-1); if (av_pix_fmt_desc_get(codec->pix_fmts[0])->flags & AV_PIX_FMT_FLAG_HWACCEL) hwfmt = codec->pix_fmts[0]; bool use_hwctx = false; if (hwfmt != AVPixelFormat(-1)) { #ifdef HAVE_AVHWCTX const AVHWDeviceType dt = fromHWAName(codec_name.section(QChar('_'), -1).toUtf8().constData()); if (dt != AVHWDeviceType(-1)) { use_hwctx = true; avctx->pix_fmt = hwfmt; hw_device_ctx = NULL; AV_ENSURE(av_hwdevice_ctx_create(&hw_device_ctx, dt, hwdev.toLatin1().constData(), NULL, 0), false); avctx->hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx); if (!avctx->hw_frames_ctx) { qWarning("Failed to create hw frame context for '%s'", codec_name.toLatin1().constData()); return false; } // get sw formats const void *hwcfg = NULL; AVHWFramesConstraints *constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx, hwcfg); const AVPixelFormat* in_fmts = constraints->valid_sw_formats; AVPixelFormat sw_fmt = AVPixelFormat(-1); if (in_fmts) { sw_fmt = in_fmts[0]; while (*in_fmts != AVPixelFormat(-1)) { if (*in_fmts == fffmt) sw_fmt = *in_fmts; sw_fmts.append(*in_fmts); ++in_fmts; } } else { sw_fmt = QTAV_PIX_FMT_C(YUV420P); } av_hwframe_constraints_free(&constraints); format_used = VideoFormat::pixelFormatFromFFmpeg(sw_fmt); // encoder surface pool parameters AVHWFramesContext* hwfs = (AVHWFramesContext*)avctx->hw_frames_ctx->data; hwfs->format = hwfmt; // must the same as avctx->pix_fmt hwfs->sw_format = sw_fmt; // if it's not set, vaapi will choose the last valid_sw_formats, but that's wrong for vaGetImage/DeriveImage. nvenc always need sw_format // hw upload parameters. encoder's hwframes is just for parameter checking, will never be intialized, so we allocate an individual one. hwframes_ref = av_hwframe_ctx_alloc(hw_device_ctx); if (!hwframes_ref) { qWarning("Failed to create hw frame context for uploading '%s'", codec_name.toLatin1().constData()); } else { hwframes = (AVHWFramesContext*)hwframes_ref->data; hwframes->format = hwfmt; } } #endif //HAVE_AVHWCTX } if (!use_hwctx) { // no hw device (videotoolbox, wrong device name etc.), or old ffmpeg // TODO: check frame is hw frame if (hwfmt == AVPixelFormat(-1)) { // sw enc if (format_used == VideoFormat::Format_Invalid) {// requested format is not supported by sw enc if (codec->pix_fmts) { //pix_fmts[0] is always a sw format here qDebug("use first supported pixel format '%d' for sw encoder", codec->pix_fmts[0]); format_used = VideoFormat::pixelFormatFromFFmpeg((int)codec->pix_fmts[0]); } } } else { if (format_used == VideoFormat::Format_Invalid) { // requested format is not supported by hw enc qDebug("use first supported sw pixel format '%d' for hw encoder", codec->pix_fmts[1]); if (codec->pix_fmts && codec->pix_fmts[1] != AVPixelFormat(-1)) format_used = VideoFormat::pixelFormatFromFFmpeg(codec->pix_fmts[1]); } } if (format_used == VideoFormat::Format_Invalid) { qWarning("fallback to yuv420p"); format_used = VideoFormat::Format_YUV420P; } avctx->pix_fmt = (AVPixelFormat)VideoFormat::pixelFormatToFFmpeg(format_used); } if (frame_rate > 0) avctx->time_base = av_d2q(1.0/frame_rate, frame_rate*1001.0+2); else avctx->time_base = av_d2q(1.0/VideoEncoder::defaultFrameRate(), VideoEncoder::defaultFrameRate()*1001.0+2); qDebug("size: %dx%d tbc: %f=%d/%d", width, height, av_q2d(avctx->time_base), avctx->time_base.num, avctx->time_base.den); avctx->bit_rate = bit_rate; //AVDictionary *dict = 0; if(avctx->codec_id == QTAV_CODEC_ID(H264)) { avctx->gop_size = 10; //avctx->max_b_frames = 3;//h264 av_dict_set(&dict, "preset", "fast", 0); //x264 av_dict_set(&dict, "tune", "zerolatency", 0); //x264 //av_dict_set(&dict, "profile", "main", 0); // conflict with vaapi (int values) } if(avctx->codec_id == AV_CODEC_ID_HEVC){ av_dict_set(&dict, "preset", "ultrafast", 0); av_dict_set(&dict, "tune", "zero-latency", 0); } if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { av_dict_set(&dict, "strict", "-2", 0); // mpeg2 arbitrary fps } applyOptionsForContext(); AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false); // from mpv ao_lavc const int buffer_size = qMax(qMax(width*height*6+200, FF_MIN_BUFFER_SIZE), sizeof(AVPicture));//?? buffer.resize(buffer_size); return true; } bool VideoEncoderFFmpegPrivate::close() { AV_ENSURE_OK(avcodec_close(avctx), false); return true; } VideoEncoderFFmpeg::VideoEncoderFFmpeg() : VideoEncoder(*new VideoEncoderFFmpegPrivate()) { } VideoEncoderId VideoEncoderFFmpeg::id() const { return VideoEncoderId_FFmpeg; } void VideoEncoderFFmpeg::setHWDevice(const QString &name) { DPTR_D(VideoEncoderFFmpeg); if (d.hwdev == name) return; d.hwdev = name; Q_EMIT hwDeviceChanged(); } QString VideoEncoderFFmpeg::hwDevice() const { return d_func().hwdev; } struct ScopedAVFrameDeleter { static inline void cleanup(void *pointer) { av_frame_free((AVFrame**)&pointer); } }; bool VideoEncoderFFmpeg::encode(const VideoFrame &frame) { DPTR_D(VideoEncoderFFmpeg); QScopedPointer f; // hwupload AVPixelFormat pixfmt = AVPixelFormat(frame.pixelFormatFFmpeg()); if (frame.isValid()) { f.reset(av_frame_alloc()); f->format = pixfmt; f->width = frame.width(); f->height = frame.height(); // f->quality = d.avctx->global_quality; switch (timestampMode()) { case TimestampCopy: f->pts = int64_t(frame.timestamp()*frameRate()); // TODO: check monotically increase and fix if not. or another mode? break; case TimestampMonotonic: f->pts = d.nb_encoded+1; break; default: break; } // pts is set in muxer const int nb_planes = frame.planeCount(); for (int i = 0; i < nb_planes; ++i) { f->linesize[i] = frame.bytesPerLine(i); f->data[i] = (uint8_t*)frame.constBits(i); } if (d.avctx->width <= 0) { d.avctx->width = frame.width(); } if (d.avctx->height <= 0) { d.avctx->height = frame.width(); } #ifdef HAVE_AVHWCTX if (d.avctx->hw_frames_ctx) { // TODO: try to map to SourceSurface // checl valid sw_formats if (!d.hwframes_ref) { qWarning("no hw frame context for uploading"); return false; } if (pixfmt != d.hwframes->sw_format) { // reinit or got an unsupported format. assume parameters will not change, so it's the 1st init // check constraints bool init_frames_ctx = d.hwframes->sw_format == AVPixelFormat(-1); if (d.sw_fmts.contains(pixfmt)) { // format changed init_frames_ctx = true; } else { // convert to supported sw format pixfmt = d.sw_fmts[0]; f->format = pixfmt; VideoFrame converted = frame.to(VideoFormat::pixelFormatFromFFmpeg(pixfmt)); for (int i = 0; i < converted.planeCount(); ++i) { f->linesize[i] = converted.bytesPerLine(i); f->data[i] = (uint8_t*)frame.constBits(i); } } if (init_frames_ctx) { d.hwframes->sw_format = pixfmt; d.hwframes->width = frame.width(); d.hwframes->height = frame.height(); AV_ENSURE(av_hwframe_ctx_init(d.hwframes_ref), false); } } // upload QScopedPointer hwf( av_frame_alloc()); AV_ENSURE(av_hwframe_get_buffer(d.hwframes_ref, hwf.data(), 0), false); //hwf->format = d.hwframes->format; // not necessary //hwf->width = f->width; //hwf->height = f->height; AV_ENSURE(av_hwframe_transfer_data(hwf.data(), f.data(), 0), false); AV_ENSURE(av_frame_copy_props(hwf.data(), f.data()), false); av_frame_unref(f.data()); av_frame_move_ref(f.data(), hwf.data()); } #endif //HAVE_AVHWCTX } AVPacket pkt; av_init_packet(&pkt); pkt.data = (uint8_t*)d.buffer.constData(); pkt.size = d.buffer.size(); int got_packet = 0; int ret = avcodec_encode_video2(d.avctx, &pkt, f.data(), &got_packet); if (ret < 0) { qWarning("error avcodec_encode_video2: %s" ,av_err2str(ret)); return false; //false } d.nb_encoded++; if (!got_packet) { qWarning("no packet got"); d.packet = Packet(); // invalid frame means eof return frame.isValid(); } // qDebug("enc avpkt.pts: %lld, dts: %lld.", pkt.pts, pkt.dts); d.packet = Packet::fromAVPacket(&pkt, av_q2d(d.avctx->time_base)); // qDebug("enc packet.pts: %.3f, dts: %.3f.", d.packet.pts, d.packet.dts); return true; } } //namespace QtAV #include "VideoEncoderFFmpeg.moc" QtAV-1.12.0/src/codec/video/tiled_yuv.S000066400000000000000000000070071312235004300175050ustar00rootroot00000000000000/* * Copyright (c) 2014 Jens Kuske * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits /* mark stack as non-executable */ #endif .text .syntax unified .arch armv7-a .fpu neon .thumb .macro thumb_function fname .global \fname #ifdef __ELF__ .hidden \fname .type \fname, %function #endif .thumb_func \fname: .endm .macro end_function fname #ifdef __ELF__ .size \fname, .-\fname #endif .endm SRC .req r0 DST .req r1 PITCH .req r2 CNT .req r3 TLINE .req r4 HEIGHT .req r5 REST .req r6 NTILES .req r7 TMPSRC .req r8 DST2 .req r9 TSIZE .req r12 NEXTLIN .req lr thumb_function tiled_to_planar push {r4, r5, r6, r7, r8, lr} ldr HEIGHT, [sp, #24] add NEXTLIN, r3, #31 lsrs NTILES, r3, #5 bic NEXTLIN, NEXTLIN, #31 and REST, r3, #31 lsl NEXTLIN, NEXTLIN, #5 subs PITCH, r2, r3 movs TLINE, #32 rsb NEXTLIN, NEXTLIN, #32 mov TSIZE, #1024 /* y loop */ 1: cbz NTILES, 3f mov CNT, NTILES /* x loop complete tiles */ 2: pld [SRC, TSIZE] vld1.8 {d0 - d3}, [SRC :256], TSIZE subs CNT, #1 vst1.8 {d0 - d3}, [DST]! bne 2b 3: cbnz REST, 4f /* fix up dest pointer if pitch != width */ 7: add DST, PITCH /* fix up src pointer at end of line */ subs TLINE, #1 itee ne addne SRC, NEXTLIN subeq SRC, #992 moveq TLINE, #32 subs HEIGHT, #1 bne 1b pop {r4, r5, r6, r7, r8, pc} /* partly copy last tile of line */ 4: mov TMPSRC, SRC tst REST, #16 beq 5f vld1.8 {d0 - d1}, [TMPSRC :128]! vst1.8 {d0 - d1}, [DST]! 5: add SRC, TSIZE ands CNT, REST, #15 beq 7b 6: vld1.8 {d0[0]}, [TMPSRC]! subs CNT, #1 vst1.8 {d0[0]}, [DST]! bne 6b b 7b end_function tiled_to_planar thumb_function tiled_deinterleave_to_planar push {r4, r5, r6, r7, r8, r9, lr} mov DST2, r2 ldr HEIGHT, [sp, #32] ldr r4, [sp, #28] add NEXTLIN, r4, #31 lsrs NTILES, r4, #5 bic NEXTLIN, NEXTLIN, #31 ubfx REST, r4, #1, #4 lsl NEXTLIN, NEXTLIN, #5 sub PITCH, r3, r4, lsr #1 movs TLINE, #32 rsb NEXTLIN, NEXTLIN, #32 mov TSIZE, #1024 /* y loop */ 1: cbz NTILES, 3f mov CNT, NTILES /* x loop complete tiles */ 2: pld [SRC, TSIZE] vld2.8 {d0 - d3}, [SRC :256], TSIZE subs CNT, #1 vst1.8 {d0 - d1}, [DST]! vst1.8 {d2 - d3}, [DST2]! bne 2b 3: cbnz REST, 4f /* fix up dest pointer if pitch != width */ 7: add DST, PITCH add DST2, PITCH /* fix up src pointer at end of line */ subs TLINE, #1 itee ne addne SRC, NEXTLIN subeq SRC, #992 moveq TLINE, #32 subs HEIGHT, #1 bne 1b pop {r4, r5, r6, r7, r8, r9, pc} /* partly copy last tile of line */ 4: mov TMPSRC, SRC tst REST, #8 beq 5f vld2.8 {d0 - d1}, [TMPSRC :128]! vst1.8 {d0}, [DST]! vst1.8 {d1}, [DST2]! 5: add SRC, TSIZE ands CNT, REST, #7 beq 7b 6: vld2.8 {d0[0], d1[0]}, [TMPSRC]! subs CNT, #1 vst1.8 {d0[0]}, [DST]! vst1.8 {d1[0]}, [DST2]! bne 6b b 7b end_function tiled_deinterleave_to_planar QtAV-1.12.0/src/compat/000077500000000000000000000000001312235004300144515ustar00rootroot00000000000000QtAV-1.12.0/src/compat/msint.txt000066400000000000000000000000741312235004300163450ustar00rootroot00000000000000http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.htmlQtAV-1.12.0/src/compat/msvc/000077500000000000000000000000001312235004300154215ustar00rootroot00000000000000QtAV-1.12.0/src/compat/msvc/changelog.txt000066400000000000000000000131101312235004300201050ustar00rootroot00000000000000------------------------------------------------------------------------ r26 | 2009-10-02 13:36:47 +0400 | 2 lines [Issue 5] Change to "stdint.h" to let compiler search for it in local directory. ------------------------------------------------------------------------ r25 | 2009-09-17 23:46:49 +0400 | 2 lines [Issue 4] Fix incorrect int8_t behaviour if compiled with /J flag. ------------------------------------------------------------------------ r24 | 2009-05-13 14:53:48 +0400 | 2 lines Forgot about #ifdef __cplusplus guard around 'extern "C"', so inclusion to C files has been broken. ------------------------------------------------------------------------ r23 | 2009-05-12 01:27:45 +0400 | 3 lines [Issue 2] Always wrap is included. ------------------------------------------------------------------------ r19 | 2007-07-04 02:14:40 +0400 | 3 lines Explicitly cast to appropriate type INT8_MIN, INT16_MIN, INT32_MIN and INT64_MIN constants. Due to their unusual definition in Visual Studio headers (-_Ix_MAX-1) they are propagated to int and thus do not have expected type, causing VS6 strict compiler to claim about type inconsistency. ------------------------------------------------------------------------ r18 | 2007-06-26 16:53:23 +0400 | 2 lines Better handling of (U)INTx_C macros - now they generate constants of exact width. ------------------------------------------------------------------------ r17 | 2007-03-29 20:16:14 +0400 | 2 lines Fix typo: Miscrosoft -> Microsoft. ------------------------------------------------------------------------ r16 | 2007-02-24 17:32:58 +0300 | 4 lines Remove include, as it is not present in Visual Studio 2005 Epxress Edition and required only for INT_PTR and UINT_PTR types. 'intptr_t' and 'uintptr_t' types now defined explicitly with #ifdef _WIN64. ------------------------------------------------------------------------ r15 | 2007-02-11 20:53:05 +0300 | 2 lines More correct fix for compilation under VS6. ------------------------------------------------------------------------ r14 | 2007-02-11 20:04:32 +0300 | 2 lines Bugfix: fix compiling under VS6, when stdint.h enclosed in 'extern "C" {}'. ------------------------------------------------------------------------ r13 | 2006-12-13 16:53:11 +0300 | 2 lines Make _inline modifier for imaxdiv default option. Use STATIC_IMAXDIV to make it static. ------------------------------------------------------------------------ r12 | 2006-12-13 16:42:24 +0300 | 2 lines Error message changed: VC6 supported from now. ------------------------------------------------------------------------ r11 | 2006-12-13 16:39:33 +0300 | 2 lines All (U)INT* types changed to (unsigned) __int*. This should make stdint.h compatible with VC6. ------------------------------------------------------------------------ r10 | 2006-12-13 16:20:57 +0300 | 3 lines Added INLINE_IMAXDIV define switch. If INLINE_IMAXDIV is defined imaxdiv() have static modifier. If not - it is _inline. ------------------------------------------------------------------------ r9 | 2006-12-13 15:53:52 +0300 | 2 lines Error message for non-MSC compiler changed. ------------------------------------------------------------------------ r8 | 2006-12-13 12:47:48 +0300 | 2 lines Added #ifndef for SIZE_MAX (it is defined in limits.h on MSVSC 8). ------------------------------------------------------------------------ r7 | 2006-12-13 01:08:02 +0300 | 2 lines License chaged to BSD-derivative. ------------------------------------------------------------------------ r6 | 2006-12-13 00:53:20 +0300 | 2 lines Added include to avoid warnings when it is included after stdint.h. ------------------------------------------------------------------------ r5 | 2006-12-12 00:58:05 +0300 | 2 lines BUGFIX: Definitions of INTPTR_MIN, INTPTR_MAX and UINTPTR_MAX for WIN32 and WIN64 was mixed up. ------------------------------------------------------------------------ r4 | 2006-12-12 00:51:55 +0300 | 2 lines Rise #error if _MSC_VER is not defined. I.e. compiler other then Microsoft Visual C++ is used. ------------------------------------------------------------------------ r3 | 2006-12-11 22:54:14 +0300 | 2 lines Added include to stdint.h. ------------------------------------------------------------------------ r2 | 2006-12-11 21:39:27 +0300 | 2 lines Initial check in. ------------------------------------------------------------------------ r1 | 2006-12-11 21:30:23 +0300 | 1 line Initial directory structure. ------------------------------------------------------------------------ QtAV-1.12.0/src/compat/msvc/inttypes.h000066400000000000000000000175041312235004300174600ustar00rootroot00000000000000// ISO C9x compliant inttypes.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_INTTYPES_H_ // [ #define _MSC_INTTYPES_H_ #if _MSC_VER > 1000 #pragma once #endif #include "stdint.h" // 7.8 Format conversion of integer types typedef struct { intmax_t quot; intmax_t rem; } imaxdiv_t; // 7.8.1 Macros for format specifiers #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 // The fprintf macros for signed integers are: #define PRId8 "d" #define PRIi8 "i" #define PRIdLEAST8 "d" #define PRIiLEAST8 "i" #define PRIdFAST8 "d" #define PRIiFAST8 "i" #define PRId16 "hd" #define PRIi16 "hi" #define PRIdLEAST16 "hd" #define PRIiLEAST16 "hi" #define PRIdFAST16 "hd" #define PRIiFAST16 "hi" #define PRId32 "I32d" #define PRIi32 "I32i" #define PRIdLEAST32 "I32d" #define PRIiLEAST32 "I32i" #define PRIdFAST32 "I32d" #define PRIiFAST32 "I32i" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIdLEAST64 "I64d" #define PRIiLEAST64 "I64i" #define PRIdFAST64 "I64d" #define PRIiFAST64 "I64i" #define PRIdMAX "I64d" #define PRIiMAX "I64i" #define PRIdPTR "Id" #define PRIiPTR "Ii" // The fprintf macros for unsigned integers are: #define PRIo8 "o" #define PRIu8 "u" #define PRIx8 "x" #define PRIX8 "X" #define PRIoLEAST8 "o" #define PRIuLEAST8 "u" #define PRIxLEAST8 "x" #define PRIXLEAST8 "X" #define PRIoFAST8 "o" #define PRIuFAST8 "u" #define PRIxFAST8 "x" #define PRIXFAST8 "X" #define PRIo16 "ho" #define PRIu16 "hu" #define PRIx16 "hx" #define PRIX16 "hX" #define PRIoLEAST16 "ho" #define PRIuLEAST16 "hu" #define PRIxLEAST16 "hx" #define PRIXLEAST16 "hX" #define PRIoFAST16 "ho" #define PRIuFAST16 "hu" #define PRIxFAST16 "hx" #define PRIXFAST16 "hX" #define PRIo32 "I32o" #define PRIu32 "I32u" #define PRIx32 "I32x" #define PRIX32 "I32X" #define PRIoLEAST32 "I32o" #define PRIuLEAST32 "I32u" #define PRIxLEAST32 "I32x" #define PRIXLEAST32 "I32X" #define PRIoFAST32 "I32o" #define PRIuFAST32 "I32u" #define PRIxFAST32 "I32x" #define PRIXFAST32 "I32X" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIX64 "I64X" #define PRIoLEAST64 "I64o" #define PRIuLEAST64 "I64u" #define PRIxLEAST64 "I64x" #define PRIXLEAST64 "I64X" #define PRIoFAST64 "I64o" #define PRIuFAST64 "I64u" #define PRIxFAST64 "I64x" #define PRIXFAST64 "I64X" #define PRIoMAX "I64o" #define PRIuMAX "I64u" #define PRIxMAX "I64x" #define PRIXMAX "I64X" #define PRIoPTR "Io" #define PRIuPTR "Iu" #define PRIxPTR "Ix" #define PRIXPTR "IX" // The fscanf macros for signed integers are: #define SCNd8 "d" #define SCNi8 "i" #define SCNdLEAST8 "d" #define SCNiLEAST8 "i" #define SCNdFAST8 "d" #define SCNiFAST8 "i" #define SCNd16 "hd" #define SCNi16 "hi" #define SCNdLEAST16 "hd" #define SCNiLEAST16 "hi" #define SCNdFAST16 "hd" #define SCNiFAST16 "hi" #define SCNd32 "ld" #define SCNi32 "li" #define SCNdLEAST32 "ld" #define SCNiLEAST32 "li" #define SCNdFAST32 "ld" #define SCNiFAST32 "li" #define SCNd64 "I64d" #define SCNi64 "I64i" #define SCNdLEAST64 "I64d" #define SCNiLEAST64 "I64i" #define SCNdFAST64 "I64d" #define SCNiFAST64 "I64i" #define SCNdMAX "I64d" #define SCNiMAX "I64i" #ifdef _WIN64 // [ # define SCNdPTR "I64d" # define SCNiPTR "I64i" #else // _WIN64 ][ # define SCNdPTR "ld" # define SCNiPTR "li" #endif // _WIN64 ] // The fscanf macros for unsigned integers are: #define SCNo8 "o" #define SCNu8 "u" #define SCNx8 "x" #define SCNX8 "X" #define SCNoLEAST8 "o" #define SCNuLEAST8 "u" #define SCNxLEAST8 "x" #define SCNXLEAST8 "X" #define SCNoFAST8 "o" #define SCNuFAST8 "u" #define SCNxFAST8 "x" #define SCNXFAST8 "X" #define SCNo16 "ho" #define SCNu16 "hu" #define SCNx16 "hx" #define SCNX16 "hX" #define SCNoLEAST16 "ho" #define SCNuLEAST16 "hu" #define SCNxLEAST16 "hx" #define SCNXLEAST16 "hX" #define SCNoFAST16 "ho" #define SCNuFAST16 "hu" #define SCNxFAST16 "hx" #define SCNXFAST16 "hX" #define SCNo32 "lo" #define SCNu32 "lu" #define SCNx32 "lx" #define SCNX32 "lX" #define SCNoLEAST32 "lo" #define SCNuLEAST32 "lu" #define SCNxLEAST32 "lx" #define SCNXLEAST32 "lX" #define SCNoFAST32 "lo" #define SCNuFAST32 "lu" #define SCNxFAST32 "lx" #define SCNXFAST32 "lX" #define SCNo64 "I64o" #define SCNu64 "I64u" #define SCNx64 "I64x" #define SCNX64 "I64X" #define SCNoLEAST64 "I64o" #define SCNuLEAST64 "I64u" #define SCNxLEAST64 "I64x" #define SCNXLEAST64 "I64X" #define SCNoFAST64 "I64o" #define SCNuFAST64 "I64u" #define SCNxFAST64 "I64x" #define SCNXFAST64 "I64X" #define SCNoMAX "I64o" #define SCNuMAX "I64u" #define SCNxMAX "I64x" #define SCNXMAX "I64X" #ifdef _WIN64 // [ # define SCNoPTR "I64o" # define SCNuPTR "I64u" # define SCNxPTR "I64x" # define SCNXPTR "I64X" #else // _WIN64 ][ # define SCNoPTR "lo" # define SCNuPTR "lu" # define SCNxPTR "lx" # define SCNXPTR "lX" #endif // _WIN64 ] #endif // __STDC_FORMAT_MACROS ] // 7.8.2 Functions for greatest-width integer types // 7.8.2.1 The imaxabs function #define imaxabs _abs64 // 7.8.2.2 The imaxdiv function // This is modified version of div() function from Microsoft's div.c found // in %MSVC.NET%\crt\src\div.c #ifdef STATIC_IMAXDIV // [ static #else // STATIC_IMAXDIV ][ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { imaxdiv_t result; result.quot = numer / denom; result.rem = numer % denom; if (numer < 0 && result.rem > 0) { // did division wrong; must fix up ++result.quot; result.rem -= denom; } return result; } // 7.8.2.3 The strtoimax and strtoumax functions #define strtoimax _strtoi64 #define strtoumax _strtoui64 // 7.8.2.4 The wcstoimax and wcstoumax functions #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 #endif // _MSC_INTTYPES_H_ ] QtAV-1.12.0/src/compat/msvc/stdint.h000066400000000000000000000170601312235004300171030ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] QtAV-1.12.0/src/cuda/000077500000000000000000000000001312235004300141025ustar00rootroot00000000000000QtAV-1.12.0/src/cuda/cuda_api.cpp000066400000000000000000001065251312235004300163640ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include #include "helper_cuda.h" #include "cuda_api.h" #include "QtAV/QtAV_Global.h" #include "utils/Logger.h" /* #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) #define DEFINE_API() ... #else #define DEFINE_API() ... ::api() #endif */ #if 0 #define DECLARE_API_CONTEXT(API, ..) \ typedef API##_t (...); \ API##_t* API##_fp; \ void resolve_##API () { API##_fp = ... } \ resolve_t *resolve_##API##_fp = resolve_##API; // in ctor //memset() for (int i = 0; i < sizeof(api_t)/sizeof(void*); ++i) { (resolve_t*)(char*)(&api)+(2*i+1)*sizeof(void*))(); } #endif #if __CUDA_API_VERSION >= 3020 typedef struct { unsigned int srcXInBytes; unsigned int srcY; CUmemorytype srcMemoryType; const void *srcHost; CUdeviceptr srcDevice; CUarray srcArray; unsigned int srcPitch; unsigned int dstXInBytes; unsigned int dstY; CUmemorytype dstMemoryType; void *dstHost; CUdeviceptr dstDevice; CUarray dstArray; unsigned int dstPitch; unsigned int WidthInBytes; unsigned int Height; } CUDA_MEMCPY2D_old; #else typedef CUDA_MEMCPY2D CUDA_MEMCPY2D_old; #endif class cuda_api::context { public: context() : loaded(true) { #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) loaded = false; memset(&api, 0, sizeof(api)); cuda_dll.setFileName(QStringLiteral("cuda")); if (!cuda_dll.isLoaded()) cuda_dll.load(); if (!cuda_dll.isLoaded()) { cuda_dll.setFileName(QStringLiteral("nvcuda")); cuda_dll.load(); } if (!cuda_dll.isLoaded()) { qWarning("can not load cuda!"); return; } cuvid_dll.setFileName(QStringLiteral("nvcuvid")); cuvid_dll.load(); if (!cuvid_dll.isLoaded()) { qWarning("can not load nvcuvid!"); return; } loaded = true; #endif // !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) } #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) ~context() { loaded = false; cuvid_dll.unload(); cuda_dll.unload(); } QLibrary cuda_dll; QLibrary cuvid_dll; typedef struct { typedef CUresult CUDAAPI tcuGetErrorName(CUresult error, const char **pStr); tcuGetErrorName* cuGetErrorName; typedef CUresult CUDAAPI tcuGetErrorString(CUresult error, const char **pStr); tcuGetErrorString* cuGetErrorString; typedef CUresult CUDAAPI tcuInit(unsigned int); tcuInit* cuInit; typedef CUresult tcuCtxGetApiVersion(CUcontext ctx, unsigned int *version); tcuCtxGetApiVersion *cuCtxGetApiVersion; typedef CUresult CUDAAPI tcuCtxCreate(CUcontext *, unsigned int, CUdevice); tcuCtxCreate* cuCtxCreate; typedef CUresult CUDAAPI tcuCtxDestroy(CUcontext); tcuCtxDestroy* cuCtxDestroy; typedef CUresult CUDAAPI tcuCtxPushCurrent(CUcontext); tcuCtxPushCurrent* cuCtxPushCurrent; typedef CUresult CUDAAPI tcuCtxPopCurrent(CUcontext *); tcuCtxPopCurrent* cuCtxPopCurrent; typedef CUresult CUDAAPI tcuCtxGetCurrent(CUcontext *pctx); tcuCtxGetCurrent* cuCtxGetCurrent; typedef CUresult CUDAAPI tcuMemAllocHost(void **pp, size_t bytesize); tcuMemAllocHost* cuMemAllocHost; typedef CUresult CUDAAPI tcuMemFreeHost(void *p); tcuMemFreeHost* cuMemFreeHost; typedef CUresult CUDAAPI tcuMemcpyDtoH(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount); tcuMemcpyDtoH* cuMemcpyDtoH; typedef CUresult CUDAAPI tcuMemcpyDtoHAsync(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount, CUstream hStream); tcuMemcpyDtoHAsync* cuMemcpyDtoHAsync; typedef CUresult CUDAAPI tcuMemcpy2DAsync(const CUDA_MEMCPY2D *pCopy, CUstream hStream); tcuMemcpy2DAsync* cuMemcpy2DAsync; typedef CUresult CUDAAPI tcuMemcpy2D(const CUDA_MEMCPY2D *pCopy); tcuMemcpy2D* cuMemcpy2D; typedef CUresult CUDAAPI tcuMemcpy2DAsync_old(const CUDA_MEMCPY2D_old *pCopy, CUstream hStream); tcuMemcpy2DAsync_old* cuMemcpy2DAsync_old; typedef CUresult CUDAAPI tcuMemcpy2D_old(const CUDA_MEMCPY2D_old *pCopy); tcuMemcpy2D_old* cuMemcpy2D_old; typedef CUresult CUDAAPI tcuStreamCreate(CUstream *phStream, unsigned int Flags); tcuStreamCreate* cuStreamCreate; typedef CUresult CUDAAPI tcuStreamDestroy(CUstream hStream); tcuStreamDestroy* cuStreamDestroy; typedef CUresult CUDAAPI tcuStreamQuery(CUstream hStream); tcuStreamQuery* cuStreamQuery; typedef CUresult CUDAAPI tcuStreamSynchronize(CUstream hStream); tcuStreamSynchronize* cuStreamSynchronize; typedef CUresult CUDAAPI tcuDeviceGetCount(int *count); tcuDeviceGetCount* cuDeviceGetCount; typedef CUresult CUDAAPI tcuDriverGetVersion(int *driverVersion); tcuDriverGetVersion* cuDriverGetVersion; typedef CUresult CUDAAPI tcuDeviceGetName(char *name, int len, CUdevice dev); tcuDeviceGetName* cuDeviceGetName; typedef CUresult CUDAAPI tcuDeviceComputeCapability(int *major, int *minor, CUdevice dev); tcuDeviceComputeCapability* cuDeviceComputeCapability; typedef CUresult CUDAAPI tcuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev); tcuDeviceGetAttribute* cuDeviceGetAttribute; typedef CUresult CUDAAPI tcuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext ctx); tcuvidCtxLockCreate* cuvidCtxLockCreate; typedef CUresult CUDAAPI tcuvidCtxLockDestroy(CUvideoctxlock lck); tcuvidCtxLockDestroy* cuvidCtxLockDestroy; typedef CUresult CUDAAPI tcuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags); tcuvidCtxLock* cuvidCtxLock; typedef CUresult CUDAAPI tcuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags); tcuvidCtxUnlock* cuvidCtxUnlock; typedef CUresult CUDAAPI tcuCtxSynchronize(); tcuCtxSynchronize* cuCtxSynchronize; typedef CUresult CUDAAPI tcuD3D9CtxCreate(CUcontext *pCtx, CUdevice *pCudaDevice, unsigned int Flags, IDirect3DDevice9 *pD3DDevice); tcuD3D9CtxCreate* cuD3D9CtxCreate; typedef CUresult CUDAAPI tcuGraphicsD3D9RegisterResource(CUgraphicsResource *pCudaResource, IDirect3DResource9 *pD3DResource, unsigned int Flags); tcuGraphicsD3D9RegisterResource* cuGraphicsD3D9RegisterResource; typedef CUresult CUDAAPI tcuGLCtxCreate(CUcontext *pCtx, unsigned int Flags, CUdevice device); tcuGLCtxCreate* cuGLCtxCreate; typedef CUresult CUDAAPI tcuGraphicsGLRegisterImage(CUgraphicsResource *pCudaResource, GLuint image, GLenum target, unsigned int Flags); tcuGraphicsGLRegisterImage* cuGraphicsGLRegisterImage; typedef CUresult CUDAAPI tcuGraphicsUnregisterResource(CUgraphicsResource resource); tcuGraphicsUnregisterResource* cuGraphicsUnregisterResource; typedef CUresult CUDAAPI tcuGraphicsMapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream); tcuGraphicsMapResources* cuGraphicsMapResources; typedef CUresult CUDAAPI tcuGraphicsSubResourceGetMappedArray(CUarray *pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel); tcuGraphicsSubResourceGetMappedArray* cuGraphicsSubResourceGetMappedArray; typedef CUresult CUDAAPI tcuGraphicsUnmapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream); tcuGraphicsUnmapResources* cuGraphicsUnmapResources; typedef CUresult CUDAAPI tcuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams); tcuvidCreateVideoParser* cuvidCreateVideoParser; typedef CUresult CUDAAPI tcuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket); tcuvidParseVideoData* cuvidParseVideoData; typedef CUresult CUDAAPI tcuvidDestroyVideoParser(CUvideoparser obj); tcuvidDestroyVideoParser* cuvidDestroyVideoParser; typedef CUresult CUDAAPI tcuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci); tcuvidCreateDecoder* cuvidCreateDecoder; typedef CUresult CUDAAPI tcuvidDestroyDecoder(CUvideodecoder hDecoder); tcuvidDestroyDecoder* cuvidDestroyDecoder; typedef CUresult CUDAAPI tcuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams); tcuvidDecodePicture* cuvidDecodePicture; typedef CUresult CUDAAPI tcuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, CUdeviceptr *pDevPtr, unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); tcuvidMapVideoFrame* cuvidMapVideoFrame; typedef CUresult CUDAAPI tcuvidUnmapVideoFrame(CUvideodecoder hDecoder, CUdeviceptr DevPtr); tcuvidUnmapVideoFrame* cuvidUnmapVideoFrame; } api_t; api_t api; #endif // !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) bool loaded; }; cuda_api::cuda_api() : ctx(new context()) { } cuda_api::~cuda_api() { delete ctx; } bool cuda_api::isLoaded() const { return ctx->loaded; } #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) //////////////////////////////////////////////////// /// CUDA functions //////////////////////////////////////////////////// CUresult cuda_api::cuGetErrorName(CUresult error, const char **pStr) { static bool fallback = false; if (fallback) { *pStr = _cudaGetErrorEnum(error); return CUDA_SUCCESS; } if (!ctx->api.cuGetErrorName) { ctx->api.cuGetErrorName = (context::api_t::tcuGetErrorName*)ctx->cuda_dll.resolve("cuGetErrorName"); if (!ctx->api.cuGetErrorName) { fallback = true; return cuGetErrorName(error, pStr); } } return ctx->api.cuGetErrorName(error, pStr); } CUresult cuda_api::cuGetErrorString(CUresult error, const char **pStr) { static bool fallback = false; if (fallback) { *pStr = ""; return CUDA_SUCCESS; } if (!ctx->api.cuGetErrorString) { ctx->api.cuGetErrorString = (context::api_t::tcuGetErrorString*)ctx->cuda_dll.resolve("cuGetErrorString"); if (!ctx->api.cuGetErrorString) { fallback = true; return cuGetErrorString(error, pStr); } } return ctx->api.cuGetErrorString(error, pStr); } CUresult cuda_api::cuInit(unsigned int Flags) { if (!ctx->api.cuInit) { ctx->api.cuInit = (context::api_t::tcuInit*)ctx->cuda_dll.resolve("cuInit"); assert(ctx->api.cuInit); } return ctx->api.cuInit(Flags); } CUresult cuda_api::cuCtxGetApiVersion(CUcontext pctx, unsigned int *version) { if (!ctx->api.cuCtxGetApiVersion) { ctx->api.cuCtxGetApiVersion = (context::api_t::tcuCtxGetApiVersion*)ctx->cuda_dll.resolve("cuCtxGetApiVersion"); assert(ctx->api.cuCtxGetApiVersion); } return ctx->api.cuCtxGetApiVersion(pctx, version); } CUresult cuda_api::cuCtxCreate(CUcontext *pctx, unsigned int flags, CUdevice dev) { if (!ctx->api.cuCtxCreate) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuCtxCreate = (context::api_t::tcuCtxCreate*)ctx->cuda_dll.resolve("cuCtxCreate_v2"); if (!ctx->api.cuCtxCreate) #endif { qDebug("fallback to old driver api: %p", ctx->api.cuCtxCreate); ctx->api.cuCtxCreate = (context::api_t::tcuCtxCreate*)ctx->cuda_dll.resolve("cuCtxCreate"); } assert(ctx->api.cuCtxCreate); } return ctx->api.cuCtxCreate(pctx, flags, dev); } CUresult cuda_api::cuD3D9CtxCreate(CUcontext *pCtx, CUdevice *pCudaDevice, unsigned int Flags, IDirect3DDevice9 *pD3DDevice) { if (!ctx->api.cuD3D9CtxCreate) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuD3D9CtxCreate = (context::api_t::tcuD3D9CtxCreate*)ctx->cuda_dll.resolve("cuD3D9CtxCreate_v2"); if (!ctx->api.cuD3D9CtxCreate) #endif ctx->api.cuD3D9CtxCreate = (context::api_t::tcuD3D9CtxCreate*)ctx->cuda_dll.resolve("cuD3D9CtxCreate"); assert(ctx->api.cuD3D9CtxCreate); } return ctx->api.cuD3D9CtxCreate(pCtx, pCudaDevice, Flags, pD3DDevice); } CUresult cuda_api::cuGraphicsD3D9RegisterResource(CUgraphicsResource *pCudaResource, IDirect3DResource9 *pD3DResource, unsigned int Flags) { if (!ctx->api.cuGraphicsD3D9RegisterResource) { ctx->api.cuGraphicsD3D9RegisterResource = (context::api_t::tcuGraphicsD3D9RegisterResource*)ctx->cuda_dll.resolve("cuGraphicsD3D9RegisterResource"); assert(ctx->api.cuGraphicsD3D9RegisterResource); } return ctx->api.cuGraphicsD3D9RegisterResource(pCudaResource, pD3DResource, Flags); } CUresult cuda_api::cuGLCtxCreate(CUcontext *pctx, unsigned int flags, CUdevice dev) { if (!ctx->api.cuGLCtxCreate) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuGLCtxCreate = (context::api_t::tcuGLCtxCreate*)ctx->cuda_dll.resolve("cuGLCtxCreate_v2"); if (!ctx->api.cuGLCtxCreate) #endif ctx->api.cuGLCtxCreate = (context::api_t::tcuGLCtxCreate*)ctx->cuda_dll.resolve("cuGLCtxCreate"); assert(ctx->api.cuGLCtxCreate); } return ctx->api.cuGLCtxCreate(pctx, flags, dev); } CUresult cuda_api::cuCtxDestroy(CUcontext cuctx) { if (!ctx->api.cuCtxDestroy) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuCtxDestroy = (context::api_t::tcuCtxDestroy*)this->ctx->cuda_dll.resolve("cuCtxDestroy_v2"); if (!ctx->api.cuCtxDestroy) #endif ctx->api.cuCtxDestroy = (context::api_t::tcuCtxDestroy*)this->ctx->cuda_dll.resolve("cuCtxDestroy"); assert(ctx->api.cuCtxDestroy); } return ctx->api.cuCtxDestroy(cuctx); } CUresult cuda_api::cuCtxPushCurrent(CUcontext cuctx) { if (!ctx->api.cuCtxPushCurrent) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuCtxPushCurrent = (context::api_t::tcuCtxPushCurrent*)this->ctx->cuda_dll.resolve("cuCtxPushCurrent_v2"); if (!ctx->api.cuCtxPushCurrent) #endif ctx->api.cuCtxPushCurrent = (context::api_t::tcuCtxPushCurrent*)this->ctx->cuda_dll.resolve("cuCtxPushCurrent"); assert(ctx->api.cuCtxPushCurrent); } return ctx->api.cuCtxPushCurrent(cuctx); } CUresult cuda_api::cuCtxPopCurrent(CUcontext *pctx) { if (!ctx->api.cuCtxPopCurrent) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuCtxPopCurrent = (context::api_t::tcuCtxPopCurrent*)this->ctx->cuda_dll.resolve("cuCtxPopCurrent_v2"); if (!ctx->api.cuCtxPopCurrent) #endif ctx->api.cuCtxPopCurrent = (context::api_t::tcuCtxPopCurrent*)this->ctx->cuda_dll.resolve("cuCtxPopCurrent"); assert(ctx->api.cuCtxPopCurrent); } return ctx->api.cuCtxPopCurrent(pctx); } CUresult cuda_api::cuCtxGetCurrent(CUcontext *pctx) { if (!ctx->api.cuCtxGetCurrent) { ctx->api.cuCtxGetCurrent = (context::api_t::tcuCtxGetCurrent*)this->ctx->cuda_dll.resolve("cuCtxGetCurrent"); assert(ctx->api.cuCtxGetCurrent); } return ctx->api.cuCtxGetCurrent(pctx); } CUresult cuda_api::cuMemAllocHost(void **pp, size_t bytesize) { if(!ctx->api.cuMemAllocHost) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuMemAllocHost = (context::api_t::tcuMemAllocHost*)ctx->cuda_dll.resolve("cuMemAllocHost_v2"); if(!ctx->api.cuMemAllocHost) #endif ctx->api.cuMemAllocHost = (context::api_t::tcuMemAllocHost*)ctx->cuda_dll.resolve("cuMemAllocHost"); assert(ctx->api.cuMemAllocHost); } return ctx->api.cuMemAllocHost(pp, bytesize); } CUresult cuda_api::cuMemFreeHost(void *p) { if (!ctx->api.cuMemFreeHost) { ctx->api.cuMemFreeHost = (context::api_t::tcuMemFreeHost*)ctx->cuda_dll.resolve("cuMemFreeHost"); assert(ctx->api.cuMemFreeHost); } return ctx->api.cuMemFreeHost(p); } CUresult cuda_api::cuMemcpyDtoH(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount) { if (!ctx->api.cuMemcpyDtoH) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuMemcpyDtoH = (context::api_t::tcuMemcpyDtoH*)ctx->cuda_dll.resolve("cuMemcpyDtoH_v2"); if (!ctx->api.cuMemcpyDtoH) #endif ctx->api.cuMemcpyDtoH = (context::api_t::tcuMemcpyDtoH*)ctx->cuda_dll.resolve("cuMemcpyDtoH"); assert(ctx->api.cuMemcpyDtoH); } return ctx->api.cuMemcpyDtoH(dstHost, srcDevice, ByteCount); } CUresult cuda_api::cuMemcpyDtoHAsync(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount, CUstream hStream) { if (!ctx->api.cuMemcpyDtoHAsync) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuMemcpyDtoHAsync = (context::api_t::tcuMemcpyDtoHAsync*)ctx->cuda_dll.resolve("cuMemcpyDtoHAsync_v2"); if (!ctx->api.cuMemcpyDtoHAsync) #endif ctx->api.cuMemcpyDtoHAsync = (context::api_t::tcuMemcpyDtoHAsync*)ctx->cuda_dll.resolve("cuMemcpyDtoHAsync"); assert(ctx->api.cuMemcpyDtoHAsync); } return ctx->api.cuMemcpyDtoHAsync(dstHost, srcDevice, ByteCount, hStream); } CUresult cuda_api::cuMemcpy2DAsync(const CUDA_MEMCPY2D *pCopy, CUstream hStream) { if (!ctx->api.cuMemcpy2DAsync && !ctx->api.cuMemcpy2DAsync_old) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuMemcpy2DAsync = (context::api_t::tcuMemcpy2DAsync*)ctx->cuda_dll.resolve("cuMemcpy2DAsync_v2"); if (!ctx->api.cuMemcpy2DAsync) #endif ctx->api.cuMemcpy2DAsync_old = (context::api_t::tcuMemcpy2DAsync_old*)ctx->cuda_dll.resolve("cuMemcpy2DAsync"); assert(ctx->api.cuMemcpy2DAsync || ctx->api.cuMemcpy2DAsync_old); } #if __CUDA_API_VERSION >= 3020 if (ctx->api.cuMemcpy2DAsync) return ctx->api.cuMemcpy2DAsync(pCopy, hStream); CUDA_MEMCPY2D_old old; old.dstArray = pCopy->dstArray; old.dstDevice = pCopy->dstDevice; old.dstHost = pCopy->dstHost; old.dstMemoryType = pCopy->dstMemoryType; old.dstPitch = (unsigned int)pCopy->dstPitch; old.dstXInBytes = (unsigned int)pCopy->dstXInBytes; old.dstY = (unsigned int)pCopy->dstY; old.Height = (unsigned int)pCopy->Height; old.srcArray = pCopy->srcArray; old.srcDevice = pCopy->srcDevice; old.srcHost = pCopy->srcHost; old.srcMemoryType = pCopy->srcMemoryType; old.srcPitch = (unsigned int)pCopy->srcPitch; old.srcXInBytes = (unsigned int)pCopy->srcXInBytes; old.srcY = (unsigned int)pCopy->srcY; old.WidthInBytes = (unsigned int)pCopy->WidthInBytes; return ctx->api.cuMemcpy2DAsync_old(&old, hStream); #else return ctx->api.cuMemcpy2DAsync_old(pCopy, hStream); #endif } CUresult cuda_api::cuMemcpy2D(const CUDA_MEMCPY2D *pCopy) { if (!ctx->api.cuMemcpy2D && !ctx->api.cuMemcpy2D_old) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuMemcpy2D = (context::api_t::tcuMemcpy2D*)ctx->cuda_dll.resolve("cuMemcpy2D_v2"); if (!ctx->api.cuMemcpy2D) #endif ctx->api.cuMemcpy2D_old = (context::api_t::tcuMemcpy2D_old*)ctx->cuda_dll.resolve("cuMemcpy2D"); assert(ctx->api.cuMemcpy2D || ctx->api.cuMemcpy2D_old); } #if __CUDA_API_VERSION >= 3020 if (ctx->api.cuMemcpy2D) return ctx->api.cuMemcpy2D(pCopy); CUDA_MEMCPY2D_old old; old.dstArray = pCopy->dstArray; old.dstDevice = pCopy->dstDevice; old.dstHost = pCopy->dstHost; old.dstMemoryType = pCopy->dstMemoryType; old.dstPitch = (unsigned int)pCopy->dstPitch; old.dstXInBytes = (unsigned int)pCopy->dstXInBytes; old.dstY = (unsigned int)pCopy->dstY; old.Height = (unsigned int)pCopy->Height; old.srcArray = pCopy->srcArray; old.srcDevice = pCopy->srcDevice; old.srcHost = pCopy->srcHost; old.srcMemoryType = pCopy->srcMemoryType; old.srcPitch = (unsigned int)pCopy->srcPitch; old.srcXInBytes = (unsigned int)pCopy->srcXInBytes; old.srcY = (unsigned int)pCopy->srcY; old.WidthInBytes = (unsigned int)pCopy->WidthInBytes; return ctx->api.cuMemcpy2D_old(&old); #else return ctx->api.cuMemcpy2D_old(pCopy); #endif } CUresult cuda_api::cuStreamCreate(CUstream *phStream, unsigned int Flags) { if (!ctx->api.cuStreamCreate) { ctx->api.cuStreamCreate = (context::api_t::tcuStreamCreate*)ctx->cuda_dll.resolve("cuStreamCreate"); assert(ctx->api.cuStreamCreate); } return ctx->api.cuStreamCreate(phStream, Flags); } CUresult cuda_api::cuStreamDestroy(CUstream hStream) { if (!ctx->api.cuStreamDestroy) { #if __CUDA_API_VERSION >= 3020 ctx->api.cuStreamDestroy = (context::api_t::tcuStreamDestroy*)ctx->cuda_dll.resolve("cuStreamDestroy_v2"); if (!ctx->api.cuStreamDestroy) #endif ctx->api.cuStreamDestroy = (context::api_t::tcuStreamDestroy*)ctx->cuda_dll.resolve("cuStreamDestroy"); assert(ctx->api.cuStreamDestroy); } return ctx->api.cuStreamDestroy(hStream); } CUresult cuda_api::cuStreamQuery(CUstream hStream) { if (!ctx->api.cuStreamQuery) { ctx->api.cuStreamQuery = (context::api_t::tcuStreamQuery*)ctx->cuda_dll.resolve("cuStreamQuery"); assert(ctx->api.cuStreamQuery); } return ctx->api.cuStreamQuery(hStream); } CUresult cuda_api::cuStreamSynchronize(CUstream hStream) { if (!ctx->api.cuStreamSynchronize) { ctx->api.cuStreamSynchronize = (context::api_t::tcuStreamSynchronize*)ctx->cuda_dll.resolve("cuStreamSynchronize"); assert(ctx->api.cuStreamSynchronize); } return ctx->api.cuStreamSynchronize(hStream); } CUresult cuda_api::cuDeviceGetCount(int *count) { if (!ctx->api.cuDeviceGetCount) { ctx->api.cuDeviceGetCount = (context::api_t::tcuDeviceGetCount*)ctx->cuda_dll.resolve("cuDeviceGetCount"); assert(ctx->api.cuDeviceGetCount); } return ctx->api.cuDeviceGetCount(count); } CUresult cuda_api::cuDriverGetVersion(int *driverVersion) { if (!ctx->api.cuDriverGetVersion) { ctx->api.cuDriverGetVersion = (context::api_t::tcuDriverGetVersion*)ctx->cuda_dll.resolve("cuDriverGetVersion"); assert(ctx->api.cuDriverGetVersion); } return ctx->api.cuDriverGetVersion(driverVersion); } CUresult cuda_api::cuDeviceGetName(char *name, int len, CUdevice dev) { if (!ctx->api.cuDeviceGetName) { ctx->api.cuDeviceGetName = (context::api_t::tcuDeviceGetName*)ctx->cuda_dll.resolve("cuDeviceGetName"); assert(ctx->api.cuDeviceGetName); } return ctx->api.cuDeviceGetName(name, len, dev); } CUresult cuda_api::cuDeviceComputeCapability(int *major, int *minor, CUdevice dev) { if (!ctx->api.cuDeviceComputeCapability) { ctx->api.cuDeviceComputeCapability = (context::api_t::tcuDeviceComputeCapability*)ctx->cuda_dll.resolve("cuDeviceComputeCapability"); assert(ctx->api.cuDeviceComputeCapability); } return ctx->api.cuDeviceComputeCapability(major, minor, dev); } CUresult cuda_api::cuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev) { if (!ctx->api.cuDeviceGetAttribute) { ctx->api.cuDeviceGetAttribute = (context::api_t::tcuDeviceGetAttribute*)ctx->cuda_dll.resolve("cuDeviceGetAttribute"); assert(ctx->api.cuDeviceGetAttribute); } return ctx->api.cuDeviceGetAttribute(pi, attrib, dev); } CUresult cuda_api::cuGraphicsGLRegisterImage(CUgraphicsResource *pCudaResource, GLuint image, GLenum target, unsigned int Flags) { if (!ctx->api.cuGraphicsGLRegisterImage) { ctx->api.cuGraphicsGLRegisterImage = (context::api_t::tcuGraphicsGLRegisterImage*)ctx->cuda_dll.resolve("cuGraphicsGLRegisterImage"); assert(ctx->api.cuGraphicsGLRegisterImage); } return ctx->api.cuGraphicsGLRegisterImage(pCudaResource, image, target, Flags); } CUresult cuda_api::cuGraphicsUnregisterResource(CUgraphicsResource resource) { if (!ctx->api.cuGraphicsUnregisterResource) { ctx->api.cuGraphicsUnregisterResource = (context::api_t::tcuGraphicsUnregisterResource*)ctx->cuda_dll.resolve("cuGraphicsUnregisterResource"); assert(ctx->api.cuGraphicsUnregisterResource); } return ctx->api.cuGraphicsUnregisterResource(resource); } CUresult cuda_api::cuGraphicsMapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream) { if (!ctx->api.cuGraphicsMapResources) { ctx->api.cuGraphicsMapResources = (context::api_t::tcuGraphicsMapResources*)ctx->cuda_dll.resolve("cuGraphicsMapResources"); assert(ctx->api.cuGraphicsMapResources); } return ctx->api.cuGraphicsMapResources(count, resources, hStream); } CUresult cuda_api::cuGraphicsSubResourceGetMappedArray(CUarray *pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel) { if (!ctx->api.cuGraphicsSubResourceGetMappedArray) { ctx->api.cuGraphicsSubResourceGetMappedArray = (context::api_t::tcuGraphicsSubResourceGetMappedArray*)ctx->cuda_dll.resolve("cuGraphicsSubResourceGetMappedArray"); assert(ctx->api.cuGraphicsSubResourceGetMappedArray); } return ctx->api.cuGraphicsSubResourceGetMappedArray(pArray, resource, arrayIndex, mipLevel); } CUresult cuda_api::cuGraphicsUnmapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream) { if (!ctx->api.cuGraphicsUnmapResources) { ctx->api.cuGraphicsUnmapResources = (context::api_t::tcuGraphicsUnmapResources*)ctx->cuda_dll.resolve("cuGraphicsUnmapResources"); assert(ctx->api.cuGraphicsUnmapResources); } return ctx->api.cuGraphicsUnmapResources(count, resources, hStream); } //////////////////////////////////////////////////// /// D3D Interop //////////////////////////////////////////////////// //CUresult cuda_api::cuD3D9CtxCreate( CUcontext *pCtx, CUdevice *pCudaDevice, unsigned int Flags, IDirect3DDevice9 *pD3DDevice); //////////////////////////////////////////////////// /// CUVID functions //////////////////////////////////////////////////// CUresult cuda_api::cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext cuctx) { if (!ctx->api.cuvidCtxLockCreate) { ctx->api.cuvidCtxLockCreate = (context::api_t::tcuvidCtxLockCreate*)this->ctx->cuvid_dll.resolve("cuvidCtxLockCreate"); assert(ctx->api.cuvidCtxLockCreate); } return ctx->api.cuvidCtxLockCreate(pLock, cuctx); } CUresult cuda_api::cuvidCtxLockDestroy(CUvideoctxlock lck) { if (!ctx->api.cuvidCtxLockDestroy) ctx->api.cuvidCtxLockDestroy = (context::api_t::tcuvidCtxLockDestroy*)ctx->cuvid_dll.resolve("cuvidCtxLockDestroy"); assert(ctx->api.cuvidCtxLockDestroy); return ctx->api.cuvidCtxLockDestroy(lck); } CUresult cuda_api::cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags) { if (!ctx->api.cuvidCtxLock) { ctx->api.cuvidCtxLock = (context::api_t::tcuvidCtxLock*)ctx->cuvid_dll.resolve("cuvidCtxLock"); assert(ctx->api.cuvidCtxLock); } return ctx->api.cuvidCtxLock(lck, reserved_flags); } CUresult cuda_api::cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags) { if (!ctx->api.cuvidCtxUnlock) { ctx->api.cuvidCtxUnlock = (context::api_t::tcuvidCtxUnlock*)ctx->cuvid_dll.resolve("cuvidCtxUnlock"); assert(ctx->api.cuvidCtxUnlock); } return ctx->api.cuvidCtxUnlock(lck, reserved_flags); } CUresult cuda_api::cuCtxSynchronize() { if (!ctx->api.cuCtxSynchronize) { ctx->api.cuCtxSynchronize = (context::api_t::tcuCtxSynchronize*)ctx->cuda_dll.resolve("cuCtxSynchronize"); assert(ctx->api.cuCtxSynchronize); } return ctx->api.cuCtxSynchronize(); } CUresult cuda_api::cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams) { if (!ctx->api.cuvidCreateVideoParser) { ctx->api.cuvidCreateVideoParser = (context::api_t::tcuvidCreateVideoParser*)ctx->cuvid_dll.resolve("cuvidCreateVideoParser"); assert(ctx->api.cuvidCreateVideoParser); } return ctx->api.cuvidCreateVideoParser(pObj, pParams); } CUresult cuda_api::cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket) { if (!ctx->api.cuvidParseVideoData) { ctx->api.cuvidParseVideoData = (context::api_t::tcuvidParseVideoData*)ctx->cuvid_dll.resolve("cuvidParseVideoData"); assert(ctx->api.cuvidParseVideoData); } return ctx->api.cuvidParseVideoData(obj, pPacket); } CUresult cuda_api::cuvidDestroyVideoParser(CUvideoparser obj) { if (!ctx->api.cuvidDestroyVideoParser) { ctx->api.cuvidDestroyVideoParser = (context::api_t::tcuvidDestroyVideoParser*)ctx->cuvid_dll.resolve("cuvidDestroyVideoParser"); assert(ctx->api.cuvidDestroyVideoParser); } return ctx->api.cuvidDestroyVideoParser(obj); } // Create/Destroy the decoder object CUresult cuda_api::cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci) { if (!ctx->api.cuvidCreateDecoder) { ctx->api.cuvidCreateDecoder = (context::api_t::tcuvidCreateDecoder*)ctx->cuvid_dll.resolve("cuvidCreateDecoder"); assert(ctx->api.cuvidCreateDecoder); } return ctx->api.cuvidCreateDecoder(phDecoder, pdci); } CUresult cuda_api::cuvidDestroyDecoder(CUvideodecoder hDecoder) { if (!ctx->api.cuvidDestroyDecoder) { ctx->api.cuvidDestroyDecoder = (context::api_t::tcuvidDestroyDecoder*)ctx->cuvid_dll.resolve("cuvidDestroyDecoder"); assert(ctx->api.cuvidDestroyDecoder); } return ctx->api.cuvidDestroyDecoder(hDecoder); } // Decode a single picture (field or frame) CUresult cuda_api::cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams) { if (!ctx->api.cuvidDecodePicture) { ctx->api.cuvidDecodePicture = (context::api_t::tcuvidDecodePicture*) ctx->cuvid_dll.resolve("cuvidDecodePicture"); assert(ctx->api.cuvidDecodePicture); } return ctx->api.cuvidDecodePicture(hDecoder, pPicParams); } // Post-process and map a video frame for use in cuda CUresult cuda_api::cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, CUdeviceptr *pDevPtr, unsigned int *pPitch, CUVIDPROCPARAMS *pVPP) { if (!ctx->api.cuvidMapVideoFrame) { #if defined(__CUVID_DEVPTR64) ctx->api.cuvidMapVideoFrame = (context::api_t::tcuvidMapVideoFrame*)ctx->cuvid_dll.resolve("cuvidMapVideoFrame64"); #else ctx->api.cuvidMapVideoFrame = (context::api_t::tcuvidMapVideoFrame*)ctx->cuvid_dll.resolve("cuvidMapVideoFrame"); #endif assert(ctx->api.cuvidMapVideoFrame); } return ctx->api.cuvidMapVideoFrame(hDecoder, nPicIdx, pDevPtr, pPitch, pVPP); } // Unmap a previously mapped video frame CUresult cuda_api::cuvidUnmapVideoFrame(CUvideodecoder hDecoder, CUdeviceptr DevPtr) { if (!ctx->api.cuvidUnmapVideoFrame) { #if defined(__CUVID_DEVPTR64) ctx->api.cuvidUnmapVideoFrame = (context::api_t::tcuvidUnmapVideoFrame*)ctx->cuvid_dll.resolve("cuvidUnmapVideoFrame64"); #else ctx->api.cuvidUnmapVideoFrame = (context::api_t::tcuvidUnmapVideoFrame*)ctx->cuvid_dll.resolve("cuvidUnmapVideoFrame"); #endif assert(ctx->api.cuvidUnmapVideoFrame); } return ctx->api.cuvidUnmapVideoFrame(hDecoder, DevPtr); } #endif // !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) int cuda_api::GetMaxGflopsGraphicsDeviceId() { CUdevice current_device = 0, max_perf_device = 0; int device_count = 0, sm_per_multiproc = 0; int max_compute_perf = 0, best_SM_arch = 0; int major = 0, minor = 0, multiProcessorCount, clockRate; int bTCC = 0, version; int devices_prohibited = 0; char deviceName[256]; cuDeviceGetCount(&device_count); if (device_count <= 0) return -1; cuDriverGetVersion(&version); qDebug("cuda driver api build version: %d, runtime version: %d", __CUDA_API_VERSION, version); // Find the best major SM Architecture GPU device that are graphics devices while (current_device < device_count) { cuDeviceGetName(deviceName, 256, current_device); cuDeviceComputeCapability(&major, &minor, current_device); if (version >= 3020) { cuDeviceGetAttribute(&bTCC, CU_DEVICE_ATTRIBUTE_TCC_DRIVER, current_device); } else { // Assume a Tesla GPU is running in TCC if we are running CUDA 3.1 if (deviceName[0] == 'T') bTCC = 1; } int computeMode; cuDeviceGetAttribute(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, current_device); if (computeMode != CU_COMPUTEMODE_PROHIBITED) { if (!bTCC) { if (major > 0 && major < 9999) { best_SM_arch = std::max(best_SM_arch, major); } } } else { devices_prohibited++; } current_device++; } if (devices_prohibited == device_count) { fprintf(stderr, "GetMaxGflopsGraphicsDeviceId error: all devices have compute mode prohibited.\n"); return -1; } // Find the best CUDA capable GPU device current_device = 0; while (current_device < device_count) { cuDeviceGetAttribute(&multiProcessorCount, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, current_device); cuDeviceGetAttribute(&clockRate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, current_device); cuDeviceComputeCapability(&major, &minor, current_device); if (version >= 3020) { cuDeviceGetAttribute(&bTCC, CU_DEVICE_ATTRIBUTE_TCC_DRIVER, current_device); } else { // Assume a Tesla GPU is running in TCC if we are running CUDA 3.1 if (deviceName[0] == 'T') bTCC = 1; } int computeMode; cuDeviceGetAttribute(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, current_device); if (computeMode != CU_COMPUTEMODE_PROHIBITED) { if (major == 9999 && minor == 9999) { sm_per_multiproc = 1; } else { sm_per_multiproc = _ConvertSMVer2Cores(major, minor); } // If this is a Tesla based GPU and SM 2.0, and TCC is disabled, this is a contendor if (!bTCC) {// Is this GPU running the TCC driver? If so we pass on this int compute_perf = multiProcessorCount * sm_per_multiproc * clockRate; printf("%s @%d compute_perf=%d max_compute_perf=%d\n", __FUNCTION__, __LINE__, compute_perf, max_compute_perf); if (compute_perf > max_compute_perf) { // If we find GPU with SM major > 2, search only these if (best_SM_arch > 2) { printf("%s @%d best_SM_arch=%d\n", __FUNCTION__, __LINE__, best_SM_arch); // If our device = dest_SM_arch, then we pick this one if (major == best_SM_arch) { max_compute_perf = compute_perf; max_perf_device = current_device; } } else { max_compute_perf = compute_perf; max_perf_device = current_device; } } cuDeviceGetName(deviceName, 256, current_device); printf("CUDA Device: %s, Compute: %d.%d, CUDA Cores: %d, Clock: %d MHz\n", deviceName, major, minor, multiProcessorCount * sm_per_multiproc, clockRate / 1000); } } ++current_device; } return max_perf_device; } QtAV-1.12.0/src/cuda/cuda_api.h000066400000000000000000000167301312235004300160270ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef CUDA_API_H #define CUDA_API_H #include "dllapi/nv_inc.h" #define CUDA_ENSURE(f, ...) CUDA_CHECK(f, return __VA_ARGS__;) //call cuda_api.cuGetErrorXXX #define CUDA_WARN(f) CUDA_CHECK(f) //call cuda_api.cuGetErrorXXX #define CUDA_ENSURE2(f, ...) CUDA_CHECK2(f, return __VA_ARGS__;) #define CUDA_WARN2(f) CUDA_CHECK2(f) #if CUDA_VERSION < 7050 #if CUDA_VERSION < 6050 #define cudaVideoCodec_HEVC cudaVideoCodec(cudaVideoCodec_H264_MVC + 1) #endif //6050 #define cudaVideoCodec_VP8 cudaVideoCodec(cudaVideoCodec_HEVC+1) #define cudaVideoCodec_VP9 cudaVideoCodec(cudaVideoCodec_VP8+1) #endif //7050 struct IDirect3DDevice9; struct IDirect3DResource9; // TODO: cuda_drvapi_dylink.c/h class cuda_api { public: cuda_api(); virtual ~cuda_api(); bool isLoaded() const; #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) typedef unsigned int GLuint; typedef unsigned int GLenum; //////////////////////////////////////////////////// /// CUDA functions //////////////////////////////////////////////////// CUresult cuGetErrorName(CUresult error, const char **pStr); // since 6.0. fallback to _cudaGetErrorEnum defined in helper_cuda.h if symbol not found CUresult cuGetErrorString(CUresult error, const char **pStr); // since 6.0. fallback to a empty string if symbol not found CUresult cuInit(unsigned int Flags); CUresult cuCtxGetApiVersion(CUcontext pctx, unsigned int *version); CUresult cuCtxCreate(CUcontext *pctx, unsigned int flags, CUdevice dev); CUresult cuCtxDestroy(CUcontext cuctx); CUresult cuCtxPushCurrent(CUcontext cuctx); CUresult cuCtxPopCurrent( CUcontext *pctx); CUresult cuCtxGetCurrent(CUcontext *pctx); CUresult cuCtxSynchronize(); CUresult cuMemAllocHost(void **pp, size_t bytesize); CUresult cuMemFreeHost(void *p); CUresult cuMemcpyDtoH(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount); //TODO: size_t in new version CUresult cuMemcpyDtoHAsync(void *dstHost, CUdeviceptr srcDevice, size_t ByteCount, CUstream hStream); //TODO: size_t in new version CUresult cuMemcpy2D(const CUDA_MEMCPY2D *pCopy); CUresult cuMemcpy2DAsync(const CUDA_MEMCPY2D *pCopy, CUstream hStream); CUresult cuStreamCreate(CUstream *phStream, unsigned int Flags); //TODO: size_t in new version CUresult cuStreamDestroy(CUstream hStream); CUresult cuStreamQuery(CUstream hStream); CUresult cuStreamSynchronize(CUstream hStream); CUresult cuDeviceGetCount(int *count); CUresult cuDriverGetVersion(int *driverVersion); CUresult cuDeviceGetName(char *name, int len, CUdevice dev); CUresult cuDeviceComputeCapability(int *major, int *minor, CUdevice dev); CUresult cuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev); CUresult cuGLCtxCreate(CUcontext *pCtx, unsigned int Flags, CUdevice device); CUresult cuGraphicsGLRegisterImage(CUgraphicsResource *pCudaResource, GLuint image, GLenum target, unsigned int Flags); CUresult cuGraphicsUnregisterResource(CUgraphicsResource resource); CUresult cuGraphicsMapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream); CUresult cuGraphicsSubResourceGetMappedArray(CUarray *pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel); CUresult cuGraphicsUnmapResources(unsigned int count, CUgraphicsResource *resources, CUstream hStream); CUresult cuD3D9CtxCreate(CUcontext *pCtx, CUdevice *pCudaDevice, unsigned int Flags, IDirect3DDevice9 *pD3DDevice); CUresult cuGraphicsD3D9RegisterResource(CUgraphicsResource *pCudaResource, IDirect3DResource9 *pD3DResource, unsigned int Flags); //////////////////////////////////////////////////// /// CUVID functions //////////////////////////////////////////////////// CUresult cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext cuctx); CUresult cuvidCtxLockDestroy(CUvideoctxlock lck); CUresult cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags); CUresult cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags); CUresult cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams); CUresult cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket); CUresult cuvidDestroyVideoParser(CUvideoparser obj); // Create/Destroy the decoder object CUresult cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci); CUresult cuvidDestroyDecoder(CUvideodecoder hDecoder); // Decode a single picture (field or frame) CUresult cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams); // Post-process and map a video frame for use in cuda. // unsigned long long* (CUdevicePtr 64) for x64+cuda3.2, otherwise unsigned int* (CUdevicePtr 32) CUresult cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, CUdeviceptr *pDevPtr, unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); // Unmap a previously mapped video frame CUresult cuvidUnmapVideoFrame(CUvideodecoder hDecoder, CUdeviceptr DevPtr); #endif // !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) // This function returns the best Graphics GPU based on performance int GetMaxGflopsGraphicsDeviceId(); private: class context; context *ctx; }; #define CUDA_CHECK(f, ...) \ do { \ CUresult cuR = f; \ if (cuR != CUDA_SUCCESS) { \ const char* errName = NULL; \ const char* errDetail = NULL; \ cuGetErrorName(cuR, &errName); \ cuGetErrorString(cuR, &errDetail); \ qWarning("CUDA error %s@%d. " #f ": %d %s - %s", __FILE__, __LINE__, cuR, errName, errDetail); \ __VA_ARGS__ \ } \ } while (0) #define CUDA_CHECK2(f, ...) \ do { \ CUresult cuR = f; \ if (cuR != CUDA_SUCCESS) { \ qWarning("CUDA error %s@%d. " #f ": %d %s", __FILE__, __LINE__, cuR, _cudaGetErrorEnum(cuR)); \ __VA_ARGS__ \ } \ } while (0) // TODO: error check class AutoCtxLock { cuda_api *m_api; CUvideoctxlock m_lock; public: AutoCtxLock(cuda_api *api, CUvideoctxlock lck) : m_api(api), m_lock(lck) { m_api->cuvidCtxLock(m_lock, 0); } ~AutoCtxLock() { m_api->cuvidCtxUnlock(m_lock, 0); } }; class CUVIDAutoUnmapper { cuda_api *api; CUvideodecoder dec; CUdeviceptr devptr; public: CUVIDAutoUnmapper(cuda_api *a, CUvideodecoder d, CUdeviceptr p) : api(a), dec(d), devptr(p) {} ~CUVIDAutoUnmapper() { api->cuvidUnmapVideoFrame(dec, devptr); } }; #endif // CUDA_API_H QtAV-1.12.0/src/cuda/dllapi/000077500000000000000000000000001312235004300153475ustar00rootroot00000000000000QtAV-1.12.0/src/cuda/dllapi/cuda.cpp000066400000000000000000000443051312235004300167750ustar00rootroot00000000000000 #include "dllapi_p.h" #include "dllapi.h" // include nv_inc.h headers later to avoid build error. have not find out why it happens #define __CUVID_INTERNAL //avoid replaced bt 64 api #define __CUDA_API_VERSION_INTERNAL #include "nv_inc.h" using namespace dllapi; namespace dllapi { namespace cuda { namespace { //the macro define class dll. so a namespace wrapper is required //DEFINE_DLL_INSTANCE_N("cuda", "nvcuda", NULL) //now may crash for vc static char* cuda_names[] = { "nvcuda", NULL }; DEFINE_DLL_INSTANCE_V("cuda", cuda_names) } DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuInit, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuDriverGetVersion, int *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuDeviceGet, CUdevice *, int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuDeviceGetCount, int *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuDeviceGetName, char *, int, CUdevice) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuDeviceComputeCapability, int *, int *, CUdevice) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuDeviceTotalMem, int *, CUdevice) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuDeviceTotalMem_v2, int *, CUdevice) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuDeviceGetProperties, CUdevprop *, CUdevice) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuDeviceGetAttribute, int *, CUdevice_attribute, CUdevice) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuCtxCreate, CUcontext *, unsigned int, CUdevice) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuCtxCreate_v2, CUcontext *, unsigned int, CUdevice) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxDestroy, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxDestroy_v2, CUcontext) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuCtxAttach, CUcontext *, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxDetach, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxPushCurrent, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxPushCurrent_v2, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxPopCurrent, CUcontext *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxPopCurrent_v2, CUcontext *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxSetCurrent, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxGetCurrent, CUcontext *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxGetDevice, CUdevice *) DEFINE_DLLAPI_M_ARG(0, CUresult, CUDAAPI, cuCtxSynchronize) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuCtxSetLimit, CUlimit, int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuCtxGetLimit, int *, CUlimit) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxGetCacheConfig, CUfunc_cache *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxSetCacheConfig, CUfunc_cache) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxGetSharedMemConfig, CUsharedconfig *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxSetSharedMemConfig, CUsharedconfig) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuCtxGetApiVersion, CUcontext, unsigned int *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuModuleLoad, CUmodule *, const char *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuModuleLoadData, CUmodule *, const void *) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuModuleLoadDataEx, CUmodule *, const void *, unsigned int, CUjit_option *, void **) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuModuleLoadFatBinary, CUmodule *, const void *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuModuleUnload, CUmodule) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuModuleGetFunction, CUfunction *, CUmodule, const char *) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuModuleGetGlobal, CUdeviceptr *, int *, CUmodule, const char *) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuModuleGetGlobal_v2, CUdeviceptr *, int *, CUmodule, const char *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuModuleGetTexRef, CUtexref *, CUmodule, const char *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuModuleGetSurfRef, CUsurfref *, CUmodule, const char *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemGetInfo, int *, int *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemGetInfo_v2, int *, int *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAlloc, CUdeviceptr *, int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAlloc_v2, CUdeviceptr *, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemAllocPitch, CUdeviceptr *, int *, int, int, unsigned int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemAllocPitch_v2, CUdeviceptr *, int *, int, int, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemFree, CUdeviceptr) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemFree_v2, CUdeviceptr) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemGetAddressRange, CUdeviceptr *, int *, CUdeviceptr) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemGetAddressRange_v2, CUdeviceptr *, int *, CUdeviceptr) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAllocHost, void **, size_t) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAllocHost_v2, void **, size_t) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemFreeHost, void *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemHostAlloc, void **, int, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemHostGetDevicePointer, CUdeviceptr *, void *, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemHostGetDevicePointer_v2, CUdeviceptr *, void *, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemHostGetFlags, unsigned int *, void *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuDeviceGetByPCIBusId, CUdevice *, char *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuDeviceGetPCIBusId, char *, int, CUdevice) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuIpcGetEventHandle, CUipcEventHandle *, CUevent) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuIpcOpenEventHandle, CUevent *, CUipcEventHandle) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuIpcGetMemHandle, CUipcMemHandle *, CUdeviceptr) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuIpcOpenMemHandle, CUdeviceptr *, CUipcMemHandle, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuIpcCloseMemHandle, CUdeviceptr) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemHostRegister, void *, int, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemHostUnregister, void *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpy, CUdeviceptr, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyPeer, CUdeviceptr, CUcontext, CUdeviceptr, CUcontext, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyHtoD, CUdeviceptr, const void *, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyHtoD_v2, CUdeviceptr, const void *, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyDtoH, void *, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyDtoH_v2, void *, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyDtoD, CUdeviceptr, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemcpyDtoD_v2, CUdeviceptr, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoA, CUarray, int, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoA_v2, CUarray, int, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyAtoD, CUdeviceptr, CUarray, int, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyAtoD_v2, CUdeviceptr, CUarray, int, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyHtoA, CUarray, int, const void *, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyHtoA_v2, CUarray, int, const void *, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyAtoH, void *, CUarray, int, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyAtoH_v2, void *, CUarray, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyAtoA, CUarray, int, CUarray, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyAtoA_v2, CUarray, int, CUarray, int, int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy2D, const CUDA_MEMCPY2D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy2D_v2, const CUDA_MEMCPY2D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy2DUnaligned, const CUDA_MEMCPY2D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy2DUnaligned_v2, const CUDA_MEMCPY2D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy3D, const CUDA_MEMCPY3D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy3D_v2, const CUDA_MEMCPY3D *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuMemcpy3DPeer, const CUDA_MEMCPY3D_PEER *) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyAsync, CUdeviceptr, CUdeviceptr, int, CUstream) DEFINE_DLLAPI_M_ARG(6, CUresult, CUDAAPI, cuMemcpyPeerAsync, CUdeviceptr, CUcontext, CUdeviceptr, CUcontext, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyHtoDAsync, CUdeviceptr, const void *, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyHtoDAsync_v2, CUdeviceptr, const void *, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoHAsync, void *, CUdeviceptr, size_t, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoHAsync_v2, void *, CUdeviceptr, size_t, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoDAsync, CUdeviceptr, CUdeviceptr, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoDAsync_v2, CUdeviceptr, CUdeviceptr, int, CUstream) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyHtoAAsync, CUarray, int, const void *, int, CUstream) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyHtoAAsync_v2, CUarray, int, const void *, int, CUstream) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyAtoHAsync, void *, CUarray, int, int, CUstream) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemcpyAtoHAsync_v2, void *, CUarray, int, int, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemcpy2DAsync, const CUDA_MEMCPY2D *, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemcpy2DAsync_v2, const CUDA_MEMCPY2D *, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemcpy3DAsync, const CUDA_MEMCPY3D *, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemcpy3DAsync_v2, const CUDA_MEMCPY3D *, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemcpy3DPeerAsync, const CUDA_MEMCPY3D_PEER *, CUstream) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD8, CUdeviceptr, unsigned char, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD8_v2, CUdeviceptr, unsigned char, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD16, CUdeviceptr, unsigned short, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD16_v2, CUdeviceptr, unsigned short, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD32, CUdeviceptr, unsigned int, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuMemsetD32_v2, CUdeviceptr, unsigned int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D8, CUdeviceptr, int, unsigned char, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D8_v2, CUdeviceptr, int, unsigned char, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D16, CUdeviceptr, int, unsigned short, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D16_v2, CUdeviceptr, int, unsigned short, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D32, CUdeviceptr, int, unsigned int, int, int) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuMemsetD2D32_v2, CUdeviceptr, int, unsigned int, int, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemsetD8Async, CUdeviceptr, unsigned char, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemsetD16Async, CUdeviceptr, unsigned short, int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemsetD32Async, CUdeviceptr, unsigned int, int, CUstream) DEFINE_DLLAPI_M_ARG(6, CUresult, CUDAAPI, cuMemsetD2D8Async, CUdeviceptr, int, unsigned char, int, int, CUstream) DEFINE_DLLAPI_M_ARG(6, CUresult, CUDAAPI, cuMemsetD2D16Async, CUdeviceptr, int, unsigned short, int, int, CUstream) DEFINE_DLLAPI_M_ARG(6, CUresult, CUDAAPI, cuMemsetD2D32Async, CUdeviceptr, int, unsigned int, int, int, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArrayCreate, CUarray *, const CUDA_ARRAY_DESCRIPTOR *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArrayCreate_v2, CUarray *, const CUDA_ARRAY_DESCRIPTOR *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArrayGetDescriptor, CUDA_ARRAY_DESCRIPTOR *, CUarray) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArrayGetDescriptor_v2, CUDA_ARRAY_DESCRIPTOR *, CUarray) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuArrayDestroy, CUarray) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArray3DCreate, CUarray *, const CUDA_ARRAY3D_DESCRIPTOR *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArray3DCreate_v2, CUarray *, const CUDA_ARRAY3D_DESCRIPTOR *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArray3DGetDescriptor, CUDA_ARRAY3D_DESCRIPTOR *, CUarray) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuArray3DGetDescriptor_v2, CUDA_ARRAY3D_DESCRIPTOR *, CUarray) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuPointerGetAttribute, void *, CUpointer_attribute, CUdeviceptr) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuStreamCreate, CUstream *, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuStreamWaitEvent, CUstream, CUevent, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuStreamQuery, CUstream) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuStreamSynchronize, CUstream) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuStreamDestroy, CUstream) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuStreamDestroy_v2, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuEventCreate, CUevent *, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuEventRecord, CUevent, CUstream) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuEventQuery, CUevent) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuEventSynchronize, CUevent) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuEventDestroy, CUevent) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuEventDestroy_v2, CUevent) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuEventElapsedTime, float *, CUevent, CUevent) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuFuncGetAttribute, int *, CUfunction_attribute, CUfunction) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuFuncSetCacheConfig, CUfunction, CUfunc_cache) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuFuncSetSharedMemConfig, CUfunction, CUsharedconfig) DEFINE_DLLAPI_M_ARG(11, CUresult, CUDAAPI, cuLaunchKernel, CUfunction, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, CUstream, void **, void **) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuFuncSetBlockShape, CUfunction, int, int, int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuFuncSetSharedSize, CUfunction, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuParamSetSize, CUfunction, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuParamSeti, CUfunction, int, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuParamSetf, CUfunction, int, float) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuParamSetv, CUfunction, int, void *, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuLaunch, CUfunction) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuLaunchGrid, CUfunction, int, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuLaunchGridAsync, CUfunction, int, int, CUstream) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuParamSetTexRef, CUfunction, int, CUtexref) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuTexRefSetArray, CUtexref, CUarray, unsigned int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuTexRefSetAddress, int *, CUtexref, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuTexRefSetAddress_v2, int *, CUtexref, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuTexRefSetAddress2D, CUtexref, const CUDA_ARRAY_DESCRIPTOR *, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuTexRefSetAddress2D_v3, CUtexref, const CUDA_ARRAY_DESCRIPTOR *, CUdeviceptr, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuTexRefSetFormat, CUtexref, CUarray_format, int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuTexRefSetAddressMode, CUtexref, int, CUaddress_mode) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefSetFilterMode, CUtexref, CUfilter_mode) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefSetFlags, CUtexref, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefGetAddress, CUdeviceptr *, CUtexref) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefGetAddress_v2, CUdeviceptr *, CUtexref) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefGetArray, CUarray *, CUtexref) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuTexRefGetAddressMode, CUaddress_mode *, CUtexref, int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefGetFilterMode, CUfilter_mode *, CUtexref) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuTexRefGetFormat, CUarray_format *, int *, CUtexref) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuTexRefGetFlags, unsigned int *, CUtexref) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuTexRefCreate, CUtexref *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuTexRefDestroy, CUtexref) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuSurfRefSetArray, CUsurfref, CUarray, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuSurfRefGetArray, CUarray *, CUsurfref) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuDeviceCanAccessPeer, int *, CUdevice, CUdevice) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuCtxEnablePeerAccess, CUcontext, unsigned int) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuCtxDisablePeerAccess, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuGraphicsUnregisterResource, CUgraphicsResource) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuGraphicsSubResourceGetMappedArray, CUarray *, CUgraphicsResource, unsigned int, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuGraphicsResourceGetMappedPointer, CUdeviceptr *, int *, CUgraphicsResource) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuGraphicsResourceGetMappedPointer_v2, CUdeviceptr *, int *, CUgraphicsResource) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuGraphicsResourceSetMapFlags, CUgraphicsResource, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuGraphicsMapResources, unsigned int, CUgraphicsResource *, CUstream) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuGraphicsUnmapResources, unsigned int, CUgraphicsResource *, CUstream) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuGetExportTable, const void **, const CUuuid *) #if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAllocHost, void **, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuMemAllocHost_v2, void **, unsigned int) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoHAsync, void *, CUdeviceptr, unsigned int, CUstream) DEFINE_DLLAPI_M_ARG(4, CUresult, CUDAAPI, cuMemcpyDtoHAsync_v2, void *, CUdeviceptr, unsigned int, CUstream) #endif } //namespace cuda } //namespace dllapi QtAV-1.12.0/src/cuda/dllapi/cuviddec.cpp000066400000000000000000000034461312235004300176500ustar00rootroot00000000000000 #include "dllapi_p.h" #include "dllapi.h" // include nv_inc.h headers later to avoid build error. have not find out why it happens #define __CUVID_INTERNAL //avoid replaced bt 64 api #define __CUDA_API_VERSION_INTERNAL #include "nv_inc.h" using namespace dllapi; namespace dllapi { namespace cuda { namespace cuviddec { //the macro define class dll. so a namespace wrapper is required //DEFINE_DLL_INSTANCE_N("cuviddec", "nvcuvid", NULL) //now may crash for vc static char* cuvid_names[] = { "nvcuvid", NULL }; DEFINE_DLL_INSTANCE_V("cuviddec", cuvid_names) } using namespace cuviddec; DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidCreateDecoder, CUvideodecoder *, CUVIDDECODECREATEINFO *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuvidDestroyDecoder, CUvideodecoder) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidDecodePicture, CUvideodecoder, CUVIDPICPARAMS *) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuvidMapVideoFrame, CUvideodecoder, int, unsigned int *, unsigned int *, CUVIDPROCPARAMS *) DEFINE_DLLAPI_M_ARG(5, CUresult, CUDAAPI, cuvidMapVideoFrame64, CUvideodecoder, int, unsigned long long *, unsigned int *, CUVIDPROCPARAMS *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidUnmapVideoFrame, CUvideodecoder, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidUnmapVideoFrame64, CUvideodecoder, unsigned long long) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuvidGetVideoFrameSurface, CUvideodecoder, int, void **) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidCtxLockCreate, CUvideoctxlock *, CUcontext) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuvidCtxLockDestroy, CUvideoctxlock) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidCtxLock, CUvideoctxlock, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidCtxUnlock, CUvideoctxlock, unsigned int) } //namespace cuviddec } //namespace dllapi QtAV-1.12.0/src/cuda/dllapi/nv_inc.h000066400000000000000000000041551312235004300170010ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef NV_INC_H #define NV_INC_H #undef NV_CONFIG #define NV_CONFIG(FEATURE) (defined QTAV_HAVE_##FEATURE && QTAV_HAVE_##FEATURE) // TODO: remove DLLAPI_CUDA code // high version will define cuXXX macro, so functions here will be not they look like #if !NV_CONFIG(DLLAPI_CUDA) && !defined(CUDA_LINK) //#define CUDA_FORCE_API_VERSION 3010 //enable this line to build with old cuda APIs ///#define __CUDA_API_VERSION_INTERNAL #endif #if NV_CONFIG(DLLAPI_CUDA) namespace dllapi { namespace cuda { #endif /*NV_CONFIG(DLLAPI_CUDA)*/ #if defined(__cplusplus) //extern "C" { #endif /* __cplusplus */ #ifdef HAVE_CUDA_H #include "cuda.h" #include "nvcuvid.h" #else #include "dynlink_cuda.h" #include "dynlink_nvcuvid.h" #endif // __CUDA_API_VERSION is undefined in cuda.h #ifdef CUDA_FORCE_API_VERSION #define __CUDA_API_VERSION CUDA_FORCE_API_VERSION #else #ifndef __CUDA_API_VERSION #define __CUDA_API_VERSION 4000 #endif #endif #if defined(__cplusplus) //} #endif /* __cplusplus */ #if NV_CONFIG(DLLAPI_CUDA) } //namespace cuda } //namespace dllapi #endif /*NV_CONFIG(DLLAPI_CUDA)*/ #endif /* NV_INC_H*/ QtAV-1.12.0/src/cuda/dllapi/nvcuvid.cpp000066400000000000000000000031651312235004300175360ustar00rootroot00000000000000#include "dllapi_p.h" #include "dllapi.h" // include nv_inc.h headers later to avoid build error. have not find out why it happens #define __CUVID_INTERNAL //avoid replaced bt 64 api #define __CUDA_API_VERSION_INTERNAL #include "nv_inc.h" using namespace dllapi; namespace dllapi { namespace cuda { namespace nvcuvid { //the macro define class dll. so a namespace wrapper is required //DEFINE_DLL_INSTANCE_N("nvcuvid", "nvcuvid", NULL) //now may crash for vc static char* cuvid_names[] = { "nvcuvid", NULL }; DEFINE_DLL_INSTANCE_V("nvcuvid", cuvid_names) } using namespace nvcuvid; DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuvidCreateVideoSource, CUvideosource *, const char *, CUVIDSOURCEPARAMS *) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuvidCreateVideoSourceW, CUvideosource *, const int *, CUVIDSOURCEPARAMS *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuvidDestroyVideoSource, CUvideosource) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidSetVideoSourceState, CUvideosource, cudaVideoState) DEFINE_DLLAPI_M_ARG(1, cudaVideoState, CUDAAPI, cuvidGetVideoSourceState, CUvideosource) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuvidGetSourceVideoFormat, CUvideosource, CUVIDEOFORMAT *, unsigned int) DEFINE_DLLAPI_M_ARG(3, CUresult, CUDAAPI, cuvidGetSourceAudioFormat, CUvideosource, CUAUDIOFORMAT *, unsigned int) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidCreateVideoParser, CUvideoparser *, CUVIDPARSERPARAMS *) DEFINE_DLLAPI_M_ARG(2, CUresult, CUDAAPI, cuvidParseVideoData, CUvideoparser, CUVIDSOURCEDATAPACKET *) DEFINE_DLLAPI_M_ARG(1, CUresult, CUDAAPI, cuvidDestroyVideoParser, CUvideoparser) } //namespace cuvid } //namespace dllapi QtAV-1.12.0/src/cuda/dynlink_cuda.h000066400000000000000000000500041312235004300167160ustar00rootroot00000000000000/* * This copyright notice applies to this header file only: * * Copyright (c) 2016 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the software, and to permit persons to whom the * software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __cuda_cuda_h__ #define __cuda_cuda_h__ #include #define CUDA_VERSION 7050 #if defined(_WIN32) || defined(__CYGWIN__) #define CUDAAPI __stdcall #else #define CUDAAPI #endif typedef int CUdevice; typedef struct CUarray_st *CUarray; /**< CUDA array */ typedef struct CUctx_st *CUcontext; /**< CUDA context */ #if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) typedef unsigned long long CUdeviceptr; #else typedef unsigned int CUdeviceptr; #endif typedef struct CUstream_st *CUstream; /**< CUDA stream */ typedef struct CUgraphicsResource_st *CUgraphicsResource; /**< CUDA graphics interop resource */ /** * Context creation flags */ typedef enum CUctx_flags_enum { CU_CTX_SCHED_AUTO = 0x00, /**< Automatic scheduling */ CU_CTX_SCHED_SPIN = 0x01, /**< Set spin as default scheduling */ CU_CTX_SCHED_YIELD = 0x02, /**< Set yield as default scheduling */ CU_CTX_SCHED_BLOCKING_SYNC = 0x04, /**< Set blocking synchronization as default scheduling */ CU_CTX_BLOCKING_SYNC = 0x04, /**< Set blocking synchronization as default scheduling \deprecated */ CU_CTX_MAP_HOST = 0x08, /**< Support mapped pinned allocations */ CU_CTX_LMEM_RESIZE_TO_MAX = 0x10, /**< Keep local memory allocation after launch */ #if __CUDA_API_VERSION < 4000 CU_CTX_SCHED_MASK = 0x03, CU_CTX_FLAGS_MASK = 0x1f #else CU_CTX_SCHED_MASK = 0x07, CU_CTX_PRIMARY = 0x20, /**< Initialize and return the primary context */ CU_CTX_FLAGS_MASK = 0x3f #endif } CUctx_flags; /** * Stream creation flags */ typedef enum CUstream_flags_enum { CU_STREAM_DEFAULT = 0x0, /**< Default stream flag */ CU_STREAM_NON_BLOCKING = 0x1 /**< Stream does not synchronize with stream 0 (the NULL stream) */ } CUstream_flags; /** * Device properties */ typedef enum CUdevice_attribute_enum { CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK = 1, /**< Maximum number of threads per block */ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X = 2, /**< Maximum block dimension X */ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y = 3, /**< Maximum block dimension Y */ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z = 4, /**< Maximum block dimension Z */ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X = 5, /**< Maximum grid dimension X */ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y = 6, /**< Maximum grid dimension Y */ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z = 7, /**< Maximum grid dimension Z */ CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK = 8, /**< Maximum shared memory available per block in bytes */ CU_DEVICE_ATTRIBUTE_SHARED_MEMORY_PER_BLOCK = 8, /**< Deprecated, use CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK */ CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY = 9, /**< Memory available on device for __constant__ variables in a CUDA C kernel in bytes */ CU_DEVICE_ATTRIBUTE_WARP_SIZE = 10, /**< Warp size in threads */ CU_DEVICE_ATTRIBUTE_MAX_PITCH = 11, /**< Maximum pitch in bytes allowed by memory copies */ CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK = 12, /**< Maximum number of 32-bit registers available per block */ CU_DEVICE_ATTRIBUTE_REGISTERS_PER_BLOCK = 12, /**< Deprecated, use CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK */ CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13, /**< Peak clock frequency in kilohertz */ CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT = 14, /**< Alignment requirement for textures */ CU_DEVICE_ATTRIBUTE_GPU_OVERLAP = 15, /**< Device can possibly copy memory and execute a kernel concurrently */ CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16, /**< Number of multiprocessors on device */ CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT = 17, /**< Specifies whether there is a run time limit on kernels */ CU_DEVICE_ATTRIBUTE_INTEGRATED = 18, /**< Device is integrated with host memory */ CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY = 19, /**< Device can map host memory into CUDA address space */ CU_DEVICE_ATTRIBUTE_COMPUTE_MODE = 20, /**< Compute mode (See ::CUcomputemode for details) */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_WIDTH = 21, /**< Maximum 1D texture width */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_WIDTH = 22, /**< Maximum 2D texture width */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_HEIGHT = 23, /**< Maximum 2D texture height */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_WIDTH = 24, /**< Maximum 3D texture width */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_HEIGHT = 25, /**< Maximum 3D texture height */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_DEPTH = 26, /**< Maximum 3D texture depth */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_ARRAY_WIDTH = 27, /**< Maximum texture array width */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_ARRAY_HEIGHT = 28, /**< Maximum texture array height */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_ARRAY_NUMSLICES = 29, /**< Maximum slices in a texture array */ CU_DEVICE_ATTRIBUTE_SURFACE_ALIGNMENT = 30, /**< Alignment requirement for surfaces */ CU_DEVICE_ATTRIBUTE_CONCURRENT_KERNELS = 31, /**< Device can possibly execute multiple kernels concurrently */ CU_DEVICE_ATTRIBUTE_ECC_ENABLED = 32, /**< Device has ECC support enabled */ CU_DEVICE_ATTRIBUTE_PCI_BUS_ID = 33, /**< PCI bus ID of the device */ CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID = 34, /**< PCI device ID of the device */ CU_DEVICE_ATTRIBUTE_TCC_DRIVER = 35 /**< Device is using TCC driver model */ #if __CUDA_API_VERSION >= 4000 , CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE = 36, /**< Peak memory clock frequency in kilohertz */ CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH = 37, /**< Global memory bus width in bits */ CU_DEVICE_ATTRIBUTE_L2_CACHE_SIZE = 38, /**< Size of L2 cache in bytes */ CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR = 39, /**< Maximum resident threads per multiprocessor */ CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT = 40, /**< Number of asynchronous engines */ CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING = 41, /**< Device uses shares a unified address space with the host */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_LAYERED_WIDTH = 42, /**< Maximum 1D layered texture width */ CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_LAYERED_LAYERS = 43 /**< Maximum layers in a 1D layered texture */ #endif } CUdevice_attribute; /** * Error codes */ typedef enum cudaError_enum { /** * The API call returned with no errors. In the case of query calls, this * can also mean that the operation being queried is complete (see * ::cuEventQuery() and ::cuStreamQuery()). */ CUDA_SUCCESS = 0, /** * This indicates that one or more of the parameters passed to the API call * is not within an acceptable range of values. */ CUDA_ERROR_INVALID_VALUE = 1, /** * The API call failed because it was unable to allocate enough memory to * perform the requested operation. */ CUDA_ERROR_OUT_OF_MEMORY = 2, /** * This indicates that the CUDA driver has not been initialized with * ::cuInit() or that initialization has failed. */ CUDA_ERROR_NOT_INITIALIZED = 3, /** * This indicates that the CUDA driver is in the process of shutting down. */ CUDA_ERROR_DEINITIALIZED = 4, /** * This indicates profiling APIs are called while application is running * in visual profiler mode. */ CUDA_ERROR_PROFILER_DISABLED = 5, /** * This indicates profiling has not been initialized for this context. * Call cuProfilerInitialize() to resolve this. */ CUDA_ERROR_PROFILER_NOT_INITIALIZED = 6, /** * This indicates profiler has already been started and probably * cuProfilerStart() is incorrectly called. */ CUDA_ERROR_PROFILER_ALREADY_STARTED = 7, /** * This indicates profiler has already been stopped and probably * cuProfilerStop() is incorrectly called. */ CUDA_ERROR_PROFILER_ALREADY_STOPPED = 8, /** * This indicates that no CUDA-capable devices were detected by the installed * CUDA driver. */ CUDA_ERROR_NO_DEVICE = 100, /** * This indicates that the device ordinal supplied by the user does not * correspond to a valid CUDA device. */ CUDA_ERROR_INVALID_DEVICE = 101, /** * This indicates that the device kernel image is invalid. This can also * indicate an invalid CUDA module. */ CUDA_ERROR_INVALID_IMAGE = 200, /** * This most frequently indicates that there is no context bound to the * current thread. This can also be returned if the context passed to an * API call is not a valid handle (such as a context that has had * ::cuCtxDestroy() invoked on it). This can also be returned if a user * mixes different API versions (i.e. 3010 context with 3020 API calls). * See ::cuCtxGetApiVersion() for more details. */ CUDA_ERROR_INVALID_CONTEXT = 201, /** * This indicated that the context being supplied as a parameter to the * API call was already the active context. * \deprecated * This error return is deprecated as of CUDA 3.2. It is no longer an * error to attempt to push the active context via ::cuCtxPushCurrent(). */ CUDA_ERROR_CONTEXT_ALREADY_CURRENT = 202, /** * This indicates that a map or register operation has failed. */ CUDA_ERROR_MAP_FAILED = 205, /** * This indicates that an unmap or unregister operation has failed. */ CUDA_ERROR_UNMAP_FAILED = 206, /** * This indicates that the specified array is currently mapped and thus * cannot be destroyed. */ CUDA_ERROR_ARRAY_IS_MAPPED = 207, /** * This indicates that the resource is already mapped. */ CUDA_ERROR_ALREADY_MAPPED = 208, /** * This indicates that there is no kernel image available that is suitable * for the device. This can occur when a user specifies code generation * options for a particular CUDA source file that do not include the * corresponding device configuration. */ CUDA_ERROR_NO_BINARY_FOR_GPU = 209, /** * This indicates that a resource has already been acquired. */ CUDA_ERROR_ALREADY_ACQUIRED = 210, /** * This indicates that a resource is not mapped. */ CUDA_ERROR_NOT_MAPPED = 211, /** * This indicates that a mapped resource is not available for access as an * array. */ CUDA_ERROR_NOT_MAPPED_AS_ARRAY = 212, /** * This indicates that a mapped resource is not available for access as a * pointer. */ CUDA_ERROR_NOT_MAPPED_AS_POINTER = 213, /** * This indicates that an uncorrectable ECC error was detected during * execution. */ CUDA_ERROR_ECC_UNCORRECTABLE = 214, /** * This indicates that the ::CUlimit passed to the API call is not * supported by the active device. */ CUDA_ERROR_UNSUPPORTED_LIMIT = 215, /** * This indicates that the ::CUcontext passed to the API call can * only be bound to a single CPU thread at a time but is already * bound to a CPU thread. */ CUDA_ERROR_CONTEXT_ALREADY_IN_USE = 216, /** * This indicates that the device kernel source is invalid. */ CUDA_ERROR_INVALID_SOURCE = 300, /** * This indicates that the file specified was not found. */ CUDA_ERROR_FILE_NOT_FOUND = 301, /** * This indicates that a link to a shared object failed to resolve. */ CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302, /** * This indicates that initialization of a shared object failed. */ CUDA_ERROR_SHARED_OBJECT_INIT_FAILED = 303, /** * This indicates that an OS call failed. */ CUDA_ERROR_OPERATING_SYSTEM = 304, /** * This indicates that a resource handle passed to the API call was not * valid. Resource handles are opaque types like ::CUstream and ::CUevent. */ CUDA_ERROR_INVALID_HANDLE = 400, /** * This indicates that a named symbol was not found. Examples of symbols * are global/constant variable names, texture names, and surface names. */ CUDA_ERROR_NOT_FOUND = 500, /** * This indicates that asynchronous operations issued previously have not * completed yet. This result is not actually an error, but must be indicated * differently than ::CUDA_SUCCESS (which indicates completion). Calls that * may return this value include ::cuEventQuery() and ::cuStreamQuery(). */ CUDA_ERROR_NOT_READY = 600, /** * An exception occurred on the device while executing a kernel. Common * causes include dereferencing an invalid device pointer and accessing * out of bounds shared memory. The context cannot be used, so it must * be destroyed (and a new one should be created). All existing device * memory allocations from this context are invalid and must be * reconstructed if the program is to continue using CUDA. */ CUDA_ERROR_LAUNCH_FAILED = 700, /** * This indicates that a launch did not occur because it did not have * appropriate resources. This error usually indicates that the user has * attempted to pass too many arguments to the device kernel, or the * kernel launch specifies too many threads for the kernel's register * count. Passing arguments of the wrong size (i.e. a 64-bit pointer * when a 32-bit int is expected) is equivalent to passing too many * arguments and can also result in this error. */ CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES = 701, /** * This indicates that the device kernel took too long to execute. This can * only occur if timeouts are enabled - see the device attribute * ::CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT for more information. The * context cannot be used (and must be destroyed similar to * ::CUDA_ERROR_LAUNCH_FAILED). All existing device memory allocations from * this context are invalid and must be reconstructed if the program is to * continue using CUDA. */ CUDA_ERROR_LAUNCH_TIMEOUT = 702, /** * This error indicates a kernel launch that uses an incompatible texturing * mode. */ CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING = 703, /** * This error indicates that a call to ::cuCtxEnablePeerAccess() is * trying to re-enable peer access to a context which has already * had peer access to it enabled. */ CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED = 704, /** * This error indicates that a call to ::cuMemPeerRegister is trying to * register memory from a context which has not had peer access * enabled yet via ::cuCtxEnablePeerAccess(), or that * ::cuCtxDisablePeerAccess() is trying to disable peer access * which has not been enabled yet. */ CUDA_ERROR_PEER_ACCESS_NOT_ENABLED = 705, /** * This error indicates that a call to ::cuMemPeerRegister is trying to * register already-registered memory. */ CUDA_ERROR_PEER_MEMORY_ALREADY_REGISTERED = 706, /** * This error indicates that a call to ::cuMemPeerUnregister is trying to * unregister memory that has not been registered. */ CUDA_ERROR_PEER_MEMORY_NOT_REGISTERED = 707, /** * This error indicates that ::cuCtxCreate was called with the flag * ::CU_CTX_PRIMARY on a device which already has initialized its * primary context. */ CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE = 708, /** * This error indicates that the context current to the calling thread * has been destroyed using ::cuCtxDestroy, or is a primary context which * has not yet been initialized. */ CUDA_ERROR_CONTEXT_IS_DESTROYED = 709, /** * This indicates that an unknown internal error has occurred. */ CUDA_ERROR_UNKNOWN = 999 } CUresult; /** * Memory types */ typedef enum CUmemorytype_enum { CU_MEMORYTYPE_HOST = 0x01, /**< Host memory */ CU_MEMORYTYPE_DEVICE = 0x02, /**< Device memory */ CU_MEMORYTYPE_ARRAY = 0x03 /**< Array memory */ #if __CUDA_API_VERSION >= 4000 , CU_MEMORYTYPE_UNIFIED = 0x04 /**< Unified device or host memory */ #endif } CUmemorytype; /** * Compute Modes */ typedef enum CUcomputemode_enum { CU_COMPUTEMODE_DEFAULT = 0, /**< Default compute mode (Multiple contexts allowed per device) */ CU_COMPUTEMODE_EXCLUSIVE = 1, /**< Compute-exclusive-thread mode (Only one context used by a single thread can be present on this device at a time) */ CU_COMPUTEMODE_PROHIBITED = 2 /**< Compute-prohibited mode (No contexts can be created on this device at this time) */ #if __CUDA_API_VERSION >= 4000 , CU_COMPUTEMODE_EXCLUSIVE_PROCESS = 3 /**< Compute-exclusive-process mode (Only one context used by a single process can be present on this device at a time) */ #endif } CUcomputemode; /** * Flags to register a graphics resource */ typedef enum CUgraphicsRegisterFlags_enum { CU_GRAPHICS_REGISTER_FLAGS_NONE = 0x00, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 0x01, CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 0x02, CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 0x04 } CUgraphicsRegisterFlags; /** * Flags for mapping and unmapping interop resources */ typedef enum CUgraphicsMapResourceFlags_enum { CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE = 0x00, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01, CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02 } CUgraphicsMapResourceFlags; typedef struct CUDA_MEMCPY2D_st { size_t srcXInBytes; size_t srcY; CUmemorytype srcMemoryType; const void *srcHost; CUdeviceptr srcDevice; CUarray srcArray; size_t srcPitch; size_t dstXInBytes; size_t dstY; CUmemorytype dstMemoryType; void *dstHost; CUdeviceptr dstDevice; CUarray dstArray; size_t dstPitch; size_t WidthInBytes; size_t Height; } CUDA_MEMCPY2D; #endif QtAV-1.12.0/src/cuda/dynlink_cuviddec.h000066400000000000000000000640311312235004300175750ustar00rootroot00000000000000/* * This copyright notice applies to this header file only: * * Copyright (c) 2010-2016 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the software, and to permit persons to whom the * software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * \file cuviddec.h * NvCuvid API provides Video Decoding interface to NVIDIA GPU devices. * \date 2015-2016 * This file contains constants, structure definitions and function prototypes used for decoding. */ #if !defined(__CUDA_VIDEO_H__) #define __CUDA_VIDEO_H__ #if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) #if (CUDA_VERSION >= 3020) && (!defined(CUDA_FORCE_API_VERSION) || (CUDA_FORCE_API_VERSION >= 3020)) #define __CUVID_DEVPTR64 #endif #endif #if defined(__cplusplus) extern "C" { #endif /* __cplusplus */ typedef void *CUvideodecoder; typedef struct _CUcontextlock_st *CUvideoctxlock; /** * \addtogroup VIDEO_DECODER Video Decoder * @{ */ /*! * \enum cudaVideoCodec * Video Codec Enums */ typedef enum cudaVideoCodec_enum { cudaVideoCodec_MPEG1=0, /**< MPEG1 */ cudaVideoCodec_MPEG2, /**< MPEG2 */ cudaVideoCodec_MPEG4, /**< MPEG4 */ cudaVideoCodec_VC1, /**< VC1 */ cudaVideoCodec_H264, /**< H264 */ cudaVideoCodec_JPEG, /**< JPEG */ cudaVideoCodec_H264_SVC, /**< H264-SVC */ cudaVideoCodec_H264_MVC, /**< H264-MVC */ cudaVideoCodec_HEVC, /**< HEVC */ cudaVideoCodec_VP8, /**< VP8 */ cudaVideoCodec_VP9, /**< VP9 */ cudaVideoCodec_NumCodecs, /**< Max COdecs */ // Uncompressed YUV cudaVideoCodec_YUV420 = (('I'<<24)|('Y'<<16)|('U'<<8)|('V')), /**< Y,U,V (4:2:0) */ cudaVideoCodec_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,V,U (4:2:0) */ cudaVideoCodec_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,UV (4:2:0) */ cudaVideoCodec_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), /**< YUYV/YUY2 (4:2:2) */ cudaVideoCodec_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) /**< UYVY (4:2:2) */ } cudaVideoCodec; /*! * \enum cudaVideoSurfaceFormat * Video Surface Formats Enums */ typedef enum cudaVideoSurfaceFormat_enum { cudaVideoSurfaceFormat_NV12=0, /**< NV12 */ cudaVideoSurfaceFormat_P016=1 /**< P016 */ } cudaVideoSurfaceFormat; /*! * \enum cudaVideoDeinterlaceMode * Deinterlacing Modes Enums */ typedef enum cudaVideoDeinterlaceMode_enum { cudaVideoDeinterlaceMode_Weave=0, /**< Weave both fields (no deinterlacing) */ cudaVideoDeinterlaceMode_Bob, /**< Drop one field */ cudaVideoDeinterlaceMode_Adaptive /**< Adaptive deinterlacing */ } cudaVideoDeinterlaceMode; /*! * \enum cudaVideoChromaFormat * Chroma Formats Enums */ typedef enum cudaVideoChromaFormat_enum { cudaVideoChromaFormat_Monochrome=0, /**< MonoChrome */ cudaVideoChromaFormat_420, /**< 4:2:0 */ cudaVideoChromaFormat_422, /**< 4:2:2 */ cudaVideoChromaFormat_444 /**< 4:4:4 */ } cudaVideoChromaFormat; /*! * \enum cudaVideoCreateFlags * Decoder Flags Enums */ typedef enum cudaVideoCreateFlags_enum { cudaVideoCreate_Default = 0x00, /**< Default operation mode: use dedicated video engines */ cudaVideoCreate_PreferCUDA = 0x01, /**< Use a CUDA-based decoder if faster than dedicated engines (requires a valid vidLock object for multi-threading) */ cudaVideoCreate_PreferDXVA = 0x02, /**< Go through DXVA internally if possible (requires D3D9 interop) */ cudaVideoCreate_PreferCUVID = 0x04 /**< Use dedicated video engines directly */ } cudaVideoCreateFlags; /*! * \struct CUVIDDECODECREATEINFO * Struct used in create decoder */ typedef struct _CUVIDDECODECREATEINFO { unsigned long ulWidth; /**< Coded Sequence Width */ unsigned long ulHeight; /**< Coded Sequence Height */ unsigned long ulNumDecodeSurfaces; /**< Maximum number of internal decode surfaces */ cudaVideoCodec CodecType; /**< cudaVideoCodec_XXX */ cudaVideoChromaFormat ChromaFormat; /**< cudaVideoChromaFormat_XXX (only 4:2:0 is currently supported) */ unsigned long ulCreationFlags; /**< Decoder creation flags (cudaVideoCreateFlags_XXX) */ unsigned long bitDepthMinus8; unsigned long Reserved1[4]; /**< Reserved for future use - set to zero */ /** * area of the frame that should be displayed */ struct { short left; short top; short right; short bottom; } display_area; cudaVideoSurfaceFormat OutputFormat; /**< cudaVideoSurfaceFormat_XXX */ cudaVideoDeinterlaceMode DeinterlaceMode; /**< cudaVideoDeinterlaceMode_XXX */ unsigned long ulTargetWidth; /**< Post-processed Output Width (Should be aligned to 2) */ unsigned long ulTargetHeight; /**< Post-processed Output Height (Should be aligbed to 2) */ unsigned long ulNumOutputSurfaces; /**< Maximum number of output surfaces simultaneously mapped */ CUvideoctxlock vidLock; /**< If non-NULL, context lock used for synchronizing ownership of the cuda context */ /** * target rectangle in the output frame (for aspect ratio conversion) * if a null rectangle is specified, {0,0,ulTargetWidth,ulTargetHeight} will be used */ struct { short left; short top; short right; short bottom; } target_rect; unsigned long Reserved2[5]; /**< Reserved for future use - set to zero */ } CUVIDDECODECREATEINFO; /*! * \struct CUVIDH264DPBENTRY * H.264 DPB Entry */ typedef struct _CUVIDH264DPBENTRY { int PicIdx; /**< picture index of reference frame */ int FrameIdx; /**< frame_num(short-term) or LongTermFrameIdx(long-term) */ int is_long_term; /**< 0=short term reference, 1=long term reference */ int not_existing; /**< non-existing reference frame (corresponding PicIdx should be set to -1) */ int used_for_reference; /**< 0=unused, 1=top_field, 2=bottom_field, 3=both_fields */ int FieldOrderCnt[2]; /**< field order count of top and bottom fields */ } CUVIDH264DPBENTRY; /*! * \struct CUVIDH264MVCEXT * H.264 MVC Picture Parameters Ext */ typedef struct _CUVIDH264MVCEXT { int num_views_minus1; int view_id; unsigned char inter_view_flag; unsigned char num_inter_view_refs_l0; unsigned char num_inter_view_refs_l1; unsigned char MVCReserved8Bits; int InterViewRefsL0[16]; int InterViewRefsL1[16]; } CUVIDH264MVCEXT; /*! * \struct CUVIDH264SVCEXT * H.264 SVC Picture Parameters Ext */ typedef struct _CUVIDH264SVCEXT { unsigned char profile_idc; unsigned char level_idc; unsigned char DQId; unsigned char DQIdMax; unsigned char disable_inter_layer_deblocking_filter_idc; unsigned char ref_layer_chroma_phase_y_plus1; signed char inter_layer_slice_alpha_c0_offset_div2; signed char inter_layer_slice_beta_offset_div2; unsigned short DPBEntryValidFlag; unsigned char inter_layer_deblocking_filter_control_present_flag; unsigned char extended_spatial_scalability_idc; unsigned char adaptive_tcoeff_level_prediction_flag; unsigned char slice_header_restriction_flag; unsigned char chroma_phase_x_plus1_flag; unsigned char chroma_phase_y_plus1; unsigned char tcoeff_level_prediction_flag; unsigned char constrained_intra_resampling_flag; unsigned char ref_layer_chroma_phase_x_plus1_flag; unsigned char store_ref_base_pic_flag; unsigned char Reserved8BitsA; unsigned char Reserved8BitsB; // For the 4 scaled_ref_layer_XX fields below, // if (extended_spatial_scalability_idc == 1), SPS field, G.7.3.2.1.4, add prefix "seq_" // if (extended_spatial_scalability_idc == 2), SLH field, G.7.3.3.4, short scaled_ref_layer_left_offset; short scaled_ref_layer_top_offset; short scaled_ref_layer_right_offset; short scaled_ref_layer_bottom_offset; unsigned short Reserved16Bits; struct _CUVIDPICPARAMS *pNextLayer; /**< Points to the picparams for the next layer to be decoded. Linked list ends at the target layer. */ int bRefBaseLayer; /**< whether to store ref base pic */ } CUVIDH264SVCEXT; /*! * \struct CUVIDH264PICPARAMS * H.264 Picture Parameters */ typedef struct _CUVIDH264PICPARAMS { // SPS int log2_max_frame_num_minus4; int pic_order_cnt_type; int log2_max_pic_order_cnt_lsb_minus4; int delta_pic_order_always_zero_flag; int frame_mbs_only_flag; int direct_8x8_inference_flag; int num_ref_frames; // NOTE: shall meet level 4.1 restrictions unsigned char residual_colour_transform_flag; unsigned char bit_depth_luma_minus8; // Must be 0 (only 8-bit supported) unsigned char bit_depth_chroma_minus8; // Must be 0 (only 8-bit supported) unsigned char qpprime_y_zero_transform_bypass_flag; // PPS int entropy_coding_mode_flag; int pic_order_present_flag; int num_ref_idx_l0_active_minus1; int num_ref_idx_l1_active_minus1; int weighted_pred_flag; int weighted_bipred_idc; int pic_init_qp_minus26; int deblocking_filter_control_present_flag; int redundant_pic_cnt_present_flag; int transform_8x8_mode_flag; int MbaffFrameFlag; int constrained_intra_pred_flag; int chroma_qp_index_offset; int second_chroma_qp_index_offset; int ref_pic_flag; int frame_num; int CurrFieldOrderCnt[2]; // DPB CUVIDH264DPBENTRY dpb[16]; // List of reference frames within the DPB // Quantization Matrices (raster-order) unsigned char WeightScale4x4[6][16]; unsigned char WeightScale8x8[2][64]; // FMO/ASO unsigned char fmo_aso_enable; unsigned char num_slice_groups_minus1; unsigned char slice_group_map_type; signed char pic_init_qs_minus26; unsigned int slice_group_change_rate_minus1; union { unsigned long long slice_group_map_addr; const unsigned char *pMb2SliceGroupMap; } fmo; unsigned int Reserved[12]; // SVC/MVC union { CUVIDH264MVCEXT mvcext; CUVIDH264SVCEXT svcext; } svcmvc; } CUVIDH264PICPARAMS; /*! * \struct CUVIDMPEG2PICPARAMS * MPEG-2 Picture Parameters */ typedef struct _CUVIDMPEG2PICPARAMS { int ForwardRefIdx; // Picture index of forward reference (P/B-frames) int BackwardRefIdx; // Picture index of backward reference (B-frames) int picture_coding_type; int full_pel_forward_vector; int full_pel_backward_vector; int f_code[2][2]; int intra_dc_precision; int frame_pred_frame_dct; int concealment_motion_vectors; int q_scale_type; int intra_vlc_format; int alternate_scan; int top_field_first; // Quantization matrices (raster order) unsigned char QuantMatrixIntra[64]; unsigned char QuantMatrixInter[64]; } CUVIDMPEG2PICPARAMS; //////////////////////////////////////////////////////////////////////////////////////////////// // // MPEG-4 Picture Parameters // // MPEG-4 has VOP types instead of Picture types #define I_VOP 0 #define P_VOP 1 #define B_VOP 2 #define S_VOP 3 /*! * \struct CUVIDMPEG4PICPARAMS * MPEG-4 Picture Parameters */ typedef struct _CUVIDMPEG4PICPARAMS { int ForwardRefIdx; // Picture index of forward reference (P/B-frames) int BackwardRefIdx; // Picture index of backward reference (B-frames) // VOL int video_object_layer_width; int video_object_layer_height; int vop_time_increment_bitcount; int top_field_first; int resync_marker_disable; int quant_type; int quarter_sample; int short_video_header; int divx_flags; // VOP int vop_coding_type; int vop_coded; int vop_rounding_type; int alternate_vertical_scan_flag; int interlaced; int vop_fcode_forward; int vop_fcode_backward; int trd[2]; int trb[2]; // Quantization matrices (raster order) unsigned char QuantMatrixIntra[64]; unsigned char QuantMatrixInter[64]; int gmc_enabled; } CUVIDMPEG4PICPARAMS; /*! * \struct CUVIDVC1PICPARAMS * VC1 Picture Parameters */ typedef struct _CUVIDVC1PICPARAMS { int ForwardRefIdx; /**< Picture index of forward reference (P/B-frames) */ int BackwardRefIdx; /**< Picture index of backward reference (B-frames) */ int FrameWidth; /**< Actual frame width */ int FrameHeight; /**< Actual frame height */ // PICTURE int intra_pic_flag; /**< Set to 1 for I,BI frames */ int ref_pic_flag; /**< Set to 1 for I,P frames */ int progressive_fcm; /**< Progressive frame */ // SEQUENCE int profile; int postprocflag; int pulldown; int interlace; int tfcntrflag; int finterpflag; int psf; int multires; int syncmarker; int rangered; int maxbframes; // ENTRYPOINT int panscan_flag; int refdist_flag; int extended_mv; int dquant; int vstransform; int loopfilter; int fastuvmc; int overlap; int quantizer; int extended_dmv; int range_mapy_flag; int range_mapy; int range_mapuv_flag; int range_mapuv; int rangeredfrm; // range reduction state } CUVIDVC1PICPARAMS; /*! * \struct CUVIDJPEGPICPARAMS * JPEG Picture Parameters */ typedef struct _CUVIDJPEGPICPARAMS { int Reserved; } CUVIDJPEGPICPARAMS; /*! * \struct CUVIDHEVCPICPARAMS * HEVC Picture Parameters */ typedef struct _CUVIDHEVCPICPARAMS { // sps int pic_width_in_luma_samples; int pic_height_in_luma_samples; unsigned char log2_min_luma_coding_block_size_minus3; unsigned char log2_diff_max_min_luma_coding_block_size; unsigned char log2_min_transform_block_size_minus2; unsigned char log2_diff_max_min_transform_block_size; unsigned char pcm_enabled_flag; unsigned char log2_min_pcm_luma_coding_block_size_minus3; unsigned char log2_diff_max_min_pcm_luma_coding_block_size; unsigned char pcm_sample_bit_depth_luma_minus1; unsigned char pcm_sample_bit_depth_chroma_minus1; unsigned char pcm_loop_filter_disabled_flag; unsigned char strong_intra_smoothing_enabled_flag; unsigned char max_transform_hierarchy_depth_intra; unsigned char max_transform_hierarchy_depth_inter; unsigned char amp_enabled_flag; unsigned char separate_colour_plane_flag; unsigned char log2_max_pic_order_cnt_lsb_minus4; unsigned char num_short_term_ref_pic_sets; unsigned char long_term_ref_pics_present_flag; unsigned char num_long_term_ref_pics_sps; unsigned char sps_temporal_mvp_enabled_flag; unsigned char sample_adaptive_offset_enabled_flag; unsigned char scaling_list_enable_flag; unsigned char IrapPicFlag; unsigned char IdrPicFlag; unsigned char bit_depth_luma_minus8; unsigned char bit_depth_chroma_minus8; unsigned char reserved1[14]; // pps unsigned char dependent_slice_segments_enabled_flag; unsigned char slice_segment_header_extension_present_flag; unsigned char sign_data_hiding_enabled_flag; unsigned char cu_qp_delta_enabled_flag; unsigned char diff_cu_qp_delta_depth; signed char init_qp_minus26; signed char pps_cb_qp_offset; signed char pps_cr_qp_offset; unsigned char constrained_intra_pred_flag; unsigned char weighted_pred_flag; unsigned char weighted_bipred_flag; unsigned char transform_skip_enabled_flag; unsigned char transquant_bypass_enabled_flag; unsigned char entropy_coding_sync_enabled_flag; unsigned char log2_parallel_merge_level_minus2; unsigned char num_extra_slice_header_bits; unsigned char loop_filter_across_tiles_enabled_flag; unsigned char loop_filter_across_slices_enabled_flag; unsigned char output_flag_present_flag; unsigned char num_ref_idx_l0_default_active_minus1; unsigned char num_ref_idx_l1_default_active_minus1; unsigned char lists_modification_present_flag; unsigned char cabac_init_present_flag; unsigned char pps_slice_chroma_qp_offsets_present_flag; unsigned char deblocking_filter_override_enabled_flag; unsigned char pps_deblocking_filter_disabled_flag; signed char pps_beta_offset_div2; signed char pps_tc_offset_div2; unsigned char tiles_enabled_flag; unsigned char uniform_spacing_flag; unsigned char num_tile_columns_minus1; unsigned char num_tile_rows_minus1; unsigned short column_width_minus1[21]; unsigned short row_height_minus1[21]; unsigned int reserved3[15]; // RefPicSets int NumBitsForShortTermRPSInSlice; int NumDeltaPocsOfRefRpsIdx; int NumPocTotalCurr; int NumPocStCurrBefore; int NumPocStCurrAfter; int NumPocLtCurr; int CurrPicOrderCntVal; int RefPicIdx[16]; // [refpic] Indices of valid reference pictures (-1 if unused for reference) int PicOrderCntVal[16]; // [refpic] unsigned char IsLongTerm[16]; // [refpic] 0=not a long-term reference, 1=long-term reference unsigned char RefPicSetStCurrBefore[8]; // [0..NumPocStCurrBefore-1] -> refpic (0..15) unsigned char RefPicSetStCurrAfter[8]; // [0..NumPocStCurrAfter-1] -> refpic (0..15) unsigned char RefPicSetLtCurr[8]; // [0..NumPocLtCurr-1] -> refpic (0..15) unsigned char RefPicSetInterLayer0[8]; unsigned char RefPicSetInterLayer1[8]; unsigned int reserved4[12]; // scaling lists (diag order) unsigned char ScalingList4x4[6][16]; // [matrixId][i] unsigned char ScalingList8x8[6][64]; // [matrixId][i] unsigned char ScalingList16x16[6][64]; // [matrixId][i] unsigned char ScalingList32x32[2][64]; // [matrixId][i] unsigned char ScalingListDCCoeff16x16[6]; // [matrixId] unsigned char ScalingListDCCoeff32x32[2]; // [matrixId] } CUVIDHEVCPICPARAMS; /*! * \struct CUVIDVP8PICPARAMS * VP8 Picture Parameters */ typedef struct _CUVIDVP8PICPARAMS { int width; int height; unsigned int first_partition_size; //Frame Indexes unsigned char LastRefIdx; unsigned char GoldenRefIdx; unsigned char AltRefIdx; union { struct { unsigned char frame_type : 1; /**< 0 = KEYFRAME, 1 = INTERFRAME */ unsigned char version : 3; unsigned char show_frame : 1; unsigned char update_mb_segmentation_data : 1; /**< Must be 0 if segmentation is not enabled */ unsigned char Reserved2Bits : 2; }; unsigned char wFrameTagFlags; } tagflags; unsigned char Reserved1[4]; unsigned int Reserved2[3]; } CUVIDVP8PICPARAMS; /*! * \struct CUVIDVP9PICPARAMS * VP9 Picture Parameters */ typedef struct _CUVIDVP9PICPARAMS { unsigned int width; unsigned int height; //Frame Indices unsigned char LastRefIdx; unsigned char GoldenRefIdx; unsigned char AltRefIdx; unsigned char colorSpace; unsigned short profile : 3; unsigned short frameContextIdx : 2; unsigned short frameType : 1; unsigned short showFrame : 1; unsigned short errorResilient : 1; unsigned short frameParallelDecoding : 1; unsigned short subSamplingX : 1; unsigned short subSamplingY : 1; unsigned short intraOnly : 1; unsigned short allow_high_precision_mv : 1; unsigned short refreshEntropyProbs : 1; unsigned short reserved2Bits : 2; unsigned short reserved16Bits; unsigned char refFrameSignBias[4]; unsigned char bitDepthMinus8Luma; unsigned char bitDepthMinus8Chroma; unsigned char loopFilterLevel; unsigned char loopFilterSharpness; unsigned char modeRefLfEnabled; unsigned char log2_tile_columns; unsigned char log2_tile_rows; unsigned char segmentEnabled : 1; unsigned char segmentMapUpdate : 1; unsigned char segmentMapTemporalUpdate : 1; unsigned char segmentFeatureMode : 1; unsigned char reserved4Bits : 4; unsigned char segmentFeatureEnable[8][4]; short segmentFeatureData[8][4]; unsigned char mb_segment_tree_probs[7]; unsigned char segment_pred_probs[3]; unsigned char reservedSegment16Bits[2]; int qpYAc; int qpYDc; int qpChDc; int qpChAc; unsigned int activeRefIdx[3]; unsigned int resetFrameContext; unsigned int mcomp_filter_type; unsigned int mbRefLfDelta[4]; unsigned int mbModeLfDelta[2]; unsigned int frameTagSize; unsigned int offsetToDctParts; unsigned int reserved128Bits[4]; } CUVIDVP9PICPARAMS; /*! * \struct CUVIDPICPARAMS * Picture Parameters for Decoding */ typedef struct _CUVIDPICPARAMS { int PicWidthInMbs; /**< Coded Frame Size */ int FrameHeightInMbs; /**< Coded Frame Height */ int CurrPicIdx; /**< Output index of the current picture */ int field_pic_flag; /**< 0=frame picture, 1=field picture */ int bottom_field_flag; /**< 0=top field, 1=bottom field (ignored if field_pic_flag=0) */ int second_field; /**< Second field of a complementary field pair */ // Bitstream data unsigned int nBitstreamDataLen; /**< Number of bytes in bitstream data buffer */ const unsigned char *pBitstreamData; /**< Ptr to bitstream data for this picture (slice-layer) */ unsigned int nNumSlices; /**< Number of slices in this picture */ const unsigned int *pSliceDataOffsets; /**< nNumSlices entries, contains offset of each slice within the bitstream data buffer */ int ref_pic_flag; /**< This picture is a reference picture */ int intra_pic_flag; /**< This picture is entirely intra coded */ unsigned int Reserved[30]; /**< Reserved for future use */ // Codec-specific data union { CUVIDMPEG2PICPARAMS mpeg2; /**< Also used for MPEG-1 */ CUVIDH264PICPARAMS h264; CUVIDVC1PICPARAMS vc1; CUVIDMPEG4PICPARAMS mpeg4; CUVIDJPEGPICPARAMS jpeg; CUVIDHEVCPICPARAMS hevc; CUVIDVP8PICPARAMS vp8; CUVIDVP9PICPARAMS vp9; unsigned int CodecReserved[1024]; } CodecSpecific; } CUVIDPICPARAMS; /*! * \struct CUVIDPROCPARAMS * Picture Parameters for Postprocessing */ typedef struct _CUVIDPROCPARAMS { int progressive_frame; /**< Input is progressive (deinterlace_mode will be ignored) */ int second_field; /**< Output the second field (ignored if deinterlace mode is Weave) */ int top_field_first; /**< Input frame is top field first (1st field is top, 2nd field is bottom) */ int unpaired_field; /**< Input only contains one field (2nd field is invalid) */ // The fields below are used for raw YUV input unsigned int reserved_flags; /**< Reserved for future use (set to zero) */ unsigned int reserved_zero; /**< Reserved (set to zero) */ unsigned long long raw_input_dptr; /**< Input CUdeviceptr for raw YUV extensions */ unsigned int raw_input_pitch; /**< pitch in bytes of raw YUV input (should be aligned appropriately) */ unsigned int raw_input_format; /**< Reserved for future use (set to zero) */ unsigned long long raw_output_dptr; /**< Reserved for future use (set to zero) */ unsigned int raw_output_pitch; /**< Reserved for future use (set to zero) */ unsigned int Reserved[48]; void *Reserved3[3]; } CUVIDPROCPARAMS; /** * * In order to minimize decode latencies, there should be always at least 2 pictures in the decode * queue at any time, in order to make sure that all decode engines are always busy. * * Overall data flow: * - cuvidCreateDecoder(...) * For each picture: * - cuvidDecodePicture(N) * - cuvidMapVideoFrame(N-4) * - do some processing in cuda * - cuvidUnmapVideoFrame(N-4) * - cuvidDecodePicture(N+1) * - cuvidMapVideoFrame(N-3) * ... * - cuvidDestroyDecoder(...) * * NOTE: * - When the cuda context is created from a D3D device, the D3D device must also be created * with the D3DCREATE_MULTITHREADED flag. * - There is a limit to how many pictures can be mapped simultaneously (ulNumOutputSurfaces) * - cuVidDecodePicture may block the calling thread if there are too many pictures pending * in the decode queue */ /** * * Context-locking: to facilitate multi-threaded implementations, the following 4 functions * provide a simple mutex-style host synchronization. If a non-NULL context is specified * in CUVIDDECODECREATEINFO, the codec library will acquire the mutex associated with the given * context before making any cuda calls. * A multi-threaded application could create a lock associated with a context handle so that * multiple threads can safely share the same cuda context: * - use cuCtxPopCurrent immediately after context creation in order to create a 'floating' context * that can be passed to cuvidCtxLockCreate. * - When using a floating context, all cuda calls should only be made within a cuvidCtxLock/cuvidCtxUnlock section. * * NOTE: This is a safer alternative to cuCtxPushCurrent and cuCtxPopCurrent, and is not related to video * decoder in any way (implemented as a critical section associated with cuCtx{Push|Pop}Current calls). */ /** @} */ /* End VIDEO_DECODER */ #if defined(__cplusplus) } #endif /* __cplusplus */ #endif // __CUDA_VIDEO_H__ QtAV-1.12.0/src/cuda/dynlink_nvcuvid.h000066400000000000000000000233221312235004300174630ustar00rootroot00000000000000/* * This copyright notice applies to this header file only: * * Copyright (c) 2010-2016 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the software, and to permit persons to whom the * software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * \file nvcuvid.h * NvCuvid API provides Video Decoding interface to NVIDIA GPU devices. * \date 2015-2015 * This file contains the interface constants, structure definitions and function prototypes. */ #if !defined(__NVCUVID_H__) #define __NVCUVID_H__ #include "dynlink_cuviddec.h" #if defined(__cplusplus) extern "C" { #endif /* __cplusplus */ //////////////////////////////////////////////////////////////////////////////////////////////// // // High-level helper APIs for video sources // typedef void *CUvideosource; typedef void *CUvideoparser; typedef long long CUvideotimestamp; /** * \addtogroup VIDEO_PARSER Video Parser * @{ */ /*! * \enum cudaVideoState * Video Source State */ typedef enum { cudaVideoState_Error = -1, /**< Error state (invalid source) */ cudaVideoState_Stopped = 0, /**< Source is stopped (or reached end-of-stream) */ cudaVideoState_Started = 1 /**< Source is running and delivering data */ } cudaVideoState; /*! * \enum cudaAudioCodec * Audio compression */ typedef enum { cudaAudioCodec_MPEG1=0, /**< MPEG-1 Audio */ cudaAudioCodec_MPEG2, /**< MPEG-2 Audio */ cudaAudioCodec_MP3, /**< MPEG-1 Layer III Audio */ cudaAudioCodec_AC3, /**< Dolby Digital (AC3) Audio */ cudaAudioCodec_LPCM /**< PCM Audio */ } cudaAudioCodec; /*! * \struct CUVIDEOFORMAT * Video format */ typedef struct { cudaVideoCodec codec; /**< Compression format */ /** * frame rate = numerator / denominator (for example: 30000/1001) */ struct { unsigned int numerator; /**< frame rate numerator (0 = unspecified or variable frame rate) */ unsigned int denominator; /**< frame rate denominator (0 = unspecified or variable frame rate) */ } frame_rate; unsigned char progressive_sequence; /**< 0=interlaced, 1=progressive */ unsigned char bit_depth_luma_minus8; /**< high bit depth Luma */ unsigned char bit_depth_chroma_minus8; /**< high bit depth Chroma */ unsigned char reserved1; /**< Reserved for future use */ unsigned int coded_width; /**< coded frame width */ unsigned int coded_height; /**< coded frame height */ /** * area of the frame that should be displayed * typical example: * coded_width = 1920, coded_height = 1088 * display_area = { 0,0,1920,1080 } */ struct { int left; /**< left position of display rect */ int top; /**< top position of display rect */ int right; /**< right position of display rect */ int bottom; /**< bottom position of display rect */ } display_area; cudaVideoChromaFormat chroma_format; /**< Chroma format */ unsigned int bitrate; /**< video bitrate (bps, 0=unknown) */ /** * Display Aspect Ratio = x:y (4:3, 16:9, etc) */ struct { int x; int y; } display_aspect_ratio; /** * Video Signal Description */ struct { unsigned char video_format : 3; unsigned char video_full_range_flag : 1; unsigned char reserved_zero_bits : 4; unsigned char color_primaries; unsigned char transfer_characteristics; unsigned char matrix_coefficients; } video_signal_description; unsigned int seqhdr_data_length; /**< Additional bytes following (CUVIDEOFORMATEX) */ } CUVIDEOFORMAT; /*! * \struct CUVIDEOFORMATEX * Video format including raw sequence header information */ typedef struct { CUVIDEOFORMAT format; unsigned char raw_seqhdr_data[1024]; } CUVIDEOFORMATEX; /*! * \struct CUAUDIOFORMAT * Audio Formats */ typedef struct { cudaAudioCodec codec; /**< Compression format */ unsigned int channels; /**< number of audio channels */ unsigned int samplespersec; /**< sampling frequency */ unsigned int bitrate; /**< For uncompressed, can also be used to determine bits per sample */ unsigned int reserved1; /**< Reserved for future use */ unsigned int reserved2; /**< Reserved for future use */ } CUAUDIOFORMAT; /*! * \enum CUvideopacketflags * Data packet flags */ typedef enum { CUVID_PKT_ENDOFSTREAM = 0x01, /**< Set when this is the last packet for this stream */ CUVID_PKT_TIMESTAMP = 0x02, /**< Timestamp is valid */ CUVID_PKT_DISCONTINUITY = 0x04 /**< Set when a discontinuity has to be signalled */ } CUvideopacketflags; /*! * \struct CUVIDSOURCEDATAPACKET * Data Packet */ typedef struct _CUVIDSOURCEDATAPACKET { unsigned long flags; /**< Combination of CUVID_PKT_XXX flags */ unsigned long payload_size; /**< number of bytes in the payload (may be zero if EOS flag is set) */ const unsigned char *payload; /**< Pointer to packet payload data (may be NULL if EOS flag is set) */ CUvideotimestamp timestamp; /**< Presentation timestamp (10MHz clock), only valid if CUVID_PKT_TIMESTAMP flag is set */ } CUVIDSOURCEDATAPACKET; // Callback for packet delivery typedef int (CUDAAPI *PFNVIDSOURCECALLBACK)(void *, CUVIDSOURCEDATAPACKET *); /*! * \struct CUVIDSOURCEPARAMS * Source Params */ typedef struct _CUVIDSOURCEPARAMS { unsigned int ulClockRate; /**< Timestamp units in Hz (0=default=10000000Hz) */ unsigned int uReserved1[7]; /**< Reserved for future use - set to zero */ void *pUserData; /**< Parameter passed in to the data handlers */ PFNVIDSOURCECALLBACK pfnVideoDataHandler; /**< Called to deliver audio packets */ PFNVIDSOURCECALLBACK pfnAudioDataHandler; /**< Called to deliver video packets */ void *pvReserved2[8]; /**< Reserved for future use - set to NULL */ } CUVIDSOURCEPARAMS; /*! * \enum CUvideosourceformat_flags * CUvideosourceformat_flags */ typedef enum { CUVID_FMT_EXTFORMATINFO = 0x100 /**< Return extended format structure (CUVIDEOFORMATEX) */ } CUvideosourceformat_flags; /** * \struct CUVIDPARSERDISPINFO */ typedef struct _CUVIDPARSERDISPINFO { int picture_index; /**< */ int progressive_frame; /**< */ int top_field_first; /**< */ int repeat_first_field; /**< Number of additional fields (1=ivtc, 2=frame doubling, 4=frame tripling, -1=unpaired field) */ CUvideotimestamp timestamp; /**< */ } CUVIDPARSERDISPINFO; // // Parser callbacks // The parser will call these synchronously from within cuvidParseVideoData(), whenever a picture is ready to // be decoded and/or displayed. // typedef int (CUDAAPI *PFNVIDSEQUENCECALLBACK)(void *, CUVIDEOFORMAT *); typedef int (CUDAAPI *PFNVIDDECODECALLBACK)(void *, CUVIDPICPARAMS *); typedef int (CUDAAPI *PFNVIDDISPLAYCALLBACK)(void *, CUVIDPARSERDISPINFO *); /** * \struct CUVIDPARSERPARAMS */ typedef struct _CUVIDPARSERPARAMS { cudaVideoCodec CodecType; /**< cudaVideoCodec_XXX */ unsigned int ulMaxNumDecodeSurfaces; /**< Max # of decode surfaces (parser will cycle through these) */ unsigned int ulClockRate; /**< Timestamp units in Hz (0=default=10000000Hz) */ unsigned int ulErrorThreshold; /**< % Error threshold (0-100) for calling pfnDecodePicture (100=always call pfnDecodePicture even if picture bitstream is fully corrupted) */ unsigned int ulMaxDisplayDelay; /**< Max display queue delay (improves pipelining of decode with display) - 0=no delay (recommended values: 2..4) */ unsigned int uReserved1[5]; /**< Reserved for future use - set to 0 */ void *pUserData; /**< User data for callbacks */ PFNVIDSEQUENCECALLBACK pfnSequenceCallback; /**< Called before decoding frames and/or whenever there is a format change */ PFNVIDDECODECALLBACK pfnDecodePicture; /**< Called when a picture is ready to be decoded (decode order) */ PFNVIDDISPLAYCALLBACK pfnDisplayPicture; /**< Called whenever a picture is ready to be displayed (display order) */ void *pvReserved2[7]; /**< Reserved for future use - set to NULL */ CUVIDEOFORMATEX *pExtVideoInfo; /**< [Optional] sequence header data from system layer */ } CUVIDPARSERPARAMS; /** @} */ /* END VIDEO_PARSER */ //////////////////////////////////////////////////////////////////////////////////////////////// #if defined(__cplusplus) } #endif /* __cplusplus */ #endif // __NVCUVID_H__ QtAV-1.12.0/src/cuda/helper_cuda.h000066400000000000000000000151241312235004300165310ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ ///from nv's helper_cuda.h #include #include "dllapi/nv_inc.h" #if NV_CONFIG(DLLAPI_CUDA) using namespace dllapi::cuda; #endif /* NV_CONFIG(DLLAPI_CUDA)*/ #ifdef __cuda_cuda_h__ // CUDA Driver API errors inline const char *_cudaGetErrorEnum(CUresult error) { switch (error) { case CUDA_SUCCESS: return "CUDA_SUCCESS"; case CUDA_ERROR_INVALID_VALUE: return "CUDA_ERROR_INVALID_VALUE"; case CUDA_ERROR_OUT_OF_MEMORY: return "CUDA_ERROR_OUT_OF_MEMORY"; case CUDA_ERROR_NOT_INITIALIZED: return "CUDA_ERROR_NOT_INITIALIZED"; case CUDA_ERROR_DEINITIALIZED: return "CUDA_ERROR_DEINITIALIZED"; case CUDA_ERROR_PROFILER_DISABLED: return "CUDA_ERROR_PROFILER_DISABLED"; case CUDA_ERROR_PROFILER_NOT_INITIALIZED: return "CUDA_ERROR_PROFILER_NOT_INITIALIZED"; case CUDA_ERROR_PROFILER_ALREADY_STARTED: return "CUDA_ERROR_PROFILER_ALREADY_STARTED"; case CUDA_ERROR_PROFILER_ALREADY_STOPPED: return "CUDA_ERROR_PROFILER_ALREADY_STOPPED"; case CUDA_ERROR_NO_DEVICE: return "CUDA_ERROR_NO_DEVICE"; case CUDA_ERROR_INVALID_DEVICE: return "CUDA_ERROR_INVALID_DEVICE"; case CUDA_ERROR_INVALID_IMAGE: return "CUDA_ERROR_INVALID_IMAGE"; case CUDA_ERROR_INVALID_CONTEXT: return "CUDA_ERROR_INVALID_CONTEXT"; case CUDA_ERROR_CONTEXT_ALREADY_CURRENT: return "CUDA_ERROR_CONTEXT_ALREADY_CURRENT"; case CUDA_ERROR_MAP_FAILED: return "CUDA_ERROR_MAP_FAILED"; case CUDA_ERROR_UNMAP_FAILED: return "CUDA_ERROR_UNMAP_FAILED"; case CUDA_ERROR_ARRAY_IS_MAPPED: return "CUDA_ERROR_ARRAY_IS_MAPPED"; case CUDA_ERROR_ALREADY_MAPPED: return "CUDA_ERROR_ALREADY_MAPPED"; case CUDA_ERROR_NO_BINARY_FOR_GPU: return "CUDA_ERROR_NO_BINARY_FOR_GPU"; case CUDA_ERROR_ALREADY_ACQUIRED: return "CUDA_ERROR_ALREADY_ACQUIRED"; case CUDA_ERROR_NOT_MAPPED: return "CUDA_ERROR_NOT_MAPPED"; case CUDA_ERROR_NOT_MAPPED_AS_ARRAY: return "CUDA_ERROR_NOT_MAPPED_AS_ARRAY"; case CUDA_ERROR_NOT_MAPPED_AS_POINTER: return "CUDA_ERROR_NOT_MAPPED_AS_POINTER"; case CUDA_ERROR_ECC_UNCORRECTABLE: return "CUDA_ERROR_ECC_UNCORRECTABLE"; case CUDA_ERROR_UNSUPPORTED_LIMIT: return "CUDA_ERROR_UNSUPPORTED_LIMIT"; case CUDA_ERROR_CONTEXT_ALREADY_IN_USE: return "CUDA_ERROR_CONTEXT_ALREADY_IN_USE"; case CUDA_ERROR_INVALID_SOURCE: return "CUDA_ERROR_INVALID_SOURCE"; case CUDA_ERROR_FILE_NOT_FOUND: return "CUDA_ERROR_FILE_NOT_FOUND"; case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND"; case CUDA_ERROR_SHARED_OBJECT_INIT_FAILED: return "CUDA_ERROR_SHARED_OBJECT_INIT_FAILED"; case CUDA_ERROR_OPERATING_SYSTEM: return "CUDA_ERROR_OPERATING_SYSTEM"; case CUDA_ERROR_INVALID_HANDLE: return "CUDA_ERROR_INVALID_HANDLE"; case CUDA_ERROR_NOT_FOUND: return "CUDA_ERROR_NOT_FOUND"; case CUDA_ERROR_NOT_READY: return "CUDA_ERROR_NOT_READY"; case CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES: return "CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES"; case CUDA_ERROR_LAUNCH_TIMEOUT: return "CUDA_ERROR_LAUNCH_TIMEOUT"; case CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING: return "CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING"; case CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED: return "CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED"; case CUDA_ERROR_PEER_ACCESS_NOT_ENABLED: return "CUDA_ERROR_PEER_ACCESS_NOT_ENABLED"; case CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE: return "CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE"; case CUDA_ERROR_CONTEXT_IS_DESTROYED: return "CUDA_ERROR_CONTEXT_IS_DESTROYED"; case CUDA_ERROR_LAUNCH_FAILED: return "CUDA_ERROR_LAUNCH_FAILED"; case CUDA_ERROR_UNKNOWN: return "CUDA_ERROR_UNKNOWN"; default: return "CUDA_ERROR_UNKNOWN"; } return ""; } #endif //__cuda_cuda_h__ //from helper_cuda // Beginning of GPU Architecture definitions inline int _ConvertSMVer2Cores(int major, int minor) { // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM typedef struct { int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version int Cores; } sSMtoCores; sSMtoCores nGpuArchCoresPerSM[] = { { 0x10, 8 }, // Tesla Generation (SM 1.0) G80 class { 0x11, 8 }, // Tesla Generation (SM 1.1) G8x class { 0x12, 8 }, // Tesla Generation (SM 1.2) G9x class { 0x13, 8 }, // Tesla Generation (SM 1.3) GT200 class { 0x20, 32 }, // Fermi Generation (SM 2.0) GF100 class { 0x21, 48 }, // Fermi Generation (SM 2.1) GF10x class { 0x30, 192}, // Kepler Generation (SM 3.0) GK10x class { 0x32, 192}, // Kepler Generation (SM 3.2) GK10x class { 0x35, 192}, // Kepler Generation (SM 3.5) GK11x class { 0x37, 192}, // Kepler Generation (SM 3.7) GK21x class { 0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class { 0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class //enc: h264 yuv444p, hevc (libavcodec/nvenc.c) { -1, -1 } }; int index = 0; while (nGpuArchCoresPerSM[index].SM != -1) { if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { return nGpuArchCoresPerSM[index].Cores; } ++index; } // If we don't find the values, we default use the previous one to run properly printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[7].Cores); return nGpuArchCoresPerSM[index - 1].Cores; } // end of GPU Architecture definitions QtAV-1.12.0/src/directx/000077500000000000000000000000001312235004300146305ustar00rootroot00000000000000QtAV-1.12.0/src/directx/D3D11VP.cpp000066400000000000000000000122731312235004300163230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "D3D11VP.h" #define DX_LOG_COMPONENT "D3D11VP" #include "utils/DirectXHelper.h" #include "utils/Logger.h" // define __mingw_uuidof #ifdef __CRT_UUID_DECL __CRT_UUID_DECL(ID3D11VideoContext,0x61F21C45,0x3C0E,0x4a74,0x9C,0xEA,0x67,0x10,0x0D,0x9A,0xD5,0xE4) __CRT_UUID_DECL(ID3D11VideoDevice,0x10EC4D5B,0x975A,0x4689,0xB9,0xE4,0xD0,0xAA,0xC3,0x0F,0xE3,0x33) #endif //__CRT_UUID_DECL namespace QtAV { namespace dx { D3D11VP::D3D11VP(ComPtr dev) : m_dev(dev) , m_w(0) , m_h(0) , m_cs(ColorSpace_BT709) , m_range(ColorRange_Limited) { DX_ENSURE(m_dev.As(&m_viddev)); } void D3D11VP::setOutput(ID3D11Texture2D *tex) { m_out = tex; m_outview.Reset(); } void D3D11VP::setSourceRect(const QRect &r) { m_srcRect = r; } void D3D11VP::setColorSpace(ColorSpace value) { m_cs = value; } void D3D11VP::setColorRange(ColorRange value) { m_range = value; } bool D3D11VP::process(ID3D11Texture2D *texture, int index) { if (!texture || !m_out) return false; D3D11_TEXTURE2D_DESC desc; texture->GetDesc(&desc); if (!ensureResource(desc.Width, desc.Height, desc.Format)) return false; D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inviewDesc = { 0, D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, (UINT)index } }; ComPtr inview; DX_ENSURE(m_viddev->CreateVideoProcessorInputView( texture, m_enum.Get(), &inviewDesc, &inview), false); ComPtr ctx; ComPtr videoctx; m_dev->GetImmediateContext(&ctx); DX_ENSURE(ctx.As(&videoctx), false); if (!m_srcRect.isEmpty()) { const RECT r = {m_srcRect.x(), m_srcRect.y(), m_srcRect.width(), m_srcRect.height()}; videoctx->VideoProcessorSetStreamSourceRect(m_vp.Get(), 0, TRUE, &r); } // disable additional processing. this can fix the output frame is too dark, also make in/out color space parameters work videoctx->VideoProcessorSetStreamAutoProcessingMode(m_vp.Get(), 0, FALSE); D3D11_VIDEO_PROCESSOR_COLOR_SPACE cs; ZeroMemory(&cs, sizeof(cs)); cs.YCbCr_Matrix = m_cs == ColorSpace_BT601 ? 0 : 1; //0: bt601, 1: bt709 // D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_xxx is desktop only? cs.Nominal_Range = m_range == ColorRange_Full ? D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_0_255 : D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_16_235; videoctx->VideoProcessorSetStreamColorSpace(m_vp.Get(), 0, &cs); #if 0 cs.RGB_Range = 1; // 0: full, 1: limited videoctx->VideoProcessorSetOutputColorSpace(m_vp.Get(), &cs); #endif D3D11_VIDEO_PROCESSOR_STREAM stream; ZeroMemory(&stream, sizeof(stream)); stream.Enable = TRUE; stream.pInputSurface = inview.Get(); DX_ENSURE(videoctx->VideoProcessorBlt(m_vp.Get(), m_outview.Get(), 0, 1, &stream), false); return true; } bool D3D11VP::ensureResource(UINT width, UINT height, DXGI_FORMAT format) { bool dirty = width != m_w || height != m_h; if (dirty || !m_enum) { D3D11_VIDEO_PROCESSOR_CONTENT_DESC vpdesc = { D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, { 0, 0 }, width, height, { 0, 0 }, width, height, D3D11_VIDEO_USAGE_PLAYBACK_NORMAL //D3D11_VIDEO_USAGE_OPTIMAL_SPEED }; DX_ENSURE(m_viddev->CreateVideoProcessorEnumerator(&vpdesc, &m_enum), false); } UINT flags; // TODO: check when format is changed, or record supported formats DX_ENSURE(m_enum->CheckVideoProcessorFormat(format, &flags), false); if (!(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT)) { qWarning("unsupported input format for d3d11 video processor: %d", format); return false; } if (dirty || !m_vp) DX_ENSURE(m_viddev->CreateVideoProcessor(m_enum.Get(), 0, &m_vp), false); if (dirty || !m_outview) { D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc; ZeroMemory(&outputDesc, sizeof(outputDesc)); outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; DX_ENSURE(m_viddev->CreateVideoProcessorOutputView(m_out.Get(), m_enum.Get(), &outputDesc, &m_outview), false); } m_w = width; m_h = height; return true; } } //namespace dx } //namespace QtAV QtAV-1.12.0/src/directx/D3D11VP.h000066400000000000000000000042141312235004300157640ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_D3D11VP_H #define QTAV_D3D11VP_H #include #include "directx/dxcompat.h" #include #include #include using namespace Microsoft::WRL; namespace QtAV { namespace dx { class D3D11VP { public: // brightness, contrast, hue, saturation, rotation, source/dest rect D3D11VP(ComPtr dev); void setOutput(ID3D11Texture2D* tex); void setSourceRect(const QRect& r); // input color space and range void setColorSpace(ColorSpace value); void setColorRange(ColorRange value); bool process(ID3D11Texture2D *texture, int index); private: bool ensureResource(UINT width, UINT height, DXGI_FORMAT format); ComPtr m_dev; ComPtr m_out; ComPtr m_viddev; ComPtr m_enum; ComPtr m_vp; ComPtr m_outview; UINT m_w, m_h; //enumerator ColorSpace m_cs; ColorRange m_range; QRect m_srcRect; }; } //namespace dx } //namespace QtAV #endif //QTAV_D3D11VP_H QtAV-1.12.0/src/directx/SurfaceInteropD3D11.cpp000066400000000000000000000124531312235004300207270ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D11.h" #include // uint8_t for windows phone #include "QtAV/VideoFrame.h" #define DX_LOG_COMPONENT "D3D11 Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" // no need to check qt4 because no ANGLE there #if QTAV_HAVE(EGL_CAPI) // always use dynamic load #if defined(QT_OPENGL_DYNAMIC) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_2_ANGLE) #define QTAV_HAVE_D3D11_EGL 1 #endif #endif //QTAV_HAVE(EGL_CAPI) #if defined(QT_OPENGL_DYNAMIC) || !defined(QT_OPENGL_ES_2) #define QTAV_HAVE_D3D11_GL 1 #endif namespace QtAV { int fourccFromDXGI(DXGI_FORMAT fmt); //FIXME: defined in d3d11 decoder VideoFormat::PixelFormat pixelFormatFromFourcc(int format); namespace d3d11 { bool InteropResource::isSupported(InteropType type) { if (type == InteropAuto || type == InteropEGL) { #if QTAV_HAVE(D3D11_EGL) if (OpenGLHelper::isOpenGLES()) return true; #endif } if (type == InteropAuto || type == InteropGL) { #if QTAV_HAVE(D3D11_GL) if (!OpenGLHelper::isOpenGLES()) return true; #endif } return false; } extern InteropResource* CreateInteropEGL(); extern InteropResource* CreateInteropGL(); InteropResource* InteropResource::create(InteropType type) { if (type == InteropAuto || type == InteropEGL) { #if QTAV_HAVE(D3D11_EGL) if (OpenGLHelper::isOpenGLES()) return CreateInteropEGL(); #endif } if (type == InteropAuto || type == InteropGL) { #if QTAV_HAVE(D3D11_GL) if (!OpenGLHelper::isOpenGLES()) return CreateInteropGL(); #endif } return NULL; } void InteropResource::setDevice(ComPtr dev) { d3ddev = dev; } void SurfaceInterop::setSurface(ComPtr surface, int index, int frame_w, int frame_h) { m_surface = surface; m_index = index; frame_width = frame_w; frame_height = frame_h; } void* SurfaceInterop::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane) { if (!handle) return NULL; if (!m_surface) return 0; if (type == GLTextureSurface) { if (m_resource->map(m_surface, m_index, *((GLuint*)handle), frame_width, frame_height, plane)) return handle; } else if (type == HostMemorySurface) { return mapToHost(fmt, handle, plane); } return NULL; } void SurfaceInterop::unmap(void *handle) { m_resource->unmap(*((GLuint*)handle)); } void* SurfaceInterop::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); ComPtr dev; m_surface->GetDevice(&dev); D3D11_TEXTURE2D_DESC desc; m_surface->GetDesc(&desc); desc.MipLevels = 1; desc.MiscFlags = 0; desc.ArraySize = 1; desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; //? ComPtr tex; DX_ENSURE(dev->CreateTexture2D(&desc, NULL, &tex), NULL); ComPtr ctx; dev->GetImmediateContext(&ctx); ctx->CopySubresourceRegion(tex.Get(), 0, 0, 0, 0 , m_surface.Get() , m_index , NULL); struct ScopedMap { ScopedMap(ComPtr ctx, ComPtr res, D3D11_MAPPED_SUBRESOURCE *mapped): c(ctx), r(res) { DX_ENSURE(c->Map(r.Get(), 0, D3D11_MAP_READ, 0, mapped)); //TODO: check error } ~ScopedMap() { c->Unmap(r.Get(), 0);} ComPtr c; ComPtr r; }; D3D11_MAPPED_SUBRESOURCE mapped; ScopedMap sm(ctx, tex, &mapped); //mingw error if ComPtr constructs from ComPtr [T=ID3D11Resource, U=ID3D11Texture2D] Q_UNUSED(sm); int pitch[3] = { (int)mapped.RowPitch, 0, 0}; //compute chroma later uint8_t *src[] = { (uint8_t*)mapped.pData, 0, 0}; //compute chroma later const VideoFormat fmt = pixelFormatFromFourcc(fourccFromDXGI(desc.Format)); VideoFrame frame = VideoFrame::fromGPU(fmt, frame_width, frame_height, desc.Height, src, pitch); if (fmt != format) frame = frame.to(format); VideoFrame *f = reinterpret_cast(handle); frame.setTimestamp(f->timestamp()); *f = frame; return f; } } //namespace d3d11 } //namespace QtAV QtAV-1.12.0/src/directx/SurfaceInteropD3D11.h000066400000000000000000000073061312235004300203750ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROPD3D11_H #define QTAV_SURFACEINTEROPD3D11_H #include "QtAV/SurfaceInterop.h" #include "directx/dxcompat.h" #include #include using namespace Microsoft::WRL; namespace QtAV { namespace d3d11 { enum InteropType { InteropAuto, InteropEGL, InteropGL //NOT IMPLEMENTED }; class InteropResource { public: typedef unsigned int GLuint; static InteropResource* create(InteropType type = InteropAuto); /*! * \brief isSupported * \return true if support 0-copy interop. Currently only d3d11+egl, i.e. check egl build environment and runtime egl support. */ static bool isSupported(InteropType type = InteropAuto); virtual ~InteropResource() {} void setDevice(ComPtr dev); virtual VideoFormat::PixelFormat format(DXGI_FORMAT dxfmt) const = 0; /*! * \brief map * \param surface dxva decoded surface * \param index ID3D11Texture2D array index * \param tex opengl texture * \param w frame width(visual width) without alignment, <= dxva surface width * \param h frame height(visual height) * \param plane useless now * \return true if success */ virtual bool map(ComPtr surface, int index, GLuint tex, int w, int h, int plane) = 0; virtual bool unmap(GLuint tex) { Q_UNUSED(tex); return true;} protected: ComPtr d3ddev; int width, height; // video frame width and dx_surface width without alignment, not dxva decoded surface width }; typedef QSharedPointer InteropResourcePtr; class SurfaceInterop Q_DECL_FINAL: public VideoSurfaceInterop { public: SurfaceInterop(const InteropResourcePtr& res) : m_resource(res), frame_width(0), frame_height(0) {} /*! * \brief setSurface * \param surface d3d11 decoded surface * \param index ID3D11Texture2D array index * \param frame_w frame width(visual width) without alignment, <= d3d11 surface width * \param frame_h frame height(visual height) */ void setSurface(ComPtr surface, int index, int frame_w, int frame_h); /// GLTextureSurface only supports rgb32 void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE; void unmap(void *handle) Q_DECL_OVERRIDE; protected: /// copy from gpu (optimized if possible) and convert to target format if necessary void* mapToHost(const VideoFormat &format, void *handle, int plane); private: ComPtr m_surface; int m_index; InteropResourcePtr m_resource; int frame_width, frame_height; }; } //namespace d3d11 } //namespace QtAV #endif //QTAV_SURFACEINTEROPD3D11_H QtAV-1.12.0/src/directx/SurfaceInteropD3D11EGL.cpp000066400000000000000000000134541312235004300212610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D11.h" #include "directx/D3D11VP.h" #define DX_LOG_COMPONENT "D3D11EGL Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" #if defined(QT_OPENGL_ES_2_ANGLE_STATIC) || defined(Q_OS_WINRT) #ifndef CAPI_LINK_EGL #define CAPI_LINK_EGL #endif //CAPI_LINK_EGL #else #define EGL_CAPI_NS #endif //QT_OPENGL_ES_2_ANGLE_STATIC #include "capi/egl_api.h" #include //include after egl_capi.h to match types namespace QtAV { namespace d3d11 { class EGL { public: EGL() : dpy(EGL_NO_DISPLAY), surface(EGL_NO_SURFACE) {} EGLDisplay dpy; EGLSurface surface; }; class EGLInteropResource Q_DECL_FINAL: public InteropResource { public: EGLInteropResource() : egl(new EGL()) , vp(0) {} ~EGLInteropResource(); VideoFormat::PixelFormat format(DXGI_FORMAT) const Q_DECL_OVERRIDE { return VideoFormat::Format_RGB32;} bool map(ComPtr surface, int index, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE; private: void releaseEGL(); bool ensureSurface(int w, int h); EGL* egl; dx::D3D11VP *vp; ComPtr d3dtex; }; InteropResource* CreateInteropEGL() { return new EGLInteropResource();} EGLInteropResource::~EGLInteropResource() { if (vp) { delete vp; vp = 0; } releaseEGL(); if (egl) { delete egl; egl = 0; } } void EGLInteropResource::releaseEGL() { if (egl->surface != EGL_NO_SURFACE) { eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); eglDestroySurface(egl->dpy, egl->surface); egl->surface = EGL_NO_SURFACE; } } bool EGLInteropResource::ensureSurface(int w, int h) { if (egl->surface && width == w && height == h) return true; qDebug("update egl and dx"); egl->dpy = eglGetCurrentDisplay(); if (!egl->dpy) { qWarning("Failed to get current EGL display"); return false; } qDebug("EGL version: %s, client api: %s", eglQueryString(egl->dpy, EGL_VERSION), eglQueryString(egl->dpy, EGL_CLIENT_APIS)); // TODO: check runtime egl>=1.4 for eglGetCurrentContext() // check extensions QList extensions = QByteArray(eglQueryString(egl->dpy, EGL_EXTENSIONS)).split(' '); // TODO: strstr is enough const bool kEGL_ANGLE_d3d_share_handle_client_buffer = extensions.contains("EGL_ANGLE_d3d_share_handle_client_buffer"); if (!kEGL_ANGLE_d3d_share_handle_client_buffer) { qWarning("EGL extension 'ANGLE_d3d_share_handle_client_buffer' is required!"); return false; } //GLint has_alpha = 1; //QOpenGLContext::currentContext()->format().hasAlpha() EGLint cfg_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, // EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_NONE }; EGLint nb_cfgs; EGLConfig egl_cfg; if (!eglChooseConfig(egl->dpy, cfg_attribs, &egl_cfg, 1, &nb_cfgs)) { qWarning("Failed to create EGL configuration"); return false; } // TODO: check alpha? CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, w, h, 1, 1); // why crash if only set D3D11_BIND_RENDER_TARGET? desc.BindFlags |= D3D11_BIND_RENDER_TARGET; // also required by VideoProcessorOutputView https://msdn.microsoft.com/en-us/library/windows/desktop/hh447791(v=vs.85).aspx desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; DX_ENSURE(d3ddev->CreateTexture2D(&desc, NULL, &d3dtex), false); ComPtr resource; DX_ENSURE(d3dtex.As(&resource), false); HANDLE share_handle = NULL; DX_ENSURE(resource->GetSharedHandle(&share_handle), false); if (!vp) vp = new dx::D3D11VP(d3ddev); vp->setOutput(d3dtex.Get()); releaseEGL(); EGLint attribs[] = { EGL_WIDTH, w, EGL_HEIGHT, h, EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE }; // egl surface size must match d3d texture's EGL_ENSURE((egl->surface = eglCreatePbufferFromClientBuffer(egl->dpy, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, share_handle, egl_cfg, attribs)), false); qDebug("pbuffer surface from client buffer: %p", egl->surface); width = w; height = h; return true; } bool EGLInteropResource::map(ComPtr surface, int index, GLuint tex, int w, int h, int) { if (!ensureSurface(w, h)) { releaseEGL(); return false; } vp->setSourceRect(QRect(0, 0, w, h)); if (!vp->process(surface.Get(), index)) return false; DYGL(glBindTexture(GL_TEXTURE_2D, tex)); eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); return true; } } //namespace d3d11 } //namespace QtAV QtAV-1.12.0/src/directx/SurfaceInteropD3D11GL.cpp000066400000000000000000000147311312235004300211530ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D11.h" #define DX_LOG_COMPONENT "D3D11GL Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" #include // TODO: does intel supports d3d11-gl interop? //dynamic gl or desktop gl namespace QtAV { namespace d3d11 { class GLInteropResource Q_DECL_FINAL: public InteropResource { public: GLInteropResource(); ~GLInteropResource(); VideoFormat::PixelFormat format(DXGI_FORMAT dxfmt) const Q_DECL_OVERRIDE { if (dxfmt == DXGI_FORMAT_NV12) return VideoFormat::Format_NV12; if (dxfmt == DXGI_FORMAT_P010) return VideoFormat::Format_YUV420P10LE; if (dxfmt == DXGI_FORMAT_P016) return VideoFormat::Format_YUV420P16LE; return VideoFormat::Format_Invalid; } bool map(ComPtr surface, int index, GLuint tex, int w, int h, int plane) Q_DECL_OVERRIDE; bool unmap(GLuint tex) Q_DECL_OVERRIDE; private: bool ensureResource(DXGI_FORMAT fmt, int w, int h, GLuint tex, int plane); HANDLE interop_dev; HANDLE interop_obj[2]; DXGI_FORMAT tex_format; QVector > d3dtex; int mapped; GLuint gltex[2]; }; InteropResource* CreateInteropGL() { return new GLInteropResource(); } GLInteropResource::GLInteropResource() : interop_dev(NULL) , tex_format(DXGI_FORMAT_UNKNOWN) , mapped(0) { d3dtex.reserve(3); d3dtex.resize(2); memset(interop_obj, 0, sizeof(interop_obj)); memset(gltex, 0, sizeof(gltex)); } GLInteropResource::~GLInteropResource() { // FIXME: why unregister/close interop obj/dev here will crash(tested on intel driver)? must be in current opengl context? } bool GLInteropResource::map(ComPtr surface, int index, GLuint tex, int w, int h, int plane) { gltex[plane] = tex; D3D11_TEXTURE2D_DESC desc; surface->GetDesc(&desc); if (!ensureResource(desc.Format, w, h, tex, plane)) return false; // open/close and register/unregster in every map/unmap to ensure called in current context and avoid crash (tested on intel driver) // interop operations begin WGL_ENSURE((interop_dev = gl().DXOpenDeviceNV(d3ddev.Get())) != NULL, false); // call in ensureResource or in map? WGL_ENSURE((interop_obj[plane] = gl().DXRegisterObjectNV(interop_dev, d3dtex[plane].Get(), tex, GL_TEXTURE_2D, WGL_ACCESS_READ_ONLY_NV)) != NULL, false); // prepare dx resources for gl D3D11_BOX box; ZeroMemory(&box, sizeof(box)); box.right = w; if (plane == 0) { box.bottom = h; } else { box.top = desc.Height; // maybe > h box.bottom = box.top + h/2; } ComPtr ctx; d3ddev->GetImmediateContext(&ctx); ctx->CopySubresourceRegion(d3dtex[plane].Get(), 0, 0, 0, 0 , surface.Get() , index , &box); // lock dx resources WGL_ENSURE(gl().DXLockObjectsNV(interop_dev, 1, &interop_obj[plane]), false); WGL_ENSURE(gl().DXObjectAccessNV(interop_obj[plane], WGL_ACCESS_READ_ONLY_NV), false); DYGL(glBindTexture(GL_TEXTURE_2D, tex)); return true; } bool GLInteropResource::unmap(GLuint tex) { Q_UNUSED(tex); int plane = 0; if (gltex[plane] != tex) plane = 1; if (!interop_obj[plane]) return false; DYGL(glBindTexture(GL_TEXTURE_2D, 0)); WGL_ENSURE(gl().DXUnlockObjectsNV(interop_dev, 1, &interop_obj[plane]), false); WGL_WARN(gl().DXUnregisterObjectNV(interop_dev, interop_obj[plane])); // interop operations end WGL_WARN(gl().DXCloseDeviceNV(interop_dev)); interop_obj[plane] = NULL; return true; } static const struct { DXGI_FORMAT fmt; DXGI_FORMAT plane_fmt[2]; } plane_formats[] = { { DXGI_FORMAT_R8G8B8A8_UNORM, {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN}}, { DXGI_FORMAT_NV12, {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM}}, { DXGI_FORMAT_P010, {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM}}, { DXGI_FORMAT_P016, {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM}}, }; static DXGI_FORMAT GetPlaneFormat(DXGI_FORMAT fmt, int plane) { for (size_t i = 0; i < sizeof(plane_formats)/sizeof(plane_formats[0]); ++i) { if (plane_formats[i].fmt == fmt) return plane_formats[i].plane_fmt[plane]; } return DXGI_FORMAT_UNKNOWN; } // IDirect3DDevice9 can not be used on WDDM OSes(>=vista) bool GLInteropResource::ensureResource(DXGI_FORMAT fmt, int w, int h, GLuint tex, int plane) { Q_UNUSED(tex); Q_ASSERT(gl().DXRegisterObjectNV && "WGL_NV_DX_interop is required"); if (fmt == tex_format && d3dtex[plane].Get() && width == w && height == h) return true; if (mapped == 2) { d3dtex.clear(); d3dtex.resize(2); mapped = 0; } CD3D11_TEXTURE2D_DESC desc(GetPlaneFormat(fmt, plane), w, h, 1, 1); desc.BindFlags |= D3D11_BIND_RENDER_TARGET; desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; DX_ENSURE(d3ddev->CreateTexture2D(&desc, NULL, &d3dtex[plane]), false); #if 0 ComPtr resource; DX_ENSURE(d3dtex[plane].As(&resource), false); HANDLE share_handle = NULL; DX_ENSURE(resource->GetSharedHandle(&share_handle), false); // required by d3d9 not d3d10&11: https://www.opengl.org/registry/specs/NV/DX_interop2.txt WGL_WARN(gl().DXSetResourceShareHandleNV(d3dtex[plane].Get(), share_handle)); #endif width = w; height = h; tex_format = fmt; mapped++; return true; } } } QtAV-1.12.0/src/directx/SurfaceInteropD3D9.cpp000066400000000000000000000145031312235004300206540ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D9.h" #include "QtAV/VideoFrame.h" #define DX_LOG_COMPONENT "D3D9 Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" // no need to check qt4 because no ANGLE there #if QTAV_HAVE(EGL_CAPI) // always use dynamic load #if defined(QT_OPENGL_DYNAMIC) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_2_ANGLE) #define QTAV_HAVE_D3D9_EGL 1 #endif #endif //QTAV_HAVE(EGL_CAPI) #if defined(QT_OPENGL_DYNAMIC) || !defined(QT_OPENGL_ES_2) #define QTAV_HAVE_D3D9_GL 1 #endif #include "utils/Logger.h" #define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID name = { l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} namespace QtAV { extern VideoFormat::PixelFormat pixelFormatFromFourcc(int format); MS_GUID(IID_IDirect3DDevice9Ex, 0xb18b10ce, 0x2649, 0x405a, 0x87, 0xf, 0x95, 0xf7, 0x77, 0xd4, 0x31, 0x3a); namespace d3d9 { bool InteropResource::isSupported(InteropType type) { Q_UNUSED(type); #if QTAV_HAVE(D3D9_EGL) if (type == InteropAuto || type == InteropEGL) { if (OpenGLHelper::isOpenGLES()) return true; } #endif #if QTAV_HAVE(D3D9_GL) if (type == InteropAuto || type == InteropGL) { if (!OpenGLHelper::isOpenGLES()) return true; } #endif return false; } extern InteropResource* CreateInteropEGL(IDirect3DDevice9 *dev); extern InteropResource* CreateInteropGL(IDirect3DDevice9 *dev); InteropResource* InteropResource::create(IDirect3DDevice9 *dev, InteropType type) { Q_UNUSED(dev); if (type == InteropAuto || type == InteropEGL) { IDirect3DDevice9Ex *devEx; dev->QueryInterface(IID_IDirect3DDevice9Ex, (void**)&devEx); qDebug("using D3D9Ex: %d", !!devEx); // if (!devEx) { qWarning("IDirect3DDevice9Ex is required to share d3d resource. It's available in vista and later. d3d9 can not CreateTexture with shared handle"); } SafeRelease(&devEx); #if QTAV_HAVE(D3D9_EGL) if (OpenGLHelper::isOpenGLES()) return CreateInteropEGL(dev); #endif } if (type == InteropAuto || type == InteropGL) { #if QTAV_HAVE(D3D9_GL) if (!OpenGLHelper::isOpenGLES()) return CreateInteropGL(dev); #endif } return NULL; } InteropResource::InteropResource(IDirect3DDevice9 *d3device) : d3ddev(d3device) , dx_texture(NULL) , dx_surface(NULL) , width(0) , height(0) { d3ddev->AddRef(); } InteropResource::~InteropResource() { releaseDX(); SafeRelease(&d3ddev); } void InteropResource::releaseDX() { SafeRelease(&dx_surface); SafeRelease(&dx_texture); } SurfaceInterop::~SurfaceInterop() { SafeRelease(&m_surface); } void SurfaceInterop::setSurface(IDirect3DSurface9 *surface, int frame_w, int frame_h) { m_surface = surface; m_surface->AddRef(); frame_width = frame_w; frame_height = frame_h; } void* SurfaceInterop::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane) { if (!handle) return NULL; if (!m_surface) return 0; if (type == GLTextureSurface) { if (!fmt.isRGB()) return NULL; if (m_resource->map(m_surface, *((GLuint*)handle), frame_width, frame_height, plane)) return handle; } else if (type == HostMemorySurface) { return mapToHost(fmt, handle, plane); } return NULL; } void SurfaceInterop::unmap(void *handle) { m_resource->unmap(*((GLuint*)handle)); } void* SurfaceInterop::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); class ScopedD3DLock { IDirect3DSurface9 *mpD3D; public: ScopedD3DLock(IDirect3DSurface9* d3d, D3DLOCKED_RECT *rect) : mpD3D(d3d) { if (FAILED(mpD3D->LockRect(rect, NULL, D3DLOCK_READONLY))) { qWarning("Failed to lock surface"); mpD3D = 0; } } ~ScopedD3DLock() { if (mpD3D) mpD3D->UnlockRect(); } }; D3DLOCKED_RECT lock; ScopedD3DLock(m_surface, &lock); if (lock.Pitch == 0) return NULL; // TODO: use the same code as VideoDecoderDXVA::frame() like vaapi. //picth >= desc.Width D3DSURFACE_DESC desc; m_surface->GetDesc(&desc); const VideoFormat fmt = VideoFormat(pixelFormatFromFourcc(desc.Format)); if (!fmt.isValid()) { qWarning("unsupported D3D9 pixel format: %#x", desc.Format); return NULL; } //YV12 need swap, not imc3? // imc3 U V pitch == Y pitch, but half of the U/V plane is space. we convert to yuv420p here // nv12 bpp(1)==1 // 3rd plane is not used for nv12 int pitch[3] = { lock.Pitch, 0, 0}; //compute chroma later quint8 *src[] = { (quint8*)lock.pBits, 0, 0}; //compute chroma later Q_ASSERT(src[0] && pitch[0] > 0); const bool swap_uv = desc.Format == MAKEFOURCC('I','M','C','3'); // try to use SSE. fallback to normal copy if SSE is not supported VideoFrame frame(VideoFrame::fromGPU(fmt, frame_width, frame_height, desc.Height, src, pitch, true, swap_uv)); // TODO: check rgb32 because d3d can use hw to convert if (format != fmt) frame = frame.to(format); VideoFrame *f = reinterpret_cast(handle); frame.setTimestamp(f->timestamp()); *f = frame; return f; } } //namespace d3d9 } //namespace QtAV QtAV-1.12.0/src/directx/SurfaceInteropD3D9.h000066400000000000000000000065751312235004300203330ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROPD3D9_H #define QTAV_SURFACEINTEROPD3D9_H #include #include "QtAV/SurfaceInterop.h" namespace QtAV { namespace d3d9 { enum InteropType { InteropAuto, InteropEGL, InteropGL }; class InteropResource { public: static bool isSupported(InteropType type = InteropAuto); static InteropResource* create(IDirect3DDevice9 * dev, InteropType type = InteropAuto); typedef unsigned int GLuint; InteropResource(IDirect3DDevice9 * d3device); virtual ~InteropResource(); /*! * \brief map * \param surface d3d9 surface * \param tex opengl texture * \param w frame width(visual width) without alignment, <= d3d9 surface width * \param h frame height(visual height) * \param plane useless now * \return true if success */ virtual bool map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int plane) = 0; virtual bool unmap(GLuint tex) { Q_UNUSED(tex); return true;} protected: void releaseDX(); IDirect3DDevice9 *d3ddev; IDirect3DTexture9 *dx_texture; IDirect3DSurface9 *dx_surface; // size is frame size(visual size) for display int width, height; // video frame width and dx_surface width without alignment, not dxva decoded surface width }; typedef QSharedPointer InteropResourcePtr; class SurfaceInterop Q_DECL_FINAL: public VideoSurfaceInterop { public: SurfaceInterop(const InteropResourcePtr& res) : m_surface(0), m_resource(res), frame_width(0), frame_height(0) {} ~SurfaceInterop(); /*! * \brief setSurface * \param surface d3d9 surface * \param frame_w frame width(visual width) without alignment, <= d3d9 surface width * \param frame_h frame height(visual height) */ void setSurface(IDirect3DSurface9* surface, int frame_w, int frame_h); /// GLTextureSurface only supports rgb32 void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE; void unmap(void *handle) Q_DECL_OVERRIDE; protected: /// copy from gpu (optimized if possible) and convert to target format if necessary void* mapToHost(const VideoFormat &format, void *handle, int plane); private: IDirect3DSurface9 *m_surface; InteropResourcePtr m_resource; int frame_width, frame_height; }; } //namespace d3d9 } //namespace QtAV #endif // QTAV_SURFACEINTEROPD3D9_H QtAV-1.12.0/src/directx/SurfaceInteropD3D9EGL.cpp000066400000000000000000000205311312235004300212020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D9.h" #define DX_LOG_COMPONENT "D3D9EGL Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" #ifdef QT_OPENGL_ES_2_ANGLE_STATIC #define CAPI_LINK_EGL #else #define EGL_CAPI_NS #endif //QT_OPENGL_ES_2_ANGLE_STATIC #include "capi/egl_api.h" #include //include after egl_capi.h to match types namespace QtAV { namespace d3d9 { //d3d9 class EGL { public: EGL() : dpy(EGL_NO_DISPLAY), surface(EGL_NO_SURFACE) {} EGLDisplay dpy; EGLSurface surface; }; class EGLInteropResource Q_DECL_FINAL: public InteropResource { public: EGLInteropResource(IDirect3DDevice9 * d3device); ~EGLInteropResource(); bool map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE; private: void releaseEGL(); bool ensureSurface(int w, int h); EGL* egl; IDirect3DQuery9 *dx_query; }; InteropResource* CreateInteropEGL(IDirect3DDevice9 *dev) { return new EGLInteropResource(dev); } EGLInteropResource::EGLInteropResource(IDirect3DDevice9 * d3device) : InteropResource(d3device) , egl(new EGL()) , dx_query(NULL) { DX_ENSURE_OK(d3device->CreateQuery(D3DQUERYTYPE_EVENT, &dx_query)); dx_query->Issue(D3DISSUE_END); } EGLInteropResource::~EGLInteropResource() { releaseEGL(); if (egl) { delete egl; egl = NULL; } SafeRelease(&dx_query); } void EGLInteropResource::releaseEGL() { if (egl->surface != EGL_NO_SURFACE) { eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); eglDestroySurface(egl->dpy, egl->surface); egl->surface = EGL_NO_SURFACE; } } bool EGLInteropResource::ensureSurface(int w, int h) { if (egl->surface && width == w && height == h) return true; releaseEGL(); // egl->dpy = eglGetCurrentDisplay(); qDebug("EGL version: %s, client api: %s", eglQueryString(egl->dpy, EGL_VERSION), eglQueryString(egl->dpy, EGL_CLIENT_APIS)); EGLint cfg_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, // EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, //remove? EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_NONE }; EGLint nb_cfgs; EGLConfig egl_cfg; if (!eglChooseConfig(egl->dpy, cfg_attribs, &egl_cfg, 1, &nb_cfgs)) { qWarning("Failed to create EGL configuration"); return false; } // check extensions QList extensions = QByteArray(eglQueryString(egl->dpy, EGL_EXTENSIONS)).split(' '); // ANGLE_d3d_share_handle_client_buffer will be used if possible // TODO: strstr is enough const bool kEGL_ANGLE_d3d_share_handle_client_buffer = extensions.contains("EGL_ANGLE_d3d_share_handle_client_buffer"); const bool kEGL_ANGLE_query_surface_pointer = extensions.contains("EGL_ANGLE_query_surface_pointer"); if (!kEGL_ANGLE_d3d_share_handle_client_buffer && !kEGL_ANGLE_query_surface_pointer) { qWarning("EGL extension 'kEGL_ANGLE_query_surface_pointer' or 'ANGLE_d3d_share_handle_client_buffer' is required!"); return false; } GLint has_alpha = 1; //QOpenGLContext::currentContext()->format().hasAlpha() eglGetConfigAttrib(egl->dpy, egl_cfg, EGL_BIND_TO_TEXTURE_RGBA, &has_alpha); //EGL_ALPHA_SIZE qDebug("choose egl display:%p config: %p/%d, has alpha: %d", egl->dpy, egl_cfg, nb_cfgs, has_alpha); EGLint attribs[] = { EGL_WIDTH, w, EGL_HEIGHT, h, EGL_TEXTURE_FORMAT, has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE }; HANDLE share_handle = NULL; if (!kEGL_ANGLE_d3d_share_handle_client_buffer && kEGL_ANGLE_query_surface_pointer) { EGL_ENSURE((egl->surface = eglCreatePbufferSurface(egl->dpy, egl_cfg, attribs)) != EGL_NO_SURFACE, false); qDebug("pbuffer surface: %p", egl->surface); PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE = reinterpret_cast(eglGetProcAddress("eglQuerySurfacePointerANGLE")); if (!eglQuerySurfacePointerANGLE) { qWarning("EGL_ANGLE_query_surface_pointer is not supported"); return false; } EGL_ENSURE(eglQuerySurfacePointerANGLE(egl->dpy, egl->surface, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle), false); } releaseDX(); // _A8 for a yuv plane /* * d3d resource share requires windows >= vista: https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800(v=vs.85).aspx * from extension files: * d3d9: level must be 1, dimensions must match EGL surface's * d3d9ex or d3d10: */ DX_ENSURE_OK(d3ddev->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &dx_texture, &share_handle) , false); DX_ENSURE_OK(dx_texture->GetSurfaceLevel(0, &dx_surface), false); if (kEGL_ANGLE_d3d_share_handle_client_buffer) { // requires extension EGL_ANGLE_d3d_share_handle_client_buffer // egl surface size must match d3d texture's // d3d9ex or d3d10 is required EGL_ENSURE((egl->surface = eglCreatePbufferFromClientBuffer(egl->dpy, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, share_handle, egl_cfg, attribs)), false); qDebug("pbuffer surface from client buffer: %p", egl->surface); } width = w; height = h; return true; } bool EGLInteropResource::map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int) { if (!ensureSurface(w, h)) { releaseEGL(); releaseDX(); return false; } const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h}; DX_ENSURE(d3ddev->StretchRect(surface, &src, dx_surface, NULL, D3DTEXF_NONE), false); if (dx_query) { // Flush the draw command now. Ideally, this should be done immediately before the draw call that uses the texture. Flush it once here though. dx_query->Issue(D3DISSUE_END); //StretchRect does not supports odd values // ensure data is copied to egl surface. Solution and comment is from chromium // The DXVA decoder has its own device which it uses for decoding. ANGLE has its own device which we don't have access to. // The above code attempts to copy the decoded picture into a surface which is owned by ANGLE. // As there are multiple devices involved in this, the StretchRect call above is not synchronous. // We attempt to flush the batched operations to ensure that the picture is copied to the surface owned by ANGLE. // We need to do this in a loop and call flush multiple times. // We have seen the GetData call for flushing the command buffer fail to return success occassionally on multi core machines, leading to an infinite loop. // Workaround is to have an upper limit of 10 on the number of iterations to wait for the Flush to finish. int k = 0; while ((dx_query->GetData(NULL, 0, D3DGETDATA_FLUSH) == FALSE) && ++k < 10) { Sleep(1); } } DYGL(glBindTexture(GL_TEXTURE_2D, tex)); eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); return true; } } //namespace d3d9 } //namespace QtAV QtAV-1.12.0/src/directx/SurfaceInteropD3D9GL.cpp000066400000000000000000000114131312235004300210740ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SurfaceInteropD3D9.h" #define DX_LOG_COMPONENT "D3D9GL Interop" #include "utils/DirectXHelper.h" #include "opengl/OpenGLHelper.h" //dynamic gl or desktop gl namespace QtAV { namespace d3d9 { class GLInteropResource Q_DECL_FINAL: public InteropResource { public: GLInteropResource(IDirect3DDevice9 * d3device); ~GLInteropResource(); bool map(IDirect3DSurface9 *surface, GLuint tex, int frame_w, int frame_h, int) Q_DECL_OVERRIDE; bool unmap(GLuint tex) Q_DECL_OVERRIDE; private: bool ensureResource(int w, int h, GLuint tex); HANDLE interop_dev; HANDLE interop_obj; }; InteropResource* CreateInteropGL(IDirect3DDevice9 *dev) { return new GLInteropResource(dev); } GLInteropResource::GLInteropResource(IDirect3DDevice9 *d3device) : InteropResource(d3device) , interop_dev(NULL) , interop_obj(NULL) { } GLInteropResource::~GLInteropResource() { // FIXME: why unregister/close interop obj/dev here will crash(tested on intel driver)? must be in current opengl context? } bool GLInteropResource::map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int) { if (!ensureResource(w, h, tex)) { releaseDX(); return false; } // open/close and register/unregster in every map/unmap to ensure called in current context and avoid crash (tested on intel driver) // interop operations begin WGL_ENSURE((interop_dev = gl().DXOpenDeviceNV(d3ddev)) != NULL, false); // call in ensureResource or in map? WGL_ENSURE((interop_obj = gl().DXRegisterObjectNV(interop_dev, dx_surface, tex, GL_TEXTURE_2D, WGL_ACCESS_READ_ONLY_NV)) != NULL, false); // prepare dx resources for gl const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h}; //StretchRect does not supports odd values DX_ENSURE_OK(d3ddev->StretchRect(surface, &src, dx_surface, NULL, D3DTEXF_NONE), false); // lock dx resources WGL_ENSURE(gl().DXLockObjectsNV(interop_dev, 1, &interop_obj), false); WGL_ENSURE(gl().DXObjectAccessNV(interop_obj, WGL_ACCESS_READ_ONLY_NV), false); DYGL(glBindTexture(GL_TEXTURE_2D, tex)); return true; } bool GLInteropResource::unmap(GLuint tex) { Q_UNUSED(tex); if (!interop_obj || !interop_dev) return false; DYGL(glBindTexture(GL_TEXTURE_2D, 0)); WGL_ENSURE(gl().DXUnlockObjectsNV(interop_dev, 1, &interop_obj), false); WGL_WARN(gl().DXUnregisterObjectNV(interop_dev, interop_obj)); // interop operations end WGL_WARN(gl().DXCloseDeviceNV(interop_dev)); interop_obj = NULL; interop_dev = NULL; return true; } // IDirect3DDevice9 can not be used on WDDM OSes(>=vista) bool GLInteropResource::ensureResource(int w, int h, GLuint tex) { Q_UNUSED(tex); Q_ASSERT(gl().DXRegisterObjectNV && "WGL_NV_DX_interop is required"); if (dx_surface && width == w && height == h) return true; releaseDX(); HANDLE share_handle = NULL; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const bool has_alpha = QOpenGLContext::currentContext()->format().hasAlpha(); #else const bool has_alpha = QOpenGLContext::currentContext()->format().alpha(); #endif // _A8 for a yuv plane DX_ENSURE_OK(d3ddev->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &dx_texture, &share_handle) , false); DX_ENSURE_OK(dx_texture->GetSurfaceLevel(0, &dx_surface), false); // required by d3d9 not d3d10&11: https://www.opengl.org/registry/specs/NV/DX_interop2.txt WGL_WARN(gl().DXSetResourceShareHandleNV(dx_surface, share_handle)); width = w; height = h; return true; } } } QtAV-1.12.0/src/directx/dxcompat.h000066400000000000000000000037001312235004300166200ustar00rootroot00000000000000#ifndef QTAV_DXCOMPAT_H #define QTAV_DXCOMPAT_H // check macros for mingw #include #ifndef _Out_writes_bytes_opt_ #define _Out_writes_bytes_opt_(s) #endif #ifndef _Inout_opt_bytecount_ #define _Inout_opt_bytecount_(s) #endif #ifndef _Pre_null_ #define _Pre_null_ #endif #ifndef _COM_Outptr_opt_ #define _COM_Outptr_opt_ #endif #ifndef _COM_Outptr_ #define _COM_Outptr_ #endif #ifndef _In_range_ #define _In_range_(...) #endif #ifndef _Inout_updates_bytes_ #define _Inout_updates_bytes_(s) #endif #ifndef _Outptr_result_bytebuffer_ #define _Outptr_result_bytebuffer_(s) #endif #ifndef _In_reads_bytes_opt_ #define _In_reads_bytes_opt_(s) #endif #ifndef _In_reads_opt_ #define _In_reads_opt_(s) #endif #ifndef _In_reads_bytes_ #define _In_reads_bytes_(s) #endif #ifndef _Out_writes_bytes_ #define _Out_writes_bytes_(s) #endif #ifndef _In_reads_ #define _In_reads_(s) #endif #ifndef _Out_writes_ #define _Out_writes_(s) #endif #ifndef _Out_opt_ #define _Out_opt_ #endif #ifndef _Inout_opt_ #define _Inout_opt_ #endif #ifndef _Out_writes_opt_ #define _Out_writes_opt_(s) #endif #ifndef _Outptr_result_maybenull_ #define _Outptr_result_maybenull_ #endif #ifndef _Outptr_opt_result_maybenull_ #define _Outptr_opt_result_maybenull_ #endif #ifndef _Field_size_ #define _Field_size_(s) #endif #ifndef _Field_size_opt_ #define _Field_size_opt_(s) #endif #ifndef _Outptr_ #define _Outptr_ #endif #ifndef _In_opt_z_ #define _In_opt_z_ #endif #ifndef _Reserved_ #define _Reserved_ #endif #ifndef __reserved #define __reserved #endif #ifndef __in_ecount #define __in_ecount(s) #endif #ifndef __in_bcount #define __in_bcount(s) #endif #ifndef __out_bcount #define __out_bcount(s) #endif #ifndef __out_ecount #define __out_ecount(s) #endif #ifndef __inout #define __inout #endif #ifndef __in_opt #define __in_opt #endif #ifndef __deref_out #define __deref_out #endif // conflict with stl vars #ifndef __in //#define __in #endif #ifndef __out //#define __out #endif #endif //QTAV_DXCOMPAT_H QtAV-1.12.0/src/filter/000077500000000000000000000000001312235004300144535ustar00rootroot00000000000000QtAV-1.12.0/src/filter/EncodeFilter.cpp000066400000000000000000000221331312235004300175230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015-2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/EncodeFilter.h" #include #include #include #include #include "QtAV/private/Filter_p.h" #include "QtAV/AudioEncoder.h" #include "QtAV/VideoEncoder.h" #include "utils/Logger.h" namespace QtAV { class AudioEncodeFilterPrivate Q_DECL_FINAL : public AudioFilterPrivate { public: AudioEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0) {} ~AudioEncodeFilterPrivate() { if (enc) { enc->close(); delete enc; } } AudioEncoder* enc; qint64 start_time; bool async; QAtomicInt finishing; QThread enc_thread; }; AudioEncodeFilter::AudioEncodeFilter(QObject *parent) : AudioFilter(*new AudioEncodeFilterPrivate(), parent) { connect(this, SIGNAL(requestToEncode(QtAV::AudioFrame)), this, SLOT(encode(QtAV::AudioFrame))); connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit())); } void AudioEncodeFilter::setAsync(bool value) { DPTR_D(AudioEncodeFilter); if (d.async == value) return; if (value) moveToThread(&d.enc_thread); else moveToThread(qApp->thread()); d.async = value; } bool AudioEncodeFilter::isAsync() const { return d_func().async; } AudioEncoder* AudioEncodeFilter::createEncoder(const QString &name) { DPTR_D(AudioEncodeFilter); if (d.enc) { d.enc->close(); delete d.enc; } d.enc = AudioEncoder::create(name.toLatin1().constData()); return d.enc; } AudioEncoder* AudioEncodeFilter::encoder() const { return d_func().enc; } qint64 AudioEncodeFilter::startTime() const { return d_func().start_time; } void AudioEncodeFilter::setStartTime(qint64 value) { DPTR_D(AudioEncodeFilter); if (d.start_time == value) return; d.start_time = value; Q_EMIT startTimeChanged(value); } void AudioEncodeFilter::finish() { DPTR_D(AudioEncodeFilter); if (isAsync() && !d.enc_thread.isRunning()) return; if (!d.finishing.testAndSetRelaxed(0, 1)) return; qDebug("About finish audio encoding"); AudioFrame f; f.setTimestamp(std::numeric_limits::max()); if (isAsync()) { Q_EMIT requestToEncode(f); } else { encode(f); //FIXME: not thread safe. lock in encode? } } void AudioEncodeFilter::process(Statistics *statistics, AudioFrame *frame) { Q_UNUSED(statistics); DPTR_D(AudioEncodeFilter); if (!isAsync()) { encode(*frame); return; } if (!d.enc_thread.isRunning()) d.enc_thread.start(); Q_EMIT requestToEncode(*frame); } void AudioEncodeFilter::encode(const AudioFrame& frame) { DPTR_D(AudioEncodeFilter); if (!d.enc) return; // encode delayed frames can pass an invalid frame if (!d.enc->isOpen() && frame.isValid()) { #if 0 //TODO: if set the input format, check whether it is supported in open() if (!d.enc->audioFormat().isValid()) { AudioFormat af(frame.format()); //if (af.isPlanar()) // af.setSampleFormat(AudioFormat::packedSampleFormat(af.sampleFormat())); af.setSampleFormat(AudioFormat::SampleFormat_Unknown); d.enc->setAudioFormat(af); } #endif if (!d.enc->open()) { // TODO: error() qWarning("Failed to open audio encoder"); return; } Q_EMIT readyToEncode(); } if (!frame.isValid() && frame.timestamp() == std::numeric_limits::max()) { while (d.enc->encode()) { qDebug("encode delayed audio frames..."); Q_EMIT frameEncoded(d.enc->encoded()); } d.enc->close(); Q_EMIT finished(); d.finishing = 0; return; } if (frame.timestamp()*1000.0 < startTime()) return; AudioFrame f(frame); if (f.format() != d.enc->audioFormat()) f = f.to(d.enc->audioFormat()); if (!d.enc->encode(f)) { if (f.timestamp() == std::numeric_limits::max()) { Q_EMIT finished(); d.finishing = 0; } return; } if (!d.enc->encoded().isValid()) return; Q_EMIT frameEncoded(d.enc->encoded()); } class VideoEncodeFilterPrivate Q_DECL_FINAL : public VideoFilterPrivate { public: VideoEncodeFilterPrivate() : enc(0), start_time(0), async(false), finishing(0) {} ~VideoEncodeFilterPrivate() { if (enc) { enc->close(); delete enc; } } VideoEncoder* enc; qint64 start_time; bool async; QAtomicInt finishing; QThread enc_thread; }; VideoEncodeFilter::VideoEncodeFilter(QObject *parent) : VideoFilter(*new VideoEncodeFilterPrivate(), parent) { connect(this, SIGNAL(requestToEncode(QtAV::VideoFrame)), this, SLOT(encode(QtAV::VideoFrame))); connect(this, SIGNAL(finished()), &d_func().enc_thread, SLOT(quit())); } void VideoEncodeFilter::setAsync(bool value) { DPTR_D(VideoEncodeFilter); if (d.async == value) return; if (value) moveToThread(&d.enc_thread); else moveToThread(qApp->thread()); // if async but not in main thread, queued sig/slot connection will not work d.async = value; } bool VideoEncodeFilter::isAsync() const { return d_func().async; } VideoEncoder* VideoEncodeFilter::createEncoder(const QString &name) { DPTR_D(VideoEncodeFilter); if (d.enc) { d.enc->close(); delete d.enc; } d.enc = VideoEncoder::create(name.toLatin1().constData()); return d.enc; } VideoEncoder* VideoEncodeFilter::encoder() const { return d_func().enc; } qint64 VideoEncodeFilter::startTime() const { return d_func().start_time; } void VideoEncodeFilter::setStartTime(qint64 value) { DPTR_D(VideoEncodeFilter); if (d.start_time == value) return; d.start_time = value; Q_EMIT startTimeChanged(value); } void VideoEncodeFilter::finish() { DPTR_D(VideoEncodeFilter); if (isAsync() && !d.enc_thread.isRunning()) return; if (!d.finishing.testAndSetRelaxed(0, 1)) return; qDebug("About finish video encoding"); VideoFrame f; f.setTimestamp(std::numeric_limits::max()); if (isAsync()) { Q_EMIT requestToEncode(f); } else { encode(f); } } void VideoEncodeFilter::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(statistics); DPTR_D(VideoEncodeFilter); if (!isAsync()) { encode(*frame); return; } if (!d.enc_thread.isRunning()) d.enc_thread.start(); requestToEncode(*frame); } void VideoEncodeFilter::encode(const VideoFrame& frame) { DPTR_D(VideoEncodeFilter); if (!d.enc) return; // encode delayed frames can pass an invalid frame if (!d.enc->isOpen() && frame.isValid()) { d.enc->setWidth(frame.width()); d.enc->setHeight(frame.height()); if (!d.enc->open()) { // TODO: error() qWarning("Failed to open video encoder"); return; } Q_EMIT readyToEncode(); } if (!frame.isValid() && frame.timestamp() == std::numeric_limits::max()) { while (d.enc->encode()) { qDebug("encode delayed video frames..."); Q_EMIT frameEncoded(d.enc->encoded()); } d.enc->close(); Q_EMIT finished(); d.finishing = 0; return; } if (d.enc->width() != frame.width() || d.enc->height() != frame.height()) { qWarning("Frame size (%dx%d) and video encoder size (%dx%d) mismatch! Close encoder please.", d.enc->width(), d.enc->height(), frame.width(), frame.height()); return; } if (frame.timestamp()*1000.0 < startTime()) return; // TODO: async VideoFrame f(frame); if (f.pixelFormat() != d.enc->pixelFormat()) f = f.to(d.enc->pixelFormat()); if (!d.enc->encode(f)) { if (f.timestamp() == std::numeric_limits::max()) { Q_EMIT finished(); d.finishing = 0; } return; } if (!d.enc->encoded().isValid()) return; Q_EMIT frameEncoded(d.enc->encoded()); } } //namespace QtAV QtAV-1.12.0/src/filter/Filter.cpp000066400000000000000000000131541312235004300164100ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Filter.h" #include "QtAV/private/Filter_p.h" #include "QtAV/Statistics.h" #include "QtAV/AVOutput.h" #include "QtAV/AVPlayer.h" #include "filter/FilterManager.h" #include "utils/Logger.h" /* * 1. parent == target (QObject): * in ~target(), remove the filter but not delete it (parent not null now). * in ~QObject, filter is deleted as a child *----------------------------------------------- * 2. parent != target.parent: * if delete filter first, filter must notify FilterManager (uninstall in dtor here) to uninstall to avoid target to access it (in ~target()) * if delete target first, target remove the filter but not delete it (parent not null now). */ namespace QtAV { Filter::Filter(FilterPrivate &d, QObject *parent) : QObject(parent) , DPTR_INIT(&d) { if (parent) setOwnedByTarget(false); } Filter::~Filter() { uninstall(); } void Filter::setEnabled(bool enabled) { DPTR_D(Filter); if (d.enabled == enabled) return; d.enabled = enabled; Q_EMIT enabledChanged(enabled); } bool Filter::isEnabled() const { DPTR_D(const Filter); return d.enabled; } void Filter::setOwnedByTarget(bool value) { d_func().owned_by_target = value; } bool Filter::isOwnedByTarget() const { return d_func().owned_by_target; } bool Filter::uninstall() { return FilterManager::instance().uninstallFilter(this); // TODO: target } AudioFilter::AudioFilter(QObject *parent) : Filter(*new AudioFilterPrivate(), parent) {} AudioFilter::AudioFilter(AudioFilterPrivate& d, QObject *parent) : Filter(d, parent) {} /*TODO: move to AVPlayer.cpp to reduce dependency?*/ bool AudioFilter::installTo(AVPlayer *player) { return player->installFilter(this); } void AudioFilter::apply(Statistics *statistics, AudioFrame *frame) { process(statistics, frame); } VideoFilter::VideoFilter(QObject *parent) : Filter(*new VideoFilterPrivate(), parent) {} VideoFilter::VideoFilter(VideoFilterPrivate &d, QObject *parent) : Filter(d, parent) {} VideoFilterContext *VideoFilter::context() { DPTR_D(VideoFilter); if (!d.context) { //fake. only to store some parameters at the beginnig. it will be destroyed and set to a new instance if context type mismatch in prepareContext, with old parameters d.context = VideoFilterContext::create(VideoFilterContext::QtPainter); } return d.context; } bool VideoFilter::isSupported(VideoFilterContext::Type ct) const { // TODO: return false return VideoFilterContext::None == ct; } bool VideoFilter::installTo(AVPlayer *player) { return player->installFilter(this); } /*TODO: move to AVOutput.cpp to reduce dependency?*/ /* * filter.installTo(target,...) calls target.installFilter(filter) * If filter is already registered in FilterManager, then return false * Otherwise, call FilterManager.register(filter) and target.filters.push_back(filter), return true * NOTE: the installed filter will be deleted by the target if filter is owned by target AND it's parent (QObject) is null. */ bool VideoFilter::installTo(AVOutput *output) { return output->installFilter(this); } bool VideoFilter::prepareContext(VideoFilterContext *&ctx, Statistics *statistics, VideoFrame *frame) { DPTR_D(VideoFilter); if (!ctx || !isSupported(ctx->type())) { //qDebug("no context: %p, or context type %d is not supported", ctx, ctx? ctx->type() : 0); return isSupported(VideoFilterContext::None); } if (!d.context || d.context->type() != ctx->type()) { VideoFilterContext* c = VideoFilterContext::create(ctx->type());//each filter has it's own context instance, but share the common parameters if (d.context) { c->pen = d.context->pen; c->brush = d.context->brush; c->clip_path = d.context->clip_path; c->rect = d.context->rect; c->transform = d.context->transform; c->font = d.context->font; c->opacity = d.context->opacity; c->paint_device = d.context->paint_device; } if (d.context) { delete d.context; } d.context = c; } d.context->video_width = statistics->video_only.width; d.context->video_height = statistics->video_only.height; ctx->video_width = statistics->video_only.width; ctx->video_height = statistics->video_only.height; // share common data d.context->shareFrom(ctx); d.context->initializeOnFrame(frame); ctx->shareFrom(d.context); return true; } void VideoFilter::apply(Statistics *statistics, VideoFrame *frame) { process(statistics, frame); } } //namespace QtAV QtAV-1.12.0/src/filter/FilterContext.cpp000066400000000000000000000177541312235004300177670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/FilterContext.h" #include #include #include #include #include "QtAV/VideoFrame.h" #include "utils/Logger.h" namespace QtAV { VideoFilterContext *VideoFilterContext::create(Type t) { VideoFilterContext *ctx = 0; switch (t) { case QtPainter: ctx = new QPainterFilterContext(); break; #if QTAV_HAVE(X11) case X11: ctx = new X11FilterContext(); break; #endif default: break; } return ctx; } void VideoFilterContext::initializeOnFrame(VideoFrame *frame) { Q_UNUSED(frame); } VideoFilterContext::VideoFilterContext(): painter(0) , opacity(1) , paint_device(0) , video_width(0) , video_height(0) , own_painter(false) , own_paint_device(false) { font.setBold(true); font.setPixelSize(26); pen.setColor(Qt::white); rect = QRect(32, 32, 0, 0); //TODO: why painting will above the visible area if the draw at (0, 0)? ascent } VideoFilterContext::~VideoFilterContext() { if (painter) { // painter is shared, so may be end() multiple times. // TODO: use shared ptr //if (painter->isActive()) // painter->end(); if (own_painter) { qDebug("VideoFilterContext %p delete painter %p", this, painter); delete painter; painter = 0; } } if (paint_device) { qDebug("VideoFilterContext %p delete paint device in %p", this, paint_device); if (own_paint_device) delete paint_device; //delete recursively for widget paint_device = 0; } } void VideoFilterContext::drawImage(const QPointF &pos, const QImage &image, const QRectF& source, Qt::ImageConversionFlags flags) { Q_UNUSED(pos); Q_UNUSED(image); Q_UNUSED(source); Q_UNUSED(flags); } void VideoFilterContext::drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags) { Q_UNUSED(target); Q_UNUSED(image); Q_UNUSED(source); Q_UNUSED(flags); } void VideoFilterContext::drawPlainText(const QPointF &pos, const QString &text) { Q_UNUSED(pos); Q_UNUSED(text); } void VideoFilterContext::drawPlainText(const QRectF &rect, int flags, const QString &text) { Q_UNUSED(rect); Q_UNUSED(flags); Q_UNUSED(text); } void VideoFilterContext::drawRichText(const QRectF &rect, const QString &text, bool wordWrap) { Q_UNUSED(rect); Q_UNUSED(text); Q_UNUSED(wordWrap); } void VideoFilterContext::shareFrom(VideoFilterContext *vctx) { if (!vctx) { qWarning("shared filter context is null!"); return; } painter = vctx->painter; paint_device = vctx->paint_device; own_painter = false; own_paint_device = false; video_width = vctx->video_width; video_height = vctx->video_height; } QPainterFilterContext::QPainterFilterContext() : VideoFilterContext() , doc(0) , cvt(0) {} QPainterFilterContext::~QPainterFilterContext() { if (doc) { delete doc; doc = 0; } if (cvt) { delete cvt; cvt = 0; } } // TODO: use drawPixmap? void QPainterFilterContext::drawImage(const QPointF &pos, const QImage &image, const QRectF& source, Qt::ImageConversionFlags flags) { if (!prepare()) return; if (source.isNull()) painter->drawImage(pos, image, QRectF(0, 0, image.width(), image.height()), flags); else painter->drawImage(pos, image, source, flags); painter->restore(); } void QPainterFilterContext::drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags) { if (!prepare()) return; if (source.isNull()) painter->drawImage(target, image, QRectF(0, 0, image.width(), image.height()), flags); else painter->drawImage(target, image, source, flags); painter->restore(); } void QPainterFilterContext::drawPlainText(const QPointF &pos, const QString &text) { if (!prepare()) return; QFontMetrics fm(font); painter->drawText(pos + QPoint(0, fm.ascent()), text); painter->restore(); } void QPainterFilterContext::drawPlainText(const QRectF &rect, int flags, const QString &text) { if (!prepare()) return; if (rect.isNull()) painter->drawText(rect.topLeft(), text); else painter->drawText(rect, flags, text); painter->restore(); } void QPainterFilterContext::drawRichText(const QRectF &rect, const QString &text, bool wordWrap) { Q_UNUSED(rect); Q_UNUSED(text); if (!prepare()) return; if (!doc) doc = new QTextDocument(); doc->setHtml(text); painter->translate(rect.topLeft()); //drawContent() can not set target rect, so move here if (wordWrap) doc->setTextWidth(rect.width()); doc->drawContents(painter); painter->restore(); } bool QPainterFilterContext::isReady() const { return !!painter && painter->isActive(); } bool QPainterFilterContext::prepare() { if (!isReady()) return false; painter->save(); //is it necessary? painter->setBrush(brush); painter->setPen(pen); painter->setFont(font); painter->setOpacity(opacity); if (!clip_path.isEmpty()) { painter->setClipPath(clip_path); } //transform at last! because clip_path is relative to paint device coord painter->setTransform(transform); return true; } void QPainterFilterContext::initializeOnFrame(VideoFrame *vframe) { if (!vframe) { if (!painter) { painter = new QPainter(); //warning: more than 1 painter on 1 device } if (!paint_device) { paint_device = painter->device(); } if (!paint_device && !painter->isActive()) { qWarning("No paint device and painter is not active. No painting!"); return; } if (!painter->isActive()) painter->begin(paint_device); return; } VideoFormat format = vframe->format(); if (!format.isValid()) { qWarning("Not a valid format"); return; } if (format.imageFormat() == QImage::Format_Invalid) { format.setPixelFormat(VideoFormat::Format_RGB32); if (!cvt) { cvt = new VideoFrameConverter(); } *vframe = cvt->convert(*vframe, format); } if (paint_device) { if (painter && painter->isActive()) { painter->end(); //destroy a paint device that is being painted is not allowed! } delete paint_device; paint_device = 0; } Q_ASSERT(video_width > 0 && video_height > 0); // direct draw on frame data, so use VideoFrame::constBits() paint_device = new QImage((uchar*)vframe->constBits(0), video_width, video_height, vframe->bytesPerLine(0), format.imageFormat()); if (!painter) painter = new QPainter(); own_painter = true; own_paint_device = true; //TODO: what about renderer is not a widget? painter->begin((QImage*)paint_device); } } //namespace QtAV QtAV-1.12.0/src/filter/FilterManager.cpp000066400000000000000000000131351312235004300177020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "filter/FilterManager.h" #include #include "QtAV/AVPlayer.h" #include "QtAV/Filter.h" #include "QtAV/AVOutput.h" #include "utils/Logger.h" namespace QtAV { class FilterManagerPrivate : public DPtrPrivate { public: FilterManagerPrivate() {} ~FilterManagerPrivate() { } QList pending_release_filters; QMap > filter_out_map; QMap > afilter_player_map; QMap > vfilter_player_map; }; FilterManager::FilterManager() { } FilterManager::~FilterManager() { } FilterManager& FilterManager::instance() { static FilterManager sMgr; return sMgr; } bool FilterManager::insert(Filter *filter, QList &filters, int pos) { int p = pos; if (p < 0) p += filters.size(); if (p < 0) p = 0; if (p > filters.size()) p = filters.size(); const int index = filters.indexOf(filter); // already installed at desired position if (p == index) return false; if (p >= 0) filters.removeAt(p); filters.insert(p, filter); return true; } bool FilterManager::registerFilter(Filter *filter, AVOutput *output, int pos) { DPTR_D(FilterManager); d.pending_release_filters.removeAll(filter); //erase? QList& fs = d.filter_out_map[output]; return insert(filter, fs, pos); } QList FilterManager::outputFilters(AVOutput *output) const { DPTR_D(const FilterManager); return d.filter_out_map.value(output); } bool FilterManager::registerAudioFilter(Filter *filter, AVPlayer *player, int pos) { DPTR_D(FilterManager); d.pending_release_filters.removeAll(filter); //erase? QList& fs = d.afilter_player_map[player]; return insert(filter, fs, pos); } QList FilterManager::audioFilters(AVPlayer *player) const { DPTR_D(const FilterManager); return d.afilter_player_map.value(player); } bool FilterManager::registerVideoFilter(Filter *filter, AVPlayer *player, int pos) { DPTR_D(FilterManager); d.pending_release_filters.removeAll(filter); //erase? QList& fs = d.vfilter_player_map[player]; return insert(filter, fs, pos); } QList FilterManager::videoFilters(AVPlayer *player) const { DPTR_D(const FilterManager); return d.vfilter_player_map.value(player); } // called by AVOutput/AVPlayer.uninstall imediatly bool FilterManager::unregisterAudioFilter(Filter *filter, AVPlayer *player) { DPTR_D(FilterManager); QList& fs = d.afilter_player_map[player]; if (fs.removeAll(filter) > 0) { if (fs.isEmpty()) d.afilter_player_map.remove(player); return true; } return false; } bool FilterManager::unregisterVideoFilter(Filter *filter, AVPlayer *player) { DPTR_D(FilterManager); QList& fs = d.vfilter_player_map[player]; if (fs.removeAll(filter) > 0) { if (fs.isEmpty()) d.vfilter_player_map.remove(player); return true; } return false; } bool FilterManager::unregisterFilter(Filter *filter, AVOutput *output) { DPTR_D(FilterManager); QList& fs = d.filter_out_map[output]; return fs.removeAll(filter) > 0; } bool FilterManager::uninstallFilter(Filter *filter) { DPTR_D(FilterManager); QMap >::iterator it = d.vfilter_player_map.begin(); while (it != d.vfilter_player_map.end()) { if (uninstallVideoFilter(filter, it.key())) return true; ++it; } it = d.afilter_player_map.begin(); while (it != d.afilter_player_map.end()) { if (uninstallAudioFilter(filter, it.key())) return true; ++it; } QMap >::iterator it2 = d.filter_out_map.begin(); while (it2 != d.filter_out_map.end()) { if (uninstallFilter(filter, it2.key())) return true; ++it2; } return false; } bool FilterManager::uninstallAudioFilter(Filter *filter, AVPlayer *player) { if (unregisterAudioFilter(filter, player)) return player->uninstallFilter(reinterpret_cast(filter)); return false; } bool FilterManager::uninstallVideoFilter(Filter *filter, AVPlayer *player) { if (unregisterVideoFilter(filter, player)) return player->uninstallFilter(reinterpret_cast(filter)); return false; } bool FilterManager::uninstallFilter(Filter *filter, AVOutput *output) { if (unregisterFilter(filter, output)) return output->uninstallFilter(filter); return false; } } //namespace QtAV QtAV-1.12.0/src/filter/FilterManager.h000066400000000000000000000054031312235004300173460ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_FILTERMANAGER_H #define QTAV_FILTERMANAGER_H #include #include "QtAV/Filter.h" // signal qobj parameter #include "QtAV/QtAV_Global.h" namespace QtAV { class AVOutput; class AVPlayer; class FilterManagerPrivate; class FilterManager { DPTR_DECLARE_PRIVATE(FilterManager) Q_DISABLE_COPY(FilterManager) public: static FilterManager& instance(); /*! * \brief registerFilter * record the filter in manager * target.installXXXFilter/filter.installTo(target) must call registerXXXFilter */ bool registerFilter(Filter *filter, AVOutput *output, int pos = 0x7FFFFFFF); QList outputFilters(AVOutput* output) const; bool registerAudioFilter(Filter *filter, AVPlayer *player, int pos = 0x7FFFFFFF); QList audioFilters(AVPlayer* player) const; bool registerVideoFilter(Filter *filter, AVPlayer *player, int pos = 0x7FFFFFFF); QList videoFilters(AVPlayer* player) const; bool unregisterAudioFilter(Filter *filter, AVPlayer *player); bool unregisterVideoFilter(Filter *filter, AVPlayer *player); bool unregisterFilter(Filter *filter, AVOutput *output); // unregister and call target.uninstall bool uninstallFilter(Filter *filter); //called by filter.uninstall bool uninstallAudioFilter(Filter *filter, AVPlayer* player); bool uninstallVideoFilter(Filter *filter, AVPlayer* player); bool uninstallFilter(Filter *filter, AVOutput* output); private: // return bool is for AVPlayer.installAudio/VideoFilter compatibility bool insert(Filter* filter, QList& filters, int pos); FilterManager(); ~FilterManager(); DPTR_DECLARE(FilterManager) }; } //namespace QtAV #endif // QTAV_FILTERMANAGER_H QtAV-1.12.0/src/filter/GLSLFilter.cpp000066400000000000000000000110251312235004300170650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/GLSLFilter.h" #include "QtAV/private/Filter_p.h" #include "QtAV/VideoFrame.h" #include "opengl/OpenGLHelper.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif #include "QtAV/SurfaceInterop.h" #include "QtAV/OpenGLVideo.h" namespace QtAV { class GLSLFilterPrivate : public VideoFilterPrivate { public: GLSLFilterPrivate() : VideoFilterPrivate() , fbo(0) {} QOpenGLFramebufferObject *fbo; QSize size; OpenGLVideo glv; }; GLSLFilter::GLSLFilter(QObject *parent) : VideoFilter(*new GLSLFilterPrivate(), parent) {} GLSLFilter::GLSLFilter(GLSLFilterPrivate &d, QObject *parent) : VideoFilter(d, parent) {} OpenGLVideo* GLSLFilter::opengl() const { return const_cast(&d_func().glv); } QOpenGLFramebufferObject* GLSLFilter::fbo() const { return d_func().fbo; } QSize GLSLFilter::outputSize() const { return d_func().size; } void GLSLFilter::setOutputSize(const QSize &value) { DPTR_D(GLSLFilter); if (d.size == value) return; d.size = value; Q_EMIT outputSizeChanged(value); } void GLSLFilter::setOutputSize(int width, int height) { setOutputSize(QSize(width, height)); } void GLSLFilter::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(statistics); if (!QOpenGLContext::currentContext()) { qWarning() << "No current gl context for glsl filter: " << this; return; } DPTR_D(GLSLFilter); if (!frame || !*frame) return; GLint currentFbo = 0; DYGL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFbo)); // now use the frame size if (d.fbo) { if (d.size.isEmpty()) { if (d.fbo->size() != frame->size()) { delete d.fbo; d.fbo = 0; } } else { if (d.fbo->size() != d.size) { delete d.fbo; d.fbo = 0; } } } if (!d.fbo) { d.fbo = new QOpenGLFramebufferObject(outputSize().isEmpty() ? frame->size() : outputSize(), GL_TEXTURE_2D); //TODO: prefer 16bit rgb QOpenGLContext *ctx = const_cast(QOpenGLContext::currentContext()); //qt4 returns const d.glv.setOpenGLContext(ctx); d.glv.setProjectionMatrixToRect(QRectF(0, 0, d.fbo->width(), d.fbo->height())); qDebug("new fbo texture: %d %dx%d", d.fbo->texture(), d.fbo->width(), d.fbo->height()); } d.fbo->bind(); DYGL(glViewport(0, 0, d.fbo->width(), d.fbo->height())); d.glv.setCurrentFrame(*frame); QMatrix4x4 mat; // flip vertical mat.scale(1, -1); d.glv.render(QRectF(), QRectF(), mat); gl().BindFramebuffer(GL_FRAMEBUFFER, (GLuint)currentFbo); VideoFormat fmt(VideoFormat::Format_RGB32); VideoFrame f(d.fbo->width(), d.fbo->height(), fmt); // f.setBytesPerLine(d.fbo->width()*fmt.bytesPerPixel(), 0); // set interop; class GLTextureInterop : public VideoSurfaceInterop { GLuint tex; public: GLTextureInterop(GLuint id) : tex(id) {} void* map(SurfaceType, const VideoFormat &, void *handle, int plane) { Q_UNUSED(plane); GLuint* t = reinterpret_cast(handle); *t = tex; return t; } }; GLTextureInterop *interop = new GLTextureInterop(d.fbo->texture()); f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr((interop)))); *frame = f; } } //namespace QtAV QtAV-1.12.0/src/filter/LibAVFilter.cpp000066400000000000000000000423451312235004300172720ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/LibAVFilter.h" #include #include "QtAV/private/Filter_p.h" #include "QtAV/Statistics.h" #include "QtAV/AudioFrame.h" #include "QtAV/VideoFrame.h" #include "QtAV/private/AVCompat.h" #include "utils/internal.h" #include "utils/Logger.h" /* * libav10.x, ffmpeg2.x: av_buffersink_read deprecated * libav9.x: only av_buffersink_read can be used * ffmpeg<2.0: av_buffersink_get_buffer_ref and av_buffersink_read */ // TODO: enabled = false if no libavfilter // TODO: filter_complex // NO COPY in push/pull #define QTAV_HAVE_av_buffersink_get_frame (LIBAV_MODULE_CHECK(LIBAVFILTER, 4, 2, 0) || FFMPEG_MODULE_CHECK(LIBAVFILTER, 3, 79, 100)) //3.79.101: ff2.0.4 namespace QtAV { #if QTAV_HAVE(AVFILTER) // local types can not be used as template parameters class AVFrameHolder { public: AVFrameHolder() { m_frame = av_frame_alloc(); #if !QTAV_HAVE_av_buffersink_get_frame picref = 0; #endif } ~AVFrameHolder() { av_frame_free(&m_frame); #if !QTAV_HAVE_av_buffersink_get_frame avfilter_unref_bufferp(&picref); #endif } AVFrame* frame() { return m_frame;} #if !QTAV_HAVE_av_buffersink_get_frame AVFilterBufferRef** bufferRef() { return &picref;} // copy properties and data ptrs(no deep copy). void copyBufferToFrame() { avfilter_copy_buf_props(m_frame, picref);} #endif private: AVFrame *m_frame; #if !QTAV_HAVE_av_buffersink_get_frame AVFilterBufferRef *picref; #endif }; typedef QSharedPointer AVFrameHolderRef; #endif //QTAV_HAVE(AVFILTER) class LibAVFilter::Private { public: Private() : avframe(0) , status(LibAVFilter::NotConfigured) { #if QTAV_HAVE(AVFILTER) filter_graph = 0; in_filter_ctx = 0; out_filter_ctx = 0; avfilter_register_all(); #endif //QTAV_HAVE(AVFILTER) } ~Private() { #if QTAV_HAVE(AVFILTER) avfilter_graph_free(&filter_graph); #endif //QTAV_HAVE(AVFILTER) if (avframe) { av_frame_free(&avframe); avframe = 0; } } bool setOptions(const QString& opt) { if (options == opt) return false; options = opt; status = LibAVFilter::NotConfigured; return true; } bool pushAudioFrame(Frame *frame, bool changed, const QString& args); bool pushVideoFrame(Frame *frame, bool changed, const QString& args); bool setup(const QString& args, bool video) { if (avframe) { av_frame_free(&avframe); avframe = 0; } status = LibAVFilter::ConfigureFailed; #if QTAV_HAVE(AVFILTER) avfilter_graph_free(&filter_graph); filter_graph = avfilter_graph_alloc(); //QString sws_flags_str; // pixel_aspect==sar, pixel_aspect is more compatible QString buffersrc_args = args; qDebug("buffersrc_args=%s", buffersrc_args.toUtf8().constData()); AVFilter *buffersrc = avfilter_get_by_name(video ? "buffer" : "abuffer"); Q_ASSERT(buffersrc); AV_ENSURE_OK(avfilter_graph_create_filter(&in_filter_ctx, buffersrc, "in", buffersrc_args.toUtf8().constData(), NULL, filter_graph) , false); /* buffer video sink: to terminate the filter chain. */ AVFilter *buffersink = avfilter_get_by_name(video ? "buffersink" : "abuffersink"); Q_ASSERT(buffersink); AV_ENSURE_OK(avfilter_graph_create_filter(&out_filter_ctx, buffersink, "out", NULL, NULL, filter_graph) , false); /* Endpoints for the filter graph. */ AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); outputs->name = av_strdup("in"); outputs->filter_ctx = in_filter_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = out_filter_ctx; inputs->pad_idx = 0; inputs->next = NULL; struct delete_helper { AVFilterInOut **x; delete_helper(AVFilterInOut **io) : x(io) {} ~delete_helper() { // libav always free it in avfilter_graph_parse. so we does nothing #if QTAV_USE_FFMPEG(LIBAVFILTER) avfilter_inout_free(x); #endif } } scoped_in(&inputs), scoped_out(&outputs); //avfilter_graph_parse, avfilter_graph_parse2? AV_ENSURE_OK(avfilter_graph_parse_ptr(filter_graph, options.toUtf8().constData(), &inputs, &outputs, NULL), false); AV_ENSURE_OK(avfilter_graph_config(filter_graph, NULL), false); avframe = av_frame_alloc(); status = LibAVFilter::ConfigureOk; #if DBG_GRAPH //not available in libav9 const char* g = avfilter_graph_dump(filter_graph, NULL); if (g) qDebug().nospace() << "filter graph:\n" << g; // use << to not print special chars in qt5.5 av_freep(&g); #endif //DBG_GRAPH return true; #endif //QTAV_HAVE(AVFILTER) return false; } #if QTAV_HAVE(AVFILTER) AVFilterGraph *filter_graph; AVFilterContext *in_filter_ctx; AVFilterContext *out_filter_ctx; #endif //QTAV_HAVE(AVFILTER) AVFrame *avframe; QString options; LibAVFilter::Status status; }; QStringList LibAVFilter::videoFilters() { static const QStringList list(LibAVFilter::registeredFilters(AVMEDIA_TYPE_VIDEO)); return list; } QStringList LibAVFilter::audioFilters() { static const QStringList list(LibAVFilter::registeredFilters(AVMEDIA_TYPE_AUDIO)); return list; } QString LibAVFilter::filterDescription(const QString &filterName) { QString s; #if QTAV_HAVE(AVFILTER) avfilter_register_all(); const AVFilter *f = avfilter_get_by_name(filterName.toUtf8().constData()); if (!f) return s; if (f->description) s.append(QString::fromUtf8(f->description)); #if AV_MODULE_CHECK(LIBAVFILTER, 3, 7, 0, 8, 100) return s.append(QLatin1String("\n")).append(QObject::tr("Options:")) .append(Internal::optionsToString((void*)&f->priv_class)); #endif #endif //QTAV_HAVE(AVFILTER) Q_UNUSED(filterName); return s; } LibAVFilter::LibAVFilter() : priv(new Private()) { } LibAVFilter::~LibAVFilter() { delete priv; } void LibAVFilter::setOptions(const QString &options) { if (!priv->setOptions(options)) return; Q_EMIT optionsChanged(); } QString LibAVFilter::options() const { return priv->options; } LibAVFilter::Status LibAVFilter::status() const { return priv->status; } bool LibAVFilter::pushVideoFrame(Frame *frame, bool changed) { return priv->pushVideoFrame(frame, changed, sourceArguments()); } bool LibAVFilter::pushAudioFrame(Frame *frame, bool changed) { return priv->pushAudioFrame(frame, changed, sourceArguments()); } void* LibAVFilter::pullFrameHolder() { #if QTAV_HAVE(AVFILTER) AVFrameHolder *holder = NULL; holder = new AVFrameHolder(); #if QTAV_HAVE_av_buffersink_get_frame int ret = av_buffersink_get_frame(priv->out_filter_ctx, holder->frame()); #else int ret = av_buffersink_read(priv->out_filter_ctx, holder->bufferRef()); #endif //QTAV_HAVE_av_buffersink_get_frame if (ret < 0) { qWarning("av_buffersink_get_frame error: %s", av_err2str(ret)); delete holder; return 0; } #if !QTAV_HAVE_av_buffersink_get_frame holder->copyBufferToFrame(); #endif return holder; #endif //QTAV_HAVE(AVFILTER) return 0; } QStringList LibAVFilter::registeredFilters(int type) { QStringList filters; #if QTAV_HAVE(AVFILTER) avfilter_register_all(); const AVFilter* f = NULL; AVFilterPad* fp = NULL; // no const in avfilter_pad_get_name() for ffmpeg<=1.2 libav<=9 #if AV_MODULE_CHECK(LIBAVFILTER, 3, 8, 0, 53, 100) while ((f = avfilter_next(f))) { #else AVFilter** ff = NULL; while ((ff = av_filter_next(ff)) && *ff) { f = (*ff); #endif fp = (AVFilterPad*)f->inputs; // only check the 1st pad if (!fp || !avfilter_pad_get_name(fp, 0) || avfilter_pad_get_type(fp, 0) != (AVMediaType)type) continue; fp = (AVFilterPad*)f->outputs; // only check the 1st pad if (!fp || !avfilter_pad_get_name(fp, 0) || avfilter_pad_get_type(fp, 0) != (AVMediaType)type) continue; filters.append(QLatin1String(f->name)); } #endif //QTAV_HAVE(AVFILTER) return filters; } class LibAVFilterVideoPrivate : public VideoFilterPrivate { public: LibAVFilterVideoPrivate() : VideoFilterPrivate() , pixfmt(QTAV_PIX_FMT_C(NONE)) , width(0) , height(0) {} AVPixelFormat pixfmt; int width, height; }; LibAVFilterVideo::LibAVFilterVideo(QObject *parent) : VideoFilter(*new LibAVFilterVideoPrivate(), parent) , LibAVFilter() { } QStringList LibAVFilterVideo::filters() const { return LibAVFilter::videoFilters(); } void LibAVFilterVideo::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(statistics); #if QTAV_HAVE(AVFILTER) if (status() == ConfigureFailed) return; DPTR_D(LibAVFilterVideo); //Status old = status(); bool changed = false; if (d.width != frame->width() || d.height != frame->height() || d.pixfmt != frame->pixelFormatFFmpeg()) { changed = true; d.width = frame->width(); d.height = frame->height(); d.pixfmt = (AVPixelFormat)frame->pixelFormatFFmpeg(); } bool ok = pushVideoFrame(frame, changed); //if (old != status()) // emit statusChanged(); if (!ok) return; AVFrameHolderRef ref((AVFrameHolder*)pullFrameHolder()); if (!ref) return; const AVFrame *f = ref->frame(); VideoFrame vf(f->width, f->height, VideoFormat(f->format)); vf.setBits((quint8**)f->data); vf.setBytesPerLine((int*)f->linesize); vf.setMetaData(QStringLiteral("avframe_hoder_ref"), QVariant::fromValue(ref)); vf.setTimestamp(ref->frame()->pts/1000000.0); //pkt_pts? //vf.setMetaData(frame->availableMetaData()); *frame = vf; #else Q_UNUSED(frame); #endif //QTAV_HAVE(AVFILTER) } QString LibAVFilterVideo::sourceArguments() const { DPTR_D(const LibAVFilterVideo); #if QTAV_USE_LIBAV(LIBAVFILTER) return QStringLiteral("%1:%2:%3:%4:%5:%6:%7") #else return QStringLiteral("video_size=%1x%2:pix_fmt=%3:time_base=%4/%5:pixel_aspect=%6/%7") #endif .arg(d.width).arg(d.height).arg(d.pixfmt) .arg(1).arg(AV_TIME_BASE) //time base 1/1? .arg(1).arg(1) //sar ; } class LibAVFilterAudioPrivate : public AudioFilterPrivate { public: LibAVFilterAudioPrivate() : AudioFilterPrivate() , sample_rate(0) , sample_fmt(AV_SAMPLE_FMT_NONE) , channel_layout(0) {} int sample_rate; AVSampleFormat sample_fmt; qint64 channel_layout; }; LibAVFilterAudio::LibAVFilterAudio(QObject *parent) : AudioFilter(*new LibAVFilterAudioPrivate(), parent) , LibAVFilter() {} QStringList LibAVFilterAudio::filters() const { return LibAVFilter::audioFilters(); } QString LibAVFilterAudio::sourceArguments() const { DPTR_D(const LibAVFilterAudio); return QStringLiteral("time_base=%1/%2:sample_rate=%3:sample_fmt=%4:channel_layout=0x%5") .arg(1) .arg(AV_TIME_BASE) .arg(d.sample_rate) //ffmpeg new: AV_OPT_TYPE_SAMPLE_FMT //libav, ffmpeg old: AV_OPT_TYPE_STRING .arg(QLatin1String(av_get_sample_fmt_name(d.sample_fmt))) .arg(d.channel_layout, 0, 16) //AV_OPT_TYPE_STRING ; } void LibAVFilterAudio::process(Statistics *statistics, AudioFrame *frame) { Q_UNUSED(statistics); #if QTAV_HAVE(AVFILTER) if (status() == ConfigureFailed) return; DPTR_D(LibAVFilterAudio); //Status old = status(); bool changed = false; const AudioFormat afmt(frame->format()); if (d.sample_rate != afmt.sampleRate() || d.sample_fmt != afmt.sampleFormatFFmpeg() || d.channel_layout != afmt.channelLayoutFFmpeg()) { changed = true; d.sample_rate = afmt.sampleRate(); d.sample_fmt = (AVSampleFormat)afmt.sampleFormatFFmpeg(); d.channel_layout = afmt.channelLayoutFFmpeg(); } bool ok = pushAudioFrame(frame, changed); //if (old != status()) // emit statusChanged(); if (!ok) return; AVFrameHolderRef ref((AVFrameHolder*)pullFrameHolder()); if (!ref) return; const AVFrame *f = ref->frame(); AudioFormat fmt; fmt.setSampleFormatFFmpeg(f->format); fmt.setChannelLayoutFFmpeg(f->channel_layout); fmt.setSampleRate(f->sample_rate); if (!fmt.isValid()) {// need more data to decode to get a frame return; } AudioFrame af(fmt); //af.setBits((quint8**)f->extended_data); //af.setBytesPerLine((int*)f->linesize); af.setBits(f->extended_data); // TODO: ref af.setBytesPerLine(f->linesize[0], 0); // for correct alignment af.setSamplesPerChannel(f->nb_samples); af.setMetaData(QStringLiteral("avframe_hoder_ref"), QVariant::fromValue(ref)); af.setTimestamp(ref->frame()->pts/1000000.0); //pkt_pts? //af.setMetaData(frame->availableMetaData()); *frame = af; #else Q_UNUSED(frame); #endif //QTAV_HAVE(AVFILTER) } bool LibAVFilter::Private::pushVideoFrame(Frame *frame, bool changed, const QString &args) { #if QTAV_HAVE(AVFILTER) VideoFrame *vf = static_cast(frame); if (status == LibAVFilter::NotConfigured || !avframe || changed) { if (!setup(args, true)) { qWarning("setup video filter graph error"); //enabled = false; // skip this filter and avoid crash return false; } } if (!vf->constBits(0)) { *vf = vf->to(vf->format()); } avframe->pts = frame->timestamp() * 1000000.0; // time_base is 1/1000000 avframe->width = vf->width(); avframe->height = vf->height(); avframe->format = (AVPixelFormat)vf->pixelFormatFFmpeg(); for (int i = 0; i < vf->planeCount(); ++i) { avframe->data[i] = (uint8_t*)vf->constBits(i); avframe->linesize[i] = vf->bytesPerLine(i); } //TODO: side data for vf_codecview etc //int ret = av_buffersrc_add_frame_flags(in_filter_ctx, avframe, AV_BUFFERSRC_FLAG_KEEP_REF); /* * av_buffersrc_write_frame equals to av_buffersrc_add_frame_flags with AV_BUFFERSRC_FLAG_KEEP_REF. * av_buffersrc_write_frame is more compatible, while av_buffersrc_add_frame_flags only exists in ffmpeg >=2.0 * add a ref if frame is ref counted * TODO: libav < 10.0 will copy the frame, prefer to use av_buffersrc_buffer */ AV_ENSURE_OK(av_buffersrc_write_frame(in_filter_ctx, avframe), false); return true; #endif //QTAV_HAVE(AVFILTER) Q_UNUSED(frame); return false; } bool LibAVFilter::Private::pushAudioFrame(Frame *frame, bool changed, const QString &args) { #if QTAV_HAVE(AVFILTER) if (status == LibAVFilter::NotConfigured || !avframe || changed) { if (!setup(args, false)) { qWarning("setup audio filter graph error"); //enabled = false; // skip this filter and avoid crash return false; } } AudioFrame *af = static_cast(frame); const AudioFormat afmt(af->format()); avframe->pts = frame->timestamp() * 1000000.0; // time_base is 1/1000000 avframe->sample_rate = afmt.sampleRate(); avframe->channel_layout = afmt.channelLayoutFFmpeg(); #if QTAV_USE_FFMPEG(LIBAVCODEC) || QTAV_USE_FFMPEG(LIBAVUTIL) //AVFrame was in avcodec avframe->channels = afmt.channels(); //MUST set because av_buffersrc_write_frame will compare channels and layout #endif avframe->format = (AVSampleFormat)afmt.sampleFormatFFmpeg(); avframe->nb_samples = af->samplesPerChannel(); for (int i = 0; i < af->planeCount(); ++i) { //avframe->data[i] = (uint8_t*)af->constBits(i); avframe->extended_data[i] = (uint8_t*)af->constBits(i); avframe->linesize[i] = af->bytesPerLine(i); } AV_ENSURE_OK(av_buffersrc_write_frame(in_filter_ctx, avframe), false); return true; #endif //QTAV_HAVE(AVFILTER) Q_UNUSED(frame); return false; } } //namespace QtAV #if QTAV_HAVE(AVFILTER) Q_DECLARE_METATYPE(QtAV::AVFrameHolderRef) #endif QtAV-1.12.0/src/filter/SubtitleFilter.cpp000066400000000000000000000137531312235004300201310ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/SubtitleFilter.h" #include "QtAV/private/Filter_p.h" #include "QtAV/private/PlayerSubtitle.h" #include "QtAV/Subtitle.h" #include "QtAV/VideoFrame.h" #include #include "utils/Logger.h" namespace QtAV { class SubtitleFilterPrivate : public VideoFilterPrivate { public: SubtitleFilterPrivate() : player_sub(new PlayerSubtitle(0)) , rect(0.0, 0.0, 1.0, 0.9) , color(Qt::white) { font.setPointSize(22); } QRect realRect(int width, int height) { if (!rect.isValid()) { return QRect(0, 0, width, height); } QRect r = rect.toRect(); // nomalized x, y < 1 bool normalized = false; if (qAbs(rect.x()) < 1) { normalized = true; r.setX(rect.x()*qreal(width)); //TODO: why not video_frame.size()? roi not correct } if (qAbs(rect.y()) < 1) { normalized = true; r.setY(rect.y()*qreal(height)); } // whole size use width or height = 0, i.e. null size // nomalized width, height <= 1. If 1 is normalized value iff |x|<1 || |y| < 1 if (qAbs(rect.width()) < 1) r.setWidth(rect.width()*qreal(width)); if (qAbs(rect.height()) < 1) r.setHeight(rect.height()*qreal(height)); if (rect.width() == 1.0 && normalized) { r.setWidth(width); } if (rect.height() == 1.0 && normalized) { r.setHeight(height); } return r; } QScopedPointer player_sub; QRectF rect; QFont font; QColor color; }; SubtitleFilter::SubtitleFilter(QObject *parent) : VideoFilter(*new SubtitleFilterPrivate(), parent) , SubtitleAPIProxy(this) { DPTR_D(SubtitleFilter); setSubtitle(d.player_sub->subtitle()); connect(this, SIGNAL(enabledChanged(bool)), d.player_sub.data(), SLOT(onEnabledChanged(bool))); connect(d.player_sub.data(), SIGNAL(autoLoadChanged(bool)), this, SIGNAL(autoLoadChanged(bool))); connect(d.player_sub.data(), SIGNAL(fileChanged()), this, SIGNAL(fileChanged())); if (parent && !qstrcmp(parent->metaObject()->className(), "AVPlayer")) { AVPlayer* p = reinterpret_cast(parent); setPlayer(p); } } void SubtitleFilter::setPlayer(AVPlayer *player) { d_func().player_sub->setPlayer(player); } void SubtitleFilter::setFile(const QString &file) { d_func().player_sub->setFile(file); } QString SubtitleFilter::file() const { return d_func().player_sub->file(); } void SubtitleFilter::setAutoLoad(bool value) { d_func().player_sub->setAutoLoad(value); } bool SubtitleFilter::autoLoad() const { return d_func().player_sub->autoLoad(); } void SubtitleFilter::setRect(const QRectF &r) { DPTR_D(SubtitleFilter); if (d.rect == r) return; d.rect = r; emit rectChanged(); } QRectF SubtitleFilter::rect() const { return d_func().rect; } void SubtitleFilter::setFont(const QFont &f) { DPTR_D(SubtitleFilter); if (d.font == f) return; d.font = f; emit fontChanged(); } QFont SubtitleFilter::font() const { return d_func().font; } void SubtitleFilter::setColor(const QColor &c) { DPTR_D(SubtitleFilter); if (d.color == c) return; d.color = c; emit colorChanged(); } QColor SubtitleFilter::color() const { return d_func().color; } QString SubtitleFilter::subtitleText(qreal t) const { DPTR_D(const SubtitleFilter); d.player_sub->subtitle()->setTimestamp(t); return d.player_sub->subtitle()->getText(); } void SubtitleFilter::process(Statistics *statistics, VideoFrame *frame) { Q_UNUSED(statistics); Q_UNUSED(frame); DPTR_D(SubtitleFilter); if (!context()->paint_device) { qWarning("no paint device!"); return; } if (frame && frame->timestamp() > 0.0) d.player_sub->subtitle()->setTimestamp(frame->timestamp()); if (d.player_sub->subtitle()->canRender()) { QRect rect; /* * image quality maybe to low if use video frame resolution for large display. * The difference is small if use paint_device size and video frame aspect ratio ~ renderer aspect ratio * if use renderer's resolution, we have to map bounding rect from video frame coordinate to renderer's */ //QImage img = d.player_sub->subtitle()->getImage(statistics->video_only.width, statistics->video_only.height, &rect); QImage img = d.player_sub->subtitle()->getImage(context()->paint_device->width(), context()->paint_device->height(), &rect); if (img.isNull()) return; context()->drawImage(rect, img); return; } context()->font = d.font; context()->pen.setColor(d.color); context()->rect = d.realRect(context()->paint_device->width(), context()->paint_device->height()); context()->drawPlainText(context()->rect, Qt::AlignHCenter | Qt::AlignBottom, d.player_sub->subtitle()->getText()); } } //namespace QtAV QtAV-1.12.0/src/filter/X11FilterContext.cpp000066400000000000000000000204171312235004300202470ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/FilterContext.h" #include #include #include #include #include #include #include "QtAV/VideoFrame.h" #include "utils/Logger.h" #include #include namespace QtAV { X11FilterContext::X11FilterContext() : VideoFilterContext() , doc(0) , cvt(0) , display(0) , gc(0) , drawable(0) , text_img(0) , mask_img(0) , mask_pix(0) , plain(0) { painter = new QPainter(); } X11FilterContext::~X11FilterContext() { if (doc) { delete doc; doc = 0; } if (cvt) { delete cvt; cvt = 0; } resetX11(); } void X11FilterContext::destroyX11Resources() { if (mask_pix) { XFreePixmap((::Display*)display, (::Pixmap)mask_pix); mask_pix = 0; } if (mask_img) { ((::XImage*)mask_img)->data = 0; //pointed to qimage data XDestroyImage((::XImage*)mask_img); mask_img = 0; } if (text_img) { ((::XImage*)text_img)->data = 0; //pointed to qimage data XDestroyImage((::XImage*)text_img); text_img = 0; } } void X11FilterContext::resetX11(Display *dpy, GC g, Drawable d) { if (dpy != display || g != gc || d != drawable) { destroyX11Resources(); display = dpy; gc = g; drawable = d; } qDebug("resetX11 display:%p,gc:%p,drawable:%p", display, g, d); } void X11FilterContext::renderTextImageX11(QImage *img, const QPointF &pos) { if (img) { destroyX11Resources(); mask_q = img->createAlphaMask(); if (mask_q.isNull()) { qWarning("mask image is null"); return; } XWindowAttributes xwa; XGetWindowAttributes((::Display*)display, (::Window)drawable, &xwa); // force the stride to ensure we can safely set ximage data ptr to qimage data ptr mask_img = (XImage*)XCreateImage((::Display*)display, xwa.visual, 1, ZPixmap, 0, NULL, mask_q.width(), mask_q.height(), 8, mask_q.bytesPerLine()); if (!mask_img) { qWarning("error create mask image"); return; } ((::XImage*)mask_img)->data = (char*)mask_q.constBits(); // force the stride to ensure we can safely set ximage data ptr to qimage data ptr text_img = (XImage*)XCreateImage((::Display*)display, xwa.visual, xwa.depth, ZPixmap, 0, NULL, img->width(), img->height(), 8, img->bytesPerLine()); ((::XImage*)text_img)->data = (char*)img->constBits(); mask_pix = XCreatePixmap((::Display*)display, drawable, ((::XImage*)mask_img)->width, ((::XImage*)mask_img)->height, ((::XImage*)mask_img)->depth); ::GC mask_gc = XCreateGC((::Display*)display, (::Pixmap)mask_pix, 0, NULL); XPutImage((::Display*)display, mask_pix, mask_gc, (::XImage*)mask_img, 0,0,0,0, ((::XImage*)mask_img)->width, ((::XImage*)mask_img)->height); } XSetClipMask((::Display*)display, (::GC)gc, (::Pixmap)mask_pix); XSetClipOrigin((::Display*)display, (::GC)gc, pos.x(), pos.y()); XPutImage((::Display*)display, drawable, (::GC)gc, (::XImage*)text_img, 0, 0, pos.x(), pos.y(), ((::XImage*)text_img)->width, ((::XImage*)text_img)->height); XSetClipMask((::Display*)display, (::GC)gc, None); XSync((::Display*)display, False); } bool X11FilterContext::isReady() const { return !!painter; } bool X11FilterContext::prepare() { if (!isReady()) return false; //painter->save(); //is it necessary? painter->setBrush(brush); painter->setPen(pen); painter->setFont(font); painter->setOpacity(opacity); #if 1 if (!clip_path.isEmpty()) { painter->setClipPath(clip_path); } //transform at last! because clip_path is relative to paint device coord painter->setTransform(transform); #endif return true; } void X11FilterContext::initializeOnFrame(VideoFrame *vframe) { Q_UNUSED(vframe); } void X11FilterContext::shareFrom(VideoFilterContext *vctx) { VideoFilterContext::shareFrom(vctx); X11FilterContext* c = static_cast(vctx); display = c->display; gc = c->gc; drawable = c->drawable; // can not set other filter's context text } void X11FilterContext::drawImage(const QPointF &pos, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags) { // qpainter transform etc } void X11FilterContext::drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags) { } void X11FilterContext::drawPlainText(const QPointF &pos, const QString &text) { if (text == this->text && plain && mask_pix) { renderTextImageX11(0, pos); return; } this->text = text; this->plain = true; QFontMetrics fm(font); text_q = QImage(fm.width(text), fm.height(), QImage::Format_ARGB32); text_q.fill(0); painter->begin(&text_q); painter->translate(0, 0); prepare(); painter->drawText(QPoint(0, fm.ascent()), text); painter->end(); renderTextImageX11(&text_q, pos); } void X11FilterContext::drawPlainText(const QRectF &rect, int flags, const QString &text) { if (text.isEmpty()) return; if (rect.isEmpty()) { drawPlainText(rect.topLeft(), text); return; } if (test_img.size() != rect.size().toSize()) { test_img = QImage(rect.size().toSize(), QImage::Format_ARGB32); //TODO: create once } painter->begin(&test_img); prepare(); QRectF br = painter->boundingRect(rect, flags, text); painter->end(); if (br.isEmpty()) return; if (text == this->text && plain && mask_pix) { renderTextImageX11(0, br.topLeft()); return; } this->text = text; this->plain = true; text_q = QImage(br.size().toSize(), QImage::Format_ARGB32); text_q.fill(0); painter->begin(&text_q); prepare(); painter->drawText(0, 0, br.width(), br.height(), Qt::AlignCenter, text); painter->end(); renderTextImageX11(&text_q, br.topLeft()); } void X11FilterContext::drawRichText(const QRectF &rect, const QString &text, bool wordWrap) { Q_UNUSED(rect); Q_UNUSED(text); if (text == this->text && plain && mask_pix) { renderTextImageX11(0, rect.topLeft()); return; } this->text = text; this->plain = false; if (!doc) doc = new QTextDocument(); doc->setHtml(text); //painter->translate(rect.topLeft()); //drawContent() can not set target rect, so move here if (wordWrap) doc->setTextWidth(rect.width()); QMatrix4x4 m(transform); const QRectF r = m.mapRect(QRectF(rect.x(), rect.y(), doc->size().width(), doc->size().height())); text_q = QImage(r.size().toSize(), QImage::Format_ARGB32); text_q.fill(0); painter->begin(&text_q); prepare(); const QPointF tl = m.map(rect.topLeft()); m.setColumn(3, QVector4D(0, 0, 0, 1)); // reset O to let painter draw from 0 const QPointF dp = tl - r.topLeft(); //painter should start from the mapped top left relative to mapped rect's top left //qDebug() << dp << r.; painter->setTransform(m.toTransform()); painter->translate(dp); doc->drawContents(painter); painter->end(); renderTextImageX11(&text_q, r.topLeft()); //TODO: use boundingRect? } } //namespace QtAV QtAV-1.12.0/src/i18n/000077500000000000000000000000001312235004300137455ustar00rootroot00000000000000QtAV-1.12.0/src/i18n/QtAV.ts000066400000000000000000000455131312235004300151400ustar00rootroot00000000000000 AudioOutputPulse PulseAudio %1, protocol: %2, server protocol: %3 QObject No error Open error Open timed out Parse stream timed out Parse stream error Stream not found Read packet timed out Read error Seek error Resource error Open codec error Close codec error Video codec not found Audio codec not found Subtitle codec not found Codec error Format error Network error Access denied Unknow error Build version Runtime version Multimedia framework base on Qt and FFmpeg. Distributed under the terms of LGPLv2.1 or later. Shanghai University->S3 Graphics->Deepin, Shanghai, China Donate Source Home page ZeroCopy DirectCopy GenericCopy copyMode Codec %1 is not supported by CUDA codecName skip_loop_filter skip_idct strict skip_frame threads thread_type vismv bug OptimizedCopy VA API version %1.%2; Vendor: %3; interop format Options: QtAV::AVDecoder No codec could be found for '%1' QtAV::AVDemuxer error reading stream data seek error failed to open media failed to find stream info interrupted by user timeout QtAV::VideoDecoderCUDA Decoding surfaces Decoder flags Performace: ZeroCopy > DirectCopy > GenericCopy ZeroCopy: no copy back from GPU to System memory. Directly render the decoded data on GPU DirectCopy: copy back to host memory but video frames and map to GL texture GenericCopy: copy back to host memory and each video frame QtAV::VideoDecoderD3D Decoding surfaces 0: auto QtAV::VideoDecoderFFmpeg Skipping the loop filter (aka deblocking) usually has determinal effect on quality. However it provides a big speedup for hi definition streams Force skipping of idct to speed up decoding for frame types (-1=None, 0=Default, 1=B-frames, 2=P-frames, 3=B+P frames, 4=all frames) Force skipping frames for speed up decoding. Number of decoding threads. Set before open. Maybe no effect for some decoders 0: auto 1: single thread decoding QtAV::VideoDecoderFFmpegHW ZeroCopy: fastest. Direct rendering without data copy between CPU and GPU Not implemented for all codecs OptimizedCopy: copy from USWC memory optimized by SSE4.1 GenericCopy: slowest. Generic cpu copy Number of decoding threads. Set before open. Maybe no effect for some decoders Multithread decoding may crash 0: auto 1: single thread decoding QtAV::VideoDecoderVAAPI Decoding surfaces 0: auto Maybe faster QtAV::VideoDecoderVDA Output pixel format from decoder. Performance NV12 > UYVY > YUV420P > YUYV. OSX < 10.7 only supports UYVY and YUV420p QtAV::VideoDecoderVideoToolbox Output pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV. OSX < 10.7 only supports UYVY, BGRA and YUV420p Interop with OpenGL CVPixelBuffer: macOS+iOS CVOpenGLES: iOS, no copy, fast IOSurface: macOS, no copy, fast Auto: choose the fastest QtAV-1.12.0/src/i18n/QtAV_zh_CN.qm000066400000000000000000000170441312235004300162060ustar00rootroot00000000000000 <ƌƌqƌƌEROSH71 n3C&v)dX.B Pȕ BT3 :BT3.BT3Z&5i~CdâF֓N, t` w=X R0rb y Q aS g g 㿔 4P & lQ c# cU A w .e; p ƴ  T 6 Vr < Vd y  B "  i k - b_Ф}c iR0PulseAudio %1, protocol: %2, server protocol: %3AudioOutputPulseb~݋ Access deniedQObject e󘑉xVhAudio codec not foundQObject erHg, Build versionQObjectQsxVhClose codec errorQObjectCUDAN e/c %1 x!Codec %1 is not supported by CUDAQObject xVh Codec errorQObjectvcb DirectCopyQObject2Distributed under the terms of LGPLv2.1 or later. QObjectcPRDonateQObjecth<_ Format errorQObjectfnb GenericCopyQObjectN;u Home pageQObject$WNQt0FFmpegvYZOShFg.,Multimedia framework base on Qt and FFmpeg. QObjectQ~ܕ Network errorQObjecteNo errorQObjectbS_xVhOpen codec errorQObjectbS_ Open errorQObjectbS_eOpen timed outQObjectOSb OptimizedCopyQObject y:Options:QObject RgmAParse stream errorQObject RgmAeParse stream timed outQObjectS֕ Read errorQObject SepcneRead packet timed outQObjectDnResource errorQObject ЈLerHg,Runtime versionQObjectl Seek errorQObject>N mwY'[f->S3 Graphics->Deepin, N-VN mw9Shanghai University->S3 Graphics->Deepin, Shanghai, ChinaQObjectnxSourceQObjecteZOSmAStream not foundQObject e[W^UxVhSubtitle codec not foundQObjectg*w Unknow errorQObject!VA API version %1.%2; Vendor: %3;QObject eƘxVhVideo codec not foundQObjectbZeroCopyQObjectbugbugQObjectx codecNameQObjectbj!_copyModeQObjecth<_formatQObjectNdO\interopQObject^' skip_frameQObject idct skip_idctQObject _sn\skip_loop_filterQObjectstrictQObject Y~z |{W thread_typeQObject~z epthreadsQObjectSSRTvismvQObjectelb~R0xVh %1 No codec could be found for '%1'QtAV::AVDecoder SmAerror reading stream dataQtAV::AVDemuxerelb~R0mAO`ofailed to find stream infoQtAV::AVDemuxerbS_YZOSY1%failed to open mediaQtAV::AVDemuxeru(b7N-einterrupted by userQtAV::AVDemuxer seek seek errorQtAV::AVDemuxeretimeoutQtAV::AVDemuxer xVhh Decoder flagsQtAV::VideoDecoderCUDAxhbDecoding surfacesQtAV::VideoDecoderCUDA2vcb: bR0N;[XTzSsN O N:OpenGL~tKDirectCopy: copy back to host memory but video frames and map to GL textureQtAV::VideoDecoderCUDAfnb: k^'b|~Q[X:GenericCopy: copy back to host memory and each video frameQtAV::VideoDecoderCUDA*`': b > vcb > fnb/Performace: ZeroCopy > DirectCopy > GenericCopyQtAV::VideoDecoderCUDABb: eGPUR0|~Q[Xvb. vcn2gGPUN ]xvepcnYZeroCopy: no copy back from GPU to System memory. Directly render the decoded data on GPUQtAV::VideoDecoderCUDA 0: R0: autoQtAV::VideoDecoderD3DxhbDecoding surfacesQtAV::VideoDecoderD3D 0: R0: autoQtAV::VideoDecoderFFmpeg1: SU~z x1: single thread decodingQtAV::VideoDecoderFFmpeg_:R6N"^'NRx.,Force skipping frames for speed up decoding.QtAV::VideoDecoderFFmpegForce skipping of idct to speed up decoding for frame types (-1=None, 0=Default, 1=B-frames, 2=P-frames, 3=B+P frames, 4=all frames)QtAV::VideoDecoderFFmpeg0x~z ep. bS_RMn. S[gNxVheeHNNumber of decoding threads. Set before open. Maybe no effect for some decodersQtAV::VideoDecoderFFmpegSkipping the loop filter (aka deblocking) usually has determinal effect on quality. However it provides a big speedup for hi definition streamsQtAV::VideoDecoderFFmpeg 0: R0: autoQtAV::VideoDecoderFFmpegHW1: SU~z x1: single thread decodingQtAV::VideoDecoderFFmpegHWfnb: gab, CPUb&GenericCopy: slowest. Generic cpu copyQtAV::VideoDecoderFFmpegHWY~z xSO])nMultithread decoding may crashQtAV::VideoDecoderFFmpegHWg*[b@g x[sNot implemented for all codecsQtAV::VideoDecoderFFmpegHW0x~z ep. bS_RMn. S[gNxVheeHNNumber of decoding threads. Set before open. Maybe no effect for some decodersQtAV::VideoDecoderFFmpegHW,OSb: Ou(SSE4.1ۈLUSWCb8OptimizedCopy: copy from USWC memory optimized by SSE4.1QtAV::VideoDecoderFFmpegHW8b: g_. vcn2geCPUTGPUNKvepcnbIZeroCopy: fastest. Direct rendering without data copy between CPU and GPUQtAV::VideoDecoderFFmpegHW 0: R0: autoQtAV::VideoDecoderVAAPIxhbDecoding surfacesQtAV::VideoDecoderVAAPISf_ Maybe fasterQtAV::VideoDecoderVAAPIxxVhQh<_0`'NV12>UYVY>YUV420P>YUYV OSX < 10.7 Se/c UYVYTYUV420PuOutput pixel format from decoder. Performance NV12 > UYVY > YUV420P > YUYV. OSX < 10.7 only supports UYVY and YUV420pQtAV::VideoDecoderVDAAuto: bg_vAuto: choose the fastestQtAV::VideoDecoderVideoToolbox.CVOpenGLES: iOS, eb, _CVOpenGLES: iOS, no copy, fastQtAV::VideoDecoderVideoToolboxCVPixelBuffer: macOS+iOSQtAV::VideoDecoderVideoToolboxIOSurface: macOS, no copy, fastQtAV::VideoDecoderVideoToolboxNOpenGLNNInterop with OpenGLQtAV::VideoDecoderVideoToolboxxVhQvP} h<_0`'NV12 > UYVY > BGRA > YUV420P > YUYV. OSX < 10.7Se/cUYVY, BGRA and YUV420pOutput pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV. OSX < 10.7 only supports UYVY, BGRA and YUV420pQtAV::VideoDecoderVideoToolboxQtAV-1.12.0/src/i18n/QtAV_zh_CN.ts000066400000000000000000000322741312235004300162210ustar00rootroot00000000000000 AudioOutputPulse PulseAudio %1, protocol: %2, server protocol: %3 QObject Build version 编译时版本 Runtime version 运行时版本 Donate 捐助 Source 源码 Distributed under the terms of LGPLv2.1 or later. Home page 主页 skip_frame 跳帧 skip_loop_filter 跳过循环滤镜 skip_idct 跳过 idct strict threads 线程数 thread_type 多线程类型 vismv 可视化运动向量 bug bug No error 无错误 Shanghai University->S3 Graphics->Deepin, Shanghai, China 上海大学->S3 Graphics->Deepin, 中国上海 Codec %1 is not supported by CUDA CUDA不支持 %1 编码 Options: 选项: Open error 打开错误 Open timed out 打开超时 Could not find stream info 无法获得视频信息 Stream not found 无媒体流 Read packet timed out 读取数据超时 Read error 读取错误 Seek error 跳转错误 Resource error 资源错误 Open codec error 打开解码器错误 Close codec error 关闭解码器错误 Video codec not found 无视频解码器 Audio codec not found 无音频解码器 Subtitle codec not found 无字幕解码器 Codec error 编解码器错误 Format error 格式错误 Network error 网络错误 Access denied 拒绝访问 Unknow error 未知错误 VA API version %1.%2; Vendor: %3; ZeroCopy 零拷贝 DirectCopy 直接拷贝 GenericCopy 普通拷贝 OptimizedCopy 优化拷贝 codecName 编码 copyMode 拷贝模式 Multimedia framework base on Qt and FFmpeg. 基于Qt、FFmpeg的多媒体框架. interop 互操作 format 格式 Parse stream timed out 分析流超时 Parse stream error 分析流错误 QtAV::AVDecoder No codec could be found for '%1' 无法找到解码器 %1 QtAV::AVDemuxer error reading stream data 读取流错误 seek error seek错误 failed to open media 打开多媒体失败 failed to find stream info 无法找到流信息 interrupted by user 用户中断 timeout 超时 QtAV::VideoDecoderCUDA Decoder flags 解码器标记 Performace: ZeroCopy > DirectCopy > GenericCopy 性能: 零拷贝 > 直接拷贝 > 普通拷贝 ZeroCopy: no copy back from GPU to System memory. Directly render the decoded data on GPU 零拷贝: 无GPU到系统内存的拷贝. 直接渲染GPU上已解码的数据 GenericCopy: copy back to host memory and each video frame 普通拷贝: 每帧拷贝至系统内存 Decoding surfaces 解码表面 DirectCopy: copy back to host memory but video frames and map to GL texture 直接拷贝: 拷贝到主存后立即上传为OpenGL纹理 QtAV::VideoDecoderD3D Decoding surfaces 解码表面 0: auto 0: 自动 QtAV::VideoDecoderDXVA 0: auto 0: 自动 Decoding surfaces 解码表面 QtAV::VideoDecoderFFmpeg Skipping the loop filter (aka deblocking) usually has determinal effect on quality. However it provides a big speedup for hi definition streams Force skipping of idct to speed up decoding for frame types (-1=None, 0=Default, 1=B-frames, 2=P-frames, 3=B+P frames, 4=all frames) Force skipping frames for speed up decoding. 强制丢帧以加速解码. Number of decoding threads. Set before open. Maybe no effect for some decoders 解码线程数. 打开前设置. 可能对某些解码器无效 0: auto 0: 自动 1: single thread decoding 1: 单线程解码 QtAV::VideoDecoderFFmpegHW ZeroCopy: fastest. Direct rendering without data copy between CPU and GPU 零拷贝: 最快. 直接渲染无CPU和GPU之间的数据拷贝 Not implemented for all codecs 未对所有编解码实现 OptimizedCopy: copy from USWC memory optimized by SSE4.1 优化拷贝: 使用SSE4.1进行USWC拷贝 GenericCopy: slowest. Generic cpu copy 普通拷贝: 最慢, CPU拷贝 Number of decoding threads. Set before open. Maybe no effect for some decoders 解码线程数. 打开前设置. 可能对某些解码器无效 0: auto 0: 自动 1: single thread decoding 1: 单线程解码 Multithread decoding may crash 多线程解码可能会崩溃 QtAV::VideoDecoderVAAPI 0: auto 0: 自动 Maybe faster 可能更快 Decoding surfaces 解码表面 QtAV::VideoDecoderVDA Output pixel format from decoder. Performance NV12 > UYVY > YUV420P > YUYV. OSX < 10.7 only supports UYVY and YUV420p 解码器输出格式。性能NV12>UYVY>YUV420P>YUYV OSX < 10.7 只支持 UYVY和YUV420P QtAV::VideoDecoderVideoToolbox Output pixel format from decoder. Performance NV12 > UYVY > BGRA > YUV420P > YUYV. OSX < 10.7 only supports UYVY, BGRA and YUV420p 解码器输出的像素格式。性能NV12 > UYVY > BGRA > YUV420P > YUYV. OSX < 10.7只支持UYVY, BGRA and YUV420p Interop with OpenGL 与OpenGL交互 CVOpenGLES: iOS, no copy, fast CVOpenGLES: iOS, 无拷贝, 快 IOSurface: OSX, no copy, fast IOSurface: OSX, 无拷贝, 快 Auto: choose the fastest Auto: 选择最快的 CVPixelBuffer: macOS+iOS IOSurface: macOS, no copy, fast QtAV-1.12.0/src/io/000077500000000000000000000000001312235004300135755ustar00rootroot00000000000000QtAV-1.12.0/src/io/AndroidIO.cpp000066400000000000000000000115241312235004300161140ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/MediaIO.h" #include "QtAV/private/MediaIO_p.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include #include #include "utils/Logger.h" // TODO: how to get filename and find subtitles? //http://stackoverflow.com/questions/5657411/android-getting-a-file-uri-from-a-content-uri //http://stackoverflow.com/questions/19834842/android-gallery-on-kitkat-returns-different-uri-for-intent-action-get-content/20559418#20559418 //http://stackoverflow.com/questions/22029815/how-to-use-the-qt-jni-class-qandroidjniobject namespace QtAV { static const MediaIOId MediaIOId_Android = mkid::id32base36_6<'A','D','r','o','i', 'd'>::value; static const char kName[] = "Android"; class AndroidIOPrivate; class AndroidIO : public MediaIO { DPTR_DECLARE_PRIVATE(AndroidIO) public: AndroidIO(); QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} const QStringList& protocols() const Q_DECL_OVERRIDE { static QStringList p = QStringList() << QStringLiteral("content"); // "file:" is supported too but we use QFile return p; } virtual bool isSeekable() const Q_DECL_OVERRIDE; virtual qint64 read(char *data, qint64 maxSize) Q_DECL_OVERRIDE; virtual bool seek(qint64 offset, int from) Q_DECL_OVERRIDE; virtual qint64 position() const Q_DECL_OVERRIDE; /*! * \brief size * \return <=0 if not support */ virtual qint64 size() const Q_DECL_OVERRIDE { return qt_file.size();} protected: AndroidIO(AndroidIOPrivate &d); void onUrlChanged() Q_DECL_OVERRIDE; private: QAndroidJniObject app_ctx; QFile qt_file; // if use Java.io.InputStream, record pos }; typedef AndroidIO MediaIOAndroid; FACTORY_REGISTER(MediaIO, Android, kName) AndroidIO::AndroidIO() : MediaIO() { QPlatformNativeInterface *interface = QGuiApplication::platformNativeInterface(); jobject activity = (jobject)interface->nativeResourceForIntegration("QtActivity"); app_ctx = QAndroidJniObject(activity).callObjectMethod("getApplicationContext","()Landroid/content/Context;"); } bool AndroidIO::isSeekable() const { return !qt_file.isSequential(); } qint64 AndroidIO::read(char *data, qint64 maxSize) { return qt_file.read(data, maxSize); } bool AndroidIO::seek(qint64 offset, int from) { if (from == SEEK_END) offset = qt_file.size() - offset; else if (from == SEEK_CUR) offset = qt_file.pos() + offset; return qt_file.seek(offset); } qint64 AndroidIO::position() const { return qt_file.pos(); } void AndroidIO::onUrlChanged() { qt_file.close(); QAndroidJniObject content_resolver = app_ctx.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;"); if (!content_resolver.isValid()) { qWarning("getContentResolver error"); return; } QAndroidJniObject s = QAndroidJniObject::fromString(url()); QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", s.object()); //input_stream = content_resolver.callObjectMethod("openInputStream", "(Landroid.net.Uri;)Ljava.io.InputStream;", uri.object()); // TODO: why error? //qDebug() << "onUrlChanged InputStream: " << input_stream.toString(); s = QAndroidJniObject::fromString(QStringLiteral("r")); QAndroidJniObject pfd = content_resolver.callObjectMethod("openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", uri.object(), s.object()); if (!pfd.isValid()) { qWarning("openFileDescriptor error"); return; } int fd = pfd.callMethod("detachFd", "()I"); qt_file.open(fd, QIODevice::ReadOnly); } } //namespace QtAV QtAV-1.12.0/src/io/MediaIO.cpp000066400000000000000000000137001312235004300155510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin Initial QAVIOContext.cpp code is from Stefan Ladage * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/MediaIO.h" #include "QtAV/private/MediaIO_p.h" #include "QtAV/private/factory.h" #include #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(MediaIO) extern bool RegisterMediaIOQIODevice_Man(); extern bool RegisterMediaIOQFile_Man(); extern bool RegisterMediaIOWinRT_Man(); void MediaIO::registerAll() { static bool done = false; if (done) return; done = true; RegisterMediaIOQIODevice_Man(); RegisterMediaIOQFile_Man(); #ifdef Q_OS_WINRT RegisterMediaIOWinRT_Man(); #endif } QStringList MediaIO::builtInNames() { static QStringList names; if (!names.isEmpty()) return names; std::vector ns(MediaIOFactory::Instance().registeredNames()); foreach (const char* n, ns) { names.append(QLatin1String(n)); } return names; } // TODO: plugin // TODO: plugin use metadata(Qt plugin system) to avoid loading MediaIO* MediaIO::createForProtocol(const QString &protocol) { std::vector ids(MediaIOFactory::Instance().registeredIds()); foreach (MediaIOId id, ids) { MediaIO *in = MediaIO::create(id); if (in->protocols().contains(protocol)) return in; delete in; } return 0; } MediaIO* MediaIO::createForUrl(const QString &url) { const int p = url.indexOf(QLatin1String(":")); if (p < 0) return 0; MediaIO *io = MediaIO::createForProtocol(url.left(p)); if (!io) return 0; io->setUrl(url); return io; } static int av_read(void *opaque, unsigned char *buf, int buf_size) { MediaIO* io = static_cast(opaque); return io->read((char*)buf, buf_size); } static int av_write(void *opaque, unsigned char *buf, int buf_size) { MediaIO* io = static_cast(opaque); return io->write((const char*)buf, buf_size); } static int64_t av_seek(void *opaque, int64_t offset, int whence) { if (whence == SEEK_SET && offset < 0) return -1; MediaIO* io = static_cast(opaque); if (!io->isSeekable()) { qWarning("Can not seek. MediaIO[%s] is not a seekable IO", MediaIO::staticMetaObject.className()); return -1; } if (whence == AVSEEK_SIZE) { // return the filesize without seeking anywhere. Supporting this is optional. return io->size() > 0 ? io->size() : 0; } if (!io->seek(offset, whence)) return -1; return io->position(); } MediaIO::MediaIO(QObject *parent) : QObject(parent) {} MediaIO::MediaIO(MediaIOPrivate &d, QObject *parent) : QObject(parent) , DPTR_INIT(&d) {} MediaIO::~MediaIO() { release(); } void MediaIO::setUrl(const QString &url) { DPTR_D(MediaIO); if (d.url == url) return; // TODO: check protocol d.url = url; onUrlChanged(); } QString MediaIO::url() const { return d_func().url; } void MediaIO::onUrlChanged() {} bool MediaIO::setAccessMode(AccessMode value) { DPTR_D(MediaIO); if (d.mode == value) return true; if (value == Write && !isWritable()) { qWarning("Can not set Write access mode to this MediaIO"); return false; } d.mode = value; return true; } MediaIO::AccessMode MediaIO::accessMode() const { return d_func().mode; } const QStringList& MediaIO::protocols() const { static QStringList no_protocols; return no_protocols; } #define IODATA_BUFFER_SIZE 32768 // TODO: user configurable static const int kBufferSizeDefault = 32768; void MediaIO::setBufferSize(int value) { DPTR_D(MediaIO); if (d.buffer_size == value) return; d.buffer_size = value; } int MediaIO::bufferSize() const { return d_func().buffer_size; } void* MediaIO::avioContext() { DPTR_D(MediaIO); if (d.ctx) return d.ctx; // buffer will be released in av_probe_input_buffer2=>ffio_rewind_with_probe_data. always is? may be another context unsigned char* buf = (unsigned char*)av_malloc(IODATA_BUFFER_SIZE); // open for write if 1. SET 0 if open for read otherwise data ptr in av_read(data, ...) does not change const int write_flag = (accessMode() == Write) && isWritable(); d.ctx = avio_alloc_context(buf, bufferSize() > 0 ? bufferSize() : kBufferSizeDefault, write_flag, this, &av_read, write_flag ? &av_write : NULL, &av_seek); // if seekable==false, containers that estimate duration from pts(or bit rate) will not seek to the last frame when computing duration // but it's still seekable if call seek outside(e.g. from demuxer) // TODO: isVariableSize: size = -real_size d.ctx->seekable = isSeekable() && !isVariableSize() ? AVIO_SEEKABLE_NORMAL : 0; return d.ctx; } void MediaIO::release() { DPTR_D(MediaIO); if (!d.ctx) return; // avio_close is called by avformat_close_input. here we only allocate but no open av_freep(&d.ctx->buffer); av_freep(&d.ctx); } } //namespace QtAV QtAV-1.12.0/src/io/QIODeviceIO.cpp000066400000000000000000000161421312235004300163050ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/MediaIO.h" #include "QtAV/private/MediaIO_p.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #ifndef TEST_QTAV_QIODeviceIO #include "utils/Logger.h" #else #include #endif namespace QtAV { class QIODeviceIOPrivate; class QIODeviceIO : public MediaIO { Q_OBJECT Q_PROPERTY(QIODevice* device READ device WRITE setDevice NOTIFY deviceChanged) DPTR_DECLARE_PRIVATE(QIODeviceIO) public: QIODeviceIO(); virtual QString name() const Q_DECL_OVERRIDE; // MUST open/close outside void setDevice(QIODevice *dev); // set private in QFileIO etc QIODevice* device() const; virtual bool isSeekable() const Q_DECL_OVERRIDE; virtual bool isWritable() const Q_DECL_OVERRIDE; virtual qint64 read(char *data, qint64 maxSize) Q_DECL_OVERRIDE; virtual qint64 write(const char *data, qint64 maxSize) Q_DECL_OVERRIDE; virtual bool seek(qint64 offset, int from) Q_DECL_OVERRIDE; virtual qint64 position() const Q_DECL_OVERRIDE; /*! * \brief size * \return <=0 if not support */ virtual qint64 size() const Q_DECL_OVERRIDE; Q_SIGNALS: void deviceChanged(); protected: QIODeviceIO(QIODeviceIOPrivate &d); }; typedef QIODeviceIO MediaIOQIODevice; static const MediaIOId MediaIOId_QIODevice = mkid::id32base36_6<'Q','I','O','D','e','v'>::value; static const char kQIODevName[] = "QIODevice"; FACTORY_REGISTER(MediaIO, QIODevice, kQIODevName) class QIODeviceIOPrivate : public MediaIOPrivate { public: QIODeviceIOPrivate() : MediaIOPrivate() , dev(0) {} QIODevice *dev; }; QIODeviceIO::QIODeviceIO() : MediaIO(*new QIODeviceIOPrivate()) {} QIODeviceIO::QIODeviceIO(QIODeviceIOPrivate &d) : MediaIO(d) {} QString QIODeviceIO::name() const { return QLatin1String(kQIODevName);} void QIODeviceIO::setDevice(QIODevice *dev) { DPTR_D(QIODeviceIO); if (d.dev == dev) return; d.dev = dev; emit deviceChanged(); } QIODevice* QIODeviceIO::device() const { return d_func().dev; } bool QIODeviceIO::isSeekable() const { DPTR_D(const QIODeviceIO); return d.dev && !d.dev->isSequential(); } bool QIODeviceIO::isWritable() const { DPTR_D(const QIODeviceIO); return d.dev && d.dev->isWritable(); } qint64 QIODeviceIO::read(char *data, qint64 maxSize) { DPTR_D(QIODeviceIO); if (!d.dev) return 0; return d.dev->read(data, maxSize); } qint64 QIODeviceIO::write(const char *data, qint64 maxSize) { DPTR_D(QIODeviceIO); if (!d.dev) return 0; return d.dev->write(data, maxSize); } bool QIODeviceIO::seek(qint64 offset, int from) { DPTR_D(QIODeviceIO); if (!d.dev) return false; if (from == SEEK_END) { offset = d.dev->size() - offset; } else if (from == SEEK_CUR) { offset = d.dev->pos() + offset; } return d.dev->seek(offset); } qint64 QIODeviceIO::position() const { DPTR_D(const QIODeviceIO); if (!d.dev) return 0; return d.dev->pos(); } qint64 QIODeviceIO::size() const { DPTR_D(const QIODeviceIO); if (!d.dev) return 0; return d.dev->size(); // sequential device returns bytesAvailable() } // qrc support static const char kQFileName[] = "QFile"; class QFileIOPrivate; class QFileIO Q_DECL_FINAL: public QIODeviceIO { DPTR_DECLARE_PRIVATE(QFileIO) public: QFileIO(); QString name() const Q_DECL_OVERRIDE { return QLatin1String(kQFileName);} const QStringList& protocols() const Q_DECL_OVERRIDE { static QStringList p = QStringList() << QStringLiteral("") << QStringLiteral("qrc") << QStringLiteral("qfile") #ifdef Q_OS_ANDROID << QStringLiteral("assets") #endif #ifdef Q_OS_IOS << QStringLiteral("assets-library") #endif ; return p; } protected: void onUrlChanged() Q_DECL_OVERRIDE; private: using QIODeviceIO::setDevice; }; typedef QFileIO MediaIOQFile; static const MediaIOId MediaIOId_QFile = mkid::id32base36_5<'Q','F','i','l','e'>::value; FACTORY_REGISTER(MediaIO, QFile, kQFileName) class QFileIOPrivate Q_DECL_FINAL: public QIODeviceIOPrivate { public: QFileIOPrivate() : QIODeviceIOPrivate() {} ~QFileIOPrivate() { if (file.isOpen()) file.close(); } QFile file; }; QFileIO::QFileIO() : QIODeviceIO(*new QFileIOPrivate()) { setDevice(&d_func().file); } void QFileIO::onUrlChanged() { DPTR_D(QFileIO); if (d.file.isOpen()) d.file.close(); QString path(url()); if (path.startsWith(QLatin1String("qrc:"))) { path = path.mid(3); } else if (path.startsWith(QLatin1String("qfile:"))) { path = path.mid(6); #ifdef Q_OS_WIN int p = path.indexOf(QLatin1Char(':')); if (p < 1) { qWarning("invalid path. ':' wrong position"); return; } p -= 1; QChar c = path.at(p).toUpper(); if (c < QLatin1Char('A') || c > QLatin1Char('Z')) { qWarning("invalid path. wrong driver"); return; } const QString path_maybe = path.mid(p); qDebug() << path_maybe; --p; while (p > 0) { c = path.at(p); if (c != QLatin1Char('\\') && c != QLatin1Char('/')) { qWarning("invalid path. wrong dir seperator"); return; } --p; } path = path_maybe; #endif } d.file.setFileName(path); if (path.isEmpty()) return; if (!d.file.open(QIODevice::ReadOnly)) qWarning() << "Failed to open [" << d.file.fileName() << "]: " << d.file.errorString(); } } //namespace QtAV #include "QIODeviceIO.moc" #ifdef TEST_QTAV_QIODeviceIO int main(int, char**) { QtAV::QFileIO fi; qDebug() << "protocols: " << fi.protocols(); fi.setUrl("qrc:/QtAV.svg"); QByteArray data(1024, 0); fi.read(data.data(), data.size()); qDebug("QFileIO url: %s, seekable: %d, size: %lld", fi.url().toUtf8().constData(), fi.isSeekable(), fi.size()); qDebug() << data; return 0; } #endif QtAV-1.12.0/src/io/WinRTIO.cpp000066400000000000000000000172271312235004300155450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/MediaIO.h" #include "QtAV/private/MediaIO_p.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include #include #include //required by qfunctions_winrt.h #include #include "utils/Logger.h" using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Storage; using namespace ABI::Windows::Storage::Streams; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; #define COM_LOG_COMPONENT "WinRTIO" #define COM_ENSURE(f, ...) COM_CHECK(f, return __VA_ARGS__;) #define COM_WARN(f) COM_CHECK(f) #define COM_CHECK(f, ...) \ do { \ HRESULT hr = f; \ if (FAILED(hr)) { \ qWarning() << QString::fromLatin1(COM_LOG_COMPONENT " error@%1. " #f ": (0x%2) %3").arg(__LINE__).arg(hr, 0, 16).arg(qt_error_string(hr)); \ __VA_ARGS__ \ } \ } while (0) namespace QtAV { static const MediaIOId MediaIOId_WinRT = mkid::id32base36_5<'W','i','n','R','T'>::value; static const char kName[] = "WinRT"; class WinRTIOPrivate; class WinRTIO : public MediaIO { DPTR_DECLARE_PRIVATE(WinRTIO) public: WinRTIO(); QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} const QStringList& protocols() const Q_DECL_OVERRIDE { static QStringList p = QStringList() << name().toLower(); return p; } virtual bool isSeekable() const Q_DECL_OVERRIDE; virtual bool isWritable() const Q_DECL_OVERRIDE; virtual qint64 read(char *data, qint64 maxSize) Q_DECL_OVERRIDE; virtual qint64 write(const char *data, qint64 maxSize) Q_DECL_OVERRIDE; virtual bool seek(qint64 offset, int from) Q_DECL_OVERRIDE; virtual qint64 position() const Q_DECL_OVERRIDE; /*! * \brief size * \return <=0 if not support */ virtual qint64 size() const Q_DECL_OVERRIDE; protected: WinRTIO(WinRTIOPrivate &d); void onUrlChanged() Q_DECL_OVERRIDE; private: // item available until this function finished. If app is launch from association, addRef there void openFromStorage(IStorageItem* item); // only special locations is supported, but seems better than qfile if not use file picker // qfile can not open those paths in standard paths directly void openFromPath(const QString& path); void open(ComPtr& file); }; typedef WinRTIO MediaIOWinRT; FACTORY_REGISTER(MediaIO, WinRT, kName) class WinRTIOPrivate : public MediaIOPrivate { public: WinRTIOPrivate() : MediaIOPrivate() , pos(0) {} ComPtr item; ComPtr stream; qint64 pos; }; WinRTIO::WinRTIO() : MediaIO(*new WinRTIOPrivate()) {} WinRTIO::WinRTIO(WinRTIOPrivate &d) : MediaIO(d) {} bool WinRTIO::isSeekable() const { DPTR_D(const WinRTIO); return !!d.stream; } bool WinRTIO::isWritable() const { DPTR_D(const WinRTIO); return !!d.stream; } //http://www.codeproject.com/Tips/489450/Creating-Custom-FFmpeg-IO-Context qint64 WinRTIO::read(char *data, qint64 maxSize) { DPTR_D(WinRTIO); if (!d.stream) return 0; ULONG bytesRead = 0; // TODO: -1 0r 0 if error? COM_ENSURE(d.stream->Read(data, maxSize, &bytesRead), -1); d.pos += bytesRead; return bytesRead; } qint64 WinRTIO::write(const char *data, qint64 maxSize) { DPTR_D(WinRTIO); if (!d.stream) return 0; ULONG bytesWritten; // TODO: -1 0r 0 if error? COM_ENSURE(d.stream->Write(data, maxSize, &bytesWritten), -1); d.pos += bytesWritten; return bytesWritten; } bool WinRTIO::seek(qint64 offset, int from) { DPTR_D(WinRTIO); if (!d.stream) return false; LARGE_INTEGER in; in.QuadPart = offset; ULARGE_INTEGER out = { 0 }; //dwOrigin: has the same value as ffmpeg. STREAM_SEEK_SET, STREAM_SEEK_CUR, STREAM_SEEK_END COM_ENSURE(d.stream->Seek(in, from, &out), false); d.pos = out.QuadPart; return true; } qint64 WinRTIO::position() const { DPTR_D(const WinRTIO); return d.pos; } qint64 WinRTIO::size() const { DPTR_D(const WinRTIO); if (!d.stream) return 0; STATSTG st; COM_ENSURE(d.stream->Stat(&st, STATFLAG_DEFAULT), 0); return st.cbSize.QuadPart; } void WinRTIO::onUrlChanged() { d_func().pos = 0; qDebug() << "onUrlChanged: " << url(); // winrt:@ptr_address:path // winrt:path if (url().startsWith(name().append(QStringLiteral(":@")), Qt::CaseInsensitive)) { const int addr_begin = name().size() + 2; const int addr_end = url().indexOf(QLatin1Char(':'), addr_begin); QString addr(url().mid(addr_begin, addr_end - addr_begin)); openFromStorage((IStorageItem*)(qptrdiff)addr.toULongLong()); } else if (url().startsWith(name().append(QStringLiteral(":")), Qt::CaseInsensitive)) { openFromPath(url().mid(name().size() + 1)); } else { return; } } void WinRTIO::openFromStorage(IStorageItem *item) { DPTR_D(WinRTIO); d.item = item; HString path; COM_ENSURE(item->get_Path(path.GetAddressOf())); quint32 pathLen; const wchar_t *pathStr = path.GetRawBuffer(&pathLen); const QString filePath = QString::fromWCharArray(pathStr, pathLen); qDebug() << "winrt.io from storage file: " << filePath; ComPtr file; COM_ENSURE(d.item.As(&file)); open(file); } void WinRTIO::openFromPath(const QString &path) { ComPtr fileFactory; COM_ENSURE(RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_StorageFile).Get(), IID_PPV_ARGS(&fileFactory))); // 2 bytes for utf-16, 4 bytes for ucs-4(unix) wchar_t *wp = new wchar_t[path.size()*4]; const int len = path.toWCharArray(wp); HString p; p.Set(wp, len); delete [] wp; ComPtr> op; COM_ENSURE(fileFactory->GetFileFromPathAsync(p.Get(), &op)); ComPtr file; COM_ENSURE(QWinRTFunctions::await(op, file.GetAddressOf())); open(file); } void WinRTIO::open(ComPtr &file) { ComPtr> op; COM_ENSURE(file->OpenAsync(accessMode() == Read ? FileAccessMode_Read : FileAccessMode_ReadWrite, &op)); ComPtr stream; COM_ENSURE(QWinRTFunctions::await(op, stream.GetAddressOf())); // Convert asynchronous IRandomAccessStream to synchronous IStream. This API requires shcore.h and shcore.lib COM_ENSURE(CreateStreamOverRandomAccessStream(reinterpret_cast(stream.Get()), IID_PPV_ARGS(&d_func().stream))); } } //namespace QtAV QtAV-1.12.0/src/io/tst_avinput.pro000066400000000000000000000001461312235004300167000ustar00rootroot00000000000000QT += av testlib CONFIG -= app_bundle INCLUDEPATH += $$[QT_INSTALL_HEADERS] SOURCES = tst_mediaio.cpp QtAV-1.12.0/src/io/tst_mediaio.cpp000066400000000000000000000024371312235004300166100ustar00rootroot00000000000000#include #include #include using namespace QtAV; class tst_MediaIO : public QObject { Q_OBJECT private slots: void create(); void createForProtocol(); void read(); }; void tst_MediaIO::create() { MediaIO *in = MediaIO::create("QFile"); QVERIFY(in); delete in; in = MediaIO::create("Other"); QVERIFY(!in); delete in; } void tst_MediaIO::createForProtocol() { MediaIO *in = MediaIO::createForProtocol("qrc"); QVERIFY(in); delete in; in = MediaIO::createForProtocol(""); QVERIFY(in); QCOMPARE(in->name(), QString("QFile")); QVERIFY(in->protocols().contains("qrc")); delete in; in = MediaIO::createForProtocol("xyz"); QVERIFY(!in); delete in; } void tst_MediaIO::read() { const QString path(":/QtAV.svg"); MediaIO *in = MediaIO::createForProtocol(path.left(path.indexOf(QChar(':')))); QVERIFY(in); QVERIFY(in->isSeekable()); in->setUrl(path); QByteArray data(1024, 0); in->read(data.data(), data.size()); QFile f(path); f.open(QIODevice::ReadOnly); QByteArray data2(1024, 0); f.read(data2.data(), data2.size()); QCOMPARE(data, data2); QCOMPARE(in->size(), f.size()); delete in; } QTEST_MAIN(tst_MediaIO) #include "tst_avinput.moc" QtAV-1.12.0/src/io/tst_qiodevinput.pro000066400000000000000000000002201312235004300175520ustar00rootroot00000000000000QT += av av-private CONFIG -= app_bundle DEFINES += TEST_QTAV_QIODEVICEINPUT INCLUDEPATH += $$[QT_INSTALL_HEADERS] SOURCES = QIODeviceInput.cpp QtAV-1.12.0/src/libQtAV.pri000066400000000000000000000112471312235004300152110ustar00rootroot00000000000000# qmake library building template pri file # Copyright (C) 2011-2016 Wang Bin # Shanghai, China. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # ############################## HOW TO ################################## # Suppose the library name is XX # Usually what you need to change are: LIB_VERSION, NAME and DLLDESTDIR. # And rename xx-buildlib and LIBXX_PRI_INCLUDED # the contents of libXX.pro is: # TEMPLATE = lib # QT -= gui # CONFIG *= xx-buildlib # STATICLINK = 1 #optional. default is detected by staticlib in CONFIG # PROJECTROOT = $$PWD/.. # include(libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # ... # the content of other pro using this library is: # TEMPLATE = app # PROJECTROOT = $$PWD/.. # STATICLINK = 1 #optional. default is detected by staticlib in CONFIG # include(dir_of_XX/libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # NAME = QtAV !isEmpty(LIB$$upper($$NAME)_PRI_INCLUDED): { error("lib$${NAME}.pri already included") unset(NAME) } eval(LIB$$upper($$NAME)_PRI_INCLUDED = 1) LIB_VERSION = $$QTAV_VERSION #0.x.y may be wrong for dll # If user haven't supplied STATICLINK, then auto-detect isEmpty(STATICLINK) { static|contains(CONFIG, staticlib) { STATICLINK = 1 } else { STATICLINK = 0 } # Override for ios. Dynamic link is only supported # in iOS 8.1. ios:STATICLINK = 1 } isEqual(STATICLINK, 1):DEFINES += BUILD_$$upper($$NAME)_STATIC TEMPLATE += fakelib PROJECT_TARGETNAME = $$qtLibraryTarget($$NAME) TEMPLATE -= fakelib isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/.. include($${PROJECTROOT}/common.pri) preparePaths($$OUT_PWD/../out) CONFIG += depend_includepath #? mac_framework: PROJECT_TARGETNAME = $$NAME PROJECT_SRCPATH = $$PWD PROJECT_LIBDIR = $$qtLongName($$BUILD_DIR/lib) INCLUDEPATH *= $$PROJECT_SRCPATH $$PROJECT_SRCPATH/.. $$PROJECT_SRCPATH/$$NAME DEPENDPATH *= $$PROJECT_SRCPATH #QMAKE_LFLAGS_RPATH += #will append to rpath dir #eval() ? !contains(CONFIG, $$lower($$NAME)-buildlib) { #The following may not need to change CONFIG *= link_prl mac_framework { LIBS += -F$$PROJECT_LIBDIR -framework $$PROJECT_TARGETNAME } else { LIBS *= -L$$PROJECT_LIBDIR -l$$qtLibName($$NAME) isEqual(STATICLINK, 1) { PRE_TARGETDEPS += $$PROJECT_LIBDIR/$$qtStaticLib($$NAME) } else { win32 { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME, $$LIB_VERSION) } else { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) } } } } else { #Add your additional configuration first. e.g. # win32: LIBS += -lUser32 # The following may not need to change !CONFIG(plugin) { #TEMPLATE = lib VERSION = $$LIB_VERSION DESTDIR= $$PROJECT_LIBDIR } TARGET = $$PROJECT_TARGETNAME ##I commented out this before, why? CONFIG *= create_prl # DEFINES += BUILD_$$upper($$NAME)_LIB #win32-msvc* isEqual(STATICLINK, 1) { CONFIG -= shared dll ##otherwise the following shared is true, why? CONFIG *= staticlib } else { CONFIG *= shared #shared includes dll } shared { !CONFIG(plugin) { !isEqual(DESTDIR, $$BUILD_DIR/bin): DLLDESTDIR = $$BUILD_DIR/bin #copy shared lib there } CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP):!mac_framework: QMAKE_POST_LINK = -$$QMAKE_STRIP $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) #copy from the pro creator creates. symbian { MMP_RULES += EXPORTUNFROZEN TARGET.UID3 = 0xE4CC8061 TARGET.CAPABILITY = TARGET.EPOCALLOWDLLDATA = 1 addFiles.sources = $$qtSharedLib($$NAME, $$LIB_VERSION) addFiles.path = !:/sys/bin DEPLOYMENT += addFiles } } unix:!symbian { maemo5 { target.path = /opt/usr/lib } else { target.path = /usr/lib } INSTALLS += target } } !no_rpath:!cross_compile:set_rpath($$PROJECT_LIBDIR) *maemo*: QMAKE_LFLAGS += -lasound unset(LIB_VERSION) unset(PROJECT_SRCPATH) unset(PROJECT_LIBDIR) unset(PROJECT_TARGETNAME) QtAV-1.12.0/src/libQtAV.pro000066400000000000000000000522651312235004300152240ustar00rootroot00000000000000TEMPLATE = lib MODULE_INCNAME = QtAV # for mac framework. also used in install_sdk.pro TARGET = QtAV QT += core gui #CONFIG *= ltcg greaterThan(QT_MAJOR_VERSION, 4) { contains(QT_CONFIG, opengl) { CONFIG *= config_opengl greaterThan(QT_MINOR_VERSION, 3) { CONFIG *= config_openglwindow } } } else { config_gl: QT += opengl } CONFIG *= qtav-buildlib static: CONFIG *= static_ffmpeg INCLUDEPATH += $$[QT_INSTALL_HEADERS] # TODO: ffmpeg dir #mac: simd.prf will load qt_build_config and the result is soname will prefixed with QT_INSTALL_LIBS and link flag will append soname after QMAKE_LFLAGS_SONAME config_libcedarv: CONFIG *= neon config_simd #need by qt4 addSimdCompiler(). neon or config_neon is required because tests/arch can not detect neon ## sse2 sse4_1 may be defined in Qt5 qmodule.pri but is not included. Qt4 defines sse and sse2 sse4_1|config_sse4_1|contains(TARGET_ARCH_SUB, sse4.1): CONFIG *= sse4_1 config_simd sse2|config_sse2|contains(TARGET_ARCH_SUB, sse2): CONFIG *= sse2 config_simd CONFIG(debug, debug|release): DEFINES += DEBUG #release: DEFINES += QT_NO_DEBUG_OUTPUT #var with '_' can not pass to pri? PROJECTROOT = $$PWD/.. !include(libQtAV.pri): error("could not find libQtAV.pri") preparePaths($$OUT_PWD/../out) exists($$PROJECTROOT/extra/qtLongName(include)): INCLUDEPATH += $$PROJECTROOT/extra/qtLongName(include) exists($$PROJECTROOT/extra/qtLongName(lib)): LIBS += -L$$PROJECTROOT/extra/qtLongName(lib) staticlib: DEFINES += BUILD_QTAV_STATIC config_uchardet { DEFINES += LINK_UCHARDET LIBS *= -luchardet } else:exists($$PROJECTROOT/contrib/uchardet/src/uchardet.h) { include($$PROJECTROOT/contrib/uchardet.pri) DEFINES += BUILD_UCHARDET } exists($$PROJECTROOT/contrib/capi/capi.pri) { include($$PROJECTROOT/contrib/capi/capi.pri) DEFINES *= QTAV_HAVE_CAPI=1 } else { warning("contrib/capi is missing. run 'git submodule update --init' first") } RESOURCES += QtAV.qrc \ shaders/shaders.qrc !rc_file { RC_ICONS = QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAV Multimedia framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV" } else:win32 { RC_FILE = QtAV.rc #no depends for rc file by default, even if rc includes a header. Makefile target use '/' as default, so not works iwth win cmd rc.target = $$clean_path($$RC_FILE) #rc obj depends on clean path target rc.depends = $$PWD/QtAV/version.h #why use multiple rule failed? i.e. add a rule without command isEmpty(QMAKE_SH) { rc.commands = @copy /B $$system_path($$RC_FILE)+,, #change file time } else { rc.commands = @touch $$RC_FILE #change file time } QMAKE_EXTRA_TARGETS += rc } OTHER_FILES += $$RC_FILE QtAV.svg TRANSLATIONS = i18n/QtAV_zh_CN.ts i18n/QtAV.ts sse4_1 { CONFIG += sse2 #only sse4.1 is checked. sse2 now can be disabled if sse4.1 is disabled DEFINES += QTAV_HAVE_SSE4_1=1 !config_simd: CONFIG *= simd SSE4_1_SOURCES += utils/CopyFrame_SSE4.cpp } sse2 { DEFINES += QTAV_HAVE_SSE2=1 !config_simd: CONFIG *= simd SSE2_SOURCES += utils/CopyFrame_SSE2.cpp } win32 { # cross build, old vc etc. !config_dx: INCLUDEPATH += $$PROJECTROOT/contrib/dxsdk } *msvc*:!winrt { #link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO win32-msvc2010|win32-msvc2008|win32-msvc2012 { QMAKE_LFLAGS *= /DEBUG #workaround for CoInitializeEx() and other symbols not found at runtime INCLUDEPATH *= compat/msvc # vs2012 only has stdint.h } debug: QMAKE_LFLAGS += /SAFESEH:NO #CXXFLAGS debug: /MTd !static:QMAKE_LFLAGS *= /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcmtd.lib #for msbuild vs2013 } capi { contains(QT_CONFIG, egl)|contains(QT_CONFIG, dynamicgl)|contains(QT_CONFIG, opengles2) { CONFIG *= enable_egl !ios { winrt: DEFINES += CAPI_LINK_EGL #required by capi_egl.* DEFINES += QTAV_HAVE_EGL_CAPI=1 HEADERS *= capi/egl_api.h SOURCES *= capi/egl_api.cpp } } } enable_egl:greaterThan(QT_MAJOR_VERSION,4):qtHaveModule(x11extras): QT *= x11extras config_gl|config_opengl { contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles2): CONFIG *= enable_desktopgl } #UINT64_C: C99 math features, need -D__STDC_CONSTANT_MACROS in CXXFLAGS DEFINES += __STDC_CONSTANT_MACROS android { CONFIG *= config_opensl !no_gui_private:qtHaveModule(androidextras) { #qt5.2 has QAndroidJniObject QT *= androidextras gui-private #QPlatformNativeInterface get "QtActivity" SOURCES *= io/AndroidIO.cpp SOURCES *= codec/video/VideoDecoderMediaCodec.cpp } } config_x11 { DEFINES += QTAV_HAVE_X11=1 SOURCES *= filter/X11FilterContext.cpp LIBS *= -lX11 } config_swresample { DEFINES += QTAV_HAVE_SWRESAMPLE=1 SOURCES += AudioResamplerFF.cpp LIBS += -lswresample } config_avresample { DEFINES += QTAV_HAVE_AVRESAMPLE=1 SOURCES += AudioResamplerLibav.cpp LIBS += -lavresample } config_avdevice { #may depends on avfilter DEFINES += QTAV_HAVE_AVDEVICE=1 LIBS *= -lavdevice static_ffmpeg { win32 { LIBS *= -lgdi32 -loleaut32 -lshlwapi #shlwapi: desktop >= xp only } else:linux { LIBS *= -lXv #-lX11 -lxcb -lxcb-shm -lxcb-xfixes -lxcb-render -lxcb-shape } else:mac { # static ffmpeg LIBS += -framework Foundation -framework CoreMedia -framework QuartzCore -framework CoreGraphics -framework CoreVideo ios { LIBS += -framework AVFoundation } else { LIBS += -framework QTKit # assume avdevice targets to the same version as Qt and always >= 10.6 !isEqual(QMAKE_MACOSX_DEPLOYMENT_TARGET, 10.6): LIBS += -framework AVFoundation } } } } config_avfilter { DEFINES += QTAV_HAVE_AVFILTER=1 LIBS += -lavfilter } config_ipp { DEFINES += QTAV_HAVE_IPP=1 ICCROOT = $$(IPPROOT)/../compiler INCLUDEPATH += $$(IPPROOT)/include SOURCES += ImageConverterIPP.cpp message("QMAKE_TARGET.arch" $$QMAKE_TARGET.arch) *64|contains(QMAKE_TARGET.arch, x86_64)|contains(TARGET_ARCH, x86_64) { IPPARCH=intel64 } else { IPPARCH=ia32 } LIBS *= -L$$(IPPROOT)/lib/$$IPPARCH -lippcc -lippcore -lippi LIBS *= -L$$(IPPROOT)/../compiler/lib/$$IPPARCH -lsvml -limf #omp for static link. _t is multi-thread static link } mac|ios { CONFIG *= config_openal SOURCES += output/audio/AudioOutputAudioToolbox.cpp LIBS += -framework AudioToolbox } win32: { HEADERS += output/audio/xaudio2_compat.h SOURCES += output/audio/AudioOutputXAudio2.cpp DEFINES *= QTAV_HAVE_XAUDIO2=1 winrt { LIBS += -lxaudio2 #only for xbox or >=win8 } else { LIBS += -lole32 #CoInitializeEx for vs2008, but can not find the symbol at runtime } } config_dsound:!winrt { SOURCES += output/audio/AudioOutputDSound.cpp DEFINES *= QTAV_HAVE_DSOUND=1 } config_portaudio { SOURCES += output/audio/AudioOutputPortAudio.cpp DEFINES *= QTAV_HAVE_PORTAUDIO=1 LIBS *= -lportaudio #win32: LIBS *= -lwinmm #-lksguid #-luuid } config_openal { SOURCES *= output/audio/AudioOutputOpenAL.cpp HEADERS *= capi/openal_api.h SOURCES *= capi/openal_api.cpp DEFINES *= QTAV_HAVE_OPENAL=1 static_openal: DEFINES += AL_LIBTYPE_STATIC # openal-soft AL_API dllimport error. mac's macro is AL_BUILD_LIBRARY ios: CONFIG *= config_openal_link !capi|config_openal_link|static_openal { DEFINES *= CAPI_LINK_OPENAL win32 { LIBS += -lOpenAL32 -lwinmm } else:mac { LIBS += -framework OpenAL DEFINES += HEADER_OPENAL_PREFIX } else:blackberry { LIBS += -lOpenAL } else { LIBS += -lopenal static_openal:!android: LIBS += -lasound } } } config_opensl { SOURCES += output/audio/AudioOutputOpenSL.cpp DEFINES *= QTAV_HAVE_OPENSL=1 LIBS += -lOpenSLES } config_pulseaudio { SOURCES += output/audio/AudioOutputPulse.cpp DEFINES *= QTAV_HAVE_PULSEAUDIO=1 LIBS += -lpulse } CONFIG += config_cuda #CONFIG += config_cuda_link config_cuda { DEFINES += QTAV_HAVE_CUDA=1 HEADERS += cuda/dllapi/nv_inc.h cuda/helper_cuda.h SOURCES += codec/video/VideoDecoderCUDA.cpp #contains(QT_CONFIG, opengl) { HEADERS += codec/video/SurfaceInteropCUDA.h SOURCES += codec/video/SurfaceInteropCUDA.cpp #} INCLUDEPATH += $$PWD/cuda cuda/dllapi config_dllapi:config_dllapi_cuda { DEFINES += QTAV_HAVE_DLLAPI_CUDA=1 INCLUDEPATH += ../depends/dllapi/src include(../depends/dllapi/src/libdllapi.pri) SOURCES += cuda/dllapi/cuda.cpp cuda/dllapi/nvcuvid.cpp cuda/dllapi/cuviddec.cpp } else:config_cuda_link { DEFINES += CUDA_LINK INCLUDEPATH += $$(CUDA_PATH)/include LIBS += -L$$(CUDA_PATH)/lib contains(TARGET_ARCH, x86): LIBS += -L$$(CUDA_PATH)/lib/Win32 else: LIBS += -L$$(CUDA_PATH)/lib/x64 LIBS += -lnvcuvid -lcuda } SOURCES += cuda/cuda_api.cpp HEADERS += cuda/cuda_api.h } config_d3d11va { CONFIG *= d3dva c++11 DEFINES *= QTAV_HAVE_D3D11VA=1 SOURCES += codec/video/VideoDecoderD3D11.cpp HEADERS += directx/SurfaceInteropD3D11.h SOURCES += directx/SurfaceInteropD3D11.cpp HEADERS += directx/D3D11VP.h SOURCES += directx/D3D11VP.cpp enable_egl { SOURCES += directx/SurfaceInteropD3D11EGL.cpp } enable_desktopgl { SOURCES += directx/SurfaceInteropD3D11GL.cpp } winrt: LIBS *= -ld3d11 } win32:!winrt { HEADERS += directx/SurfaceInteropD3D9.h SOURCES += directx/SurfaceInteropD3D9.cpp enable_egl { SOURCES += directx/SurfaceInteropD3D9EGL.cpp } enable_desktopgl { SOURCES += directx/SurfaceInteropD3D9GL.cpp } } config_dxva { CONFIG *= d3dva DEFINES *= QTAV_HAVE_DXVA=1 SOURCES += codec/video/VideoDecoderDXVA.cpp LIBS += -lole32 } d3dva { HEADERS += codec/video/VideoDecoderD3D.h SOURCES += codec/video/VideoDecoderD3D.cpp } config_vaapi* { DEFINES *= QTAV_HAVE_VAAPI=1 SOURCES += codec/video/VideoDecoderVAAPI.cpp vaapi/vaapi_helper.cpp HEADERS += vaapi/vaapi_helper.h #contains(QT_CONFIG, opengl) { HEADERS += vaapi/SurfaceInteropVAAPI.h SOURCES += vaapi/SurfaceInteropVAAPI.cpp #} LIBS *= -lva -lX11 #dynamic load va-glx va-x11 using dllapi. -lX11: used by tfp } config_libcedarv { DEFINES *= QTAV_HAVE_CEDARV=1 QMAKE_CXXFLAGS *= -march=armv7-a # Can not use NEON_SOURCE because it can not work with moc SOURCES += codec/video/VideoDecoderCedarv.cpp !config_simd: CONFIG *= simd #addSimdCompiler xxx_ASM CONFIG += no_clang_integrated_as #see qtbase/src/gui/painting/painting.pri. add -fno-integrated-as from simd.prf NEON_ASM += codec/video/tiled_yuv.S #from libvdpau-sunxi LIBS += -lvecore -lcedarv OTHER_FILES += $$NEON_ASM } mac { HEADERS *= codec/video/SurfaceInteropCV.h SOURCES *= codec/video/SurfaceInteropCV.cpp ios { OBJECTIVE_SOURCES *= codec/video/SurfaceInteropCVOpenGLES.mm } else { CONFIG += config_vda SOURCES *= codec/video/SurfaceInteropIOSurface.cpp #SOURCES *= codec/video/SurfaceInteropCVOpenGL.cpp LIBS += -framework IOSurface } LIBS += -framework CoreVideo -framework CoreFoundation } config_vda { DEFINES *= QTAV_HAVE_VDA=1 SOURCES += codec/video/VideoDecoderVDA.cpp LIBS += -framework VideoDecodeAcceleration } config_videotoolbox { DEFINES *= QTAV_HAVE_VIDEOTOOLBOX=1 SOURCES *= codec/video/VideoDecoderVideoToolbox.cpp LIBS += -framework CoreMedia -framework VideoToolbox } config_gl|config_opengl { contains(QT_CONFIG, egl) { DEFINES *= QTAV_HAVE_QT_EGL=1 #if a platform plugin depends on egl (for example, eglfs), egl is defined } OTHER_FILES += shaders/planar.f.glsl shaders/rgb.f.glsl SDK_HEADERS *= \ QtAV/Geometry.h \ QtAV/GeometryRenderer.h \ QtAV/GLSLFilter.h \ QtAV/OpenGLRendererBase.h \ QtAV/OpenGLTypes.h \ QtAV/OpenGLVideo.h \ QtAV/ConvolutionShader.h \ QtAV/VideoShaderObject.h \ QtAV/VideoShader.h SDK_PRIVATE_HEADERS = \ QtAV/private/OpenGLRendererBase_p.h HEADERS *= \ opengl/gl_api.h \ opengl/OpenGLHelper.h \ opengl/SubImagesGeometry.h \ opengl/SubImagesRenderer.h \ opengl/ShaderManager.h SOURCES *= \ filter/GLSLFilter.cpp \ output/video/OpenGLRendererBase.cpp \ opengl/gl_api.cpp \ opengl/OpenGLTypes.cpp \ opengl/Geometry.cpp \ opengl/GeometryRenderer.cpp \ opengl/SubImagesGeometry.cpp \ opengl/SubImagesRenderer.cpp \ opengl/OpenGLVideo.cpp \ opengl/VideoShaderObject.cpp \ opengl/VideoShader.cpp \ opengl/ShaderManager.cpp \ opengl/ConvolutionShader.cpp \ opengl/OpenGLHelper.cpp } config_openglwindow { SDK_HEADERS *= QtAV/OpenGLWindowRenderer.h SOURCES *= output/video/OpenGLWindowRenderer.cpp } config_libass { #link against libass instead of dynamic load !capi|winrt|android|ios|config_libass_link { LIBS += -lass #-lfribidi -lfontconfig -lxml2 -lfreetype -lharfbuzz -lz DEFINES += CAPI_LINK_ASS } DEFINES *= QTAV_HAVE_LIBASS=1 HEADERS *= capi/ass_api.h SOURCES *= capi/ass_api.cpp SOURCES *= subtitle/SubtitleProcessorLibASS.cpp } # mac is -FQTDIR we need -LQTDIR LIBS *= -L$$[QT_INSTALL_LIBS] -lavcodec -lavformat -lswscale -lavutil win32 { HEADERS *= utils/DirectXHelper.h SOURCES *= utils/DirectXHelper.cpp #dynamicgl: __impl__GetDC __impl_ReleaseDC __impl_GetDesktopWindow !winrt:LIBS += -luser32 } winrt { SOURCES *= io/WinRTIO.cpp LIBS *= -lshcore } # compat with old system # use old libva.so to link against glibc_compat: *linux*: LIBS += -lrt # do not use clock_gettime in libc, GLIBC_2.17 is not available on old system static_ffmpeg { # libs needed by mac static ffmpeg. corefoundation: vda, avdevice. coca: vf_coreimage mac|ios: LIBS += -liconv -lbz2 -llzma -lz -framework CoreFoundation -framework Security # -framework Cocoa Cocoa is not available on ios10 win32: LIBS *= -lws2_32 -lstrmiids -lvfw32 -luuid !mac:*g++* { LIBS *= -lz QMAKE_LFLAGS *= -Wl,-Bsymbolic #link to static lib, see http://ffmpeg.org/platform.html } } SOURCES += \ AVCompat.cpp \ QtAV_Global.cpp \ subtitle/SubImage.cpp \ subtitle/CharsetDetector.cpp \ subtitle/PlainText.cpp \ subtitle/PlayerSubtitle.cpp \ subtitle/Subtitle.cpp \ subtitle/SubtitleProcessor.cpp \ subtitle/SubtitleProcessorFFmpeg.cpp \ utils/GPUMemCopy.cpp \ utils/Logger.cpp \ AudioThread.cpp \ utils/internal.cpp \ AVThread.cpp \ AudioFormat.cpp \ AudioFrame.cpp \ AudioResampler.cpp \ AudioResamplerTemplate.cpp \ codec/audio/AudioDecoder.cpp \ codec/audio/AudioDecoderFFmpeg.cpp \ codec/audio/AudioEncoder.cpp \ codec/audio/AudioEncoderFFmpeg.cpp \ codec/AVDecoder.cpp \ codec/AVEncoder.cpp \ AVMuxer.cpp \ AVDemuxer.cpp \ AVDemuxThread.cpp \ ColorTransform.cpp \ Frame.cpp \ FrameReader.cpp \ filter/Filter.cpp \ filter/FilterContext.cpp \ filter/FilterManager.cpp \ filter/LibAVFilter.cpp \ filter/SubtitleFilter.cpp \ filter/EncodeFilter.cpp \ ImageConverter.cpp \ ImageConverterFF.cpp \ Packet.cpp \ PacketBuffer.cpp \ AVError.cpp \ AVPlayer.cpp \ AVPlayerPrivate.cpp \ AVTranscoder.cpp \ AVClock.cpp \ VideoCapture.cpp \ VideoFormat.cpp \ VideoFrame.cpp \ io/MediaIO.cpp \ io/QIODeviceIO.cpp \ output/audio/AudioOutput.cpp \ output/audio/AudioOutputBackend.cpp \ output/audio/AudioOutputNull.cpp \ output/video/VideoRenderer.cpp \ output/video/VideoOutput.cpp \ output/video/QPainterRenderer.cpp \ output/AVOutput.cpp \ output/OutputSet.cpp \ Statistics.cpp \ codec/video/VideoDecoder.cpp \ codec/video/VideoDecoderFFmpegBase.cpp \ codec/video/VideoDecoderFFmpeg.cpp \ codec/video/VideoDecoderFFmpegHW.cpp \ codec/video/VideoEncoder.cpp \ codec/video/VideoEncoderFFmpeg.cpp \ VideoThread.cpp \ VideoFrameExtractor.cpp SDK_HEADERS *= \ QtAV/QtAV \ QtAV/QtAV.h \ QtAV/dptr.h \ QtAV/QtAV_Global.h \ QtAV/AudioResampler.h \ QtAV/AudioDecoder.h \ QtAV/AudioEncoder.h \ QtAV/AudioFormat.h \ QtAV/AudioFrame.h \ QtAV/AudioOutput.h \ QtAV/AVDecoder.h \ QtAV/AVEncoder.h \ QtAV/AVDemuxer.h \ QtAV/AVMuxer.h \ QtAV/Filter.h \ QtAV/FilterContext.h \ QtAV/LibAVFilter.h \ QtAV/EncodeFilter.h \ QtAV/Frame.h \ QtAV/FrameReader.h \ QtAV/QPainterRenderer.h \ QtAV/Packet.h \ QtAV/AVError.h \ QtAV/AVPlayer.h \ QtAV/AVTranscoder.h \ QtAV/VideoCapture.h \ QtAV/VideoRenderer.h \ QtAV/VideoOutput.h \ QtAV/MediaIO.h \ QtAV/AVOutput.h \ QtAV/AVClock.h \ QtAV/VideoDecoder.h \ QtAV/VideoEncoder.h \ QtAV/VideoFormat.h \ QtAV/VideoFrame.h \ QtAV/VideoFrameExtractor.h \ QtAV/FactoryDefine.h \ QtAV/Statistics.h \ QtAV/SubImage.h \ QtAV/Subtitle.h \ QtAV/SubtitleFilter.h \ QtAV/SurfaceInterop.h \ QtAV/version.h SDK_PRIVATE_HEADERS *= \ QtAV/private/factory.h \ QtAV/private/mkid.h \ QtAV/private/prepost.h \ QtAV/private/singleton.h \ QtAV/private/PlayerSubtitle.h \ QtAV/private/SubtitleProcessor.h \ QtAV/private/AVCompat.h \ QtAV/private/AudioOutputBackend.h \ QtAV/private/AudioResampler_p.h \ QtAV/private/AVDecoder_p.h \ QtAV/private/AVEncoder_p.h \ QtAV/private/MediaIO_p.h \ QtAV/private/AVOutput_p.h \ QtAV/private/Filter_p.h \ QtAV/private/Frame_p.h \ QtAV/private/VideoShader_p.h \ QtAV/private/VideoRenderer_p.h \ QtAV/private/QPainterRenderer_p.h # QtAV/private/* may be used by developers to extend QtAV features without changing QtAV library # headers not in QtAV/ and it's subdirs are used only by QtAV internally HEADERS *= \ $$SDK_HEADERS \ $$SDK_PRIVATE_HEADERS \ AVPlayerPrivate.h \ AVDemuxThread.h \ AVThread.h \ AVThread_p.h \ AudioThread.h \ PacketBuffer.h \ VideoThread.h \ ImageConverter.h \ ImageConverter_p.h \ codec/video/VideoDecoderFFmpegBase.h \ codec/video/VideoDecoderFFmpegHW.h \ codec/video/VideoDecoderFFmpegHW_p.h \ filter/FilterManager.h \ subtitle/CharsetDetector.h \ subtitle/PlainText.h \ utils/BlockingQueue.h \ utils/GPUMemCopy.h \ utils/Logger.h \ utils/SharedPtr.h \ utils/ring.h \ utils/internal.h \ output/OutputSet.h \ ColorTransform.h # from mkspecs/features/qt_module.prf # OS X and iOS frameworks mac_framework { # from common.pri #QMAKE_FRAMEWORK_VERSION = 4.0 CONFIG += lib_bundle sliced_bundle qt_framework CONFIG -= qt_install_headers #no need to install these as well !debug_and_release|!build_all|CONFIG(release, debug|release) { FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$SDK_HEADERS FRAMEWORK_HEADERS.path = Headers # 5.4(beta) workaround for wrong include path # TODO: why can be found? qtAtLeast(5,3): FRAMEWORK_HEADERS.path = Headers/$$MODULE_INCNAME FRAMEWORK_PRIVATE_HEADERS.version = Versions FRAMEWORK_PRIVATE_HEADERS.files = $$SDK_PRIVATE_HEADERS FRAMEWORK_PRIVATE_HEADERS.path = Headers/$$VERSION/$$MODULE_INCNAME/private QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS FRAMEWORK_PRIVATE_HEADERS } } mac { CONFIG += explicitlib macx-g++ { QMAKE_CFLAGS += -fconstant-cfstrings QMAKE_CXXFLAGS += -fconstant-cfstrings } } unix:!mac:!cross_compile { icon.files = $$PWD/$${TARGET}.svg icon.path = /usr/share/icons/hicolor/scalable/apps INSTALLS += icon #debian DEB_INSTALL_LIST = .$$[QT_INSTALL_LIBS]/libQt*AV.so.* libqtav.target = libqtav.install libqtav.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${libqtav.target} QMAKE_EXTRA_TARGETS += libqtav target.depends *= $${libqtav.target} DEB_INSTALL_LIST = $$join(SDK_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/, .$$[QT_INSTALL_HEADERS]/) DEB_INSTALL_LIST += .$$[QT_INSTALL_LIBS]/libQt*AV.prl .$$[QT_INSTALL_LIBS]/libQt*AV.so MKSPECS_DIR=$$[QT_HOST_DATA]/mkspecs # we only build deb for qt5, so QT_HOST_DATA is fine. qt4 can use $$[QMAKE_MKSPECS] DEB_INSTALL_LIST += .$${MKSPECS_DIR}/features/av.prf .$${MKSPECS_DIR}/modules/qt_lib_av.pri qtav_dev.target = qtav-dev.install qtav_dev.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${qtav_dev.target} QMAKE_EXTRA_TARGETS += qtav_dev target.depends *= $${qtav_dev.target} DEB_INSTALL_LIST = $$join(SDK_PRIVATE_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/QtAV/*/, .$$[QT_INSTALL_HEADERS]/QtAV/*/) DEB_INSTALL_LIST += .$${MKSPECS_DIR}/modules/qt_lib_av_private.pri qtav_private_dev.target = qtav-private-dev.install qtav_private_dev.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${qtav_private_dev.target} QMAKE_EXTRA_TARGETS += qtav_private_dev target.depends *= $${qtav_private_dev.target} greaterThan(QT_MAJOR_VERSION, 4) { qtav_dev_links.target = qtav-dev.links qtav_dev_links.commands = echo \"$$[QT_INSTALL_LIBS]/libQtAV.so $$[QT_INSTALL_LIBS]/libQt$${QT_MAJOR_VERSION}AV.so\" >$$PROJECTROOT/debian/$${qtav_dev_links.target} QMAKE_EXTRA_TARGETS *= qtav_dev_links target.depends *= $${qtav_dev_links.target} } #Qt>=5 } #debian MODULE_INCNAME = QtAV MODULE_VERSION = $$VERSION #use Qt version. limited by qmake # windows: Qt5AV.dll, not Qt1AV.dll !mac_framework: MODULE_VERSION = $${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} !contains(QMAKE_HOST.os, Windows):include($$PROJECTROOT/deploy.pri) QtAV-1.12.0/src/opengl/000077500000000000000000000000001312235004300144525ustar00rootroot00000000000000QtAV-1.12.0/src/opengl/ConvolutionShader.cpp000066400000000000000000000067131312235004300206330ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/ConvolutionShader.h" #include "QtAV/private/VideoShader_p.h" namespace QtAV { class ConvolutionShaderPrivate : public VideoShaderPrivate { public: ConvolutionShaderPrivate() : VideoShaderPrivate() , u_Kernel(-1) , radius(1) { kernel.resize((2*radius+1)*(2*radius+1)); updateShaderCode(); } void updateShaderCode() { const int ks = (2*radius+1)*(2*radius+1); header = QStringLiteral("uniform float u_Kernel[%1];").arg(ks).toUtf8(); QString s = QStringLiteral("vec4 sample2d(sampler2D tex, vec2 pos, int p) { vec4 c = vec4(0.0);"); const int kd = 2*radius+1; for (int i = 0; i < ks; ++i) { const int x = i % kd - radius; const int y = i / kd - radius; s += QStringLiteral("c += texture(tex, pos + u_texelSize[p]*vec2(%1.0,%2.0))*u_Kernel[%3];") .arg(x).arg(y).arg(i); } s += "c.a = texture(tex, pos).a;" "return c;}\n"; sample_func = s.toUtf8(); } int u_Kernel; int radius; QVector kernel; QByteArray header, sample_func; }; ConvolutionShader::ConvolutionShader() : VideoShader(*new ConvolutionShaderPrivate()) {} ConvolutionShader::ConvolutionShader(ConvolutionShaderPrivate &d) : VideoShader(d) {} int ConvolutionShader::kernelRadius() const { return d_func().radius; } void ConvolutionShader::setKernelRadius(int value) { DPTR_D(ConvolutionShader); if (d.radius == value) return; d.radius = value; d.kernel.resize(kernelSize()); d.updateShaderCode(); rebuildLater(); } int ConvolutionShader::kernelSize() const { return (2*kernelRadius() + 1)*(2*kernelRadius() + 1); } const char* ConvolutionShader::userShaderHeader(QOpenGLShader::ShaderType t) const { if (t == QOpenGLShader::Vertex) return 0; return kernelUniformHeader().constData(); } const char* ConvolutionShader::userSample() const { return kernelSample().constData(); } bool ConvolutionShader::setUserUniformValues() { setKernelUniformValue(); return true; } const QByteArray& ConvolutionShader::kernelUniformHeader() const { return d_func().header; } const QByteArray& ConvolutionShader::kernelSample() const { return d_func().sample_func; } void ConvolutionShader::setKernelUniformValue() { program()->setUniformValueArray("u_Kernel", kernel(), kernelSize(), 1); } } //namespace QtAV QtAV-1.12.0/src/opengl/Geometry.cpp000066400000000000000000000257161312235004300167640ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Geometry.h" #include #include namespace QtAV { Attribute::Attribute(DataType type, int tupleSize, int offset, bool normalize) : m_normalize(normalize) , m_type(type) , m_tupleSize(tupleSize) , m_offset(offset) {} Attribute::Attribute(const QByteArray& name, DataType type, int tupleSize, int offset, bool normalize) : m_normalize(normalize) , m_type(type) , m_tupleSize(tupleSize) , m_offset(offset) , m_name(name) {} #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const Attribute &a) { dbg.nospace() << "attribute: " << a.name(); dbg.nospace() << ", offset " << a.offset(); dbg.nospace() << ", tupleSize " << a.tupleSize(); dbg.nospace() << ", dataType " << a.type(); dbg.nospace() << ", normalize " << a.normalize(); return dbg.space(); } #endif Geometry::Geometry(int vertexCount, int indexCount, DataType indexType) : m_primitive(TriangleStrip) , m_itype(indexType) , m_vcount(vertexCount) , m_icount(indexCount) {} int Geometry::indexDataSize() const { switch (indexType()) { case TypeU16: return indexCount()*2; case TypeU32: return indexCount()*4; default: return indexCount(); } } void Geometry::setIndexValue(int index, int value) { switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); *(d+index) = value; } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); *(d+index) = value; } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); *(d+index) = value; } break; default: break; } } void Geometry::setIndexValue(int index, int v1, int v2, int v3) { // TODO: *(d + 3*index), *(d + 3*index + 1), *(d + 3*index + 2) switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v2; } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v3; } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v3; } break; default: break; } } void Geometry::dumpVertexData() { printf("vertex %p: ", m_vdata.constData()); const int n = stride()/sizeof(float); for (int i = 0; i < m_vcount; ++i) { const float* f = (const float*)(m_vdata.constData()+i*stride()); for (int j = 0; j < n; ++j) { printf("%f, ", *(f+j)); } printf(";"); } printf("\n");fflush(0); } void Geometry::dumpIndexData() { switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; default: break; } printf("\n");fflush(0); } void Geometry::allocate(int nbVertex, int nbIndex) { m_icount = nbIndex; m_vcount = nbVertex; m_vdata.resize(nbVertex*stride()); memset(m_vdata.data(), 0, m_vdata.size()); if (nbIndex <= 0) { m_idata.clear(); // required? return; } switch (indexType()) { case TypeU8: m_idata.resize(nbIndex*sizeof(quint8)); break; case TypeU16: m_idata.resize(nbIndex*sizeof(quint16)); break; case TypeU32: m_idata.resize(nbIndex*sizeof(quint32)); break; default: break; } memset((void*)m_idata.constData(), 0, m_idata.size()); } bool Geometry::compare(const Geometry *other) const { // this == other: attributes and stride can be different if (!other) return false; if (stride() != other->stride()) return false; return attributes() == other->attributes(); } TexturedGeometry::TexturedGeometry() : Geometry() , nb_tex(0) , geo_rect(-1, 1, 2, -2) // (-1, -1, 2, 2) flip y { setVertexCount(4); a = QVector() << Attribute(TypeF32, 2, 0) << Attribute(TypeF32, 2, 2*sizeof(float)) ; setTextureCount(1); } void TexturedGeometry::setTextureCount(int value) { if (value == nb_tex) return; texRect.resize(value); nb_tex = value; } int TexturedGeometry::textureCount() const { return nb_tex; } void TexturedGeometry::setPoint(int index, const QPointF &p, const QPointF &tp, int texIndex) { setGeometryPoint(index, p); setTexturePoint(index, tp, texIndex); } void TexturedGeometry::setGeometryPoint(int index, const QPointF &p) { float *v = (float*)(m_vdata.constData() + index*stride()); *v = p.x(); *(v+1) = p.y(); } void TexturedGeometry::setTexturePoint(int index, const QPointF &tp, int texIndex) { float *v = (float*)(m_vdata.constData() + index*stride() + (texIndex+1)*2*sizeof(float)); *v = tp.x(); *(v+1) = tp.y(); } void TexturedGeometry::setRect(const QRectF &r, const QRectF &tr, int texIndex) { setPoint(0, r.topLeft(), tr.topLeft(), texIndex); setPoint(1, r.bottomLeft(), tr.bottomLeft(), texIndex); switch (primitive()) { case TriangleStrip: setPoint(2, r.topRight(), tr.topRight(), texIndex); setPoint(3, r.bottomRight(), tr.bottomRight(), texIndex); break; case TriangleFan: setPoint(3, r.topRight(), tr.topRight(), texIndex); setPoint(2, r.bottomRight(), tr.bottomRight(), texIndex); break; case Triangles: break; default: break; } } void TexturedGeometry::setGeometryRect(const QRectF &r) { geo_rect = r; } void TexturedGeometry::setTextureRect(const QRectF &tr, int texIndex) { if (texRect.size() <= texIndex) texRect.resize(texIndex+1); texRect[texIndex] = tr; } const QVector& TexturedGeometry::attributes() const { return a; } void TexturedGeometry::create() { allocate(vertexCount()); if (a.size()-1 < textureCount()) { // the first is position for (int i = a.size()-1; i < textureCount(); ++i) a << Attribute(TypeF32, 2, int((i+1)* 2*sizeof(float))); } else { a.resize(textureCount() + 1); } setGeometryPoint(0, geo_rect.topLeft()); setGeometryPoint(1, geo_rect.bottomLeft()); switch (primitive()) { case TriangleStrip: setGeometryPoint(2, geo_rect.topRight()); setGeometryPoint(3, geo_rect.bottomRight()); break; case TriangleFan: setGeometryPoint(3, geo_rect.topRight()); setGeometryPoint(2, geo_rect.bottomRight()); break; case Triangles: break; default: break; } for (int i = 0; i < texRect.size(); ++i) { const QRectF tr = texRect[i]; setTexturePoint(0, tr.topLeft(), i); setTexturePoint(1, tr.bottomLeft(), i); switch (primitive()) { case TriangleStrip: setTexturePoint(2, tr.topRight(), i); setTexturePoint(3, tr.bottomRight(), i); break; case TriangleFan: setTexturePoint(3, tr.topRight(), i); setTexturePoint(2, tr.bottomRight(), i); break; case Triangles: break; default: break; } } } Sphere::Sphere() : TexturedGeometry() , r(1) { setPrimitive(Triangles); setResolution(128, 128); a = QVector() << Attribute(TypeF32, 3, 0) << Attribute(TypeF32, 2, 3*sizeof(float)) ; } void Sphere::setResolution(int w, int h) { ru = w; rv = h; setVertexCount((ru+1)*(rv+1)); } void Sphere::setRadius(float value) { r = value; } float Sphere::radius() const { return r; } void Sphere::create() { allocate(vertexCount(), ru*rv*3*2); // quads * 2 triangles, if (a.size()-1 < nb_tex) { // the first is position for (int i = a.size()-1; i < nb_tex; ++i) a << Attribute(TypeF32, 2, 3*sizeof(float) + int(i* 2*sizeof(float))); } else { a.resize(nb_tex + 1); } // TODO: use geo_rect? float *vd = (float*)m_vdata.constData(); const float dTheta = M_PI*2.0/float(ru); const float dPhi = M_PI/float(rv); //const float du = 1.0f/float(ru); //const float dv = 1.0f/float(rv); for (int lat = 0; lat <= rv; ++lat) { const float phi = M_PI_2 - float(lat)*dPhi; const float cosPhi = qCos(phi); const float sinPhi = qSin(phi); //const float v = 1.0f - float(lat)*dv; // flip y? for (int lon = 0; lon <= ru; ++lon) { const float theta = float(lon)*dTheta; const float cosTheta = qCos(theta); const float sinTheta = qSin(theta); //const float u = float(lon) * du; *vd++ = r*cosPhi*cosTheta;//2.0*float(lon)/float(ru) -1.0;// *vd++ = r*sinPhi;//2.0*float(lat)/float(rv)-1.0;// *vd++ = r*cosPhi*sinTheta; for (int i = 0; i < nb_tex; ++i) { *vd++ = texRect[i].x()+texRect[i].width()/float(ru) * float(lon); *vd++ = texRect[i].y()+texRect[i].height()/float(rv) * float(lat); } } } // create index data if (m_icount > 0) { int idx = 0; for (int lat = 0; lat < rv; ++lat) { for (int lon = 0; lon < ru; ++lon) { const int ring = lat*(ru+1) + lon; const int ringNext = ring + ru+1; setIndexValue(idx, ring, ringNext, ring+1); setIndexValue(idx+3, ringNext, ringNext+1, ring+1); idx += 6; } } } } } //namespace QtAV QtAV-1.12.0/src/opengl/GeometryRenderer.cpp000066400000000000000000000212451312235004300204440ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/GeometryRenderer.h" #include "opengl/OpenGLHelper.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #define QGLF(f) QOpenGLContext::currentContext()->functions()->f #else #define QGLF(f) QGLFunctions(NULL).f #endif namespace QtAV { GeometryRenderer::GeometryRenderer() : g(NULL) , features_(kVBO|kIBO|kVAO|kMapBuffer) , vbo_size(0) , ibo_size(0) , ibo(QOpenGLBuffer::IndexBuffer) , stride(0) { static bool disable_ibo = qgetenv("QTAV_NO_IBO").toInt() > 0; setFeature(kIBO, !disable_ibo); static bool disable_vbo = qgetenv("QTAV_NO_VBO").toInt() > 0; setFeature(kVBO, !disable_vbo); static bool disable_vao = qgetenv("QTAV_NO_VAO").toInt() > 0; setFeature(kVAO, !disable_vao); } void GeometryRenderer::setFeature(int f, bool on) { if (on) features_ |= f; else features_ ^= f; } void GeometryRenderer::setFeatures(int value) { features_ = value; } int GeometryRenderer::features() const { return features_; } int GeometryRenderer::actualFeatures() const { int f = 0; if (vbo.isCreated()) f |= kVBO; if (ibo.isCreated()) f |= kIBO; #if QT_VAO if (vao.isCreated()) f |= kVAO; #endif return f; } bool GeometryRenderer::testFeatures(int value) const { return !!(features() & value); } void GeometryRenderer::updateGeometry(Geometry *geo) { g = geo; if (!g) { ibo.destroy(); vbo.destroy(); #if QT_VAO vao.destroy(); #endif vbo_size = 0; ibo_size = 0; return; } static int support_map = -1; if (support_map < 0) { static const char* ext[] = { "GL_OES_mapbuffer", NULL}; if (OpenGLHelper::isOpenGLES()) { support_map = QOpenGLContext::currentContext()->format().majorVersion() > 2 || OpenGLHelper::hasExtension(ext); } else { support_map = 1; } } if (testFeatures(kIBO) && !ibo.isCreated()) { if (g->indexCount() > 0) { qDebug("creating IBO..."); if (!ibo.create()) qDebug("IBO create error"); } } if (ibo.isCreated()) { ibo.bind(); const int bs = g->indexDataSize(); if (bs == ibo_size) { void * p = NULL; if (support_map && testFeatures(kMapBuffer)) p = ibo.map(QOpenGLBuffer::WriteOnly); if (p) { memcpy(p, g->constIndexData(), bs); ibo.unmap(); } else { ibo.write(0, g->constIndexData(), bs); } } else { ibo.allocate(g->indexData(), bs); // TODO: allocate NULL and then map or BufferSubData? ibo_size = bs; } ibo.release(); } if (testFeatures(kVBO) && !vbo.isCreated()) { qDebug("creating VBO..."); if (!vbo.create()) qWarning("VBO create error"); } if (vbo.isCreated()) { vbo.bind(); const int bs = g->vertexCount()*g->stride(); /* Notes from https://www.opengl.org/sdk/docs/man/html/glBufferSubData.xhtml When replacing the entire data store, consider using glBufferSubData rather than completely recreating the data store with glBufferData. This avoids the cost of reallocating the data store. */ if (bs == vbo_size) { // vbo.size() error 0x501 on rpi, and query gl value can be slow void* p = NULL; if (support_map && testFeatures(kMapBuffer)) p = vbo.map(QOpenGLBuffer::WriteOnly); if (p) { memcpy(p, g->constVertexData(), bs); vbo.unmap(); } else { vbo.write(0, g->constVertexData(), bs); vbo_size = bs; } } else { vbo.allocate(g->vertexData(), bs); } vbo.release(); } #if QT_VAO if (stride == g->stride() && attrib == g->attributes()) return; stride = g->stride(); attrib = g->attributes(); if (testFeatures(kVAO) && !vao.isCreated()) { qDebug("creating VAO..."); if (!vao.create()) qDebug("VAO create error"); } qDebug("vao updated"); if (vao.isCreated()) // can not use vao binder because it will create a vao if necessary vao.bind(); // can set data before vao bind if (!vao.isCreated()) return; qDebug("geometry attributes changed, rebind vao..."); // call once is enough if no feature and no geometry attribute is changed if (vbo.isCreated()) { vbo.bind(); for (int an = 0; an < g->attributes().size(); ++an) { // FIXME: assume bind order is 0,1,2... const Attribute& a = g->attributes().at(an); QGLF(glVertexAttribPointer(an, a.tupleSize(), a.type(), a.normalize(), g->stride(), reinterpret_cast(qptrdiff(a.offset())))); //TODO: in setActiveShader QGLF(glEnableVertexAttribArray(an)); } vbo.release(); // unbind after vao unbind? http://www.zwqxin.com/archives/opengl/vao-and-vbo-stuff.html } // TODO: bind pointers if vbo is disabled // bind ibo to vao thus no bind is required later if (ibo.isCreated())// if not bind here, glDrawElements(...,NULL) crashes and must use ibo data ptr, why? ibo.bind(); vao.release(); if (ibo.isCreated()) ibo.release(); #endif qDebug("geometry updated"); } void GeometryRenderer::bindBuffers() { bool bind_vbo = vbo.isCreated(); bool bind_ibo = ibo.isCreated(); bool setv_skip = false; #if QT_VAO if (vao.isCreated()) { vao.bind(); // vbo, ibo is ok now setv_skip = bind_vbo; bind_vbo = false; bind_ibo = false; } #endif //qDebug("bind ibo: %d vbo: %d; set v: %d", bind_ibo, bind_vbo, !setv_skip); if (bind_ibo) ibo.bind(); // no vbo: set vertex attributes // has vbo, no vao: bind vbo & set vertex attributes // has vbo, has vao: skip if (setv_skip) return; if (!g) return; const char* vdata = static_cast(g->vertexData()); if (bind_vbo) { vbo.bind(); vdata = NULL; } for (int an = 0; an < g->attributes().size(); ++an) { const Attribute& a = g->attributes().at(an); QGLF(glVertexAttribPointer(an, a.tupleSize(), a.type(), a.normalize(), g->stride(), vdata + a.offset())); QGLF(glEnableVertexAttribArray(an)); //TODO: in setActiveShader } } void GeometryRenderer::unbindBuffers() { bool unbind_vbo = vbo.isCreated(); bool unbind_ibo = ibo.isCreated(); bool unsetv_skip = false; #if QT_VAO if (vao.isCreated()) { vao.release(); unsetv_skip = unbind_vbo; unbind_vbo = false; unbind_ibo = false; } #endif //QT_VAO //qDebug("unbind ibo: %d vbo: %d; unset v: %d", unbind_ibo, unbind_vbo, !unsetv_skip); if (unbind_ibo) ibo.release(); // release vbo. qpainter is affected if vbo is bound if (unbind_vbo) vbo.release(); // no vbo: disable vertex attributes // has vbo, no vao: unbind vbo & set vertex attributes // has vbo, has vao: skip if (unsetv_skip) return; if (!g) return; for (int an = 0; an < g->attributes().size(); ++an) { QGLF(glDisableVertexAttribArray(an)); } } void GeometryRenderer::render() { if (!g) return; bindBuffers(); if (g->indexCount() > 0) { DYGL(glDrawElements(g->primitive(), g->indexCount(), g->indexType(), ibo.isCreated() ? NULL : g->indexData())); // null: data in vao or ibo. not null: data in memory } else { DYGL(glDrawArrays(g->primitive(), 0, g->vertexCount())); } unbindBuffers(); } } //namespace QtAV QtAV-1.12.0/src/opengl/OpenGLHelper.cpp000066400000000000000000000671131312235004300174520ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "OpenGLHelper.h" #include //strstr #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) #include #endif #else #include #endif #ifdef QT_OPENGL_DYNAMIC #include #endif #if QTAV_HAVE(EGL_CAPI) // && QTAV_HAVE(QT_EGL) //make sure no crash if no egl library #define EGL_CAPI_NS #include "capi/egl_api.h" #endif //QTAV_HAVE(EGL_CAPI) #include "utils/Logger.h" #define BUG_GLES3_ANDROID 1 //FIXME: N7 android6 gles3 displays red images, only rgb32 is correct namespace QtAV { namespace OpenGLHelper { // glGetTexParameteriv is supported by es2 does not support GL_TEXTURE_INTERNAL_FORMAT. /// 16bit (R16 e.g.) texture does not support >8bit a BE channel, fallback to 2 channel texture int depth16BitTexture() { static int depth = qgetenv("QTAV_TEXTURE16_DEPTH").toInt() == 8 ? 8 : 16;//8 ? 8 : 16; return depth; } bool useDeprecatedFormats() { static bool v = qgetenv("QTAV_GL_DEPRECATED").toInt() == 1; return v; } QString removeComments(const QString &code) { QString c(code); c.remove(QRegExp(QStringLiteral("(/\\*([^*]|(\\*+[^*/]))*\\*+/)|(//[^\r^\n]*)"))); return c; } /// current shader works fine for gles 2~3 only with commonShaderHeader(). It's mainly for desktop core profile static QByteArray commonShaderHeader(QOpenGLShader::ShaderType type) { // TODO: check useDeprecatedFormats() or useDeprecated()? QByteArray h; if (isOpenGLES()) { h += "precision mediump int;\n" "precision mediump float;\n" ; } else { h += "#define highp\n" "#define mediump\n" "#define lowp\n" ; } if (type == QOpenGLShader::Fragment) { // >=1.30: texture(sampler2DRect,...). 'texture' is defined in header // we can't check GLSLVersion() here because it the actually version used can be defined by "#version" h += "#if __VERSION__ < 130\n" "#define texture texture2D\n" "#else\n" "#define texture2D texture\n" "#endif // < 130\n" ; } return h; } QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type) { #if BUG_GLES3_ANDROID if (isOpenGLES()) return commonShaderHeader(type); #endif //BUG_GLES3_ANDROID QByteArray h; // #version directive must occur in a compilation unit before anything else, except for comments and white spaces. Default is 100 if not set h.append("#version ").append(QByteArray::number(GLSLVersion())); if (isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2) h += " es"; h += "\n"; h += commonShaderHeader(type); if (GLSLVersion() >= 130) { // gl(es) 3 if (type == QOpenGLShader::Vertex) { h += "#define attribute in\n" "#define varying out\n" ; } else if (type == QOpenGLShader::Fragment) { h += "#define varying in\n" "#define gl_FragColor out_color\n" //can not starts with 'gl_' "out vec4 gl_FragColor;\n" ; } } return h; } int GLSLVersion() { static int v = -1; if (v >= 0) return v; if (!QOpenGLContext::currentContext()) { qWarning("%s: current context is null", __FUNCTION__); return 0; } const char* vs = (const char*)DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION)); int major = 0, minor = 0; // es: "OpenGL ES GLSL ES 1.00 (ANGLE 2.1.99...)" can use ""%*[ a-zA-Z] %d.%d" in sscanf, desktop: "2.1" //QRegExp rx("(\\d+)\\.(\\d+)"); if (strncmp(vs, "OpenGL ES GLSL ES ", 18) == 0) vs += 18; if (sscanf(vs, "%d.%d", &major, &minor) == 2) { v = major * 100 + minor; } else { qWarning("Failed to detect glsl version using GL_SHADING_LANGUAGE_VERSION!"); v = 110; if (isOpenGLES()) v = QOpenGLContext::currentContext()->format().majorVersion() >= 3 ? 300 : 100; } return v; } bool isEGL() { static int is_egl = -1; if (is_egl >= 0) return !!is_egl; #ifdef Q_OS_IOS is_egl = 0; return false; #endif if (isOpenGLES()) { //TODO: ios has no egl is_egl = 1; return true; } // angle has no QTAV_HAVE(QT_EGL). TODO: no assert in capi, or check egl loaded #if QTAV_HAVE(EGL_CAPI) //&& QTAV_HAVE(QT_EGL) //make sure no crash if no egl library if (!egl::api().loaded()) { //load twice, here and ns func call is_egl = 0; return false; } if (eglGetCurrentDisplay() != EGL_NO_DISPLAY) { //egl can be loaded but glx is used is_egl = 1; return true; } #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) if (QGuiApplication::platformName().contains(QLatin1String("egl"))) { is_egl = 1; return true; } #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) if (QGuiApplication::platformName().contains(QLatin1String("xcb"))) { is_egl = qgetenv("QT_XCB_GL_INTEGRATION") == "xcb_egl"; qDebug("xcb_egl=%d", is_egl); return !!is_egl; } #endif //5.5.0 #endif // we can use QOpenGLContext::currentContext()->nativeHandle().value(). but gl context is required if (QOpenGLContext::currentContext()) is_egl = 0; return false; } bool isOpenGLES() { #ifdef QT_OPENGL_DYNAMIC QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx) return ctx->isOpenGLES(); if (qstrcmp(qApp->metaObject()->className(), "QCoreApplication") == 0) // QGuiApplication is required by QOpenGLContext::openGLModuleType return false; // desktop openGLModuleType() can create es compatible context, so prefer QOpenGLContext::isOpenGLES(). // qApp->testAttribute(Qt::AA_UseOpenGLES) is what user requested, but not the result can be different. reproduce: dygl set AA_ShareOpenGLContexts|AA_UseOpenGLES, fallback to desktop (why?) return QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL; #endif //QT_OPENGL_DYNAMIC #ifdef QT_OPENGL_ES_2 return true; #endif //QT_OPENGL_ES_2 #if defined(QT_OPENGL_ES_2_ANGLE_STATIC) || defined(QT_OPENGL_ES_2_ANGLE) return true; #endif //QT_OPENGL_ES_2_ANGLE_STATIC return false; } bool hasExtensionEGL(const char *exts[]) { if (!isEGL()) return false; #if QTAV_HAVE(EGL_CAPI) static QList supported; if (supported.isEmpty()) { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, NULL, NULL); supported = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' '); } static bool print_exts = true; if (print_exts) { print_exts = false; qDebug() << "EGL extensions: " << supported; } for (int i = 0; exts[i]; ++i) { if (supported.contains(QByteArray(exts[i]))) return true; } #endif return false; } bool hasExtension(const char *exts[]) { const QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!ctx) { qWarning("no gl context for hasExtension"); return false; } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) const char *ext = (const char*)glGetString(GL_EXTENSIONS); if (!ext) return false; #endif for (int i = 0; exts[i]; ++i) { #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) if (ctx->hasExtension(exts[i])) #else if (strstr(ext, exts[i])) #endif return true; } return false; } bool isPBOSupported() { // check pbo support static bool support = false; static bool pbo_checked = false; if (pbo_checked) return support; const QOpenGLContext *ctx = QOpenGLContext::currentContext(); Q_ASSERT(ctx); if (!ctx) return false; const char* exts[] = { "GL_ARB_pixel_buffer_object", "GL_EXT_pixel_buffer_object", "GL_NV_pixel_buffer_object", //OpenGL ES NULL }; support = hasExtension(exts); if (QOpenGLContext::currentContext()->format().majorVersion() > 2) support = true; pbo_checked = true; return support; } typedef struct { GLint internal_format; GLenum format; GLenum type; } gl_param_t; // es formats: ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA // es types: UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1 (NO UNSIGNED_SHORT) /*! c: number of channels(components) in the plane b: componet size result is gl_param_compat[(c-1)+4*(b-1)] */ static const gl_param_t gl_param_compat[] = { // it's legacy { GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, { GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, { GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, { GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, //2 x 8 fallback to ra {0,0,0}, }; static const gl_param_t gl_param_3r16[] = { {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 {GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // 1 x 16 {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // 2 x 16 {GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, // 3 x 16 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // 4 x 16 {0,0,0}, }; static const gl_param_t gl_param_desktop_fallback[] = { {GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 {GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 {GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 {0,0,0}, }; static const gl_param_t gl_param_es3rg8[] = { {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 {GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg {0,0,0}, }; //https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt // supported by ANGLE+D3D11 static const gl_param_t gl_param_es2rg[] = { {GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 //es2: GL_EXT_texture_rg. R8, RG8 are for render buffer {GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 {GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg {0,0,0}, }; bool test_gl_param(const gl_param_t& gp, bool* has_16 = 0) { if (!QOpenGLContext::currentContext()) { qWarning("%s: current context is null", __FUNCTION__); return false; } GLuint tex; DYGL(glGenTextures(1, &tex)); DYGL(glBindTexture(GL_TEXTURE_2D, tex)); while (DYGL(glGetError()) != GL_NO_ERROR) {} DYGL(glTexImage2D(GL_TEXTURE_2D, 0, gp.internal_format, 64, 64, 0, gp.format, gp.type, NULL)); if (DYGL(glGetError()) != GL_NO_ERROR) { DYGL(glDeleteTextures(1, &tex)); return false; } if (!gl().GetTexLevelParameteriv) { qDebug("Do not support glGetTexLevelParameteriv. test_gl_param returns false"); DYGL(glDeleteTextures(1, &tex)); return false; } GLint param = 0; //GL_PROXY_TEXTURE_2D and no glGenTextures? #ifndef GL_TEXTURE_INTERNAL_FORMAT //only in desktop #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #endif gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, ¶m); if (param != gp.internal_format) { qDebug("Do not support texture internal format: %#x (result %#x)", gp.internal_format, param); DYGL(glDeleteTextures(1, &tex)); return false; } if (!has_16) { DYGL(glDeleteTextures(1, &tex)); return true; } *has_16 = false; GLenum pname = 0; #ifndef GL_TEXTURE_RED_SIZE #define GL_TEXTURE_RED_SIZE 0x805C #endif #ifndef GL_TEXTURE_LUMINANCE_SIZE #define GL_TEXTURE_LUMINANCE_SIZE 0x8060 #endif switch (gp.format) { case GL_RED: pname = GL_TEXTURE_RED_SIZE; break; case GL_LUMINANCE: pname = GL_TEXTURE_LUMINANCE_SIZE; break; } param = 0; if (pname) gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, ¶m); if (param) { qDebug("16 bit texture depth: %d.\n", (int)param); *has_16 = (int)param == 16; } DYGL(glDeleteTextures(1, &tex)); return true; } bool hasRG() { static int has_rg = -1; if (has_rg >= 0) return !!has_rg; qDebug("check gl3 rg: %#X", gl_param_3r16[1].internal_format); if (test_gl_param(gl_param_3r16[1])) { has_rg = 1; return true; } qDebug("check es3 rg: %#X", gl_param_es3rg8[1].internal_format); if (test_gl_param(gl_param_es3rg8[1])) { has_rg = 1; return true; } qDebug("check GL_EXT_texture_rg"); static const char* ext[] = { "GL_EXT_texture_rg", 0}; //RED, RG, R8, RG8 if (hasExtension(ext)) { qDebug("has extension GL_EXT_texture_rg"); has_rg = 1; return true; } qDebug("check gl es>=3 rg"); if (QOpenGLContext::currentContext()) has_rg = isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2; // Mesa GLES3 does not support (from qt) return has_rg; } static int has_16_tex = -1; static const gl_param_t* get_gl_param() { if (!QOpenGLContext::currentContext()) { qWarning("%s: current context is null", __FUNCTION__); return gl_param_compat; } static gl_param_t* gp = 0; if (gp) return gp; bool has_16 = false; // [4] is always available if (test_gl_param(gl_param_3r16[4], &has_16)) { if (has_16 && depth16BitTexture() == 16) gp = (gl_param_t*)gl_param_3r16; else gp = (gl_param_t*)gl_param_desktop_fallback; has_16_tex = has_16; if (!useDeprecatedFormats()) { qDebug("using gl_param_%s", gp == gl_param_3r16? "3r16" : "desktop_fallback"); return gp; } } else if (test_gl_param(gl_param_es3rg8[4], &has_16)) { //3.0 will fail because no glGetTexLevelParameteriv gp = (gl_param_t*)gl_param_es3rg8; has_16_tex = has_16; if (!useDeprecatedFormats()) { qDebug("using gl_param_es3rg8"); return gp; } } else if (isOpenGLES()) { if (QOpenGLContext::currentContext()->format().majorVersion() > 2) gp = (gl_param_t*)gl_param_es3rg8; //for 3.0 else if (hasRG()) gp = (gl_param_t*)gl_param_es2rg; has_16_tex = has_16; if (gp && !useDeprecatedFormats()) { qDebug("using gl_param_%s", gp == gl_param_es3rg8 ? "es3rg8" : "es2rg"); return gp; } } qDebug("fallback to gl_param_compat"); gp = (gl_param_t*)gl_param_compat; has_16_tex = false; return gp; } bool has16BitTexture() { if (has_16_tex >= 0) return !!has_16_tex; if (!QOpenGLContext::currentContext()) { qWarning("%s: current context is null", __FUNCTION__); return false; } get_gl_param(); return !!has_16_tex; } typedef struct { VideoFormat::PixelFormat pixfmt; quint8 channels[4]; } reorder_t; // use with gl_param_compat static const reorder_t gl_channel_maps[] = { { VideoFormat::Format_ARGB32, {1, 2, 3, 0}}, { VideoFormat::Format_ABGR32, {3, 2, 1, 0}}, // R->gl.?(a)->R { VideoFormat::Format_BGR24, {2, 1, 0, 3}}, { VideoFormat::Format_BGR565, {2, 1, 0, 3}}, { VideoFormat::Format_BGRA32, {2, 1, 0, 3}}, { VideoFormat::Format_BGR32, {2, 1, 0, 3}}, { VideoFormat::Format_BGR48LE,{2, 1, 0, 3}}, { VideoFormat::Format_BGR48BE,{2, 1, 0, 3}}, { VideoFormat::Format_BGR48, {2, 1, 0, 3}}, { VideoFormat::Format_BGR555, {2, 1, 0, 3}}, // TODO: rgb444le/be etc { VideoFormat::Format_Invalid,{1, 2, 3}} }; static QMatrix4x4 channelMap(const VideoFormat& fmt) { if (fmt.isPlanar()) //currently only for planar return QMatrix4x4(); switch (fmt.pixelFormat()) { case VideoFormat::Format_UYVY: return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); case VideoFormat::Format_YUYV: return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); case VideoFormat::Format_VYUY: return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); case VideoFormat::Format_YVYU: return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); case VideoFormat::Format_VYU: return QMatrix4x4(0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); default: break; } const quint8 *channels = NULL;//{ 0, 1, 2, 3}; for (int i = 0; gl_channel_maps[i].pixfmt != VideoFormat::Format_Invalid; ++i) { if (gl_channel_maps[i].pixfmt == fmt.pixelFormat()) { channels = gl_channel_maps[i].channels; break; } } QMatrix4x4 m; if (!channels) return m; m.fill(0); for (int i = 0; i < 4; ++i) { m(i, channels[i]) = 1; } qDebug() << m; return m; } //template Q_CONSTEXPR size_t array_size(const T (&)[N]) { return N;} //does not support local type if no c++11 #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat) { typedef struct fmt_entry { VideoFormat::PixelFormat pixfmt; GLint internal_format; GLenum format; GLenum type; } fmt_entry; static const fmt_entry pixfmt_to_gles[] = { {VideoFormat::Format_BGRA32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //tested for angle {VideoFormat::Format_RGB32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_Invalid, 0, 0, 0} }; Q_UNUSED(pixfmt_to_gles); static const fmt_entry pixfmt_to_desktop[] = { {VideoFormat::Format_BGRA32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //bgra bgra works on win but not macOS {VideoFormat::Format_RGB32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //FIXMEL endian check //{VideoFormat::Format_BGRA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, //{2,1,0,3} //{VideoFormat::Format_BGR24, GL_RGB, GL_BGR, GL_UNSIGNED_BYTE }, //{0,1,2,3} #ifdef GL_UNSIGNED_SHORT_5_6_5_REV {VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // es error, use channel map #endif #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV {VideoFormat::Format_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, #endif #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV {VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, #endif // TODO: BE formats not implemeted {VideoFormat::Format_RGB48, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT }, //TODO: they are not work for ANGLE, and rgb16 works on desktop gl, so remove these lines to use rgb16? {VideoFormat::Format_RGB48LE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT }, {VideoFormat::Format_RGB48BE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT }, {VideoFormat::Format_BGR48, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, //RGB16? {VideoFormat::Format_BGR48LE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, {VideoFormat::Format_BGR48BE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, {VideoFormat::Format_RGBA64LE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT }, {VideoFormat::Format_RGBA64BE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT }, {VideoFormat::Format_BGRA64LE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT }, {VideoFormat::Format_BGRA64BE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT }, {VideoFormat::Format_Invalid, 0, 0, 0} }; Q_UNUSED(pixfmt_to_desktop); const fmt_entry *pixfmt_gl_entry = pixfmt_to_desktop; if (OpenGLHelper::isOpenGLES()) pixfmt_gl_entry = pixfmt_to_gles; // Very special formats, for which OpenGL happens to have direct support static const fmt_entry pixfmt_gl_base[] = { {VideoFormat::Format_RGBA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // only tested for macOS, win, angle {VideoFormat::Format_RGB24, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE }, {VideoFormat::Format_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, {VideoFormat::Format_BGR32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //rgba(tested) or abgr, depending on endian }; const VideoFormat::PixelFormat pixfmt = fmt.pixelFormat(); // can not use array size because pixfmt_gl_entry is set on runtime for (const fmt_entry* e = pixfmt_gl_entry; e->pixfmt != VideoFormat::Format_Invalid; ++e) { if (e->pixfmt == pixfmt) { *internal_format = e->internal_format; *data_format = e->format; *data_type = e->type; if (mat) *mat = QMatrix4x4(); return true; } } for (size_t i = 0; i < ARRAY_SIZE(pixfmt_gl_base); ++i) { const fmt_entry& e = pixfmt_gl_base[i]; if (e.pixfmt == pixfmt) { *internal_format = e.internal_format; *data_format = e.format; *data_type = e.type; if (mat) *mat = QMatrix4x4(); return true; } } static const fmt_entry pixfmt_to_gl_swizzele[] = { {VideoFormat::Format_VYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_UYVY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_YUYV, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_VYUY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_YVYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, {VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, //swizzle {VideoFormat::Format_RGB555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working {VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working }; for (size_t i = 0; i < ARRAY_SIZE(pixfmt_to_gl_swizzele); ++i) { const fmt_entry& e = pixfmt_to_gl_swizzele[i]; if (e.pixfmt == pixfmt) { *internal_format = e.internal_format; *data_format = e.format; *data_type = e.type; if (mat) *mat = channelMap(fmt); return true; } } GLint *i_f = internal_format; GLenum *d_f = data_format; GLenum *d_t = data_type; gl_param_t* gp = (gl_param_t*)get_gl_param(); const int nb_planes = fmt.planeCount(); if (gp == gl_param_3r16 && ( //nb_planes == 2 || // nv12 UV plane is 16bit, but we use rg (OpenGLHelper::depth16BitTexture() == 16 && OpenGLHelper::has16BitTexture() && fmt.isBigEndian() && fmt.bitsPerComponent() > 8) // 16bit texture does not support be channel now )) { gp = (gl_param_t*)gl_param_desktop_fallback; qDebug("desktop_fallback for %s", nb_planes == 2 ? "bi-plane format" : "16bit big endian channel"); } for (int p = 0; p < nb_planes; ++p) { // for packed rgb(swizzle required) and planar formats const int c = (fmt.channels(p)-1) + 4*((fmt.bitsPerComponent() + 7)/8 - 1); if (gp[c].format == 0) return false; const gl_param_t& f = gp[c]; *(i_f++) = f.internal_format; *(d_f++) = f.format; *(d_t++) = f.type; } if (nb_planes > 2 && data_format[2] == GL_LUMINANCE && fmt.bytesPerPixel(1) == 1) { // QtAV uses the same shader for planar and semi-planar yuv format internal_format[2] = data_format[2] = GL_ALPHA; if (nb_planes == 4) internal_format[3] = data_format[3] = data_format[2]; // vec4(,,,A) } if (mat) *mat = channelMap(fmt); return true; } // TODO: format + datatype? internal format == format? //https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt // TODO: special format size, or componentsize(dataType)*components(format) int bytesOfGLFormat(GLenum format, GLenum dataType) // TODO: rename bytesOfTexel { int component_size = 0; switch (dataType) { #ifdef GL_UNSIGNED_INT_8_8_8_8_REV case GL_UNSIGNED_INT_8_8_8_8_REV: return 4; #endif #ifdef GL_UNSIGNED_BYTE_3_3_2 case GL_UNSIGNED_BYTE_3_3_2: return 1; #endif //GL_UNSIGNED_BYTE_3_3_2 #ifdef GL_UNSIGNED_BYTE_2_3_3_REV case GL_UNSIGNED_BYTE_2_3_3_REV: return 1; #endif case GL_UNSIGNED_SHORT_5_5_5_1: #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV case GL_UNSIGNED_SHORT_1_5_5_5_REV: #endif //GL_UNSIGNED_SHORT_1_5_5_5_REV #ifdef GL_UNSIGNED_SHORT_5_6_5_REV case GL_UNSIGNED_SHORT_5_6_5_REV: #endif //GL_UNSIGNED_SHORT_5_6_5_REV case GL_UNSIGNED_SHORT_5_6_5: // gles #ifdef GL_UNSIGNED_SHORT_4_4_4_4_REV case GL_UNSIGNED_SHORT_4_4_4_4_REV: #endif //GL_UNSIGNED_SHORT_4_4_4_4_REV case GL_UNSIGNED_SHORT_4_4_4_4: return 2; case GL_UNSIGNED_BYTE: component_size = 1; break; // mpv returns 2 #ifdef GL_UNSIGNED_SHORT_8_8_APPLE case GL_UNSIGNED_SHORT_8_8_APPLE: case GL_UNSIGNED_SHORT_8_8_REV_APPLE: return 2; #endif case GL_UNSIGNED_SHORT: component_size = 2; break; } switch (format) { case GL_RED: case GL_LUMINANCE: case GL_ALPHA: return component_size; case GL_RG: case GL_LUMINANCE_ALPHA: return 2*component_size; #ifdef GL_YCBCR_422_APPLE case GL_YCBCR_422_APPLE: return 2; #endif #ifdef GL_RGB_422_APPLE case GL_RGB_422_APPLE: return 2; #endif #ifdef GL_BGR //ifndef GL_ES case GL_BGR: #endif case GL_RGB: return 3*component_size; #ifdef GL_BGRA //ifndef GL_ES case GL_BGRA: #endif case GL_RGBA: return 4*component_size; default: qWarning("bytesOfGLFormat - Unknown format %u", format); return 1; } } } //namespace OpenGLHelper } //namespace QtAV QtAV-1.12.0/src/opengl/OpenGLHelper.h000066400000000000000000000100751312235004300171120ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLHELPER_H #define QTAV_OPENGLHELPER_H #ifndef QT_NO_OPENGL #include "QtAV/VideoFormat.h" #include "opengl/gl_api.h" // for dynamicgl. qglfunctions before qt5.3 does not have portable gl functions #ifdef QT_OPENGL_DYNAMIC #define DYGL(glFunc) QOpenGLContext::currentContext()->functions()->glFunc #else #define DYGL(glFunc) glFunc #endif #define EGL_ENSURE(x, ...) \ do { \ if (!(x)) { \ EGLint err = eglGetError(); \ qWarning("EGL error@%d<<%s. " #x ": %#x %s", __LINE__, __FILE__, err, eglQueryString(eglGetCurrentDisplay(), err)); \ return __VA_ARGS__; \ } \ } while(0) #define EGL_WARN(x, ...) \ do { \ if (!(x)) { \ EGLint err = eglGetError(); \ qWarning("EGL error@%d<<%s. " #x ": %#x %s", __LINE__, __FILE__, err, eglQueryString(eglGetCurrentDisplay(), err)); \ } \ } while(0) #define WGL_ENSURE(x, ...) \ do { \ if (!(x)) { \ qWarning() << "WGL error " << __FILE__ << "@" << __LINE__ << " " << #x << ": " << qt_error_string(GetLastError()); \ return __VA_ARGS__; \ } \ } while(0) #define WGL_WARN(x, ...) \ do { \ if (!(x)) { \ qWarning() << "WGL error " << __FILE__ << "@" << __LINE__ << " " << #x << ": " << qt_error_string(GetLastError()); \ } \ } while(0) QT_BEGIN_NAMESPACE class QMatrix4x4; QT_END_NAMESPACE namespace QtAV { namespace OpenGLHelper { QString removeComments(const QString& code); QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type); int GLSLVersion(); bool isEGL(); bool isOpenGLES(); /*! * \brief hasExtensionEGL * Test if any of the given extensions is supported * \param exts Ends with NULL * \return true if one of extension is supported */ bool hasExtensionEGL(const char* exts[]); bool hasRG(); bool has16BitTexture(); // set by user (environment var "QTAV_TEXTURE16_DEPTH=8 or 16", default now is 8) int depth16BitTexture(); // set by user (environment var "QTAV_GL_DEPRECATED=1") bool useDeprecatedFormats(); /*! * \brief hasExtension * Test if any of the given extensions is supported. Current OpenGL context must be valid. * \param exts Ends with NULL * \return true if one of extension is supported */ bool hasExtension(const char* exts[]); bool isPBOSupported(); /*! * \brief videoFormatToGL * \param fmt * \param internal_format an array with size fmt.planeCount() * \param data_format an array with size fmt.planeCount() * \param data_type an array with size fmt.planeCount() * \param mat channel reorder matrix used in glsl * \return false if fmt is not supported */ bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat = NULL); int bytesOfGLFormat(GLenum format, GLenum dataType = GL_UNSIGNED_BYTE); } //namespace OpenGLHelper } //namespace QtAV #else namespace QtAV { namespace OpenGLHelper { #define DYGL(f) f inline bool isOpenGLES() {return false;} } //namespace OpenGLHelper } //namespace QtAV #endif //QT_NO_OPENGL #endif // QTAV_OPENGLHELPER_H QtAV-1.12.0/src/opengl/OpenGLTypes.cpp000066400000000000000000000222051312235004300173300ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/OpenGLTypes.h" #include "opengl/OpenGLHelper.h" #include #include #include #include "utils/Logger.h" namespace QtAV { struct uniform_type_name { QByteArray name; Uniform::Type type; } uniform_type_names[] ={ {"sample2D", Uniform::Sampler}, {"bool", Uniform::Bool}, {"int", Uniform::Int}, {"uint", Uniform::Int}, {"float", Uniform::Float}, {"vec2", Uniform::Vec2}, {"vec3", Uniform::Vec3}, {"vec4", Uniform::Vec4}, {"mat2", Uniform::Mat2}, {"mat3", Uniform::Mat3}, {"mat4", Uniform::Mat4}, {"bvec2", Uniform::BVec2}, {"bvec3", Uniform::BVec3}, {"bvec4", Uniform::BVec4}, {"ivec2", Uniform::IVec2}, {"ivec3", Uniform::IVec3}, {"ivec4", Uniform::IVec4}, {"uvec2", Uniform::UVec2}, {"uvec3", Uniform::UVec3}, {"uvec4", Uniform::UVec4}, {"mat2x2", Uniform::Mat2}, {"mat3x3", Uniform::Mat3}, {"mat4x4", Uniform::Mat4}, {"dmat2", Uniform::DMat2}, {"dmat3", Uniform::DMat3}, {"dmat4", Uniform::DMat4}, }; static Uniform::Type UniformTypeFromName(const QByteArray& name) { for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) { if (un->name == name) return un->type; } return Uniform::Unknown; } static QByteArray UniformTypeToName(Uniform::Type ut) { for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) { if (un->type == ut) return un->name; } return "unknown"; } Uniform::Uniform(Type tp, int count) : dirty(true) , location(-1) , tuple_size(1) , array_size(1) , t(tp) { setType(tp, count); } Uniform& Uniform::setType(Type tp, int count) { t = tp; array_size = count; if (isVec()) { tuple_size = (t >> (V+1)) & ((1<<3) - 1); } else if (isMat()) { tuple_size = (t >> (M+1)) & ((1<<3) - 1); tuple_size *= tuple_size; } int element_size = sizeof(float); if (isInt() || isUInt() || isBool()) { element_size = sizeof(int); } data = QVector(element_size/sizeof(int)*tupleSize()*arraySize()); return *this; } template bool set_uniform_value(QVector& dst, const T* v, int count) { Q_ASSERT(sizeof(T)*count <= sizeof(int)*dst.size() && "set_uniform_value: Bad type or array size"); // why not dst.constData()? const QVector old(dst); memcpy((char*)dst.data(), (const char*)v, count*sizeof(T)); return old != dst; } template<> bool set_uniform_value(QVector& dst, const bool* v, int count) { const QVector old(dst); for (int i = 0; i < count; ++i) { dst[i] = *(v + i); } return old != dst; } void Uniform::set(const float &v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, &v, count); } void Uniform::set(const int &v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, &v, count); } void Uniform::set(const unsigned &v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, &v, count); } void Uniform::set(const float *v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, v, count); } void Uniform::set(const int *v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, v, count); } void Uniform::set(const unsigned *v, int count) { if (count <= 0) count = tupleSize()*arraySize(); dirty = set_uniform_value(data, v, count); } void Uniform::set(const QVariant &v) { if (tupleSize() > 1 || arraySize() > 1) { if (isFloat()) { //TODO: what if QVector but uniform is float? set(v.value >().data()); } else if (isInt() || isBool()) { set(v.value >().data()); } else if (isUInt()) { set(v.value >().data()); } else if (type() == Uniform::Sampler) { } } else { if (isFloat()) { set(v.toFloat()); } else if (isInt() || isBool()) { set(v.toInt()); } else if (isUInt()) { set(v.toUInt()); } else if (type() == Uniform::Sampler) { } } } bool Uniform::setGL() { if (location < 0) { return false; } switch (type()) { case Uniform::Bool: case Uniform::Int: gl().Uniform1iv(location, arraySize(), address()); break; case Uniform::Float: gl().Uniform1fv(location, arraySize(), address()); break; case Uniform::Vec2: gl().Uniform2fv(location, arraySize(), address()); break; case Uniform::Vec3: gl().Uniform3fv(location, arraySize(), address()); break; case Uniform::Vec4: gl().Uniform4fv(location, arraySize(), address()); break; case Uniform::Mat2: gl().UniformMatrix2fv(location, arraySize(), GL_FALSE, address()); break; case Uniform::Mat3: gl().UniformMatrix3fv(location, arraySize(), GL_FALSE, address()); break; case Uniform::Mat4: gl().UniformMatrix4fv(location, arraySize(), GL_FALSE, address()); break; case Uniform::IVec2: gl().Uniform2iv(location, arraySize(), address()); break; case Uniform::IVec3: gl().Uniform3iv(location, arraySize(), address()); break; case Uniform::IVec4: gl().Uniform4iv(location, arraySize(), address()); break; default: qDebug() << *this; qWarning("Unsupported uniform type in Qt. You should use 'VideoShader::setUserUniformValues()' to call glUniformXXX or directly call glUniformXXX instead"); return false; } dirty = false; return true; } #ifndef QT_NO_DEBUG_STREAM Q_AV_EXPORT QDebug operator<<(QDebug dbg, const Uniform &u) { dbg.nospace() << "uniform " << UniformTypeToName(u.type()) << " " << u.name.constData(); if (u.arraySize() > 1) { dbg.nospace() << "[" << u.arraySize() << "]"; } dbg.nospace() << ", dirty: " << u.dirty; dbg.nospace() << ", location: " << u.location << ", " << "tupleSize: " << u.tupleSize() << ", "; if (u.isBool() || u.isInt()) { dbg.nospace() << "value: " << u.value(); } else if (u.isUInt()) { dbg.nospace() << "value: " << u.value(); } else if (u.isDouble()) { dbg.nospace() << "value: " << u.value(); } else { dbg.nospace() << "value: " << u.value(); } return dbg.space(); } Q_AV_EXPORT QDebug operator<<(QDebug dbg, Uniform::Type ut); #endif QVector ParseUniforms(const QByteArray &text, GLuint programId = 0) { QVector uniforms; const QString code = OpenGLHelper::removeComments(QString(text)); const QStringList lines = code.split(';'); // TODO: highp lowp etc. const QString exp(QStringLiteral("\\s*uniform\\s+([\\w\\d]+)\\s+([\\w\\d]+)\\s*")); const QString exp_array = exp + QStringLiteral("\\[(\\d+)\\]\\s*"); foreach (QString line, lines) { line = line.trimmed(); if (!line.startsWith(QStringLiteral("uniform "))) continue; QRegExp rx(exp_array); if (rx.indexIn(line) < 0) { rx = QRegExp(exp); if (rx.indexIn(line) < 0) continue; } Uniform u; const QStringList x = rx.capturedTexts(); //qDebug() << x; u.name = x.at(2).toUtf8(); int array_size = 1; if (x.size() > 3) array_size = x[3].toInt(); const QByteArray t(x[1].toLatin1()); u.setType(UniformTypeFromName(t), array_size); if (programId > 0) u.location = gl().GetUniformLocation(programId, u.name.constData()); uniforms.append(u); } qDebug() << uniforms; return uniforms; } } //namespace QtAV QtAV-1.12.0/src/opengl/OpenGLVideo.cpp000066400000000000000000000317521312235004300173010ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/OpenGLVideo.h" #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #include #endif //5.0 #include "QtAV/SurfaceInterop.h" #include "QtAV/VideoShader.h" #include "ShaderManager.h" #include "QtAV/GeometryRenderer.h" #include "opengl/OpenGLHelper.h" #include "utils/Logger.h" namespace QtAV { // FIXME: why crash if inherits both QObject and DPtrPrivate? class OpenGLVideoPrivate : public DPtrPrivate { public: OpenGLVideoPrivate() : ctx(0) , manager(0) , material(new VideoMaterial()) , material_type(0) , norm_viewport(true) , update_geo(true) , tex_target(0) , valiad_tex_width(1.0) , mesh_type(OpenGLVideo::RectMesh) , geometry(NULL) , gr(NULL) , user_shader(NULL) { } ~OpenGLVideoPrivate() { if (material) { delete material; material = 0; } delete geometry; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) || !defined(Q_COMPILER_LAMBDA) delete gr; #endif } void resetGL() { ctx = 0; if (gr) gr->updateGeometry(NULL); if (!manager) return; manager->setParent(0); delete manager; manager = 0; if (material) { delete material; material = 0; } } // update geometry(vertex array) set attributes or bind VAO/VBO. void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r); public: QOpenGLContext *ctx; ShaderManager *manager; VideoMaterial *material; qint64 material_type; bool norm_viewport; bool has_a; bool update_geo; int tex_target; qreal valiad_tex_width; QSize video_size; QRectF target; QRectF roi; //including invalid padding width OpenGLVideo::MeshType mesh_type; TexturedGeometry *geometry; GeometryRenderer* gr; QRectF rect; QMatrix4x4 matrix; VideoShader *user_shader; }; void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r) { // also check size change for normalizedROI computation if roi is not normalized const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize(); const int tc = shader->textureLocationCount(); if (roi_changed) { roi = r; valiad_tex_width = material->validTextureWidth(); video_size = material->frameSize(); } if (tex_target != shader->textureTarget()) { tex_target = shader->textureTarget(); update_geo = true; } bool update_gr = false; static QThreadStorage new_thread; if (!new_thread.hasLocalData()) new_thread.setLocalData(true); update_gr = new_thread.localData(); if (!gr || update_gr) { // TODO: only update VAO, not the whole GeometryRenderer update_geo = true; new_thread.setLocalData(false); GeometryRenderer *r = new GeometryRenderer(); // local var is captured by lambda gr = r; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && defined(Q_COMPILER_LAMBDA) QObject::connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, [r]{ qDebug("destroy GeometryRenderer %p", r); delete r; }); #endif } // (-1, -1, 2, 2) must flip y QRectF target_rect = norm_viewport ? QRectF(-1, 1, 2, -2) : rect; if (target.isValid()) { if (roi_changed || target != t) { target = t; update_geo = true; //target_rect = target (if valid). // relate to gvf bug? } } else { if (roi_changed) { update_geo = true; } } if (!update_geo) return; delete geometry; geometry = NULL; if (mesh_type == OpenGLVideo::SphereMesh) geometry = new Sphere(); else geometry = new TexturedGeometry(); //qDebug("updating geometry..."); // setTextureCount may change the vertex data. Call it before setRect() qDebug() << "target rect: " << target_rect ; geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1); geometry->setGeometryRect(target_rect); geometry->setTextureRect(material->mapToTexture(0, roi)); if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) { for (int i = 1; i < tc; ++i) { // tc can > planes, but that will compute chroma plane geometry->setTextureRect(material->mapToTexture(i, roi), i); } } geometry->create(); update_geo = false; gr->updateGeometry(geometry); } OpenGLVideo::OpenGLVideo() {} bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt) { return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid; } void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx) { DPTR_D(OpenGLVideo); if (d.ctx == ctx) return; qreal b = 0, c = 0, h = 0, s = 0; if (d.material) { b = d.material->brightness(); c = d.material->contrast(); h = d.material->hue(); s = d.material->saturation(); delete d.material; d.material = 0; } d.resetGL(); //TODO: is it ok to destroygl resources in another context? d.ctx = ctx; // Qt4: set to null in resetGL() if (!ctx) { return; } d.material = new VideoMaterial(); d.material->setBrightness(b); d.material->setContrast(c); d.material->setHue(h); d.material->setSaturation(s); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = ctx->findChild(QStringLiteral("__qtav_shader_manager")); QSizeF surfaceSize = ctx->surface()->size(); #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) surfaceSize *= ctx->screen()->devicePixelRatio(); #else surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's #endif #else QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height()); #endif setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); if (d.manager) return; // TODO: what if ctx is delete? #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = new ShaderManager(ctx); QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()? #else d.manager = new ShaderManager(this); #endif d.manager->setObjectName(QStringLiteral("__qtav_shader_manager")); /// get gl info here because context is current(qt ensure it) //const QByteArray extensions(reinterpret_cast(glGetString(GL_EXTENSIONS))); bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL); static bool sInfo = true; if (sInfo) { sInfo = false; qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION))); qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR))); qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER))); qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION))); /// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null bool v = OpenGLHelper::isOpenGLES(); qDebug("Is OpenGLES: %d", v); v = OpenGLHelper::isEGL(); qDebug("Is EGL: %d", v); const int glsl_ver = OpenGLHelper::GLSLVersion(); qDebug("GLSL version: %d", glsl_ver); v = OpenGLHelper::isPBOSupported(); qDebug("Has PBO: %d", v); v = OpenGLHelper::has16BitTexture(); qDebug("Has 16bit texture: %d", v); v = OpenGLHelper::hasRG(); qDebug("Has RG texture: %d", v); qDebug() << ctx->format(); } } QOpenGLContext* OpenGLVideo::openGLContext() { return d_func().ctx; } void OpenGLVideo::setCurrentFrame(const VideoFrame &frame) { d_func().material->setCurrentFrame(frame); d_func().has_a = frame.format().hasAlpha(); } void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v) { setViewport(v); } void OpenGLVideo::setViewport(const QRectF &r) { DPTR_D(OpenGLVideo); d.rect = r; if (d.norm_viewport) { d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh) d.matrix.perspective(45, 1, 0.1, 100); // for sphere } else { d.matrix.setToIdentity(); d.matrix.ortho(r); d.update_geo = true; // even true for target_rect != d.rect } // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. //mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0; if (d.ctx && d.ctx == QOpenGLContext::currentContext()) { DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); } } void OpenGLVideo::setBrightness(qreal value) { d_func().material->setBrightness(value); } void OpenGLVideo::setContrast(qreal value) { d_func().material->setContrast(value); } void OpenGLVideo::setHue(qreal value) { d_func().material->setHue(value); } void OpenGLVideo::setSaturation(qreal value) { d_func().material->setSaturation(value); } void OpenGLVideo::setUserShader(VideoShader *shader) { d_func().user_shader = shader; } VideoShader* OpenGLVideo::userShader() const { return d_func().user_shader; } void OpenGLVideo::setMeshType(MeshType value) { DPTR_D(OpenGLVideo); if (d.mesh_type == value) return; d.mesh_type = value; d.update_geo = true; if (d.mesh_type == SphereMesh && d.norm_viewport) { d.matrix.setToIdentity(); d.matrix.perspective(45, 1, 0.1, 100); // for sphere } } OpenGLVideo::MeshType OpenGLVideo::meshType() const { return d_func().mesh_type; } void OpenGLVideo::fill(const QColor &color) { DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF())); DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform) { DPTR_D(OpenGLVideo); Q_ASSERT(d.manager); Q_EMIT beforeRendering(); DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) const qint64 mt = d.material->type(); if (d.material_type != mt) { qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); d.material_type = mt; } if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it return; VideoShader *shader = d.user_shader; if (!shader) shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) shader->update(d.material); shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); // uniform end. attribute begin d.updateGeometry(shader, target, roi); // normalize? const bool blending = d.has_a; if (blending) { DYGL(glEnable(GL_BLEND)); gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); // } //if (d.mesh_type == OpenGLVideo::SphereMesh) //DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf d.gr->render(); if (blending) DYGL(glDisable(GL_BLEND)); // d.shader->program()->release(); //glUseProgram(0) d.material->unbind(); Q_EMIT afterRendering(); } void OpenGLVideo::resetGL() { qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender()); d_func().resetGL(); } } //namespace QtAV QtAV-1.12.0/src/opengl/ShaderManager.cpp000066400000000000000000000017421312235004300176630ustar00rootroot00000000000000#include "ShaderManager.h" #include "QtAV/VideoShader.h" namespace QtAV { class ShaderManager::Private { public: ~Private() { // TODO: thread safe required? qDeleteAll(shader_cache.values()); shader_cache.clear(); } QHash shader_cache; }; ShaderManager::ShaderManager(QObject *parent) : QObject(parent) , d(new Private()) { } ShaderManager::~ShaderManager() { delete d; d = 0; } VideoShader* ShaderManager::prepareMaterial(VideoMaterial *material, qint32 materialType) { const qint32 type = materialType != -1 ? materialType : material->type(); VideoShader *shader = d->shader_cache.value(type, 0); if (shader) return shader; qDebug() << QString("[ShaderManager] cache a new shader material type(%1): %2").arg(type).arg(VideoMaterial::typeName(type)); shader = material->createShader(); shader->initialize(); d->shader_cache[type] = shader; return shader; } } //namespace QtAV QtAV-1.12.0/src/opengl/ShaderManager.h000066400000000000000000000035511312235004300173300ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SHADERMANAGER_H #define QTAV_SHADERMANAGER_H #include namespace QtAV { class VideoShader; class VideoMaterial; /*! * \brief The ShaderManager class * Cache VideoShader and shader programes for different video material type. * TODO: ShaderManager does not change for a given vo, so we can expose VideoRenderer.shaderManager() to set custom shader. It's better than VideoRenderer.opengl() because OpenGLVideo exposes too many apis that may confuse user. */ class ShaderManager : public QObject { Q_OBJECT public: ShaderManager(QObject *parent = 0); ~ShaderManager(); VideoShader* prepareMaterial(VideoMaterial *material, qint32 materialType = -1); // void setCacheSize(int value); private: class Private; Private* d; }; } //namespace QtAV #endif // QTAV_SHADERMANAGER_H QtAV-1.12.0/src/opengl/SubImagesGeometry.cpp000066400000000000000000000141201312235004300205470ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SubImagesGeometry.h" #include "utils/Logger.h" namespace QtAV { #define U8COLOR 0 static const int kMaxTexWidth = 4096; //FIXME: glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); // if texture1d, we can directly copy ASS_Image.bitmap without line by line copy, i.e. tiled, and upload only once typedef struct { float x, y; // depends on target rect float tx, ty; // depends on texture size and rects layout #if U8COLOR union { quint8 r, g, b, a; //to be normalized quint32 rgba; }; #else float r, g, b, a; #endif } VertexData; static VertexData* SetUnnormalizedVertexData(VertexData* v, int tx, int ty, int tw, int th, quint32 color, bool useIndecies) { #if U8COLOR union { quint8 r, g, b, a; quint32 rgba; }; r = color >> 24; g = (color >> 16) & 0xff; b = (color >> 8) & 0xff; a = 255 - (color & 0xff); #else float r, g, b, a; r = (float)(color >> 24)/255.0; g = (float)((color >> 16) & 0xff)/255.0; b = (float)((color >> 8) & 0xff)/255.0; a = (float)(255 - (color & 0xff))/255.0; #endif // normalize later v[0].tx = tx; v[0].ty = ty; v[1].tx = tx; v[1].ty = ty + th; v[2].tx = tx + tw; v[2].ty = ty; v[3].tx = tx + tw; v[3].ty = ty + th; #if U8COLOR v[0].rgba = rgba; v[1].rgba = rgba; v[2].rgba = rgba; v[3].rgba = rgba; #else #define SETC(x) x.r = r; x.g=g; x.b=b; x.a=a; SETC(v[0]); SETC(v[1]); SETC(v[2]); SETC(v[3]); #endif if (!useIndecies) { v[4] = v[1]; v[5] = v[2]; return v + 6; } return v + 4; } static VertexData* SetVertexPositionAndNormalize(VertexData* v, float x, float y, float w, float h, float texW, float texH, bool useIndecies) { v[0].x = x; v[0].y = y; v[1].x = x; v[1].y = y + h; v[2].x = x + w; v[2].y = y; v[3].x = x + w; v[3].y = y + h; v[0].tx /= texW; v[0].ty /= texH; v[1].tx /= texW; v[1].ty /= texH; v[2].tx /= texW; v[2].ty /= texH; v[3].tx /= texW; v[3].ty /= texH; //qDebug("%f,%f<=%f,%f; %u,%u,%u,%u", v[3].x, v[3].y, v[3].tx, v[3].ty, v[3].r, v[3].g, v[3].b, v[3].a); if (!useIndecies) { v[4] = v[1]; v[5] = v[2]; return v + 6; } return v + 4; } SubImagesGeometry::SubImagesGeometry() : Geometry() , m_normalized(false) , m_w(0) , m_h(0) { setPrimitive(Geometry::Triangles); m_attributes << Attribute(TypeF32, 2) << Attribute(TypeF32, 2, 2*sizeof(float)) #if U8COLOR << Attribute(TypeU8, 4, 4*sizeof(float), true); #else << Attribute(TypeF32, 4, 4*sizeof(float)); #endif } bool SubImagesGeometry::setSubImages(const SubImageSet &images) { // TODO: operator == if (m_images == images) return false; m_images = images; return true; } bool SubImagesGeometry::generateVertexData(const QRect &rect, bool useIndecies, int maxWidth) { if (maxWidth < 0) maxWidth = kMaxTexWidth; if (useIndecies) allocate(4*m_images.images.size(), 6*m_images.images.size()); else allocate(6*m_images.images.size()); qDebug("images: %d/%d, %dx%d", m_images.isValid(), m_images.images.size(), m_images.width(), m_images.height()); m_rects_upload.clear(); m_w = m_h = 0; m_normalized = false; if (!m_images.isValid()) return false; int W = 0, H = 0; int x = 0, h = 0; VertexData* vd = (VertexData*)vertexData(); int index = 0; foreach (const SubImage& i, m_images.images) { if (x + i.stride > maxWidth && maxWidth > 0) { W = qMax(W, x); H += h; x = 0; h = 0; } // we use w instead of stride even if we must upload stride. when maping texture coordinates and view port coordinates, we can use the visual rects instead of stride, i.e. the geometry vertices are (x, y, w, h), not (x, y, stride, h) m_rects_upload.append(QRect(x, H, i.stride, i.h)); vd = SetUnnormalizedVertexData(vd, x, H, i.w, i.h, i.color, useIndecies); if (useIndecies) { // TODO: set only once because it never changes, use IBO const int v0 = index*4/6; setIndexValue(index, v0, v0+1, v0+2); setIndexValue(index+3, v0+1, v0+2, v0+3); index += 6; } x += i.w; h = qMax(h, i.h); } W = qMax(W, x); H += h; m_w = W; m_h = H; //qDebug("sub texture %dx%d", m_w, m_h); const float dx0 = rect.x(); const float dy0 = rect.y(); const float sx = float(rect.width())/float(m_images.width()); const float sy = float(rect.height())/float(m_images.height()); vd = (VertexData*)vertexData(); foreach (const SubImage& i, m_images.images) { //qDebug() << rect; //qDebug("i: %d,%d", i.x, i.y); vd = SetVertexPositionAndNormalize(vd, dx0 + float(i.x)*sx, dy0 + float(i.y)*sy, i.w*sx, i.h*sy, m_w, m_h, useIndecies); m_normalized = true; } return true; } int SubImagesGeometry::stride() const { return sizeof(VertexData); } } //namespace QtAV QtAV-1.12.0/src/opengl/SubImagesGeometry.h000066400000000000000000000045331312235004300202230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBIMAGESGEOMETRY_H #define QTAV_SUBIMAGESGEOMETRY_H #include "QtAV/Geometry.h" #include "QtAV/SubImage.h" namespace QtAV { class SubImagesGeometry : public Geometry { public: SubImagesGeometry(); bool setSubImages(const SubImageSet& images); /*! * \brief generateVertexData * \param rect rect render to. If it's viewport rect, and fit video aspect ratio, ass images created from video frame size needs a scale transform is required when rendering * \param useIndecies * \param maxWidth * \return false if current SubImageSet is invalid */ bool generateVertexData(const QRect& rect, bool useIndecies = false, int maxWidth = -1); // available after generateVertexData is called int width() { return m_w;} int height() { return m_h;} int stride() const Q_DECL_OVERRIDE; const QVector& attributes() const Q_DECL_OVERRIDE { return m_attributes;} const SubImageSet& images() const { return m_images; } const QVector& uploadRects() const { return m_rects_upload;} private: using Geometry::allocate; bool m_normalized; int m_w, m_h; QVector m_attributes; SubImageSet m_images; // for texture upload parameters QVector m_rects_upload; }; } //namespace QtAV #endif //QTAV_SUBIMAGESGEOMETRY_H QtAV-1.12.0/src/opengl/SubImagesRenderer.cpp000066400000000000000000000123441312235004300205300ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "SubImagesRenderer.h" #include "opengl/SubImagesGeometry.h" #include "QtAV/GeometryRenderer.h" namespace QtAV { #define GLSL(x) #x "\n" static const char kVert[] = GLSL( attribute vec4 a_Position; attribute vec2 a_TexCoords; attribute vec4 a_Color; uniform mat4 u_Matrix; varying vec2 v_TexCoords; varying vec4 v_Color; void main() { gl_Position = u_Matrix * a_Position; v_TexCoords = a_TexCoords; v_Color = a_Color; }); static const char kFrag[] = GLSL( uniform sampler2D u_Texture; varying vec2 v_TexCoords; varying vec4 v_Color; void main() { gl_FragColor.rgb = v_Color.rgb; gl_FragColor.a = v_Color.a*texture2D(u_Texture, v_TexCoords).r; } ); SubImagesRenderer::SubImagesRenderer() : m_geometry(new SubImagesGeometry()) , m_renderer(new GeometryRenderer()) , m_tex(0) {} SubImagesRenderer::~SubImagesRenderer() { delete m_geometry; delete m_renderer; } void SubImagesRenderer::render(const SubImageSet &ass, const QRect &target, const QMatrix4x4 &transform) { if (m_geometry->setSubImages(ass) || m_rect != target) { m_rect = target; if (!m_geometry->generateVertexData(m_rect, true)) return; uploadTexture(m_geometry); m_renderer->updateGeometry(m_geometry); } if (!m_program.isLinked()) { m_program.removeAllShaders(); QByteArray vs(kVert); vs.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Vertex)); m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vs); QByteArray fs(kFrag); fs.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Fragment)); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fs); // TODO: foreach geometry.attributes m_program.bindAttributeLocation("a_Position", 0); m_program.bindAttributeLocation("a_TexCoords", 1); m_program.bindAttributeLocation("a_Color", 2); if (!m_program.link()) qWarning() << m_program.log(); } m_program.bind(); gl().ActiveTexture(GL_TEXTURE0); DYGL(glBindTexture(GL_TEXTURE_2D, m_tex)); m_program.setUniformValue("u_Texture", 0); m_program.setUniformValue("u_Matrix", transform*m_mat); DYGL(glEnable(GL_BLEND)); if (m_geometry->images().format() == SubImageSet::ASS) gl().BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); else gl().BlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); m_renderer->render(); DYGL(glDisable(GL_BLEND)); } void SubImagesRenderer::setProjectionMatrixToRect(const QRectF &v) { m_mat.setToIdentity(); m_mat.ortho(v); } void SubImagesRenderer::uploadTexture(SubImagesGeometry *g) { if (!m_tex) { DYGL(glGenTextures(1, &m_tex)); //TODO: delete } GLint internal_fmt; GLenum data_type; GLenum fmt; if (g->images().format() == SubImageSet::ASS) OpenGLHelper::videoFormatToGL(VideoFormat(VideoFormat::Format_Y8), &internal_fmt, &fmt, &data_type); else //rgb32 OpenGLHelper::videoFormatToGL(VideoFormat(VideoFormat::Format_ARGB32), &internal_fmt, &fmt, &data_type); DYGL(glBindTexture(GL_TEXTURE_2D, m_tex)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); DYGL(glTexImage2D(GL_TEXTURE_2D, 0, internal_fmt, g->width(), g->height(), 0, fmt, data_type, NULL)); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int i = 0; i < g->uploadRects().size(); ++i) { const QRect& r = g->uploadRects().at(i); const SubImage& sub = g->images().images.at(i); DYGL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), fmt, data_type, sub.data.constData())); } //glPixelStorei(GL_UNPACK_ALIGNMENT, 4); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); } } //namespace QtAV QtAV-1.12.0/src/opengl/SubImagesRenderer.h000066400000000000000000000045211312235004300201730ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SUBIMAGESRENDERER_H #define QTAV_SUBIMAGESRENDERER_H #include #include #include #include "opengl/OpenGLHelper.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #else #include #include #undef QOpenGLShaderProgram #undef QOpenGLShader #define QOpenGLShaderProgram QGLShaderProgram #define QOpenGLShader QGLShader #endif namespace QtAV { class SubImagesGeometry; class GeometryRenderer; class SubImagesRenderer { public: SubImagesRenderer(); ~SubImagesRenderer(); /*! * \brief render * \param ass * \param target * \param transform additional transform, e.g. aspect ratio */ void render(const SubImageSet& ass, const QRect& target, const QMatrix4x4& transform = QMatrix4x4()); /*! * \brief setProjectionMatrixToRect * the rect will be viewport */ void setProjectionMatrixToRect(const QRectF& v); private: void uploadTexture(SubImagesGeometry* g); SubImagesGeometry *m_geometry; GeometryRenderer *m_renderer; QMatrix4x4 m_mat; QRect m_rect; GLuint m_tex; QOpenGLShaderProgram m_program; }; } //namespace QtAV #endif //QTAV_SUBIMAGESRENDERER_H QtAV-1.12.0/src/opengl/VideoShader.cpp000066400000000000000000001202031312235004300173510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoShader.h" #include "QtAV/private/VideoShader_p.h" #include "ColorTransform.h" #include "opengl/OpenGLHelper.h" #include #include #include #include #include #include "utils/Logger.h" #define YUVA_DONE 0 //#define QTAV_DEBUG_GLSL namespace QtAV { extern QVector ParseUniforms(const QByteArray& text, GLuint programId = 0); VideoShader::VideoShader(VideoShaderPrivate &d): DPTR_INIT(&d) { } VideoShader::VideoShader() { //d.planar_frag = shaderSourceFromFile("shaders/planar.f.glsl"); //d.packed_frag = shaderSourceFromFile("shaders/rgb.f.glsl"); } VideoShader::~VideoShader() { } /* * use gl api to get active attributes/uniforms * use glsl parser to get attributes? */ char const *const* VideoShader::attributeNames() const { static const char *names[] = { "a_Position", "a_TexCoords0", 0 }; if (textureTarget() == GL_TEXTURE_2D) return names; DPTR_D(const VideoShader); static const char *names_multicoord[] = { "a_Position", "a_TexCoords0", "a_TexCoords1", "a_TexCoords2", 0 }; #if YUVA_DONE static const char *names_multicoord_4[] = { "a_Position", "a_TexCoords0", "a_TexCoords1", "a_TexCoords2", "a_TexCoords3", 0 }; if (d_func().video_format.planeCount() == 4) return names_multicoord_4; #endif // TODO: names_multicoord_4planes return d.video_format.isPlanar() ? names_multicoord : names; } const char* VideoShader::vertexShader() const { DPTR_D(const VideoShader); // because we have to modify the shader, and shader source must be kept, so read the origin d.vert = shaderSourceFromFile(QStringLiteral("shaders/video.vert")); QByteArray& vert = d.vert; if (vert.isEmpty()) { qWarning("Empty vertex shader!"); return 0; } if (textureTarget() == GL_TEXTURE_RECTANGLE && d.video_format.isPlanar()) { vert.prepend("#define MULTI_COORD\n"); #if YUVA_DONE if (d.video_format.hasAlpha()) vert.prepend("#define HAS_ALPHA\n"); #endif } vert.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Vertex)); if (userShaderHeader(QOpenGLShader::Vertex)) { QByteArray header("*/"); header.append(userShaderHeader(QOpenGLShader::Vertex)); header += "/*"; vert.replace("%userHeader%", header); } #ifdef QTAV_DEBUG_GLSL QString s(vert); s = OpenGLHelper::removeComments(s); qDebug() << s.toUtf8().constData(); #endif //QTAV_DEBUG_GLSL return vert.constData(); } const char* VideoShader::fragmentShader() const { DPTR_D(const VideoShader); // because we have to modify the shader, and shader source must be kept, so read the origin if (d.video_format.isPlanar()) { d.planar_frag = shaderSourceFromFile(QStringLiteral("shaders/planar.f.glsl")); } else { d.packed_frag = shaderSourceFromFile(QStringLiteral("shaders/packed.f.glsl")); } QByteArray& frag = d.video_format.isPlanar() ? d.planar_frag : d.packed_frag; if (frag.isEmpty()) { qWarning("Empty fragment shader!"); return 0; } const int nb_planes = d.video_format.planeCount(); if (nb_planes == 2) //TODO: nv21 must be swapped frag.prepend("#define IS_BIPLANE\n"); if (OpenGLHelper::hasRG() && !OpenGLHelper::useDeprecatedFormats()) frag.prepend("#define USE_RG\n"); const bool has_alpha = d.video_format.hasAlpha(); if (d.video_format.isPlanar()) { const int bpc = d.video_format.bitsPerComponent(); if (bpc > 8) { //// has and use 16 bit texture (r16 for example): If channel depth is 16 bit, no range convertion required. Otherwise, must convert to color.r*(2^16-1)/(2^bpc-1) if (OpenGLHelper::depth16BitTexture() < 16 || !OpenGLHelper::has16BitTexture() || d.video_format.isBigEndian()) frag.prepend("#define CHANNEL16_TO8\n"); } #if YUVA_DONE if (has_alpha) frag.prepend("#define HAS_ALPHA\n"); #endif } else { if (has_alpha) frag.prepend("#define HAS_ALPHA\n"); if (d.video_format.isXYZ()) frag.prepend("#define XYZ_GAMMA\n"); } if (d.texture_target == GL_TEXTURE_RECTANGLE) { frag.prepend("#extension GL_ARB_texture_rectangle : enable\n" "#define sampler2D sampler2DRect\n"); if (OpenGLHelper::GLSLVersion() < 140) frag.prepend("#undef texture\n" "#define texture texture2DRect\n" ); frag.prepend("#define MULTI_COORD\n"); } frag.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Fragment)); QByteArray header("*/"); if (userShaderHeader(QOpenGLShader::Fragment)) header += QByteArray(userShaderHeader(QOpenGLShader::Fragment)); header += "\n"; header += "uniform vec2 u_texelSize[" + QByteArray::number(nb_planes) + "];\n"; header += "uniform vec2 u_textureSize[" + QByteArray::number(nb_planes) + "];\n"; header += "/*"; frag.replace("%userHeader%", header); if (userSample()) { QByteArray sample_code("*/\n#define USER_SAMPLER\n"); sample_code += QByteArray(userSample()); sample_code += "/*"; frag.replace("%userSample%", sample_code); } if (userPostProcess()) { QByteArray pp_code("*/"); pp_code += QByteArray(userPostProcess()); //why the content is wrong sometimes if no ctor? pp_code += "/*"; frag.replace("%userPostProcess%", pp_code); } frag.replace("%planes%", QByteArray::number(nb_planes)); #ifdef QTAV_DEBUG_GLSL QString s(frag); s = OpenGLHelper::removeComments(s); qDebug() << s.toUtf8().constData(); #endif //QTAV_DEBUG_GLSL return frag.constData(); } void VideoShader::initialize(QOpenGLShaderProgram *shaderProgram) { DPTR_D(VideoShader); if (!textureLocationCount()) return; d.owns_program = !shaderProgram; if (shaderProgram) { d.program = shaderProgram; } shaderProgram = program(); if (!shaderProgram->isLinked()) { build(shaderProgram); } d.u_Matrix = shaderProgram->uniformLocation("u_Matrix"); // fragment shader d.u_colorMatrix = shaderProgram->uniformLocation("u_colorMatrix"); d.u_to8 = shaderProgram->uniformLocation("u_to8"); d.u_opacity = shaderProgram->uniformLocation("u_opacity"); d.u_c = shaderProgram->uniformLocation("u_c"); d.u_texelSize = shaderProgram->uniformLocation("u_texelSize"); d.u_textureSize = shaderProgram->uniformLocation("u_textureSize"); d.u_Texture.resize(textureLocationCount()); qDebug("uniform locations:"); for (int i = 0; i < d.u_Texture.size(); ++i) { const QString tex_var = QStringLiteral("u_Texture%1").arg(i); d.u_Texture[i] = shaderProgram->uniformLocation(tex_var); qDebug("%s: %d", tex_var.toUtf8().constData(), d.u_Texture[i]); } qDebug("u_Matrix: %d", d.u_Matrix); qDebug("u_colorMatrix: %d", d.u_colorMatrix); qDebug("u_opacity: %d", d.u_opacity); if (d.u_c >= 0) qDebug("u_c: %d", d.u_c); if (d.u_to8 >= 0) qDebug("u_to8: %d", d.u_to8); if (d.u_texelSize >= 0) qDebug("u_texelSize: %d", d.u_texelSize); if (d.u_textureSize >= 0) qDebug("u_textureSize: %d", d.u_textureSize); d.user_uniforms[VertexShader].clear(); d.user_uniforms[FragmentShader].clear(); if (userShaderHeader(QOpenGLShader::Vertex)) { qDebug("user uniform locations in vertex shader:"); d.user_uniforms[VertexShader] = ParseUniforms(QByteArray(userShaderHeader(QOpenGLShader::Vertex)), shaderProgram->programId()); } if (userShaderHeader(QOpenGLShader::Fragment)) { qDebug("user uniform locations in fragment shader:"); d.user_uniforms[FragmentShader] = ParseUniforms(QByteArray(userShaderHeader(QOpenGLShader::Fragment)), shaderProgram->programId()); } d.rebuild_program = false; d.update_builtin_uniforms = true; programReady(); // program and uniforms are ready } int VideoShader::textureLocationCount() const { DPTR_D(const VideoShader); // TODO: avoid accessing video_format. if (!d.video_format.isPlanar()) return 1; return d.video_format.channels(); } int VideoShader::textureLocation(int channel) const { DPTR_D(const VideoShader); Q_ASSERT(channel < d.u_Texture.size()); return d.u_Texture[channel]; } int VideoShader::matrixLocation() const { return d_func().u_Matrix; } int VideoShader::colorMatrixLocation() const { return d_func().u_colorMatrix; } int VideoShader::opacityLocation() const { return d_func().u_opacity; } int VideoShader::channelMapLocation() const { return d_func().u_c; } int VideoShader::texelSizeLocation() const { return d_func().u_texelSize; } int VideoShader::textureSizeLocation() const { return d_func().u_textureSize; } int VideoShader::uniformLocation(const char *name) const { DPTR_D(const VideoShader); if (!d.program) return -1; return d.program->uniformLocation(name); //TODO: store in a hash } int VideoShader::textureTarget() const { return d_func().texture_target; } void VideoShader::setTextureTarget(int type) { d_func().texture_target = type; } void VideoShader::setMaterialType(qint32 value) { d_func().material_type = value; } VideoFormat VideoShader::videoFormat() const { return d_func().video_format; } void VideoShader::setVideoFormat(const VideoFormat &format) { d_func().video_format = format; } QOpenGLShaderProgram* VideoShader::program() { DPTR_D(VideoShader); if (!d.program) { d.owns_program = true; d.program = new QOpenGLShaderProgram(); } return d.program; } bool VideoShader::update(VideoMaterial *material) { Q_ASSERT(material && "null material"); DPTR_D(VideoShader); const qint32 mt = material->type(); if (mt != d.material_type || d.rebuild_program) { // TODO: use shader program cache (per shader), check shader type qDebug("Rebuild shader program requested: %d. Material type %d=>%d", d.rebuild_program, d.material_type, mt); program()->removeAllShaders(); //not linked // initialize shader, the same as VideoMaterial::createShader setVideoFormat(material->currentFormat()); setTextureTarget(material->textureTarget()); setMaterialType(material->type()); initialize(); } //material->unbind(); const VideoFormat fmt(material->currentFormat()); //FIXME: maybe changed in setCurrentFrame( //format is out of date because we may use the same shader for different formats setVideoFormat(fmt); // uniforms begin program()->bind(); //glUseProgram(id). for glUniform if (!setUserUniformValues()) { if (!d.user_uniforms[VertexShader].isEmpty()) { for (int i = 0; i < d.user_uniforms[VertexShader].size(); ++i) { Uniform& u = d.user_uniforms[VertexShader][i]; setUserUniformValue(u); if (u.dirty) u.setGL(); } } if (!d.user_uniforms[FragmentShader].isEmpty()) { for (int i = 0; i < d.user_uniforms[FragmentShader].size(); ++i) { Uniform& u = d.user_uniforms[FragmentShader][i]; setUserUniformValue(u); if (u.dirty) u.setGL(); } } } // shader type changed, eq mat changed, or other material properties changed (e.g. texture, 8bit=>10bit) if (!d.update_builtin_uniforms && !material->isDirty()) return true; d.update_builtin_uniforms = false; // all texture ids should be binded when renderering even for packed plane! const int nb_planes = fmt.planeCount(); //number of texture id // TODO: sample2D array for (int i = 0; i < nb_planes; ++i) { // use glUniform1i to swap planes. swap uv: i => (3-i)%3 program()->setUniformValue(textureLocation(i), (GLint)i); } if (nb_planes < textureLocationCount()) { for (int i = nb_planes; i < textureLocationCount(); ++i) { program()->setUniformValue(textureLocation(i), (GLint)(nb_planes - 1)); } } program()->setUniformValue(colorMatrixLocation(), material->colorMatrix()); program()->setUniformValue(opacityLocation(), (GLfloat)1.0); if (d_func().u_to8 >= 0) program()->setUniformValue(d_func().u_to8, material->vectorTo8bit()); if (channelMapLocation() >= 0) program()->setUniformValue(channelMapLocation(), material->channelMap()); //program()->setUniformValue(matrixLocation(), ); //what about sgnode? state.combindMatrix()? if (texelSizeLocation() >= 0) program()->setUniformValueArray(texelSizeLocation(), material->texelSize().constData(), nb_planes); if (textureSizeLocation() >= 0) program()->setUniformValueArray(textureSizeLocation(), material->textureSize().constData(), nb_planes); // uniform end. attribute begins return true; } QByteArray VideoShader::shaderSourceFromFile(const QString &fileName) const { QFile f(qApp->applicationDirPath() + QStringLiteral("/") + fileName); if (!f.exists()) { f.setFileName(QStringLiteral(":/") + fileName); } if (!f.open(QIODevice::ReadOnly)) { qWarning("Can not load shader %s: %s", f.fileName().toUtf8().constData(), f.errorString().toUtf8().constData()); return QByteArray(); } QByteArray src = f.readAll(); f.close(); return src; } bool VideoShader::build(QOpenGLShaderProgram *shaderProgram) { if (shaderProgram->isLinked()) { qWarning("Shader program is already linked"); } shaderProgram->removeAllShaders(); shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader()); shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader()); int maxVertexAttribs = 0; DYGL(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs)); char const *const *attr = attributeNames(); for (int i = 0; attr[i]; ++i) { if (i >= maxVertexAttribs) { qFatal("List of attribute names is either too long or not null-terminated.\n" "Maximum number of attributes on this hardware is %i.\n" "Vertex shader:\n%s\n" "Fragment shader:\n%s\n", maxVertexAttribs, vertexShader(), fragmentShader()); } // why must min location == 0? if (*attr[i]) { shaderProgram->bindAttributeLocation(attr[i], i); qDebug("bind attribute: %s => %d", attr[i], i); } } if (!shaderProgram->link()) { qWarning("QSGMaterialShader: Shader compilation failed:"); qWarning() << shaderProgram->log(); return false; } return true; } void VideoShader::rebuildLater() { d_func().rebuild_program = true; } VideoMaterial::VideoMaterial() { } void VideoMaterial::setCurrentFrame(const VideoFrame &frame) { DPTR_D(VideoMaterial); d.update_texure = true; // TODO: move to another function before rendering? d.width = frame.width(); d.height = frame.height(); GLenum new_target = GL_TEXTURE_2D; // not d.target. because metadata "target" is not always set QByteArray t = frame.metaData(QStringLiteral("target")).toByteArray().toLower(); if (t == QByteArrayLiteral("rect")) new_target = GL_TEXTURE_RECTANGLE; if (new_target != d.target) { qDebug("texture target: %#x=>%#x", d.target, new_target); // FIXME: not thread safe (in qml) d.target = new_target; d.init_textures_required = true; } // TODO: check hw interop change. if change from an interop owns texture to not owns texture, VideoShader must recreate textures because old textures are deleted by previous interop const VideoFormat fmt(frame.format()); const int bpc_old = d.bpc; d.bpc = fmt.bitsPerComponent(); if (d.bpc > 8 && (d.bpc != bpc_old || d.video_format.isBigEndian() != fmt.isBigEndian())) { //FIXME: Assume first plane has 1 channel. So not work with NV21 const int range = (1 << d.bpc) - 1; // FFmpeg supports 9, 10, 12, 14, 16 bits // 10p in little endian: yyyyyyyy yy000000 => (L, L, L, A) //(yyyyyyyy, 000000yy)? if (OpenGLHelper::depth16BitTexture() < 16 || !OpenGLHelper::has16BitTexture() || fmt.isBigEndian()) { if (fmt.isBigEndian()) d.vec_to8 = QVector2D(256.0, 1.0)*255.0/(float)range; else d.vec_to8 = QVector2D(1.0, 256.0)*255.0/(float)range; d.colorTransform.setChannelDepthScale(1.0); } else { /// 16bit (R16 e.g.) texture does not support >8bit be channels /// 10p be: R2 R1(Host) = R1*2^8+R2 = 000000rr rrrrrrrr ->(GL) R=R2*2^8+R1 /// 10p le: R1 R2(Host) = rrrrrrrr rr000000 //d.vec_to8 = QVector2D(1.0, 0.0)*65535.0/(float)range; d.colorTransform.setChannelDepthScale(65535.0/(qreal)range, YUVA_DONE && fmt.hasAlpha()); } } else { if (d.bpc <= 8) d.colorTransform.setChannelDepthScale(1.0); } // http://forum.doom9.org/archive/index.php/t-160211.html ColorSpace cs = frame.colorSpace();// ColorSpace_RGB; if (cs == ColorSpace_Unknown) { if (fmt.isRGB()) { if (fmt.isPlanar()) cs = ColorSpace_GBR; else cs = ColorSpace_RGB; } else if (fmt.isXYZ()) { cs = ColorSpace_XYZ; } else { if (frame.width() >= 1280 || frame.height() > 576) //values from mpv cs = ColorSpace_BT709; else cs = ColorSpace_BT601; } } d.colorTransform.setInputColorSpace(cs); d.colorTransform.setInputColorRange(frame.colorRange()); // TODO: use graphics driver's color range option if possible static const ColorRange kRgbDispRange = qgetenv("QTAV_DISPLAY_RGB_RANGE") == "limited" ? ColorRange_Limited : ColorRange_Full; d.colorTransform.setOutputColorRange(kRgbDispRange); d.frame = frame; if (fmt != d.video_format) { qDebug() << fmt; qDebug("pixel format changed: %s => %s %d", qPrintable(d.video_format.name()), qPrintable(fmt.name()), fmt.pixelFormat()); d.video_format = fmt; d.init_textures_required = true; } } VideoFormat VideoMaterial::currentFormat() const { DPTR_D(const VideoMaterial); return d.video_format; } VideoShader* VideoMaterial::createShader() const { VideoShader *shader = new VideoShader(); // initialize shader shader->setVideoFormat(currentFormat()); shader->setTextureTarget(textureTarget()); shader->setMaterialType(type()); //resize texture locations to avoid access format later return shader; } QString VideoMaterial::typeName(qint32 value) { return QString("gl material 16to8bit: %1, planar: %2, has alpha: %3, 2d texture: %4, 2nd plane rg: %5, xyz: %6") .arg(!!(value&1)) .arg(!!(value&(1<<1))) .arg(!!(value&(1<<2))) .arg(!!(value&(1<<3))) .arg(!!(value&(1<<4))) .arg(!!(value&(1<<5))) ; } qint32 VideoMaterial::type() const { DPTR_D(const VideoMaterial); const VideoFormat &fmt = d.video_format; const bool tex_2d = d.target == GL_TEXTURE_2D; // 2d,alpha,planar,8bit const int rg_biplane = fmt.planeCount()==2 && !OpenGLHelper::useDeprecatedFormats() && OpenGLHelper::hasRG(); const int channel16_to8 = d.bpc > 8 && (OpenGLHelper::depth16BitTexture() < 16 || !OpenGLHelper::has16BitTexture() || fmt.isBigEndian()); return (fmt.isXYZ()<<5)|(rg_biplane<<4)|(tex_2d<<3)|(fmt.hasAlpha()<<2)|(fmt.isPlanar()<<1)|(channel16_to8); } bool VideoMaterial::bind() { DPTR_D(VideoMaterial); if (!d.ensureResources()) return false; const int nb_planes = d.textures.size(); //number of texture id if (nb_planes <= 0) return false; if (nb_planes > 4) //why? return false; d.ensureTextures(); for (int i = 0; i < nb_planes; ++i) { const int p = (i + 1) % nb_planes; //0 must active at last? d.uploadPlane(p, d.update_texure); } #if 0 //move to unbind should be fine if (d.update_texure) { d.update_texure = false; d.frame = VideoFrame(); //FIXME: why need this? we must unmap correctly before frame is reset. } #endif return true; } // TODO: move bindPlane to d.uploadPlane void VideoMaterialPrivate::uploadPlane(int p, bool updateTexture) { GLuint &tex = textures[p]; gl().ActiveTexture(GL_TEXTURE0 + p); //0 must active? if (!updateTexture) { DYGL(glBindTexture(target, tex)); return; } if (!frame.constBits(0)) { // try_pbo ? pbo_id : 0. 0= > interop.createHandle GLuint tex0 = tex; if (frame.map(GLTextureSurface, &tex, p)) { if (tex0 != tex) { if (owns_texture[tex0]) DYGL(glDeleteTextures(1, &tex0)); owns_texture.remove(tex0); owns_texture[tex] = false; } DYGL(glBindTexture(target, tex)); // glActiveTexture was called, but maybe bind to 0 in map return; } qWarning("map hw surface error"); return; } // FIXME: why happens on win? if (frame.bytesPerLine(p) <= 0) return; if (try_pbo) { //qDebug("bind PBO %d", p); QOpenGLBuffer &pb = pbo[p]; pb.bind(); // glMapBuffer() causes sync issue. // Call glBufferData() with NULL pointer before glMapBuffer(), the previous data in PBO will be discarded and // glMapBuffer() returns a new allocated pointer or an unused block immediately even if GPU is still working with the previous data. // https://www.opengl.org/wiki/Buffer_Object_Streaming#Buffer_re-specification pb.allocate(pb.size()); GLubyte* ptr = (GLubyte*)pb.map(QOpenGLBuffer::WriteOnly); if (ptr) { memcpy(ptr, frame.constBits(p), pb.size()); pb.unmap(); } } //qDebug("bpl[%d]=%d width=%d", p, frame.bytesPerLine(p), frame.planeWidth(p)); DYGL(glBindTexture(target, tex)); //setupQuality(); //DYGL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); //DYGL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures //glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride)); 8, 4, 2, 1 // glPixelStorei(GL_UNPACK_ROW_LENGTH, stride/glbpp); // for stride%glbpp > 0? DYGL(glTexSubImage2D(target, 0, 0, 0, texture_size[p].width(), texture_size[p].height(), data_format[p], data_type[p], try_pbo ? 0 : frame.constBits(p))); if (false) { //texture_size[].width()*gl_bpp != bytesPerLine[] for (int y = 0; y < plane0Size.height(); ++y) DYGL(glTexSubImage2D(target, 0, 0, y, texture_size[p].width(), 1, data_format[p], data_type[p], try_pbo ? 0 : frame.constBits(p)+y*plane0Size.width())); } //DYGL(glBindTexture(target, 0)); // no bind 0 because glActiveTexture was called if (try_pbo) { pbo[p].release(); } } void VideoMaterial::unbind() { DPTR_D(VideoMaterial); const int nb_planes = d.textures.size(); //number of texture id for (int i = 0; i < nb_planes; ++i) { // unbind planes in the same order as bind. GPU frame's unmap() can be async works, assume the work finished earlier if it started in map() earlier, thus unbind order matter const int p = (i + 1) % nb_planes; //0 must active at last? d.frame.unmap(&d.textures[p]); } if (d.update_texure) { d.update_texure = false; d.frame = VideoFrame(); //FIXME: why need this? we must unmap correctly before frame is reset. } setDirty(false); } int VideoMaterial::compare(const VideoMaterial *other) const { DPTR_D(const VideoMaterial); for (int i = 0; i < d.textures.size(); ++i) { const int diff = d.textures[i] - other->d_func().textures[i]; //TODO if (diff) return diff; } return d.bpc - other->bitsPerComponent(); } int VideoMaterial::textureTarget() const { return d_func().target; } bool VideoMaterial::isDirty() const { return d_func().dirty; } void VideoMaterial::setDirty(bool value) { d_func().dirty = value; } const QMatrix4x4& VideoMaterial::colorMatrix() const { return d_func().colorTransform.matrixRef(); } const QMatrix4x4 &VideoMaterial::channelMap() const { return d_func().channel_map; } int VideoMaterial::bitsPerComponent() const { return d_func().bpc; } QVector2D VideoMaterial::vectorTo8bit() const { return d_func().vec_to8; } int VideoMaterial::planeCount() const { return d_func().frame.planeCount(); } qreal VideoMaterial::brightness() const { return d_func().colorTransform.brightness(); } void VideoMaterial::setBrightness(qreal value) { d_func().colorTransform.setBrightness(value); d_func().dirty = true; } qreal VideoMaterial::contrast() const { return d_func().colorTransform.contrast(); } void VideoMaterial::setContrast(qreal value) { d_func().colorTransform.setContrast(value); d_func().dirty = true; } qreal VideoMaterial::hue() const { return d_func().colorTransform.hue(); } void VideoMaterial::setHue(qreal value) { d_func().colorTransform.setHue(value); d_func().dirty = true; } qreal VideoMaterial::saturation() const { return d_func().colorTransform.saturation(); } void VideoMaterial::setSaturation(qreal value) { d_func().colorTransform.setSaturation(value); d_func().dirty = true; } qreal VideoMaterial::validTextureWidth() const { return d_func().effective_tex_width_ratio; } QSize VideoMaterial::frameSize() const { return QSize(d_func().width, d_func().height); } QSizeF VideoMaterial::texelSize(int plane) const { DPTR_D(const VideoMaterial); return QSizeF(1.0/(qreal)d.texture_size[plane].width(), 1.0/(qreal)d.texture_size[plane].height()); } QVector VideoMaterial::texelSize() const { return d_func().v_texel_size; } QSize VideoMaterial::textureSize(int plane) const { return d_func().texture_size[plane]; } QVector VideoMaterial::textureSize() const { return d_func().v_texture_size; } QRectF VideoMaterial::normalizedROI(const QRectF &roi) const { return mapToTexture(0, roi, 1); } QPointF VideoMaterial::mapToTexture(int plane, const QPointF &p, int normalize) const { if (p.isNull()) return p; DPTR_D(const VideoMaterial); if (d.texture_size.isEmpty()) { //It should not happen if it's called in QtAV qWarning("textures not ready"); return p; } float x = p.x(); float y = p.y(); const qreal tex0W = d.texture_size[0].width(); const qreal s = tex0W/qreal(d.width); // only apply to unnormalized input roi if (normalize < 0) normalize = d.target != GL_TEXTURE_RECTANGLE; if (normalize) { if (qAbs(x) > 1) { x /= (float)tex0W; x *= s; } if (qAbs(y) > 1) y /= (float)d.height; } else { if (qAbs(x) <= 1) x *= (float)tex0W; else x *= s; if (qAbs(y) <= 1) y *= (float)d.height; } // multiply later because we compare with 1 before it x *= d.effective_tex_width_ratio; const qreal pw = d.video_format.normalizedWidth(plane); const qreal ph = d.video_format.normalizedHeight(plane); return QPointF(x*pw, y*ph); } // mapToTexture QRectF VideoMaterial::mapToTexture(int plane, const QRectF &roi, int normalize) const { DPTR_D(const VideoMaterial); if (d.texture_size.isEmpty()) { //It should not happen if it's called in QtAV qWarning("textures not ready"); return QRectF(); } const qreal tex0W = d.texture_size[0].width(); const qreal s = tex0W/qreal(d.width); // only apply to unnormalized input roi const qreal pw = d.video_format.normalizedWidth(plane); const qreal ph = d.video_format.normalizedHeight(plane); if (normalize < 0) normalize = d.target != GL_TEXTURE_RECTANGLE; if (!roi.isValid()) { if (normalize) return QRectF(0, 0, d.effective_tex_width_ratio, 1); //NOTE: not (0, 0, 1, 1) return QRectF(0, 0, tex0W*pw, d.height*ph); } float x = roi.x(); float w = roi.width(); //TODO: texturewidth float y = roi.y(); float h = roi.height(); if (normalize) { if (qAbs(x) > 1) { x /= tex0W; x *= s; } if (qAbs(y) > 1) y /= (float)d.height; if (qAbs(w) > 1) { w /= tex0W; w *= s; } if (qAbs(h) > 1) h /= (float)d.height; } else { //FIXME: what about ==1? if (qAbs(x) <= 1) x *= tex0W; else x *= s; if (qAbs(y) <= 1) y *= (float)d.height; if (qAbs(w) <= 1) w *= tex0W; else w *= s; if (qAbs(h) <= 1) h *= (float)d.height; } // multiply later because we compare with 1 before it x *= d.effective_tex_width_ratio; w *= d.effective_tex_width_ratio; return QRectF(x*pw, y*ph, w*pw, h*ph); } bool VideoMaterialPrivate::initPBO(int plane, int size) { QOpenGLBuffer &pb = pbo[plane]; if (!pb.isCreated()) { qDebug("Creating PBO for plane %d, size: %d...", plane, size); pb.create(); } if (!pb.bind()) { qWarning("Failed to bind PBO for plane %d!!!!!!", plane); try_pbo = false; return false; } //pb.setUsagePattern(QOpenGLBuffer::DynamicCopy); qDebug("Allocate PBO size %d", size); pb.allocate(size); pb.release(); //bind to 0 return true; } bool VideoMaterialPrivate::initTexture(GLuint tex, GLint internal_format, GLenum format, GLenum dataType, int width, int height) { DYGL(glBindTexture(target, tex)); setupQuality(); // This is necessary for non-power-of-two textures DYGL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); DYGL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); DYGL(glTexImage2D(target, 0, internal_format, width, height, 0/*border, ES not support*/, format, dataType, NULL)); DYGL(glBindTexture(target, 0)); return true; } VideoMaterialPrivate::~VideoMaterialPrivate() { // FIXME: when to delete if (!QOpenGLContext::currentContext()) { qWarning("No gl context"); return; } if (!textures.isEmpty()) { for (int i = 0; i < textures.size(); ++i) { GLuint &tex = textures[i]; if (owns_texture[tex]) DYGL(glDeleteTextures(1, &tex)); } //DYGL(glDeleteTextures(textures.size(), textures.data())); } owns_texture.clear(); textures.clear(); pbo.clear(); } bool VideoMaterialPrivate::updateTextureParameters(const VideoFormat& fmt) { // isSupported(pixfmt) if (!fmt.isValid()) return false; //http://www.berkelium.com/OpenGL/GDC99/internalformat.html //NV12: UV is 1 plane. 16 bits as a unit. GL_LUMINANCE4, 8, 16, ... 32? //GL_LUMINANCE, GL_LUMINANCE_ALPHA are deprecated in GL3, removed in GL3.1 //replaced by GL_RED, GL_RG, GL_RGB, GL_RGBA? for 1, 2, 3, 4 channel image //http://www.gamedev.net/topic/634850-do-luminance-textures-still-exist-to-opengl/ //https://github.com/kivy/kivy/issues/1738: GL_LUMINANCE does work on a Galaxy Tab 2. LUMINANCE_ALPHA very slow on Linux //ALPHA: vec4(1,1,1,A), LUMINANCE: (L,L,L,1), LUMINANCE_ALPHA: (L,L,L,A) const int nb_planes = fmt.planeCount(); internal_format.resize(nb_planes); data_format.resize(nb_planes); data_type.resize(nb_planes); if (!OpenGLHelper::videoFormatToGL(fmt, (GLint*)internal_format.constData(), (GLenum*)data_format.constData(), (GLenum*)data_type.constData(), &channel_map)) { qWarning() << "No OpenGL support for " << fmt; return false; } qDebug() << "texture internal format: " << internal_format; qDebug() << "texture data format: " << data_format; qDebug() << "texture data type: " << data_type; qDebug("///////////bpp %d, bpc: %d", fmt.bytesPerPixel(), fmt.bitsPerComponent()); for (int i = 0; i < nb_planes; ++i) { const int bpp_gl = OpenGLHelper::bytesOfGLFormat(data_format[i], data_type[i]); const int pad = std::ceil((qreal)(texture_size[i].width() - effective_tex_width[i])/(qreal)bpp_gl); texture_size[i].setWidth(std::ceil((qreal)texture_size[i].width()/(qreal)bpp_gl)); effective_tex_width[i] /= bpp_gl; //fmt.bytesPerPixel(i); v_texture_size[i] = QVector2D(texture_size[i].width(), texture_size[i].height()); //effective_tex_width_ratio = qDebug("texture width: %d - %d = pad: %d. bpp(gl): %d", texture_size[i].width(), effective_tex_width[i], pad, bpp_gl); if (target == GL_TEXTURE_RECTANGLE) v_texel_size[i] = QVector2D(1.0, 1.0); else v_texel_size[i] = QVector2D(1.0/(float)texture_size[i].width(), 1.0/(float)texture_size[i].height()); } /* * there are 2 fragment shaders: rgb and yuv. * only 1 texture for packed rgb. planar rgb likes yuv * To support both planar and packed yuv, and mixed yuv(NV12), we give a texture sample * for each channel. For packed, each (channel) texture sample is the same. For planar, * packed channels has the same texture sample. * But the number of actural textures we upload is plane count. * Which means the number of texture id equals to plane count */ // always delete old textures otherwise old textures are not initialized with correct parameters if (textures.size() > nb_planes) { //TODO: why check this? const int nb_delete = textures.size() - nb_planes; qDebug("try to delete %d textures", nb_delete); if (!textures.isEmpty()) { for (int i = 0; i < nb_delete; ++i) { GLuint &t = textures[nb_planes+i]; qDebug("try to delete texture[%d]: %u. can delete: %d", nb_planes+i, t, owns_texture[t]); if (owns_texture[t]) DYGL(glDeleteTextures(1, &t)); } //DYGL(glDeleteTextures(nb_delete, textures.data() + nb_planes)); } owns_texture.clear(); } textures.resize(nb_planes); init_textures_required = true; return true; } bool VideoMaterialPrivate::ensureResources() { if (!update_texure) //video frame is already uploaded and displayed return true; const VideoFormat &fmt = video_format; if (!fmt.isValid()) return false; // update textures if format, texture target, valid texture width(normalized), plane 0 size or plane 1 line size changed bool update_textures = init_textures_required; const int nb_planes = fmt.planeCount(); // effective size may change even if plane size not changed bool effective_tex_width_ratio_changed = true; for (int i = 0; i < nb_planes; ++i) { if ((qreal)frame.effectiveBytesPerLine(i)/(qreal)frame.bytesPerLine(i) == effective_tex_width_ratio) { effective_tex_width_ratio_changed = false; break; } } const int linsize0 = frame.bytesPerLine(0); if (update_textures || effective_tex_width_ratio_changed || linsize0 != plane0Size.width() || frame.height() != plane0Size.height() || (plane1_linesize > 0 && frame.bytesPerLine(1) != plane1_linesize)) { // no need to check height if plane 0 sizes are equal? update_textures = true; dirty = true; v_texel_size.resize(nb_planes); v_texture_size.resize(nb_planes); texture_size.resize(nb_planes); effective_tex_width.resize(nb_planes); effective_tex_width_ratio = 1.0; for (int i = 0; i < nb_planes; ++i) { qDebug("plane linesize %d: padded = %d, effective = %d. theoretical plane size: %dx%d", i, frame.bytesPerLine(i), frame.effectiveBytesPerLine(i), frame.planeWidth(i), frame.planeHeight(i)); // we have to consider size of opengl format. set bytesPerLine here and change to width later texture_size[i] = QSize(frame.bytesPerLine(i), frame.planeHeight(i)); effective_tex_width[i] = frame.effectiveBytesPerLine(i); //store bytes here, modify as width later // usually they are the same. If not, the difference is small. min value can avoid rendering the invalid data. effective_tex_width_ratio = qMin(effective_tex_width_ratio, (qreal)frame.effectiveBytesPerLine(i)/(qreal)frame.bytesPerLine(i)); } plane1_linesize = 0; if (nb_planes > 1) { // height? how about odd? plane1_linesize = frame.bytesPerLine(1); } /* let wr[i] = valid_bpl[i]/bpl[i], frame from avfilter maybe wr[1] < wr[0] e.g. original frame plane 0: 720/768; plane 1,2: 360/384, filtered frame plane 0: 720/736, ... (16 aligned?) */ qDebug("effective_tex_width_ratio=%f", effective_tex_width_ratio); plane0Size.setWidth(linsize0); plane0Size.setHeight(frame.height()); } if (update_textures) { updateTextureParameters(fmt); // check pbo support try_pbo = try_pbo && OpenGLHelper::isPBOSupported(); // check PBO support with bind() is fine, no need to check extensions if (try_pbo) { pbo.resize(nb_planes); for (int i = 0; i < nb_planes; ++i) { qDebug("Init PBO for plane %d", i); pbo[i] = QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); //QOpenGLBuffer is shared, must initialize 1 by 1 but not use fill if (!initPBO(i, frame.bytesPerLine(i)*frame.planeHeight(i))) { qWarning("Failed to init PBO for plane %d", i); break; } } } } return true; } bool VideoMaterialPrivate::ensureTextures() { if (!init_textures_required) return true; // create in bindPlane loop will cause wrong texture binding const int nb_planes = video_format.planeCount(); for (int p = 0; p < nb_planes; ++p) { GLuint &tex = textures[p]; if (tex) { // can be 0 if resized to a larger size qDebug("try to delete texture for plane %d (id=%u). can delete: %d", p, tex, owns_texture[tex]); if (owns_texture[tex]) DYGL(glDeleteTextures(1, &tex)); owns_texture.remove(tex); tex = 0; } if (!tex) { qDebug("creating texture for plane %d", p); GLuint* handle = (GLuint*)frame.createInteropHandle(&tex, GLTextureSurface, p); // take the ownership if (handle) { tex = *handle; owns_texture[tex] = true; } else { DYGL(glGenTextures(1, &tex)); owns_texture[tex] = true; initTexture(tex, internal_format[p], data_format[p], data_type[p], texture_size[p].width(), texture_size[p].height()); } qDebug("texture for plane %d is created (id=%u)", p, tex); } } init_textures_required = false; return true; } void VideoMaterialPrivate::setupQuality() { DYGL(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); DYGL(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); } } //namespace QtAV QtAV-1.12.0/src/opengl/VideoShaderObject.cpp000066400000000000000000000141671312235004300205130ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoShaderObject.h" #include "QtAV/private/VideoShader_p.h" #include #include #include namespace QtAV { class VideoShaderObjectPrivate : public VideoShaderPrivate { public: ~VideoShaderObjectPrivate() { qDeleteAll(sigMap[VertexShader]); qDeleteAll(sigMap[FragmentShader]); sigMap[VertexShader].clear(); sigMap[FragmentShader].clear(); } QVector sigMap[ShaderTypeCount]; }; VideoShaderObject::VideoShaderObject(QObject *parent) : QObject(parent) , VideoShader(*new VideoShaderObjectPrivate()) {} VideoShaderObject::VideoShaderObject(VideoShaderObjectPrivate &d, QObject *parent) : QObject(parent) , VideoShader(d) {} bool VideoShaderObject::event(QEvent *event) { DPTR_D(VideoShaderObject); if (event->type() != QEvent::DynamicPropertyChange) return QObject::event(event); QDynamicPropertyChangeEvent *e = static_cast(event); for (int shaderType = VertexShader; shaderType < ShaderTypeCount; ++shaderType) { QVector &uniforms = d.user_uniforms[shaderType]; for (int i = 0; i < uniforms.size(); ++i) { if (uniforms.at(i).name == e->propertyName()) { propertyChanged(i|(shaderType<<16)); } } } return QObject::event(event); } void VideoShaderObject::propertyChanged(int id) { DPTR_D(VideoShaderObject); const int st = id>>16; const int idx = id&0xffff; Uniform &u = d.user_uniforms[st][idx]; const QVariant v = property(u.name.constData()); u.set(v); //if (u.dirty) update(); } void VideoShaderObject::programReady() { DPTR_D(VideoShaderObject); // find property name. if has property, bind to property for (int st = VertexShader; st < ShaderTypeCount; ++st) { qDeleteAll(d.sigMap[st]); d.sigMap[st].clear(); const QVector &uniforms = d.user_uniforms[st]; for (int i = 0; i < uniforms.size(); ++i) { const Uniform& u = uniforms[i]; const int idx = metaObject()->indexOfProperty(u.name.constData()); if (idx < 0) { qDebug("VideoShaderObject has no meta property '%s'. Setting initial value from dynamic property", u.name.constData()); const_cast(u).set(property(u.name.constData())); continue; } QMetaProperty mp = metaObject()->property(idx); if (!mp.hasNotifySignal()) { qWarning("VideoShaderObject property '%s' has no signal", mp.name()); continue; } QMetaMethod mm = mp.notifySignal(); QSignalMapper *mapper = new QSignalMapper(); mapper->setMapping(this, i|(st<<16)); #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) connect(this, mm, mapper, mapper->metaObject()->method(mapper->metaObject()->indexOfSlot("map()"))); #else #endif connect(mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int))); d.sigMap[st].append(mapper); qDebug() << "set uniform property: " << u.name << property(u.name.constData()); propertyChanged(i|(st<<16)); // set the initial value } } //ready(); } class DynamicShaderObjectPrivate : public VideoShaderObjectPrivate { public: QString header; QString sampleFunc; QString pp; }; DynamicShaderObject::DynamicShaderObject(QObject *parent) : VideoShaderObject(*new DynamicShaderObjectPrivate(), parent) {} DynamicShaderObject::DynamicShaderObject(DynamicShaderObjectPrivate &d, QObject *parent) : VideoShaderObject(d, parent) {} QString DynamicShaderObject::header() const { return d_func().header; } void DynamicShaderObject::setHeader(const QString &text) { DPTR_D(DynamicShaderObject); if (d.header == text) return; d.header = text; Q_EMIT headerChanged(); rebuildLater(); } QString DynamicShaderObject::sample() const { return d_func().sampleFunc; } void DynamicShaderObject::setSample(const QString &text) { DPTR_D(DynamicShaderObject); if (d.sampleFunc == text) return; d.sampleFunc = text; Q_EMIT sampleChanged(); rebuildLater(); } QString DynamicShaderObject::postProcess() const { return d_func().pp; } void DynamicShaderObject::setPostProcess(const QString &text) { DPTR_D(DynamicShaderObject); if (d.pp == text) return; d.pp = text; Q_EMIT postProcessChanged(); rebuildLater(); } const char* DynamicShaderObject::userShaderHeader(QOpenGLShader::ShaderType st) const { if (st == QOpenGLShader::Vertex) return 0; if (d_func().header.isEmpty()) return 0; return d_func().header.toUtf8().constData(); } const char* DynamicShaderObject::userSample() const { if (d_func().sampleFunc.isEmpty()) return 0; return d_func().sampleFunc.toUtf8().constData(); } const char* DynamicShaderObject::userPostProcess() const { if (d_func().pp.isEmpty()) return 0; return d_func().pp.toUtf8().constData(); } } //namespace QtAV QtAV-1.12.0/src/opengl/gl_api.cpp000066400000000000000000000130111312235004300164050ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "gl_api.h" #include "OpenGLHelper.h" namespace QtAV { typedef void *(*GetProcAddress_t)(const char *); static GetProcAddress_t sGetProcAddress; void* GetProcAddress_Qt(const char *name) { if (!QOpenGLContext::currentContext()) return 0; void* p = (void*)QOpenGLContext::currentContext()->getProcAddress(QByteArray((const char*)name)); if (!p) { #if defined(Q_OS_WIN) && defined(QT_OPENGL_DYNAMIC) HMODULE handle = (HMODULE)QOpenGLContext::openGLModuleHandle(); if (handle) p = (void*)GetProcAddress(handle, name); #endif } //fallback to QOpenGLFunctions_1_0? return p; } static void* GetProcAddressWithExt(GetProcAddress_t get, const char *name) { void* fp = get(name); if (fp) return fp; static const char *ext[] = { "ARB", "OES", "EXT", "ANGLE", "NV" //TODO: MESA, INTEL? #ifdef __APPLE__ , "APPLE" #endif , NULL }; char f[512]; memcpy(f, name, strlen(name)); char* const p = f + strlen(name); for (int i = 0; ext[i]; ++i) { memcpy(p, ext[i], sizeof(ext[i]) + 1); //copy trail '\0' fp = get(f); if (fp) { printf("extension resolved: %s", f); return fp; } } return NULL; } static void* GetProcAddressDefault(const char *name) { return GetProcAddressWithExt(GetProcAddress_Qt, name); } #ifdef QT_OPENGL_DYNAMIC #define GETPROCADDRESS_RESOLVE // GL_RESOLVE_ES_X_X will link to lib or use getProcAddress #define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name) #else #ifdef GL_ES_VERSION_2_0 #define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_0(name) GL_RESOLVE_EXT(name) #define GL_RESOLVE_ES_3_1(name) GL_RESOLVE_EXT(name) #elif GL_ES_VERSION_3_0 #define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_1(name) GL_RESOLVE_NONE(name) //gl3ext is empty #elif GL_ES_VERSION_3_1 #define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name) #else #define GETPROCADDRESS_RESOLVE // GL_RESOLVE_ES_X_X will link to lib or use getProcAddress #define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name) #define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name) #endif #endif //QT_OPENGL_DYNAMIC #define GL_RESOLVE_NONE(name) do { name = NULL;}while(0) #define GL_RESOLVE_EXT(name) do {\ void** fp = (void**)(&name); \ *fp = GetProcAddressDefault("gl" # name); \ } while(0) #ifdef GETPROCADDRESS_RESOLVE #define GL_RESOLVE(name) GL_RESOLVE_EXT(name) #else #define GL_RESOLVE(name) do {\ name = ::gl##name; \ } while(0) #endif #define WGL_RESOLVE(name) do {\ void** fp = (void**)(&name); \ *fp = sGetProcAddress("wgl" # name); \ } while(0) void api::resolve() { //memset(g, 0, sizeof(g)); sGetProcAddress = GetProcAddressDefault; GL_RESOLVE(GetString); GL_RESOLVE(GetError); GL_RESOLVE(ActiveTexture); GL_RESOLVE(BindFramebuffer); GL_RESOLVE(GetUniformLocation); GL_RESOLVE(Uniform1f); GL_RESOLVE(Uniform2f); GL_RESOLVE(Uniform3f); GL_RESOLVE(Uniform4f); GL_RESOLVE(Uniform1fv); GL_RESOLVE(Uniform2fv); GL_RESOLVE(Uniform3fv); GL_RESOLVE(Uniform4fv); GL_RESOLVE(Uniform1iv); GL_RESOLVE(Uniform2iv); GL_RESOLVE(Uniform3iv); GL_RESOLVE(Uniform4iv); GL_RESOLVE(UniformMatrix2fv); GL_RESOLVE(UniformMatrix3fv); GL_RESOLVE(UniformMatrix4fv); GL_RESOLVE(BlendFuncSeparate); GL_RESOLVE_ES_3_1(GetTexLevelParameteriv); #ifdef Q_OS_WIN32 if (!OpenGLHelper::isOpenGLES()) { static const char* ext[] = { "WGL_NV_DX_interop2", "WGL_NV_DX_interop", NULL, }; if (OpenGLHelper::hasExtension(ext)) { // TODO: use wgl getprocaddress function (for qt4) qDebug("resolving WGL_NV_DX_interop..."); WGL_RESOLVE(DXSetResourceShareHandleNV); WGL_RESOLVE(DXOpenDeviceNV); WGL_RESOLVE(DXCloseDeviceNV); WGL_RESOLVE(DXRegisterObjectNV); WGL_RESOLVE(DXUnregisterObjectNV); WGL_RESOLVE(DXObjectAccessNV); WGL_RESOLVE(DXLockObjectsNV); WGL_RESOLVE(DXUnlockObjectsNV); } } #endif //Q_OS_WIN32 } api& gl() { static api g; if (!sGetProcAddress) { g.resolve(); } return g; } } //namespace QtAV QtAV-1.12.0/src/opengl/gl_api.h000066400000000000000000000140431312235004300160600ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GL_API_H #define QTAV_GL_API_H #ifndef QT_NO_OPENGL #include # if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #include #include # elif defined(QT_OPENGL_LIB) # if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) #include # endif //4.8 #include #include #include #define QOpenGLShaderProgram QGLShaderProgram typedef QGLBuffer QOpenGLBuffer; #define QOpenGLContext QGLContext #define QOpenGLShaderProgram QGLShaderProgram #define QOpenGLShader QGLShader #define QOpenGLFunctions QGLFunctions #define initializeOpenGLFunctions() initializeGLFunctions() #include # else //used by vaapi even qtopengl module is disabled # if defined(QT_OPENGL_ES_2) # if defined(Q_OS_MAC) // iOS #include #include # else // "uncontrolled" ES2 platforms #include # endif // Q_OS_MAC # else // non-ES2 platforms # if defined(Q_OS_MAC) #include # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 #include # endif #include # else #include # endif // Q_OS_MAC # endif // QT_OPENGL_ES_2 # endif #ifndef GL_APIENTRY #ifdef Q_OS_WIN #define GL_APIENTRY __stdcall #else #define GL_APIENTRY #endif #endif #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif //GL_BGRA is available in OpenGL >= 1.2 #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif #ifndef GL_BGR #define GL_BGR 0x80E0 #endif #ifndef GL_RED #define GL_RED 0x1903 #endif #ifndef GL_RG #define GL_RG 0x8227 #endif #ifndef GL_R8 #define GL_R8 0x8229 #endif #ifndef GL_R16 #define GL_R16 0x822A #endif #ifndef GL_RG8 #define GL_RG8 0x822B #endif #ifndef GL_RG16 #define GL_RG16 0x822C #endif #ifndef GL_RGB8 #define GL_RGB8 0x8051 #endif #ifndef GL_RGB16 #define GL_RGB16 0x8054 #endif #ifndef GL_RGBA8 #define GL_RGBA8 0x8058 #endif #ifndef GL_RGBA16 #define GL_RGBA16 0x805B #endif namespace QtAV { typedef char GLchar; // for qt4 mingw struct api; api& gl(); struct api { void resolve(); // TODO: static, so gl::GetString const GLubyte *(GL_APIENTRY *GetString)(GLenum); GLenum (GL_APIENTRY *GetError)(void); void (GL_APIENTRY *ActiveTexture)(GLenum); void (GL_APIENTRY *BindFramebuffer)(GLenum target, GLuint framebuffer); GLint (GL_APIENTRY *GetUniformLocation)(GLuint, const GLchar *); void (GL_APIENTRY *Uniform1f)(GLint, GLfloat); void (GL_APIENTRY *Uniform2f)(GLint, GLfloat, GLfloat); void (GL_APIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat); void (GL_APIENTRY *Uniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat); void (GL_APIENTRY *Uniform1fv)(GLint location, GLsizei count, const GLfloat *value); void (GL_APIENTRY *Uniform2fv)(GLint location, GLsizei count, const GLfloat *value); void (GL_APIENTRY *Uniform3fv)(GLint location, GLsizei count, const GLfloat *value); void (GL_APIENTRY *Uniform4fv)(GLint location, GLsizei count, const GLfloat *value); void (GL_APIENTRY *Uniform1iv)(GLint location, GLsizei count, const GLint *value); void (GL_APIENTRY *Uniform2iv)(GLint location, GLsizei count, const GLint *value); void (GL_APIENTRY *Uniform3iv)(GLint location, GLsizei count, const GLint *value); void (GL_APIENTRY *Uniform4iv)(GLint location, GLsizei count, const GLint *value); void (GL_APIENTRY *UniformMatrix2fv)(GLint, GLsizei, GLboolean, const GLfloat *); void (GL_APIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat *); void (GL_APIENTRY *UniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void (GL_APIENTRY *BlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); // Before using the following members, check null ptr first because they are not valid everywhere // ES3.1 void (GL_APIENTRY *GetTexLevelParameteriv)(GLenum, GLint, GLenum, GLint *); #if defined(Q_OS_WIN32) //#include //not found in vs2013 //https://www.opengl.org/registry/specs/NV/DX_interop.txt #ifndef WGL_ACCESS_READ_ONLY_NV #define WGL_ACCESS_READ_ONLY_NV 0x00000000 #define WGL_ACCESS_READ_WRITE_NV 0x00000001 #define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 #endif BOOL (WINAPI* DXSetResourceShareHandleNV)(void *dxObject, HANDLE shareHandle); HANDLE (WINAPI* DXOpenDeviceNV)(void *dxDevice); BOOL (WINAPI* DXCloseDeviceNV)(HANDLE hDevice); HANDLE (WINAPI* DXRegisterObjectNV)(HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); BOOL (WINAPI* DXUnregisterObjectNV)(HANDLE hDevice, HANDLE hObject); BOOL (WINAPI* DXObjectAccessNV)(HANDLE hObject, GLenum access); BOOL (WINAPI* DXLockObjectsNV)(HANDLE hDevice, GLint count, HANDLE *hObjects); BOOL (WINAPI* DXUnlockObjectsNV)(HANDLE hDevice, GLint count, HANDLE *hObjects); #endif }; } //namespace QtAV #endif //QT_NO_OPENGL #endif //QTAV_GL_API_H QtAV-1.12.0/src/output/000077500000000000000000000000001312235004300145265ustar00rootroot00000000000000QtAV-1.12.0/src/output/AVOutput.cpp000066400000000000000000000114271312235004300167660ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AVOutput.h" #include "QtAV/private/AVOutput_p.h" #include "QtAV/Filter.h" #include "QtAV/FilterContext.h" #include "filter/FilterManager.h" #include "output/OutputSet.h" #include "utils/Logger.h" namespace QtAV { AVOutputPrivate::~AVOutputPrivate() { cond.wakeAll(); //WHY: failed to wake up } AVOutput::AVOutput() { } AVOutput::AVOutput(AVOutputPrivate &d) :DPTR_INIT(&d) { } AVOutput::~AVOutput() { pause(false); //Does not work. cond may still waiting when destroyed detach(); DPTR_D(AVOutput); if (d.filter_context) { delete d.filter_context; d.filter_context = 0; } foreach (Filter *f, d.pending_uninstall_filters) { d.filters.removeAll(f); } QList::iterator it = d.filters.begin(); while (it != d.filters.end()) { // if not uninstall here, if AVOutput is also an QObject (for example, widget based renderers) // then qobject children filters will be deleted when parent is destroying and call FilterManager::uninstallFilter() // and FilterManager::instance().unregisterFilter(filter, this) too late that AVOutput is almost be destroyed uninstallFilter(*it); // 1 filter has 1 target. so if has output filter in manager, the output is this output /*FilterManager::instance().hasOutputFilter(*it) && */ if ((*it)->isOwnedByTarget() && !(*it)->parent()) delete *it; ++it; } d.filters.clear(); } bool AVOutput::isAvailable() const { return d_func().available; } void AVOutput::pause(bool p) { DPTR_D(AVOutput); if (d.paused == p) return; d.paused = p; } bool AVOutput::isPaused() const { return d_func().paused; } //TODO: how to call this automatically before write()? bool AVOutput::tryPause() { DPTR_D(AVOutput); if (!d.paused) return false; QMutexLocker lock(&d.mutex); Q_UNUSED(lock); d.cond.wait(&d.mutex); return true; } void AVOutput::addOutputSet(OutputSet *set) { d_func().output_sets.append(set); } void AVOutput::removeOutputSet(OutputSet *set) { d_func().output_sets.removeAll(set); } void AVOutput::attach(OutputSet *set) { set->addOutput(this); } void AVOutput::detach(OutputSet *set) { DPTR_D(AVOutput); if (set) { set->removeOutput(this); return; } foreach(OutputSet *set, d.output_sets) { set->removeOutput(this); } } QList& AVOutput::filters() { return d_func().filters; } void AVOutput::setStatistics(Statistics *statistics) { DPTR_D(AVOutput); d.statistics = statistics; } bool AVOutput::installFilter(Filter *filter, int index) { return onInstallFilter(filter, index); } bool AVOutput::onInstallFilter(Filter *filter, int index) { if (!FilterManager::instance().registerFilter(filter, this, index)) { return false; } DPTR_D(AVOutput); d.filters = FilterManager::instance().outputFilters(this); return true; } /* * FIXME: how to ensure thread safe using mutex etc? for a video filter, both are in main thread. * an audio filter on audio output may be in audio thread */ bool AVOutput::uninstallFilter(Filter *filter) { return onUninstallFilter(filter); } bool AVOutput::onUninstallFilter(Filter *filter) { DPTR_D(AVOutput); FilterManager::instance().unregisterFilter(filter, this); d.pending_uninstall_filters.push_back(filter); return true; } void AVOutput::hanlePendingTasks() { onHanlePendingTasks(); } bool AVOutput::onHanlePendingTasks() { DPTR_D(AVOutput); if (d.pending_uninstall_filters.isEmpty()) return false; foreach (Filter *filter, d.pending_uninstall_filters) { d.filters.removeAll(filter); } d.pending_uninstall_filters.clear(); return true; } } //namespace QtAV QtAV-1.12.0/src/output/OutputSet.cpp000066400000000000000000000065141312235004300172140ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "output/OutputSet.h" #include "QtAV/AVPlayer.h" #include "QtAV/VideoRenderer.h" namespace QtAV { OutputSet::OutputSet(AVPlayer *player): QObject(player) , mCanPauseThread(false) , mpPlayer(player) , mPauseCount(0) { } OutputSet::~OutputSet() { mCond.wakeAll(); //delete? may be deleted by vo's parent clearOutputs(); } void OutputSet::lock() { mMutex.lock(); } void OutputSet::unlock() { mMutex.unlock(); } QList OutputSet::outputs() { return mOutputs; } void OutputSet::sendVideoFrame(const VideoFrame &frame) { if (mOutputs.isEmpty()) return; VideoFrame f(frame); foreach(AVOutput *output, mOutputs) { if (!output->isAvailable()) continue; VideoRenderer *vo = static_cast(output); // TODO: sort vo by supported formats when a new vo is added to reduce convertion if (!vo->isSupported(frame.pixelFormat())) f = frame.to(vo->preferredPixelFormat()); vo->receive(f); } } void OutputSet::clearOutputs() { QMutexLocker lock(&mMutex); Q_UNUSED(lock); if (mOutputs.isEmpty()) return; foreach(AVOutput *output, mOutputs) { output->removeOutputSet(this); } mOutputs.clear(); } void OutputSet::addOutput(AVOutput *output) { QMutexLocker lock(&mMutex); Q_UNUSED(lock); mOutputs.append(output); output->addOutputSet(this); } void OutputSet::removeOutput(AVOutput *output) { QMutexLocker lock(&mMutex); Q_UNUSED(lock); mOutputs.removeAll(output); output->removeOutputSet(this); } void OutputSet::notifyPauseChange(AVOutput *output) { if (output->isPaused()) { mPauseCount++; if (mPauseCount == mOutputs.size()) { mCanPauseThread = true; } //DO NOT pause here because it must be paused in AVThread } else { mPauseCount--; mCanPauseThread = false; if (mPauseCount == mOutputs.size() - 1) { resumeThread(); } } } bool OutputSet::canPauseThread() const { return mCanPauseThread; } bool OutputSet::pauseThread(unsigned long timeout) { QMutexLocker lock(&mMutex); Q_UNUSED(lock); return mCond.wait(&mMutex, timeout); } void OutputSet::resumeThread() { mCond.wakeAll(); } } //namespace QtAV QtAV-1.12.0/src/output/OutputSet.h000066400000000000000000000051321312235004300166540ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OUTPUTSET_H #define QTAV_OUTPUTSET_H #include #include #include #include "QtAV/QtAV_Global.h" #include "QtAV/AVOutput.h" namespace QtAV { class AVPlayer; class VideoFrame; class OutputSet : public QObject { Q_OBJECT public: OutputSet(AVPlayer *player); virtual ~OutputSet(); //required when accessing renderers void lock(); void unlock(); //implicity shared //QList outputs(); QList outputs(); //each(OutputOperation(data)) // void sendData(const QByteArray& data); void sendVideoFrame(const VideoFrame& frame); void clearOutputs(); void addOutput(AVOutput* output); void notifyPauseChange(AVOutput *output); bool canPauseThread() const; //in AVThread bool pauseThread(unsigned long timeout = ULONG_MAX); //There are 2 ways to pause AVThread: 1. pause thread directly. 2. pause all outputs /* * in user thread when pause count < set size. * 1. AVPlayer.pause(false) in player thread then call each output pause(false) * 2. shortcut for AVOutput.pause(false) */ void resumeThread(); public slots: //connect to renderer->aboutToClose(). test whether delete on close void removeOutput(AVOutput *output); private: volatile bool mCanPauseThread; AVPlayer *mpPlayer; int mPauseCount; //pause AVThread if equals to mOutputs.size() QList mOutputs; QMutex mMutex; QWaitCondition mCond; //pause }; } //namespace QtAV #endif // QTAV_OUTPUTSET_H QtAV-1.12.0/src/output/audio/000077500000000000000000000000001312235004300156275ustar00rootroot00000000000000QtAV-1.12.0/src/output/audio/AudioOutput.cpp000066400000000000000000000575231312235004300206310ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/AudioOutput.h" #include "QtAV/private/AVOutput_p.h" #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/AVCompat.h" #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #include #else #include typedef QTime QElapsedTimer; #endif #include "utils/ring.h" #include "utils/Logger.h" #define AO_USE_TIMER 1 namespace QtAV { // chunk static const int kBufferSamples = 512; static const int kBufferCount = 8*2; // may wait too long at the beginning (oal) if too large. if buffer count is too small, can not play for high sample rate audio. typedef void (*scale_samples_func)(quint8 *dst, const quint8 *src, int nb_samples, int volume, float volumef); /// from libavfilter/af_volume begin static inline void scale_samples_u8(quint8 *dst, const quint8 *src, int nb_samples, int volume, float) { for (int i = 0; i < nb_samples; i++) dst[i] = av_clip_uint8(((((qint64)src[i] - 128) * volume + 128) >> 8) + 128); } static inline void scale_samples_u8_small(quint8 *dst, const quint8 *src, int nb_samples, int volume, float) { for (int i = 0; i < nb_samples; i++) dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128); } static inline void scale_samples_s16(quint8 *dst, const quint8 *src, int nb_samples, int volume, float) { int16_t *smp_dst = (int16_t *)dst; const int16_t *smp_src = (const int16_t *)src; for (int i = 0; i < nb_samples; i++) smp_dst[i] = av_clip_int16(((qint64)smp_src[i] * volume + 128) >> 8); } static inline void scale_samples_s16_small(quint8 *dst, const quint8 *src, int nb_samples, int volume, float) { int16_t *smp_dst = (int16_t *)dst; const int16_t *smp_src = (const int16_t *)src; for (int i = 0; i < nb_samples; i++) smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8); } static inline void scale_samples_s32(quint8 *dst, const quint8 *src, int nb_samples, int volume, float) { qint32 *smp_dst = (qint32 *)dst; const qint32 *smp_src = (const qint32 *)src; for (int i = 0; i < nb_samples; i++) smp_dst[i] = av_clipl_int32((((qint64)smp_src[i] * volume + 128) >> 8)); } /// from libavfilter/af_volume end //TODO: simd template static inline void scale_samples(quint8 *dst, const quint8 *src, int nb_samples, int, float volume) { T *smp_dst = (T *)dst; const T *smp_src = (const T *)src; for (int i = 0; i < nb_samples; ++i) smp_dst[i] = smp_src[i] * (T)volume; } scale_samples_func get_scaler(AudioFormat::SampleFormat fmt, qreal vol, int* voli) { int v = (int)(vol * 256.0 + 0.5); if (voli) *voli = v; switch (fmt) { case AudioFormat::SampleFormat_Unsigned8: case AudioFormat::SampleFormat_Unsigned8Planar: return v < 0x1000000 ? scale_samples_u8_small : scale_samples_u8; case AudioFormat::SampleFormat_Signed16: case AudioFormat::SampleFormat_Signed16Planar: return v < 0x10000 ? scale_samples_s16_small : scale_samples_s16; case AudioFormat::SampleFormat_Signed32: case AudioFormat::SampleFormat_Signed32Planar: return scale_samples_s32; case AudioFormat::SampleFormat_Float: case AudioFormat::SampleFormat_FloatPlanar: return scale_samples; case AudioFormat::SampleFormat_Double: case AudioFormat::SampleFormat_DoublePlanar: return scale_samples; default: return 0; } } class AudioOutputPrivate : public AVOutputPrivate { public: AudioOutputPrivate(): mute(false) , sw_volume(true) , sw_mute(true) , volume_i(256) , vol(1) , speed(1.0) , nb_buffers(kBufferCount) , buffer_samples(kBufferSamples) , features(0) , play_pos(0) , processed_remain(0) , msecs_ahead(0) , scale_samples(0) , backend(0) , update_backend(true) , index_enqueue(-1) , index_deuqueue(-1) , frame_infos(ring(nb_buffers)) { available = false; } virtual ~AudioOutputPrivate(); void playInitialData(); //required by some backends, e.g. openal void onCallback() { cond.wakeAll();} virtual void uwait(qint64 us) { QMutexLocker lock(&mutex); Q_UNUSED(lock); cond.wait(&mutex, (us+500LL)/1000LL); } struct FrameInfo { FrameInfo(const QByteArray& d = QByteArray(), qreal t = 0, int us = 0) : timestamp(t), duration(us), data(d) {} qreal timestamp; int duration; // in us QByteArray data; }; void resetStatus() { play_pos = 0; processed_remain = 0; msecs_ahead = 0; #if AO_USE_TIMER timer.invalidate(); #endif frame_infos = ring(nb_buffers); } /// call this if sample format or volume is changed void updateSampleScaleFunc(); void tryVolume(qreal value); void tryMute(bool value); bool mute; bool sw_volume, sw_mute; int volume_i; qreal vol; qreal speed; AudioFormat format; AudioFormat requested; //AudioFrame audio_frame; quint32 nb_buffers; qint32 buffer_samples; int features; int play_pos; // index or bytes int processed_remain; int msecs_ahead; #if AO_USE_TIMER QElapsedTimer timer; #endif scale_samples_func scale_samples; AudioOutputBackend *backend; bool update_backend; QStringList backends; //private: // the index of current enqueue/dequeue int index_enqueue, index_deuqueue; ring frame_infos; }; void AudioOutputPrivate::updateSampleScaleFunc() { scale_samples = get_scaler(format.sampleFormat(), vol, &volume_i); } AudioOutputPrivate::~AudioOutputPrivate() { if (backend) { backend->close(); delete backend; } } void AudioOutputPrivate::playInitialData() { const char c = (format.sampleFormat() == AudioFormat::SampleFormat_Unsigned8 || format.sampleFormat() == AudioFormat::SampleFormat_Unsigned8Planar) ? 0x80 : 0; for (quint32 i = 0; i < nb_buffers; ++i) { const QByteArray data(backend->buffer_size, c); backend->write(data); // fill silence byte, not always 0. AudioFormat.silenceByte frame_infos.push_back(FrameInfo(data, 0, 0)); // initial data can be small (1 instead of buffer_samples) } backend->play(); } void AudioOutputPrivate::tryVolume(qreal value) { // if not open, try later if (!available) return; if (features & AudioOutput::SetVolume) { sw_volume = !backend->setVolume(value); //if (!qFuzzyCompare(backend->volume(), value)) // sw_volume = true; if (sw_volume) backend->setVolume(1.0); // TODO: partial software? } else { sw_volume = true; } } void AudioOutputPrivate::tryMute(bool value) { // if not open, try later if (!available) return; if ((features & AudioOutput::SetMute) && backend) sw_mute = !backend->setMute(value); else sw_mute = true; } AudioOutput::AudioOutput(QObject* parent) : QObject(parent) , AVOutput(*new AudioOutputPrivate()) { qDebug() << "Registered audio backends: " << AudioOutput::backendsAvailable(); // call this to register setBackends(AudioOutputBackend::defaultPriority()); //ensure a backend is available } AudioOutput::~AudioOutput() { close(); } extern void AudioOutput_RegisterAll(); //why vc link error if put in the following a exported class member function? QStringList AudioOutput::backendsAvailable() { AudioOutput_RegisterAll(); static QStringList all; if (!all.isEmpty()) return all; AudioOutputBackendId* i = NULL; while ((i = AudioOutputBackend::next(i)) != NULL) { all.append(AudioOutputBackend::name(*i)); } all = AudioOutputBackend::defaultPriority() << all; all.removeDuplicates(); return all; } void AudioOutput::setBackends(const QStringList &backendNames) { DPTR_D(AudioOutput); if (d.backends == backendNames) return; d.update_backend = true; d.backends = backendNames; // create backend here because we have to check format support before open which needs a backend d.update_backend = false; if (d.backend) { d.backend->close(); delete d.backend; d.backend = 0; } // TODO: empty backends use dummy backend if (!d.backends.isEmpty()) { foreach (const QString& b, d.backends) { d.backend = AudioOutputBackend::create(b.toLatin1().constData()); if (!d.backend) continue; if (d.backend->available) break; delete d.backend; d.backend = NULL; } } if (d.backend) { // default: set all features when backend is ready setDeviceFeatures(d.backend->supportedFeatures()); // connect volumeReported connect(d.backend, SIGNAL(volumeReported(qreal)), SLOT(reportVolume(qreal))); connect(d.backend, SIGNAL(muteReported(bool)), SLOT(reportMute(bool))); } Q_EMIT backendsChanged(); } QStringList AudioOutput::backends() const { return d_func().backends; } QString AudioOutput::backend() const { DPTR_D(const AudioOutput); if (d.backend) return d.backend->name(); return QString(); } void AudioOutput::flush() { DPTR_D(AudioOutput); while (!d.frame_infos.empty()) { if (d.backend) d.backend->flush(); waitForNextBuffer(); } } void AudioOutput::clear() { DPTR_D(AudioOutput); if (!d.backend || !d.backend->clear()) flush(); d.resetStatus(); } bool AudioOutput::open() { DPTR_D(AudioOutput); QMutexLocker lock(&d.mutex); Q_UNUSED(lock); d.available = false; d.paused = false; d.resetStatus(); if (!d.backend) return false; d.backend->audio = this; d.backend->buffer_size = bufferSize(); d.backend->buffer_count = bufferCount(); d.backend->format = audioFormat(); // TODO: open next backend if fail and emit backendChanged() if (!d.backend->open()) return false; d.available = true; d.tryVolume(volume()); d.tryMute(isMute()); d.playInitialData(); return true; } bool AudioOutput::close() { DPTR_D(AudioOutput); QMutexLocker lock(&d.mutex); Q_UNUSED(lock); d.available = false; d.paused = false; d.resetStatus(); if (!d.backend) return false; // TODO: drain() before close d.backend->audio = 0; return d.backend->close(); } bool AudioOutput::isOpen() const { return d_func().available; } bool AudioOutput::play(const QByteArray &data, qreal pts) { DPTR_D(AudioOutput); if (!d.backend) return false; if (!receiveData(data, pts)) return false; return d.backend->play(); } void AudioOutput::pause(bool value) { DPTR_D(AudioOutput); d.paused = value; // backend pause? Without backend pause, the buffered data will be played } bool AudioOutput::isPaused() const { return d_func().paused; } bool AudioOutput::receiveData(const QByteArray &data, qreal pts) { DPTR_D(AudioOutput); if (isPaused()) return false; QByteArray queue_data(data); if (isMute() && d.sw_mute) { char s = 0; if (d.format.isUnsigned() && !d.format.isFloat()) s = 1<<((d.format.bytesPerSample() << 3)-1); queue_data.fill(s); } else { if (!qFuzzyCompare(volume(), (qreal)1.0) && d.sw_volume && d.scale_samples ) { // TODO: af_volume needs samples_align to get nb_samples const int nb_samples = queue_data.size()/d.format.bytesPerSample(); quint8 *dst = (quint8*)queue_data.constData(); d.scale_samples(dst, dst, nb_samples, d.volume_i, volume()); } } // wait after all data processing finished to reduce time error if (!waitForNextBuffer()) { // TODO: wait or not parameter, set by user (async) qWarning("ao backend maybe not open"); d.resetStatus(); return false; } d.frame_infos.push_back(AudioOutputPrivate::FrameInfo(queue_data, pts, d.format.durationForBytes(queue_data.size()))); return d.backend->write(queue_data); // backend is not null here } AudioFormat AudioOutput::setAudioFormat(const AudioFormat& format) { DPTR_D(AudioOutput); // no support check because that may require an open device(AL) while this function is called before ao.open() if (d.format == format) return format; d.requested = format; if (!d.backend) { d.format = AudioFormat(); d.scale_samples = NULL; return AudioFormat(); } if (d.backend->isSupported(format)) { d.format = format; d.updateSampleScaleFunc(); return format; } AudioFormat af(format); // set channel layout first so that isSupported(AudioFormat) will not always false if (!d.backend->isSupported(format.channelLayout())) af.setChannelLayout(AudioFormat::ChannelLayout_Stereo); // assume stereo is supported bool check_up = af.bytesPerSample() == 1; while (!d.backend->isSupported(af) && !d.backend->isSupported(af.sampleFormat())) { if (af.isPlanar()) { af.setSampleFormat(ToPacked(af.sampleFormat())); continue; } if (af.isFloat()) { if (af.bytesPerSample() == 8) af.setSampleFormat(AudioFormat::SampleFormat_Float); else af.setSampleFormat(AudioFormat::SampleFormat_Signed32); } else { af.setSampleFormat(AudioFormat::make(af.bytesPerSample()/2, false, (af.bytesPerSample() == 2) | af.isUnsigned() /* U8, no S8 */, false)); } if (af.bytesPerSample() < 1) { if (!check_up) { qWarning("No sample format found"); break; } af.setSampleFormat(AudioFormat::SampleFormat_Float); check_up = false; continue; } } d.format = af; d.updateSampleScaleFunc(); return af; } const AudioFormat& AudioOutput::requestedFormat() const { return d_func().requested; } const AudioFormat& AudioOutput::audioFormat() const { return d_func().format; } void AudioOutput::setVolume(qreal value) { DPTR_D(AudioOutput); if (value < 0.0) return; if (d.vol == value) //fuzzy compare? return; d.vol = value; Q_EMIT volumeChanged(value); d.updateSampleScaleFunc(); d.tryVolume(value); } qreal AudioOutput::volume() const { return qMax(d_func().vol, 0); } void AudioOutput::setMute(bool value) { DPTR_D(AudioOutput); if (d.mute == value) return; d.mute = value; Q_EMIT muteChanged(value); d.tryMute(value); } bool AudioOutput::isMute() const { return d_func().mute; } void AudioOutput::setSpeed(qreal speed) { d_func().speed = speed; } qreal AudioOutput::speed() const { return d_func().speed; } bool AudioOutput::isSupported(const AudioFormat &format) const { DPTR_D(const AudioOutput); if (!d.backend) return false; return d.backend->isSupported(format); } int AudioOutput::bufferSize() const { return bufferSamples() * d_func().format.bytesPerSample(); } int AudioOutput::bufferSamples() const { return d_func().buffer_samples; } void AudioOutput::setBufferSamples(int value) { d_func().buffer_samples = value; } int AudioOutput::bufferCount() const { return d_func().nb_buffers; } void AudioOutput::setBufferCount(int value) { d_func().nb_buffers = value; } // no virtual functions inside because it can be called in ctor void AudioOutput::setDeviceFeatures(DeviceFeatures value) { DPTR_D(AudioOutput); //Qt5: QFlags::Int (int or uint) const int s(supportedDeviceFeatures()); const int f(value); if (d.features == (f & s)) return; d.features = (f & s); emit deviceFeaturesChanged(); } AudioOutput::DeviceFeatures AudioOutput::deviceFeatures() const { return (DeviceFeature)d_func().features; } AudioOutput::DeviceFeatures AudioOutput::supportedDeviceFeatures() const { DPTR_D(const AudioOutput); if (!d.backend) return NoFeature; return d.backend->supportedFeatures(); } bool AudioOutput::waitForNextBuffer() // parameter bool wait: if no wait and no next buffer, return false { DPTR_D(AudioOutput); if (d.frame_infos.empty()) return true; //don't return even if we can add buffer because we don't know when a buffer is processed and we have /to update dequeue index // openal need enqueue to a dequeued buffer! why sl crash bool no_wait = false;//d.canAddBuffer(); const AudioOutputBackend::BufferControl f = d.backend->bufferControl(); int remove = 0; const AudioOutputPrivate::FrameInfo &fi(d.frame_infos.front()); if (f & AudioOutputBackend::Blocking) { remove = 1; } else if (f & AudioOutputBackend::CountCallback) { d.backend->acquireNextBuffer(); remove = 1; } else if (f & AudioOutputBackend::BytesCallback) { #if AO_USE_TIMER d.timer.restart(); #endif //AO_USE_TIMER int processed = d.processed_remain; d.processed_remain = d.backend->getWritableBytes(); if (d.processed_remain < 0) return false; const int next = fi.data.size(); //qDebug("remain: %d-%d, size: %d, next: %d", processed, d.processed_remain, d.data.size(), next); qint64 last_wait = 0LL; while (d.processed_remain - processed < next || d.processed_remain < fi.data.size()) { //implies next > 0 const qint64 us = d.format.durationForBytes(next - (d.processed_remain - processed)); d.uwait(us); d.processed_remain = d.backend->getWritableBytes(); if (d.processed_remain < 0) return false; #if AO_USE_TIMER if (!d.timer.isValid()) { qWarning("invalid timer. closed in another thread"); return false; } #endif if (us >= last_wait #if AO_USE_TIMER && d.timer.elapsed() > 1000 #endif //AO_USE_TIMER ) { return false; } last_wait = us; } processed = d.processed_remain - processed; d.processed_remain -= fi.data.size(); //ensure d.processed_remain later is greater remove = -processed; // processed_this_period } else if (f & AudioOutputBackend::PlayedBytes) { d.processed_remain = d.backend->getPlayedBytes(); const int next = fi.data.size(); // TODO: avoid always 0 // TODO: compare processed_remain with fi.data.size because input chuncks can be in different sizes while (!no_wait && d.processed_remain < next) { const qint64 us = d.format.durationForBytes(next - d.processed_remain); if (us < 1000LL) d.uwait(1000LL); else d.uwait(us); d.processed_remain = d.backend->getPlayedBytes(); // what if s always 0? } remove = -d.processed_remain; } else if (f & AudioOutputBackend::PlayedCount) { #if AO_USE_TIMER if (!d.timer.isValid()) d.timer.start(); qint64 elapsed = 0; #endif //AO_USE_TIMER int c = d.backend->getPlayedCount(); // TODO: avoid always 0 qint64 us = 0; while (!no_wait && c < 1) { if (us <= 0) us = fi.duration; #if AO_USE_TIMER elapsed = d.timer.restart(); if (elapsed > 0 && us > elapsed*1000LL) us -= elapsed*1000LL; if (us < 1000LL) us = 1000LL; //opensl crash if 1ms #endif //AO_USE_TIMER d.uwait(us); c = d.backend->getPlayedCount(); } // what if c always 0? remove = c; } else if (f & AudioOutputBackend::OffsetBytes) { //TODO: similar to Callback+getWritableBytes() int s = d.backend->getOffsetByBytes(); int processed = s - d.play_pos; //qDebug("s: %d, play_pos: %d, processed: %d, bufferSizeTotal: %d", s, d.play_pos, processed, bufferSizeTotal()); if (processed < 0) processed += bufferSizeTotal(); d.play_pos = s; const int next = fi.data.size(); int writable_size = d.processed_remain + processed; while (!no_wait && (/*processed < next ||*/ writable_size < fi.data.size()) && next > 0) { const qint64 us = d.format.durationForBytes(next - writable_size); d.uwait(us); s = d.backend->getOffsetByBytes(); processed += s - d.play_pos; if (processed < 0) processed += bufferSizeTotal(); writable_size = d.processed_remain + processed; d.play_pos = s; } d.processed_remain += processed; d.processed_remain -= fi.data.size(); //ensure d.processed_remain later is greater remove = -processed; } else if (f & AudioOutputBackend::OffsetIndex) { int n = d.backend->getOffset(); int processed = n - d.play_pos; if (processed < 0) processed += bufferCount(); d.play_pos = n; // TODO: timer // TODO: avoid always 0 while (!no_wait && processed < 1) { d.uwait(fi.duration); n = d.backend->getOffset(); processed = n - d.play_pos; if (processed < 0) processed += bufferCount(); d.play_pos = n; } remove = processed; } else { qFatal("User defined waitForNextBuffer() not implemented!"); return false; } if (remove < 0) { int next = fi.data.size(); int free_bytes = -remove;//d.processed_remain; while (free_bytes >= next && next > 0) { free_bytes -= next; if (d.frame_infos.empty()) { // qWarning("buffer queue empty"); break; } d.frame_infos.pop_front(); next = d.frame_infos.front().data.size(); } //qDebug("remove: %d, unremoved bytes < %d, writable_bytes: %d", remove, free_bytes, d.processed_remain); return true; } //qDebug("remove count: %d", remove); while (remove-- > 0) { if (d.frame_infos.empty()) { // qWarning("empty. can not pop!"); break; } d.frame_infos.pop_front(); } return true; } qreal AudioOutput::timestamp() const { DPTR_D(const AudioOutput); return d.frame_infos.front().timestamp; } void AudioOutput::reportVolume(qreal value) { if (qFuzzyCompare(value + 1.0, volume() + 1.0)) return; DPTR_D(AudioOutput); d.vol = value; Q_EMIT volumeChanged(value); // skip sw sample scale d.sw_volume = false; } void AudioOutput::reportMute(bool value) { if (value == isMute()) return; DPTR_D(AudioOutput); d.mute = value; Q_EMIT muteChanged(value); // skip sw sample scale d.sw_mute = false; } void AudioOutput::onCallback() { d_func().onCallback(); } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputAudioToolbox.cpp000066400000000000000000000207231312235004300231520ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016-02-11) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ // FIXME: pause=>resume error #include "QtAV/private/AudioOutputBackend.h" #include #include #include #include //qt4 #include #include #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { static const char kName[] = "AudioToolbox"; class AudioOutputAudioToolbox Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputAudioToolbox(QObject *parent = 0); QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} bool isSupported(AudioFormat::SampleFormat smpfmt) const Q_DECL_OVERRIDE; bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; //bool flush() Q_DECL_OVERRIDE; BufferControl bufferControl() const Q_DECL_OVERRIDE; void onCallback() Q_DECL_OVERRIDE; bool write(const QByteArray& data) Q_DECL_OVERRIDE; bool play() Q_DECL_OVERRIDE; bool setVolume(qreal value) override; private: static void outCallback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer); void tryPauseTimeline(); QVector m_buffer; QVector m_buffer_fill; AudioQueueRef m_queue; AudioStreamBasicDescription m_desc; bool m_waiting; QMutex m_mutex; QWaitCondition m_cond; QSemaphore sem; }; typedef AudioOutputAudioToolbox AudioOutputBackendAudioToolbox; static const AudioOutputBackendId AudioOutputBackendId_AudioToolbox = mkid::id32base36_2<'A', 'T'>::value; FACTORY_REGISTER(AudioOutputBackend, AudioToolbox, kName) #define AT_ENSURE(FUNC, ...) AQ_RUN_CHECK(FUNC, return __VA_ARGS__) #define AT_WARN(FUNC, ...) AQ_RUN_CHECK(FUNC) #define AQ_RUN_CHECK(FUNC, ...) \ do { \ OSStatus ret = FUNC; \ if (ret != noErr) { \ qWarning("AudioBackendAudioQueue Error>>> " #FUNC ": %#x", ret); \ __VA_ARGS__; \ } \ } while(0) static AudioStreamBasicDescription audioFormatToAT(const AudioFormat &format) { // AudioQueue only supports interleave AudioStreamBasicDescription desc; desc.mSampleRate = format.sampleRate(); desc.mFormatID = kAudioFormatLinearPCM; desc.mFormatFlags = kAudioFormatFlagIsPacked; // TODO: kAudioFormatFlagIsPacked? if (format.isFloat()) desc.mFormatFlags |= kAudioFormatFlagIsFloat; else if (!format.isUnsigned()) desc.mFormatFlags |= kAudioFormatFlagIsSignedInteger; desc.mFramesPerPacket = 1;// FIXME:?? desc.mChannelsPerFrame = format.channels(); desc.mBitsPerChannel = format.bytesPerSample()*8; desc.mBytesPerFrame = format.bytesPerFrame(); desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket; return desc; } void AudioOutputAudioToolbox::outCallback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { Q_UNUSED(inAQ); AudioOutputAudioToolbox *ao = reinterpret_cast(inUserData); if (ao->bufferControl() & AudioOutputBackend::CountCallback) { ao->onCallback(); } QMutexLocker locker(&ao->m_mutex); Q_UNUSED(locker); ao->m_buffer_fill.push_back(inBuffer); if (ao->m_waiting) { ao->m_waiting = false; qDebug("wake up to fill buffer"); ao->m_cond.wakeOne(); } //qDebug("callback. sem: %d, fill queue: %d", ao->sem.available(), ao->m_buffer_fill.size()); ao->tryPauseTimeline(); } AudioOutputAudioToolbox::AudioOutputAudioToolbox(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures() |AudioOutput::SetVolume , parent) , m_queue(NULL) , m_waiting(false) { available = false; available = true; } AudioOutputBackend::BufferControl AudioOutputAudioToolbox::bufferControl() const { return CountCallback;//BufferControl(Callback | PlayedCount); } void AudioOutputAudioToolbox::onCallback() { if (bufferControl() & CountCallback) sem.release(); } void AudioOutputAudioToolbox::tryPauseTimeline() { /// All buffers are rendered but the AudioQueue timeline continues. If the next buffer sample time is earlier than AudioQueue timeline value, for example resume after pause, the buffer will not be rendered if (sem.available() == buffer_count) { AudioTimeStamp t; AudioQueueGetCurrentTime(m_queue, NULL, &t, NULL); qDebug("pause audio queue timeline @%.3f (sample time)/%lld (host time)", t.mSampleTime, t.mHostTime); AT_ENSURE(AudioQueuePause(m_queue)); } } bool AudioOutputAudioToolbox::isSupported(AudioFormat::SampleFormat smpfmt) const { return !IsPlanar(smpfmt); } bool AudioOutputAudioToolbox::open() { m_buffer.resize(buffer_count); m_desc = audioFormatToAT(format); AT_ENSURE(AudioQueueNewOutput(&m_desc, AudioOutputAudioToolbox::outCallback, this, NULL, kCFRunLoopCommonModes/*NULL*/, 0, &m_queue), false); for (int i = 0; i < m_buffer.size(); ++i) { AT_ENSURE(AudioQueueAllocateBuffer(m_queue, buffer_size, &m_buffer[i]), false); } m_buffer_fill = m_buffer; sem.release(buffer_count - sem.available()); m_waiting = false; return true; } bool AudioOutputAudioToolbox::close() { if (!m_queue) { qDebug("AudioQueue is not created. skip close"); return true; } UInt32 running = 0, s = 0; AT_ENSURE(AudioQueueGetProperty(m_queue, kAudioQueueProperty_IsRunning, &running, &s), false); if (running) AT_ENSURE(AudioQueueStop(m_queue, true), false); AT_ENSURE(AudioQueueDispose(m_queue, true), false); // dispose all resouces including buffers, so we can remove AudioQueueFreeBuffer m_queue = NULL; m_buffer.clear(); return true; } bool AudioOutputAudioToolbox::write(const QByteArray& data) { // blocking queue. // if queue not full, fill buffer and enqueue buffer //qDebug("write. sem: %d", sem.available()); if (bufferControl() & CountCallback) sem.acquire(); AudioQueueBufferRef buf = NULL; { QMutexLocker locker(&m_mutex); Q_UNUSED(locker); // put to data queue, if has buffer to fill (was available in callback), fill the front data if (m_buffer_fill.isEmpty()) { qDebug("buffer queue to fill is empty, wait a valid buffer to fill"); m_waiting = true; m_cond.wait(&m_mutex); } buf = m_buffer_fill.front(); m_buffer_fill.pop_front(); } assert(buf->mAudioDataBytesCapacity >= (UInt32)data.size() && "too many data to write to audio queue buffer"); memcpy(buf->mAudioData, data.constData(), data.size()); buf->mAudioDataByteSize = data.size(); //buf->mUserData AT_ENSURE(AudioQueueEnqueueBuffer(m_queue, buf, 0, NULL), false); return true; } bool AudioOutputAudioToolbox::play() { OSType err = AudioQueueStart(m_queue, nullptr); if (err == '!pla') { //AVAudioSessionErrorCodeCannotStartPlaying qWarning("AudioQueueStart error: AVAudioSessionErrorCodeCannotStartPlaying. May play in background"); close(); open(); return false; } if (err != noErr) { qWarning("AudioQueueStart error: %#x", noErr); return false; } return true; } bool AudioOutputAudioToolbox::setVolume(qreal value) { // iOS document says the range is [0,1]. But >1.0 works on macOS. So no manually check range here AT_ENSURE(AudioQueueSetParameter(m_queue, kAudioQueueParam_Volume, value), false); return true; } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputBackend.cpp000066400000000000000000000070031312235004300220650ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" namespace QtAV { QStringList AudioOutputBackend::defaultPriority() { static const QStringList sBackends = QStringList() #ifdef Q_OS_MAC << QStringLiteral("AudioToolbox") #endif #if QTAV_HAVE(XAUDIO2) << QStringLiteral("XAudio2") #endif #if QTAV_HAVE(OPENSL) << QStringLiteral("OpenSL") #endif #if QTAV_HAVE(OPENAL) << QStringLiteral("OpenAL") #endif #if QTAV_HAVE(PORTAUDIO) << QStringLiteral("PortAudio") #endif #if QTAV_HAVE(PULSEAUDIO)&& !defined(Q_OS_MAC) << QStringLiteral("Pulse") #endif #if QTAV_HAVE(DSOUND) << QStringLiteral("DirectSound") #endif ; return sBackends; } AudioOutputBackend::AudioOutputBackend(AudioOutput::DeviceFeatures f, QObject *parent) : QObject(parent) , audio(0) , available(true) , buffer_size(0) , buffer_count(0) , m_features(f) {} void AudioOutputBackend::onCallback() { if (!audio) return; audio->onCallback(); } FACTORY_DEFINE(AudioOutputBackend) void AudioOutput_RegisterAll() { static bool initialized = false; if (initialized) return; initialized = true; // check whether ids are registered automatically if (!AudioOutputBackendFactory::Instance().registeredIds().empty()) return; extern bool RegisterAudioOutputBackendNull_Man(); RegisterAudioOutputBackendNull_Man(); #ifdef Q_OS_DARWIN extern bool RegisterAudioOutputBackendAudioToolbox_Man(); RegisterAudioOutputBackendAudioToolbox_Man(); #endif #if QTAV_HAVE(OPENSL) extern bool RegisterAudioOutputBackendOpenSL_Man(); RegisterAudioOutputBackendOpenSL_Man(); #endif //QTAV_HAVE(OPENSL) #if QTAV_HAVE(XAUDIO2) extern bool RegisterAudioOutputBackendXAudio2_Man(); RegisterAudioOutputBackendXAudio2_Man(); #endif #if QTAV_HAVE(OPENAL) extern bool RegisterAudioOutputBackendOpenAL_Man(); RegisterAudioOutputBackendOpenAL_Man(); #endif //QTAV_HAVE(OPENAL) #if QTAV_HAVE(PULSEAUDIO) extern bool RegisterAudioOutputBackendPulse_Man(); RegisterAudioOutputBackendPulse_Man(); #endif #if QTAV_HAVE(PORTAUDIO) extern bool RegisterAudioOutputBackendPortAudio_Man(); RegisterAudioOutputBackendPortAudio_Man(); #endif //QTAV_HAVE(PORTAUDIO) #if QTAV_HAVE(DSOUND) extern bool RegisterAudioOutputBackendDSound_Man(); RegisterAudioOutputBackendDSound_Man(); #endif } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputDSound.cpp000066400000000000000000000402511312235004300217340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include #include #define DIRECTSOUND_VERSION 0x0600 #include #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" #define DX_LOG_COMPONENT "DSound" #include "utils/DirectXHelper.h" namespace QtAV { static const char kName[] = "DirectSound"; class AudioOutputDSound Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputDSound(QObject *parent = 0); QString name() const Q_DECL_OVERRIDE { return QString::fromLatin1(kName);} bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_OVERRIDE; BufferControl bufferControl() const Q_DECL_OVERRIDE; bool write(const QByteArray& data) Q_DECL_OVERRIDE; bool play() Q_DECL_OVERRIDE; int getOffsetByBytes() Q_DECL_OVERRIDE; bool setVolume(qreal value) Q_DECL_OVERRIDE; qreal getVolume() const Q_DECL_OVERRIDE; void onCallback() Q_DECL_OVERRIDE; private: bool loadDll(); bool unloadDll(); bool init(); bool destroy() { SafeRelease(¬ify); SafeRelease(&prim_buf); SafeRelease(&stream_buf); SafeRelease(&dsound); unloadDll(); return true; } bool createDSoundBuffers(); static DWORD WINAPI notificationThread(LPVOID lpThreadParameter); HINSTANCE dll; LPDIRECTSOUND dsound; ///direct sound object LPDIRECTSOUNDBUFFER prim_buf; ///primary direct sound buffer LPDIRECTSOUNDBUFFER stream_buf; ///secondary direct sound buffer (stream buffer) LPDIRECTSOUNDNOTIFY notify; HANDLE notify_event; QSemaphore sem; int write_offset; ///offset of the write cursor in the direct sound buffer QAtomicInt buffers_free; class PositionWatcher : public QThread { AudioOutputDSound *ao; public: PositionWatcher(AudioOutputDSound* dsound) : ao(dsound) {} void run() Q_DECL_OVERRIDE { DWORD dwResult = 0; while (ao->available) { dwResult = WaitForSingleObjectEx(ao->notify_event, 2000, FALSE); if (dwResult != WAIT_OBJECT_0) { //qWarning("WaitForSingleObjectEx for ao->notify_event error: %#lx", dwResult); continue; } ao->onCallback(); } } }; PositionWatcher watcher; }; typedef AudioOutputDSound AudioOutputBackendDSound; static const AudioOutputBackendId AudioOutputBackendId_DSound = mkid::id32base36_6<'D', 'S', 'o', 'u', 'n', 'd'>::value; FACTORY_REGISTER(AudioOutputBackend, DSound, kName) // use the definitions from the win32 api headers when they define these #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* GUID SubFormat IDs */ /* We need both b/c const variables are not compile-time constants in C, giving * us an error if we use the const GUID in an enum */ #undef DEFINE_GUID #define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_UNKNOWN, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); #ifndef MS_GUID #define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID name = { l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} #endif //MS_GUID #ifndef _WAVEFORMATEXTENSIBLE_ typedef struct { WAVEFORMATEX Format; union { WORD wValidBitsPerSample; /* bits of precision */ WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */ WORD wReserved; /* If neither applies, set to zero. */ } Samples; DWORD dwChannelMask; /* which channels are */ /* present in stream */ GUID SubFormat; } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; #endif /* Microsoft speaker definitions. key/values are equal to FFmpeg's */ #define SPEAKER_FRONT_LEFT 0x1 #define SPEAKER_FRONT_RIGHT 0x2 #define SPEAKER_FRONT_CENTER 0x4 #define SPEAKER_LOW_FREQUENCY 0x8 #define SPEAKER_BACK_LEFT 0x10 #define SPEAKER_BACK_RIGHT 0x20 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 #define SPEAKER_BACK_CENTER 0x100 #define SPEAKER_SIDE_LEFT 0x200 #define SPEAKER_SIDE_RIGHT 0x400 #define SPEAKER_TOP_CENTER 0x800 #define SPEAKER_TOP_FRONT_LEFT 0x1000 #define SPEAKER_TOP_FRONT_CENTER 0x2000 #define SPEAKER_TOP_FRONT_RIGHT 0x4000 #define SPEAKER_TOP_BACK_LEFT 0x8000 #define SPEAKER_TOP_BACK_CENTER 0x10000 #define SPEAKER_TOP_BACK_RIGHT 0x20000 #define SPEAKER_RESERVED 0x80000000 static int channelMaskToMS(qint64 av) { if (av >= (qint64)SPEAKER_RESERVED) return 0; return (int)av; } static int channelLayoutToMS(qint64 av) { return channelMaskToMS(av); } AudioOutputDSound::AudioOutputDSound(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures()|AudioOutput::SetVolume, parent) , dll(NULL) , dsound(NULL) , prim_buf(NULL) , stream_buf(NULL) , notify(NULL) , notify_event(NULL) , write_offset(0) , watcher(this) { //setDeviceFeatures(AudioOutput::DeviceFeatures()|AudioOutput::SetVolume); } bool AudioOutputDSound::open() { if (!init()) goto error; if (!createDSoundBuffers()) goto error; return true; error: unloadDll(); SafeRelease(&dsound); return false; } bool AudioOutputDSound::close() { available = false; destroy(); CloseHandle(notify_event); // FIXME: is it ok if thread is still waiting? return true; } bool AudioOutputDSound::isSupported(AudioFormat::SampleFormat sampleFormat) const { return !IsPlanar(sampleFormat); } AudioOutputBackend::BufferControl AudioOutputDSound::bufferControl() const { // Both works. I prefer CountCallback return CountCallback;// OffsetBytes; } void AudioOutputDSound::onCallback() { if (bufferControl() & CountCallback) { //qDebug("callback: %d", sem.available()); if (sem.available() < buffer_count) { sem.release(); return; } } else { // if (buffers_free.deref()) { // return; //} // buffers_free.ref(); } DWORD status; stream_buf->GetStatus(&status); //qDebug("status: %lu", status); return; if (status & DSBSTATUS_LOOPING) { // sound will loop even if buffer is finished DX_ENSURE(stream_buf->Stop()); // reset positions to ensure the notification positions and played buffer matches DX_ENSURE(stream_buf->SetCurrentPosition(0)); write_offset = 0; } } bool AudioOutputDSound::write(const QByteArray &data) { //qDebug("sem %d %d", sem.available(), buffers_free.load()); if (bufferControl() & CountCallback) { sem.acquire(); } else { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) if (buffers_free <= buffer_count) #else if (buffers_free.load() <= buffer_count) #endif buffers_free.ref(); } LPVOID dst1= NULL, dst2 = NULL; DWORD size1 = 0, size2 = 0; if (write_offset >= buffer_size*buffer_count) ///!!!>= write_offset = 0; HRESULT res = stream_buf->Lock(write_offset, data.size(), &dst1, &size1, &dst2, &size2, 0); //DSBLOCK_ENTIREBUFFER if (res == DSERR_BUFFERLOST) { qDebug("buffer lost"); DX_ENSURE(stream_buf->Restore(), false); DX_ENSURE(stream_buf->Lock(write_offset, data.size(), &dst1, &size1, &dst2, &size2, 0), false); } memcpy(dst1, data.constData(), size1); if (dst2) memcpy(dst2, data.constData() + size1, size2); write_offset += size1 + size2; if (write_offset >= buffer_size*buffer_count) write_offset = size2; DX_ENSURE_OK(stream_buf->Unlock(dst1, size1, dst2, size2), false); return true; } bool AudioOutputDSound::play() { DWORD status; stream_buf->GetStatus(&status); if (!(status & DSBSTATUS_PLAYING)) { //must be DSBPLAY_LOOPING. Sound will be very slow if set to 0. I was fucked for a long time. DAMN! stream_buf->Play(0, 0, DSBPLAY_LOOPING); } return true; } int AudioOutputDSound::getOffsetByBytes() { DWORD read_offset = 0; stream_buf->GetCurrentPosition(&read_offset /*play*/, NULL /*write*/); //what's this write_offset? return (int)read_offset; } bool AudioOutputDSound::setVolume(qreal value) { // dsound supports [0, 1] const LONG vol = value <= 0 ? DSBVOLUME_MIN : LONG(log10(value*100.0) * 5000.0) + DSBVOLUME_MIN; // +DSBVOLUME_MIN == -100dB DX_ENSURE_OK(stream_buf->SetVolume(vol), false); return true; } qreal AudioOutputDSound::getVolume() const { LONG vol = 0; DX_ENSURE_OK(stream_buf->GetVolume(&vol), 1.0); return pow(10.0, double(vol - DSBVOLUME_MIN)/5000.0)/100.0; } bool AudioOutputDSound::loadDll() { dll = LoadLibrary(TEXT("dsound.dll")); if (!dll) { qWarning("Can not load dsound.dll"); return false; } return true; } bool AudioOutputDSound::unloadDll() { if (dll) FreeLibrary(dll); return true; } bool AudioOutputDSound::init() { if (!loadDll()) return false; typedef HRESULT (WINAPI *DirectSoundCreateFunc)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); //typedef HRESULT (WINAPI *DirectSoundEnumerateFunc)(LPDSENUMCALLBACKA, LPVOID); DirectSoundCreateFunc dsound_create = (DirectSoundCreateFunc)GetProcAddress(dll, "DirectSoundCreate"); //DirectSoundEnumerateFunc dsound_enumerate = (DirectSoundEnumerateFunc)GetProcAddress(dll, "DirectSoundEnumerateA"); if (!dsound_create) { qWarning("Failed to resolve 'DirectSoundCreate'"); unloadDll(); return false; } DX_ENSURE_OK(dsound_create(NULL/*dev guid*/, &dsound, NULL), false); /* DSSCL_EXCLUSIVE: can modify the settings of the primary buffer, only the sound of this app will be hearable when it will have the focus. */ DX_ENSURE_OK(dsound->SetCooperativeLevel(GetDesktopWindow(), DSSCL_EXCLUSIVE), false); qDebug("DirectSound initialized."); DSCAPS dscaps; memset(&dscaps, 0, sizeof(DSCAPS)); dscaps.dwSize = sizeof(DSCAPS); DX_ENSURE_OK(dsound->GetCaps(&dscaps), false); if (dscaps.dwFlags & DSCAPS_EMULDRIVER) qDebug("DirectSound is emulated"); write_offset = 0; return true; } /** * Creates a DirectSound buffer of the required format. * * This function creates the buffer we'll use to play audio. * In DirectSound there are two kinds of buffers: * - the primary buffer: which is the actual buffer that the soundcard plays * - the secondary buffer(s): these buffers are the one actually used by * applications and DirectSound takes care of mixing them into the primary. * * Once you create a secondary buffer, you cannot change its format anymore so * you have to release the current one and create another. */ bool AudioOutputDSound::createDSoundBuffers() { WAVEFORMATEXTENSIBLE wformat; // TODO: Dolby Digital AC3 ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE)); WAVEFORMATEX wf; wf.cbSize = 0; wf.nChannels = format.channels(); wf.nSamplesPerSec = format.sampleRate(); // FIXME: use supported values wf.wBitsPerSample = format.bytesPerSample() * 8; wf.nBlockAlign = wf.nChannels * format.bytesPerSample(); wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wformat.dwChannelMask = channelLayoutToMS(format.channelLayoutFFmpeg()); if (format.channels() > 2) { wf.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM; wformat.Samples.wValidBitsPerSample = wf.wBitsPerSample; } if (format.isFloat()) { wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { wf.wFormatTag = WAVE_FORMAT_PCM; wformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM; } wformat.Format = wf; if (true) {//format.channels() <= 2 && !format.isFloat()) { //openal use this, don't know why // fill in primary sound buffer descriptor DSBUFFERDESC dsbpridesc; memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC)); dsbpridesc.dwSize = sizeof(DSBUFFERDESC); dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // create primary buffer and set its format DX_ENSURE(dsound->CreateSoundBuffer(&dsbpridesc, &prim_buf, NULL), (destroy() && false)); DX_ENSURE(prim_buf->SetFormat((WAVEFORMATEX *)&wformat), false); } // fill in the secondary sound buffer (=stream buffer) descriptor DSBUFFERDESC dsbdesc; memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */ | DSBCAPS_GLOBALFOCUS /** Allows background playing */ | DSBCAPS_CTRLVOLUME /** volume control enabled */ | DSBCAPS_CTRLPOSITIONNOTIFY; dsbdesc.dwBufferBytes = buffer_size*buffer_count; dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat; // Needed for 5.1 on emu101k - shit soundblaster if (format.channels() > 2) dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; // now create the stream buffer (secondary buffer) HRESULT res = dsound->CreateSoundBuffer(&dsbdesc, &stream_buf, NULL); if (res != DS_OK) { if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) { // Try without DSBCAPS_LOCHARDWARE dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; DX_ENSURE_OK(dsound->CreateSoundBuffer(&dsbdesc, &stream_buf, NULL), (destroy() && false)); } } qDebug( "Secondary (stream)buffer created"); MS_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); DX_ENSURE(stream_buf->QueryInterface(IID_IDirectSoundNotify, (void**)¬ify), false); notify_event = CreateEvent(NULL, FALSE, FALSE, NULL); QVector notification(buffer_count); for (int i = 0; i < buffer_count; ++i) { notification[i].dwOffset = buffer_size*(i+1)-1; notification[i].hEventNotify = notify_event; } //notification[buffer_count].dwOffset = DSBPN_OFFSETSTOP; //notification[buffer_count].hEventNotify = stop_notify_event; DX_ENSURE(notify->SetNotificationPositions(notification.size(), notification.constData()), false); available = true; watcher.start(); sem.release(buffer_count - sem.available()); return true; } } // namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputNull.cpp000066400000000000000000000041161312235004300214520ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" namespace QtAV { //TODO: block internally static const char kName[] = "null"; class AudioOutputNull : public AudioOutputBackend { public: AudioOutputNull(QObject *parent = 0); QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} bool open() Q_DECL_OVERRIDE { return true;} bool close() Q_DECL_OVERRIDE { return true;} // TODO: check channel layout. Null supports channels>2 BufferControl bufferControl() const Q_DECL_OVERRIDE { return Blocking;} bool write(const QByteArray&) Q_DECL_OVERRIDE { return true;} bool play() Q_DECL_OVERRIDE { return true;} }; typedef AudioOutputNull AudioOutputBackendNull; static const AudioOutputBackendId AudioOutputBackendId_Null = mkid::id32base36_4<'n', 'u', 'l', 'l'>::value; FACTORY_REGISTER(AudioOutputBackend, Null, kName) AudioOutputNull::AudioOutputNull(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures(), parent) {} } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputOpenAL.cpp000066400000000000000000000341111312235004300216540ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include #if QTAV_HAVE(CAPI) #define OPENAL_CAPI_NS // CAPI_LINK_OPENAL will override it #include "capi/openal_api.h" #else #if defined(HEADER_OPENAL_PREFIX) #include #include #else #include #include #endif #endif //QTAV_HAVE(CAPI) #include "utils/Logger.h" #define UNQUEUE_QUICK 0 namespace QtAV { static const char kName[] = "OpenAL"; class AudioOutputOpenAL Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputOpenAL(QObject* parent = 0); QString name() const Q_DECL_FINAL { return QLatin1String(kName);} QString deviceName() const; bool open() Q_DECL_FINAL; bool close() Q_DECL_FINAL; bool isSupported(const AudioFormat& format) const Q_DECL_FINAL; bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_FINAL; bool isSupported(AudioFormat::ChannelLayout channelLayout) const Q_DECL_FINAL; protected: BufferControl bufferControl() const Q_DECL_FINAL; bool write(const QByteArray& data) Q_DECL_FINAL; bool play() Q_DECL_FINAL; int getPlayedCount() Q_DECL_FINAL; bool setVolume(qreal value) Q_DECL_FINAL; qreal getVolume() const Q_DECL_FINAL; int getQueued(); bool openDevice() { if (context) return true; const ALCchar *default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); qDebug("OpenAL opening default device: %s", default_device); device = alcOpenDevice(NULL); //parameter: NULL or default_device if (!device) { qWarning("OpenAL failed to open sound device: %s", alcGetString(0, alcGetError(0))); return false; } qDebug("AudioOutputOpenAL creating context..."); context = alcCreateContext(device, NULL); alcMakeContextCurrent(context); return true; } ALCdevice *device; ALCcontext *context; ALenum format_al; QVector buffer; ALuint source; ALint state; QMutex mutex; QWaitCondition cond; // used for 1 context per instance. lock when makeCurrent static QMutex global_mutex; }; typedef AudioOutputOpenAL AudioOutputBackendOpenAL; static const AudioOutputBackendId AudioOutputBackendId_OpenAL = mkid::id32base36_6<'O', 'p', 'e', 'n', 'A', 'L'>::value; FACTORY_REGISTER(AudioOutputBackend, OpenAL, kName) #define AL_ENSURE(expr, ...) \ do { \ expr; \ const ALenum err = alGetError(); \ if (err != AL_NO_ERROR) { \ qWarning("AudioOutputOpenAL Error>>> " #expr " (%d) : %s", err, alGetString(err)); \ return __VA_ARGS__; \ } \ } while(0) #define SCOPE_LOCK_CONTEXT() \ QMutexLocker ctx_lock(&global_mutex); \ Q_UNUSED(ctx_lock); \ if (context) \ alcMakeContextCurrent(context) static ALenum audioFormatToAL(const AudioFormat& fmt) { if (fmt.isPlanar()) return 0; typedef union { const char* ext; ALenum fmt; } al_fmt_t; ALenum format = 0; // al functions need a context ALCcontext *ctx = alcGetCurrentContext(); //a context is required for al functions! const int c = fmt.channels(); const AudioFormat::SampleFormat spfmt = fmt.sampleFormat(); //TODO: planar formats are fine too if (AudioFormat::SampleFormat_Unsigned8 == spfmt) { static const al_fmt_t u8fmt[] = { {(const char*)AL_FORMAT_MONO8}, {(const char*)AL_FORMAT_STEREO8}, {(const char*)0}, {"AL_FORMAT_QUAD8"}, {"AL_FORMAT_REAR8"}, {"AL_FORMAT_51CHN8"}, {"AL_FORMAT_61CHN8"}, {"AL_FORMAT_71CHN8"} }; if (c < 3) { format = u8fmt[c-1].fmt; } else if (c > 3 && c <= 8 && ctx) { if (alIsExtensionPresent("AL_EXT_MCFORMATS")) format = alGetEnumValue(u8fmt[c-1].ext); } } else if (AudioFormat::SampleFormat_Signed16 == spfmt) { static const al_fmt_t s16fmt[] = { {(const char*)AL_FORMAT_MONO16}, {(const char*)AL_FORMAT_STEREO16}, {(const char*)0}, {"AL_FORMAT_QUAD16"}, {"AL_FORMAT_REAR16"}, {"AL_FORMAT_51CHN16"}, {"AL_FORMAT_61CHN16"}, {"AL_FORMAT_71CHN16"} }; if (c < 3) { format = s16fmt[c-1].fmt; } else if (c > 3 && c <= 8 && ctx) { if (alIsExtensionPresent("AL_EXT_MCFORMATS")) format = alGetEnumValue(s16fmt[c-1].ext); } } else if (ctx) { if (AudioFormat::SampleFormat_Float == spfmt) { static const al_fmt_t f32fmt[] = { {"AL_FORMAT_MONO_FLOAT32"}, {"AL_FORMAT_STEREO_FLOAT32"}, {0}, // AL_EXT_MCFORMATS {"AL_FORMAT_QUAD32"}, {"AL_FORMAT_REAR32"}, {"AL_FORMAT_51CHN32"}, {"AL_FORMAT_61CHN32"}, {"AL_FORMAT_71CHN32"} }; if (c <=8 && f32fmt[c-1].ext) { format = alGetEnumValue(f32fmt[c-1].ext); } } else if (AudioFormat::SampleFormat_Double == spfmt) { if (c < 3) { if (alIsExtensionPresent("AL_EXT_double")) { static const al_fmt_t d64fmt[] = { {"AL_FORMAT_MONO_DOUBLE_EXT"}, {"AL_FORMAT_STEREO_DOUBLE_EXT"} }; format = alGetEnumValue(d64fmt[c-1].ext); } } } } ALCenum err = alGetError(); if (err != AL_NO_ERROR) { if (ctx) qWarning("OpenAL audioFormatToAL error: %s", alGetString(err)); else qWarning("OpenAL audioFormatToAL error (null context): %#x", err); } if (format == 0) { qWarning("AudioOutputOpenAL Error: No OpenAL format available for audio data format %s %s." , qPrintable(fmt.sampleFormatName()) , qPrintable(fmt.channelLayoutName())); } qDebug("OpenAL audio format: %#x ch:%d, sample format: %s", format, fmt.channels(), qPrintable(fmt.sampleFormatName())); return format; } QMutex AudioOutputOpenAL::global_mutex; AudioOutputOpenAL::AudioOutputOpenAL(QObject *parent) : AudioOutputBackend(AudioOutput::SetVolume, parent) , device(0) , context(0) , format_al(AL_FORMAT_STEREO16) , state(0) { #if QTAV_HAVE(CAPI) #ifndef CAPI_LINK_OPENAL if (!openal::capi::loaded()) { available = false; return; } #endif //CAPI_LINK_OPENAL #endif //setDeviceFeatures(AudioOutput::SetVolume); // ensure we have a context to check format support // TODO: AudioOutput::getDevices() => ao.setDevice() => ao.open QVector _devices; const char *p = NULL; if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) { // ALC_ALL_DEVICES_SPECIFIER maybe not defined p = alcGetString(NULL, alcGetEnumValue(NULL, "ALC_ALL_DEVICES_SPECIFIER")); } else { p = alcGetString(NULL, ALC_DEVICE_SPECIFIER); } while (p && *p) { _devices.push_back(p); p += _devices.last().size() + 1; } qDebug() << _devices; available = openDevice(); //ensure isSupported(AudioFormat) works correctly } bool AudioOutputOpenAL::open() { if (!openDevice()) return false; { SCOPE_LOCK_CONTEXT(); // alGetString: alsoft needs a context. apple does not qDebug("OpenAL %s vendor: %s; renderer: %s", alGetString(AL_VERSION), alGetString(AL_VENDOR), alGetString(AL_RENDERER)); //alcProcessContext(ctx); //used when dealing witg multiple contexts ALCenum err = alcGetError(device); if (err != ALC_NO_ERROR) { qWarning("AudioOutputOpenAL Error: %s", alcGetString(device, err)); return false; } qDebug("device: %p, context: %p", device, context); //init params. move to another func? format_al = audioFormatToAL(format); buffer.resize(buffer_count); alGenBuffers(buffer.size(), buffer.data()); err = alGetError(); if (err != AL_NO_ERROR) { qWarning("Failed to generate OpenAL buffers: %s", alGetString(err)); goto fail; } alGenSources(1, &source); err = alGetError(); if (err != AL_NO_ERROR) { qWarning("Failed to generate OpenAL source: %s", alGetString(err)); alDeleteBuffers(buffer.size(), buffer.constData()); goto fail; } alSourcei(source, AL_LOOPING, AL_FALSE); alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); alSourcei(source, AL_ROLLOFF_FACTOR, 0); alSource3f(source, AL_POSITION, 0.0, 0.0, 0.0); alSource3f(source, AL_VELOCITY, 0.0, 0.0, 0.0); alListener3f(AL_POSITION, 0.0, 0.0, 0.0); state = 0; qDebug("AudioOutputOpenAL open ok..."); } return true; fail: alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device); context = 0; device = 0; return false; } bool AudioOutputOpenAL::close() { state = 0; if (!context) return true; SCOPE_LOCK_CONTEXT(); alSourceStop(source); do { alGetSourcei(source, AL_SOURCE_STATE, &state); } while (alGetError() == AL_NO_ERROR && state == AL_PLAYING); ALint processed = 0; //android need this!! otherwise the value may be undefined alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); ALuint buf; while (processed-- > 0) { alSourceUnqueueBuffers(source, 1, &buf); } alDeleteSources(1, &source); alDeleteBuffers(buffer.size(), buffer.constData()); alcMakeContextCurrent(NULL); qDebug("alcDestroyContext(%p)", context); alcDestroyContext(context); ALCenum err = alcGetError(device); if (err != ALC_NO_ERROR) { //ALC_INVALID_CONTEXT qWarning("AudioOutputOpenAL Failed to destroy context: %s", alcGetString(device, err)); return false; } context = 0; if (device) { qDebug("alcCloseDevice(%p)", device); alcCloseDevice(device); // ALC_INVALID_DEVICE now device = 0; } return true; } bool AudioOutputOpenAL::isSupported(const AudioFormat& format) const { //if (!context) // openDevice(); //not const SCOPE_LOCK_CONTEXT(); return !!audioFormatToAL(format); } bool AudioOutputOpenAL::isSupported(AudioFormat::SampleFormat sampleFormat) const { if (sampleFormat == AudioFormat::SampleFormat_Unsigned8 || sampleFormat == AudioFormat::SampleFormat_Signed16) return true; if (IsPlanar(sampleFormat)) return false; SCOPE_LOCK_CONTEXT(); if (sampleFormat == AudioFormat::SampleFormat_Float) return alIsExtensionPresent("AL_EXT_float32"); if (sampleFormat == AudioFormat::SampleFormat_Double) return alIsExtensionPresent("AL_EXT_double"); // because preferredChannelLayout() is stereo while s32 only supports >3 channels, so always false return false; } bool AudioOutputOpenAL::isSupported(AudioFormat::ChannelLayout channelLayout) const // FIXME: check { return channelLayout == AudioFormat::ChannelLayout_Mono || channelLayout == AudioFormat::ChannelLayout_Stereo; } QString AudioOutputOpenAL::deviceName() const { if (!device) return QString(); const ALCchar *name = alcGetString(device, ALC_DEVICE_SPECIFIER); return QString::fromUtf8(name); } AudioOutputBackend::BufferControl AudioOutputOpenAL::bufferControl() const { return PlayedCount; //TODO: AL_BYTE_OFFSET } // http://kcat.strangesoft.net/openal-tutorial.html bool AudioOutputOpenAL::write(const QByteArray& data) { if (data.isEmpty()) return false; SCOPE_LOCK_CONTEXT(); ALuint buf = 0; if (state <= 0) { //state used for filling initial data buf = buffer[(-state)%buffer_count]; --state; } else { AL_ENSURE(alSourceUnqueueBuffers(source, 1, &buf), false); } AL_ENSURE(alBufferData(buf, format_al, data.constData(), data.size(), format.sampleRate()), false); AL_ENSURE(alSourceQueueBuffers(source, 1, &buf), false); return true; } bool AudioOutputOpenAL::play() { SCOPE_LOCK_CONTEXT(); alGetSourcei(source, AL_SOURCE_STATE, &state); if (state != AL_PLAYING) { qDebug("AudioOutputOpenAL: !AL_PLAYING alSourcePlay"); alSourcePlay(source); } return true; } int AudioOutputOpenAL::getPlayedCount() { SCOPE_LOCK_CONTEXT(); ALint processed = 0; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); return processed; } bool AudioOutputOpenAL::setVolume(qreal value) { SCOPE_LOCK_CONTEXT(); AL_ENSURE(alListenerf(AL_GAIN, value), false); return true; } qreal AudioOutputOpenAL::getVolume() const { SCOPE_LOCK_CONTEXT(); ALfloat v = 1.0; alGetListenerf(AL_GAIN, &v); ALenum err = alGetError(); if (err != AL_NO_ERROR) { qWarning("AudioOutputOpenAL Error>>> getVolume (%d) : %s", err, alGetString(err)); } return v; } int AudioOutputOpenAL::getQueued() { SCOPE_LOCK_CONTEXT(); ALint queued = 0; alGetSourcei(source, AL_BUFFERS_QUEUED, &queued); return queued; } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputOpenSL.cpp000066400000000000000000000413171312235004300217040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include #include #include #ifdef Q_OS_ANDROID #include #include #include #endif #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" // TODO: native sample rate, so AUDIO_OUTPUT_FLAG_FAST is enabled namespace QtAV { // OpenSL 1.1, or __ANDROID_API__ >= 21 #ifndef SL_DATAFORMAT_PCM_EX #define SL_PCM_REPRESENTATION_SIGNED_INT ((SLuint32) 0x00000001) #define SL_PCM_REPRESENTATION_UNSIGNED_INT ((SLuint32) 0x00000002) #define SL_PCM_REPRESENTATION_FLOAT ((SLuint32) 0x00000003) #define SL_DATAFORMAT_PCM_EX ((SLuint32) 0x00000004) struct SLDataFormat_PCM_EX : SLDataFormat_PCM { SLuint32 representation; }; #endif static const char kName[] = "OpenSL"; class AudioOutputOpenSL Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputOpenSL(QObject *parent = 0); ~AudioOutputOpenSL(); int engineVersion() const { return m_sl_major*100+m_sl_minor*10+m_sl_step;} QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);} bool isSupported(const AudioFormat& format) const Q_DECL_OVERRIDE; bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_OVERRIDE; bool isSupported(AudioFormat::ChannelLayout channelLayout) const Q_DECL_OVERRIDE; bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; BufferControl bufferControl() const Q_DECL_OVERRIDE; void onCallback() Q_DECL_OVERRIDE; void acquireNextBuffer() Q_DECL_OVERRIDE; bool write(const QByteArray& data) Q_DECL_OVERRIDE; bool play() Q_DECL_OVERRIDE; //default return -1. means not the control int getPlayedCount() Q_DECL_OVERRIDE; bool setVolume(qreal value) Q_DECL_OVERRIDE; qreal getVolume() const Q_DECL_OVERRIDE; bool setMute(bool value = true) Q_DECL_OVERRIDE; #ifdef Q_OS_ANDROID static void bufferQueueCallbackAndroid(SLAndroidSimpleBufferQueueItf bufferQueue, void *context); #endif static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *context); static void playCallback(SLPlayItf player, void *ctx, SLuint32 event); private: SLDataFormat_PCM_EX audioFormatToSL(const AudioFormat &format); SLObjectItf engineObject; SLEngineItf engine; SLEngineCapabilitiesItf m_cap; SLObjectItf m_outputMixObject; SLObjectItf m_playerObject; SLPlayItf m_playItf; SLVolumeItf m_volumeItf; SLBufferQueueItf m_bufferQueueItf; #ifdef Q_OS_ANDROID SLAndroidSimpleBufferQueueItf m_bufferQueueItf_android; // supports decoding, recording #endif bool m_android; int m_android_api_level; SLint16 m_sl_major, m_sl_minor, m_sl_step; SLint32 m_streamType; quint32 buffers_queued; QSemaphore sem; // Enqueue does not copy data. We MUST keep the data until it is played out int queue_data_write; QByteArray queue_data; }; typedef AudioOutputOpenSL AudioOutputBackendOpenSL; static const AudioOutputBackendId AudioOutputBackendId_OpenSL = mkid::id32base36_6<'O', 'p', 'e', 'n', 'S', 'L'>::value; FACTORY_REGISTER(AudioOutputBackend, OpenSL, kName) #define SL_ENSURE(FUNC, ...) \ do { \ SLresult ret = FUNC; \ if (ret != SL_RESULT_SUCCESS) { \ qWarning("AudioOutputOpenSL Error>>> " #FUNC " (%lu)", ret); \ return __VA_ARGS__; \ } \ } while(0) SLDataFormat_PCM_EX AudioOutputOpenSL::audioFormatToSL(const AudioFormat &format) { SLDataFormat_PCM_EX format_pcm; if (format.isFloat()) { if (format.sampleSize() == sizeof(float)) format_pcm.representation = SL_PCM_REPRESENTATION_FLOAT; } else if (format.isUnsigned()) { format_pcm.representation = SL_PCM_REPRESENTATION_UNSIGNED_INT; } else { format_pcm.representation = SL_PCM_REPRESENTATION_SIGNED_INT; } format_pcm.formatType = m_android_api_level >= 21 || engineVersion() >= 110 ? SL_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM; format_pcm.numChannels = format.channels(); format_pcm.samplesPerSec = format.sampleRate() * 1000; format_pcm.bitsPerSample = format.bytesPerSample()*8; //raw sample size, e.g. s24 is 24. currently we do not support such formats format_pcm.containerSize = format_pcm.bitsPerSample; // TODO: more layouts format_pcm.channelMask = format.channels() == 1 ? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; #ifdef SL_BYTEORDER_NATIVE format_pcm.endianness = SL_BYTEORDER_NATIVE; #else union { unsigned short num; char buf[sizeof(unsigned short)]; } endianness; endianness.num = 1; format_pcm.endianness = endianness.buf[0] ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; #endif return format_pcm; } #ifdef Q_OS_ANDROID void AudioOutputOpenSL::bufferQueueCallbackAndroid(SLAndroidSimpleBufferQueueItf bufferQueue, void *context) { #if 0 SLAndroidSimpleBufferQueueState state; (*bufferQueue)->GetState(bufferQueue, &state); qDebug(">>>>>>>>>>>>>>bufferQueueCallback state.count=%lu .playIndex=%lu", state.count, state.playIndex); #endif AudioOutputOpenSL *ao = reinterpret_cast(context); if (ao->bufferControl() & AudioOutputBackend::CountCallback) { ao->onCallback(); } } #endif void AudioOutputOpenSL::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *context) { #if 0 SLBufferQueueState state; (*bufferQueue)->GetState(bufferQueue, &state); qDebug(">>>>>>>>>>>>>>bufferQueueCallback state.count=%lu .playIndex=%lu", state.count, state.playIndex); #endif AudioOutputOpenSL *ao = reinterpret_cast(context); if (ao->bufferControl() & AudioOutputBackend::CountCallback) { ao->onCallback(); } } void AudioOutputOpenSL::playCallback(SLPlayItf player, void *ctx, SLuint32 event) { Q_UNUSED(player); Q_UNUSED(ctx); Q_UNUSED(event); //qDebug("---------%s event=%lu", __FUNCTION__, event); } AudioOutputOpenSL::AudioOutputOpenSL(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures() |AudioOutput::SetVolume |AudioOutput::SetMute, parent) , m_outputMixObject(0) , m_playerObject(0) , m_playItf(0) , m_volumeItf(0) , m_bufferQueueItf(0) , m_bufferQueueItf_android(0) , m_android(false) , m_android_api_level(0) , m_sl_major(0) , m_sl_minor(0) , m_sl_step(0) , m_streamType(-1) , buffers_queued(0) , queue_data_write(0) { #ifdef Q_OS_ANDROID char v[PROP_VALUE_MAX+1]; __system_property_get("ro.build.version.sdk", v); m_android_api_level = atoi(v); #endif available = false; SLEngineOption opt[] = {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}; SL_ENSURE(slCreateEngine(&engineObject, 1, opt, 0, NULL, NULL)); // SLEngineOption is ignored by android SL_ENSURE((*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE)); SL_ENSURE((*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engine)); available = true; if ((*engineObject)->GetInterface(engineObject, SL_IID_ENGINECAPABILITIES, &m_cap) == SL_RESULT_SUCCESS) { // SL_RESULT_FEATURE_UNSUPPORTED if ((*m_cap)->QueryAPIVersion(m_cap, &m_sl_major, &m_sl_minor, &m_sl_step) == SL_RESULT_SUCCESS) { printf("OpenSL version: %d.%d.%d\n", m_sl_major, m_sl_minor, m_sl_step); } } } AudioOutputOpenSL::~AudioOutputOpenSL() { if (engineObject) (*engineObject)->Destroy(engineObject); } bool AudioOutputOpenSL::isSupported(const AudioFormat& format) const { return isSupported(format.sampleFormat()) && isSupported(format.channelLayout()); } bool AudioOutputOpenSL::isSupported(AudioFormat::SampleFormat sampleFormat) const { if (sampleFormat == AudioFormat::SampleFormat_Unsigned8 || sampleFormat == AudioFormat::SampleFormat_Signed16) // TODO: android api 21 supports s32? return true; if (m_android_api_level < 21 || (engineVersion() > 0 && engineVersion() < 110)) return false; if (!IsFloat(sampleFormat) || IsPlanar(sampleFormat)) return false; return RawSampleSize(sampleFormat) == sizeof(float); // TODO: test s32 etc } bool AudioOutputOpenSL::isSupported(AudioFormat::ChannelLayout channelLayout) const { return channelLayout == AudioFormat::ChannelLayout_Mono || channelLayout == AudioFormat::ChannelLayout_Stereo; } AudioOutputBackend::BufferControl AudioOutputOpenSL::bufferControl() const { return CountCallback;//BufferControl(Callback | PlayedCount); } void AudioOutputOpenSL::onCallback() { if (bufferControl() & CountCallback) sem.release(); } void AudioOutputOpenSL::acquireNextBuffer() { if (bufferControl() & CountCallback) sem.acquire(); } bool AudioOutputOpenSL::open() { queue_data.resize(buffer_size*buffer_count); SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, (SLuint32)buffer_count }; SLDataFormat_PCM_EX pcmFormat = audioFormatToSL(format); SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; #ifdef Q_OS_ANDROID SLDataLocator_AndroidSimpleBufferQueue bufferQueueLocator_android = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, (SLuint32)buffer_count }; if (m_android) audioSrc.pLocator = &bufferQueueLocator_android; #endif // OutputMix SL_ENSURE((*engine)->CreateOutputMix(engine, &m_outputMixObject, 0, NULL, NULL), false); SL_ENSURE((*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE), false); SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject }; SLDataSink audioSink = { &outputMixLocator, NULL }; const SLInterfaceID ids[] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME #ifdef Q_OS_ANDROID , SL_IID_ANDROIDCONFIGURATION #endif }; const SLboolean req[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE #ifdef Q_OS_ANDROID , SL_BOOLEAN_TRUE #endif }; // AudioPlayer SL_ENSURE((*engine)->CreateAudioPlayer(engine, &m_playerObject, &audioSrc, &audioSink, sizeof(ids)/sizeof(ids[0]), ids, req), false); #ifdef Q_OS_ANDROID if (m_android) { m_streamType = SL_ANDROID_STREAM_MEDIA; SLAndroidConfigurationItf cfg; if ((*m_playerObject)->GetInterface(m_playerObject, SL_IID_ANDROIDCONFIGURATION, &cfg)) { (*cfg)->SetConfiguration(cfg, SL_ANDROID_KEY_STREAM_TYPE, &m_streamType, sizeof(SLint32)); } } #endif SL_ENSURE((*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE), false); // Buffer interface #ifdef Q_OS_ANDROID if (m_android) { SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &m_bufferQueueItf_android), false); SL_ENSURE((*m_bufferQueueItf_android)->RegisterCallback(m_bufferQueueItf_android, AudioOutputOpenSL::bufferQueueCallbackAndroid, this), false); } else #endif { SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_BUFFERQUEUE, &m_bufferQueueItf), false); SL_ENSURE((*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf, AudioOutputOpenSL::bufferQueueCallback, this), false); } // Play interface SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_PLAY, &m_playItf), false); // call when SL_PLAYSTATE_STOPPED SL_ENSURE((*m_playItf)->RegisterCallback(m_playItf, AudioOutputOpenSL::playCallback, this), false); SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false); #if 0 SLuint32 mask = SL_PLAYEVENT_HEADATEND; // TODO: what does this do? SL_ENSURE((*m_playItf)->SetPositionUpdatePeriod(m_playItf, 100), false); SL_ENSURE((*m_playItf)->SetCallbackEventsMask(m_playItf, mask), false); #endif // Volume interface //SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false); sem.release(buffer_count - sem.available()); return true; } bool AudioOutputOpenSL::close() { if (m_playItf) (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); #ifdef Q_OS_ANDROID if (m_android) { if (m_bufferQueueItf_android && SL_RESULT_SUCCESS != (*m_bufferQueueItf_android)->Clear(m_bufferQueueItf_android)) qWarning("Unable to clear buffer"); m_bufferQueueItf_android = NULL; } #endif if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) qWarning("Unable to clear buffer"); if (m_playerObject) { (*m_playerObject)->Destroy(m_playerObject); m_playerObject = NULL; } if (m_outputMixObject) { (*m_outputMixObject)->Destroy(m_outputMixObject); m_outputMixObject = NULL; } m_playItf = NULL; m_volumeItf = NULL; m_bufferQueueItf = NULL; queue_data.clear(); queue_data_write = 0; return true; } bool AudioOutputOpenSL::write(const QByteArray& data) { // assume data.size() <= buffer_size. It's true in QtAV const int s = qMin(queue_data.size() - queue_data_write, data.size()); // assume data.size() <= buffer_size. It's true in QtAV if (s < data.size()) queue_data_write = 0; memcpy((char*)queue_data.constData() + queue_data_write, data.constData(), data.size()); //qDebug("enqueue %p, queue_data_write: %d/%d available:%d", data.constData(), queue_data_write, queue_data.size(), sem.available()); #ifdef Q_OS_ANDROID if (m_android) SL_ENSURE((*m_bufferQueueItf_android)->Enqueue(m_bufferQueueItf_android, queue_data.constData() + queue_data_write, data.size()), false); else #endif SL_ENSURE((*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, queue_data.constData() + queue_data_write, data.size()), false); buffers_queued++; queue_data_write += data.size(); if (queue_data_write == queue_data.size()) queue_data_write = 0; return true; } bool AudioOutputOpenSL::play() { SLuint32 state = SL_PLAYSTATE_PLAYING; (*m_playItf)->GetPlayState(m_playItf, &state); if (state == SL_PLAYSTATE_PLAYING) return true; SL_ENSURE((*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING), false); return true; } int AudioOutputOpenSL::getPlayedCount() { int processed = buffers_queued; SLuint32 count = 0; #ifdef Q_OS_ANDROID if (m_android) { SLAndroidSimpleBufferQueueState state; (*m_bufferQueueItf_android)->GetState(m_bufferQueueItf_android, &state); count = state.count; } else #endif { SLBufferQueueState state; (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state); count = state.count; } buffers_queued = count; processed -= count; return processed; } bool AudioOutputOpenSL::setVolume(qreal value) { if (!m_volumeItf) return false; SLmillibel v = 0; if (qFuzzyIsNull(value)) v = SL_MILLIBEL_MIN; else if (!qFuzzyCompare(value, 1.0)) v = 20.0*log10(value)*100.0; SLmillibel vmax = SL_MILLIBEL_MAX; SL_ENSURE((*m_volumeItf)->GetMaxVolumeLevel(m_volumeItf, &vmax), false); if (vmax < v) { qDebug("OpenSL does not support volume: %f %d/%d. sw scale will be used", value, v, vmax); return false; } SL_ENSURE((*m_volumeItf)->SetVolumeLevel(m_volumeItf, v), false); return true; } qreal AudioOutputOpenSL::getVolume() const { if (!m_volumeItf) return false; SLmillibel v = 0; SL_ENSURE((*m_volumeItf)->GetVolumeLevel(m_volumeItf, &v), 1.0); if (v == SL_MILLIBEL_MIN) return 0; return pow(10.0, qreal(v)/2000.0); } bool AudioOutputOpenSL::setMute(bool value) { if (!m_volumeItf) return false; SL_ENSURE((*m_volumeItf)->SetMute(m_volumeItf, value), false); return true; } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputPortAudio.cpp000066400000000000000000000140151312235004300224450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include "utils/Logger.h" namespace QtAV { static const char kName[] = "PortAudio"; class AudioOutputPortAudio Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputPortAudio(QObject *parent = 0); ~AudioOutputPortAudio(); QString name() const Q_DECL_FINAL { return QString::fromLatin1(kName);} bool open() Q_DECL_FINAL; bool close() Q_DECL_FINAL; virtual BufferControl bufferControl() const Q_DECL_FINAL; virtual bool write(const QByteArray& data) Q_DECL_FINAL; virtual bool play() Q_DECL_FINAL { return true;} private: bool initialized; PaStreamParameters *outputParameters; PaStream *stream; double outputLatency; }; typedef AudioOutputPortAudio AudioOutputBackendPortAudio; static const AudioOutputBackendId AudioOutputBackendId_PortAudio = mkid::id32base36_5<'P', 'o', 'r', 't', 'A'>::value; FACTORY_REGISTER(AudioOutputBackend, PortAudio, kName) AudioOutputPortAudio::AudioOutputPortAudio(QObject *parent) : AudioOutputBackend(AudioOutput::NoFeature, parent) , initialized(false) , outputParameters(new PaStreamParameters) , stream(0) { PaError err = paNoError; if ((err = Pa_Initialize()) != paNoError) { qWarning("Error when init portaudio: %s", Pa_GetErrorText(err)); return; } initialized = true; int numDevices = Pa_GetDeviceCount(); for (int i = 0 ; i < numDevices ; ++i) { const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); if (deviceInfo) { const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); QString name = QString::fromUtf8(hostApiInfo->name) + QStringLiteral(": ") + QString::fromLocal8Bit(deviceInfo->name); qDebug("audio device %d: %s", i, name.toUtf8().constData()); qDebug("max in/out channels: %d/%d", deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); } } memset(outputParameters, 0, sizeof(PaStreamParameters)); outputParameters->device = Pa_GetDefaultOutputDevice(); if (outputParameters->device == paNoDevice) { qWarning("PortAudio get device error!"); return; } const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(outputParameters->device); qDebug("DEFAULT max in/out channels: %d/%d", deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); qDebug("audio device: %s", QString::fromLocal8Bit(Pa_GetDeviceInfo(outputParameters->device)->name).toUtf8().constData()); outputParameters->hostApiSpecificStreamInfo = NULL; outputParameters->suggestedLatency = Pa_GetDeviceInfo(outputParameters->device)->defaultHighOutputLatency; } AudioOutputPortAudio::~AudioOutputPortAudio() { if (outputParameters) { delete outputParameters; outputParameters = 0; } } AudioOutputBackend::BufferControl AudioOutputPortAudio::bufferControl() const { return Blocking; } bool AudioOutputPortAudio::write(const QByteArray& data) { if (Pa_IsStreamStopped(stream)) Pa_StartStream(stream); PaError err = Pa_WriteStream(stream, data.constData(), data.size()/format.channels()/format.bytesPerSample()); if (err == paUnanticipatedHostError) { qWarning("Write portaudio stream error: %s", Pa_GetErrorText(err)); return false; } return true; } //TODO: what about planar, int8, int24 etc that FFmpeg or Pa not support? static int toPaSampleFormat(AudioFormat::SampleFormat format) { switch (format) { case AudioFormat::SampleFormat_Unsigned8: return paUInt8; case AudioFormat::SampleFormat_Signed16: return paInt16; case AudioFormat::SampleFormat_Signed32: return paInt32; case AudioFormat::SampleFormat_Float: return paFloat32; default: return paCustomFormat; } } //TODO: call open after audio format changed? bool AudioOutputPortAudio::open() { outputParameters->sampleFormat = toPaSampleFormat(format.sampleFormat()); outputParameters->channelCount = format.channels(); PaError err = Pa_OpenStream(&stream, NULL, outputParameters, format.sampleRate(), 0, paNoFlag, NULL, NULL); if (err != paNoError) { qWarning("Open portaudio stream error: %s", Pa_GetErrorText(err)); return false; } outputLatency = Pa_GetStreamInfo(stream)->outputLatency; return true; } bool AudioOutputPortAudio::close() { if (!stream) { return true; } PaError err = Pa_StopStream(stream); //may be already stopped: paStreamIsStopped if (err != paNoError) { qWarning("Stop portaudio stream error: %s", Pa_GetErrorText(err)); //return err == paStreamIsStopped; } err = Pa_CloseStream(stream); if (err != paNoError) { qWarning("Close portaudio stream error: %s", Pa_GetErrorText(err)); return false; } stream = NULL; if (initialized) Pa_Terminate(); //Do NOT call this if init failed. See document return true; } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputPulse.cpp000066400000000000000000000376361312235004300216450ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include #include #include #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include "utils/Logger.h" #ifndef Q_LIKELY #define Q_LIKELY(x) (!!(x)) #endif namespace QtAV { static const char kName[] = "Pulse"; class AudioOutputPulse Q_DECL_FINAL: public AudioOutputBackend { public: AudioOutputPulse(QObject *parent = 0); QString name() const Q_DECL_FINAL { return QString::fromLatin1(kName);} bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_FINAL; bool open() Q_DECL_FINAL; bool close() Q_DECL_FINAL; protected: bool write(const QByteArray& data) Q_DECL_FINAL; bool play() Q_DECL_FINAL; BufferControl bufferControl() const Q_DECL_FINAL; int getWritableBytes() Q_DECL_FINAL; bool setVolume(qreal value) Q_DECL_FINAL; qreal getVolume() const Q_DECL_FINAL; bool setMute(bool value = true) Q_DECL_FINAL; private: bool init(const AudioFormat& format); static void contextStateCallback(pa_context *c, void *userdata); static void contextSubscribeCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); static void stateCallback(pa_stream *s, void *userdata); static void latencyUpdateCallback(pa_stream *s, void *userdata); static void underflowCallback(pa_stream *s, void *userdata); static void writeCallback(pa_stream *s, size_t length, void *userdata); static void successCallback(pa_stream*s, int success, void *userdata); static void sinkInfoCallback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata); bool waitPAOperation(pa_operation *op) const { if (!op) { return false; } pa_operation_state_t state = pa_operation_get_state(op); while (state == PA_OPERATION_RUNNING) { pa_threaded_mainloop_wait(loop); state = pa_operation_get_state(op); } pa_operation_unref(op); return state == PA_OPERATION_DONE; } pa_threaded_mainloop *loop; pa_context *ctx; pa_stream *stream; pa_sink_input_info info; size_t writable_size; //has the same effect as pa_stream_writable_size }; typedef AudioOutputPulse AudioOutputBackendPulse; static const AudioOutputBackendId AudioOutputBackendId_Pulse = mkid::id32base36_5<'P', 'u', 'l', 's', 'e'>::value; FACTORY_REGISTER(AudioOutputBackend, Pulse, kName) #define PA_ENSURE_TRUE(expr, ...) \ do { \ if (!(expr)) { \ qWarning("PulseAudio error @%d " #expr ": %s", __LINE__, pa_strerror(pa_context_errno(ctx))); \ return __VA_ARGS__; \ } \ } while(0) static const struct format_entry { AudioFormat::SampleFormat spformat; pa_sample_format_t pa; } format_map[] = { {AudioFormat::SampleFormat_Signed16, PA_SAMPLE_S16NE}, {AudioFormat::SampleFormat_Signed32, PA_SAMPLE_S32NE}, {AudioFormat::SampleFormat_Float, PA_SAMPLE_FLOAT32NE}, {AudioFormat::SampleFormat_Unsigned8, PA_SAMPLE_U8}, {AudioFormat::SampleFormat_Unknown, PA_SAMPLE_INVALID} }; AudioFormat::SampleFormat sampleFormatFromPulse(pa_sample_format pa) { for (int i = 0; format_map[i].spformat != AudioFormat::SampleFormat_Unknown; ++i) { if (format_map[i].pa == pa) return format_map[i].spformat; } return AudioFormat::SampleFormat_Unknown; } static pa_sample_format sampleFormatToPulse(AudioFormat::SampleFormat format) { for (int i = 0; format_map[i].spformat != AudioFormat::SampleFormat_Unknown; ++i) { if (format_map[i].spformat == format) return format_map[i].pa; } return PA_SAMPLE_INVALID; } class ScopedPALocker { pa_threaded_mainloop *ml; public: ScopedPALocker(pa_threaded_mainloop *loop) : ml(loop) { pa_threaded_mainloop_lock(ml); } ~ScopedPALocker() { pa_threaded_mainloop_unlock(ml); } }; void AudioOutputPulse::contextStateCallback(pa_context *c, void *userdata) { AudioOutputPulse *p = reinterpret_cast(userdata); switch (pa_context_get_state(c)) { case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: case PA_CONTEXT_READY: pa_threaded_mainloop_signal(p->loop, 0); break; default: break; } } static void sink_input_info_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { Q_UNUSED(c); if (eol) return; AudioOutputPulse *ao = reinterpret_cast(userdata); QMetaObject::invokeMethod(ao, "volumeReported", Q_ARG(qreal, (qreal)pa_cvolume_avg(&i->volume)/qreal(PA_VOLUME_NORM))); QMetaObject::invokeMethod(ao, "muteReported", Q_ARG(bool, i->mute)); } static void sink_input_event(pa_context* c, pa_subscription_event_type_t t, uint32_t idx, AudioOutputPulse* ao) { switch (t) { case PA_SUBSCRIPTION_EVENT_REMOVE: qWarning("PulseAudio sink killed"); break; default: pa_operation *op = pa_context_get_sink_input_info(c, idx, sink_input_info_cb, ao); if (Q_LIKELY(!!op)) pa_operation_unref(op); break; } } void AudioOutputPulse::contextSubscribeCallback(pa_context *c, pa_subscription_event_type_t type, uint32_t idx, void *userdata) { AudioOutputPulse *p = reinterpret_cast(userdata); unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; pa_subscription_event_type_t t = pa_subscription_event_type_t(type & PA_SUBSCRIPTION_EVENT_TYPE_MASK); switch (facility) { case PA_SUBSCRIPTION_EVENT_SINK: break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if (p->stream && idx == pa_stream_get_index(p->stream)) sink_input_event(c, t, idx, p); break; case PA_SUBSCRIPTION_EVENT_CARD: qDebug("PA_SUBSCRIPTION_EVENT_CARD"); break; default: break; } } void AudioOutputPulse::stateCallback(pa_stream *s, void *userdata) { AudioOutputPulse *p = reinterpret_cast(userdata); switch (pa_stream_get_state(s)) { case PA_STREAM_FAILED: qWarning("PA_STREAM_FAILED"); pa_threaded_mainloop_signal(p->loop, 0); break; case PA_STREAM_READY: case PA_STREAM_TERMINATED: pa_threaded_mainloop_signal(p->loop, 0); break; default: break; } } void AudioOutputPulse::latencyUpdateCallback(pa_stream *s, void *userdata) { Q_UNUSED(s); AudioOutputPulse *p = reinterpret_cast(userdata); pa_threaded_mainloop_signal(p->loop, 0); } void AudioOutputPulse::writeCallback(pa_stream *s, size_t length, void *userdata) { Q_UNUSED(s); // length: writable bytes. callback is called pirioddically AudioOutputPulse *p = reinterpret_cast(userdata); //qDebug("write callback: %d + %d", p->writable_size, length); p->writable_size = length; p->onCallback(); } void AudioOutputPulse::successCallback(pa_stream *s, int success, void *userdata) { Q_UNUSED(success); //? AudioOutputPulse *p = reinterpret_cast(userdata); pa_threaded_mainloop_signal(p->loop, 0); } void AudioOutputPulse::sinkInfoCallback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { Q_UNUSED(c); AudioOutputPulse *p = reinterpret_cast(userdata); if (is_last < 0) { qWarning("Failed to get sink input info"); return; } if (!i) return; p->info = *i; pa_threaded_mainloop_signal(p->loop, 0); } bool AudioOutputPulse::init(const AudioFormat &format) { writable_size = 0; loop = pa_threaded_mainloop_new(); if (pa_threaded_mainloop_start(loop) < 0) { qWarning("PulseAudio failed to start mainloop"); return false; } ScopedPALocker lock(loop); Q_UNUSED(lock); pa_mainloop_api *api = pa_threaded_mainloop_get_api(loop); ctx = pa_context_new(api, qApp->applicationName().append(QLatin1String(" @%1 (QtAV)")).arg((quintptr)this).toUtf8().constData()); if (!ctx) { qWarning("PulseAudio failed to allocate a context"); return false; } qDebug() << tr("PulseAudio %1, protocol: %2, server protocol: %3").arg(QString::fromLatin1(pa_get_library_version())).arg(pa_context_get_protocol_version(ctx)).arg(pa_context_get_server_protocol_version(ctx)); // TODO: host property pa_context_connect(ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); pa_context_set_state_callback(ctx, AudioOutputPulse::contextStateCallback, this); while (true) { const pa_context_state_t st = pa_context_get_state(ctx); if (st == PA_CONTEXT_READY) break; if (!PA_CONTEXT_IS_GOOD(st)) { qWarning("PulseAudio context init failed"); return false; } pa_threaded_mainloop_wait(loop); } pa_context_set_subscribe_callback(ctx, AudioOutputPulse::contextSubscribeCallback, this); pa_context_subscribe(ctx, pa_subscription_mask_t( PA_SUBSCRIPTION_MASK_CARD | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT), NULL, NULL); //pa_sample_spec // setup format pa_format_info *fi = pa_format_info_new(); fi->encoding = PA_ENCODING_PCM; pa_format_info_set_sample_format(fi, sampleFormatToPulse(format.sampleFormat())); pa_format_info_set_channels(fi, format.channels()); pa_format_info_set_rate(fi, format.sampleRate()); // pa_format_info_set_channel_map(fi, NULL); // TODO if (!pa_format_info_valid(fi)) { qWarning("PulseAudio: invalid format"); return false; } pa_proplist *pl = pa_proplist_new(); if (pl) { pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "video"); pa_proplist_sets(pl, PA_PROP_MEDIA_ICON_NAME, qApp->applicationName().append(QLatin1String(" (QtAV)")).toUtf8().constData()); } stream = pa_stream_new_extended(ctx, "audio stream", &fi, 1, pl); if (!stream) { pa_format_info_free(fi); pa_proplist_free(pl); qWarning("PulseAudio: failed to create a stream"); return false; } pa_format_info_free(fi); pa_proplist_free(pl); pa_stream_set_write_callback(stream, AudioOutputPulse::writeCallback, this); pa_stream_set_state_callback(stream, AudioOutputPulse::stateCallback, this); pa_stream_set_latency_update_callback(stream, AudioOutputPulse::latencyUpdateCallback, this); pa_buffer_attr ba; ba.maxlength = PA_STREAM_ADJUST_LATENCY;//-1;//buffer_size*buffer_count; // max buffer size on the server ba.tlength = PA_STREAM_ADJUST_LATENCY;//(uint32_t)-1; // ? ba.prebuf = 1;//(uint32_t)-1; // play as soon as possible ba.minreq = (uint32_t)-1; //ba.fragsize = (uint32_t)-1; //latency // PA_STREAM_NOT_MONOTONIC? pa_stream_flags_t flags = pa_stream_flags_t(PA_STREAM_NOT_MONOTONIC|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE); if (pa_stream_connect_playback(stream, NULL /*sink*/, &ba, flags, NULL, NULL) < 0) { qWarning("PulseAudio failed: pa_stream_connect_playback"); return false; } while (true) { const pa_stream_state_t st = pa_stream_get_state(stream); if (st == PA_STREAM_READY) break; if (!PA_STREAM_IS_GOOD(st)) { qWarning("PulseAudio stream init failed"); return false; } pa_threaded_mainloop_wait(loop); } if (pa_stream_is_suspended(stream)) { qWarning("PulseAudio stream is suspende"); return false; } return true; } AudioOutputPulse::AudioOutputPulse(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures() |AudioOutput::SetVolume |AudioOutput::SetMute |AudioOutput::SetSampleRate, parent) , loop(0) , ctx(0) , stream(0) , writable_size(0) { //setDeviceFeatures(DeviceFeatures()|SetVolume|SetMute); } bool AudioOutputPulse::isSupported(AudioFormat::SampleFormat spformat) const { for (int i = 0; format_map[i].spformat != AudioFormat::SampleFormat_Unknown; ++i) { if (format_map[i].spformat == spformat) return true; } return false; } bool AudioOutputPulse::open() { if (!init(format)) { if (ctx) qWarning("%s", pa_strerror(pa_context_errno(ctx))); close(); return false; } return true; } bool AudioOutputPulse::close() { if (stream) { ScopedPALocker palock(loop); Q_UNUSED(palock); PA_ENSURE_TRUE(waitPAOperation(pa_stream_drain(stream, AudioOutputPulse::successCallback, this)), false); } if (loop) { pa_threaded_mainloop_stop(loop); } if (stream) { pa_stream_disconnect(stream); pa_stream_unref(stream); stream = NULL; } if (ctx) { pa_context_disconnect(ctx); pa_context_unref(ctx); ctx = NULL; } if (loop) { pa_threaded_mainloop_free(loop); loop = NULL; } return true; } AudioOutputBackend::BufferControl AudioOutputPulse::bufferControl() const { return BytesCallback; } int AudioOutputPulse::getWritableBytes() { //return writable_size; if (!loop || !stream) { qWarning("pulseaudio is not open"); return 0; } ScopedPALocker palock(loop); Q_UNUSED(palock); return pa_stream_writable_size(stream); } bool AudioOutputPulse::write(const QByteArray &data) { ScopedPALocker palock(loop); Q_UNUSED(palock); PA_ENSURE_TRUE(pa_stream_write(stream, data.constData(), data.size(), NULL, 0LL, PA_SEEK_RELATIVE) >= 0, false); writable_size -= data.size(); return true; } bool AudioOutputPulse::play() { return true; } bool AudioOutputPulse::setVolume(qreal value) { ScopedPALocker palock(loop); Q_UNUSED(palock); uint32_t stream_idx = pa_stream_get_index(stream); struct pa_cvolume vol; // TODO: per-channel volume pa_cvolume_reset(&vol, format.channels()); pa_cvolume_set(&vol, format.channels(), pa_volume_t(value*qreal(PA_VOLUME_NORM))); pa_operation *o = 0; PA_ENSURE_TRUE((o = pa_context_set_sink_input_volume(ctx, stream_idx, &vol, NULL, NULL)) != NULL, false); pa_operation_unref(o); return true; } qreal AudioOutputPulse::getVolume() const { ScopedPALocker palock(loop); Q_UNUSED(palock); uint32_t stream_idx = pa_stream_get_index(stream); PA_ENSURE_TRUE(waitPAOperation(pa_context_get_sink_input_info(ctx, stream_idx, AudioOutputPulse::sinkInfoCallback, (void*)this)), 0.0); return (qreal)pa_cvolume_avg(&info.volume)/qreal(PA_VOLUME_NORM); } bool AudioOutputPulse::setMute(bool value) { ScopedPALocker palock(loop); Q_UNUSED(palock); uint32_t stream_idx = pa_stream_get_index(stream); pa_operation *o = 0; PA_ENSURE_TRUE((o = pa_context_set_sink_input_mute(ctx, stream_idx, value, NULL, NULL)) != NULL, false); pa_operation_unref(o); return true; } } //namespace QtAV QtAV-1.12.0/src/output/audio/AudioOutputXAudio2.cpp000066400000000000000000000274171312235004300220240ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/AudioOutputBackend.h" #include "QtAV/private/mkid.h" #include "QtAV/private/factory.h" #include #include #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" #define DX_LOG_COMPONENT "XAudio2" #include "utils/DirectXHelper.h" #include "xaudio2_compat.h" // ref: DirectXTK, SDL, wine namespace QtAV { static const char kName[] = "XAudio2"; class AudioOutputXAudio2 Q_DECL_FINAL: public AudioOutputBackend, public IXAudio2VoiceCallback { public: AudioOutputXAudio2(QObject *parent = 0); ~AudioOutputXAudio2(); QString name() const Q_DECL_OVERRIDE { return QString::fromLatin1(kName);} bool open() Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; // TODO: check channel layout. xaudio2 supports channels>2 bool isSupported(const AudioFormat& format) const Q_DECL_OVERRIDE; bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_OVERRIDE; bool isSupported(AudioFormat::ChannelLayout channelLayout) const Q_DECL_OVERRIDE; BufferControl bufferControl() const Q_DECL_OVERRIDE; void onCallback() Q_DECL_OVERRIDE; bool write(const QByteArray& data) Q_DECL_OVERRIDE; bool play() Q_DECL_OVERRIDE; bool setVolume(qreal value) Q_DECL_OVERRIDE; qreal getVolume() const Q_DECL_OVERRIDE; public: STDMETHOD_(void, OnVoiceProcessingPassStart)(THIS_ UINT32 bytesRequired) Q_DECL_OVERRIDE {Q_UNUSED(bytesRequired);} STDMETHOD_(void, OnVoiceProcessingPassEnd)(THIS) Q_DECL_OVERRIDE {} STDMETHOD_(void, OnStreamEnd)(THIS) Q_DECL_OVERRIDE {} STDMETHOD_(void, OnBufferStart)(THIS_ void* bufferContext) Q_DECL_OVERRIDE { Q_UNUSED(bufferContext);} STDMETHOD_(void, OnBufferEnd)(THIS_ void* bufferContext) Q_DECL_OVERRIDE { AudioOutputXAudio2 *ao = reinterpret_cast(bufferContext); if (ao->bufferControl() & AudioOutputBackend::CountCallback) { ao->onCallback(); } } STDMETHOD_(void, OnLoopEnd)(THIS_ void* bufferContext) Q_DECL_OVERRIDE { Q_UNUSED(bufferContext);} STDMETHOD_(void, OnVoiceError)(THIS_ void* bufferContext, HRESULT error) Q_DECL_OVERRIDE { Q_UNUSED(bufferContext); qWarning() << __FUNCTION__ << ": (" << error << ") " << qt_error_string(error); } private: bool xaudio2_winsdk; bool uninit_com; // TODO: com ptr IXAudio2SourceVoice* source_voice; union { struct { DXSDK::IXAudio2* xaudio; DXSDK::IXAudio2MasteringVoice* master; } dxsdk; struct { WinSDK::IXAudio2* xaudio; WinSDK::IXAudio2MasteringVoice* master; } winsdk; }; QSemaphore sem; int queue_data_write; QByteArray queue_data; QLibrary dll; }; typedef AudioOutputXAudio2 AudioOutputBackendXAudio2; static const AudioOutputBackendId AudioOutputBackendId_XAudio2 = mkid::id32base36_6<'X', 'A', 'u', 'd', 'i', 'o'>::value; FACTORY_REGISTER(AudioOutputBackend, XAudio2, kName) AudioOutputXAudio2::AudioOutputXAudio2(QObject *parent) : AudioOutputBackend(AudioOutput::DeviceFeatures()|AudioOutput::SetVolume, parent) , xaudio2_winsdk(true) , uninit_com(false) , source_voice(NULL) , queue_data_write(0) { memset(&dxsdk, 0, sizeof(dxsdk)); available = false; //setDeviceFeatures(AudioOutput::DeviceFeatures()|AudioOutput::SetVolume); #ifdef Q_OS_WINRT qDebug("XAudio2 for WinRT"); // winrt can only load package dlls DX_ENSURE(XAudio2Create(&winsdk.xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR)); #else // https://github.com/wang-bin/QtAV/issues/518 // already initialized in qtcore for main thread. If RPC_E_CHANGED_MODE no ref is added, CoUninitialize can lead to crash uninit_com = CoInitializeEx(NULL, COINIT_MULTITHREADED) != RPC_E_CHANGED_MODE; // load dll. = 7; ver--) { dll.setFileName(QStringLiteral("XAudio2_%1").arg(ver)); qDebug() << dll.fileName(); if (!dll.load()) { qWarning() << dll.errorString(); continue; } #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) // defined as an inline function qDebug("Build with XAudio2 from DXSDK"); #else qDebug("Build with XAudio2 from Win8 or later SDK"); #endif bool ready = false; if (!ready && ver >= 8) { xaudio2_winsdk = true; qDebug("Try symbol 'XAudio2Create' from WinSDK dll"); typedef HRESULT (__stdcall *XAudio2Create_t)(WinSDK::IXAudio2** ppXAudio2, UINT32 Flags, XAUDIO2_PROCESSOR XAudio2Processor); XAudio2Create_t XAudio2Create = (XAudio2Create_t)dll.resolve("XAudio2Create"); if (XAudio2Create) ready = SUCCEEDED(XAudio2Create(&winsdk.xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR)); } if (!ready && ver < 8) { xaudio2_winsdk = false; #ifdef _XBOX // xbox < win8 is inline XAudio2Create qDebug("Try symbol 'XAudio2Create' from DXSDK dll (XBOX)"); typedef HRESULT (__stdcall *XAudio2Create_t)(DXSDK::IXAudio2** ppXAudio2, UINT32 Flags, XAUDIO2_PROCESSOR XAudio2Processor); XAudio2Create_t XAudio2Create = (XAudio2Create_t)dll.resolve("XAudio2Create"); if (XAudio2Create) ready = SUCCEEDED(XAudio2Create(&dxsdk.xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR)); #else // try xaudio2 from dxsdk without symbol qDebug("Try inline function 'XAudio2Create' from DXSDK"); ready = SUCCEEDED(DXSDK::XAudio2Create(&dxsdk.xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR)); #endif } if (ready) break; dll.unload(); } #endif //Q_OS_WINRT qDebug("xaudio2: %p", winsdk.xaudio); available = !!(winsdk.xaudio); } AudioOutputXAudio2::~AudioOutputXAudio2() { qDebug(); if (xaudio2_winsdk) SafeRelease(&winsdk.xaudio); else SafeRelease(&dxsdk.xaudio); #ifndef Q_OS_WINRT //again, for COM. not for winrt if (uninit_com) CoUninitialize(); #endif //Q_OS_WINRT } bool AudioOutputXAudio2::open() { if (!available) return false; #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) // TODO: also check runtime version before call // XAUDIO2_DEVICE_DETAILS details; #endif WAVEFORMATEX wf; wf.cbSize = 0; //sdl: sizeof(wf) wf.nChannels = format.channels(); wf.nSamplesPerSec = format.sampleRate(); // FIXME: use supported values wf.wFormatTag = format.isFloat() ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; wf.wBitsPerSample = format.bytesPerSample() * 8; wf.nBlockAlign = wf.nChannels * format.bytesPerSample(); wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; // dwChannelMask // TODO: channels >2, see dsound const UINT32 flags = 0; //XAUDIO2_VOICE_NOSRC | XAUDIO2_VOICE_NOPITCH; // TODO: sdl freq 1.0 if (xaudio2_winsdk) { // TODO: device Id property // TODO: parameters now default. DX_ENSURE_OK(winsdk.xaudio->CreateMasteringVoice(&winsdk.master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE), false); DX_ENSURE_OK(winsdk.xaudio->CreateSourceVoice(&source_voice, &wf, flags, XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL), false); DX_ENSURE_OK(winsdk.xaudio->StartEngine(), false); } else { DX_ENSURE_OK(dxsdk.xaudio->CreateMasteringVoice(&dxsdk.master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE), false); DX_ENSURE_OK(dxsdk.xaudio->CreateSourceVoice(&source_voice, &wf, flags, XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL), false); DX_ENSURE_OK(dxsdk.xaudio->StartEngine(), false); } DX_ENSURE_OK(source_voice->Start(0, XAUDIO2_COMMIT_NOW), false); qDebug("source_voice:%p", source_voice); queue_data.resize(buffer_size*buffer_count); sem.release(buffer_count - sem.available()); return true; } bool AudioOutputXAudio2::close() { qDebug("source_voice: %p, master: %p", source_voice, winsdk.master); if (source_voice) { source_voice->Stop(0, XAUDIO2_COMMIT_NOW); source_voice->FlushSourceBuffers(); source_voice->DestroyVoice(); source_voice = NULL; } if (xaudio2_winsdk) { if (winsdk.master) { winsdk.master->DestroyVoice(); winsdk.master = NULL; } if (winsdk.xaudio) winsdk.xaudio->StopEngine(); } else { if (dxsdk.master) { dxsdk.master->DestroyVoice(); dxsdk.master = NULL; } if (dxsdk.xaudio) dxsdk.xaudio->StopEngine(); } queue_data.clear(); queue_data_write = 0; return true; } bool AudioOutputXAudio2::isSupported(const AudioFormat& format) const { return isSupported(format.sampleFormat()) && isSupported(format.channelLayout()); } bool AudioOutputXAudio2::isSupported(AudioFormat::SampleFormat sampleFormat) const { return !IsPlanar(sampleFormat) && RawSampleSize(sampleFormat) < sizeof(double); // TODO: what about s64? } // FIXME: bool AudioOutputXAudio2::isSupported(AudioFormat::ChannelLayout channelLayout) const { return channelLayout == AudioFormat::ChannelLayout_Mono || channelLayout == AudioFormat::ChannelLayout_Stereo; } AudioOutputBackend::BufferControl AudioOutputXAudio2::bufferControl() const { return CountCallback; } void AudioOutputXAudio2::onCallback() { if (bufferControl() & CountCallback) sem.release(); } bool AudioOutputXAudio2::write(const QByteArray &data) { //qDebug("sem: %d, write: %d/%d", sem.available(), queue_data_write, queue_data.size()); if (bufferControl() & CountCallback) sem.acquire(); const int s = qMin(queue_data.size() - queue_data_write, data.size()); // assume data.size() <= buffer_size. It's true in QtAV if (s < data.size()) queue_data_write = 0; memcpy((char*)queue_data.constData() + queue_data_write, data.constData(), data.size()); XAUDIO2_BUFFER xb; //IMPORTANT! wrong value(playbegin/length, loopbegin/length) will result in commit sourcebuffer fail memset(&xb, 0, sizeof(XAUDIO2_BUFFER)); xb.AudioBytes = data.size(); //xb.Flags = XAUDIO2_END_OF_STREAM; xb.pContext = this; xb.pAudioData = (const BYTE*)(queue_data.constData() + queue_data_write); queue_data_write += data.size(); if (queue_data_write == queue_data.size()) queue_data_write = 0; DX_ENSURE_OK(source_voice->SubmitSourceBuffer(&xb, NULL), false); // TODO: XAUDIO2_E_DEVICE_INVALIDATED return true; } bool AudioOutputXAudio2::play() { return true; } bool AudioOutputXAudio2::setVolume(qreal value) { // master or source? DX_ENSURE_OK(source_voice->SetVolume(value), false); return true; } qreal AudioOutputXAudio2::getVolume() const { FLOAT value; source_voice->GetVolume(&value); return value; } } // namespace QtAV QtAV-1.12.0/src/output/audio/xaudio2_compat.h000066400000000000000000000261411312235004300207220ustar00rootroot00000000000000/****************************************************************************** XAudio2 compat layer with both DXSDK and WINSDK support Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_XAUDIO2_COMPAT_H #define QTAV_XAUDIO2_COMPAT_H #define PREFER_WINSDK 0 //1 if use xaudio2 from windows sdk, _WIN32_WINNT must >= win8 #if PREFER_WINSDK #if _WIN32_WINNT < _WIN32_WINNT_WIN8 #undef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_WIN8 #endif #endif //PREFER_WINSDK /*! IXAudio2 and IXAudio2MasteringVoice are different defined in DXSDK(2010 June) and WINSDK(>=win8). You MUST explicitly prefix with namespace DXSDK or WinSDK for IXAudio2MasteringVoice, IXAudio2, */ #include #ifndef _WIN32_WINNT_WIN8 #define _WIN32_WINNT_WIN8 0x0602 #endif #include "directx/dxcompat.h" #ifdef __GNUC__ // macros used by XAudio 2.7 (June 2010 SDK) #ifndef __in #define __in #endif #ifndef __out #define __out #endif #endif //TODO: winrt test // do not add dxsdk xaudio2.h dir to INCLUDE for other file build with winsdk to avoid runtime crash // currently you can use dxsdk 2010 header for mingw // MinGW64 cross: XAudio2.h #include /* sdk check * winsdk: defines XAUDIO2_DLL * dxsdk: defines XAUDIO2_DEBUG_ENGINE */ #define XA2_WINSDK (_WIN32_WINNT >= _WIN32_WINNT_WIN8) namespace DXSDK { #if defined(__WINRT__) typedef ::IXAudio2 IXAudio2; #elif XA2_WINSDK // Used in XAUDIO2_DEVICE_DETAILS below to describe the types of applications // that the user has specified each device as a default for. 0 means that the // device isn't the default for any role. typedef enum XAUDIO2_DEVICE_ROLE { NotDefaultDevice = 0x0, DefaultConsoleDevice = 0x1, DefaultMultimediaDevice = 0x2, DefaultCommunicationsDevice = 0x4, DefaultGameDevice = 0x8, GlobalDefaultDevice = 0xf, InvalidDeviceRole = ~GlobalDefaultDevice } XAUDIO2_DEVICE_ROLE; // Returned by IXAudio2::GetDeviceDetails typedef struct XAUDIO2_DEVICE_DETAILS { WCHAR DeviceID[256]; // String identifier for the audio device. WCHAR DisplayName[256]; // Friendly name suitable for display to a human. XAUDIO2_DEVICE_ROLE Role; // Roles that the device should be used for. WAVEFORMATEXTENSIBLE OutputFormat; // The device's native PCM audio output format. } XAUDIO2_DEVICE_DETAILS; // from wine idl. If use code from dx header. TODO: why crash if use code from dx header? interface IXAudio2MasteringVoice : public IXAudio2Voice { virtual void STDMETHODCALLTYPE GetChannelMask( DWORD *pChannelMask) = 0; }; DEFINE_GUID(IID_IXAudio27, 0x8bcf1f58, 0x9fe7, 0x4583, 0x8a,0xc6, 0xe2,0xad,0xc4,0x65,0xc8,0xbb); MIDL_INTERFACE("8bcf1f58-9fe7-4583-8ac6-e2adc465c8bb") IXAudio2 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetDeviceCount( UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetDeviceDetails( UINT32 Index, XAUDIO2_DEVICE_DETAILS *pDeviceDetails) = 0; virtual HRESULT STDMETHODCALLTYPE Initialize( UINT32 Flags = 0, XAUDIO2_PROCESSOR XAudio2Processor = XAUDIO2_DEFAULT_PROCESSOR) = 0; virtual HRESULT STDMETHODCALLTYPE RegisterForCallbacks( IXAudio2EngineCallback *pCallback) = 0; virtual void STDMETHODCALLTYPE UnregisterForCallbacks( IXAudio2EngineCallback *pCallback) = 0; virtual HRESULT STDMETHODCALLTYPE CreateSourceVoice( IXAudio2SourceVoice **ppSourceVoice, const WAVEFORMATEX *pSourceFormat, UINT32 Flags = 0, float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO, IXAudio2VoiceCallback *pCallback = 0, const XAUDIO2_VOICE_SENDS *pSendList = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0) = 0; virtual HRESULT STDMETHODCALLTYPE CreateSubmixVoice( IXAudio2SubmixVoice **ppSubmixVoice, UINT32 InputChannels, UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0, const XAUDIO2_VOICE_SENDS *pSendList = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0) = 0; virtual HRESULT STDMETHODCALLTYPE CreateMasteringVoice( IXAudio2MasteringVoice **ppMasteringVoice, UINT32 InputChannels = XAUDIO2_DEFAULT_CHANNELS, UINT32 InputSampleRate = XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0, UINT32 DeviceIndex = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0) = 0; virtual HRESULT STDMETHODCALLTYPE StartEngine( ) = 0; virtual void STDMETHODCALLTYPE StopEngine( ) = 0; virtual HRESULT STDMETHODCALLTYPE CommitChanges( UINT32 OperationSet) = 0; virtual void STDMETHODCALLTYPE GetPerformanceData( XAUDIO2_PERFORMANCE_DATA *pPerfData) = 0; virtual void STDMETHODCALLTYPE SetDebugConfiguration( const XAUDIO2_DEBUG_CONFIGURATION *pDebugConfiguration, void *pReserved = 0) = 0; }; #ifndef GUID_SECT #define GUID_SECT #endif #define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} #define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} #define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ __DEFINE_CLSID(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) #define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ __DEFINE_IID(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) // XAudio 2.7 (June 2010 SDK) DEFINE_CLSID(XAudio2, 5a508685, a254, 4fba, 9b, 82, 9a, 24, b0, 03, 06, af); DEFINE_CLSID(XAudio2_Debug, db05ea35, 0329, 4d4b, a5, 3a, 6d, ea, d0, 3d, 38, 52); DEFINE_IID(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb); // Flags // NOTE: XAUDIO2_DEBUG_ENGINE is NOT defined in winsdk!!! #define XAUDIO2_DEBUG_ENGINE 0x0001 // Used in XAudio2Create on Windows only HRESULT XAudio2Create(__deref_out IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0), XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) { // Instantiate the appropriate XAudio2 engine IXAudio2* pXAudio2; HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, (void**)&pXAudio2); if (SUCCEEDED(hr)) { hr = pXAudio2->Initialize(Flags, XAudio2Processor); if (SUCCEEDED(hr)) { *ppXAudio2 = pXAudio2; } else { pXAudio2->Release(); } } return hr; } #else #ifndef _XBOX HRESULT XAudio2Create(__deref_out IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0), XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) { return ::XAudio2Create(ppXAudio2, Flags, XAudio2Processor); } #endif //_XBOX typedef ::IXAudio2 IXAudio2; typedef ::IXAudio2MasteringVoice IXAudio2MasteringVoice; #endif // At last, define the same types. MUST BE AT LAST to avoid ambigous, for example IXAudio2MasteringVoice is a ::IXAudio2Voice but we can use IXAudio2Voice //typedef ::IXAudio2Voice IXAudio2Voice; //typedef ::IXAudio2SourceVoice IXAudio2SourceVoice; } namespace WinSDK { #if defined(__WINRT__) typedef ::IXAudio2 IXAudio2; #elif XA2_WINSDK typedef ::IXAudio2 IXAudio2; typedef ::IXAudio2MasteringVoice IXAudio2MasteringVoice; #else typedef enum _AUDIO_STREAM_CATEGORY { AudioCategory_Other = 0, AudioCategory_ForegroundOnlyMedia, AudioCategory_BackgroundCapableMedia, AudioCategory_Communications, AudioCategory_Alerts, AudioCategory_SoundEffects, AudioCategory_GameEffects, AudioCategory_GameMedia, } AUDIO_STREAM_CATEGORY; // from wine idl. If use code from dx header. TODO: why crash if use code from dx header? interface IXAudio2MasteringVoice : public IXAudio2Voice { virtual void STDMETHODCALLTYPE GetChannelMask( DWORD *pChannelMask) = 0; }; DEFINE_GUID(IID_IXAudio2, 0x60d8dac8, 0x5aa1, 0x4e8e, 0xb5,0x97, 0x2f,0x5e,0x28,0x83,0xd4,0x84); MIDL_INTERFACE("60d8dac8-5aa1-4e8e-b597-2f5e2883d484") IXAudio2 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE RegisterForCallbacks( IXAudio2EngineCallback *pCallback) = 0; virtual void STDMETHODCALLTYPE UnregisterForCallbacks( IXAudio2EngineCallback *pCallback) = 0; virtual HRESULT STDMETHODCALLTYPE CreateSourceVoice( IXAudio2SourceVoice **ppSourceVoice, const WAVEFORMATEX *pSourceFormat, UINT32 Flags = 0, float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO, IXAudio2VoiceCallback *pCallback = 0, const XAUDIO2_VOICE_SENDS *pSendList = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0) = 0; virtual HRESULT STDMETHODCALLTYPE CreateSubmixVoice( IXAudio2SubmixVoice **ppSubmixVoice, UINT32 InputChannels, UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0, const XAUDIO2_VOICE_SENDS *pSendList = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0) = 0; virtual HRESULT STDMETHODCALLTYPE CreateMasteringVoice( IXAudio2MasteringVoice **ppMasteringVoice, UINT32 InputChannels = XAUDIO2_DEFAULT_CHANNELS, UINT32 InputSampleRate = XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0, LPCWSTR DeviceId = 0, const XAUDIO2_EFFECT_CHAIN *pEffectChain = 0, AUDIO_STREAM_CATEGORY StreamCategory = AudioCategory_GameEffects) = 0; virtual HRESULT STDMETHODCALLTYPE StartEngine( ) = 0; virtual void STDMETHODCALLTYPE StopEngine( ) = 0; virtual HRESULT STDMETHODCALLTYPE CommitChanges( UINT32 OperationSet) = 0; virtual void STDMETHODCALLTYPE GetPerformanceData( XAUDIO2_PERFORMANCE_DATA *pPerfData) = 0; virtual void STDMETHODCALLTYPE SetDebugConfiguration( const XAUDIO2_DEBUG_CONFIGURATION *pDebugConfiguration, void *pReserved = 0) = 0; }; #endif // At last, define the same types. MUST BE AT LAST to avoid ambigous, for example IXAudio2MasteringVoice is a ::IXAudio2Voice but we can use IXAudio2Voice //typedef ::IXAudio2Voice IXAudio2Voice; //typedef ::IXAudio2SourceVoice IXAudio2SourceVoice; } #endif //QTAV_XAUDIO2_COMPAT_H QtAV-1.12.0/src/output/video/000077500000000000000000000000001312235004300156345ustar00rootroot00000000000000QtAV-1.12.0/src/output/video/OpenGLRendererBase.cpp000066400000000000000000000131121312235004300217440ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/OpenGLRendererBase.h" #include "QtAV/private/OpenGLRendererBase_p.h" #include "QtAV/OpenGLVideo.h" #include "QtAV/FilterContext.h" #include #include "opengl/OpenGLHelper.h" #include "utils/Logger.h" namespace QtAV { OpenGLRendererBasePrivate::OpenGLRendererBasePrivate(QPaintDevice* pd) : painter(new QPainter()) , frame_changed(false) { filter_context = VideoFilterContext::create(VideoFilterContext::QtPainter); filter_context->paint_device = pd; filter_context->painter = painter; } OpenGLRendererBasePrivate::~OpenGLRendererBasePrivate() { if (painter) { delete painter; painter = 0; } } void OpenGLRendererBasePrivate::setupAspectRatio() { matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); if (orientation) matrix.rotate(orientation, 0, 0, 1); // Z axis } OpenGLRendererBase::OpenGLRendererBase(OpenGLRendererBasePrivate &d) : VideoRenderer(d) { setPreferredPixelFormat(VideoFormat::Format_YUV420P); } OpenGLRendererBase::~OpenGLRendererBase() { d_func().glv.setOpenGLContext(0); } bool OpenGLRendererBase::isSupported(VideoFormat::PixelFormat pixfmt) const { return OpenGLVideo::isSupported(pixfmt); } OpenGLVideo* OpenGLRendererBase::opengl() const { return const_cast(&d_func().glv); } bool OpenGLRendererBase::receiveFrame(const VideoFrame& frame) { DPTR_D(OpenGLRendererBase); d.video_frame = frame; d.frame_changed = true; updateUi(); //can not call updateGL() directly because no event and paintGL() will in video thread return true; } void OpenGLRendererBase::drawBackground() { d_func().glv.fill(backgroundColor()); } void OpenGLRendererBase::drawFrame() { DPTR_D(OpenGLRendererBase); QRect roi = realROI(); //d.glv.render(QRectF(-1, 1, 2, -2), roi, d.matrix); // QRectF() means the whole viewport if (d.frame_changed) { d.glv.setCurrentFrame(d.video_frame); d.frame_changed = false; } d.glv.render(QRectF(), roi, d.matrix); } void OpenGLRendererBase::onInitializeGL() { DPTR_D(OpenGLRendererBase); //makeCurrent(); #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) initializeOpenGLFunctions(); #endif QOpenGLContext *ctx = const_cast(QOpenGLContext::currentContext()); //qt4 returns const d.glv.setOpenGLContext(ctx); } void OpenGLRendererBase::onPaintGL() { DPTR_D(OpenGLRendererBase); /* we can mix gl and qpainter. * QPainter painter(this); * painter.beginNativePainting(); * gl functions... * painter.endNativePainting(); * swapBuffers(); */ handlePaintEvent(); //context()->swapBuffers(this); if (d.painter && d.painter->isActive()) d.painter->end(); } void OpenGLRendererBase::onResizeGL(int w, int h) { if (!QOpenGLContext::currentContext()) return; DPTR_D(OpenGLRendererBase); d.glv.setProjectionMatrixToRect(QRectF(0, 0, w, h)); d.setupAspectRatio(); } void OpenGLRendererBase::onResizeEvent(int w, int h) { DPTR_D(OpenGLRendererBase); d.update_background = true; resizeRenderer(w, h); d.setupAspectRatio(); //QOpenGLWindow::resizeEvent(e); //will call resizeGL(). TODO:will call paintEvent()? } //TODO: out_rect not correct when top level changed void OpenGLRendererBase::onShowEvent() { DPTR_D(OpenGLRendererBase); d.update_background = true; /* * Do something that depends on widget below! e.g. recreate render target for direct2d. * When Qt::WindowStaysOnTopHint changed, window will hide first then show. If you * don't do anything here, the widget content will never be updated. */ } void OpenGLRendererBase::onSetOutAspectRatio(qreal ratio) { Q_UNUSED(ratio); DPTR_D(OpenGLRendererBase); d.setupAspectRatio(); } void OpenGLRendererBase::onSetOutAspectRatioMode(OutAspectRatioMode mode) { Q_UNUSED(mode); DPTR_D(OpenGLRendererBase); d.setupAspectRatio(); } bool OpenGLRendererBase::onSetOrientation(int value) { Q_UNUSED(value) d_func().setupAspectRatio(); return true; } bool OpenGLRendererBase::onSetBrightness(qreal b) { d_func().glv.setBrightness(b); return true; } bool OpenGLRendererBase::onSetContrast(qreal c) { d_func().glv.setContrast(c); return true; } bool OpenGLRendererBase::onSetHue(qreal h) { d_func().glv.setHue(h); return true; } bool OpenGLRendererBase::onSetSaturation(qreal s) { d_func().glv.setSaturation(s); return true; } } //namespace QtAV QtAV-1.12.0/src/output/video/OpenGLWindowRenderer.cpp000066400000000000000000000046151312235004300223510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/OpenGLWindowRenderer.h" #include "QtAV/private/OpenGLRendererBase_p.h" #include "QtAV/private/factory.h" #include #include "utils/Logger.h" namespace QtAV { FACTORY_REGISTER(VideoRenderer, OpenGLWindow, "OpenGLWindow") class OpenGLWindowRendererPrivate : public OpenGLRendererBasePrivate { public: OpenGLWindowRendererPrivate(QPaintDevice* pd) : OpenGLRendererBasePrivate(pd) {} }; OpenGLWindowRenderer::OpenGLWindowRenderer(UpdateBehavior updateBehavior, QWindow *parent): QOpenGLWindow(updateBehavior, parent) , OpenGLRendererBase(*new OpenGLWindowRendererPrivate(this)) { } VideoRendererId OpenGLWindowRenderer::id() const { return VideoRendererId_OpenGLWindow; } // MUST call update() on gui(main) thread that the window belongs to because update() will finally call startTimer void OpenGLWindowRenderer::initializeGL() { onInitializeGL(); } void OpenGLWindowRenderer::paintGL() { onPaintGL(); } void OpenGLWindowRenderer::resizeGL(int w, int h) { onResizeGL(w, h); } void OpenGLWindowRenderer::resizeEvent(QResizeEvent *e) { onResizeEvent(e->size().width(), e->size().height()); QOpenGLWindow::resizeEvent(e); //will call resizeGL(). TODO:will call paintEvent()? } void OpenGLWindowRenderer::showEvent(QShowEvent *) { onShowEvent(); resizeGL(width(), height()); } } //namespace QtAV QtAV-1.12.0/src/output/video/QPainterRenderer.cpp000066400000000000000000000112671312235004300215610ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include namespace QtAV { QPainterRenderer::QPainterRenderer() :VideoRenderer(*new QPainterRendererPrivate()) { DPTR_D(QPainterRenderer); d.filter_context = VideoFilterContext::create(VideoFilterContext::QtPainter); } QPainterRenderer::QPainterRenderer(QPainterRendererPrivate &d) :VideoRenderer(d) { d.filter_context = VideoFilterContext::create(VideoFilterContext::QtPainter); } bool QPainterRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { return VideoFormat::imageFormatFromPixelFormat(pixfmt) != QImage::Format_Invalid; } bool QPainterRenderer::preparePixmap(const VideoFrame &frame) { DPTR_D(QPainterRenderer); // already locked in a larger scope of receive() QImage::Format imgfmt = frame.imageFormat(); if (frame.constBits(0)) { d.video_frame = frame; } else { if (imgfmt == QImage::Format_Invalid) { d.video_frame = frame.to(VideoFormat::Format_RGB32); imgfmt = d.video_frame.imageFormat(); } else { d.video_frame = frame.to(frame.pixelFormat()); } } const bool swapRGB = (int)imgfmt < 0; if (swapRGB) { imgfmt = (QImage::Format)(-imgfmt); } // DO NOT use frameData().data() because it's temp ptr while d.image does not deep copy the data QImage image = QImage((uchar*)d.video_frame.constBits(), d.video_frame.width(), d.video_frame.height(), d.video_frame.bytesPerLine(), imgfmt); if (swapRGB) image = image.rgbSwapped(); d.pixmap = QPixmap::fromImage(image); //Format_RGB32 is fast. see document return true; } void QPainterRenderer::drawBackground() { DPTR_D(QPainterRenderer); if (!d.painter) return; const QRegion bgRegion(backgroundRegion()); if (bgRegion.isEmpty()) return; #if 0 d.painter->save(); d.painter->setClipRegion(bgRegion); d.painter->fillRect(QRect(QPoint(), rendererSize()), backgroundColor()); d.painter->restore(); #else const QVector bg(bgRegion.rects()); foreach (const QRect& r, bg) { d.painter->fillRect(r, backgroundColor()); } #endif } void QPainterRenderer::drawFrame() { DPTR_D(QPainterRenderer); if (!d.painter) return; if (d.pixmap.isNull()) return; QRect roi = realROI(); if (orientation() == 0) { //assume that the image data is already scaled to out_size(NOT renderer size!) if (roi.size() == d.out_rect.size()) { d.painter->drawPixmap(d.out_rect.topLeft(), d.pixmap, roi); } else { d.painter->drawPixmap(d.out_rect, d.pixmap, roi); //what's the difference? //d.painter->drawPixmap(QPoint(), d.pixmap.scaled(d.renderer_width, d.renderer_height)); } return; } // render to whole renderer rect in painter's transformed coordinate // scale ratio is different from gl based renderers. gl always fill the whole rect d.painter->save(); d.painter->translate(rendererWidth()/2, rendererHeight()/2); // TODO: why rotate then scale gives wrong result? if (orientation() % 180) d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererHeight(), (qreal)d.out_rect.height()/(qreal)rendererWidth()); else d.painter->scale((qreal)d.out_rect.width()/(qreal)rendererWidth(), (qreal)d.out_rect.height()/(qreal)rendererHeight()); d.painter->rotate(orientation()); d.painter->translate(-rendererWidth()/2, -rendererHeight()/2); d.painter->drawPixmap(QRect(0, 0, rendererWidth(), rendererHeight()), d.pixmap, roi); d.painter->restore(); } } //namespace QtAV QtAV-1.12.0/src/output/video/VideoOutput.cpp000066400000000000000000000257021312235004300206350ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoOutput.h" #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/version.h" #include #include #include "utils/Logger.h" /*! * onSetXXX(...): impl->onSetXXX(...); set value as impl; return ; */ namespace QtAV { class VideoOutputPrivate : public VideoRendererPrivate { public: VideoOutputPrivate(VideoRendererId rendererId, bool force) { if (!VideoRenderer::id("Widget")) { //TODO: dso version check? #if defined(Q_OS_DARWIN) avwidgets.setFileName(QStringLiteral("QtAVWidgets.framework/QtAVWidgets")); //no dylib check #elif defined(Q_OS_WIN) avwidgets.setFileName(QStringLiteral("QtAVWidgets").append(QString::number(QTAV_VERSION_MAJOR(QtAV_Version())))); #else avwidgets.setFileNameAndVersion(QStringLiteral("QtAVWidgets"), QTAV_VERSION_MAJOR(QtAV_Version())); #endif qDebug() << "Loading QtAVWidgets module: " << avwidgets.fileName(); if (!avwidgets.load()) { qWarning("Failed to load QtAVWidgets module"); } } impl = VideoRenderer::create(rendererId); if (!impl && !force) { VideoRendererId *vid = NULL; while ((vid = VideoRenderer::next(vid))) { qDebug("next id: %d, name: %s", *vid, VideoRenderer::name(*vid)); if (impl) { delete impl; impl = 0; } impl = VideoRenderer::create(*vid); if (impl && impl->isAvailable() && impl->widget()) break; } } available = !!impl; if (!available) return; // set members as impl's that may be already set in ctor filters = impl->filters(); renderer_width = impl->rendererWidth(); renderer_height = impl->rendererHeight(); src_width = impl->videoFrameSize().width(); src_height = impl->videoFrameSize().height(); source_aspect_ratio = qreal(src_width)/qreal(src_height); out_aspect_ratio_mode = impl->outAspectRatioMode(); out_aspect_ratio = impl->outAspectRatio(); quality = impl->quality(); out_rect = impl->videoRect(); roi = impl->regionOfInterest(); preferred_format = impl->preferredPixelFormat(); force_preferred = impl->isPreferredPixelFormatForced(); brightness = impl->brightness(); contrast = impl->contrast(); hue = impl->hue(); saturation = impl->saturation(); } ~VideoOutputPrivate() { if (impl) { QObject* obj = reinterpret_cast(impl->widget()); if (obj && !obj->parent()) obj->deleteLater(); impl = 0; } } VideoRenderer *impl; QLibrary avwidgets; }; VideoOutput::VideoOutput(QObject *parent) : QObject(parent) , VideoRenderer(*new VideoOutputPrivate(0, false)) { if (d_func().impl && d_func().impl->widget()) { ((QObject*)d_func().impl->widget())->installEventFilter(this); } } VideoOutput::VideoOutput(VideoRendererId rendererId, QObject *parent) : QObject(parent) , VideoRenderer(*new VideoOutputPrivate(rendererId, true)) { if (d_func().impl && d_func().impl->widget()) { ((QObject*)d_func().impl->widget())->installEventFilter(this); } } VideoOutput::~VideoOutput() { if (d_func().impl && d_func().impl->widget()) { ((QObject*)d_func().impl->widget())->removeEventFilter(this); } } VideoRendererId VideoOutput::id() const { if (!isAvailable()) return 0; return d_func().impl->id(); } bool VideoOutput::onSetPreferredPixelFormat(VideoFormat::PixelFormat pixfmt) { if (!isAvailable()) return false; DPTR_D(VideoOutput); d.impl->setPreferredPixelFormat(pixfmt); return pixfmt == d.impl->preferredPixelFormat(); } VideoFormat::PixelFormat VideoOutput::preferredPixelFormat() const { if (!isAvailable()) return VideoFormat::Format_Invalid; return d_func().impl->preferredPixelFormat(); } bool VideoOutput::isSupported(VideoFormat::PixelFormat pixfmt) const { if (!isAvailable()) return false; return d_func().impl->isSupported(pixfmt); } QWindow* VideoOutput::qwindow() { if (!isAvailable()) return 0; return d_func().impl->qwindow(); } QWidget* VideoOutput::widget() { if (!isAvailable()) return 0; return d_func().impl->widget(); } QGraphicsItem* VideoOutput::graphicsItem() { if (!isAvailable()) return 0; return d_func().impl->graphicsItem(); } OpenGLVideo* VideoOutput::opengl() const { if (!isAvailable()) return 0; return d_func().impl->opengl(); } bool VideoOutput::eventFilter(QObject *obj, QEvent *event) { DPTR_D(VideoOutput); if (!d.impl || (QObject*)d.impl->widget() != obj) return QObject::eventFilter(obj, event); if (event->type() == QEvent::Resize) { QResizeEvent *re = static_cast(event); resizeRenderer(re->size()); } return QObject::eventFilter(obj, event); } bool VideoOutput::receiveFrame(const VideoFrame& frame) { if (!isAvailable()) return false; DPTR_D(VideoOutput); d.impl->d_func().source_aspect_ratio = d.source_aspect_ratio; d.impl->setInSize(frame.size()); QMutexLocker locker(&d.impl->d_func().img_mutex); Q_UNUSED(locker); //TODO: double buffer for display/dec frame to avoid mutex return d.impl->receiveFrame(frame); } void VideoOutput::drawBackground() { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->drawBackground(); } void VideoOutput::drawFrame() { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->drawFrame(); } void VideoOutput::handlePaintEvent() { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->handlePaintEvent(); } bool VideoOutput::onForcePreferredPixelFormat(bool force) { if (!isAvailable()) return false; DPTR_D(VideoOutput); d.impl->forcePreferredPixelFormat(force); return d.impl->isPreferredPixelFormatForced() == force; } void VideoOutput::onSetOutAspectRatioMode(OutAspectRatioMode mode) { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->setOutAspectRatioMode(mode); } void VideoOutput::onSetOutAspectRatio(qreal ratio) { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->setOutAspectRatio(ratio); } bool VideoOutput::onSetQuality(Quality q) { if (!isAvailable()) return false; DPTR_D(VideoOutput); d.impl->setQuality(q); return d.impl->quality() == q; } bool VideoOutput::onSetOrientation(int value) { if (!isAvailable()) return false; value = (value + 360) % 360; DPTR_D(VideoOutput); d.impl->setOrientation(value); if (d.impl->orientation() != value) { return false; } return true; } void VideoOutput::onResizeRenderer(int width, int height) { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->resizeRenderer(width, height); } bool VideoOutput::onSetRegionOfInterest(const QRectF& roi) { if (!isAvailable()) return false; DPTR_D(VideoOutput); d.impl->setRegionOfInterest(roi); return true; } QPointF VideoOutput::onMapToFrame(const QPointF& p) const { if (!isAvailable()) return QPointF(); DPTR_D(const VideoOutput); return d.impl->onMapToFrame(p); } QPointF VideoOutput::onMapFromFrame(const QPointF& p) const { if (!isAvailable()) return QPointF(); DPTR_D(const VideoOutput); return d.impl->onMapFromFrame(p); } bool VideoOutput::onSetBrightness(qreal brightness) { if (!isAvailable()) return false; DPTR_D(VideoOutput); // not call onSetXXX here, otherwise states in impl will not change d.impl->setBrightness(brightness); if (brightness != d.impl->brightness()) { return false; } return true; } bool VideoOutput::onSetContrast(qreal contrast) { if (!isAvailable()) return false; DPTR_D(VideoOutput); // not call onSetXXX here, otherwise states in impl will not change d.impl->setContrast(contrast); if (contrast != d.impl->contrast()) { return false; } return true; } bool VideoOutput::onSetHue(qreal hue) { if (!isAvailable()) return false; DPTR_D(VideoOutput); // not call onSetXXX here, otherwise states in impl will not change d.impl->setHue(hue); if (hue != d.impl->hue()) { return false; } return true; } bool VideoOutput::onSetSaturation(qreal saturation) { if (!isAvailable()) return false; DPTR_D(VideoOutput); // not call onSetXXX here, otherwise states in impl will not change d.impl->setSaturation(saturation); if (saturation != d.impl->saturation()) { return false; } return true; } void VideoOutput::onSetBackgroundColor(const QColor &color) { if (!isAvailable()) return; d_func().impl->setBackgroundColor(color); } void VideoOutput::setStatistics(Statistics* statistics) { if (!isAvailable()) return; DPTR_D(VideoOutput); d.impl->setStatistics(statistics); // only used internally for AVOutput //d.statistics = } bool VideoOutput::onInstallFilter(Filter *filter, int index) { if (!isAvailable()) return false; DPTR_D(VideoOutput); bool ret = d.impl->onInstallFilter(filter, index); d.filters = d.impl->filters(); return ret; } bool VideoOutput::onUninstallFilter(Filter *filter) { if (!isAvailable()) return false; DPTR_D(VideoOutput); bool ret = d.impl->onUninstallFilter(filter); // only used internally for AVOutput //d.pending_uninstall_filters = return ret; } bool VideoOutput::onHanlePendingTasks() { if (!isAvailable()) return false; DPTR_D(VideoOutput); if (!d.impl->onHanlePendingTasks()) return false; d.filters = d.impl->filters(); return true; } } //namespace QtAV QtAV-1.12.0/src/output/video/VideoRenderer.cpp000066400000000000000000000466521312235004300211120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoRenderer.h" #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/Filter.h" #include #include #include "QtAV/Statistics.h" #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(VideoRenderer) VideoRendererId VideoRendererId_OpenGLWindow = mkid::id32base36_6<'Q', 'O', 'G', 'L', 'W', 'w'>::value; VideoRenderer::VideoRenderer() :AVOutput(*new VideoRendererPrivate) { // can not do 'if (widget()) connect to update()' because widget() is virtual } VideoRenderer::VideoRenderer(VideoRendererPrivate &d) :AVOutput(d) { } VideoRenderer::~VideoRenderer() { } bool VideoRenderer::receive(const VideoFrame &frame) { DPTR_D(VideoRenderer); const qreal dar_old = d.source_aspect_ratio; d.source_aspect_ratio = frame.displayAspectRatio(); if (dar_old != d.source_aspect_ratio) sourceAspectRatioChanged(d.source_aspect_ratio); setInSize(frame.width(), frame.height()); QMutexLocker locker(&d.img_mutex); Q_UNUSED(locker); //TODO: double buffer for display/dec frame to avoid mutex return receiveFrame(frame); } bool VideoRenderer::setPreferredPixelFormat(VideoFormat::PixelFormat pixfmt) { DPTR_D(VideoRenderer); if (d.preferred_format == pixfmt) return false; if (!isSupported(pixfmt)) { qWarning("pixel format '%s' is not supported", VideoFormat(pixfmt).name().toUtf8().constData()); return false; } VideoFormat::PixelFormat old = d.preferred_format; d.preferred_format = pixfmt; if (!onSetPreferredPixelFormat(pixfmt)) { qWarning("onSetPreferredPixelFormat failed"); d.preferred_format = old; return false; } return true; } bool VideoRenderer::onSetPreferredPixelFormat(VideoFormat::PixelFormat pixfmt) { Q_UNUSED(pixfmt); return true; } VideoFormat::PixelFormat VideoRenderer::preferredPixelFormat() const { return d_func().preferred_format; } void VideoRenderer::forcePreferredPixelFormat(bool force) { DPTR_D(VideoRenderer); if (d.force_preferred == force) return; bool old = d.force_preferred; d.force_preferred = force; if (!onForcePreferredPixelFormat(force)) { qWarning("onForcePreferredPixelFormat failed"); d.force_preferred = old; } } bool VideoRenderer::onForcePreferredPixelFormat(bool force) { Q_UNUSED(force); return true; } bool VideoRenderer::isPreferredPixelFormatForced() const { return d_func().force_preferred; } qreal VideoRenderer::sourceAspectRatio() const { return d_func().source_aspect_ratio; } void VideoRenderer::setOutAspectRatioMode(OutAspectRatioMode mode) { DPTR_D(VideoRenderer); if (mode == d.out_aspect_ratio_mode) return; d.aspect_ratio_changed = true; d.out_aspect_ratio_mode = mode; if (mode == RendererAspectRatio) { QRect out_rect0(d.out_rect); //compute out_rect d.out_rect = QRect(0, 0, d.renderer_width, d.renderer_height); //remove? already in computeOutParameters() setOutAspectRatio(qreal(d.renderer_width)/qreal(d.renderer_height)); if (out_rect0 != d.out_rect) { Q_EMIT videoRectChanged(); Q_EMIT contentRectChanged(); } //is that thread safe? } else if (mode == VideoAspectRatio) { setOutAspectRatio(d.source_aspect_ratio); } onSetOutAspectRatioMode(mode); Q_EMIT outAspectRatioModeChanged(); } void VideoRenderer::onSetOutAspectRatioMode(OutAspectRatioMode mode) { Q_UNUSED(mode); } VideoRenderer::OutAspectRatioMode VideoRenderer::outAspectRatioMode() const { return d_func().out_aspect_ratio_mode; } void VideoRenderer::setOutAspectRatio(qreal ratio) { DPTR_D(VideoRenderer); bool ratio_changed = d.out_aspect_ratio != ratio; d.out_aspect_ratio = ratio; //indicate that this function is called by user. otherwise, called in VideoRenderer if (!d.aspect_ratio_changed) { if (d.out_aspect_ratio_mode != CustomAspectRation) { d.out_aspect_ratio_mode = CustomAspectRation; Q_EMIT outAspectRatioModeChanged(); } } d.aspect_ratio_changed = false; //TODO: when is false? if (d.out_aspect_ratio_mode != RendererAspectRatio) { d.update_background = true; //can not fill the whole renderer with video } //compute the out out_rect if (d.computeOutParameters(ratio)) { Q_EMIT videoRectChanged(); Q_EMIT contentRectChanged(); } if (ratio_changed) { onSetOutAspectRatio(ratio); Q_EMIT outAspectRatioChanged(); } updateUi(); } void VideoRenderer::onSetOutAspectRatio(qreal ratio) { Q_UNUSED(ratio); } qreal VideoRenderer::outAspectRatio() const { return d_func().out_aspect_ratio; } void VideoRenderer::setQuality(Quality q) { DPTR_D(VideoRenderer); if (d.quality == q) return; Quality old = quality(); d.quality = q; if (!onSetQuality(q)) { d.quality = old; } else { updateUi(); } } bool VideoRenderer::onSetQuality(Quality q) { Q_UNUSED(q); return true; } VideoRenderer::Quality VideoRenderer::quality() const { return d_func().quality; } void VideoRenderer::setInSize(const QSize& s) { setInSize(s.width(), s.height()); } void VideoRenderer::setInSize(int width, int height) { DPTR_D(VideoRenderer); if (d.src_width != width || d.src_height != height) { d.aspect_ratio_changed = true; //?? for VideoAspectRatio mode d.src_width = width; d.src_height = height; Q_EMIT videoFrameSizeChanged(); } if (!d.aspect_ratio_changed)// && (d.src_width == width && d.src_height == height)) return; //d.source_aspect_ratio = qreal(d.src_width)/qreal(d.src_height); qDebug("%s => calculating aspect ratio from converted input data(%f)", __FUNCTION__, d.source_aspect_ratio); //see setOutAspectRatioMode if (d.out_aspect_ratio_mode == VideoAspectRatio) { //source_aspect_ratio equals to original video aspect ratio here, also equals to out ratio setOutAspectRatio(d.source_aspect_ratio); } d.aspect_ratio_changed = false; //TODO: why graphicsitemrenderer need this? otherwise aspect_ratio_changed is always true? } void VideoRenderer::resizeRenderer(const QSize &size) { resizeRenderer(size.width(), size.height()); } void VideoRenderer::resizeRenderer(int width, int height) { DPTR_D(VideoRenderer); if (width == 0 || height == 0 || (d.renderer_width == width && d.renderer_height == height)) return; d.renderer_width = width; d.renderer_height = height; if (d.out_aspect_ratio_mode == RendererAspectRatio) Q_EMIT outAspectRatioChanged(); if (d.computeOutParameters(d.out_aspect_ratio)) { Q_EMIT videoRectChanged(); Q_EMIT contentRectChanged(); } onResizeRenderer(width, height); //TODO: resize widget } void VideoRenderer::onResizeRenderer(int width, int height) { Q_UNUSED(width); Q_UNUSED(height); } QSize VideoRenderer::rendererSize() const { DPTR_D(const VideoRenderer); return QSize(d.renderer_width, d.renderer_height); } int VideoRenderer::rendererWidth() const { return d_func().renderer_width; } int VideoRenderer::rendererHeight() const { return d_func().renderer_height; } void VideoRenderer::setOrientation(int value) { // currently only supports a multiple of 90 value = (value + 360) % 360; if (value % 90) return; DPTR_D(VideoRenderer); if (d.orientation == value) return; int old = orientation(); d.orientation = value; if (!onSetOrientation(value)) { d.orientation = old; } else { orientationChanged(); if (d.computeOutParameters(d.out_aspect_ratio)) { Q_EMIT videoRectChanged(); Q_EMIT contentRectChanged(); } onSetOutAspectRatio(outAspectRatio()); updateUi(); } } int VideoRenderer::orientation() const { return d_func().orientation; } // only qpainter and opengl based renderers support orientation. bool VideoRenderer::onSetOrientation(int value) { Q_UNUSED(value); return false; } QSize VideoRenderer::videoFrameSize() const { DPTR_D(const VideoRenderer); return QSize(d.src_width, d.src_height); } QRect VideoRenderer::videoRect() const { return d_func().out_rect; } QRectF VideoRenderer::regionOfInterest() const { return d_func().roi; } void VideoRenderer::setRegionOfInterest(qreal x, qreal y, qreal width, qreal height) { setRegionOfInterest(QRectF(x, y, width, height)); } void VideoRenderer::setRegionOfInterest(const QRectF &roi) { DPTR_D(VideoRenderer); if (d.roi == roi) return; QRectF old = regionOfInterest(); d.roi = roi; if (!onSetRegionOfInterest(roi)) { d.roi = old; } else { Q_EMIT regionOfInterestChanged(); updateUi(); } // TODO: how to fill video? what's out_rect now? } bool VideoRenderer::onSetRegionOfInterest(const QRectF &roi) { Q_UNUSED(roi); return true; } QRect VideoRenderer::realROI() const { DPTR_D(const VideoRenderer); if (!d.roi.isValid()) { return QRect(QPoint(), d.video_frame.size()); } QRect r = d.roi.toRect(); // nomalized x, y < 1 bool normalized = false; if (qAbs(d.roi.x()) < 1) { normalized = true; r.setX(d.roi.x()*qreal(d.src_width)); //TODO: why not video_frame.size()? roi not correct } if (qAbs(d.roi.y()) < 1) { normalized = true; r.setY(d.roi.y()*qreal(d.src_height)); } // whole size use width or height = 0, i.e. null size // nomalized width, height <= 1. If 1 is normalized value iff |x|<1 || |y| < 1 if (qAbs(d.roi.width()) < 1) r.setWidth(d.roi.width()*qreal(d.src_width)); if (qAbs(d.roi.height()) < 1) r.setHeight(d.roi.height()*qreal(d.src_height)); if (d.roi.width() == 1.0 && normalized) { r.setWidth(d.src_width); } if (d.roi.height() == 1.0 && normalized) { r.setHeight(d.src_height); } //TODO: insect with source rect? return r; } QRectF VideoRenderer::normalizedROI() const { DPTR_D(const VideoRenderer); if (!d.roi.isValid()) { return QRectF(0, 0, 1, 1); } QRectF r = d.roi; bool normalized = false; if (qAbs(r.x()) >= 1) r.setX(r.x()/qreal(d.src_width)); else normalized = true; if (qAbs(r.y()) >= 1) r.setY(r.y()/qreal(d.src_height)); else normalized = true; if (r.width() > 1 || (!normalized && r.width() == 1)) r.setWidth(r.width()/qreal(d.src_width)); if (r.height() > 1 || (!normalized && r.width() == 1)) { r.setHeight(r.height()/qreal(d.src_height)); } return r; } QPointF VideoRenderer::mapToFrame(const QPointF &p) const { return onMapToFrame(p); } // TODO: orientation QPointF VideoRenderer::onMapToFrame(const QPointF &p) const { QRectF roi = realROI(); // zoom=roi.w/roi.h>vo.w/vo.h?roi.w/vo.w:roi.h/vo.h qreal zoom = qMax(roi.width()/rendererWidth(), roi.height()/rendererHeight()); QPointF delta = p - QPointF(rendererWidth()/2, rendererHeight()/2); return roi.center() + delta * zoom; } QPointF VideoRenderer::mapFromFrame(const QPointF &p) const { return onMapFromFrame(p); } QPointF VideoRenderer::onMapFromFrame(const QPointF &p) const { QRectF roi = realROI(); // zoom=roi.w/roi.h>vo.w/vo.h?roi.w/vo.w:roi.h/vo.h qreal zoom = qMax(roi.width()/rendererWidth(), roi.height()/rendererHeight()); // (p-roi.c)/zoom + c QPointF delta = p - roi.center(); return QPointF(rendererWidth()/2, rendererHeight()/2) + delta / zoom; } QRegion VideoRenderer::backgroundRegion() const { return QRegion(0, 0, rendererWidth(), rendererHeight()) - QRegion(d_func().out_rect); } void VideoRenderer::drawBackground() { } void VideoRenderer::handlePaintEvent() { DPTR_D(VideoRenderer); d.setupQuality(); //begin paint. how about QPainter::beginNativePainting()? { //lock is required only when drawing the frame QMutexLocker locker(&d.img_mutex); Q_UNUSED(locker); // do not apply filters if d.video_frame is already filtered. e.g. rendering an image and resize window to repaint if (!d.video_frame.metaData(QStringLiteral("gpu_filtered")).toBool() && !d.filters.isEmpty() && d.statistics) { // vo filter will not modify video frame, no lock required foreach(Filter* filter, d.filters) { VideoFilter *vf = static_cast(filter); if (!vf) { qWarning("a null filter!"); //d.filters.removeOne(filter); continue; } if (!vf->isEnabled()) continue; // qpainter on video frame always runs on video thread. qpainter on renderer's paint device can work on rendering thread // Here apply filters on frame on video thread, for example, GPU filters //vf->prepareContext(d.filter_context, d.statistics, 0); //if (!vf->context() || vf->context()->type() != VideoFilterContext::OpenGL) if (!vf->isSupported(VideoFilterContext::OpenGL)) continue; vf->apply(d.statistics, &d.video_frame); //painter and paint device are ready, pass video frame is ok. d.video_frame.setMetaData(QStringLiteral("gpu_filtered"), true); } } /* begin paint. how about QPainter::beginNativePainting()? * fill background color when necessary, e.g. renderer is resized, image is null * if we access d.data which will be modified in AVThread, the following must be * protected by mutex. otherwise, e.g. QPainterRenderer, it's not required if drawing * on the shared data is safe */ drawBackground(); /* * NOTE: if data is not copyed in receiveFrame(), you should always call drawFrame() */ if (d.video_frame.isValid()) { drawFrame(); if (d.statistics) { d.statistics->video_only.frameDisplayed(d.video_frame.timestamp()); d.statistics->video.current_time = QTime(0, 0, 0).addMSecs(int(d.video_frame.timestamp() * 1000.0)); } } } hanlePendingTasks(); //TODO: move to AVOutput::applyFilters() //protected? if (!d.filters.isEmpty() && d.filter_context && d.statistics) { // vo filter will not modify video frame, no lock required foreach(Filter* filter, d.filters) { VideoFilter *vf = static_cast(filter); if (!vf) { qWarning("a null filter!"); //d.filters.removeOne(filter); continue; } if (!vf->isEnabled()) continue; // qpainter rendering on renderer's paint device. only supported by none-null paint engine if (!vf->context() || vf->context()->type() == VideoFilterContext::OpenGL) continue; if (vf->prepareContext(d.filter_context, d.statistics, 0)) { if (!vf->isSupported(d.filter_context->type())) continue; vf->apply(d.statistics, &d.video_frame); //painter and paint device are ready, pass video frame is ok. } } } //end paint. how about QPainter::endNativePainting()? } qreal VideoRenderer::brightness() const { return d_func().brightness; } bool VideoRenderer::setBrightness(qreal brightness) { DPTR_D(VideoRenderer); if (d.brightness == brightness) return true; if (!onSetBrightness(brightness)) return false; d.brightness = brightness; Q_EMIT brightnessChanged(brightness); updateUi(); return true; } qreal VideoRenderer::contrast() const { return d_func().contrast; } bool VideoRenderer::setContrast(qreal contrast) { DPTR_D(VideoRenderer); if (d.contrast == contrast) return true; if (!onSetContrast(contrast)) return false; d.contrast = contrast; Q_EMIT contrastChanged(contrast); updateUi(); return true; } qreal VideoRenderer::hue() const { return d_func().hue; } bool VideoRenderer::setHue(qreal hue) { DPTR_D(VideoRenderer); if (d.hue == hue) return true; if (!onSetHue(hue)) return false; d.hue = hue; Q_EMIT hueChanged(hue); updateUi(); return true; } qreal VideoRenderer::saturation() const { return d_func().saturation; } bool VideoRenderer::setSaturation(qreal saturation) { DPTR_D(VideoRenderer); if (d.saturation == saturation) return true; if (!onSetSaturation(saturation)) return false; d.saturation = saturation; Q_EMIT saturationChanged(saturation); updateUi(); return true; } bool VideoRenderer::onSetBrightness(qreal b) { Q_UNUSED(b); return false; } bool VideoRenderer::onSetContrast(qreal c) { Q_UNUSED(c); return false; } bool VideoRenderer::onSetHue(qreal h) { Q_UNUSED(h); return false; } bool VideoRenderer::onSetSaturation(qreal s) { Q_UNUSED(s); return false; } QColor VideoRenderer::backgroundColor() const { return d_func().bg_color; } void VideoRenderer::onSetBackgroundColor(const QColor &color) { Q_UNUSED(color); } void VideoRenderer::setBackgroundColor(const QColor &c) { DPTR_D(VideoRenderer); if (d.bg_color == c) return; onSetBackgroundColor(c); d.bg_color = c; Q_EMIT backgroundColorChanged(); updateUi(); } void VideoRenderer::updateUi() { QObject *obj = (QObject*)widget(); if (obj) { // UpdateRequest only sync backing store but do not shedule repainting. UpdateLater does // Copy from qwidget_p.h. QWidget::event() will convert UpdateLater to QUpdateLaterEvent and get it's region() class QUpdateLaterEvent : public QEvent { public: explicit QUpdateLaterEvent(const QRegion& paintRegion) : QEvent(UpdateLater), m_region(paintRegion) {} ~QUpdateLaterEvent() {} inline const QRegion ®ion() const { return m_region; } protected: QRegion m_region; }; QCoreApplication::instance()->postEvent(obj, new QUpdateLaterEvent(QRegion(0, 0, rendererWidth(), rendererHeight()))); } else { obj = (QObject*)qwindow(); if (obj) QCoreApplication::instance()->postEvent(obj, new QEvent(QEvent::UpdateRequest)); } } } //namespace QtAV QtAV-1.12.0/src/shaders/000077500000000000000000000000001312235004300146175ustar00rootroot00000000000000QtAV-1.12.0/src/shaders/packed.f.glsl000066400000000000000000000034541312235004300171630ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ uniform sampler2D u_Texture0; varying vec2 v_TexCoords0; uniform mat4 u_colorMatrix; uniform float u_opacity; uniform mat4 u_c; /***User header code***%userHeader%***/ /***User sampling function here***%userSample%***/ #ifndef USER_SAMPLER vec4 sample2d(sampler2D tex, vec2 pos, int plane) { return texture(tex, pos); } #endif void main() { vec4 c = sample2d(u_Texture0, v_TexCoords0, 0); c = u_c * c; #ifndef HAS_ALPHA c.a = 1.0; // before color mat transform! #endif //HAS_ALPHA #ifdef XYZ_GAMMA c.rgb = pow(c.rgb, vec3(2.6)); #endif // XYZ_GAMMA c = u_colorMatrix * c; #ifdef XYZ_GAMMA c.rgb = pow(c.rgb, vec3(1.0/2.2)); #endif //XYZ_GAMMA gl_FragColor = clamp(c, 0.0, 1.0) * u_opacity; /***User post processing here***%userPostProcess%***/ } QtAV-1.12.0/src/shaders/planar.f.glsl000066400000000000000000000073751312235004300172170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ // u_TextureN: yuv. use array? uniform sampler2D u_Texture0; uniform sampler2D u_Texture1; uniform sampler2D u_Texture2; #ifdef HAS_ALPHA uniform sampler2D u_Texture3; #endif //HAS_ALPHA varying vec2 v_TexCoords0; #ifdef MULTI_COORD varying vec2 v_TexCoords1; varying vec2 v_TexCoords2; #ifdef HAS_ALPHA varying vec2 v_TexCoords3; #endif #else #define v_TexCoords1 v_TexCoords0 #define v_TexCoords2 v_TexCoords0 #define v_TexCoords3 v_TexCoords0 #endif //MULTI_COORD uniform float u_opacity; uniform mat4 u_colorMatrix; #ifdef CHANNEL16_TO8 uniform vec2 u_to8; #endif /***User header code***%userHeader%***/ // matrixCompMult for convolution /***User sampling function here***%userSample%***/ #ifndef USER_SAMPLER vec4 sample2d(sampler2D tex, vec2 pos, int plane) { return texture(tex, pos); } #endif // 10, 16bit: http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578%28v=vs.85%29.aspx void main() { gl_FragColor = clamp(u_colorMatrix * vec4( #ifdef CHANNEL16_TO8 #ifdef USE_RG dot(sample2d(u_Texture0, v_TexCoords0, 0).rg, u_to8), dot(sample2d(u_Texture1, v_TexCoords1, 1).rg, u_to8), dot(sample2d(u_Texture2, v_TexCoords2, 2).rg, u_to8), #else dot(sample2d(u_Texture0, v_TexCoords0, 0).ra, u_to8), dot(sample2d(u_Texture1, v_TexCoords1, 1).ra, u_to8), dot(sample2d(u_Texture2, v_TexCoords2, 2).ra, u_to8), #endif //USE_RG #else #ifdef USE_RG sample2d(u_Texture0, v_TexCoords0, 0).r, sample2d(u_Texture1, v_TexCoords1, 1).r, #ifdef IS_BIPLANE sample2d(u_Texture2, v_TexCoords2, 2).g, #else sample2d(u_Texture2, v_TexCoords2, 2).r, #endif //IS_BIPLANE #else // use r, g, a to work for both yv12 and nv12. idea from xbmc sample2d(u_Texture0, v_TexCoords0, 0).r, sample2d(u_Texture1, v_TexCoords1, 1).g, sample2d(u_Texture2, v_TexCoords2, 2).a, #endif //USE_RG #endif //CHANNEL16_TO8 1.0 ) , 0.0, 1.0) * u_opacity; #ifdef HAS_ALPHA float a = #ifdef CHANNEL16_TO8 #ifdef USE_RG dot(sample2d(u_Texture3, v_TexCoords3, 3).rg, u_to8); #else dot(sample2d(u_Texture3, v_TexCoords3, 3).ra, u_to8); #endif #else #ifdef USE_RG sample2d(u_Texture3, v_TexCoords3, 3).r; #else sample2d(u_Texture3, v_TexCoords3, 3).a; #endif #endif gl_FragColor.rgb = gl_FragColor.rgb*a; gl_FragColor.a = a; #endif //HAS_ALPHA /***User post processing here***%userPostProcess%***/ } QtAV-1.12.0/src/shaders/shaders.qrc000066400000000000000000000002461312235004300167610ustar00rootroot00000000000000 planar.f.glsl packed.f.glsl video.vert QtAV-1.12.0/src/shaders/video.vert000066400000000000000000000032171312235004300166320ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ attribute vec4 a_Position; attribute vec2 a_TexCoords0; uniform mat4 u_Matrix; varying vec2 v_TexCoords0; #ifdef MULTI_COORD attribute vec2 a_TexCoords1; attribute vec2 a_TexCoords2; varying vec2 v_TexCoords1; varying vec2 v_TexCoords2; #ifdef HAS_ALPHA attribute vec2 a_TexCoords3; varying vec2 v_TexCoords3; #endif #endif //MULTI_COORD /***User header code***%userHeader%***/ void main() { gl_Position = u_Matrix * a_Position; v_TexCoords0 = a_TexCoords0; #ifdef MULTI_COORD v_TexCoords1 = a_TexCoords1; v_TexCoords2 = a_TexCoords2; #ifdef HAS_ALPHA v_TexCoords3 = a_TexCoords3; #endif #endif //MULTI_COORD } QtAV-1.12.0/src/subtitle/000077500000000000000000000000001312235004300150215ustar00rootroot00000000000000QtAV-1.12.0/src/subtitle/CharsetDetector.cpp000066400000000000000000000045621312235004300206170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "CharsetDetector.h" #ifdef LINK_UCHARDET #include #define HAVE_UCHARDET #else #ifdef BUILD_UCHARDET #include "uchardet.h" #define HAVE_UCHARDET #endif #endif //LINK_UCHARDET #ifndef HAVE_UCHARDET typedef struct uchardet* uchardet_t; #endif class CharsetDetector::Private { public: Private() : m_det(NULL) { #ifdef HAVE_UCHARDET m_det = uchardet_new(); #endif } ~Private() { if (!m_det) return; #ifdef HAVE_UCHARDET uchardet_delete(m_det); #endif m_det = NULL; } QByteArray detect(const QByteArray& data) { #ifdef HAVE_UCHARDET if (!m_det) return QByteArray(); if (uchardet_handle_data(m_det, data.constData(), data.size()) != 0) return QByteArray(); uchardet_data_end(m_det); QByteArray cs(uchardet_get_charset(m_det)); uchardet_reset(m_det); return cs.trimmed(); #else return QByteArray(); #endif } uchardet_t m_det; }; CharsetDetector::CharsetDetector() : priv(new Private()) { } CharsetDetector::~CharsetDetector() { if (priv) { delete priv; priv = 0; } } bool CharsetDetector::isAvailable() const { return !!priv->m_det; } QByteArray CharsetDetector::detect(const QByteArray &data) { return priv->detect(data); } QtAV-1.12.0/src/subtitle/CharsetDetector.h000066400000000000000000000027031312235004300202570ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_UCHARDET_H #define QTAV_UCHARDET_H #include class CharsetDetector { public: CharsetDetector(); ~CharsetDetector(); bool isAvailable() const; /*! * \brief detect * \param data text to parse * \return charset name */ QByteArray detect(const QByteArray& data); private: class Private; Private *priv; }; #endif // QTAV_UCHARDET_H QtAV-1.12.0/src/subtitle/PlainText.cpp000066400000000000000000000114441312235004300174410ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "PlainText.h" #include "QtAV/QtAV_Global.h" #include #include namespace QtAV { namespace PlainText { /* * from mpv/sub/sd_ass.c * ass_to_plaintext() was written by wm4 and he says it can be under LGPL */ struct buf { char *start; int size; int len; }; static void append(struct buf *b, char c) { if (b->len < b->size) { b->start[b->len] = c; b->len++; } } static void ass_to_plaintext(struct buf *b, const char *in) { bool in_tag = false; const char *open_tag_pos = NULL; bool in_drawing = false; while (*in) { if (in_tag) { if (in[0] == '}') { in += 1; in_tag = false; } else if (in[0] == '\\' && in[1] == 'p') { in += 2; // Skip text between \pN and \p0 tags. A \p without a number // is the same as \p0, and leading 0s are also allowed. in_drawing = false; while (in[0] >= '0' && in[0] <= '9') { if (in[0] != '0') in_drawing = true; in += 1; } } else { in += 1; } } else { if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) { in += 2; append(b, '\n'); } else if (in[0] == '\\' && in[1] == 'h') { in += 2; append(b, ' '); } else if (in[0] == '{') { open_tag_pos = in; in += 1; in_tag = true; } else { if (!in_drawing) append(b, in[0]); in += 1; } } } // A '{' without a closing '}' is always visible. if (in_tag) { while (*open_tag_pos) append(b, *open_tag_pos++); } } QString fromAss(const char* ass) { char text[512]; memset(text, 0, sizeof(text)); struct buf b; b.start = text; b.size = sizeof(text) - 1; b.len = 0; ass_to_plaintext(&b, ass); int hour1, min1, sec1, hunsec1,hour2, min2, sec2, hunsec2; char line[512], *ret; // fixme: "\0" maybe not allowed if (sscanf(b.start, "Dialogue: Marked=%*d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]", //¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line) < 9) if (sscanf(b.start, "Dialogue: %*d,%d:%d:%d.%d,%d:%d:%d.%d%[^\r\n]", //¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line) < 9) return QString::fromUtf8(b.start); //libass ASS_Event.Text has no Dialogue ret = strchr(line, ','); if (!ret) return QString::fromUtf8(line); static const char kDefaultStyle[] = "Default,"; for (int comma = 0; comma < 6; comma++) { if (!(ret = strchr(++ret, ','))) { // workaround for ffmpeg decoded srt in ass format: "Dialogue: 0,0:42:29.20,0:42:31.08,Default,Chinese\NEnglish. if (!(ret = strstr(line, kDefaultStyle))) { if (line[0] == ',') //work around for libav-9- return QString::fromUtf8(line+1); return QString::fromUtf8(line); } else { ret += sizeof(kDefaultStyle) - 1 - 1; // tail \0 } } } ret++; int p = strcspn(b.start, "\r\n"); if (p == b.len) //not found return QString::fromUtf8(ret); QString line2 = QString::fromUtf8(b.start + p + 1).trimmed(); if (line2.isEmpty()) return QString::fromUtf8(ret); return QString::fromUtf8(ret) + QStringLiteral("\n") + line2; } } //namespace PlainText } // namespace QtAV QtAV-1.12.0/src/subtitle/PlainText.h000066400000000000000000000024111312235004300171000ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_PLAINTEXT_H #define QTAV_PLAINTEXT_H #include namespace QtAV { namespace PlainText { QString fromAss(const char* ass); } //namespace PlainText } //namespace QtAV #endif // QTAV_PLAINTEXT_H QtAV-1.12.0/src/subtitle/PlayerSubtitle.cpp000066400000000000000000000222201312235004300204730ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/PlayerSubtitle.h" #include #include #include "QtAV/AVPlayer.h" #include "QtAV/Subtitle.h" #include "utils/internal.h" #include "utils/Logger.h" namespace QtAV { // /xx/oo/a.01.mov => /xx/oo/a.01. native dir separator => / /*! * \brief getSubtitleBasePath * \param fullPath path of video or without extension * \return absolute path without extension and file:// */ static QString getSubtitleBasePath(const QString fullPath) { QString path(QDir::fromNativeSeparators(fullPath)); //path.remove(p->source().scheme() + "://"); // QString name = QFileInfo(path).completeBaseName(); // why QFileInfo(path).dir() starts with qml app dir? QString name(path); int lastSep = path.lastIndexOf(QLatin1Char('/')); if (lastSep >= 0) { name = name.mid(lastSep + 1); path = path.left(lastSep + 1); // endsWidth "/" } int lastDot = name.lastIndexOf(QLatin1Char('.')); // not path.lastIndexof("."): xxx.oo/xx if (lastDot > 0) name = name.left(lastDot); if (path.startsWith(QLatin1String("file:"))) // can skip convertion here. Subtitle class also convert the path path = Internal::Path::toLocal(path); path.append(name); return path; } PlayerSubtitle::PlayerSubtitle(QObject *parent) : QObject(parent) , m_auto(true) , m_enabled(true) , m_player(0) , m_sub(new Subtitle(this)) { } Subtitle* PlayerSubtitle::subtitle() { return m_sub; } void PlayerSubtitle::setPlayer(AVPlayer *player) { if (m_player == player) return; if (m_player) { disconnectSignals(); } m_player = player; if (!m_player) return; connectSignals(); } void PlayerSubtitle::setFile(const QString &file) { if (m_file != file) Q_EMIT fileChanged(); // always load // file was set but now playing with fuzzy match. if file is set again to the same value, subtitle must load that file m_file = file; if (!m_enabled) return; m_sub->setFileName(file); m_sub->setFuzzyMatch(false); m_sub->loadAsync(); } QString PlayerSubtitle::file() const { return m_file; } void PlayerSubtitle::setAutoLoad(bool value) { if (m_auto == value) return; m_auto = value; Q_EMIT autoLoadChanged(value); } bool PlayerSubtitle::autoLoad() const { return m_auto; } void PlayerSubtitle::onPlayerSourceChanged() { if (!m_auto) { m_sub->setFileName(QString()); return; } if (!m_enabled) return; AVPlayer *p = qobject_cast(sender()); if (!p) return; m_sub->setFileName(getSubtitleBasePath(p->file())); m_sub->setFuzzyMatch(true); m_sub->loadAsync(); } void PlayerSubtitle::onPlayerPositionChanged() { AVPlayer *p = qobject_cast(sender()); if (!p) return; m_sub->setTimestamp(qreal(p->position())/1000.0); } void PlayerSubtitle::onPlayerStart() { if (!m_enabled) return; // priority: user file > auto load file > embedded if (!m_file.isEmpty()) { if (m_file == m_sub->fileName()) return; m_sub->setFileName(m_file); m_sub->setFuzzyMatch(false); m_sub->loadAsync(); return; } if (autoLoad() && !m_sub->fileName().isEmpty()) return; //already loaded in onPlayerSourceChanged() // try embedded subtitles const int n = m_player->currentSubtitleStream(); if (n < 0 || m_tracks.isEmpty() || m_tracks.size() <= n) { m_sub->processHeader(QByteArray(), QByteArray()); // reset return; } QVariantMap track = m_tracks[n].toMap(); QByteArray codec(track.value(QStringLiteral("codec")).toByteArray()); QByteArray data(track.value(QStringLiteral("extra")).toByteArray()); m_sub->processHeader(codec, data); return; } void PlayerSubtitle::onEnabledChanged(bool value) { m_enabled = value; if (!m_enabled) { disconnectSignals(); return; } connectSignals(); // priority: user file > auto load file > embedded if (!m_file.isEmpty()) { if (m_sub->fileName() == m_file && m_sub->isLoaded()) return; m_sub->setFileName(m_file); m_sub->setFuzzyMatch(false); m_sub->loadAsync(); } if (!m_player) return; if (!autoLoad()) // fallback to internal subtitles return; m_sub->setFileName(getSubtitleBasePath(m_player->file())); m_sub->setFuzzyMatch(true); m_sub->loadAsync(); return; } void PlayerSubtitle::tryReload() { tryReload(3); } void PlayerSubtitle::tryReloadInternalSub() { tryReload(1); } void PlayerSubtitle::tryReload(int flag) { if (!m_enabled) return; if (!m_player->isPlaying()) return; const int kReloadExternal = 1<<1; if (flag & kReloadExternal) { //engine or charset changed m_sub->processHeader(QByteArray(), QByteArray()); // reset m_sub->loadAsync(); return; } // kReloadExternal is not set, kReloadInternal is unknown //fallback to external sub if no valid internal sub track is set const int n = m_player->currentSubtitleStream(); if (n < 0 || m_tracks.isEmpty() || m_tracks.size() <= n) { m_sub->processHeader(QByteArray(), QByteArray()); // reset, null processor m_sub->loadAsync(); return; } // try internal subtitles QVariantMap track = m_tracks[n].toMap(); QByteArray codec(track.value(QStringLiteral("codec")).toByteArray()); QByteArray data(track.value(QStringLiteral("extra")).toByteArray()); m_sub->processHeader(codec, data); Packet pkt(m_current_pkt[n]); if (pkt.isValid()) { processInternalSubtitlePacket(n, pkt); } } void PlayerSubtitle::updateInternalSubtitleTracks(const QVariantList &tracks) { m_tracks = tracks; m_current_pkt.resize(tracks.size()); } void PlayerSubtitle::processInternalSubtitlePacket(int track, const QtAV::Packet &packet) { m_sub->processLine(packet.data, packet.pts, packet.duration); m_current_pkt[track] = packet; } void PlayerSubtitle::processInternalSubtitleHeader(const QByteArray& codec, const QByteArray &data) { m_sub->processHeader(codec, data); } void PlayerSubtitle::connectSignals() { if (!m_player) return; connect(m_player, SIGNAL(sourceChanged()), this, SLOT(onPlayerSourceChanged())); connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(onPlayerPositionChanged())); connect(m_player, SIGNAL(started()), this, SLOT(onPlayerStart())); connect(m_player, SIGNAL(internalSubtitlePacketRead(int,QtAV::Packet)), this, SLOT(processInternalSubtitlePacket(int,QtAV::Packet))); connect(m_player, SIGNAL(internalSubtitleHeaderRead(QByteArray,QByteArray)), this, SLOT(processInternalSubtitleHeader(QByteArray,QByteArray))); connect(m_player, SIGNAL(internalSubtitleTracksChanged(QVariantList)), this, SLOT(updateInternalSubtitleTracks(QVariantList))); // try to reload internal subtitle track. if failed and external subtitle is enabled, fallback to external connect(m_player, SIGNAL(subtitleStreamChanged(int)), this, SLOT(tryReloadInternalSub())); connect(m_sub, SIGNAL(codecChanged()), this, SLOT(tryReload())); connect(m_sub, SIGNAL(enginesChanged()), this, SLOT(tryReload())); } void PlayerSubtitle::disconnectSignals() { if (!m_player) return; disconnect(m_player, SIGNAL(sourceChanged()), this, SLOT(onPlayerSourceChanged())); disconnect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(onPlayerPositionChanged())); disconnect(m_player, SIGNAL(started()), this, SLOT(onPlayerStart())); disconnect(m_player, SIGNAL(internalSubtitlePacketRead(int,QtAV::Packet)), this, SLOT(processInternalSubtitlePacket(int,QtAV::Packet))); disconnect(m_player, SIGNAL(internalSubtitleHeaderRead(QByteArray,QByteArray)), this, SLOT(processInternalSubtitleHeader(QByteArray,QByteArray))); disconnect(m_player, SIGNAL(internalSubtitleTracksChanged(QVariantList)), this, SLOT(updateInternalSubtitleTracks(QVariantList))); disconnect(m_sub, SIGNAL(codecChanged()), this, SLOT(tryReload())); disconnect(m_sub, SIGNAL(enginesChanged()), this, SLOT(tryReload())); } } //namespace QtAV QtAV-1.12.0/src/subtitle/SubImage.cpp000066400000000000000000000107721312235004300172300ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and LibASS Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/SubImage.h" #include namespace QtAV { SubImage::SubImage(int x, int y, int w, int h, int stride) : x(x) , y(y) , w(w) , h(h) , stride(stride) {} SubImageSet::SubImageSet(int width, int height, Format format) : fmt(format) , w(width) , h(height) , id(0) {} #define _r(c) ((c)>>24) #define _g(c) (((c)>>16)&0xFF) #define _b(c) (((c)>>8)&0xFF) #define _a(c) ((c)&0xFF) #define qRgba2(r, g, b, a) ((a << 24) | (r << 16) | (g << 8) | b) /* * ASS_Image: 1bit alpha per pixel + 1 rgb per image. less memory usage */ //0xAARRGGBB #if (Q_BYTE_ORDER == Q_BIG_ENDIAN) #define ARGB32_SET(C, R, G, B, A) \ C[0] = (A); \ C[1] = (R); \ C[2] = (G); \ C[3] = (B); #define ARGB32_ADD(C, R, G, B, A) \ C[0] += (A); \ C[1] += (R); \ C[2] += (G); \ C[3] += (B); #define ARGB32_A(C) (C[0]) #define ARGB32_R(C) (C[1]) #define ARGB32_G(C) (C[2]) #define ARGB32_B(C) (C[3]) #else #define ARGB32_SET(C, R, G, B, A) \ C[0] = (B); \ C[1] = (G); \ C[2] = (R); \ C[3] = (A); #define ARGB32_ADD(C, R, G, B, A) \ C[0] += (B); \ C[1] += (G); \ C[2] += (R); \ C[3] += (A); #define ARGB32_A(C) (C[3]) #define ARGB32_R(C) (C[2]) #define ARGB32_G(C) (C[1]) #define ARGB32_B(C) (C[0]) #endif #define USE_QRGBA 0 // C[i] = C'[i] = (k*c[i]+(255-k)*C[i])/255 = C[i] + k*(c[i]-C[i])/255, min(c[i],C[i]) <= C'[i] <= max(c[i],C[i]) // render 1 ass image into a 32bit QImage with alpha channel. //use dstX, dstY instead of img->dst_x/y because image size is small then ass renderer size void RenderASS(QImage *image, const SubImage& img, int dstX, int dstY) { const quint8 a = 255 - _a(img.color); if (a == 0) return; const quint8 r = _r(img.color); const quint8 g = _g(img.color); const quint8 b = _b(img.color); const quint8 *src = (const quint8*)img.data.constData(); // use QRgb to avoid endian issue QRgb *dst = (QRgb*)image->constBits() + dstY * image->width() + dstX; // k*src+(1-k)*dst for (int y = 0; y < img.h; ++y) { for (int x = 0; x < img.w; ++x) { const unsigned k = ((unsigned) src[x])*a/255; #if USE_QRGBA const unsigned A = qAlpha(dst[x]); #else quint8 *c = (quint8*)(&dst[x]); const unsigned A = ARGB32_A(c); #endif if (A == 0) { // dst color can be ignored #if USE_QRGBA dst[x] = qRgba(r, g, b, k); #else ARGB32_SET(c, r, g, b, k); #endif //USE_QRGBA } else if (k == 0) { //no change //dst[x] = qRgba(qRed(dst[x])), qGreen(dst[x]), qBlue(dst[x]), qAlpha(dst[x])) == dst[x]; } else if (k == 255) { #if USE_QRGBA dst[x] = qRgba(r, g, b, k); #else ARGB32_SET(c, r, g, b, k); #endif //USE_QRGBA } else { // c=k*dc/255=k*dc/256 * (1-1/256), -1width(); } } } //namespace QtAV QtAV-1.12.0/src/subtitle/Subtitle.cpp000066400000000000000000000664361312235004300173370ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/Subtitle.h" #include "QtAV/private/SubtitleProcessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "subtitle/CharsetDetector.h" #include "utils/Logger.h" namespace QtAV { const int kMaxSubtitleSize = 10 * 1024 * 1024; // TODO: remove because we find the matched extenstions class Subtitle::Private { public: Private() : loaded(false) , fuzzy_match(true) , update_text(true) , update_image(true) , last_can_render(false) , processor(0) , codec("AutoDetect") , t(0) , delay(0) , current_count(0) , force_font_file(false) {} void reset() { QMutexLocker lock(&mutex); Q_UNUSED(lock); loaded = false; processor = 0; update_text = true; update_image = true; t = 0; frame = SubtitleFrame(); frames.clear(); itf = frames.begin(); current_count = 0; } // width/height == 0: do not create image // return true if both frame time and content(currently is text) changed bool prepareCurrentFrame(); QStringList find(); /*! * \brief readFromFile * read subtilte content from path * \param path * \return utf8 encoded content */ QByteArray readFromFile(const QString& path); /*! * \brief processRawData * process utf8 encoded subtitle content. maybe a temp file is created if subtitle processor does not * support raw data * \param data utf8 subtitle content */ bool processRawData(const QByteArray& data); bool processRawData(SubtitleProcessor* sp, const QByteArray& data); bool loaded; bool fuzzy_match; bool update_text; bool update_image; //TODO: detect image change from engine bool last_can_render; SubtitleProcessor *processor; QList processors; QByteArray codec; QStringList engine_names; QLinkedList frames; QUrl url; QByteArray raw_data; QString file_name; QStringList dirs; QStringList suffixes; QStringList supported_suffixes; QIODevice *dev; // last time image qreal t; qreal delay; SubtitleFrame frame; QString current_text; QImage current_image; SubImageSet current_ass; QLinkedList::iterator itf; /* number of subtitle frames at current time. * <0 means itf is the last. >0 means itf is the 1st */ int current_count; QMutex mutex; bool force_font_file; QString font_file; QString fonts_dir; }; Subtitle::Subtitle(QObject *parent) : QObject(parent) , priv(new Private()) { // TODO: use factory.registedNames() and the order setEngines(QStringList() << QStringLiteral("LibASS") << QStringLiteral("FFmpeg")); } Subtitle::~Subtitle() { if (priv) { delete priv; priv = 0; } } bool Subtitle::isLoaded() const { return priv->loaded; } void Subtitle::setCodec(const QByteArray &value) { if (priv->codec == value) return; priv->codec = value; Q_EMIT codecChanged(); } QByteArray Subtitle::codec() const { return priv->codec; } void Subtitle::setEngines(const QStringList &value) { if (priv->engine_names == value) return; // can not reset processor here, not thread safe. priv->supported_suffixes.clear(); priv->engine_names = value; if (priv->engine_names.isEmpty()) { Q_EMIT enginesChanged(); Q_EMIT supportedSuffixesChanged(); return; } QList sps; foreach (const QString& e, priv->engine_names) { qDebug() << "engine:" << e; QList::iterator it = priv->processors.begin(); while (it != priv->processors.end()) { if (!(*it)) { it = priv->processors.erase(it); continue; } if ((*it)->name() != e) { ++it; continue; } sps.append(*it); it = priv->processors.erase(it); break; } if (it == priv->processors.end()) { SubtitleProcessor* sp = SubtitleProcessor::create(e.toLatin1().constData()); if (sp) sps.append(sp); } } // release the processors not wanted qDeleteAll(priv->processors); priv->processors = sps; if (sps.isEmpty()) { Q_EMIT enginesChanged(); Q_EMIT supportedSuffixesChanged(); return; } foreach (SubtitleProcessor* sp, sps) { priv->supported_suffixes.append(sp->supportedTypes()); } priv->supported_suffixes.removeDuplicates(); // DO NOT set priv->suffixes Q_EMIT enginesChanged(); Q_EMIT supportedSuffixesChanged(); // it's safe to reload } QStringList Subtitle::engines() const { return priv->engine_names; } QString Subtitle::engine() const { if (!priv->processor) return QString(); return priv->processor->name(); } void Subtitle::setFuzzyMatch(bool value) { if (priv->fuzzy_match == value) return; priv->fuzzy_match = value; Q_EMIT fuzzyMatchChanged(); } bool Subtitle::fuzzyMatch() const { return priv->fuzzy_match; } void Subtitle::setRawData(const QByteArray &data) { // compare the whole content is not a good idea if (priv->raw_data.size() == data.size()) return; priv->raw_data = data; Q_EMIT rawDataChanged(); priv->url.clear(); priv->file_name.clear(); } QByteArray Subtitle::rawData() const { return priv->raw_data; } extern QString getLocalPath(const QString& fullPath); void Subtitle::setFileName(const QString &name) { if (priv->file_name == name) return; priv->url.clear(); priv->raw_data.clear(); priv->file_name = name; if (priv->file_name.startsWith(QLatin1String("file:"))) priv->file_name = getLocalPath(priv->file_name); Q_EMIT fileNameChanged(); } QString Subtitle::fileName() const { return priv->file_name; } void Subtitle::setDirs(const QStringList &value) { if (priv->dirs == value) return; priv->dirs = value; Q_EMIT dirsChanged(); } QStringList Subtitle::dirs() const { return priv->dirs; } QStringList Subtitle::supportedSuffixes() const { return priv->supported_suffixes; } void Subtitle::setSuffixes(const QStringList &value) { if (priv->suffixes == value) return; priv->suffixes = value; Q_EMIT suffixesChanged(); } QStringList Subtitle::suffixes() const { //if (priv->suffixes.isEmpty()) // return supportedSuffixes(); return priv->suffixes; } void Subtitle::setTimestamp(qreal t) { // TODO: detect image change? { QMutexLocker lock(&priv->mutex); Q_UNUSED(lock); priv->t = t; if (!isLoaded()) return; if (!priv->prepareCurrentFrame()) return; priv->update_text = true; priv->update_image = true; } Q_EMIT contentChanged(); } qreal Subtitle::timestamp() const { return priv->t; } void Subtitle::setDelay(qreal value) { if (priv->delay == value) return; priv->delay = value; Q_EMIT delayChanged(); } qreal Subtitle::delay() const { return priv->delay; } QString Subtitle::fontFile() const { return priv->font_file; } void Subtitle::setFontFile(const QString &value) { if (priv->font_file == value) return; priv->font_file = value; Q_EMIT fontFileChanged(); if (priv->processor) { priv->processor->setFontFile(value); } } QString Subtitle::fontsDir() const { return priv->fonts_dir; } void Subtitle::setFontsDir(const QString &value) { if (priv->fonts_dir == value) return; priv->fonts_dir = value; Q_EMIT fontsDirChanged(); if (priv->processor) { priv->processor->setFontsDir(value); } } bool Subtitle::isFontFileForced() const { return priv->force_font_file; } void Subtitle::setFontFileForced(bool value) { if (priv->force_font_file == value) return; priv->force_font_file = value; Q_EMIT fontFileForcedChanged(); if (priv->processor) { priv->processor->setFontFileForced(value); } } void Subtitle::load() { SubtitleProcessor *old_processor = priv->processor; priv->reset(); Q_EMIT contentChanged(); //notify user to update subtitle // lock is not needed because it's not loaded now if (!priv->url.isEmpty()) { // need qt network module network if (old_processor != priv->processor) Q_EMIT engineChanged(); return; } // raw data is set, file name and url are empty QByteArray u8 = priv->raw_data; if (!u8.isEmpty()) { priv->loaded = priv->processRawData(u8); if (priv->loaded) Q_EMIT loaded(); checkCapability(); if (old_processor != priv->processor) Q_EMIT engineChanged(); return; } // read from a url QFile f(QUrl::fromPercentEncoding(priv->url.toEncoded())); if (f.exists()) { u8 = priv->readFromFile(f.fileName()); if (u8.isEmpty()) return; priv->loaded = priv->processRawData(u8); if (priv->loaded) Q_EMIT loaded(QUrl::fromPercentEncoding(priv->url.toEncoded())); checkCapability(); if (old_processor != priv->processor) Q_EMIT engineChanged(); return; } // read from a file QStringList paths = priv->find(); if (paths.isEmpty()) { checkCapability(); if (old_processor != priv->processor) Q_EMIT engineChanged(); return; } foreach (const QString& path, paths) { if (path.isEmpty()) continue; u8 = priv->readFromFile(path); if (u8.isEmpty()) continue; if (!priv->processRawData(u8)) continue; priv->loaded = true; Q_EMIT loaded(path); break; } checkCapability(); if (old_processor != priv->processor) Q_EMIT engineChanged(); if (priv->processor) { priv->processor->setFontFile(priv->font_file); priv->processor->setFontsDir(priv->fonts_dir); priv->processor->setFontFileForced(priv->force_font_file); } } void Subtitle::checkCapability() { if (priv->last_can_render == canRender()) return; priv->last_can_render = canRender(); Q_EMIT canRenderChanged(); } void Subtitle::loadAsync() { if (fileName().isEmpty()) return; class Loader : public QRunnable { public: Loader(Subtitle *sub) : m_sub(sub) {} void run() { if (m_sub) m_sub->load(); } private: Subtitle *m_sub; }; QThreadPool::globalInstance()->start(new Loader(this)); } bool Subtitle::canRender() const { return priv->processor && priv->processor->canRender(); } QString Subtitle::getText() const { QMutexLocker lock(&priv->mutex); Q_UNUSED(lock); if (!isLoaded()) return QString(); if (!priv->current_count) return QString(); if (!priv->update_text) return priv->current_text; priv->update_text = false; priv->current_text.clear(); const int count = qAbs(priv->current_count); QLinkedList::iterator it = priv->current_count > 0 ? priv->itf : priv->itf + (priv->current_count+1); for (int i = 0; i < count; ++i) { priv->current_text.append(it->text).append(QStringLiteral("\n")); ++it; } priv->current_text = priv->current_text.trimmed(); return priv->current_text; } QImage Subtitle::getImage(int width, int height, QRect* boundingRect) { QMutexLocker lock(&priv->mutex); Q_UNUSED(lock); if (!isLoaded()) return QImage(); if (width == 0 || height == 0) return QImage(); #if 0 if (!priv->current_count) //seems ok to use this code return QImage(); // always render the image to support animations if (!priv->update_image && width == priv->current_image.width() && height == priv->current_image.height()) return priv->current_image; #endif priv->update_image = false; if (!canRender()) return QImage(); priv->processor->setFrameSize(width, height); // TODO: store bounding rect here and not in processor priv->current_image = priv->processor->getImage(priv->t - priv->delay, boundingRect); return priv->current_image; } SubImageSet Subtitle::getSubImages(int width, int height, QRect *boundingRect) { QMutexLocker lock(&priv->mutex); Q_UNUSED(lock); if (!isLoaded()) return SubImageSet(); if (width == 0 || height == 0) return SubImageSet(); priv->update_image = false; if (!canRender()) return SubImageSet(); priv->processor->setFrameSize(width, height); // TODO: store bounding rect here and not in processor priv->current_ass = priv->processor->getSubImages(priv->t - priv->delay, boundingRect); return priv->current_ass; } bool Subtitle::processHeader(const QByteArray& codec, const QByteArray &data) { qDebug() << "codec: " << codec; qDebug() << "header: " << data; SubtitleProcessor *old_processor = priv->processor; priv->reset(); // reset for the new subtitle stream (internal) if (priv->processors.isEmpty()) return false; foreach (SubtitleProcessor *sp, priv->processors) { if (sp->supportedTypes().contains(QLatin1String(codec))) { priv->processor = sp; qDebug() << "current subtitle processor: " << sp->name(); break; } } if (old_processor != priv->processor) Q_EMIT engineChanged(); if (!priv->processor) { qWarning("No subtitle processor supports the codec '%s'", codec.constData()); return false; } if (!priv->processor->processHeader(codec, data)) return false; priv->loaded = true; priv->processor->setFontFile(priv->font_file); priv->processor->setFontsDir(priv->fonts_dir); priv->processor->setFontFileForced(priv->force_font_file); return true; } bool Subtitle::processLine(const QByteArray &data, qreal pts, qreal duration) { if (!priv->processor) return false; SubtitleFrame f = priv->processor->processLine(data, pts, duration); if (!f.isValid()) return false; // TODO: if seek to previous position, an invalid frame is returned. if (priv->frames.isEmpty() || priv->frames.last() < f) { priv->frames.append(f); priv->itf = priv->frames.begin(); return true; } // usually add to the end. TODO: test QLinkedList::iterator it = priv->frames.end(); if (it != priv->frames.begin()) --it; while (it != priv->frames.begin() && f < (*it)) {--it;} if (it != priv->frames.begin()) // found in middle, insert before next ++it; priv->frames.insert(it, f); priv->itf = it; return true; } // DO NOT set frame's image to reduce memory usage // assume frame.text is already set // check previous text if now no subtitle bool Subtitle::Private::prepareCurrentFrame() { if (frames.isEmpty()) return false; QLinkedList::iterator it = itf; int found = 0; const int old_current_count = current_count; const qreal t = this->t - delay; if (t < it->begin) { while (it != frames.begin()) { --it; if (t > it->end) { if (found > 0) break; // no subtitle at that time. check previous text if (old_current_count) { current_count = 0; return true; } return false; } if (t >= (*it).begin) { if (found == 0) itf = it; found++; } } current_count = -found; if (found > 0) { frame = *it; return true; } // no subtitle at that time. it == begin(). check previous text if (old_current_count) return true; return false; } bool it_changed = false; while (it != frames.end()) { if (t > it->end) { ++it; it_changed = true; continue; } if (t < it->begin) { if (found > 0) break; // no subtitle at that time. check previous text if (old_current_count) { current_count = 0; return true; } return false; } if (found == 0) itf = it; ++found; ++it; } current_count = found; if (found > 0) return it_changed || current_count != old_current_count; // no subtitle at that time, it == end(). check previous text if (old_current_count) return true; return false; } QStringList Subtitle::Private::find() { if (file_name.isEmpty()) return QStringList(); // !fuzzyMatch: return the file if (!fuzzy_match) { return QStringList() << file_name; } // found files will be sorted by extensions in sfx order QStringList sfx(suffixes); if (sfx.isEmpty()) sfx = supported_suffixes; if (sfx.isEmpty()) return QStringList() << file_name; QFileInfo fi(file_name); QString name = fi.fileName(); QString base_name = fi.completeBaseName(); // a.mp4=>a, video suffix has only 1 dot QStringList filters, filters_base; foreach (const QString& suf, sfx) { filters.append(QStringLiteral("%1*.%2").arg(name).arg(suf)); if (name != base_name) filters_base.append(QStringLiteral("%1*.%2").arg(base_name).arg(suf)); } QStringList search_dirs(dirs); search_dirs.prepend(fi.absolutePath()); QFileInfoList list; foreach (const QString& d, search_dirs) { QDir dir(d); //qDebug() << "dir: " << dir; QFileInfoList fis = dir.entryInfoList(filters, QDir::Files, QDir::Unsorted); if (fis.isEmpty()) { if (filters_base.isEmpty()) continue; fis = dir.entryInfoList(filters_base, QDir::Files, QDir::Unsorted); } if (fis.isEmpty()) continue; list.append(fis); } if (list.isEmpty()) return QStringList(); // TODO: sort. get entryList from nameFilters 1 by 1 is slower? QStringList sorted; // sfx is not empty, sort to the given order (sfx's order) foreach (const QString& suf, sfx) { if (list.isEmpty()) break; QRegExp rx(QStringLiteral("*.") + suf); rx.setPatternSyntax(QRegExp::Wildcard); QFileInfoList::iterator it = list.begin(); while (it != list.end()) { if (!it->fileName().startsWith(name) && !it->fileName().startsWith(base_name)) {// why it happens? it = list.erase(it); continue; } if (!rx.exactMatch(it->fileName())) { ++it; continue; } sorted.append(it->absoluteFilePath()); it = list.erase(it); } } // the given name is at the highest priority. if (QFile(file_name).exists()) { sorted.removeAll(file_name); sorted.prepend(file_name); } qDebug() << "subtitles found: " << sorted; return sorted; } QByteArray Subtitle::Private::readFromFile(const QString &path) { qDebug() << "read subtitle from: " << path; QFile f(path); if (f.size() > kMaxSubtitleSize) return QByteArray(); if (!f.open(QIODevice::ReadOnly)) { qDebug() << "Failed to open subtitle [" << path << "]: " << f.errorString(); return QByteArray(); } QTextStream ts(&f); ts.setAutoDetectUnicode(true); if (!codec.isEmpty()) { if (codec.toLower() == "system") { ts.setCodec(QTextCodec::codecForLocale()); } else if (codec.toLower() == "autodetect") { CharsetDetector det; if (det.isAvailable()) { QByteArray charset = det.detect(f.readAll()); qDebug("charset>>>>>>>>: %s", charset.constData()); f.seek(0); if (!charset.isEmpty()) ts.setCodec(QTextCodec::codecForName(charset)); } } else { ts.setCodec(QTextCodec::codecForName(codec)); } } return ts.readAll().toUtf8(); } bool Subtitle::Private::processRawData(const QByteArray &data) { processor = 0; frames.clear(); if (data.size() > kMaxSubtitleSize) return false; foreach (SubtitleProcessor* sp, processors) { if (processRawData(sp, data)) { processor = sp; break; } } if (!processor) return false; QList fs(processor->frames()); if (fs.isEmpty()) return false; std::sort(fs.begin(), fs.end()); foreach (const SubtitleFrame& f, fs) { frames.push_back(f); } itf = frames.begin(); frame = *itf; return true; } bool Subtitle::Private::processRawData(SubtitleProcessor *sp, const QByteArray &data) { qDebug("processing subtitle from raw data..."); QByteArray u8(data); QBuffer buf(&u8); if (buf.open(QIODevice::ReadOnly)) { const bool ok = sp->process(&buf); if (buf.isOpen()) buf.close(); if (ok) return true; } else { qWarning() << "open subtitle qbuffer error: " << buf.errorString(); } qDebug("processing subtitle from a tmp utf8 file..."); QString name = QUrl::fromPercentEncoding(url.toEncoded()).section(ushort('/'), -1); if (name.isEmpty()) name = QFileInfo(file_name).fileName(); //priv->name.section('/', -1); // if no seperator? if (name.isEmpty()) name = QStringLiteral("QtAV_u8_sub_cache"); name.append(QStringLiteral("_%1").arg((quintptr)this)); QFile w(QDir::temp().absoluteFilePath(name)); if (w.open(QIODevice::WriteOnly)) { w.write(data); w.close(); } else { if (!w.exists()) return false; } return sp->process(w.fileName()); } SubtitleAPIProxy::SubtitleAPIProxy(QObject* obj) : m_obj(obj) , m_s(0) {} void SubtitleAPIProxy::setSubtitle(Subtitle *sub) { m_s = sub; QObject::connect(m_s, SIGNAL(canRenderChanged()), m_obj, SIGNAL(canRenderChanged())); QObject::connect(m_s, SIGNAL(contentChanged()), m_obj, SIGNAL(contentChanged())); QObject::connect(m_s, SIGNAL(loaded(QString)), m_obj, SIGNAL(loaded(QString))); QObject::connect(m_s, SIGNAL(codecChanged()), m_obj, SIGNAL(codecChanged())); QObject::connect(m_s, SIGNAL(enginesChanged()), m_obj, SIGNAL(enginesChanged())); QObject::connect(m_s, SIGNAL(engineChanged()), m_obj, SIGNAL(engineChanged())); // QObject::connect(m_s, SIGNAL(fileNameChanged()), m_obj, SIGNAL(fileNameChanged())); QObject::connect(m_s, SIGNAL(dirsChanged()), m_obj, SIGNAL(dirsChanged())); QObject::connect(m_s, SIGNAL(fuzzyMatchChanged()), m_obj, SIGNAL(fuzzyMatchChanged())); QObject::connect(m_s, SIGNAL(suffixesChanged()), m_obj, SIGNAL(suffixesChanged())); QObject::connect(m_s, SIGNAL(supportedSuffixesChanged()), m_obj, SIGNAL(supportedSuffixesChanged())); QObject::connect(m_s, SIGNAL(delayChanged()), m_obj, SIGNAL(delayChanged())); QObject::connect(m_s, SIGNAL(fontFileChanged()), m_obj, SIGNAL(fontFileChanged())); QObject::connect(m_s, SIGNAL(fontsDirChanged()), m_obj, SIGNAL(fontsDirChanged())); QObject::connect(m_s, SIGNAL(fontFileForcedChanged()), m_obj, SIGNAL(fontFileForcedChanged())); } void SubtitleAPIProxy::setCodec(const QByteArray& value) { if (!m_s) return; m_s->setCodec(value); } QByteArray SubtitleAPIProxy::codec() const { if (!m_s) return QByteArray(); return m_s->codec(); } bool SubtitleAPIProxy::isLoaded() const { return m_s && m_s->isLoaded(); } void SubtitleAPIProxy::setEngines(const QStringList& value) { if (!m_s) return; m_s->setEngines(value); } QStringList SubtitleAPIProxy::engines() const { if (!m_s) return QStringList(); return m_s->engines(); } QString SubtitleAPIProxy::engine() const { if (!m_s) return QString(); return m_s->engine(); } void SubtitleAPIProxy::setFuzzyMatch(bool value) { if (!m_s) return; m_s->setFuzzyMatch(value); } bool SubtitleAPIProxy::fuzzyMatch() const { return m_s && m_s->fuzzyMatch(); } #if 0 void SubtitleAPIProxy::setFileName(const QString& name) { if (!m_s) return; m_s->setFileName(name); } QString SubtitleAPIProxy::fileName() const { if (!m_s) return QString(); return m_s->fileName(); } #endif void SubtitleAPIProxy::setDirs(const QStringList &value) { if (!m_s) return; m_s->setDirs(value); } QStringList SubtitleAPIProxy::dirs() const { if (!m_s) return QStringList(); return m_s->dirs(); } QStringList SubtitleAPIProxy::supportedSuffixes() const { if (!m_s) return QStringList(); return m_s->supportedSuffixes(); } void SubtitleAPIProxy::setSuffixes(const QStringList& value) { if (!m_s) return; m_s->setSuffixes(value); } QStringList SubtitleAPIProxy::suffixes() const { if (!m_s) return QStringList(); return m_s->suffixes(); } bool SubtitleAPIProxy::canRender() const { return m_s && m_s->canRender(); } void SubtitleAPIProxy::setDelay(qreal value) { if (!m_s) return; m_s->setDelay(value); } qreal SubtitleAPIProxy::delay() const { return m_s ? m_s->delay() : 0; } QString SubtitleAPIProxy::fontFile() const { return m_s ? m_s->fontFile() : QString(); } void SubtitleAPIProxy::setFontFile(const QString &value) { if (!m_s) return; m_s->setFontFile(value); } QString SubtitleAPIProxy::fontsDir() const { return m_s ? m_s->fontsDir() : QString(); } void SubtitleAPIProxy::setFontsDir(const QString &value) { if (!m_s) return; m_s->setFontsDir(value); } bool SubtitleAPIProxy::isFontFileForced() const { return m_s && m_s->isFontFileForced(); } void SubtitleAPIProxy::setFontFileForced(bool value) { if (!m_s) return; m_s->setFontFileForced(value); } } //namespace QtAV QtAV-1.12.0/src/subtitle/SubtitleProcessor.cpp000066400000000000000000000054211312235004300212220ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/SubtitleProcessor.h" #include "QtAV/private/factory.h" #include #include "utils/Logger.h" namespace QtAV { FACTORY_DEFINE(SubtitleProcessor) // can not declare in class member extern bool RegisterSubtitleProcessorFFmpeg_Man(); extern bool RegisterSubtitleProcessorLibASS_Man(); void SubtitleProcessor::registerAll() { static bool done = false; if (done) return; done = true; RegisterSubtitleProcessorFFmpeg_Man(); #if QTAV_HAVE(LIBASS) RegisterSubtitleProcessorLibASS_Man(); #endif } SubtitleProcessor::SubtitleProcessor() : m_width(0) , m_height(0) { } bool SubtitleProcessor::process(const QString &path) { QFile f(path); if (!f.open(QIODevice::ReadOnly)) { qWarning() << "open subtitle file error: " << f.errorString(); return false; } bool ok = process(&f); f.close(); return ok; } QImage SubtitleProcessor::getImage(qreal pts, QRect *boundingRect) { Q_UNUSED(pts) Q_UNUSED(boundingRect) return QImage(); } SubImageSet SubtitleProcessor::getSubImages(qreal pts, QRect *boundingRect) { Q_UNUSED(pts); Q_UNUSED(boundingRect); return SubImageSet(); } void SubtitleProcessor::setFrameSize(int width, int height) { if (width == m_width && height == m_height) return; m_width = width; m_height = height; onFrameSizeChanged(m_width, m_height); } QSize SubtitleProcessor::frameSize() const { return QSize(m_width, m_height); } int SubtitleProcessor::frameWidth() const { return m_width; } int SubtitleProcessor::frameHeight() const { return m_height; } void SubtitleProcessor::onFrameSizeChanged(int width, int height) { Q_UNUSED(width); Q_UNUSED(height); } } //namespace QtAV QtAV-1.12.0/src/subtitle/SubtitleProcessorFFmpeg.cpp000066400000000000000000000325141312235004300223120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/SubtitleProcessor.h" #include "QtAV/private/factory.h" #include "QtAV/AVDemuxer.h" #include "QtAV/Packet.h" #include "QtAV/private/AVCompat.h" #include "PlainText.h" #include "utils/Logger.h" namespace QtAV { class SubtitleProcessorFFmpeg Q_DECL_FINAL: public SubtitleProcessor { public: SubtitleProcessorFFmpeg(); ~SubtitleProcessorFFmpeg(); SubtitleProcessorId id() const Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; QStringList supportedTypes() const Q_DECL_OVERRIDE; bool process(QIODevice* dev) Q_DECL_OVERRIDE; // supportsFromFile must be true bool process(const QString& path) Q_DECL_OVERRIDE; QList frames() const Q_DECL_OVERRIDE; bool processHeader(const QByteArray& codec, const QByteArray& data) Q_DECL_OVERRIDE; SubtitleFrame processLine(const QByteArray& data, qreal pts = -1, qreal duration = 0) Q_DECL_OVERRIDE; QString getText(qreal pts) const Q_DECL_OVERRIDE; private: bool processSubtitle(); AVCodecContext *codec_ctx; AVDemuxer m_reader; QList m_frames; }; static const SubtitleProcessorId SubtitleProcessorId_FFmpeg = QStringLiteral("qtav.subtitle.processor.ffmpeg"); namespace { static const char kName[] = "FFmpeg"; } FACTORY_REGISTER(SubtitleProcessor, FFmpeg, kName) SubtitleProcessorFFmpeg::SubtitleProcessorFFmpeg() : codec_ctx(0) { } SubtitleProcessorFFmpeg::~SubtitleProcessorFFmpeg() { avcodec_free_context(&codec_ctx); } SubtitleProcessorId SubtitleProcessorFFmpeg::id() const { return SubtitleProcessorId_FFmpeg; } QString SubtitleProcessorFFmpeg::name() const { return QLatin1String(kName);//SubtitleProcessorFactory::name(id()); } QStringList ffmpeg_supported_sub_extensions_by_codec() { QStringList exts; AVCodec *c = av_codec_next(NULL); while (c) { if (c->type != AVMEDIA_TYPE_SUBTITLE) { c = av_codec_next(c); continue; } qDebug("sub codec: %s", c->name); AVInputFormat *i = av_iformat_next(NULL); while (i) { if (!strcmp(i->name, c->name)) { qDebug("found iformat"); if (i->extensions) { exts.append(QString::fromLatin1(i->extensions).split(QLatin1Char(','))); } else { qDebug("has no exts"); exts.append(QString::fromLatin1(i->name)); } break; } i = av_iformat_next(i); } if (!i) { //qDebug("codec name '%s' is not found in AVInputFormat, just append codec name", c->name); //exts.append(c->name); } c = av_codec_next(c); } return exts; } QStringList ffmpeg_supported_sub_extensions() { QStringList exts; AVInputFormat *i = NULL; while ((i = av_iformat_next(i))) { // strstr parameters can not be null if (i->long_name && strstr(i->long_name, "subtitle")) { if (i->extensions) { exts.append(QString::fromLatin1(i->extensions).split(QLatin1Char(','))); } else { exts.append(QString::fromLatin1(i->name)); } } } // AVCodecDescriptor.name and AVCodec.name may be different. avcodec_get_name() use AVCodecDescriptor if possible QStringList codecs; const AVCodec* c = NULL; while ((c = av_codec_next(c))) { if (c->type == AVMEDIA_TYPE_SUBTITLE) codecs.append(QString::fromLatin1(c->name)); } const AVCodecDescriptor *desc = NULL; while ((desc = avcodec_descriptor_next(desc))) { if (desc->type == AVMEDIA_TYPE_SUBTITLE) codecs.append(QString::fromLatin1(desc->name)); } exts << codecs; exts.removeDuplicates(); return exts; } QStringList SubtitleProcessorFFmpeg::supportedTypes() const { // TODO: mp4. check avformat classes #if 0 typedef struct { const char* ext; const char* name; } sub_ext_t; static const sub_ext_t sub_ext[] = { { "ass", "ass" }, { "ssa", "ass" }, { "sub", "subviewer" }, { "" } // from ffmpeg/tests/fate/subtitles.mak static const QStringList sSuffixes = QStringList() << "ass" << "ssa" << "sub" << "srt" << "txt" << "vtt" << "smi" << "pjs" << "jss" << "aqt"; #endif static const QStringList sSuffixes = ffmpeg_supported_sub_extensions(); return sSuffixes; } bool SubtitleProcessorFFmpeg::process(QIODevice *dev) { if (!dev->isOpen()) { if (!dev->open(QIODevice::ReadOnly)) { qWarning() << "open qiodevice error: " << dev->errorString(); return false; } } m_reader.setMedia(dev); if (!m_reader.load()) goto error; if (m_reader.subtitleStreams().isEmpty()) goto error; qDebug("subtitle format: %s", m_reader.formatContext()->iformat->name); if (!processSubtitle()) goto error; m_reader.unload(); return true; error: m_reader.unload(); return false; } bool SubtitleProcessorFFmpeg::process(const QString &path) { m_reader.setMedia(path); if (!m_reader.load()) goto error; if (m_reader.subtitleStreams().isEmpty()) goto error; qDebug("subtitle format: %s", m_reader.formatContext()->iformat->name); if (!processSubtitle()) goto error; m_reader.unload(); return true; error: m_reader.unload(); return false; } QList SubtitleProcessorFFmpeg::frames() const { return m_frames; } QString SubtitleProcessorFFmpeg::getText(qreal pts) const { QString text; for (int i = 0; i < m_frames.size(); ++i) { if (m_frames[i].begin <= pts && m_frames[i].end >= pts) { text += m_frames[i].text + QStringLiteral("\n"); continue; } if (!text.isEmpty()) break; } return text.trimmed(); } bool SubtitleProcessorFFmpeg::processHeader(const QByteArray &codec, const QByteArray &data) { Q_UNUSED(data); if (codec_ctx) { avcodec_free_context(&codec_ctx); } AVCodec *c = avcodec_find_decoder_by_name(codec.constData()); if (!c) { qDebug("subtitle avcodec_descriptor_get_by_name %s", codec.constData()); const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(codec.constData()); if (!desc) { qWarning("No codec descriptor found for %s", codec.constData()); return false; } c = avcodec_find_decoder(desc->id); } if (!c) { qWarning("No subtitle decoder found for codec: %s, try fron descriptor", codec.constData()); return false; } codec_ctx = avcodec_alloc_context3(c); if (!codec_ctx) return false; // no way to get time base. the pts unit used in processLine() is 's', ffmpeg use ms, so set 1/1000 here codec_ctx->time_base.num = 1; codec_ctx->time_base.den = 1000; if (!data.isEmpty()) { av_free(codec_ctx->extradata); codec_ctx->extradata = (uint8_t*)av_mallocz(data.size() + FF_INPUT_BUFFER_PADDING_SIZE); if (!codec_ctx->extradata) return false; codec_ctx->extradata_size = data.size(); memcpy(codec_ctx->extradata, data.constData(), data.size()); } if (avcodec_open2(codec_ctx, c, NULL) < 0) { avcodec_free_context(&codec_ctx); return false; } return true;//codec != QByteArrayLiteral("ass") && codec != QByteArrayLiteral("ssa"); } SubtitleFrame SubtitleProcessorFFmpeg::processLine(const QByteArray &data, qreal pts, qreal duration) { //qDebug() << "line: " << data; if (!codec_ctx) { return SubtitleFrame(); } // AV_CODEC_ID_xxx and srt, subrip are available for ffmpeg >= 1.0. AV_CODEC_ID_xxx // TODO: what about other formats? // libav-9: packet data from demuxer contains time and but duration is 0, must decode // Always decode the data because it may contain styles if (false && duration > 0 && (!codec_ctx #if QTAV_USE_FFMPEG(LIBAVCODEC) || codec_ctx->codec_id == AV_CODEC_ID_SUBRIP #endif || codec_ctx->codec_id == AV_CODEC_ID_SRT )) { SubtitleFrame f; f.begin = pts; f.end = pts + duration; if (data.startsWith("Dialogue:")) // e.g. decoding embedded subtitles f.text = PlainText::fromAss(data.constData()); else f.text = QString::fromUtf8(data.constData(), data.size()); //utf-8 is required return f; } AVPacket packet; av_init_packet(&packet); packet.size = data.size(); packet.data = (uint8_t*)data.constData(); /* * ffmpeg <2.5: AVPakcet.data has original text including time info, decoder use that info to get correct time * "Dialogue: 0,0:00:20.21,0:00:22.96,*Default,NTP,0000,0000,0000,blablabla * ffmpeg >=2.5: AVPakcet.data changed, we have to set correct pts & duration for packet to be decoded * 16,0,*Default,NTP,0000,0000,0000,,blablabla */ // no codec_ctx for internal sub const double unit = 1.0/av_q2d(codec_ctx->time_base); //time_base is deprecated, use framerate since 17085a0, check FF_API_AVCTX_TIMEBASE packet.pts = pts * unit; packet.duration = duration * unit; AVSubtitle sub; memset(&sub, 0, sizeof(sub)); int got_subtitle = 0; int ret = avcodec_decode_subtitle2(codec_ctx, &sub, &got_subtitle, &packet); if (ret < 0 || !got_subtitle) { av_packet_unref(&packet); avsubtitle_free(&sub); return SubtitleFrame(); } SubtitleFrame frame; // start_display_time and duration are in ms frame.begin = pts + qreal(sub.start_display_time)/1000.0; frame.end = pts + qreal(sub.end_display_time)/1000.0; //qDebug() << QTime(0, 0, 0).addMSecs(frame.begin*1000.0) << "-" << QTime(0, 0, 0).addMSecs(frame.end*1000.0) << " fmt: " << sub.format << " pts: " << m_reader.packet().pts //sub.pts // << " rects: " << sub.num_rects << " end: " << sub.end_display_time; for (unsigned i = 0; i < sub.num_rects; i++) { switch (sub.rects[i]->type) { case SUBTITLE_ASS: //qDebug("ass frame: %s", sub.rects[i]->ass); frame.text.append(PlainText::fromAss(sub.rects[i]->ass)).append(ushort('\n')); break; case SUBTITLE_TEXT: //qDebug("txt frame: %s", sub.rects[i]->text); frame.text.append(QString::fromUtf8(sub.rects[i]->text)).append(ushort('\n')); break; case SUBTITLE_BITMAP: //sub.rects[i]->w > 0 && sub.rects[i]->h > 0 //qDebug("bmp sub"); frame = SubtitleFrame(); // not support bmp subtitle now break; default: break; } } av_packet_unref(&packet); avsubtitle_free(&sub); return frame; } bool SubtitleProcessorFFmpeg::processSubtitle() { m_frames.clear(); int ss = m_reader.subtitleStream(); if (ss < 0) { qWarning("no subtitle stream found"); return false; } codec_ctx = m_reader.subtitleCodecContext(); AVCodec *dec = avcodec_find_decoder(codec_ctx->codec_id); const AVCodecDescriptor *dec_desc = avcodec_descriptor_get(codec_ctx->codec_id); if (!dec) { if (dec_desc) qWarning("Failed to find subtitle codec %s", dec_desc->name); else qWarning("Failed to find subtitle codec %d", codec_ctx->codec_id); return false; } qDebug("found subtitle decoder '%s'", dec_desc->name); // AV_CODEC_PROP_TEXT_SUB: ffmpeg >= 2.0 #ifdef AV_CODEC_PROP_TEXT_SUB if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { qWarning("Only text based subtitles are currently supported"); return false; } #endif AVDictionary *codec_opts = NULL; int ret = avcodec_open2(codec_ctx, dec, &codec_opts); if (ret < 0) { qWarning("open subtitle codec error: %s", av_err2str(ret)); av_dict_free(&codec_opts); return false; } while (!m_reader.atEnd()) { if (!m_reader.readFrame()) { // eof or other errors continue; } if (m_reader.stream() != ss) continue; const Packet pkt = m_reader.packet(); if (!pkt.isValid()) continue; SubtitleFrame frame = processLine(pkt.data, pkt.pts, pkt.duration); if (frame.isValid()) m_frames.append(frame); } avcodec_close(codec_ctx); codec_ctx = 0; return true; } } //namespace QtAV QtAV-1.12.0/src/subtitle/SubtitleProcessorLibASS.cpp000066400000000000000000000575361312235004300222360ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and LibASS Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/private/SubtitleProcessor.h" #include #include #include #include #include #include #include "QtAV/Packet.h" #include "QtAV/private/factory.h" #include "PlainText.h" #include "utils/internal.h" #include "utils/Logger.h" //#define ASS_CAPI_NS // do not unload() manually! //#define CAPI_LINK_ASS #include "capi/ass_api.h" #include //#include //include after ass_api.h, stdio.h is included there in a different namespace namespace QtAV { void RenderASS(QImage *image, const SubImage &img, int dstX, int dstY); class SubtitleProcessorLibASS Q_DECL_FINAL: public SubtitleProcessor, protected ass::api { public: SubtitleProcessorLibASS(); ~SubtitleProcessorLibASS(); void updateFontCache(); SubtitleProcessorId id() const Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; QStringList supportedTypes() const Q_DECL_OVERRIDE; bool process(QIODevice* dev) Q_DECL_OVERRIDE; // supportsFromFile must be true bool process(const QString& path) Q_DECL_OVERRIDE; QList frames() const Q_DECL_OVERRIDE; bool canRender() const Q_DECL_OVERRIDE { return true;} QString getText(qreal pts) const Q_DECL_OVERRIDE; QImage getImage(qreal pts, QRect *boundingRect = 0) Q_DECL_OVERRIDE; SubImageSet getSubImages(qreal pts, QRect *boundingRect) Q_DECL_OVERRIDE; bool processHeader(const QByteArray& codec, const QByteArray& data) Q_DECL_OVERRIDE; SubtitleFrame processLine(const QByteArray& data, qreal pts = -1, qreal duration = 0) Q_DECL_OVERRIDE; void setFontFile(const QString& file) Q_DECL_OVERRIDE; void setFontsDir(const QString& dir) Q_DECL_OVERRIDE; void setFontFileForced(bool force) Q_DECL_OVERRIDE; protected: void onFrameSizeChanged(int width, int height) Q_DECL_OVERRIDE; private: bool initRenderer(); void updateFontCacheAsync(); SubImageSet getSubImages(qreal pts, QRect *boundingRect, QImage* qimg, bool copy); void processTrack(ASS_Track *track); bool m_update_cache; bool force_font_file; // works only iff font_file is set QString font_file; QString fonts_dir; QByteArray m_codec; ASS_Library *m_ass; ASS_Renderer *m_renderer; ASS_Track *m_track; QList m_frames; //cache the image for the last invocation. return this if image does not change QImage m_image; SubImageSet m_assimages; QRect m_bound; mutable QMutex m_mutex; }; static const SubtitleProcessorId SubtitleProcessorId_LibASS = QStringLiteral("qtav.subtitle.processor.libass"); namespace { static const char kName[] = "LibASS"; } FACTORY_REGISTER(SubtitleProcessor, LibASS, kName) // log level from ass_utils.h #define MSGL_FATAL 0 #define MSGL_ERR 1 #define MSGL_WARN 2 #define MSGL_INFO 4 #define MSGL_V 6 #define MSGL_DBG2 7 static void ass_msg_cb(int level, const char *fmt, va_list va, void *data) { Q_UNUSED(data) if (level > MSGL_INFO) return; #ifdef Q_OS_WIN if (level == MSGL_WARN) { return; //crash at warnings from fontselect } #endif printf("[libass]: "); vprintf(fmt, va); printf("\n"); fflush(0); return; QString msg(QStringLiteral("{libass} ") + QString().vsprintf(fmt, va)); //QString.vsprintf() may crash at strlen(). if (level == MSGL_FATAL) qFatal("%s", msg.toUtf8().constData()); else if (level <= 2) qWarning() << msg; else if (level <= MSGL_INFO) qDebug() << msg; } SubtitleProcessorLibASS::SubtitleProcessorLibASS() : m_update_cache(true) , force_font_file(true) , m_ass(0) , m_renderer(0) , m_track(0) { if (!ass::api::loaded()) return; m_ass = ass_library_init(); if (!m_ass) { qWarning("ass_library_init failed!"); return; } ass_set_message_cb(m_ass, ass_msg_cb, NULL); } SubtitleProcessorLibASS::~SubtitleProcessorLibASS() { // ass dll is loaded if ass objects are available if (m_track) { ass_free_track(m_track); m_track = 0; } if (m_renderer) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); ass_renderer_done(m_renderer); // check async update cache!! m_renderer = 0; } if (m_ass) { ass_library_done(m_ass); m_ass = 0; } } SubtitleProcessorId SubtitleProcessorLibASS::id() const { return SubtitleProcessorId_LibASS; } QString SubtitleProcessorLibASS::name() const { return QLatin1String(kName);//SubtitleProcessorFactory::name(id()); } QStringList SubtitleProcessorLibASS::supportedTypes() const { // from LibASS/tests/fate/subtitles.mak // TODO: mp4 static const QStringList sSuffixes = QStringList() << QStringLiteral("ass") << QStringLiteral("ssa"); return sSuffixes; } QList SubtitleProcessorLibASS::frames() const { return m_frames; } bool SubtitleProcessorLibASS::process(QIODevice *dev) { if (!ass::api::loaded()) return false; QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (m_track) { ass_free_track(m_track); m_track = 0; } if (!dev->isOpen()) { if (!dev->open(QIODevice::ReadOnly)) { qWarning() << "open qiodevice error: " << dev->errorString(); return false; } } QByteArray data(dev->readAll()); m_track = ass_read_memory(m_ass, (char*)data.constData(), data.size(), NULL); //utf-8 if (!m_track) { qWarning("ass_read_memory error, ass track init failed!"); return false; } processTrack(m_track); return true; } bool SubtitleProcessorLibASS::process(const QString &path) { if (!ass::api::loaded()) return false; QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (m_track) { ass_free_track(m_track); m_track = 0; } m_track = ass_read_file(m_ass, (char*)path.toUtf8().constData(), NULL); if (!m_track) { qWarning("ass_read_file error, ass track init failed!"); return false; } processTrack(m_track); return true; } bool SubtitleProcessorLibASS::processHeader(const QByteArray& codec, const QByteArray &data) { if (!ass::api::loaded()) return false; QMutexLocker lock(&m_mutex); Q_UNUSED(lock); m_codec = codec; m_frames.clear(); setFrameSize(-1, -1); if (m_track) { ass_free_track(m_track); m_track = 0; } m_track = ass_new_track(m_ass); if (!m_track) { qWarning("failed to create an ass track"); return false; } ass_process_codec_private(m_track, (char*)data.constData(), data.size()); return true; } SubtitleFrame SubtitleProcessorLibASS::processLine(const QByteArray &data, qreal pts, qreal duration) { if (!ass::api::loaded()) return SubtitleFrame(); if (data.isEmpty() || data.at(0) == 0) return SubtitleFrame(); QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (!m_track) return SubtitleFrame(); const int nb_tracks = m_track->n_events; // TODO: confirm. ass/ssa path from mpv if (m_codec == QByteArrayLiteral("ass")) { ass_process_chunk(m_track, (char*)data.constData(), data.size(), pts*1000.0, duration*1000.0); } else { //ssa //ssa. mpv: flush_on_seek, broken ffmpeg ASS packet format ass_process_data(m_track, (char*)data.constData(), data.size()); } if (nb_tracks == m_track->n_events) return SubtitleFrame(); //qDebug("events: %d", m_track->n_events); for (int i = m_track->n_events-1; i >= 0; --i) { const ASS_Event& ae = m_track->events[i]; //qDebug("ass_event[%d] %lld+%lld/%lld+%lld: %s", i, ae.Start, ae.Duration, (long long)(pts*1000.0), (long long)(duration*1000.0), ae.Text); //packet.duration can be 0 if (ae.Start == (long long)(pts*1000.0)) {// && ae.Duration == (long long)(duration*1000.0)) { SubtitleFrame frame; frame.text = PlainText::fromAss(ae.Text); frame.begin = qreal(ae.Start)/1000.0; frame.end = frame.begin + qreal(ae.Duration)/1000.0; return frame; } } return SubtitleFrame(); } QString SubtitleProcessorLibASS::getText(qreal pts) const { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); QString text; for (int i = 0; i < m_frames.size(); ++i) { if (m_frames[i].begin <= pts && m_frames[i].end >= pts) { text += m_frames[i].text + QStringLiteral("\n"); continue; } if (!text.isEmpty()) break; } return text.trimmed(); } void renderASS32(QImage *image, ASS_Image *img, int dstX, int dstY); QImage SubtitleProcessorLibASS::getImage(qreal pts, QRect *boundingRect) { // ass dll is loaded if ass library is available getSubImages(pts, boundingRect, &m_image, false); m_assimages.reset(); return m_image; } SubImageSet SubtitleProcessorLibASS::getSubImages(qreal pts, QRect *boundingRect) { m_assimages = getSubImages(pts, boundingRect, NULL, true); return m_assimages; } SubImageSet SubtitleProcessorLibASS::getSubImages(qreal pts, QRect *boundingRect, QImage *qimg, bool copy) { // ass dll is loaded if ass library is available { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (!m_ass) { qWarning("ass library not available"); return SubImageSet(); } if (!m_track) { qWarning("ass track not available"); return SubImageSet(); } if (!m_renderer) { initRenderer(); if (!m_renderer) { qWarning("ass renderer not available"); return SubImageSet(); } } } if (m_update_cache) updateFontCache(); QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (!m_renderer) //reset in setFontXXX return SubImageSet(); int detect_change = 0; ASS_Image *img = ass_render_frame(m_renderer, m_track, (long long)(pts * 1000.0), &detect_change); if (!detect_change && !m_assimages.isValid()) { if (boundingRect) *boundingRect = m_bound; return m_assimages; } m_image = QImage(); m_assimages.reset(frameWidth(), frameHeight(), SubImageSet::ASS); QRect rect(0, 0, 0, 0); ASS_Image *i = img; while (i) { const quint8 a = 255 - (i->color&0xff); //qDebug("ass %d rect %d: %d,%d-%dx%d", a, n, i->dst_x, i->dst_y, i->w, i->h); if (i->w <= 0 || i->h <= 0 || a == 0) { i = i->next; continue; } SubImage s(i->dst_x, i->dst_y, i->w, i->h, i->stride); s.color = i->color; if (copy) { s.data.reserve(i->stride*i->h); s.data.resize(i->stride*i->h); memcpy(s.data.data(), i->bitmap, i->stride*(i->h-1) + i->w); } else { s.data = QByteArray::fromRawData((const char*)i->bitmap, i->stride*(i->h-1) + i->w); } m_assimages.images.append(s); rect |= QRect(i->dst_x, i->dst_y, i->w, i->h); i = i->next; } m_bound = rect; if (boundingRect) { *boundingRect = m_bound; } if (!qimg) return m_assimages; *qimg = QImage(rect.size(), QImage::Format_ARGB32); qimg->fill(Qt::transparent); foreach (const SubImage& i, m_assimages.images) { RenderASS(qimg, i, i.x - rect.x(), i.y - rect.y()); } return m_assimages; } void SubtitleProcessorLibASS::onFrameSizeChanged(int width, int height) { if (width < 0 || height < 0) return; if (!m_renderer) { initRenderer(); } if (!m_renderer) return; ass_set_frame_size(m_renderer, width, height); } void SubtitleProcessorLibASS::setFontFile(const QString &file) { if (font_file == file) return; font_file = file; m_update_cache = true; //update renderer when getting the next image if (m_renderer) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); // resize frame to ensure renderer can be resized later setFrameSize(-1, -1); ass_renderer_done(m_renderer); m_renderer = 0; } } void SubtitleProcessorLibASS::setFontFileForced(bool force) { if (force_font_file == force) return; force_font_file = force; // FIXME: sometimes crash m_update_cache = true; //update renderer when getting the next image if (m_renderer) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); // resize frame to ensure renderer can be resized later setFrameSize(-1, -1); ass_renderer_done(m_renderer); m_renderer = 0; } } void SubtitleProcessorLibASS::setFontsDir(const QString &dir) { if (fonts_dir == dir) return; fonts_dir = dir; m_update_cache = true; //update renderer when getting the next image if (m_renderer) { QMutexLocker lock(&m_mutex); Q_UNUSED(lock); // resize frame to ensure renderer can be resized later setFrameSize(-1, -1); ass_renderer_done(m_renderer); m_renderer = 0; } } bool SubtitleProcessorLibASS::initRenderer() { //ass_set_extract_fonts(m_ass, 1); //ass_set_style_overrides(m_ass, 0); m_renderer = ass_renderer_init(m_ass); if (!m_renderer) { qWarning("ass_renderer_init failed!"); return false; } #if LIBASS_VERSION >= 0x01000000 ass_set_shaper(m_renderer, ASS_SHAPING_SIMPLE); #endif return true; } // TODO: set font cache dir. default is working dir which may be not writable on some platforms void SubtitleProcessorLibASS::updateFontCache() { // ass dll is loaded if renderer is valid QMutexLocker lock(&m_mutex); Q_UNUSED(lock); if (!m_renderer) return; // fonts in assets and qrc may change. so check before appFontsDir static const QStringList kFontsDirs = QStringList() << qApp->applicationDirPath().append(QLatin1String("/fonts")) << qApp->applicationDirPath() // for winrt << QStringLiteral("assets:/fonts") << QStringLiteral(":/fonts") << Internal::Path::appFontsDir() #ifndef Q_OS_WINRT << Internal::Path::fontsDir() #endif ; // TODO: modify fontconfig cache dir in fonts.conf then save to conf static QString conf(0, QChar()); //FC_CONFIG_FILE? if (conf.isEmpty() && !conf.isNull()) { static const QString kFontCfg(QStringLiteral("fonts.conf")); foreach (const QString& fdir, kFontsDirs) { qDebug() << "looking up " << kFontCfg << " in: " << fdir; QFile cfg(QStringLiteral("%1/%2").arg(fdir).arg(kFontCfg)); if (!cfg.exists()) continue; conf = cfg.fileName(); if (fdir.isEmpty() || fdir.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive) || fdir.startsWith(QLatin1String(":"), Qt::CaseInsensitive) || fdir.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive) ) { conf = QStringLiteral("%1/%2").arg(Internal::Path::appFontsDir()).arg(kFontCfg); qDebug() << "Fonts dir (for config) is not supported by libass. Copy fonts to app fonts dir: " << fdir; if (!QDir(Internal::Path::appFontsDir()).exists()) { if (!QDir().mkpath(Internal::Path::appFontsDir())) { qWarning("Failed to create fonts dir: %s", Internal::Path::appFontsDir().toUtf8().constData()); } } QFile cfgout(conf); if (cfgout.exists() && cfgout.size() != cfg.size()) { // TODO: qDebug() << "new " << kFontCfg << " with the same name. remove old: " << cfgout.fileName(); cfgout.remove(); } if (!cfgout.exists() && !cfg.copy(conf)) { qWarning() << "Copy font config file [" << cfg.fileName() << "] error: " << cfg.errorString(); continue; } } break; } if (!QFile(conf).exists()) conf.clear(); qDebug() << "FontConfig: " << conf; } /* * Fonts dir look up: * - appdir/fonts has fonts * - assets:/fonts, qrc:/fonts: copy fonts to appFontsDir * - appFontsDir (appdir/fonts has no fonts) * - fontsDir if it has font files * If defaults.ttf exists in fonts dir(for 'assets:' and 'qrc:' appFontsDir is checked), use it as default font and disable font provider * (for example fontconfig) to speed up(skip) libass font look up. * Skip setting fonts dir */ static QString sFont(0, QChar()); // if exists, fontconfig will be disabled and directly use this font static QString sFontsDir(0, QChar()); if (sFontsDir.isEmpty() && !sFontsDir.isNull()) { static const QString kDefaultFontName(QStringLiteral("default.ttf")); static const QStringList ft_filters = QStringList() << QStringLiteral("*.ttf") << QStringLiteral("*.otf") << QStringLiteral("*.ttc"); QStringList fonts; foreach (const QString& fdir, kFontsDirs) { qDebug() << "looking up fonts in: " << fdir; QDir d(fdir); if (!d.exists()) //avoid winrt crash (system fonts dir) continue; fonts = d.entryList(ft_filters, QDir::Files); if (fonts.isEmpty()) continue; if (fonts.contains(kDefaultFontName)) { QFile ff(QStringLiteral("%1/%2").arg(fdir).arg(kDefaultFontName)); if (ff.exists() && ff.size() == 0) { qDebug("invalid default font"); fonts.clear(); continue; } } sFontsDir = fdir; break; } if (fonts.isEmpty()) { sFontsDir.clear(); } else { qDebug() << "fonts dir: " << sFontsDir << " font files: " << fonts; if (sFontsDir.isEmpty() || sFontsDir.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive) || sFontsDir.startsWith(QLatin1String(":"), Qt::CaseInsensitive) || sFontsDir.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive) ) { const QString fontsdir_in(sFontsDir); sFontsDir = Internal::Path::appFontsDir(); qDebug() << "Fonts dir is not supported by libass. Copy fonts to app fonts dir if not exist: " << sFontsDir; if (!QDir(Internal::Path::appFontsDir()).exists()) { if (!QDir().mkpath(Internal::Path::appFontsDir())) { qWarning("Failed to create fonts dir: %s", Internal::Path::appFontsDir().toUtf8().constData()); } } foreach (const QString& f, fonts) { QFile ff(QStringLiteral("%1/%2").arg(fontsdir_in).arg(f)); const QString kOut(QStringLiteral("%1/%2").arg(sFontsDir).arg(f)); QFile ffout(kOut); if (ffout.exists() && ffout.size() != ff.size()) { // TODO: qDebug() << "new font with the same name. remove old: " << ffout.fileName(); ffout.remove(); } if (!ffout.exists() && !ff.copy(kOut)) qWarning() << "Copy font file [" << ff.fileName() << "] error: " << ff.errorString(); } } if (fonts.contains(kDefaultFontName)) { sFont = QStringLiteral("%1/%2").arg(sFontsDir).arg(kDefaultFontName); qDebug() << "default font file: " << sFont << "; fonts dir: " << sFontsDir; } } } static QByteArray family; //fallback to Arial? if (family.isEmpty()) { family = qgetenv("QTAV_SUB_FONT_FAMILY_DEFAULT"); //Setting default font to the Arial from default.ttf (used if FontConfig fails) if (family.isEmpty()) family = QByteArrayLiteral("Arial"); } // prefer user settings const QString kFont = font_file.isEmpty() ? sFont : Internal::Path::toLocal(font_file); const QString kFontsDir = fonts_dir.isEmpty() ? sFontsDir : Internal::Path::toLocal(fonts_dir); qDebug() << "font file: " << kFont << "; fonts dir: " << kFontsDir; // setup libass #ifdef Q_OS_WINRT if (!kFontsDir.isEmpty()) qDebug("BUG: winrt libass set a valid fonts dir results in crash. skip fonts dir setup."); #else // will call strdup, so safe to use temp array .toUtf8().constData() if (!force_font_file || (!font_file.isEmpty() && !QFile::exists(kFont))) ass_set_fonts_dir(m_ass, kFontsDir.isEmpty() ? 0 : kFontsDir.toUtf8().constData()); // look up fonts in fonts dir can be slow. force font file to skip lookup #endif /* ass_set_fonts: * fc/dfp=false(auto font provider): Prefer font provider to find a font(FC needs fonts.conf) in font_dir, or provider's configuration. If failed, try the given font * fc/dfp=true(no font provider): only try the given font */ // user can prefer font provider(force_font_file=false), or disable font provider to force the given font // if provider is enabled, libass can fallback to the given font if provider can not provide a font const QByteArray a_conf(conf.toUtf8()); const char* kConf = conf.isEmpty() ? 0 : a_conf.constData(); if (kFont.isEmpty()) { // TODO: always use font provider if no font file is set, i.e. ignore force_font_file qDebug("No font file is set, use font provider"); ass_set_fonts(m_renderer, NULL, family.constData(), !force_font_file, kConf, 1); } else { qDebug("Font file is set. force font file: %d", force_font_file); ass_set_fonts(m_renderer, kFont.toUtf8().constData(), family.constData(), !force_font_file, kConf, 1); } //ass_fonts_update(m_renderer); // update in ass_set_fonts(....,1) m_update_cache = false; //TODO: set true if user set a new font or fonts dir } void SubtitleProcessorLibASS::updateFontCacheAsync() { class FontCacheUpdater : public QThread { SubtitleProcessorLibASS *sp; public: FontCacheUpdater(SubtitleProcessorLibASS *p) : sp(p) {} void run() { if (!sp) return; sp->updateFontCache(); } }; FontCacheUpdater updater(this); QEventLoop loop; //QObject::connect(&updater, SIGNAL(finished()), &loop, SLOT(quit())); updater.start(); while (updater.isRunning()) { loop.processEvents(); } //loop.exec(); // what if updater is finished before exec()? //updater.wait(); } void SubtitleProcessorLibASS::processTrack(ASS_Track *track) { // language, track type m_frames.clear(); for (int i = 0; i < track->n_events; ++i) { SubtitleFrame frame; const ASS_Event& ae = track->events[i]; frame.text = PlainText::fromAss(ae.Text); frame.begin = qreal(ae.Start)/1000.0; frame.end = frame.begin + qreal(ae.Duration)/1000.0; m_frames.append(frame); } } } //namespace QtAV QtAV-1.12.0/src/utils/000077500000000000000000000000001312235004300143265ustar00rootroot00000000000000QtAV-1.12.0/src/utils/BlockingQueue.h000066400000000000000000000221451312235004300172400ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_BLOCKINGQUEUE_H #define QTAV_BLOCKINGQUEUE_H #include #include #include //TODO: block full and empty condition separately QT_BEGIN_NAMESPACE template class QQueue; QT_END_NAMESPACE namespace QtAV { template class Container = QQueue> class BlockingQueue { public: BlockingQueue(); virtual ~BlockingQueue() {} void setCapacity(int max); //enqueue is allowed if less than capacity /*! * \brief setThreshold * do nothing if min >= capacity() * \param min */ void setThreshold(int min); //wake up and enqueue void put(const T& t); T take(); void setBlocking(bool block); //will wake if false. called when no more data can enqueue void blockEmpty(bool block); void blockFull(bool block); //TODO:setMinBlock,MaxBlock inline void clear(); // TODO: checkEmpty, Enough, Full? inline bool isEmpty() const; inline bool isEnough() const; //size > thres inline bool isFull() const; //size >= cap inline int size() const; inline int threshold() const; inline int capacity() const; class StateChangeCallback { public: virtual ~StateChangeCallback(){} virtual void call() = 0; }; void setEmptyCallback(StateChangeCallback* call); void setThresholdCallback(StateChangeCallback* call); void setFullCallback(StateChangeCallback* call); protected: /*! * \brief checkFull * Check whether the queue is full. Default implemention is compare queue size to capacity. * Full is now a more generic notion. You can implement it as checking queued bytes etc. * \return true if queue is full */ virtual bool checkFull() const; virtual bool checkEmpty() const; virtual bool checkEnough() const; virtual void onPut(const T&) {} virtual void onTake(const T&) {} bool block_empty, block_full; int cap, thres; Container queue; private: mutable QReadWriteLock lock; //locker in const func QReadWriteLock block_change_lock; QWaitCondition cond_full, cond_empty; //upto_threshold_callback, downto_threshold_callback QScopedPointer empty_callback, threshold_callback, full_callback; }; /* cap - thres = 24, about 1s * if fps is large, then larger capacity and threshold is preferred */ template class Container> BlockingQueue::BlockingQueue() :block_empty(true),block_full(true),cap(48),thres(32) , empty_callback(0) , threshold_callback(0) , full_callback(0) { } template class Container> void BlockingQueue::setCapacity(int max) { //qDebug("queue capacity==>>%d", max); QWriteLocker locker(&lock); Q_UNUSED(locker); cap = max; if (thres > cap) thres = cap; } template class Container> void BlockingQueue::setThreshold(int min) { //qDebug("queue threshold==>>%d", min); QWriteLocker locker(&lock); Q_UNUSED(locker); if (min > cap) return; thres = min; } template class Container> void BlockingQueue::put(const T& t) { QWriteLocker locker(&lock); Q_UNUSED(locker); if (checkFull()) { //qDebug("queue full"); //too frequent if (full_callback) { full_callback->call(); } if (block_full) cond_full.wait(&lock); } queue.enqueue(t); onPut(t); // emit bufferProgressChanged here if buffering if (checkEnough()) { cond_empty.wakeOne(); //emit buffering finished here //qDebug("queue is enough: %d/%d~%d", queue.size(), thres, cap); } else { //qDebug("buffering: %d/%d~%d", queue.size(), thres, cap); } } template class Container> T BlockingQueue::take() { QWriteLocker locker(&lock); Q_UNUSED(locker); if (checkEmpty()) {//TODO:always block? //qDebug("queue empty!!"); if (empty_callback) { empty_callback->call(); } if (block_empty) cond_empty.wait(&lock); //block when empty only } if (checkEmpty()) { //qWarning("Queue is still empty"); if (empty_callback) { empty_callback->call(); } return T(); } T t(queue.dequeue()); cond_full.wakeOne(); onTake(t); // emit start buffering here if empty return t; } template class Container> void BlockingQueue::setBlocking(bool block) { QWriteLocker locker(&lock); Q_UNUSED(locker); block_empty = block_full = block; if (!block) { cond_empty.wakeAll(); //empty still wait. setBlock=>setCapacity(-1) cond_full.wakeAll(); } } template class Container> void BlockingQueue::blockEmpty(bool block) { if (!block) { cond_empty.wakeAll(); } QWriteLocker locker(&block_change_lock); Q_UNUSED(locker); block_empty = block; } template class Container> void BlockingQueue::blockFull(bool block) { if (!block) { cond_full.wakeAll(); } //DO NOT use the same lock that put() get() use. it may be already locked //this function usualy called in demux thread, so no lock is ok QWriteLocker locker(&block_change_lock); Q_UNUSED(locker); block_full = block; } template class Container> void BlockingQueue::clear() { QWriteLocker locker(&lock); Q_UNUSED(locker); //cond_empty.wakeAll(); cond_full.wakeAll(); queue.clear(); //TODO: assert not empty onTake(T()); } template class Container> bool BlockingQueue::isEmpty() const { QReadLocker locker(&lock); Q_UNUSED(locker); return queue.isEmpty(); } template class Container> bool BlockingQueue::isEnough() const { QReadLocker locker(&lock); Q_UNUSED(locker); return queue.size() >= thres; } template class Container> bool BlockingQueue::isFull() const { QReadLocker locker(&lock); Q_UNUSED(locker); return queue.size() >= cap; } template class Container> int BlockingQueue::size() const { QReadLocker locker(&lock); Q_UNUSED(locker); return queue.size(); } template class Container> int BlockingQueue::threshold() const { QReadLocker locker(&lock); Q_UNUSED(locker); return thres; } template class Container> int BlockingQueue::capacity() const { QReadLocker locker(&lock); Q_UNUSED(locker); return cap; } template class Container> void BlockingQueue::setEmptyCallback(StateChangeCallback *call) { QWriteLocker locker(&lock); Q_UNUSED(locker); empty_callback.reset(call); } template class Container> void BlockingQueue::setThresholdCallback(StateChangeCallback *call) { QWriteLocker locker(&lock); Q_UNUSED(locker); threshold_callback.reset(call); } template class Container> void BlockingQueue::setFullCallback(StateChangeCallback *call) { QWriteLocker locker(&lock); Q_UNUSED(locker); full_callback.reset(call); } template class Container> bool BlockingQueue::checkFull() const { return queue.size() >= cap; } template class Container> bool BlockingQueue::checkEmpty() const { return queue.isEmpty(); } template class Container> bool BlockingQueue::checkEnough() const { return queue.size() >= thres && !checkEmpty(); } } //namespace QtAV #endif // QTAV_BLOCKINGQUEUE_H QtAV-1.12.0/src/utils/CopyFrame_SSE2.cpp000066400000000000000000000224111312235004300175130ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #if defined(__SSE__) || defined(_M_IX86) || defined(_M_X64) // gcc, clang defines __SSE__, vc does not #ifndef INC_FROM_NAMESPACE #include //intptr_t #include #include #endif // from https://software.intel.com/en-us/articles/copying-accelerated-video-decode-frame-buffers // modified by wang-bin to support unaligned src/dest and sse2 /* * 1. Fill a 4K byte cached (WB) memory buffer from the USWC video frame * 2. Copy the 4K byte cache contents to the destination WB frame * 3. Repeat steps 1 and 2 until the whole frame buffer has been copied. * * _mm_store_si128 and _mm_load_si128 intrinsics will compile to the MOVDQA instruction, _mm_stream_load_si128 and _mm_stream_si128 intrinsics compile to the MOVNTDQA and MOVNTDQ instructions * * using the same pitch (which is assumed to be a multiple of 64 bytes), and expecting 64 byte alignment of every row of the source, cached 4K buffer and destination buffers. * The MOVNTDQA streaming load instruction and the MOVNTDQ streaming store instruction require at least 16 byte alignment in their memory addresses. */ // // COPIES VIDEO FRAMES FROM USWC MEMORY TO WB SYSTEM MEMORY VIA CACHED BUFFER // ASSUMES PITCH IS A MULTIPLE OF 64B CACHE LINE SIZE, WIDTH MAY NOT BE #ifndef STREAM_LOAD_SI128 #define STREAM_LOAD_SI128(x) _mm_load_si128(x) #endif //STREAM_LOAD_SI128 #define CACHED_BUFFER_SIZE 4096 #define UINT unsigned int // copy plane //QT_FUNCTION_TARGET("sse2") void CopyFrame_SSE2(void *pSrc, void *pDest, void *pCacheBlock, UINT width, UINT height, UINT pitch) { //assert(((intptr_t)pCacheBlock & 0x0f) == 0 && (dst_pitch & 0x0f) == 0); __m128i x0, x1, x2, x3; __m128i *pCache; UINT x, y, yLoad, yStore; UINT rowsPerBlock = CACHED_BUFFER_SIZE / pitch; const UINT width64 = (width + 63) & ~0x03f; const UINT extraPitch = (pitch - width64) / 16; __m128i *pLoad = (__m128i*)pSrc; __m128i *pStore = (__m128i*)pDest; const bool src_unaligned = !!((intptr_t)pSrc & 0x0f); const bool dst_unaligned = !!((intptr_t)pDest & 0x0f); //if (src_unaligned || dst_unaligned) // qDebug("===========unaligned: src %d, dst: %d, extraPitch: %d", src_unaligned, dst_unaligned, extraPitch); // COPY THROUGH 4KB CACHED BUFFER for (y = 0; y < height; y += rowsPerBlock) { // ROWS LEFT TO COPY AT END if (y + rowsPerBlock > height) rowsPerBlock = height - y; pCache = (__m128i *)pCacheBlock; _mm_mfence(); // LOAD ROWS OF PITCH WIDTH INTO CACHED BLOCK for (yLoad = 0; yLoad < rowsPerBlock; yLoad++) { // COPY A ROW, CACHE LINE AT A TIME for (x = 0; x < pitch; x +=64) { // movntdqa x0 = STREAM_LOAD_SI128(pLoad + 0); x1 = STREAM_LOAD_SI128(pLoad + 1); x2 = STREAM_LOAD_SI128(pLoad + 2); x3 = STREAM_LOAD_SI128(pLoad + 3); if (src_unaligned) { // movdqu _mm_storeu_si128(pCache +0, x0); _mm_storeu_si128(pCache +1, x1); _mm_storeu_si128(pCache +2, x2); _mm_storeu_si128(pCache +3, x3); } else { // movdqa _mm_store_si128(pCache +0, x0); _mm_store_si128(pCache +1, x1); _mm_store_si128(pCache +2, x2); _mm_store_si128(pCache +3, x3); } pCache += 4; pLoad += 4; } } _mm_mfence(); pCache = (__m128i *)pCacheBlock; // STORE ROWS OF FRAME WIDTH FROM CACHED BLOCK for (yStore = 0; yStore < rowsPerBlock; yStore++) { // copy a row, cache line at a time for (x = 0; x < width64; x += 64) { // movdqa x0 = _mm_load_si128(pCache); x1 = _mm_load_si128(pCache + 1); x2 = _mm_load_si128(pCache + 2); x3 = _mm_load_si128(pCache + 3); if (dst_unaligned) { // movdqu _mm_storeu_si128(pStore, x0); _mm_storeu_si128(pStore + 1, x1); _mm_storeu_si128(pStore + 2, x2); _mm_storeu_si128(pStore + 3, x3); } else { // movntdq _mm_stream_si128(pStore, x0); _mm_stream_si128(pStore + 1, x1); _mm_stream_si128(pStore + 2, x2); _mm_stream_si128(pStore + 3, x3); } pCache += 4; pStore += 4; } pCache += extraPitch; pStore += extraPitch; } } } //Taken from the QuickSync decoder by Eric Gur // a memcpy style function that copied data very fast from a GPU tiled memory (write back) // Performance tip: page offset (12 lsb) of both addresses should be different // optimally use a 2K offset between them. void *memcpy_sse2(void* dst, const void* src, size_t size) { static const size_t kRegsInLoop = sizeof(size_t) * 2; // 8 or 16 if (!dst || !src) return NULL; // If memory is not aligned, use memcpy // TODO: only check dst aligned const bool isAligned = !(((size_t)(src) | (size_t)(dst)) & 0x0F); if (!isAligned) return memcpy(dst, src, size); __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; #ifdef __x86_64__ __m128i xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; #endif size_t reminder = size & (kRegsInLoop * sizeof(xmm0) - 1); // Copy 128 or 256 bytes every loop size_t end = 0; __m128i* pTrg = (__m128i*)dst; __m128i* pTrgEnd = pTrg + ((size - reminder) >> 4); __m128i* pSrc = (__m128i*)src; // Make sure source is synced - doesn't hurt if not needed. _mm_sfence(); while (pTrg < pTrgEnd) { // _mm_stream_load_si128 emits the Streaming SIMD Extensions 4 (SSE4.1) instruction MOVNTDQA // Fastest method for copying GPU RAM. Available since Penryn (45nm Core 2 Duo/Quad) xmm0 = STREAM_LOAD_SI128(pSrc); xmm1 = STREAM_LOAD_SI128(pSrc + 1); xmm2 = STREAM_LOAD_SI128(pSrc + 2); xmm3 = STREAM_LOAD_SI128(pSrc + 3); xmm4 = STREAM_LOAD_SI128(pSrc + 4); xmm5 = STREAM_LOAD_SI128(pSrc + 5); xmm6 = STREAM_LOAD_SI128(pSrc + 6); xmm7 = STREAM_LOAD_SI128(pSrc + 7); #ifdef __x86_64__ // Use all 16 xmm registers xmm8 = STREAM_LOAD_SI128(pSrc + 8); xmm9 = STREAM_LOAD_SI128(pSrc + 9); xmm10 = STREAM_LOAD_SI128(pSrc + 10); xmm11 = STREAM_LOAD_SI128(pSrc + 11); xmm12 = STREAM_LOAD_SI128(pSrc + 12); xmm13 = STREAM_LOAD_SI128(pSrc + 13); xmm14 = STREAM_LOAD_SI128(pSrc + 14); xmm15 = STREAM_LOAD_SI128(pSrc + 15); #endif pSrc += kRegsInLoop; // _mm_store_si128 emit the SSE2 intruction MOVDQA (aligned store) // TODO: why not _mm_stream_si128? it works _mm_store_si128(pTrg , xmm0); _mm_store_si128(pTrg + 1, xmm1); _mm_store_si128(pTrg + 2, xmm2); _mm_store_si128(pTrg + 3, xmm3); _mm_store_si128(pTrg + 4, xmm4); _mm_store_si128(pTrg + 5, xmm5); _mm_store_si128(pTrg + 6, xmm6); _mm_store_si128(pTrg + 7, xmm7); #ifdef __x86_64__ // Use all 16 xmm registers _mm_store_si128(pTrg + 8, xmm8); _mm_store_si128(pTrg + 9, xmm9); _mm_store_si128(pTrg + 10, xmm10); _mm_store_si128(pTrg + 11, xmm11); _mm_store_si128(pTrg + 12, xmm12); _mm_store_si128(pTrg + 13, xmm13); _mm_store_si128(pTrg + 14, xmm14); _mm_store_si128(pTrg + 15, xmm15); #endif pTrg += kRegsInLoop; } // Copy in 16 byte steps if (reminder >= 16) { size = reminder; reminder = size & 15; end = size >> 4; for (size_t i = 0; i < end; ++i) pTrg[i] = STREAM_LOAD_SI128(pSrc + i); } // Copy last bytes - shouldn't happen as strides are modulu 16 if (reminder) { __m128i temp = STREAM_LOAD_SI128(pSrc + end); char* ps = (char*)(&temp); char* pt = (char*)(pTrg + end); for (size_t i = 0; i < reminder; ++i) pt[i] = ps[i]; } return dst; } #endif QtAV-1.12.0/src/utils/CopyFrame_SSE4.cpp000066400000000000000000000033511312235004300175170ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #if defined(__SSE__) || defined(_M_IX86) || defined(_M_X64) // gcc, clang defines __SSE__, vc does not // for mingw gcc #include //stream load #include //intptr_t #include #define STREAM_LOAD_SI128(x) _mm_stream_load_si128(x) namespace sse4 { //avoid name conflict #define INC_FROM_NAMESPACE #include "CopyFrame_SSE2.cpp" } //namespace sse4 //QT_FUNCTION_TARGET("sse4.1") void CopyFrame_SSE4(void *pSrc, void *pDest, void *pCacheBlock, UINT width, UINT height, UINT pitch) { sse4::CopyFrame_SSE2(pSrc, pDest, pCacheBlock, width, height, pitch); } void *memcpy_sse4(void* dst, const void* src, size_t size) { return sse4::memcpy_sse2(dst, src, size); } #endif QtAV-1.12.0/src/utils/DirectXHelper.cpp000066400000000000000000000130421312235004300175340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "DirectXHelper.h" #include "utils/Logger.h" namespace QtAV { namespace DXHelper { const char* vendorName(unsigned id) { static const struct { unsigned id; char name[32]; } vendors [] = { { 0x1002, "ATI" }, { 0x10DE, "NVIDIA" }, { 0x1106, "VIA" }, { 0x8086, "Intel" }, { 0x5333, "S3 Graphics" }, { 0x4D4F4351, "Qualcomm" }, { 0, "" } }; const char *vendor = "Unknown"; for (int i = 0; vendors[i].id != 0; i++) { if (vendors[i].id == id) { vendor = vendors[i].name; break; } } return vendor; } #ifndef Q_OS_WINRT static void InitParameters(D3DPRESENT_PARAMETERS* d3dpp) { ZeroMemory(d3dpp, sizeof(*d3dpp)); // use mozilla's parameters d3dpp->Flags = D3DPRESENTFLAG_VIDEO; d3dpp->Windowed = TRUE; d3dpp->hDeviceWindow = ::GetShellWindow(); //NULL; d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; //d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; //d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; d3dpp->BackBufferCount = 1; //0; /* FIXME what to put here */ d3dpp->BackBufferFormat = D3DFMT_UNKNOWN; //D3DFMT_X8R8G8B8; /* FIXME what to put here */ d3dpp->BackBufferWidth = 1; //0; d3dpp->BackBufferHeight = 1; //0; //d3dpp->EnableAutoDepthStencil = FALSE; } IDirect3DDevice9* CreateDevice9Ex(HINSTANCE dll, IDirect3D9Ex** d3d9ex, D3DADAPTER_IDENTIFIER9 *d3dai) { qDebug("creating d3d9 device ex... dll: %p", dll); //http://msdn.microsoft.com/en-us/library/windows/desktop/bb219676(v=vs.85).aspx typedef HRESULT (WINAPI *Create9ExFunc)(UINT SDKVersion, IDirect3D9Ex **ppD3D); //IDirect3D9Ex: void is ok Create9ExFunc Create9Ex = (Create9ExFunc)GetProcAddress(dll, "Direct3DCreate9Ex"); if (!Create9Ex) { qWarning("Symbol not found: Direct3DCreate9Ex"); return NULL; } DX_ENSURE(Create9Ex(D3D_SDK_VERSION, d3d9ex), NULL); //TODO: will D3D_SDK_VERSION be override by other headers? if (d3dai) DX_WARN((*d3d9ex)->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, d3dai)); D3DPRESENT_PARAMETERS d3dpp; InitParameters(&d3dpp); // D3DCREATE_MULTITHREADED is required by gl interop. https://www.opengl.org/registry/specs/NV/DX_interop.txt // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING in cuda samples DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_MIXED_VERTEXPROCESSING; IDirect3DDevice9Ex *d3d9dev = NULL; // mpv: /* Direct3D needs a HWND to create a device, even without using ::Present this HWND is used to alert Direct3D when there's a change of focus window. For now, use GetDesktopWindow, as it looks harmless */ DX_ENSURE((*d3d9ex)->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetShellWindow(),// GetDesktopWindow(), //GetShellWindow()? flags, &d3dpp, NULL, (IDirect3DDevice9Ex**)(&d3d9dev)) , NULL); qDebug("IDirect3DDevice9Ex created"); return d3d9dev; } IDirect3DDevice9* CreateDevice9(HINSTANCE dll, IDirect3D9** d3d9, D3DADAPTER_IDENTIFIER9 *d3dai) { qDebug("creating d3d9 device..."); typedef IDirect3D9* (WINAPI *Create9Func)(UINT SDKVersion); Create9Func Create9 = (Create9Func)GetProcAddress(dll, "Direct3DCreate9"); if (!Create9) { qWarning("Symbol not found: Direct3DCreate9"); return NULL; } *d3d9 = Create9(D3D_SDK_VERSION); if (!(*d3d9)) { qWarning("Direct3DCreate9 failed"); return NULL; } if (d3dai) DX_WARN((*d3d9)->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, d3dai)); D3DPRESENT_PARAMETERS d3dpp; InitParameters(&d3dpp); DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_MIXED_VERTEXPROCESSING; IDirect3DDevice9 *d3d9dev = NULL; DX_ENSURE(((*d3d9)->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetShellWindow(),// GetDesktopWindow(), //GetShellWindow()? flags, &d3dpp, &d3d9dev)) , NULL); qDebug("IDirect3DDevice9 created"); return d3d9dev; } #endif //Q_OS_WINRT } // namespace DXHelper } //namespace QtAV QtAV-1.12.0/src/utils/DirectXHelper.h000066400000000000000000000043101312235004300171770ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_DIRECTXHELPER_H #define QTAV_DIRECTXHELPER_H #include #ifndef Q_OS_WINRT #include #endif namespace QtAV { #ifndef DX_LOG_COMPONENT #define DX_LOG_COMPONENT "DirectX" #endif //DX_LOG_COMPONENT #define DX_ENSURE(f, ...) DX_CHECK(f, return __VA_ARGS__;) #define DX_WARN(f) DX_CHECK(f) #define DX_ENSURE_OK(f, ...) DX_CHECK(f, return __VA_ARGS__;) #define DX_CHECK(f, ...) \ do { \ HRESULT hr = f; \ if (FAILED(hr)) { \ qWarning() << QString::fromLatin1(DX_LOG_COMPONENT " error@%1. " #f ": (0x%2) %3").arg(__LINE__).arg(hr, 0, 16).arg(qt_error_string(hr)); \ __VA_ARGS__ \ } \ } while (0) template void SafeRelease(T **ppT) { if (*ppT) { (*ppT)->Release(); *ppT = NULL; } } namespace DXHelper { const char* vendorName(unsigned id); #ifndef Q_OS_WINRT IDirect3DDevice9* CreateDevice9Ex(HINSTANCE dll, IDirect3D9Ex **d3d9ex, D3DADAPTER_IDENTIFIER9* d3dai = NULL); IDirect3DDevice9* CreateDevice9(HINSTANCE dll, IDirect3D9 **d3d9, D3DADAPTER_IDENTIFIER9* d3dai = NULL); #endif //Q_OS_WINRT } //namespace DXHelper } //namespace QtAV #endif //QTAV_DIRECTXHELPER_H QtAV-1.12.0/src/utils/GPUMemCopy.cpp000066400000000000000000000076551312235004300167740ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "GPUMemCopy.h" #include "QtAV/QtAV_Global.h" #include //memcpy #include extern "C" { #include } #ifndef Q_PROCESSOR_X86 // qt4 #if defined(__SSE__) || defined(_M_IX86) || defined(_M_X64) #define Q_PROCESSOR_X86 #endif #endif // read qsimd_p.h #define UINT unsigned int void CopyFrame_SSE2(void *pSrc, void *pDest, void *pCacheBlock, UINT width, UINT height, UINT pitch); void CopyFrame_SSE4(void *pSrc, void *pDest, void *pCacheBlock, UINT width, UINT height, UINT pitch); void *memcpy_sse2(void* dst, const void* src, size_t size); void *memcpy_sse4(void* dst, const void* src, size_t size); namespace QtAV { bool detect_sse4() { static bool is_sse4 = !!(av_get_cpu_flags() & AV_CPU_FLAG_SSE4); return is_sse4; } bool detect_sse2() { static bool is_sse2 = !!(av_get_cpu_flags() & AV_CPU_FLAG_SSE2); return is_sse2; } bool GPUMemCopy::isAvailable() { #if QTAV_HAVE(SSE4_1) && defined(Q_PROCESSOR_X86) if (detect_sse4()) return true; #endif #if QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) if (detect_sse2()) return true; #endif return false; } GPUMemCopy::GPUMemCopy() : mInitialized(false) { #if QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) mCache.buffer = 0; mCache.size = 0; #endif } GPUMemCopy::~GPUMemCopy() { cleanCache(); } bool GPUMemCopy::isReady() const { return mInitialized && GPUMemCopy::isAvailable(); } #define CACHED_BUFFER_SIZE 4096 bool GPUMemCopy::initCache(unsigned width) { mInitialized = false; #if QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) mCache.size = std::max((width + 0x0f) & ~ 0x0f, CACHED_BUFFER_SIZE); mCache.buffer = (unsigned char*)qMallocAligned(mCache.size, 16); mInitialized = !!mCache.buffer; return mInitialized; #else Q_UNUSED(width); #endif return false; } void GPUMemCopy::cleanCache() { mInitialized = false; #if QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) if (mCache.buffer) { qFreeAligned(mCache.buffer); } mCache.buffer = 0; mCache.size = 0; #endif } void GPUMemCopy::copyFrame(void *pSrc, void *pDest, unsigned width, unsigned height, unsigned pitch) { #if QTAV_HAVE(SSE4_1) && defined(Q_PROCESSOR_X86) if (detect_sse4()) CopyFrame_SSE4(pSrc, pDest, mCache.buffer, width, height, pitch); #elif QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) if (detect_sse2()) CopyFrame_SSE2(pSrc, pDest, mCache.buffer, width, height, pitch); #else Q_UNUSED(pSrc); Q_UNUSED(pDest); Q_UNUSED(width); Q_UNUSED(height); Q_UNUSED(pitch); #endif } void* gpu_memcpy(void *dst, const void *src, size_t size) { #if QTAV_HAVE(SSE4_1) && defined(Q_PROCESSOR_X86) if (detect_sse4()) return memcpy_sse4(dst, src, size); #elif QTAV_HAVE(SSE2) && defined(Q_PROCESSOR_X86) if (detect_sse2()) return memcpy_sse2(dst, src, size); #endif return memcpy(dst, src, size); } } //namespace QtAV QtAV-1.12.0/src/utils/GPUMemCopy.h000066400000000000000000000032661312235004300164330ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef GPUMemCopy_H #define GPUMemCopy_H #include namespace QtAV { class GPUMemCopy { public: static bool isAvailable(); GPUMemCopy(); ~GPUMemCopy(); // available and initialized bool isReady() const; bool initCache(unsigned int width); void cleanCache(); void copyFrame(void *pSrc, void *pDest, unsigned int width, unsigned int height, unsigned int pitch); //memcpy private: bool mInitialized; typedef struct { unsigned char* buffer; size_t size; } cache_t; cache_t mCache; }; void* gpu_memcpy(void* dst, const void* src, size_t size); } //namespace QtAV #endif // GPUMemCopy_H QtAV-1.12.0/src/utils/Logger.cpp000066400000000000000000000205541312235004300162570ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ /*! * DO NOT appear qDebug, qWanring etc in Logger.cpp! They are undefined and redefined to QtAV:Internal::Logger.xxx */ // we need LogLevel so must include QtAV_Global.h #include #include #include "QtAV/QtAV_Global.h" #include "Logger.h" #ifndef QTAV_NO_LOG_LEVEL void ffmpeg_version_print(); namespace QtAV { namespace Internal { static QString gQtAVLogTag = QString(); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) typedef Logger::Context QMessageLogger; #endif static void log_helper(QtMsgType msgType, const QMessageLogger *qlog, const char* msg, va_list ap) { static QMutex m; QMutexLocker lock(&m); Q_UNUSED(lock); static int repeat = 0; static QString last_msg; static QtMsgType last_type = QtDebugMsg; QString qmsg(gQtAVLogTag); QString formated; if (msg) { formated = QString().vsprintf(msg, ap); } // repeate check if (last_type == msgType && last_msg == formated) { repeat++; return; } if (repeat > 0) { // print repeat message and current message qmsg = QStringLiteral("%1(repeat %2)%3\n%4%5") .arg(qmsg).arg(repeat).arg(last_msg).arg(qmsg).arg(formated); } else { qmsg += formated; } repeat = 0; last_type = msgType; last_msg = formated; // qt_message_output is a public api #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) qt_message_output(msgType, qmsg.toUtf8().constData()); return; #else if (!qlog) { QMessageLogContext ctx; qt_message_output(msgType, ctx, qmsg); return; } #endif #ifndef QT_NO_DEBUG_STREAM if (msgType == QtWarningMsg) qlog->warning() << qmsg; else if (msgType == QtCriticalMsg) qlog->critical() << qmsg; else if (msgType == QtFatalMsg) qlog->fatal("%s", qmsg.toUtf8().constData()); else qlog->debug() << qmsg; #else if (msgType == QtFatalMsg) qlog->fatal("%s", qmsg.toUtf8().constData()); #endif // } // macro does not support A::##X void Logger::debug(const char *msg, ...) const { QtAVDebug d; // initialize something. e.g. environment check Q_UNUSED(d); const int v = (int)logLevel(); if (v <= (int)LogOff) return; if (v > (int)LogDebug && v < (int)LogAll) return; va_list ap; va_start(ap, msg); // can not use ctx.debug() <<... because QT_NO_DEBUG_STREAM maybe defined log_helper(QtDebugMsg, &ctx, msg, ap); va_end(ap); } void Logger::warning(const char *msg, ...) const { QtAVDebug d; // initialize something. e.g. environment check Q_UNUSED(d); const int v = (int)logLevel(); if (v <= (int)LogOff) return; if (v > (int)LogWarning && v < (int)LogAll) return; va_list ap; va_start(ap, msg); log_helper(QtWarningMsg, &ctx, msg, ap); va_end(ap); } void Logger::critical(const char *msg, ...) const { QtAVDebug d; // initialize something. e.g. environment check Q_UNUSED(d); const int v = (int)logLevel(); if (v <= (int)LogOff) return; if (v > (int)LogCritical && v < (int)LogAll) return; va_list ap; va_start(ap, msg); log_helper(QtCriticalMsg, &ctx, msg, ap); va_end(ap); } void Logger::fatal(const char *msg, ...) const Q_DECL_NOTHROW { QtAVDebug d; // initialize something. e.g. environment check Q_UNUSED(d); const int v = (int)logLevel(); /* if (v <= (int)LogOff) abort(); if (v > (int)LogFatal && v < (int)LogAll) abort(); */ if (v > (int)LogOff) { va_list ap; va_start(ap, msg); log_helper(QtFatalMsg, &ctx, msg, ap); va_end(ap); } abort(); } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifndef QT_NO_DEBUG_STREAM // internal used by Logger::fatal(const char*,...) with log level checked, so always do things here void Logger::Context::fatal(const char *msg, ...) const Q_DECL_NOTHROW { va_list ap; va_start(ap, msg); QString qmsg; if (msg) qmsg += QString().vsprintf(msg, ap); qt_message_output(QtFatalMsg, qmsg.toUtf8().constData()); va_end(ap); abort(); } #endif //QT_NO_DEBUG_STREAM #endif //QT_VERSION #ifndef QT_NO_DEBUG_STREAM // will print message in ~QDebug() // can not use QDebug on stack. It must lives in QtAVDebug QtAVDebug Logger::debug() const { QtAVDebug d(QtDebugMsg); //// initialize something. e.g. environment check const int v = (int)logLevel(); if (v <= (int)LogOff) return d; if (v <= (int)LogDebug || v >= (int)LogAll) d.setQDebug(new QDebug(ctx.debug())); return d; //ref > 0 } QtAVDebug Logger::warning() const { QtAVDebug d(QtWarningMsg); const int v = (int)logLevel(); if (v <= (int)LogOff) return d; if (v <= (int)LogWarning || v >= (int)LogAll) d.setQDebug(new QDebug(ctx.warning())); return d; } QtAVDebug Logger::critical() const { QtAVDebug d(QtCriticalMsg); const int v = (int)logLevel(); if (v <= (int)LogOff) return d; if (v <= (int)LogCritical || v >= (int)LogAll) d.setQDebug(new QDebug(ctx.critical())); return d; } // no QMessageLogger::fatal() #endif //QT_NO_DEBUG_STREAM bool isLogLevelSet(); void print_library_info(); QtAVDebug::QtAVDebug(QtMsgType t, QDebug *d) : type(t) , dbg(0) { if (d) setQDebug(d); // call *dbg << gQtAVLogTag static bool sFirstRun = true; if (!sFirstRun) return; sFirstRun = false; printf("%s\n", aboutQtAV_PlainText().toUtf8().constData()); // check environment var and call other functions at first Qt logging call // always override setLogLevel() QByteArray env = qgetenv("QTAV_LOG_LEVEL"); if (env.isEmpty()) env = qgetenv("QTAV_LOG"); if (!env.isEmpty()) { bool ok = false; const int level = env.toInt(&ok); if (ok) { if (level < (int)LogOff) setLogLevel(LogOff); else if (level > (int)LogAll) setLogLevel(LogAll); else setLogLevel((LogLevel)level); } else { env = env.toLower(); if (env.endsWith("off")) setLogLevel(LogOff); else if (env.endsWith("debug")) setLogLevel(LogDebug); else if (env.endsWith("warning")) setLogLevel(LogWarning); else if (env.endsWith("critical")) setLogLevel(LogCritical); else if (env.endsWith("fatal")) setLogLevel(LogFatal); else if (env.endsWith("all") || env.endsWith("default")) setLogLevel(LogAll); } } env = qgetenv("QTAV_LOG_TAG"); if (!env.isEmpty()) { gQtAVLogTag = QString::fromUtf8(env); } if ((int)logLevel() > (int)LogOff) { print_library_info(); } } QtAVDebug::~QtAVDebug() { } void QtAVDebug::setQDebug(QDebug *d) { dbg = QSharedPointer(d); if (dbg && !gQtAVLogTag.isEmpty()) { *dbg << gQtAVLogTag; } } #if 0 QtAVDebug debug(const char *msg, ...) { if ((int)logLevel() > (int)Debug && logLevel() != All) { return QtAVDebug(); } va_list ap; va_start(ap, msg); // use variable arg list QMessageLogContext ctx; log_helper(QtDebugMsg, &ctx, msg, ap); va_end(ap); return QtAVDebug(); } #endif } //namespace Internal } // namespace QtAV #endif //QTAV_NO_LOG_LEVEL QtAV-1.12.0/src/utils/Logger.h000066400000000000000000000165211312235004300157230ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2013) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ /* * Qt Logging Hack * in qmacdefines_mac.h, if qDebug is defined, then if will be defined as QT_NO_QDEBUG_MACRO, don't know why * So you have to include this only as the last #include in cpp file! * DO NOT use qDebug in public header! */ #ifndef QTAV_LOGGER_H #define QTAV_LOGGER_H /*! Environment var QTAV_LOG_TAG: prefix the value to log message QTAV_LOG_LEVEL: set log level, can be "off", "debug", "warning", "critical", "fatal", "all" */ #include //always include #ifndef QTAV_NO_LOG_LEVEL #include #include #ifndef Q_DECL_CONSTEXPR #define Q_DECL_CONSTEXPR #endif //Q_DECL_CONSTEXPR #ifndef Q_DECL_NOTHROW #define Q_DECL_NOTHROW #endif //Q_DECL_NOTHROW #ifndef Q_ATTRIBUTE_FORMAT_PRINTF #define Q_ATTRIBUTE_FORMAT_PRINTF(...) #endif //Q_ATTRIBUTE_FORMAT_PRINTF #ifndef Q_NORETURN #define Q_NORETURN #endif #ifndef Q_FUNC_INFO #define Q_FUNC_INFO __FUNCTION__ #endif namespace QtAV { namespace Internal { // internal use when building QtAV library class QtAVDebug { public: /*! * \brief QtAVDebug * QDebug can be copied from QMessageLogger or others. take the ownership of d * \param d nothing will be logged and t is ignored if null */ QtAVDebug(QtMsgType t = QtDebugMsg, QDebug *d = 0); ~QtAVDebug(); void setQDebug(QDebug* d); // QDebug api inline QtAVDebug &space() { if (dbg) dbg->space(); return *this; } inline QtAVDebug &nospace() { if (dbg) dbg->nospace(); return *this; } inline QtAVDebug &maybeSpace() { if (dbg) dbg->maybeSpace(); return *this; } template QtAVDebug &operator<<(T t) { if (!dbg) return *this; const int l = (int)logLevel(); if (l <= (int)LogOff) return *this; if (l >= (int)LogAll) { *dbg << t; return *this; } if (l == (int)LogDebug) { *dbg << t; return *this; } if (l == (int)LogWarning) { if ((int)type >= (int)QtWarningMsg) *dbg << t; return *this; } if (l == (int)LogCritical) { if ((int)type >= (int)QtCriticalMsg) *dbg << t; return *this; } if (l == (int)LogFatal) { if ((int)type >= (int)QtFatalMsg) *dbg << t; return *this; } return *this; } private: QtMsgType type; // use ptr. otherwise ~QDebug() will print message. QSharedPointer dbg; }; class Logger { Q_DISABLE_COPY(Logger) public:Q_DECL_CONSTEXPR Logger(const char *file = "unknown", int line = 0, const char *function = "unknown", const char *category = "default") : ctx(file, line, function, category) {} void debug(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3); void noDebug(const char *, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3) {} void warning(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3); void critical(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3); //TODO: QLoggingCategory #ifndef Q_CC_MSVC Q_NORETURN #endif void fatal(const char *msg, ...) const Q_DECL_NOTHROW Q_ATTRIBUTE_FORMAT_PRINTF(2, 3); #ifndef QT_NO_DEBUG_STREAM //TODO: QLoggingCategory QtAVDebug debug() const; QtAVDebug warning() const; QtAVDebug critical() const; //QtAVDebug fatal() const; QNoDebug noDebug() const Q_DECL_NOTHROW; #endif // QT_NO_DEBUG_STREAM #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) public: //public can typedef outside class Context { Q_DISABLE_COPY(Context) public: Q_DECL_CONSTEXPR Context(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) : version(1), line(lineNumber), file(fileName), function(functionName), category(categoryName) {} #ifndef Q_CC_MSVC Q_NORETURN #endif void fatal(const char *msg, ...) const Q_DECL_NOTHROW Q_ATTRIBUTE_FORMAT_PRINTF(2, 3); #ifndef QT_NO_DEBUG_STREAM inline QDebug debug() const { return QDebug(QtDebugMsg);} inline QDebug warning() const { return QDebug(QtWarningMsg);} inline QDebug critical() const { return QDebug(QtCriticalMsg);} inline QDebug fatal() const { return QDebug(QtFatalMsg);} #endif //QT_NO_DEBUG_STREAM int version; int line; const char *file; const char *function; const char *category; }; private: Context ctx; #else private: QMessageLogger ctx; #endif }; //simple way #if 0 #undef qDebug #define qDebug(fmt, ...) QtAV::Logger::debug(#fmt, ##__VA_ARGS__) #undef qDebug #if !defined(QT_NO_DEBUG_STREAM) QtAVDebug debug(const char *msg, ...); /* print debug message */ #else // QT_NO_DEBUG_STREAM #undef qDebug QNoDebug debug(const char *msg, ...); /* print debug message */ #define qDebug QT_NO_QDEBUG_MACRO #endif //QT_NO_DEBUG_STREAM #endif //0 // complex way like Qt5. can use Qt5's logging context features // including qDebug() << ... //qDebug() Log(__FILE__, __LINE__, Q_FUNC_INFO).debug // DO NOT appear qDebug, qWanring etc in Logger.cpp! They are undefined and redefined to QtAV:Internal::Logger.xxx #undef qDebug //was defined as QMessageLogger... #undef qWarning #undef qCritical #undef qFatal // debug and warning output can be disabled at build time in Qt // from qdebug.h #ifdef QT_NO_DEBUG_OUTPUT #define QT_NO_WARNING_OUTPUT ////FIXME. qWarning() => Logger.warning() not declared #undef qDebug inline QNoDebug qDebug() { return QNoDebug(); } #define qDebug QT_NO_QDEBUG_MACRO #else inline QtAVDebug qDebug() { return QtAVDebug(QtDebugMsg); } #define qDebug QtAV::Internal::Logger(__FILE__, __LINE__, Q_FUNC_INFO).debug #endif //QT_NO_DEBUG_OUTPUT #ifdef QT_NO_WARNING_OUTPUT #undef qWarning inline QNoDebug qWarning() { return QNoDebug(); } #define qWarning QT_NO_QWARNING_MACRO #else inline QtAVDebug qWarning() { return QtAVDebug(QtWarningMsg); } #define qWarning QtAV::Internal::Logger(__FILE__, __LINE__, Q_FUNC_INFO).warning #endif //QT_NO_WARNING_OUTPUT #define qCritical QtAV::Internal::Logger(__FILE__, __LINE__, Q_FUNC_INFO).critical #define qFatal QtAV::Internal::Logger(__FILE__, __LINE__, Q_FUNC_INFO).fatal } // namespace Internal } // namespace QtAV #endif //QTAV_NO_LOG_LEVEL #endif // QTAV_LOGGER_H QtAV-1.12.0/src/utils/SharedPtr.h000066400000000000000000000066321312235004300164020ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef SHARED_PTR #define SHARED_PTR #include #include /* * a simple thread safe shared ptr. QSharedPointer does not provide a way to get how many the ref count is. */ namespace impl { template class SharedPtrImpl { public: explicit SharedPtrImpl(T *ptr = 0) : m_ptr(ptr), m_counter(1) { } ~SharedPtrImpl() { delete m_ptr;} T * operator->() const { return m_ptr;} T & operator*() const { return *m_ptr;} T * get() const { return m_ptr;} void ref() { m_counter.ref(); } // return false if becomes 0 bool deref() { return m_counter.deref();} int count() const { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) return m_counter; #else return m_counter.load(); #endif } bool isNull() const { return !m_ptr;} private: T *m_ptr; QAtomicInt m_counter; }; } // namespace impl template class SharedPtr { public: explicit SharedPtr(T *ptr = 0) : m_impl(new impl::SharedPtrImpl(ptr)) { } template explicit SharedPtr(U *ptr = 0) : m_impl(new impl::SharedPtrImpl(ptr)) { } ~SharedPtr() { if (!m_impl->deref()) delete m_impl; } SharedPtr(const SharedPtr &other) { m_impl = other.m_impl; m_impl->ref(); } template SharedPtr(const SharedPtr &other) { m_impl = other.m_impl; m_impl->ref(); } SharedPtr & operator=(const SharedPtr &other) { SharedPtr tmp(other); swap(tmp); return *this; } template SharedPtr & operator=(const SharedPtr &other) { SharedPtr tmp(other); swap(tmp); return *this; } T * operator->() const { return m_impl->operator->();} T & operator*() const { return **m_impl;} bool operator !() const { return isNull();} template bool operator<(const SharedPtr &other) { return m_impl->get() < other.get();} bool isNull() const { return m_impl->isNull();} T * get() const { return m_impl->get();} int count() const { return m_impl->count();} void swap(SharedPtr &other) { std::swap(m_impl, other.m_impl);} private: impl::SharedPtrImpl *m_impl; }; template void swap(SharedPtr &left, SharedPtr &right) { left.swap(right); } #endif //SHARED_PTR QtAV-1.12.0/src/utils/internal.cpp000066400000000000000000000305341312235004300166530ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "internal.h" #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif #ifdef Q_OS_MAC #include #endif #include "QtAV/private/AVCompat.h" #include "utils/Logger.h" namespace QtAV { static const char kFileScheme[] = "file:"; #define CHAR_COUNT(s) (sizeof(s) - 1) // tail '\0' #ifdef Q_OS_MAC static QString fromCFString(CFStringRef string) { #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) return QString().fromCFString(string); #else if (!string) return QString(); CFIndex length = CFStringGetLength(string); // Fast path: CFStringGetCharactersPtr does not copy but may return null for any and no reason. const UniChar *chars = CFStringGetCharactersPtr(string); if (chars) return QString(reinterpret_cast(chars), length); QString ret(length, Qt::Uninitialized); CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast(ret.data())); return ret; #endif } QString absolutePathFromOSX(const QString& s) { QString result; #if !(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060) CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault, s.toUtf8().constData(), kCFStringEncodingUTF8); if (cfStr) { CFURLRef cfUrl = CFURLCreateWithString(kCFAllocatorDefault, cfStr, NULL); if (cfUrl) { CFErrorRef error = 0; CFURLRef cfUrlAbs = CFURLCreateFilePathURL(kCFAllocatorDefault, cfUrl, &error); if (cfUrlAbs) { CFStringRef cfStrAbsUrl = CFURLGetString(cfUrlAbs); result = fromCFString(cfStrAbsUrl); CFRelease(cfUrlAbs); } CFRelease(cfUrl); } CFRelease(cfStr); } #else Q_UNUSED(s); #endif return result; } #endif //Q_OS_MAC /*! * \brief getLocalPath * get path that works for both ffmpeg and QFile * Windows: ffmpeg does not supports file:///C:/xx.mov, only supports file:C:/xx.mov or C:/xx.mov * QFile: does not support file: scheme * fullPath can be file:///path from QUrl. QUrl.toLocalFile will remove file:// */ QString getLocalPath(const QString& fullPath) { #ifdef Q_OS_MAC if (fullPath.startsWith(QLatin1String("file:///.file/id=")) || fullPath.startsWith(QLatin1String("/.file/id="))) return absolutePathFromOSX(fullPath); #endif int pos = fullPath.indexOf(QLatin1String(kFileScheme)); if (pos >= 0) { pos += CHAR_COUNT(kFileScheme); bool has_slash = false; while (fullPath.at(pos) == QLatin1Char('/')) { has_slash = true; ++pos; } // win: ffmpeg does not supports file:///C:/xx.mov, only supports file:C:/xx.mov or C:/xx.mov #ifndef Q_OS_WIN // for QUrl if (has_slash) --pos; #endif } // always remove "file:" even thought it works for ffmpeg.but fileName() may be used for QFile which does not file: if (pos > 0) return fullPath.mid(pos); return fullPath; } #undef CHAR_COUNT namespace Internal { namespace Path { QString toLocal(const QString &fullPath) { return getLocalPath(fullPath); } QString appDataDir() { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) return QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) return QStandardPaths::writableLocation(QStandardPaths::DataLocation); #else return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); #endif //5.4.0 #endif // 5.0.0 } QString appFontsDir() { #if 0 //qt may return an read only path, for example OSX /System/Library/Fonts #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QString dir(QStandardPaths::writableLocation(QStandardPaths::FontsLocation)); if (!dir.isEmpty()) return dir; #endif #endif return appDataDir() + QStringLiteral("/fonts"); } QString fontsDir() { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) return QDesktopServices::storageLocation(QDesktopServices::FontsLocation); #else return QStandardPaths::standardLocations(QStandardPaths::FontsLocation).first(); #endif } } //namespace Path QString options2StringHelper(void* obj, const char* unit) { qDebug("obj: %p", obj); QString s; const AVOption* opt = NULL; while ((opt = av_opt_next(obj, opt))) { if (opt->type == AV_OPT_TYPE_CONST) { if (!unit) continue; if (!qstrcmp(unit, opt->unit)) s.append(QStringLiteral(" %1=%2").arg(QLatin1String(opt->name)).arg(opt->default_val.i64)); continue; } else { if (unit) continue; } s.append(QStringLiteral("\n%1: ").arg(QLatin1String(opt->name))); switch (opt->type) { case AV_OPT_TYPE_FLAGS: case AV_OPT_TYPE_INT: case AV_OPT_TYPE_INT64: s.append(QStringLiteral("(%1)").arg(opt->default_val.i64)); break; case AV_OPT_TYPE_DOUBLE: case AV_OPT_TYPE_FLOAT: s.append(QStringLiteral("(%1)").arg(opt->default_val.dbl, 0, 'f')); break; case AV_OPT_TYPE_STRING: if (opt->default_val.str) s.append(QStringLiteral("(%1)").arg(QString::fromUtf8(opt->default_val.str))); break; case AV_OPT_TYPE_RATIONAL: s.append(QStringLiteral("(%1/%2)").arg(opt->default_val.q.num).arg(opt->default_val.q.den)); break; default: break; } if (opt->help) s.append(QLatin1String(" ")).append(QString::fromUtf8(opt->help)); if (opt->unit && opt->type != AV_OPT_TYPE_CONST) s.append(QLatin1String("\n ")).append(options2StringHelper(obj, opt->unit)); } return s; } QString optionsToString(void* obj) { return options2StringHelper(obj, NULL); } void setOptionsToFFmpegObj(const QVariant& opt, void* obj) { if (!opt.isValid()) return; AVClass *c = obj ? *(AVClass**)obj : 0; if (c) qDebug() << QStringLiteral("%1.%2 options:").arg(QLatin1String(c->class_name)).arg(QLatin1String(c->item_name(obj))); else qDebug() << "options:"; if (opt.type() == QVariant::Map) { QVariantMap options(opt.toMap()); if (options.isEmpty()) return; QMapIterator i(options); while (i.hasNext()) { i.next(); const QVariant::Type vt = i.value().type(); if (vt == QVariant::Map) continue; const QByteArray key(i.key().toUtf8()); qDebug("%s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); if (vt == QVariant::Int || vt == QVariant::UInt || vt == QVariant::Bool) { // QVariant.toByteArray(): "true" or "false", can not recognized by avcodec av_opt_set_int(obj, key.constData(), i.value().toInt(), AV_OPT_SEARCH_CHILDREN); } else if (vt == QVariant::LongLong || vt == QVariant::ULongLong) { av_opt_set_int(obj, key.constData(), i.value().toLongLong(), AV_OPT_SEARCH_CHILDREN); } else if (vt == QVariant::Double) { av_opt_set_double(obj, key.constData(), i.value().toDouble(), AV_OPT_SEARCH_CHILDREN); } } return; } QVariantHash options(opt.toHash()); if (options.isEmpty()) return; QHashIterator i(options); while (i.hasNext()) { i.next(); const QVariant::Type vt = i.value().type(); if (vt == QVariant::Hash) continue; const QByteArray key(i.key().toUtf8()); qDebug("%s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); if (vt == QVariant::Int || vt == QVariant::UInt || vt == QVariant::Bool) { av_opt_set_int(obj, key.constData(), i.value().toInt(), AV_OPT_SEARCH_CHILDREN); } else if (vt == QVariant::LongLong || vt == QVariant::ULongLong) { av_opt_set_int(obj, key.constData(), i.value().toLongLong(), AV_OPT_SEARCH_CHILDREN); } } } //FIXME: why to lower case? void setOptionsToDict(const QVariant& opt, AVDictionary** dict) { if (!opt.isValid()) return; if (opt.type() == QVariant::Map) { QVariantMap options(opt.toMap()); if (options.isEmpty()) return; QMapIterator i(options); while (i.hasNext()) { i.next(); const QVariant::Type vt = i.value().type(); if (vt == QVariant::Map) continue; const QByteArray key(i.key().toUtf8()); switch (vt) { case QVariant::Bool: { // QVariant.toByteArray(): "true" or "false", can not recognized by avcodec av_dict_set(dict, key.constData(), QByteArray::number(i.value().toInt()), 0); } break; default: // key and value are in lower case av_dict_set(dict, i.key().toUtf8().constData(), i.value().toByteArray().constData(), 0); break; } qDebug("dict: %s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); } return; } QVariantHash options(opt.toHash()); if (options.isEmpty()) return; QHashIterator i(options); while (i.hasNext()) { i.next(); const QVariant::Type vt = i.value().type(); if (vt == QVariant::Hash) continue; const QByteArray key(i.key().toUtf8()); switch (vt) { case QVariant::Bool: { // QVariant.toByteArray(): "true" or "false", can not recognized by avcodec av_dict_set(dict, key.constData(), QByteArray::number(i.value().toInt()), 0); } break; default: // key and value are in lower case av_dict_set(dict, i.key().toUtf8().constData(), i.value().toByteArray().constData(), 0); break; } qDebug("dict: %s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); } } void setOptionsForQObject(const QVariant& opt, QObject *obj) { if (!opt.isValid()) return; qDebug() << QStringLiteral("set %1(%2) meta properties:").arg(QLatin1String(obj->metaObject()->className())).arg(obj->objectName()); if (opt.type() == QVariant::Hash) { QVariantHash options(opt.toHash()); if (options.isEmpty()) return; QHashIterator i(options); while (i.hasNext()) { i.next(); if (i.value().type() == QVariant::Hash) // for example "vaapi": {...} continue; obj->setProperty(i.key().toUtf8().constData(), i.value()); qDebug("%s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); } } if (opt.type() != QVariant::Map) return; QVariantMap options(opt.toMap()); if (options.isEmpty()) return; QMapIterator i(options); while (i.hasNext()) { i.next(); if (i.value().type() == QVariant::Map) // for example "vaapi": {...} continue; obj->setProperty(i.key().toUtf8().constData(), i.value()); qDebug("%s=>%s", i.key().toUtf8().constData(), i.value().toByteArray().constData()); } } } //namespace Internal } //namespace QtAV QtAV-1.12.0/src/utils/internal.h000066400000000000000000000045641312235004300163240ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_INTERNAL_H #define QTAV_INTERNAL_H #include #include #include #include "QtAV/private/AVCompat.h" namespace QtAV { namespace Internal { namespace Path { QString toLocal(const QString& fullPath); /// It may be variant from Qt version /*! * \brief appDataDir * use QStandardPath::AppDataLocation for Qt>=5.4, or QStarnardPath::DataLocation for Qt<5.4 * \return */ QString appDataDir(); // writable font dir. it's fontsDir() if writable or appFontsDir()/fonts /*! * \brief appFontsDir * It's "appDataDir()/fonts". writable fonts dir from Qt may be not writable (OSX) * TODO: It's a writable fonts dir for QtAV apps. Equals to fontsDir() if it's writable, for example "~/.fonts" for linux and "/Documents/.fonts" for iOS. * If fontsDir() is not writable, it's equals to "appDataDir()/fonts". */ QString appFontsDir(); // usually not writable. Maybe empty for some platforms, for example winrt QString fontsDir(); } // TODO: use namespace Options QString optionsToString(void* obj); void setOptionsToFFmpegObj(const QVariant& opt, void* obj); void setOptionsToDict(const QVariant& opt, AVDictionary** dict); // set qobject meta properties void setOptionsForQObject(const QVariant& opt, QObject* obj); } //namespace Internal } //namespace QtAV #endif //QTAV_INTERNAL_H QtAV-1.12.0/src/utils/ring.h000066400000000000000000000061021312235004300154350ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_RING_H #define QTAV_RING_H #include #include namespace QtAV { template class ring_api { public: ring_api() : m_0(0), m_1(0), m_s(0) {} void push_back(const T &t); void pop_front(); T &front() { return m_data[m_0]; } const T &front() const { return m_data[m_0]; } T &back() { return m_data[m_1]; } const T &back() const { return m_data[m_1]; } virtual size_t capacity() const = 0; size_t size() const { return m_s;} bool empty() const { return size() == 0;} // need at() []? const T &at(size_t i) const { assert(i < m_s); return m_data[index(m_0+i)];} const T &operator[](size_t i) const { return at(i);} T &operator[](size_t i) {assert(i < m_s); return m_data[index(m_0+i)];} protected: size_t index(size_t i) const { return i < capacity() ? i : i - capacity();} // i always [0,capacity()) size_t m_0, m_1; size_t m_s; C m_data; }; template class ring : public ring_api > { using ring_api >::m_data; // why need this? public: ring(size_t capacity) : ring_api >() { m_data.reserve(capacity); m_data.resize(capacity); } size_t capacity() const {return m_data.size();} }; template class static_ring : public ring_api { using ring_api::m_data; // why need this? public: static_ring() : ring_api() {} size_t capacity() const {return N;} }; template void ring_api::push_back(const T &t) { if (m_s == capacity()) { m_data[m_0] = t; m_0 = index(++m_0); m_1 = index(++m_1); } else if (empty()) { m_s = 1; m_0 = m_1 = 0; m_data[m_0] = t; } else { m_data[index(m_0 + m_s)] = t; ++m_1; ++m_s; } } template void ring_api::pop_front() { assert(!empty()); if (empty()) return; m_data[m_0] = T(); //erase the old data m_0 = index(++m_0); --m_s; } } //namespace QtAV #endif // QTAV_RING_H QtAV-1.12.0/src/vaapi/000077500000000000000000000000001312235004300142665ustar00rootroot00000000000000QtAV-1.12.0/src/vaapi/SurfaceInteropVAAPI.cpp000066400000000000000000000475251312235004300205210ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ /// The EGL+DMA part is from mpv and under LGPL #include "SurfaceInteropVAAPI.h" #ifndef QT_NO_OPENGL #include "opengl/OpenGLHelper.h" #include "QtAV/VideoFrame.h" #include "utils/Logger.h" #if VA_X11_INTEROP #ifdef QT_X11EXTRAS_LIB #include #endif //QT_X11EXTRAS_LIB #include #if QTAV_HAVE(EGL_CAPI) #define EGL_CAPI_NS #include "capi/egl_api.h" #include #if VA_CHECK_VERSION(0, 33, 0) #include #else /** \brief Kernel DRM buffer memory type. */ #define VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM 0x10000000 /** \brief DRM PRIME memory type. */ #define VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME 0x20000000 #endif #endif //QTAV_HAVE(EGL_CAPI) #if !defined(QT_OPENGL_ES_2) #include #endif //!defined(QT_OPENGL_ES_2) #endif //VA_X11_INTEROP namespace QtAV { VideoFormat::PixelFormat pixelFormatFromVA(uint32_t fourcc); namespace vaapi { //2013 https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt bool checkEGL_DMA() { #ifndef QT_NO_OPENGL static const char* eglexts[] = { "EGL_EXT_image_dma_buf_import", NULL}; return OpenGLHelper::hasExtensionEGL(eglexts); #endif return false; } //https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_image_pixmap.txt bool checkEGL_Pixmap() { #ifndef QT_NO_OPENGL static const char* eglexts[] = { "EGL_KHR_image_pixmap", NULL}; return OpenGLHelper::hasExtensionEGL(eglexts); #endif return false; } void SurfaceInteropVAAPI::setSurface(const surface_ptr& surface, int w, int h) { m_surface = surface; frame_width = (w ? w : surface->width()); frame_height = (h ? h : surface->height()); } void* SurfaceInteropVAAPI::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane) { if (!handle) return NULL; // if (!fmt.isRGB()) // return 0; if (!m_surface) return 0; if (type == GLTextureSurface) { if (m_resource->map(m_surface, *((GLuint*)handle), frame_width, frame_height, plane)) return handle; return NULL; } else if (type == HostMemorySurface) { return mapToHost(fmt, handle, plane); } return NULL; } void SurfaceInteropVAAPI::unmap(void *handle) { m_resource->unmap(m_surface, *((GLuint*)handle)); } void* SurfaceInteropVAAPI::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); VAImage image; static const unsigned int fcc[] = { VA_FOURCC_NV12, VA_FOURCC_YV12, VA_FOURCC_IYUV, 0}; va_new_image(m_surface->vadisplay(), fcc, &image, m_surface->width(), m_surface->height()); if (image.image_id == VA_INVALID_ID) return NULL; void *p_base; VA_ENSURE(vaGetImage(m_surface->vadisplay(), m_surface->get(), 0, 0, m_surface->width(), m_surface->height(), image.image_id), NULL); VA_ENSURE(vaMapBuffer(m_surface->vadisplay(), image.buf, &p_base), NULL); //TODO: destroy image before return VideoFormat::PixelFormat pixfmt = pixelFormatFromVA(image.format.fourcc); bool swap_uv = image.format.fourcc != VA_FOURCC_NV12; if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", image.format.fourcc); VA_ENSURE(vaDestroyImage(m_surface->vadisplay(), image.image_id), NULL); return NULL; } const VideoFormat fmt(pixfmt); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { src[i] = (uint8_t*)p_base + image.offsets[i]; pitch[i] = image.pitches[i]; } VideoFrame frame = VideoFrame::fromGPU(fmt, frame_width, frame_height, m_surface->height(), src, pitch, true, swap_uv); if (format != fmt) frame = frame.to(format); VAWARN(vaUnmapBuffer(m_surface->vadisplay(), image.buf)); VAWARN(vaDestroyImage(m_surface->vadisplay(), image.image_id)); image.image_id = VA_INVALID_ID; VideoFrame *f = reinterpret_cast(handle); frame.setTimestamp(f->timestamp()); *f = frame; return f; } #ifndef QT_NO_OPENGL surface_glx_ptr GLXInteropResource::surfaceGLX(const display_ptr &dpy, GLuint tex) { surface_glx_ptr glx = glx_surfaces[tex]; if (glx) return glx; glx = surface_glx_ptr(new surface_glx_t(dpy)); if (!glx->create(tex)) return surface_glx_ptr(); glx_surfaces[tex] = glx; return glx; } bool GLXInteropResource::map(const surface_ptr& surface, GLuint tex, int w, int h, int) { Q_UNUSED(w); Q_UNUSED(h); surface_glx_ptr glx = surfaceGLX(surface->display(), tex); if (!glx) { qWarning("Fail to create vaapi glx surface"); return false; } if (!glx->copy(surface)) return false; VAWARN(vaSyncSurface(surface->vadisplay(), surface->get())); return true; } #endif //QT_NO_OPENGL #if VA_X11_INTEROP class X11 { protected: Display *display; public: X11() : display(NULL), pixmap(0) {} virtual ~X11() { if (pixmap) XFreePixmap((::Display*)display, pixmap); pixmap = 0; } virtual Display* ensureGL() = 0; virtual bool bindPixmap(int w, int h) = 0; virtual void bindTexture() = 0; Pixmap pixmap; protected: int createPixmap(int w, int h) { if (pixmap) { qDebug("XFreePixmap"); XFreePixmap((::Display*)display, pixmap); pixmap = 0; } XWindowAttributes xwa; XGetWindowAttributes((::Display*)display, DefaultRootWindow((::Display*)display), &xwa); pixmap = XCreatePixmap((::Display*)display, DefaultRootWindow((::Display*)display), w, h, xwa.depth); // mpv always use 24 bpp qDebug("XCreatePixmap %lu: %dx%d, depth: %d", pixmap, w, h, xwa.depth); if (!pixmap) { qWarning("X11InteropResource could not create pixmap"); return 0; } return xwa.depth; } }; #define RESOLVE_FUNC(func, resolver, st) do {\ if (!func) { \ typedef void (*ft)(void); \ ft f = resolver((st)#func); \ if (!f) { \ qWarning(#func " is not available"); \ return 0; \ } \ ft *fp = (ft*)(&func); \ *fp = f; \ }} while(0) #define RESOLVE_EGL(func) RESOLVE_FUNC(func, eglGetProcAddress, const char*) #define RESOLVE_GLX(func) RESOLVE_FUNC(func, glXGetProcAddressARB, const GLubyte*) #if QTAV_HAVE(EGL_CAPI) //static PFNEGLQUERYNATIVEDISPLAYNVPROC eglQueryNativeDisplayNV = NULL; static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = NULL; static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = NULL; #ifndef GL_OES_EGL_image typedef void *GLeglImageOES; typedef void (EGLAPIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); #endif static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = NULL; class X11_EGL Q_DECL_FINAL: public X11 { public: X11_EGL() : dpy(EGL_NO_DISPLAY) { for (unsigned i = 0; i < sizeof(image)/sizeof(image[0]); ++i) image[i] = EGL_NO_IMAGE_KHR; } ~X11_EGL() { for (unsigned i = 0; i < sizeof(image)/sizeof(image[0]); ++i) { if (image[i] != EGL_NO_IMAGE_KHR) { EGL_WARN(eglDestroyImageKHR(dpy, image[i])); } } } Display* ensureGL() Q_DECL_OVERRIDE { if (display && eglCreateImageKHR && glEGLImageTargetTexture2DOES) return display; // we must use the native display egl used, otherwise eglCreateImageKHR will fail. #ifdef QT_X11EXTRAS_LIB display = (Display*)QX11Info::display(); #endif //QT_X11EXTRAS_LIB //RESOLVE_EGL(eglQueryNativeDisplayNV); RESOLVE_EGL(glEGLImageTargetTexture2DOES); RESOLVE_EGL(eglCreateImageKHR); RESOLVE_EGL(eglDestroyImageKHR); return display; } bool bindPixmap(int w, int h) Q_DECL_OVERRIDE { if (!createPixmap(w, h)) return false; if (dpy == EGL_NO_DISPLAY) { qDebug("eglGetCurrentDisplay"); dpy = eglGetCurrentDisplay(); } EGL_ENSURE(image[0] = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)pixmap, NULL), false); return true; } void bindTexture() Q_DECL_OVERRIDE { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image[0]); } EGLDisplay dpy; EGLImageKHR image[4]; }; #endif //QTAV_HAVE(EGL_CAPI) #if !defined(QT_OPENGL_ES_2) typedef void (*glXBindTexImageEXT_t)(Display *dpy, GLXDrawable draw, int buffer, int *a); typedef void (*glXReleaseTexImageEXT_t)(Display *dpy, GLXDrawable draw, int buffer); static glXReleaseTexImageEXT_t glXReleaseTexImageEXT = 0; static glXBindTexImageEXT_t glXBindTexImageEXT = 0; class X11_GLX Q_DECL_FINAL: public X11 { public: X11_GLX() : fbc(0), glxpixmap(0) {} ~X11_GLX() { if (glxpixmap) { //TODO: does the thread matters? glXReleaseTexImageEXT(display, glxpixmap, GLX_FRONT_EXT); XSync((::Display*)display, False); glXDestroyPixmap((::Display*)display, glxpixmap); } glxpixmap = 0; } Display* ensureGL() Q_DECL_OVERRIDE { if (fbc && display) return display; if (!display) { qDebug("glXGetCurrentDisplay"); display = (Display*)glXGetCurrentDisplay(); // use for all and not x11info if (!display) return 0; } int xscr = DefaultScreen(display); const char *glxext = glXQueryExtensionsString((::Display*)display, xscr); if (!glxext || !strstr(glxext, "GLX_EXT_texture_from_pixmap")) return 0; RESOLVE_GLX(glXBindTexImageEXT); RESOLVE_GLX(glXReleaseTexImageEXT); int attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, //xbmc GLX_X_RENDERABLE, True, //xbmc GLX_BIND_TO_TEXTURE_RGBA_EXT, True, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, GLX_Y_INVERTED_EXT, True, GLX_DOUBLEBUFFER, False, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, //0 for 24 bpp(vdpau)? mpv is 0 None }; int fbcount; GLXFBConfig *fbcs = glXChooseFBConfig((::Display*)display, xscr, attribs, &fbcount); if (!fbcount) { qWarning("No texture-from-pixmap support"); return 0; } if (fbcount) fbc = fbcs[0]; XFree(fbcs); return display; } bool bindPixmap(int w, int h) Q_DECL_OVERRIDE { const int depth = createPixmap(w, h); if (depth <= 0) return false; const int attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, depth == 32 ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, GLX_MIPMAP_TEXTURE_EXT, False, None, }; glxpixmap = glXCreatePixmap((::Display*)display, fbc, pixmap, attribs); return true; } void bindTexture() Q_DECL_OVERRIDE { glXBindTexImageEXT(display, glxpixmap, GLX_FRONT_EXT, NULL); } GLXFBConfig fbc; GLXPixmap glxpixmap; }; #endif // !defined(QT_OPENGL_ES_2) X11InteropResource::X11InteropResource() : InteropResource() , VAAPI_X11() , xdisplay(NULL) , width(0) , height(0) , x11(NULL) { qDebug("X11InteropResource"); } X11InteropResource::~X11InteropResource() { delete x11; } bool X11InteropResource::ensurePixmaps(int w, int h) { if (width == w && height == h) return true; if (!x11) { #if QTAV_HAVE(EGL_CAPI) if (OpenGLHelper::isEGL()) { x11 = new X11_EGL(); } else #endif //QTAV_HAVE(EGL_CAPI) { #if !defined(QT_OPENGL_ES_2) // TODO: check GLX_EXT_texture_from_pixmap x11 = new X11_GLX(); #endif // defined(QT_OPENGL_ES_2) } } if (!x11) { qWarning("no EGL and GLX interop (TFP) support"); return false; } xdisplay = x11->ensureGL(); if (!xdisplay) return false; if (!x11->bindPixmap(w, h)) return false; width = w; height = h; return true; } bool X11InteropResource::map(const surface_ptr& surface, GLuint tex, int w, int h, int) { if (surface->width() <= 0 || surface->height() <= 0) { qWarning("invalid surface size"); return false; } if (!ensurePixmaps(w, h)) //pixmap with frame size return false; VAWARN(vaSyncSurface(surface->vadisplay(), surface->get())); // FIXME: invalid surface at the first time vaPutSurface is called. If return false, vaPutSurface will always fail, why? VAWARN(vaPutSurface(surface->vadisplay(), surface->get(), x11->pixmap , 0, 0, w, h , 0, 0, w, h , NULL, 0, VA_FRAME_PICTURE | surface->colorSpace()) ); XSync((::Display*)xdisplay, False); DYGL(glBindTexture(GL_TEXTURE_2D, tex)); x11->bindTexture(); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); return true; } bool X11InteropResource::unmap(const surface_ptr &surface, GLuint tex) { Q_UNUSED(surface); Q_UNUSED(tex); // can not call glXReleaseTexImageEXT otherwise the texture will containts no image data return true; } #endif //VA_X11_INTEROP #if QTAV_HAVE(EGL_CAPI) #ifndef EGL_LINUX_DMA_BUF_EXT #define EGL_LINUX_DMA_BUF_EXT 0x3270 #define EGL_LINUX_DRM_FOURCC_EXT 0x3271 #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272 #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273 #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274 #endif //2010 https://www.khronos.org/registry/egl/extensions/MESA/EGL_MESA_drm_image.txt: only support EGL_DRM_BUFFER_FORMAT_ARGB32_MESA class EGL { public: EGL() : dpy(EGL_NO_DISPLAY) { for (unsigned i = 0; i < sizeof(image)/sizeof(image[0]); ++i) image[i] = EGL_NO_IMAGE_KHR; } ~EGL() { for (unsigned i = 0; i < sizeof(image)/sizeof(image[0]); ++i) { destroyImages(i); } } void destroyImages(int plane) { //qDebug("destroyImage %d image:%p, this: %p", plane, image,this); if (image[plane] != EGL_NO_IMAGE_KHR) { EGL_WARN(eglDestroyImageKHR(dpy, image[plane])); image[plane] = EGL_NO_IMAGE_KHR; } } bool ensureGL() { if (eglCreateImageKHR && eglDestroyImageKHR && glEGLImageTargetTexture2DOES) return true; RESOLVE_EGL(glEGLImageTargetTexture2DOES); RESOLVE_EGL(eglCreateImageKHR); RESOLVE_EGL(eglDestroyImageKHR); return eglCreateImageKHR && eglDestroyImageKHR && glEGLImageTargetTexture2DOES; } bool bindImage(int plane, const EGLint *attrib_list) { if (dpy == EGL_NO_DISPLAY) { qDebug("eglGetCurrentDisplay"); dpy = eglGetCurrentDisplay(); } EGL_ENSURE(image[plane] = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list), false); return true; } void bindTexture(int plane) { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image[plane]); } EGLDisplay dpy; EGLImageKHR image[4]; }; EGLInteropResource::EGLInteropResource() : InteropResource() , vabuf_handle(0) , egl(0) { va_image.buf = va_image.image_id = VA_INVALID_ID; } EGLInteropResource::~EGLInteropResource() { delete egl; // TODO: thread } bool EGLInteropResource::map(const surface_ptr &surface, GLuint tex, int w, int h, int plane) { if (!ensure()) return false; if (va_image.image_id == VA_INVALID_ID) { // TODO: try vaGetImage. it's yuv420p. RG texture is not supported by gles2, so let's use yuv420p, or change the shader VA_ENSURE(vaDeriveImage(surface->vadisplay(), surface->get(), &va_image), false); } if (!vabuf_handle) { va_0_38::VABufferInfo vabuf; memset(&vabuf, 0, sizeof(vabuf)); vabuf.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; VA_ENSURE(va_0_38::vaAcquireBufferHandle(surface->vadisplay(), va_image.buf, &vabuf), false); vabuf_handle = vabuf.handle; } // (it would be nice if we could use EGL_IMAGE_INTERNAL_FORMAT_EXT) #define FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((unsigned)(d)<<24)) static const int drm_fmts[] = { FOURCC('R', '8', ' ', ' '), // DRM_FORMAT_R8 FOURCC('G', 'R', '8', '8'), // DRM_FORMAT_GR88. RG88 does not work FOURCC('R', 'G', '2', '4'), // DRM_FORMAT_RGB888 FOURCC('R', 'A', '2', '4') // DRM_FORMAT_RGBA8888 }; #undef FOURCC // TODO: yv12 swap uv const VideoFormat fmt(pixelFormatFromVA(va_image.format.fourcc)); const EGLint attribs[] = { EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[fmt.bytesPerPixel(plane) - 1], EGL_WIDTH, fmt.width(w, plane), EGL_HEIGHT, fmt.height(h, plane), EGL_DMA_BUF_PLANE0_FD_EXT, (EGLint)vabuf_handle, EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)va_image.offsets[plane], EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)va_image.pitches[plane], EGL_NONE }; if (!egl->bindImage(plane, attribs)) return false; DYGL(glBindTexture(GL_TEXTURE_2D, tex)); egl->bindTexture(plane); DYGL(glBindTexture(GL_TEXTURE_2D, 0)); mapped.insert(tex, plane); return true; } bool EGLInteropResource::unmap(const surface_ptr &surface, GLuint tex) { if (!egl) return false; if (!mapped.contains(tex)) { if (mapped.isEmpty()) { destroy(surface->vadisplay()); } return false; } const int plane = mapped.value(tex); egl->destroyImages(plane); mapped.remove(tex); if (mapped.isEmpty()) { destroy(surface->vadisplay()); } return true; } void EGLInteropResource::destroy(VADisplay va_dpy) { if (!va_dpy) return; if (va_image.buf != VA_INVALID_ID) { VAWARN(va_0_38::vaReleaseBufferHandle(va_dpy, va_image.buf)); va_image.buf = VA_INVALID_ID; vabuf_handle = 0; //qDebug("vabuf_handle: %#x", vabuf_handle); } if (va_image.image_id != VA_INVALID_ID) { VAWARN(vaDestroyImage(va_dpy, va_image.image_id)); va_image.image_id = VA_INVALID_ID; } } bool EGLInteropResource::ensure() { if (egl) return true; #if QTAV_HAVE(EGL_CAPI) if (!OpenGLHelper::isEGL()) { qWarning("Not using EGL"); return false; } #else qWarning("build QtAV with capi is required"); return false; #endif //QTAV_HAVE(EGL_CAPI) static bool has_ext = false; if (!has_ext) { if (!vaapi::checkEGL_DMA()) return false; static const char* glexts[] = { "GL_OES_EGL_image", 0 }; if (!OpenGLHelper::hasExtension(glexts)) { qWarning("missing extension: GL_OES_EGL_image"); return false; } has_ext = true; } if (!egl) egl = new EGL(); if (!egl->ensureGL()) return false; return true; } #endif //QTAV_HAVE(EGL_CAPI) } //namespace QtAV } //namespace vaapi #endif //QT_NO_OPENGL QtAV-1.12.0/src/vaapi/SurfaceInteropVAAPI.h000066400000000000000000000111511312235004300201500ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_SURFACEINTEROPVAAPI_H #define QTAV_SURFACEINTEROPVAAPI_H #include #include "vaapi_helper.h" #define VA_X11_INTEROP 1 #ifndef QT_NO_OPENGL #include #include #include "QtAV/SurfaceInterop.h" namespace QtAV { namespace vaapi { bool checkEGL_DMA(); bool checkEGL_Pixmap(); class InteropResource { public: virtual ~InteropResource() {} // egl supports yuv extension /*! * \brief map * \param surface va decoded surface * \param tex opengl texture * \param w frame width(visual width) without alignment, <= dxva surface width * \param h frame height(visual height) * \param plane useless now * \return true if success */ virtual bool map(const surface_ptr &surface, GLuint tex, int w, int h, int plane) = 0; virtual bool unmap(const surface_ptr &surface, GLuint tex) { Q_UNUSED(surface); Q_UNUSED(tex); return true; } }; typedef QSharedPointer InteropResourcePtr; class SurfaceInteropVAAPI Q_DECL_FINAL: public VideoSurfaceInterop { public: SurfaceInteropVAAPI(const InteropResourcePtr& res) : frame_width(0), frame_height(0), m_resource(res) {} void setSurface(const surface_ptr& surface, int w, int h); // use surface->width/height if w/h is 0 void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE; void unmap(void *handle) Q_DECL_OVERRIDE; protected: void* mapToHost(const VideoFormat &format, void *handle, int plane); private: int frame_width, frame_height; // NOTE: must ensure va-x11/va-glx is unloaded after all va calls(don't know why, but it's true), for example vaTerminate(), to avoid crash // so declare InteropResourcePtr first then surface_ptr. InteropResource (va-xxx.so) will be destroyed later than surface_t (vaTerminate()) // also call vaInitialize() before vaTerminate() can avoid such crashes. Don't know why. InteropResourcePtr m_resource; surface_ptr m_surface; }; // load/resolve symbols only once in decoder and pass a VAAPI_XXX ptr // or use pool class GLXInteropResource Q_DECL_FINAL: public InteropResource, protected VAAPI_GLX { public: bool map(const surface_ptr &surface, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE; private: surface_glx_ptr surfaceGLX(const display_ptr& dpy, GLuint tex); QMap glx_surfaces; // render to different texture. surface_glx_ptr is created with texture }; class X11; class X11InteropResource Q_DECL_FINAL: public InteropResource, protected VAAPI_X11 { public: X11InteropResource(); ~X11InteropResource(); bool map(const surface_ptr &surface, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE; bool unmap(const surface_ptr &surface, GLuint tex) Q_DECL_OVERRIDE; private: bool ensurePixmaps(int w, int h); Display *xdisplay; int width, height; X11 *x11; }; #if QTAV_HAVE(EGL_CAPI) // libva-egl is dead and not complete. here we use dma class EGL; class EGLInteropResource Q_DECL_FINAL : public InteropResource { public: EGLInteropResource(); ~EGLInteropResource(); bool map(const surface_ptr &surface, GLuint tex, int w, int h, int plane) Q_DECL_OVERRIDE; bool unmap(const surface_ptr &surface, GLuint tex) Q_DECL_OVERRIDE; private: bool ensure(); void destroy(VADisplay va_dpy); //destroy dma buffer and egl images uintptr_t vabuf_handle; VAImage va_image; QMap mapped; EGL *egl; }; #endif //QTAV_HAVE(EGL_CAPI) } //namespace vaapi } //namespace QtAV #endif //QT_NO_OPENGL #endif // QTAV_SURFACEINTEROPVAAPI_H QtAV-1.12.0/src/vaapi/vaapi_helper.cpp000066400000000000000000000310201312235004300174250ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "vaapi_helper.h" #include //open() #include //close() #include "utils/Logger.h" namespace QtAV { namespace OpenGLHelper { bool isEGL(); } namespace vaapi { dll_helper::dll_helper(const QString &soname, int version) { if (version >= 0) m_lib.setFileNameAndVersion(soname, version); else m_lib.setFileName(soname); if (m_lib.load()) { qDebug("%s loaded", m_lib.fileName().toUtf8().constData()); } else { if (version >= 0) { m_lib.setFileName(soname); m_lib.load(); } } if (!m_lib.isLoaded()) qDebug("can not load %s: %s", m_lib.fileName().toUtf8().constData(), m_lib.errorString().toUtf8().constData()); } va_0_38::vaAcquireBufferHandle_t va_0_38::f_vaAcquireBufferHandle = 0; va_0_38::vaReleaseBufferHandle_t va_0_38::f_vaReleaseBufferHandle = 0; #define CONCAT(a, b) CONCAT_(a, b) #define CONCAT_(a, b) a##b #define STRINGIFY(x) STRINGIFY_(x) #define STRINGIFY_(x) #x #define STRCASEP(p, x) STRCASE(CONCAT(p, x)) #define STRCASE(x) case x: return STRINGIFY(x) /* Return a string representation of a VAProfile */ const char *profileName(VAProfile profile) { switch (profile) { #define MAP(profile) \ STRCASEP(VAProfile, profile) MAP(MPEG2Simple); MAP(MPEG2Main); MAP(MPEG4Simple); MAP(MPEG4AdvancedSimple); MAP(MPEG4Main); #if VA_CHECK_VERSION(0,32,0) MAP(JPEGBaseline); MAP(H263Baseline); MAP(H264ConstrainedBaseline); #endif MAP(H264Baseline); MAP(H264Main); MAP(H264High); MAP(VC1Simple); MAP(VC1Main); MAP(VC1Advanced); #if VA_CHECK_VERSION(0, 38, 0) MAP(HEVCMain); MAP(HEVCMain10); MAP(VP9Profile0); MAP(VP8Version0_3); //defined in 0.37 #endif //VA_CHECK_VERSION(0, 38, 0) #undef MAP default: break; } return ""; } VAImageFormat va_new_image(VADisplay display, const unsigned int *fourccs, VAImage *img, int w, int h, VASurfaceID s) { VAImageFormat fmt; memset(&fmt, 0, sizeof(fmt)); int nb_fmts = vaMaxNumImageFormats(display); VAImageFormat *p_fmt = (VAImageFormat*)calloc(nb_fmts, sizeof(*p_fmt)); if (!p_fmt) return fmt; if (vaQueryImageFormats(display, p_fmt, &nb_fmts)) { free(p_fmt); return fmt; } for (int i = 0; fourccs[i]; i++) { // TODO: loop fourccs for (int j = 0; j < nb_fmts; ++j) { if (p_fmt[j].fourcc == fourccs[i]) { fmt = p_fmt[j]; break; } } const unsigned int fcc = fmt.fourcc; if (!fcc) continue; if (img && w > 0 && h > 0) { qDebug("vaCreateImage: %c%c%c%c", fcc<<24>>24, fcc<<16>>24, fcc<<8>>24, fcc>>24); if (vaCreateImage(display, &fmt, w, h, img) != VA_STATUS_SUCCESS) { img->image_id = VA_INVALID_ID; memset(&fmt, 0, sizeof(fmt)); qDebug("vaCreateImage error: %c%c%c%c", fcc<<24>>24, fcc<<16>>24, fcc<<8>>24, fcc>>24); continue; } // Validate that vaGetImage works with this format if (s != VA_INVALID_SURFACE) { VAStatus st; if ((st = vaGetImage(display, s, 0, 0, w, h, img->image_id)) != VA_STATUS_SUCCESS) { VAWARN(vaDestroyImage(display, img->image_id)); qDebug("vaGetImage error: %c%c%c%c (%#x) %s", fcc<<24>>24, fcc<<16>>24, fcc<<8>>24, fcc>>24, st, vaErrorStr(st)); img->image_id = VA_INVALID_ID; memset(&fmt, 0, sizeof(fmt)); continue; } } } break; } free(p_fmt); return fmt; } //TODO: use macro template. DEFINE_DL_SYMB(R, NAME, ARG....); class X11_API : protected dll_helper { public: typedef Display* XOpenDisplay_t(const char* name); typedef int XCloseDisplay_t(Display* dpy); typedef int XInitThreads_t(); X11_API(): dll_helper(QString::fromLatin1("X11"),6) { fp_XOpenDisplay = (XOpenDisplay_t*)resolve("XOpenDisplay"); fp_XCloseDisplay = (XCloseDisplay_t*)resolve("XCloseDisplay"); fp_XInitThreads = (XInitThreads_t*)resolve("XInitThreads"); } Display* XOpenDisplay(const char* name) { assert(fp_XOpenDisplay); return fp_XOpenDisplay(name); } int XCloseDisplay(Display* dpy) { assert(fp_XCloseDisplay); return fp_XCloseDisplay(dpy); } int XInitThreads() { assert(fp_XInitThreads); return fp_XInitThreads(); } private: XOpenDisplay_t* fp_XOpenDisplay; XCloseDisplay_t* fp_XCloseDisplay; XInitThreads_t* fp_XInitThreads; }; class NativeDisplayBase { Q_DISABLE_COPY(NativeDisplayBase) public: NativeDisplayBase() :m_handle(0) {} virtual ~NativeDisplayBase() {} virtual bool initialize(const NativeDisplay& display) = 0; virtual VADisplay getVADisplay() = 0; uintptr_t handle() { return m_handle;} virtual NativeDisplay::Type type() const = 0; protected: virtual bool acceptValidExternalHandle(const NativeDisplay& display) { if (display.handle && display.handle != -1) { //drm can be 0? m_handle = display.handle; m_selfCreated = false; return true; } return false; } intptr_t m_handle; bool m_selfCreated; }; class NativeDisplayX11 Q_DECL_FINAL: public NativeDisplayBase, protected VAAPI_X11, protected X11_API { public: NativeDisplayX11() :NativeDisplayBase() { } ~NativeDisplayX11() { if (m_selfCreated && m_handle) XCloseDisplay((Display*)(m_handle)); } bool initialize (const NativeDisplay& display) Q_DECL_OVERRIDE { assert(display.type == NativeDisplay::X11 || display.type == NativeDisplay::Auto); if (acceptValidExternalHandle(display)) return true; qDebug("NativeDisplayX11..............."); if (!XInitThreads()) { qWarning("XInitThreads failed!"); return false; } m_handle = (uintptr_t)XOpenDisplay(NULL); m_selfCreated = true; return !!m_handle; } VADisplay getVADisplay() Q_DECL_OVERRIDE { if (!m_handle) return 0; if (!VAAPI_X11::isLoaded()) return 0; return vaGetDisplay((Display*)m_handle); } NativeDisplay::Type type() const Q_DECL_OVERRIDE { return NativeDisplay::X11;} }; #ifndef QT_NO_OPENGL class NativeDisplayGLX Q_DECL_FINAL: public NativeDisplayBase, protected VAAPI_GLX, protected X11_API { public: NativeDisplayGLX() :NativeDisplayBase() { } ~NativeDisplayGLX() { if (m_selfCreated && m_handle) XCloseDisplay((Display*)(m_handle)); } bool initialize (const NativeDisplay& display) Q_DECL_OVERRIDE { assert(display.type == NativeDisplay::GLX || display.type == NativeDisplay::Auto); if (acceptValidExternalHandle(display)) return true; qDebug("NativeDisplayGLX.............."); if (!XInitThreads()) { qWarning("XInitThreads failed!"); return false; } m_handle = (uintptr_t)XOpenDisplay(NULL); m_selfCreated = true; return !!m_handle; } VADisplay getVADisplay() Q_DECL_OVERRIDE { if (!m_handle) return 0; if (!VAAPI_GLX::isLoaded()) return 0; return vaGetDisplayGLX((Display*)m_handle); } NativeDisplay::Type type() const Q_DECL_OVERRIDE { return NativeDisplay::GLX;} }; #endif //QT_NO_OPENGL class NativeDisplayDrm Q_DECL_FINAL: public NativeDisplayBase, protected VAAPI_DRM { public: NativeDisplayDrm() :NativeDisplayBase(){ } ~NativeDisplayDrm() { if (m_selfCreated && m_handle && m_handle != -1) ::close(m_handle); } bool initialize (const NativeDisplay& display) Q_DECL_OVERRIDE { assert(display.type == NativeDisplay::DRM || display.type == NativeDisplay::Auto); if (acceptValidExternalHandle(display)) return true; qDebug("NativeDisplayDrm.............."); // try drmOpen()? static const char* drm_dev[] = { "/dev/dri/renderD128", // DRM Render-Nodes "/dev/dri/card0", NULL }; for (int i = 0; drm_dev[i]; ++i) { m_handle = ::open(drm_dev[i], O_RDWR); if (m_handle < 0) continue; qDebug("using drm device: %s, handle: %p", drm_dev[i], (void*)m_handle); //drmGetDeviceNameFromFd break; } m_selfCreated = true; return m_handle != -1; } VADisplay getVADisplay() Q_DECL_OVERRIDE { if (m_handle == -1) return 0; if (!VAAPI_DRM::isLoaded()) return 0; return vaGetDisplayDRM(m_handle); } NativeDisplay::Type type() const Q_DECL_OVERRIDE { return NativeDisplay::DRM;} }; class NativeDisplayVADisplay Q_DECL_FINAL: public NativeDisplayBase{ public: NativeDisplayVADisplay() :NativeDisplayBase(){ } bool initialize (const NativeDisplay& display) Q_DECL_OVERRIDE { assert(display.type == NativeDisplay::VA); return acceptValidExternalHandle(display); } VADisplay getVADisplay() Q_DECL_OVERRIDE { return (VADisplay)m_handle;} NativeDisplay::Type type() const Q_DECL_OVERRIDE { return NativeDisplay::VA;} }; // TODO: use display_ptr cache display_ptr display_t::create(const NativeDisplay &display) { NativeDisplayPtr native; switch (display.type) { case NativeDisplay::X11: native = NativeDisplayPtr(new NativeDisplayX11()); break; case NativeDisplay::DRM: native = NativeDisplayPtr(new NativeDisplayDrm()); break; case NativeDisplay::VA: native = NativeDisplayPtr(new NativeDisplayVADisplay()); break; case NativeDisplay::GLX: #ifndef QT_NO_OPENGL native = NativeDisplayPtr(new NativeDisplayGLX()); #else qWarning("No OpenGL support in Qt"); #endif break; default: break; } if (!native) return display_ptr(); if (!native->initialize(display)) return display_ptr(); VADisplay va = native->getVADisplay(); int majorVersion, minorVersion; VA_ENSURE(vaInitialize(va, &majorVersion, &minorVersion), display_ptr()); display_ptr d(new display_t()); d->m_display = va; d->m_native = native; d->m_major = majorVersion; d->m_minor = minorVersion; return d; } display_t::~display_t() { if (!m_display) return; bool init_va = false; #ifndef QT_NO_OPENGL // TODO: if drm+egl works, init_va should be true for DRM init_va = OpenGLHelper::isEGL() && nativeDisplayType() == NativeDisplay::X11; #endif #if defined(WORKAROUND_VATERMINATE_CRASH) init_va = true; #endif if (init_va) { int mj, mn; // FIXME: for libva-xxx we can unload after vaTerminate to avoid crash. But does not work for egl+dma/drm. I really don't know the reason qDebug("vaInitialize before terminate. (work around for vaTerminate() crash)"); VAWARN(vaInitialize(m_display, &mj, &mn)); } qDebug("vaapi: destroy display %p", m_display); VAWARN(vaTerminate(m_display)); //FIXME: what about thread? m_display = 0; } NativeDisplay::Type display_t::nativeDisplayType() const { if (!m_native) return NativeDisplay::Auto; return m_native->type(); } intptr_t display_t::nativeHandle() const { if (!m_native) return 0; return m_native->handle(); } } //namespace vaapi } //namespace QtAV QtAV-1.12.0/src/vaapi/vaapi_helper.h000066400000000000000000000307751312235004300171120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VAAPI_HELPER_H #define QTAV_VAAPI_HELPER_H #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #elif defined(QT_OPENGL_LIB) #include #endif #include "utils/SharedPtr.h" namespace QtAV { #ifndef VA_FOURCC_RGBX #define VA_FOURCC_RGBX 0x58424752 #endif #ifndef VA_FOURCC_BGRX #define VA_FOURCC_BGRX 0x58524742 #endif #ifndef VA_SURFACE_ATTRIB_SETTABLE // travis-ci use old vaapi struct VASurfaceAttrib; inline VAStatus vaCreateSurfaces(VADisplay dpy, unsigned int format, unsigned int width, unsigned int height, VASurfaceID *surfaces, unsigned int num_surfaces, VASurfaceAttrib *attrib_list, unsigned int num_attribs ) { return ::vaCreateSurfaces(dpy, width, height, format, num_surfaces, surfaces); } #endif #define VA_ENSURE_TRUE(x, ...) \ do { \ VAStatus ret = x; \ if (ret != VA_STATUS_SUCCESS) { \ qWarning("VA-API error@%d. " #x ": %#x %s", __LINE__, ret, vaErrorStr(ret)); \ return __VA_ARGS__; \ } \ } while(0) #define VA_ENSURE(...) VA_ENSURE_TRUE(__VA_ARGS__) #define VAWARN(a) \ do { \ VAStatus res = a; \ if(res != VA_STATUS_SUCCESS) \ qWarning("VA-API error %s@%d. " #a ": %#x %s", __FILE__, __LINE__, res, vaErrorStr(res)); \ } while(0); namespace vaapi { const char *profileName(VAProfile profile); /*! * \brief va_new_image * create image (if img is not null)/find format for the first supported fourcc from given fourcc list. * if s is not null, also test vaGetImage for the fourcc */ VAImageFormat va_new_image(VADisplay display, const unsigned int* fourccs, VAImage* img = 0, int w = 0, int h = 0, VASurfaceID s = VA_INVALID_SURFACE); class dll_helper { public: dll_helper(const QString& soname, int version = -1); virtual ~dll_helper() { m_lib.unload();} bool isLoaded() const { return m_lib.isLoaded(); } void* resolve(const char *symbol) { return (void*)m_lib.resolve(symbol);} private: QLibrary m_lib; }; class va_0_38 : protected dll_helper { public: typedef struct { uintptr_t handle; uint32_t type; uint32_t mem_type; size_t mem_size; } VABufferInfo; static va_0_38& instance() { static va_0_38 self; return self; } static bool isValid() { return instance().f_vaAcquireBufferHandle && instance().f_vaReleaseBufferHandle;} static VAStatus vaAcquireBufferHandle(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info) { if (!instance().f_vaAcquireBufferHandle) return VA_STATUS_ERROR_UNIMPLEMENTED; return instance().f_vaAcquireBufferHandle(dpy, buf_id, buf_info); } static VAStatus vaReleaseBufferHandle(VADisplay dpy, VABufferID buf_id) { if (!instance().f_vaReleaseBufferHandle) return VA_STATUS_ERROR_UNIMPLEMENTED; return instance().f_vaReleaseBufferHandle(dpy, buf_id); } protected: va_0_38() : dll_helper(QString::fromLatin1("va"), 1) { f_vaAcquireBufferHandle = (vaAcquireBufferHandle_t)resolve("vaAcquireBufferHandle"); f_vaReleaseBufferHandle = (vaReleaseBufferHandle_t)resolve("vaReleaseBufferHandle"); } private: typedef VAStatus (*vaAcquireBufferHandle_t)(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info); typedef VAStatus (*vaReleaseBufferHandle_t)(VADisplay dpy, VABufferID buf_id); static vaAcquireBufferHandle_t f_vaAcquireBufferHandle; static vaReleaseBufferHandle_t f_vaReleaseBufferHandle; }; class VAAPI_DRM : protected dll_helper { public: typedef VADisplay vaGetDisplayDRM_t(int fd); VAAPI_DRM(): dll_helper(QString::fromLatin1("va-drm"),1) { fp_vaGetDisplayDRM = (vaGetDisplayDRM_t*)resolve("vaGetDisplayDRM"); } VADisplay vaGetDisplayDRM(int fd) { assert(fp_vaGetDisplayDRM); return fp_vaGetDisplayDRM(fd); } private: vaGetDisplayDRM_t* fp_vaGetDisplayDRM; }; typedef struct _XDisplay Display; class VAAPI_X11 : protected dll_helper { public: typedef unsigned long Drawable; typedef VADisplay vaGetDisplay_t(Display *); typedef VAStatus vaPutSurface_t(VADisplay, VASurfaceID, Drawable, short, short, unsigned short, unsigned short, short, short, unsigned short, unsigned short, VARectangle *, unsigned int, unsigned int); VAAPI_X11(): dll_helper(QString::fromLatin1("va-x11"),1) { fp_vaGetDisplay = (vaGetDisplay_t*)resolve("vaGetDisplay"); fp_vaPutSurface = (vaPutSurface_t*)resolve("vaPutSurface"); } VADisplay vaGetDisplay(Display *dpy) { assert(fp_vaGetDisplay); return fp_vaGetDisplay(dpy); } VAStatus vaPutSurface (VADisplay dpy, VASurfaceID surface, Drawable draw, /* X Drawable */ short srcx, short srcy, unsigned short srcw, unsigned short srch, short destx, short desty, unsigned short destw, unsigned short desth, VARectangle *cliprects, /* client supplied destination clip list */ unsigned int number_cliprects, /* number of clip rects in the clip list */ unsigned int flags /* PutSurface flags */ ) { assert(fp_vaPutSurface); return fp_vaPutSurface(dpy, surface, draw, srcx, srcy, srcw, srch, destx, desty, destw, desth, cliprects, number_cliprects, flags); } private: vaGetDisplay_t* fp_vaGetDisplay; vaPutSurface_t* fp_vaPutSurface; }; typedef void* EGLClientBuffer; class VAAPI_EGL : protected dll_helper { //not implemented typedef VAStatus vaGetEGLClientBufferFromSurface_t(VADisplay dpy, VASurfaceID surface, EGLClientBuffer *buffer/* out*/); vaGetEGLClientBufferFromSurface_t* fp_vaGetEGLClientBufferFromSurface; public: VAAPI_EGL(): dll_helper(QString::fromLatin1("va-egl"),1) { fp_vaGetEGLClientBufferFromSurface = (vaGetEGLClientBufferFromSurface_t*)resolve("vaGetEGLClientBufferFromSurface"); } VAStatus vaGetEGLClientBufferFromSurface(VADisplay dpy, VASurfaceID surface, EGLClientBuffer *buffer/* out*/) { assert(fp_vaGetEGLClientBufferFromSurface); return fp_vaGetEGLClientBufferFromSurface(dpy, surface, buffer); } }; #ifndef QT_NO_OPENGL class VAAPI_GLX : protected dll_helper { public: typedef VADisplay vaGetDisplayGLX_t(Display *); typedef VAStatus vaCreateSurfaceGLX_t(VADisplay, GLenum, GLuint, void **); typedef VAStatus vaDestroySurfaceGLX_t(VADisplay, void *); typedef VAStatus vaCopySurfaceGLX_t(VADisplay, void *, VASurfaceID, unsigned int); VAAPI_GLX(): dll_helper(QString::fromLatin1("va-glx"),1) { fp_vaGetDisplayGLX = (vaGetDisplayGLX_t*)resolve("vaGetDisplayGLX"); fp_vaCreateSurfaceGLX = (vaCreateSurfaceGLX_t*)resolve("vaCreateSurfaceGLX"); fp_vaDestroySurfaceGLX = (vaDestroySurfaceGLX_t*)resolve("vaDestroySurfaceGLX"); fp_vaCopySurfaceGLX = (vaCopySurfaceGLX_t*)resolve("vaCopySurfaceGLX"); } VADisplay vaGetDisplayGLX(Display *dpy) { assert(fp_vaGetDisplayGLX); return fp_vaGetDisplayGLX(dpy); } VAStatus vaCreateSurfaceGLX(VADisplay dpy, GLenum target, GLuint texture, void **gl_surface) { assert(fp_vaCreateSurfaceGLX); return fp_vaCreateSurfaceGLX(dpy, target, texture, gl_surface); } VAStatus vaDestroySurfaceGLX(VADisplay dpy, void *gl_surface) { assert(fp_vaDestroySurfaceGLX); return fp_vaDestroySurfaceGLX(dpy, gl_surface); } /** * Copy a VA surface to a VA/GLX surface * * This function will not return until the copy is completed. At this * point, the underlying GL texture will contain the surface pixels * in an RGB format defined by the user. * * The application shall maintain the live GLX context itself. * Implementations are free to use glXGetCurrentContext() and * glXGetCurrentDrawable() functions for internal purposes. * * @param[in] dpy the VA display * @param[in] gl_surface the VA/GLX destination surface * @param[in] surface the VA source surface * @param[in] flags the PutSurface flags * @return VA_STATUS_SUCCESS if successful */ VAStatus vaCopySurfaceGLX(VADisplay dpy, void *gl_surface, VASurfaceID surface, unsigned int flags) { assert(fp_vaCopySurfaceGLX); return fp_vaCopySurfaceGLX(dpy, gl_surface, surface, flags); } private: vaGetDisplayGLX_t* fp_vaGetDisplayGLX; vaCreateSurfaceGLX_t* fp_vaCreateSurfaceGLX; vaDestroySurfaceGLX_t* fp_vaDestroySurfaceGLX; vaCopySurfaceGLX_t* fp_vaCopySurfaceGLX; }; #endif //QT_NO_OPENGL class NativeDisplayBase; typedef QSharedPointer NativeDisplayPtr; struct NativeDisplay { enum Type { Auto, X11, GLX, //the same as X11 but use vaGetDisplayGLX()? DRM, Wayland, VA }; intptr_t handle; Type type; NativeDisplay() : handle(-1), type(Auto) {} }; class display_t; typedef QSharedPointer display_ptr; class display_t { public: // display can have a valid handle (!=-1, 0), then it's an external display. you have to manager the external display handle yourself static display_ptr create(const NativeDisplay& display); ~display_t(); operator VADisplay() const { return m_display;} VADisplay get() const {return m_display;} void getVersion(int* majorV, int* minorV) { *majorV = m_major; *minorV = m_minor;} NativeDisplay::Type nativeDisplayType() const; intptr_t nativeHandle() const; private: VADisplay m_display; NativeDisplayPtr m_native; int m_major, m_minor; }; class surface_t { public: surface_t(int w, int h, VASurfaceID id, const display_ptr& display) : m_id(id) , m_display(display) , m_width(w) , m_height(h) , color_space(VA_SRC_BT709) {} ~surface_t() { //qDebug("VAAPI - destroying surface 0x%x", (int)m_id); if (m_id != VA_INVALID_SURFACE) VAWARN(vaDestroySurfaces(m_display->get(), &m_id, 1)) } operator VASurfaceID() const { return m_id;} VASurfaceID get() const { return m_id;} int width() const { return m_width;} int height() const { return m_height;} void setColorSpace(int cs = VA_SRC_BT709) { color_space = cs;} int colorSpace() const { return color_space;} display_ptr display() const { return m_display;} VADisplay vadisplay() const { return m_display->get();} private: VASurfaceID m_id; display_ptr m_display; int m_width, m_height; int color_space; }; typedef SharedPtr surface_ptr; #ifndef QT_NO_OPENGL class surface_glx_t : public VAAPI_GLX { public: surface_glx_t(const display_ptr& dpy) : m_dpy(dpy), m_glx(0) {} ~surface_glx_t() {destroy();} bool create(GLuint tex) { destroy(); VA_ENSURE_TRUE(vaCreateSurfaceGLX(m_dpy->get(), GL_TEXTURE_2D, tex, &m_glx), false); return true; } bool destroy() { if (!m_glx) return true; VA_ENSURE_TRUE(vaDestroySurfaceGLX(m_dpy->get(), m_glx), false); m_glx = 0; return true; } bool copy(const surface_ptr& surface) { if (!m_glx) return false; VA_ENSURE_TRUE(vaCopySurfaceGLX(m_dpy->get(), m_glx, surface->get(), VA_FRAME_PICTURE | surface->colorSpace()), false); return true; } private: display_ptr m_dpy; void* m_glx; }; typedef QSharedPointer surface_glx_ptr; //store in a vector #endif //QT_NO_OPENGL } //namespace vaapi } //namespace QtAV #endif // QTAV_VAAPI_HELPER_H QtAV-1.12.0/tests/000077500000000000000000000000001312235004300135415ustar00rootroot00000000000000QtAV-1.12.0/tests/ao/000077500000000000000000000000001312235004300141405ustar00rootroot00000000000000QtAV-1.12.0/tests/ao/ao.pro000066400000000000000000000002241312235004300152570ustar00rootroot00000000000000CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += \ main.cpp QtAV-1.12.0/tests/ao/main.cpp000066400000000000000000000060031312235004300155670ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include #include #include #include #include #include #include using namespace QtAV; const int kTableSize = 200; const int kFrames = 512; qint16 sin_table[kTableSize]; void help() { qDebug() << QLatin1String("parameters: [-ao ") << AudioOutput::backendsAvailable().join(QLatin1String("|")) << QLatin1String("]"); } int main(int argc, char** argv) { help(); /* initialise sinusoidal wavetable */ for(int i=0; i::max() * sin(((double)i/(double)kTableSize)*3.1415926*2.0)); } QCoreApplication app(argc, argv); //only used qapp to get parameter easily AudioOutput ao; int idx = app.arguments().indexOf(QLatin1String("-ao")); if (idx > 0) ao.setBackends(QStringList() << app.arguments().at(idx+1)); if (ao.backend().isEmpty()) { qWarning("unknow backend"); return -1; } AudioFormat af; af.setChannels(2); af.setSampleFormat(AudioFormat::SampleFormat_Signed16); af.setSampleRate(44100); if (!ao.isSupported(af)) { qDebug() << "does not support format: " << af; return -1; } ao.setAudioFormat(af); QByteArray data(af.bytesPerFrame()*kFrames, 0); //bytesPerSample*channels*1024 ao.setBufferSamples(kFrames); if (!ao.open()) { qWarning("open audio error"); return -1; } int left =0, right = 0; QElapsedTimer timer; timer.start(); while (timer.elapsed() < 3000) { qint16 *d = (qint16*)data.data(); for (int k = 0; k < kFrames; k++) { *d++ = sin_table[left]; *d++ = sin_table[right]; left = (left+1) % kTableSize; right = (right+3)% kTableSize; } ao.setVolume(2*sin(2.0*M_PI/1000.0*timer.elapsed())); ao.play(data); } ao.close(); return 0; } QtAV-1.12.0/tests/arch/000077500000000000000000000000001312235004300144565ustar00rootroot00000000000000QtAV-1.12.0/tests/arch/arch.cpp000066400000000000000000000252141312235004300161030ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ // change from qtbase/config.tests/arch/arch.cpp #define QGLOBAL_H #include "qprocessordetection.h" /* vc: arm, mips, sh, x86, x86_64, ia64*/ #if defined(Q_PROCESSOR_ALPHA) #warning "ARCH*=alpha" #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_ARM_32) #ifdef _MSC_VER #pragma message ("ARCH*=arm") #else #warning "ARCH*=arm" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_ARM_64) #ifdef _MSC_VER #pragma message ("ARCH*=arm64") #else #warning "ARCH*=arm64" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_ARM) #ifdef _MSC_VER #pragma message ("ARCH*=arm") #else #warning "ARCH*=arm" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_AVR32) #warning "ARCH*=avr32" #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_BLACKFIN) #warning "ARCH*=bfin" #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_X86_32) #ifdef _MSC_VER #pragma message ("ARCH*=x86") #else #warning "ARCH*=x86" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_X86_64) #ifdef _MSC_VER #pragma message ("ARCH*=x86_64") #else #warning "ARCH*=x86_64" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_IA64) #ifdef _MSC_VER #pragma message ("ARCH*=ia64") #else #warning "ARCH*=ia64" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_MIPS) #ifdef _MSC_VER #pragma message ("ARCH*=mips") #else #warning "ARCH*=mips" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_POWER) #warning "ARCH*=power" #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_S390) #warning "ARCH*=s390" #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_SH) #ifdef _MSC_VER #pragma message ("ARCH*=sh") #else #warning "ARCH*=sh" #endif /*_MSC_VER*/ #define ARCH_DETECTED #endif #if defined(Q_PROCESSOR_SPARC) #warning "ARCH*=sparc" #define ARCH_DETECTED #endif #ifndef ARCH_DETECTED #warning "ARCH=unknown" #endif // This is the list of features found in GCC or MSVC // We don't use all of them, but this is ready for future expansion // -- x86 -- #ifdef __3dNOW__ // 3dNow!, introduced with the AMD K6-2, discontinued after 2010 #ifdef _MSC_VER #pragma message ("ARCH_SUB*=3dnow") #else #warning "ARCH_SUB*=3dnow" #endif /*_MSC_VER*/ #endif #ifdef __3dNOW_A__ // Athlon #ifdef _MSC_VER #pragma message ("ARCH_SUB*=3dnow-a") #else #warning "ARCH_SUB*=3dnow-a" #endif /*_MSC_VER*/ #endif #ifdef __ABM__ // Advanced Bit Manipulation, AMD Barcelona (family 10h) #ifdef _MSC_VER #pragma message ("ARCH_SUB*=abm") #else #warning "ARCH_SUB*=abm" #endif /*_MSC_VER*/ #endif #ifdef __AES__ // AES New Instructions, Intel Core-i7 second generation ("Sandy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=aes") #else #warning "ARCH_SUB*=aes" #endif /*_MSC_VER*/ #endif #ifdef __AVX__ // Advanced Vector Extensions, Intel Core-i7 second generation ("Sandy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx") #else #warning "ARCH_SUB*=avx" #endif /*_MSC_VER*/ #endif #ifdef __AVX2__ // AVX 2, Intel Core 4th Generation ("Haswell") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx2") #else #warning "ARCH_SUB*=avx2" #endif /*_MSC_VER*/ #endif #ifdef __AVX512F__ // AVX512 Foundation, Intel Xeon Phi codename "Knights Landing" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx512f") #else #warning "ARCH_SUB*=avx512f" #endif /*_MSC_VER*/ #endif #ifdef __AVX512CD__ // AVX512 Conflict Detection, Intel Xeon Phi codename "Knights Landing" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx512cd") #else #warning "ARCH_SUB*=avx512cd" #endif /*_MSC_VER*/ #endif #ifdef __AVX512ER__ // AVX512 Exponentiation & Reciprocal, Intel Xeon Phi codename "Knights Landing" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx512ef") #else #warning "ARCH_SUB*=avx512ef" #endif /*_MSC_VER*/ #endif #ifdef __AVX512PF__ // AVX512 Prefetch, Intel Xeon Phi codename "Knights Landing" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=avx512pf") #else #warning "ARCH_SUB*=avx512pf" #endif /*_MSC_VER*/ #endif #ifdef __BMI__ // Bit Manipulation Instructions 1, Intel Core 4th Generation ("Haswell"), AMD "Bulldozer 2" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=bmi") #else #warning "ARCH_SUB*=bmi" #endif /*_MSC_VER*/ #endif #ifdef __BMI2__ // Bit Manipulation Instructions 2, Intel Core 4th Generation ("Haswell") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=bmi12") #else #warning "ARCH_SUB*=bmi12" #endif /*_MSC_VER*/ #endif #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 // cmpxchg16b instruction, Intel Pentium 4 64-bit ("Nocona"), AMD Barcelona (family 10h) // Notably, this instruction is missing on earlier AMD Athlon 64 #warning "ARCH_SUB*=cx16" #endif #ifdef __F16C__ // 16-bit floating point conversion, Intel Core 3rd Generation ("Ivy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=f16c") #else #warning "ARCH_SUB*=f16c" #endif /*_MSC_VER*/ #endif #ifdef __FMA__ // Fused Multiply-Add with 3 arguments, Intel Core 4th Generation ("Haswell"), AMD "Bulldozer 2" // a.k.a. "FMA3" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=fma") #else #warning "ARCH_SUB*=fma" #endif /*_MSC_VER*/ #endif #ifdef __FMA4__ // Fused Multiply-Add with 4 arguments, AMD "Bulldozer" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=fma4") #else #warning "ARCH_SUB*=fma4" #endif /*_MSC_VER*/ #endif #ifdef __FSGSBASE__ // rdfsgsbase, wrfsgsbase, Intel Core 3rd Generation ("Ivy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=fsgsbase") #else #warning "ARCH_SUB*=fsgsbase" #endif /*_MSC_VER*/ #endif #ifdef __LWP__ // LWP instructions, AMD "Bulldozer" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=lwp") #else #warning "ARCH_SUB*=lwp" #endif /*_MSC_VER*/ #endif #ifdef __LZCNT__ // Leading-Zero bit count, Intel Core 4th Generation ("Haswell") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=lzcnt") #else #warning "ARCH_SUB*=lzcnt" #endif /*_MSC_VER*/ #endif #ifdef __MMX__ // Multimedia Extensions, Pentium MMX, AMD K6-2 #ifdef _MSC_VER #pragma message ("ARCH_SUB*=mmx") #else #warning "ARCH_SUB*=mmx" #endif /*_MSC_VER*/ #endif #ifdef __MOVBE__ // Move Big Endian, Intel Atom & "Haswell" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=movbe") #else #warning "ARCH_SUB*=movbe" #endif /*_MSC_VER*/ #endif #ifdef __NO_SAHF__ // missing SAHF instruction in 64-bit, up to Intel Pentium 4 64-bit ("Nocona"), AMD Athlon FX // Note: the macro is not defined, so this will never show up #ifdef _MSC_VER #pragma message ("ARCH_SUB*=no-sahf") #else #warning "ARCH_SUB*=no-sahf" #endif /*_MSC_VER*/ #endif #ifdef __PCLMUL__ // (Packed) Carry-less multiplication, Intel Core-i7 second generation ("Sandy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=pclmul") #else #warning "ARCH_SUB*=pclmul" #endif /*_MSC_VER*/ #endif #ifdef __POPCNT__ // Population Count (count of set bits), Intel Core-i7 second generation ("Sandy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=popcnt") #else #warning "ARCH_SUB*=popcnt" #endif /*_MSC_VER*/ #endif #ifdef __RDRND__ // Random number generator, Intel Core 3rd Generation ("Ivy Bridge") #ifdef _MSC_VER #pragma message ("ARCH_SUB*=rdrnd") #else #warning "ARCH_SUB*=rdrnd" #endif /*_MSC_VER*/ #endif #ifdef __SHA__ // SHA-1 and SHA-256 instructions, Intel processor TBA #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sha") #else #warning "ARCH_SUB*=sha" #endif /*_MSC_VER*/ #endif #if defined(__SSE__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) || defined(_M_X64) // Streaming SIMD Extensions, Intel Pentium III, AMD Athlon #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse") #else #warning "ARCH_SUB*=sse" #endif /*_MSC_VER*/ #endif #if defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(_M_X64) // SSE2, Intel Pentium-M, Intel Pentium 4, AMD Opteron and Athlon 64 #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse2") #else #warning "ARCH_SUB*=sse2" #endif /*_MSC_VER*/ #endif #ifdef __SSE3__ // SSE3, Intel Pentium 4 "Prescott", AMD Athlon 64 rev E #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse3") #else #warning "ARCH_SUB*=sse3" #endif /*_MSC_VER*/ #endif #ifdef __SSSE3__ // Supplemental SSE3, Intel Core 2 ("Merom"), AMD "Bulldozer" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=ssse3") #else #warning "ARCH_SUB*=ssse3" #endif /*_MSC_VER*/ #endif #ifdef __SSE4A__ // SSE4a, AMD Barcelona #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse4a") #else #warning "ARCH_SUB*=sse4a" #endif /*_MSC_VER*/ #endif #ifdef __SSE4_1__ // SSE 4.1, Intel Core2 45nm shrink ("Penryn"), AMD "Bulldozer" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse4.1") #else #warning "ARCH_SUB*=sse4.1" #endif /*_MSC_VER*/ #endif #ifdef __SSE4_2__ // SSE 4.2, Intel Core-i7 ("Nehalem"), AMD "Bulldozer" // Since no processor supports SSE4.2 without 4.1 and since no Intel processor // supports SSE4a, define "sse4" to indicate SSE4" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=sse4.2") #pragma message ("ARCH_SUB*=sse4") #else #warning "ARCH_SUB*=sse4.2" #warning "ARCH_SUB*=sse4" #endif /*_MSC_VER*/ #endif #ifdef __TBM__ // TBM, AMD "Bulldozer" #ifdef _MSC_VER #pragma message ("ARCH_SUB*=tbm") #else #warning "ARCH_SUB*=tbm" #endif /*_MSC_VER*/ #endif #ifdef __XOP__ // XOP, AMD "Bulldozer" #warning "ARCH_SUB*=xop" #endif // -- ARM -- #ifdef __ARM_NEON__ #warning "ARCH_SUB*=neon" #endif #ifdef __IWMMXT__ #ifdef _MSC_VER #pragma message ("ARCH_SUB*=iwmmxt") #else #warning "ARCH_SUB*=iwmmxt" #endif /*_MSC_VER*/ #endif // -- SPARC -- #ifdef __VIS__ #warning "ARCH_SUB*=vis" # if __VIS__ >= 0x200 #warning "ARCH_SUB*=vis2" # endif # if __VIS__ >= 0x300 #warning "ARCH_SUB*=vis3" # endif #endif // -- MIPS -- # if __mips_dsp #ifdef _MSC_VER #pragma message ("ARCH_SUB*=dsp") #else #warning "ARCH_SUB*=dsp" #endif /*_MSC_VER*/ # endif # if __mips_dspr2 #ifdef _MSC_VER #pragma message ("ARCH_SUB*=dsp2") #else #warning "ARCH_SUB*=dsp2" #endif /*_MSC_VER*/ # endif // -- POWER, PowerPC -- #ifdef __ALTIVEC__ #warning "ARCH_SUB*=altivec" #endif QtAV-1.12.0/tests/arch/arch.pro000066400000000000000000000007171312235004300161220ustar00rootroot00000000000000TARGET = arch SOURCES = arch.cpp CONFIG -= qt dylib release debug_and_release CONFIG += debug console warn_on win32-icc { QMAKE_CXXFLAGS *= -arch:SSE4.1 #AVX } *msvc* { } else { ## gcc like. can not add here otherwise other archs can not be detected # QMAKE_CXXFLAGS *= -msse4.1 } arch_pp.target = preprocess arch_pp.commands = $$QMAKE_CXX \$< #for gnu make arch_pp.depends = $$PWD/arch.h #TODO: win path. shell_path()? QMAKE_EXTRA_TARGETS = arch_pp QtAV-1.12.0/tests/arch/qprocessordetection.h000066400000000000000000000301051312235004300207250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QGLOBAL_H # include #endif #ifndef QPROCESSORDETECTION_H #define QPROCESSORDETECTION_H /* This file uses preprocessor #defines to set various Q_PROCESSOR_* #defines based on the following patterns: Q_PROCESSOR_{FAMILY} Q_PROCESSOR_{FAMILY}_{VARIANT} Q_PROCESSOR_{FAMILY}_{REVISION} The first is always defined. Defines for the various revisions/variants are optional and usually dependent on how the compiler was invoked. Variants that are a superset of another should have a define for the superset. In addition to the procesor family, variants, and revisions, we also set Q_BYTE_ORDER appropriately for the target processor. For bi-endian processors, we try to auto-detect the byte order using the __BIG_ENDIAN__, __LITTLE_ENDIAN__, or __BYTE_ORDER__ preprocessor macros. Note: when adding support for new processors, be sure to update config.tests/arch/arch.cpp to ensure that configure can detect the target and host architectures. */ /* Machine byte-order, reuse preprocessor provided macros when available */ #if defined(__ORDER_BIG_ENDIAN__) # define Q_BIG_ENDIAN __ORDER_BIG_ENDIAN__ #else # define Q_BIG_ENDIAN 4321 #endif #if defined(__ORDER_LITTLE_ENDIAN__) # define Q_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ #else # define Q_LITTLE_ENDIAN 1234 #endif /* Alpha family, no revisions or variants Alpha is bi-endian, use endianness auto-detection implemented below. */ #if defined(__alpha__) || defined(_M_ALPHA) # define Q_PROCESSOR_ALPHA // Q_BYTE_ORDER not defined, use endianness auto-detection #endif /* ARM family, known revisions: V5, V6, V7, V8 ARM is bi-endian, detect using __ARMEL__ or __ARMEB__, falling back to auto-detection implemented below. */ #if defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM) || defined(__aarch64__) # define Q_PROCESSOR_ARM # if defined(__aarch64__) # define Q_PROCESSOR_ARM_64 # define Q_PROCESSOR_WORDSIZE 8 # else # define Q_PROCESSOR_ARM_32 # endif # if defined(__ARM64_ARCH_8__) # define Q_PROCESSOR_ARM_V8 # define Q_PROCESSOR_ARM_V7 # define Q_PROCESSOR_ARM_V6 # define Q_PROCESSOR_ARM_V5 # elif defined(__ARM_ARCH_7__) \ || defined(__ARM_ARCH_7A__) \ || defined(__ARM_ARCH_7R__) \ || defined(__ARM_ARCH_7M__) \ || defined(__ARM_ARCH_7S__) \ || defined(_ARM_ARCH_7) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) \ || (defined(_M_ARM) && _M_ARM-0 >= 7) # define Q_PROCESSOR_ARM_V7 # define Q_PROCESSOR_ARM_V6 # define Q_PROCESSOR_ARM_V5 # elif defined(__ARM_ARCH_6__) \ || defined(__ARM_ARCH_6J__) \ || defined(__ARM_ARCH_6T2__) \ || defined(__ARM_ARCH_6Z__) \ || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6ZK__) \ || defined(__ARM_ARCH_6M__) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) \ || (defined(_M_ARM) && _M_ARM-0 >= 6) # define Q_PROCESSOR_ARM_V6 # define Q_PROCESSOR_ARM_V5 # elif defined(__ARM_ARCH_5TEJ__) \ || defined(__ARM_ARCH_5TE__) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) \ || (defined(_M_ARM) && _M_ARM-0 >= 5) # define Q_PROCESSOR_ARM_V5 # endif # if defined(__ARMEL__) # define Q_BYTE_ORDER Q_LITTLE_ENDIAN # elif defined(__ARMEB__) # define Q_BYTE_ORDER Q_BIG_ENDIAN # else // Q_BYTE_ORDER not defined, use endianness auto-detection #endif /* AVR32 family, no revisions or variants AVR32 is big-endian. */ // #elif defined(__avr32__) // # define Q_PROCESSOR_AVR32 // # define Q_BYTE_ORDER Q_BIG_ENDIAN /* Blackfin family, no revisions or variants Blackfin is little-endian. */ // #elif defined(__bfin__) // # define Q_PROCESSOR_BLACKFIN // # define Q_BYTE_ORDER Q_LITTLE_ENDIAN /* X86 family, known variants: 32- and 64-bit X86 is little-endian. */ #elif defined(__i386) || defined(__i386__) || defined(_M_IX86) # define Q_PROCESSOR_X86_32 # define Q_BYTE_ORDER Q_LITTLE_ENDIAN # define Q_PROCESSOR_WORDSIZE 4 /* * We define Q_PROCESSOR_X86 == 6 for anything above a equivalent or better * than a Pentium Pro (the processor whose architecture was called P6) or an * Athlon. * * All processors since the Pentium III and the Athlon 4 have SSE support, so * we use that to detect. That leaves the original Athlon, Pentium Pro and * Pentium II. */ # if defined(_M_IX86) # define Q_PROCESSOR_X86 (_M_IX86/100) # elif defined(__i686__) || defined(__athlon__) || defined(__SSE__) # define Q_PROCESSOR_X86 6 # elif defined(__i586__) || defined(__k6__) # define Q_PROCESSOR_X86 5 # elif defined(__i486__) # define Q_PROCESSOR_X86 4 # else # define Q_PROCESSOR_X86 3 # endif #elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) # define Q_PROCESSOR_X86 6 # define Q_PROCESSOR_X86_64 # define Q_BYTE_ORDER Q_LITTLE_ENDIAN # define Q_PROCESSOR_WORDSIZE 8 /* Itanium (IA-64) family, no revisions or variants Itanium is bi-endian, use endianness auto-detection implemented below. */ #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) # define Q_PROCESSOR_IA64 # define Q_PROCESSOR_WORDSIZE 8 // Q_BYTE_ORDER not defined, use endianness auto-detection /* MIPS family, known revisions: I, II, III, IV, 32, 64 MIPS is bi-endian, use endianness auto-detection implemented below. */ #elif defined(__mips) || defined(__mips__) || defined(_M_MRX000) # define Q_PROCESSOR_MIPS # if defined(_MIPS_ARCH_MIPS1) || (defined(__mips) && __mips - 0 >= 1) # define Q_PROCESSOR_MIPS_I # endif # if defined(_MIPS_ARCH_MIPS2) || (defined(__mips) && __mips - 0 >= 2) # define Q_PROCESSOR_MIPS_II # endif # if defined(_MIPS_ARCH_MIPS32) || defined(__mips32) # define Q_PROCESSOR_MIPS_32 # endif # if defined(_MIPS_ARCH_MIPS3) || (defined(__mips) && __mips - 0 >= 3) # define Q_PROCESSOR_MIPS_III # endif # if defined(_MIPS_ARCH_MIPS4) || (defined(__mips) && __mips - 0 >= 4) # define Q_PROCESSOR_MIPS_IV # endif # if defined(_MIPS_ARCH_MIPS5) || (defined(__mips) && __mips - 0 >= 5) # define Q_PROCESSOR_MIPS_V # endif # if defined(_MIPS_ARCH_MIPS64) || defined(__mips64) # define Q_PROCESSOR_MIPS_64 # define Q_PROCESSOR_WORDSIZE 8 # endif # if defined(__MIPSEL__) # define Q_BYTE_ORDER Q_LITTLE_ENDIAN # elif defined(__MIPSEB__) # define Q_BYTE_ORDER Q_BIG_ENDIAN # else // Q_BYTE_ORDER not defined, use endianness auto-detection # endif /* Power family, known variants: 32- and 64-bit There are many more known variants/revisions that we do not handle/detect. See http://en.wikipedia.org/wiki/Power_Architecture and http://en.wikipedia.org/wiki/File:PowerISA-evolution.svg Power is bi-endian, use endianness auto-detection implemented below. */ #elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \ || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \ || defined(_M_MPPC) || defined(_M_PPC) # define Q_PROCESSOR_POWER # if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) # define Q_PROCESSOR_POWER_64 # define Q_PROCESSOR_WORDSIZE 8 # else # define Q_PROCESSOR_POWER_32 # endif // Q_BYTE_ORDER not defined, use endianness auto-detection /* S390 family, known variant: S390X (64-bit) S390 is big-endian. */ #elif defined(__s390__) # define Q_PROCESSOR_S390 # if defined(__s390x__) # define Q_PROCESSOR_S390_X # endif # define Q_BYTE_ORDER Q_BIG_ENDIAN /* SuperH family, optional revision: SH-4A SuperH is bi-endian, use endianness auto-detection implemented below. */ // #elif defined(__sh__) // # define Q_PROCESSOR_SH // # if defined(__sh4a__) // # define Q_PROCESSOR_SH_4A // # endif // Q_BYTE_ORDER not defined, use endianness auto-detection /* SPARC family, optional revision: V9 SPARC is big-endian only prior to V9, while V9 is bi-endian with big-endian as the default byte order. Assume all SPARC systems are big-endian. */ #elif defined(__sparc__) # define Q_PROCESSOR_SPARC # if defined(__sparc_v9__) # define Q_PROCESSOR_SPARC_V9 # endif # if defined(__sparc64__) # define Q_PROCESSOR_SPARC_64 # endif # define Q_BYTE_ORDER Q_BIG_ENDIAN #endif /* NOTE: GCC 4.6 added __BYTE_ORDER__, __ORDER_BIG_ENDIAN__, __ORDER_LITTLE_ENDIAN__ and __ORDER_PDP_ENDIAN__ in SVN r165881. If you are using GCC 4.6 or newer, this code will properly detect your target byte order; if you are not, and the __LITTLE_ENDIAN__ or __BIG_ENDIAN__ macros are not defined, then this code will fail to detect the target byte order. */ // Some processors support either endian format, try to detect which we are using. #if !defined(Q_BYTE_ORDER) # if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == Q_BIG_ENDIAN || __BYTE_ORDER__ == Q_LITTLE_ENDIAN) // Reuse __BYTE_ORDER__ as-is, since our Q_*_ENDIAN #defines match the preprocessor defaults # define Q_BYTE_ORDER __BYTE_ORDER__ # elif defined(__BIG_ENDIAN__) || defined(_big_endian__) || defined(_BIG_ENDIAN) # define Q_BYTE_ORDER Q_BIG_ENDIAN # elif defined(__LITTLE_ENDIAN__) || defined(_little_endian__) || defined(_LITTLE_ENDIAN) \ || defined(_WIN32_WCE) || defined(WINAPI_FAMILY) // Windows CE is always little-endian according to MSDN. # define Q_BYTE_ORDER Q_LITTLE_ENDIAN # else # error "Unable to determine byte order!" # endif #endif /* Size of a pointer and the machine register size. We detect a 64-bit system by: * GCC and compatible compilers (Clang, ICC on OS X and Windows) always define __SIZEOF_POINTER__. This catches all known cases of ILP32 builds on 64-bit processors. * Most other Unix compilers define __LP64__ or _LP64 on 64-bit mode (Long and Pointer 64-bit) * If Q_PROCESSOR_WORDSIZE was defined above, it's assumed to match the pointer size. Otherwise, we assume to be 32-bit and then check in qglobal.cpp that it is right. */ #if defined __SIZEOF_POINTER__ # define QT_POINTER_SIZE __SIZEOF_POINTER__ #elif defined(__LP64__) || defined(_LP64) # define QT_POINTER_SIZE 8 #elif defined(Q_PROCESSOR_WORDSIZE) # define QT_POINTER_SIZE Q_PROCESSOR_WORDSIZE #else # define QT_POINTER_SIZE 4 #endif /* Define Q_PROCESSOR_WORDSIZE to be the size of the machine's word (usually, the size of the register). On some architectures where a pointer could be smaller than the register, the macro is defined above. Falls back to QT_POINTER_SIZE if not set explicitly for the platform. */ #ifndef Q_PROCESSOR_WORDSIZE # define Q_PROCESSOR_WORDSIZE QT_POINTER_SIZE #endif #endif // QPROCESSORDETECTION_H QtAV-1.12.0/tests/decoder/000077500000000000000000000000001312235004300151465ustar00rootroot00000000000000QtAV-1.12.0/tests/decoder/decoder.pro000066400000000000000000000003361312235004300172770ustar00rootroot00000000000000greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle TEMPLATE = app TARGET = decoder PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tests/decoder/main.cpp000066400000000000000000000051161312235004300166010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString file = QString::fromLatin1("test.avi"); int idx = a.arguments().indexOf(QLatin1String("-f")); if (idx > 0) file = a.arguments().at(idx + 1); QString decName = QString::fromLatin1("FFmpeg"); idx = a.arguments().indexOf(QLatin1String("-vc")); if (idx < 0) idx = a.arguments().indexOf(QLatin1String("-vd")); if (idx > 0) decName = a.arguments().at(idx + 1); QString opt; QVariantHash decopt; idx = decName.indexOf(QLatin1String(":")); if (idx > 0) { opt = decName.right(decName.size() - idx -1); decName = decName.left(idx); QStringList opts(opt.split(QString::fromLatin1(";"))); QVariantHash subopt; foreach (QString o, opts) { idx = o.indexOf(QLatin1String(":")); subopt[o.left(idx)] = o.right(o.size() - idx - 1); } decopt[decName] = subopt; } qDebug() << decopt; VideoDecoder *dec = VideoDecoder::create(decName.toLatin1().constData()); if (!dec) { fprintf(stderr, "Can not find decoder: %s\n", decName.toUtf8().constData()); return 1; } if (!decopt.isEmpty()) dec->setOptions(decopt); AVDemuxer demux; demux.setMedia(file); if (!demux.load()) { qWarning("Failed to load file: %s", file.toUtf8().constData()); return 1; } dec->setCodecContext(demux.videoCodecContext()); dec->open(); int count = 0; int vstream = demux.videoStream(); QQueue t; qint64 t0 = QDateTime::currentMSecsSinceEpoch(); while (!demux.atEnd()) { if (!demux.readFrame()) continue; if (demux.stream() != vstream) continue; const Packet pkt = demux.packet(); if (dec->decode(pkt)) { VideoFrame frame = dec->frame(); // why is faster to call frame() for hwdec? no frame() is very slow for VDA Q_UNUSED(frame); count++; const qint64 now = QDateTime::currentMSecsSinceEpoch(); const qint64 dt = now - t0; t.enqueue(now); printf("decode count: %d, elapsed: %lld, fps: %.1f/%.1f\r", count, dt, count*1000.0/dt, t.size()*1000.0/(now - t.first()));fflush(0); if (t.size() > 10) t.dequeue(); } } return 0; } QtAV-1.12.0/tests/extract/000077500000000000000000000000001312235004300152135ustar00rootroot00000000000000QtAV-1.12.0/tests/extract/extract.pro000066400000000000000000000003771312235004300174160ustar00rootroot00000000000000TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tests/extract/main.cpp000066400000000000000000000070751312235004300166540ustar00rootroot00000000000000/****************************************************************************** VideoGroup: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include using namespace QtAV; class VideoFrameObserver : public QObject { Q_OBJECT public: VideoFrameObserver(QObject *parent = 0) : QObject(parent) , pos(0) , nb(1) , extracted(0) { view = VideoRenderer::create(VideoRendererId_GLWidget2); view->widget()->resize(400, 300); view->widget()->show(); connect(&extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), this, SLOT(onVideoFrameExtracted(QtAV::VideoFrame))); } void setParameters(qint64 msec, int count) { pos = msec; nb = count; } void start(const QString& file) { extractor.setAsync(false); extractor.setSource(file); startTimer(20); timer.start(); extractor.setPosition(pos); } void startAsync(const QString& file) { extractor.setAsync(true); extractor.setSource(file); startTimer(20); timer.start(); extractor.setPosition(pos); } public Q_SLOTS: void onVideoFrameExtracted(const QtAV::VideoFrame& frame) { view->receive(frame); qApp->processEvents(); frame.toImage().save(QString::fromLatin1("%1.png").arg(frame.timestamp())); qDebug("frame %dx%d @%f", frame.width(), frame.height(), frame.timestamp()); if (++extracted >= nb) { qDebug("elapsed: %lld.", timer.elapsed()); return; } extractor.setPosition(pos + extracted*1000); } protected: void timerEvent(QTimerEvent *) { qApp->processEvents(); // avoid ui blocking if async is not used } private: VideoRenderer *view; qint64 pos; int nb; int extracted; VideoFrameExtractor extractor; QElapsedTimer timer; }; int main(int argc, char** argv) { QApplication a(argc, argv); int idx = a.arguments().indexOf(QLatin1String("-f")); if (idx < 0) { qDebug("-f file -t sec -n count -asyc"); return -1; } QString file = a.arguments().at(idx+1); idx = a.arguments().indexOf(QLatin1String("-t")); int t = 0; if (idx > 0) t = a.arguments().at(idx+1).toInt(); int n = 1; idx = a.arguments().indexOf(QLatin1String("-n")); if (idx > 0) n = a.arguments().at(idx+1).toInt(); bool async = a.arguments().contains(QString::fromLatin1("-async")); VideoFrameObserver obs; obs.setParameters(t*1000, n); if (async) obs.startAsync(file); else obs.start(file); return a.exec(); } #include "main.moc" QtAV-1.12.0/tests/playerthread/000077500000000000000000000000001312235004300162255ustar00rootroot00000000000000QtAV-1.12.0/tests/playerthread/main.cpp000066400000000000000000000032101312235004300176510ustar00rootroot00000000000000/****************************************************************************** VideoGroup: this file is part of QtAV examples Copyright (C) 2013-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include using namespace QtAV; class Thread : public QThread { public: Thread(AVPlayer *player): QThread(0) , mpPlayer(player) {} protected: virtual void run() { //mpPlayer->play(); exec(); } AVPlayer *mpPlayer; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); AVPlayer player; WidgetRenderer renderer; renderer.show(); player.addVideoRenderer(&renderer); player.setFile(a.arguments().last()); Thread thread(&player); player.moveToThread(&thread); thread.start(); player.play(); return a.exec(); } QtAV-1.12.0/tests/playerthread/playerthread.pro000066400000000000000000000006351312235004300214370ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2014-01-28T10:24:19 # #------------------------------------------------- TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tests/qiodevice/000077500000000000000000000000001312235004300155115ustar00rootroot00000000000000QtAV-1.12.0/tests/qiodevice/main.cpp000066400000000000000000000036541312235004300171510ustar00rootroot00000000000000/****************************************************************************** VideoGroup: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QApplication a(argc, argv); QFile vidfile(a.arguments().last()); if (!vidfile.open(QIODevice::ReadOnly)) return 1; AVPlayer player[2]; WidgetRenderer renderer[2]; renderer[0].show(); renderer[0].widget()->setWindowTitle(QString::fromLatin1("Test QFile")); renderer[1].show(); renderer[1].widget()->setWindowTitle(QString::fromLatin1("Test QBuffer. Play <=1M video from memory")); player[0].addVideoRenderer(&renderer[0]); player[1].addVideoRenderer(&renderer[1]); QByteArray data = vidfile.read(1024*1024); vidfile.seek(0); QBuffer buf(&data); if (buf.open(QIODevice::ReadOnly)) { player[1].setIODevice(&buf); } player[0].setIODevice(&vidfile); player[0].play(); player[1].play(); return a.exec(); } QtAV-1.12.0/tests/qiodevice/qiodevice.pro000066400000000000000000000006361312235004300202100ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2014-01-28T10:24:19 # #------------------------------------------------- TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tests/qrc/000077500000000000000000000000001312235004300143265ustar00rootroot00000000000000QtAV-1.12.0/tests/qrc/main.cpp000066400000000000000000000027031312235004300157600ustar00rootroot00000000000000/****************************************************************************** Simple Player: this file is part of QtAV examples Copyright (C) 2014-2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include int main(int argc, char *argv[]) { QtAV::Widgets::registerRenderers(); QApplication a(argc, argv); QtAV::VideoOutput renderer; renderer.widget()->show(); renderer.widget()->setWindowTitle(QString::fromLatin1("Play video from qrc--QtAV %1 wbsecg1@gmail.com").arg(QtAV_Version_String_Long())); QtAV::AVPlayer player; player.setRenderer(&renderer); player.play(QString::fromLatin1("qrc:/test.mp4")); return a.exec(); } QtAV-1.12.0/tests/qrc/media.qrc000066400000000000000000000001271312235004300161140ustar00rootroot00000000000000 test.mp4 QtAV-1.12.0/tests/qrc/qrc.pro000066400000000000000000000005431312235004300156370ustar00rootroot00000000000000TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) include($$PROJECTROOT/widgets/libQtAVWidgets.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp exists(test.mp4) { RESOURCES += media.qrc } else { warning("put test.mp4 in this directory!") } QtAV-1.12.0/tests/subtitle/000077500000000000000000000000001312235004300153745ustar00rootroot00000000000000QtAV-1.12.0/tests/subtitle/main.cpp000066400000000000000000000070631312235004300170320ustar00rootroot00000000000000#include #include #include #include #include #include using namespace QtAV; class SubtitleObserver : public QObject { Q_OBJECT public: SubtitleObserver(QObject* parent = 0) : QObject(parent) {} void observe(Subtitle* sub) { connect(sub, SIGNAL(contentChanged()), this, SLOT(onSubtitleChanged()));} private slots: void onSubtitleChanged() { Subtitle *sub = qobject_cast(sender()); qDebug() << "subtitle changed at " << sub->timestamp() << "s\n" << sub->getText(); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "help: ./subtitle [-engine engine] [-f file] [-fuzzy] [-t sec] [-t1 sec] [-count n]"; qDebug() << "-fuzzy: fuzzy match subtitle name"; qDebug() << "-t: set subtitle begin time"; qDebug() << "-t1: set subtitle end time"; qDebug() << "-count: set subtitle frame count from t to t1"; qDebug() << "-engine: subtitle processing engine, can be 'ffmpeg' and 'libass'"; qDebug() << "-dir: add subtitle search directories"; QString file; bool fuzzy = false; int t = -1, t1 = -1, count = 1; int i = a.arguments().indexOf(QLatin1String("-f")); if (i > 0) { file = a.arguments().at(i+1); } i = a.arguments().indexOf(QLatin1String("-fuzzy")); if (i > 0) fuzzy = true; i = a.arguments().indexOf(QLatin1String("-t")); if (i > 0) t = a.arguments().at(i+1).toInt(); i = a.arguments().indexOf(QLatin1String("-t1")); if (i > 0) t1 = a.arguments().at(i+1).toInt(); i = a.arguments().indexOf(QLatin1String("-count")); if (i > 0) count = a.arguments().at(i+1).toInt(); QString engine; i = a.arguments().indexOf(QLatin1String("-engine")); if (i > 0) engine = a.arguments().at(i+1); QStringList dirs; i = a.arguments().indexOf(QLatin1String("-dir")); while (i > 0) { dirs += a.arguments().at(i+1).split(QLatin1Char(';')); i = a.arguments().indexOf(QLatin1String("-dir"), i+2); } Subtitle sub; if (!engine.isEmpty()) sub.setEngines(QStringList() << engine); qDebug() << "supported extensions: " << sub.supportedSuffixes(); if (file.isEmpty()) return 0; sub.setFileName(file); sub.setDirs(dirs); sub.setFuzzyMatch(fuzzy); SubtitleObserver sob; sob.observe(&sub); QElapsedTimer timer; timer.start(); sub.load(); if (!sub.isLoaded()) return -1; qDebug() << "process subtitle file elapsed: " << timer.elapsed() << "ms"; timer.restart(); if (t < 0 && t1 >= 0) { t = 0; count = 1; } if (t >= 0) { if (t1 <= t) { sub.setTimestamp(qreal(t)); qDebug() << sub.timestamp() << "s: " << sub.getText(); QImage img(sub.getImage(720, 400)); img.save(QString::fromLatin1("sub-%1.png").arg(sub.timestamp(), 0, 'f', 2)); } else { if (count < 2) count = 2; const qreal kInterval = (t1-t)/qreal(count-1); for (int n = 0; n < count; ++n) { sub.setTimestamp(qreal(t) + qreal(n)*kInterval); qDebug() << sub.timestamp() << "s: " << sub.getText(); QImage img(sub.getImage(720, 400)); img.save(QString::fromLatin1("sub-%1.png").arg(sub.timestamp(), 0, 'f', 2)); } } } qDebug() << "find subtitle content elapsed: " << timer.elapsed() << "ms"; return 0; } #include "main.moc" QtAV-1.12.0/tests/subtitle/subtitle.pro000066400000000000000000000004771312235004300177610ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2014-08-05T17:12:03 # #------------------------------------------------- TARGET = subtitle CONFIG -= app_bundle PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tests/tests.pro000066400000000000000000000002561312235004300154300ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ ao \ decoder \ subtitle \ transcode !no-widgets { SUBDIRS += \ extract \ qiodevice \ qrc \ playerthread } QtAV-1.12.0/tests/transcode/000077500000000000000000000000001312235004300155235ustar00rootroot00000000000000QtAV-1.12.0/tests/transcode/main.cpp000066400000000000000000000140171312235004300171560ustar00rootroot00000000000000/****************************************************************************** transcode: this file is part of QtAV examples Copyright (C) 2015 Wang Bin * This file is part of QtAV This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include #include #include #include #include #include #include #include using namespace QtAV; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString file = QString::fromLatin1("test.avi"); int idx = a.arguments().indexOf(QLatin1String("-i")); if (idx > 0) file = a.arguments().at(idx + 1); QString decName = QString::fromLatin1("FFmpeg"); idx = a.arguments().indexOf(QLatin1String("-d:v")); if (idx > 0) decName = a.arguments().at(idx + 1); QString outFile = QString::fromLatin1("/tmp/out.mp4"); idx = a.arguments().indexOf(QLatin1String("-o")); if (idx > 0) outFile = a.arguments().at(idx + 1); QDir().mkpath(outFile.left(outFile.lastIndexOf(QLatin1Char('/'))+1)); QString cv = QString::fromLatin1("libx264"); idx = a.arguments().indexOf(QLatin1String("-c:v")); if (idx > 0) cv = a.arguments().at(idx + 1); QString fmt; idx = a.arguments().indexOf(QLatin1String("-f")); if (idx > 0) fmt = a.arguments().at(idx + 1); QString opt; QVariantHash decopt; idx = decName.indexOf(QLatin1String(":")); if (idx > 0) { opt = decName.right(decName.size() - idx -1); decName = decName.left(idx); QStringList opts(opt.split(QString::fromLatin1(";"))); QVariantHash subopt; foreach (QString o, opts) { idx = o.indexOf(QLatin1String(":")); subopt[o.left(idx)] = o.right(o.size() - idx - 1); } decopt[decName] = subopt; } qDebug() << decopt; VideoDecoder *dec = VideoDecoder::create(decName.toLatin1().constData()); if (!dec) { qWarning("Can not find decoder: %s", decName.toUtf8().constData()); return 1; } if (!decopt.isEmpty()) dec->setOptions(decopt); AVDemuxer demux; demux.setMedia(file); if (!demux.load()) { qWarning("Failed to load file: %s", file.toUtf8().constData()); return 1; } dec->setCodecContext(demux.videoCodecContext()); dec->open(); QElapsedTimer timer; timer.start(); int count = 0; int vstream = demux.videoStream(); VideoEncoder *venc = VideoEncoder::create("FFmpeg"); venc->setCodecName(cv); //venc->setCodecName("png"); venc->setBitRate(1024*1024); //venc->setPixelFormat(VideoFormat::Format_RGBA32); AVMuxer mux; //mux.setMedia("/Users/wangbin/Movies/m3u8/bbb%05d.ts"); //mux.setMedia("/Users/wangbin/Movies/img2/bbb%05d.png"); mux.setMedia(outFile); QVariantHash muxopt, avfopt; avfopt[QString::fromLatin1("segment_time")] = 4; avfopt[QString::fromLatin1("segment_list_size")] = 0; avfopt[QString::fromLatin1("segment_list")] = outFile.left(outFile.lastIndexOf(QLatin1Char('/'))+1).append(QString::fromLatin1("index.m3u8")); avfopt[QString::fromLatin1("segment_format")] = QString::fromLatin1("mpegts"); muxopt[QString::fromLatin1("avformat")] = avfopt; qreal fps = 0; while (!demux.atEnd()) { if (!demux.readFrame()) continue; if (demux.stream() != vstream) continue; const Packet pkt = demux.packet(); if (dec->decode(pkt)) { VideoFrame frame = dec->frame(); // why is faster to call frame() for hwdec? no frame() is very slow for VDA if (!frame) continue; if (!venc->isOpen()) { venc->setWidth(frame.width()); venc->setHeight(frame.height()); if (!venc->open()) { qWarning("failed to open encoder"); return 1; } } if (!mux.isOpen()) { mux.copyProperties(venc); mux.setOptions(muxopt); if (!fmt.isEmpty()) mux.setFormat(fmt); //mux.setFormat("segment"); // mux.setFormat("image2"); if (!mux.open()) { qWarning("failed to open muxer"); return 1; } //mux.setOptions(muxopt); } if (frame.pixelFormat() != venc->pixelFormat()) frame = frame.to(venc->pixelFormat()); if (venc->encode(frame)) { Packet pkt(venc->encoded()); mux.writeVideo(pkt); count++; if (count%20 == 0) { fps = qreal(count*1000)/qreal(timer.elapsed()); } printf("decode count: %d, fps: %.2f frame size: %dx%d %d\r", count, fps, frame.width(), frame.height(), frame.data().size());fflush(0); } } } // get delayed frames while (venc->encode()) { qDebug("encode delayed frames...\r"); Packet pkt(venc->encoded()); mux.writeVideo(pkt); } qint64 elapsed = timer.elapsed(); int msec = elapsed/1000LL+1; qDebug("decoded frames: %d, time: %d, average speed: %d", count, msec, count/msec); venc->close(); mux.close(); return 0; } QtAV-1.12.0/tests/transcode/transcode.pro000066400000000000000000000002601312235004300202250ustar00rootroot00000000000000CONFIG -= app_bundle TEMPLATE = app TARGET = transcode PROJECTROOT = $$PWD/../.. include($$PROJECTROOT/src/libQtAV.pri) preparePaths($$OUT_PWD/../../out) SOURCES += main.cpp QtAV-1.12.0/tools/000077500000000000000000000000001312235004300135375ustar00rootroot00000000000000QtAV-1.12.0/tools/build_ffmpeg/000077500000000000000000000000001312235004300161625ustar00rootroot00000000000000QtAV-1.12.0/tools/ci/000077500000000000000000000000001312235004300141325ustar00rootroot00000000000000QtAV-1.12.0/tools/ci/after_success.sh000077500000000000000000000015601312235004300173240ustar00rootroot00000000000000exit 0 set -ev # download ifw for linux, mingw # upload packages # TRAVIS_TAG echo TRAVIS_TAG=$TRAVIS_TAG PKG=$TRAVIS_BUILD_DIR/$QTAV_OUT.tar.xz tar Jcvf $PKG $QTAV_OUT/{bin,lib*,build.log} wget http://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz/download -O sshpass.tar.gz tar zxf sshpass.tar.gz cd sshpass-1.05 ./configure make sudo make install export PATH=$PWD:$PATH cd $TRAVIS_BUILD_DIR type -a sshpass # ignore known hosts: -o StrictHostKeyChecking=no sshpass -p $SF_PWD scp -o StrictHostKeyChecking=no $PKG $SF_USER,qtav@frs.sourceforge.net:/home/frs/project/q/qt/qtav/ci/ #rsync --password-file=$PWF -avP -e ssh $PKG $SF_USER@frs.sourceforge.net:/home/frs/project/qtav/ci/ #curl -H "Accept: application/json" -X PUT -d "default=linux&default=bsd&default=solaris" -d "api_key=$SF_API_KEY" https://sourceforge.net/projects/qtav/files/ci/$PKG QtAV-1.12.0/tools/ci/linux/000077500000000000000000000000001312235004300152715ustar00rootroot00000000000000QtAV-1.12.0/tools/ci/linux/after_success.sh000077700000000000000000000000001312235004300240512../after_success.shustar00rootroot00000000000000QtAV-1.12.0/tools/ci/linux/install.sh000077500000000000000000000015561312235004300173050ustar00rootroot00000000000000set -ev sudo apt-get update -qq sudo apt-get install -qq -y libegl1-mesa-dev portaudio19-dev libpulse-dev libopenal-dev libva-dev libass-dev libxv-dev parallel # TODO: do not use apt, use the latest code instead wget http://sourceforge.net/projects/qtav/files/depends/FFmpeg/linux/${AV}-linux-x86+x64.tar.xz/download -O ${AV}-linux-x86+x64.tar.xz tar Jxf ${AV}-linux-x86+x64.tar.xz wget http://sourceforge.net/projects/buildqt/files/lite/Qt${QT}-Linux64.tar.xz/download -O Qt${QT}-Linux64.tar.xz tar -I xz -xf Qt${QT}-Linux64.tar.xz export FFMPEG_DIR=$PWD/${AV}-linux-x86+x64 export PATH=$PWD/Qt${QT}-Linux64/bin:$PATH export CPATH=$FFMPEG_DIR/include export LIBRARY_PATH=$FFMPEG_DIR/lib/x64 export LD_LIBRARY_PATH=$FFMPEG_DIR/lib/x64 export QTAV_OUT=QtAV-Qt${QT}-${AV}-ubuntu1204-${TRAVIS_COMMIT:0:7} mkdir -p $QTAV_OUT #if [ "$TRAVIS_BRANCH" == "prelease" ]; then #linux QtAV-1.12.0/tools/ci/linux/script.sh000077500000000000000000000006121312235004300171330ustar00rootroot00000000000000set -ev echo "$TRAVIS_BUILD_DIR" tail -n 27 /proc/cpuinfo uname -a cat /etc/issue echo "QtAV build script for travis-ci" jobs=`cat /proc/cpuinfo |grep 'cpu cores' |wc -l` cd $QTAV_OUT rm -f build.log type -a moc SPEC=linux-clang test -n "$CC" && test "${CC/gcc/}" != "$CC" && SPEC=linux-g++ qmake -r $TRAVIS_BUILD_DIR -spec $SPEC "CONFIG+=recheck" make -j$((jobs+1)) cd $TRAVIS_BUILD_DIR QtAV-1.12.0/tools/ci/osx/000077500000000000000000000000001312235004300147435ustar00rootroot00000000000000QtAV-1.12.0/tools/ci/osx/after_success.sh000077500000000000000000000016001312235004300201300ustar00rootroot00000000000000PKG=$TRAVIS_BUILD_DIR/${QTAV_OUT}-Player.dmg set -ev cd $QTAV_OUT chmod +x *.sh ./sdk_install.sh mkdir -p bin/Player.app/Contents/Resources/qml/QtAV cp -avf lib_*/*.dylib bin/Player.app/Contents/Resources/qml/QtAV cp -avf $TRAVIS_BUILD_DIR/qml/{plugins.qmltypes,Video.qml,qmldir} bin/Player.app/Contents/Resources/qml/QtAV cp -avf $TRAVIS_BUILD_DIR/tools/sdk_osx.sh bin/Player.app/ cd bin macdeployqt Player.app -dmg hdiutil convert -format UDBZ Player.dmg -o $PKG cd - wget http://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz/download -O sshpass.tar.gz tar zxf sshpass.tar.gz cd sshpass-1.05 ./configure make sudo make install export PATH=$PWD:$PATH cd $TRAVIS_BUILD_DIR type -a sshpass # ignore known hosts: -o StrictHostKeyChecking=no sshpass -p $SF_PWD scp -o StrictHostKeyChecking=no $PKG $SF_USER,qtav@frs.sourceforge.net:/home/frs/project/q/qt/qtav/ci/ exit 0 QtAV-1.12.0/tools/ci/osx/install.sh000077500000000000000000000005611312235004300167520ustar00rootroot00000000000000#!/bin/bash set -ev brew update brew install ffmpeg qt5 uchardet ls /usr/local/bin export QTAV_OUT=QtAV-OSX-${TRAVIS_COMMIT:0:7} mkdir -p $QTAV_OUT export PATH=/usr/local/opt/qt5/bin:$PATH ## /usr/local/include is fine too export CPATH=/usr/local/opt/ffmpeg/include:/usr/local/opt/libass/include export LIBRARY_PATH=/usr/local/opt/ffmpeg/lib:/usr/local/opt/libass/lib QtAV-1.12.0/tools/ci/osx/script.sh000077500000000000000000000003511312235004300166050ustar00rootroot00000000000000set -ev echo "$TRAVIS_BUILD_DIR" sysctl -b machdep.cpu sysctl -n hw.ncpu uname -a jobs=`sysctl -n hw.ncpu` cd $QTAV_OUT rm -f build.log type -a moc qmake -r $TRAVIS_BUILD_DIR "CONFIG+=recheck" make -j$jobs cd $TRAVIS_BUILD_DIR QtAV-1.12.0/tools/ci/travis_build.sh000077500000000000000000000042671312235004300171710ustar00rootroot00000000000000#!/bin/bash # http://docs.travis-ci.com/user/installing-dependencies/ # http://docs.travis-ci.com/user/customizing-the-build/ # http://docs.travis-ci.com/user/build-configuration/ # - sudo add-apt-repository ppa:wsnipex/vaapi set -ev echo "$TRAVIS_BUILD_DIR" tail -n 27 /proc/cpuinfo uname -a cat /etc/issue echo "QtAV build script for travis-ci" WORK_DIR=$PWD echo "WORK_DIR=$WORK_DIR" mkdir -p build cd build #ffmpeg 2.3 libavutil clock_gettime need -lrt if glibc < 2.17 FFMPEG=$1 : ${FFMPEG:=ffmpeg-2.2.5} FFVER=(1.0.10 1.2.10 2.0.6 2.2.11) QTVER=(4.8.6 5.0.2 5.1.1 5.2.1) MKSPEC=(linux-clang) cat > Makefile.download <&1 |tee build.log cd $WORK_DIR/build } buildqtav() { local FF=$1 local VER=$2 local QT=$3 local FFSDK=$WORK_DIR/build/$FF-$VER-linux-x86+x64 export PATH=$WORK_DIR/build/${QT}-Linux64/bin:$PATH export CPATH=$FFSDK/include export LIBRARY_PATH=$FFSDK/lib/x64 export LD_LIBRARY_PATH=$FFSDK/lib/x64 echo "PATH=$PATH" echo "CPATH=$CPATH" echo "building... with $QT $FFSDK" OUT=qtav_${QT}_$FF$VER mkdir -p $OUT cd $OUT for spec in ${MKSPEC[@]}; do build_spec $spec cd $OUT ln -sf $FFSDK/lib/x64/* $spec/bin done cd $WORK_DIR/build } time ( for (( i=0; i<${#FFVER[@]}; i++ )); do buildqtav ffmpeg "${FFVER[i]}" "Qt${QTVER[i]}" done ) QtAV-1.12.0/tools/ci/win/000077500000000000000000000000001312235004300147275ustar00rootroot00000000000000QtAV-1.12.0/tools/ci/win/build.bat000066400000000000000000000006171312235004300165220ustar00rootroot00000000000000echo cc=%cc% where qbs if "%cc%" == "MinGW" ( qmake %APPVEYOR_BUILD_FOLDER% "CONFIG+=%mode%" mingw32-make -j%NUMBER_OF_PROCESSORS% ) else ( if "%mode%" == "debug" ( qmake %APPVEYOR_BUILD_FOLDER% -r -tp vc "CONFIG+=debug" msbuild /p:Configuration=DEBUG ) else ( qmake %APPVEYOR_BUILD_FOLDER% where jom if %ERRORLEVEL% == 0 ( jom ) else ( nmake ) ) ) QtAV-1.12.0/tools/ci/win/deploy_win.bat000066400000000000000000000061151312235004300175730ustar00rootroot00000000000000:: TODO: use windeployqt copy /y %QTDIR%\bin\av*.dll bin copy /y %QTDIR%\bin\sw*.dll bin if exist %QTDIR%\bin\ass.dll copy /y %QTDIR%\bin\ass.dll bin if exist %QTDIR%\bin\libass.dll copy /y %QTDIR%\bin\libass.dll bin if exist %QTDIR%\bin\po*.dll copy /y %QTDIR%\bin\po*.dll bin if exist %QTDIR%\bin\msvc*.dll copy /y %QTDIR%\bin\msvc*.dll bin if exist %QTDIR%\bin\OpenAL32*.dll copy /y %QTDIR%\bin\OpenAL32*.dll bin if exist %QTDIR%\bin\OpenAL32-%cc%.dll copy /y %QTDIR%\bin\OpenAL32-%cc%.dll bin\OpenAL32.dll if exist bin\OpenAL32-*.dll del bin\OpenAL32-*.dll if exist %QTDIR%\bin\D3DCompiler_*.dll copy /y %QTDIR%\bin\D3DCompiler_*.dll bin :: libEGL, GLESv2, gcc_s_dw2-1, stdc++-6, winpthread-1 if exist %QTDIR%\bin\lib*.dll copy /y %QTDIR%\bin\lib*.dll bin echo [Paths] > bin\qt.conf echo Prefix=. >> bin\qt.conf if "%mode%" == "debug" ( if exist bin\libEGL*.dll del bin\libEGL*.dll if exist bin\libGLESv2*.dll del bin\libGLESv2*.dll ) else ( if exist %QTDIR%\qml\QtQuick.2 xcopy /syi %QTDIR%\qml\QtQuick.2 bin\qml\QtQuick.2 > NUL if exist %QTDIR%\plugins\platforms xcopy /syi %QTDIR%\plugins\platforms bin\plugins\platforms > NUL if exist %QTDIR%\plugins\imageformats xcopy /syi %QTDIR%\plugins\imageformats bin\plugins\imageformats > NUL if exist %QTDIR%\plugins\iconengines xcopy /syi %QTDIR%\plugins\iconengines bin\plugins\iconengines > NUL if exist %QTDIR%\plugins\sqldrivers xcopy /syi %QTDIR%\plugins\sqldrivers bin\plugins\sqldrivers > NUL if exist %QTDIR%\qml\Qt xcopy /syi %QTDIR%\qml\Qt bin\qml\Qt > NUL if exist %QTDIR%\qml\QtQml xcopy /syi %QTDIR%\qml\QtQml bin\qml\QtQml > NUL if exist %QTDIR%\qml\QtQuick xcopy /syi %QTDIR%\qml\QtQuick bin\qml\QtQuick > NUL if exist %QTDIR%\bin\Qt5Core.dll copy /y %QTDIR%\bin\Qt5Core.dll bin if exist %QTDIR%\bin\Qt5Gui.dll copy /y %QTDIR%\bin\Qt5Gui.dll bin if exist %QTDIR%\bin\Qt5OpenGL.dll copy /y %QTDIR%\bin\Qt5OpenGL.dll bin if exist %QTDIR%\bin\Qt5Sql.dll copy /y %QTDIR%\bin\Qt5Sql.dll bin if exist %QTDIR%\bin\Qt5Svg.dll copy /y %QTDIR%\bin\Qt5Svg.dll bin if exist %QTDIR%\bin\Qt5Widgets.dll copy /y %QTDIR%\bin\Qt5Widgets.dll bin if exist %QTDIR%\bin\Qt5Network.dll copy /y %QTDIR%\bin\Qt5Network.dll bin if exist %QTDIR%\bin\Qt5Qml.dll copy /y %QTDIR%\bin\Qt5Qml.dll bin if exist %QTDIR%\bin\Qt5Quick.dll copy /y %QTDIR%\bin\Qt5Quick.dll bin echo deleting debug files if exist bin\lib*GL*d.dll del bin\lib*GL*d.dll :: delete debug dlls. assume no *dd.dll forfiles /S /M *d.dll /P bin /C "cmd /C del @path" forfiles /S /M *.pdb /P bin /C "cmd /C del @path" ) dir bin xcopy /syi tools\install_sdk\mkspecs mkspecs copy /y ..\qtc_packaging\ifw\packages\com.qtav.product.dev\data\sdk_deploy.bat . copy /y ..\qtc_packaging\ifw\packages\com.qtav.product.player\data\*.bat . xcopy /syi ..\src\QtAV include\QtAV xcopy /syi ..\widgets\QtAVWidgets include\QtAVWidgets move lib_* lib_ if exist lib_\*AV*.lib xcopy /syi lib_\*AV*.lib lib if exist lib_\*AV*.a xcopy /syi lib_\*AV*.a lib 7z a %APPVEYOR_BUILD_FOLDER%\QtAV-Qt%qt%-%cc%%arch%-%mode%-%APPVEYOR_REPO_COMMIT:~0,7%.7z bin lib include mkspecs sdk_deploy.bat install.bat uninstall.bat > NUL QtAV-1.12.0/tools/ci/win/install_dep.bat000066400000000000000000000011071312235004300177140ustar00rootroot00000000000000:: TODO: check md5 if not exist AV ( echo downloading qtav dep "http://sourceforge.net/projects/qtav/files/depends/QtAV-depends-windows-x86+x64.7z/download" appveyor DownloadFile "http://sourceforge.net/projects/qtav/files/depends/QtAV-depends-windows-x86+x64.7z/download" -FileName av.7z 7z x av.7z > NUL move QtAV-depends* AV ) xcopy /syi AV\include %QTDIR%\include > NUL if "%arch%" == "x64" ( xcopy /syi AV\lib\x64 %QTDIR%\lib > NUL copy /y AV\bin\x64\*.dll %QTDIR%\bin > NUL ) else ( xcopy /syi AV\lib %QTDIR%\lib > NUL copy /y AV\bin\*.dll %QTDIR%\bin > NUL ) QtAV-1.12.0/tools/deploy_osx.sh000077500000000000000000000120141312235004300162610ustar00rootroot00000000000000# Copyright (C) 2014-2015 Wang Bin if [ $# -lt 1 ]; then echo "${0##*/} QtAV_build_dir" exit 1 fi #set -ev THIS_DIR=$PWD BUILD_DIR=$1 QTBIN=`grep -m 1 QT_BIN $BUILD_DIR/.qmake.cache |cut -d "=" -f 2 | tr -d ' '` #TODO: use last line QTDIR=$QTBIN/.. echo "BUILD_DIR: $BUILD_DIR" echo "QTDIR: $QTDIR" export PATH=$QTBIN:$PATH fix_qt() { local dir=bin/$1.app/Contents local all=`find $dir/Frameworks -name 'Qt*' -type f | grep -E '/Qt[^/.]+$' |grep -v Headers` all+=" "`find $dir -name '*.dylib'` all+=" $dir/MacOS/$1" for one in $all; do echo fixing $one... list=`otool -L $one` for l in $list; do local name=`echo $l | grep -E "[^@].*/Qt[^\.]+\.framework.*[^:]$"` if [ $name ]; then local fw="@executable_path/../Frameworks/"`echo $name | sed -E "s/^.*(Qt[^\.]+.framework.*Qt.*)$/\1/"` install_name_tool -change $name $fw $one fi done done } #TODO: fix_av deploy() { cd $BUILD_DIR local APP=$1 local FRAMEWORK_DIR=bin/${APP}.app/Contents/Frameworks local EXE=bin/${APP}.app/Contents/MacOS/$APP [ -f $THIS_DIR/sdk_osx.sh ] && cp -Lf $THIS_DIR/sdk_osx.sh bin/${APP}.app/ mkdir -p $FRAMEWORK_DIR local LIBCOMMON=`otool -L $EXE |awk '{print $1}' |grep libcommon` [ ! -f $LIBCOMMON ] && LIBCOMMON=lib_*/libcommon.1.dylib if [ -f $LIBCOMMON ]; then cp -Lf $LIBCOMMON $FRAMEWORK_DIR local LIB_PATH=$FRAMEWORK_DIR/${LIBCOMMON/*\//} local LIBCOMMON_SELF=`otool -L $LIB_PATH |awk '{print $1}' |grep -v : |grep libcommon` install_name_tool -id "@executable_path/../Frameworks/${LIBCOMMON/*\//}" $LIB_PATH install_name_tool -change "$LIBCOMMON_SELF" "@executable_path/../Frameworks/${LIBCOMMON/*\//}" $EXE fi local QT_LIBS=`$QTBIN/qmake -query QT_INSTALL_LIBS` local QT_PLUGINS=`$QTBIN/qmake -query QT_INSTALL_PLUGINS` local QT_QML=`$QTBIN/qmake -query QT_INSTALL_QML` #cocoa depends on QtPrintSupport and DBus(5.5), QtSVG & QtPrintSupport depends on QtWidgets QTMODULES="QtCore QtGui QtSvg QtPrintSupport QtWidgets QtDBus QtSql" otool -L $EXE |grep QtOpenGL && QTMODULES+=" QtOpenGL" otool -L $EXE |grep QtQuick && QML="Qt QtQml QtQuick QtQuick.2" && QTMODULES+=" QtQuick QtQml QtNetwork" for q in $QTMODULES; do cp -af $QT_LIBS/${q}.framework $FRAMEWORK_DIR &>/dev/null find $FRAMEWORK_DIR/${q}.framework -name Headers -exec rm -rf {} \; &>/dev/null local Q=$FRAMEWORK_DIR/${q}.framework/Versions/5/${q} test -f $Q && install_name_tool -id @executable_path/../Frameworks/${q}.framework/Versions/5/${q} $Q done mkdir -p $FRAMEWORK_DIR/../PlugIns/{platforms,imageformats,iconengines,sqldrivers} for q in platforms/libqcocoa.dylib imageformats/libqsvg.dylib imageformats/libqjpeg.dylib iconengines/libqsvgicon.dylib sqldrivers/libqsqlite.dylib; do cp -aLf $QT_PLUGINS/${q} $FRAMEWORK_DIR/../PlugIns/${q} done mkdir -p $FRAMEWORK_DIR/../Resources/qml for q in $QML; do cp -aLf $QT_QML/$q $FRAMEWORK_DIR/../Resources/qml done cp -aLf bin/QtAV $FRAMEWORK_DIR/../Resources/qml local AVMODULES="QtAV" otool -L $EXE |grep QtWidgets && AVMODULES+=" QtAVWidgets" for module in $AVMODULES; do rm -rf $FRAMEWORK_DIR/${module}.framework cp -af lib*/${module}.framework $FRAMEWORK_DIR local MODULE_LIB=`otool -D $FRAMEWORK_DIR/${module}.framework/${module} |grep -v ":"` local MODULE_LIB_REL=${MODULE_LIB/*${module}.framework/${module}.framework} if [ "MODULE_LIB" = "MODULE_LIB_REL" ]; then MODULE_LIB_REL=${MODULE_LIB##*/} #a dylib fi echo MODULE_LIB_REL=$MODULE_LIB_REL local LIB_PATH=$FRAMEWORK_DIR/${MODULE_LIB/*\//} install_name_tool -id "@executable_path/../Frameworks/${MODULE_LIB_REL}" $LIB_PATH local EXE_DEP=`otool -L $EXE |awk '{print $1}' |grep -v : |grep "${MODULE_LIB_REL}"` install_name_tool -change "$EXE_DEP" "@executable_path/../Frameworks/${MODULE_LIB_REL}" $EXE done :<bin/${APP}.app/Contents/Resources/qt.conf< $d" echo "\$ORIGIN:\$ORIGIN$d/lib:\$ORIGIN$d" } [ -d "$BINDIR" ] || BINDIR=$PWD qmlso=(`find $BINDIR/qml -name "*.so" -type f`) for q in ${qmlso[@]}; do echo patching $q patchelf --set-rpath `runpath $q $BINDIR/` $q done plugins=(`find $BINDIR/plugins -name "*.so" -type f`) for p in ${plugins[@]}; do echo patching $p patchelf --set-rpath `runpath $p $BINDIR/` $p done for qt in $BINDIR/libQt5*.so.5; do echo patching $qt patchelf --set-rpath '$ORIGIN' $qt done QMLAV=$BINDIR/QtAV/libQmlAV.so test -f $QMLAV && echo "$QMLAV" && patchelf --set-rpath '$ORIGIN/../../lib:$ORIGIN/..:$ORIGIN/../..' $QMLAV QMLAV=$BINDIR/qml/QtAV/libQmlAV.so test -f $QMLAV && echo "$QMLAV" && patchelf --set-rpath '$ORIGIN/../../lib:$ORIGIN/..:$ORIGIN/../..' $QMLAV EXE=(`find $BINDIR -maxdepth 1 -executable -type f |grep -v lib`) for exe in ${EXE[@]}; do echo "patching $exe..." patchelf --set-rpath '$ORIGIN:$ORIGIN/lib:$ORIGIN/../lib' $exe done QtAV-1.12.0/tools/qtav_app_template.pro000066400000000000000000000005761312235004300177770ustar00rootroot00000000000000 # "avwidgets" module is required only for qwidget apps. QML apps only need "av" module greaterThan(QT_MAJOR_VERSION, 4) { QT += av avwidgets } else { CONFIG += av avwidgets } #rpath for apple mac { RPATHDIR *= @loader_path/../Frameworks isEmpty(QMAKE_LFLAGS_RPATH): QMAKE_LFLAGS_RPATH=-Wl,-rpath, for(R,RPATHDIR) { QMAKE_LFLAGS *= \'$${QMAKE_LFLAGS_RPATH}$$R\' } } QtAV-1.12.0/tools/sdk_osx.sh000077500000000000000000000111721312235004300155520ustar00rootroot00000000000000THIS_DIR=`dirname "${BASH_SOURCE[0]}"` ARGV=($@) ARGC=${#ARGV[@]} echo $THIS_DIR |grep "\.app" && test -d $THIS_DIR/Contents/Frameworks/QtCore.framework && IS_BOUNDLE=0 [ $ARGC -gt 0 ] && QT_LIBS=${ARGV[ARGC-1]} if [ -n "$IS_BOUNDLE" ]; then if [ ! -e $QT_LIBS/QtCore.framework ]; then QT_VER=`otool -L $THIS_DIR/Contents/Frameworks/QtCore.framework/QtCore | grep -v ":" |grep "QtCore\.framework" |sed 's,.*current version \(.*\)\.[0-9]),\1,'` echo "Input the absolute path of Qt$QT_VER installed lib dir. For example: /Users/xxx/Qt5.4.1/5.4/clang_64/lib" read QT_LIBS fi fi if [ "$1" = "-uninstall" ]; then rm -rfv $QT_LIBS/QtAV* $QT_LIBS/../qml/QtAV $QT_LIBS/../include/QtAV* rm -fv $QT_LIBS/../mkspecs/modules/qt_lib_av* $QT_LIBS/../mkspecs/features/av*.prf exit 0 fi test -e $QT_LIBS/QtCore.framework || { echo "./${0##*/} paths_of_qtav_frameworks \$QTDIR/lib" echo "foo.app/${0##*/} \$QTDIR/lib" exit 0 } # TODO: read from stdin if no arguments fix_qt() { local lib=$1 echo fixing $lib... local list=`otool -L $lib` for l in $list; do local name=`echo $l | grep -E "[^@].*/Qt[^\.]+\.framework.*[^:]$"` if [ $name ]; then local fw="$QT_LIBS/"`echo $name | sed -E "s/^.*(Qt[^\.]+.framework.*Qt.*)$/\1/"` install_name_tool -change $name $fw $lib fi done } echo "deploying frameworks..." unset ARGV[$ARGC-1] AV_FRAMEWORKS=(${ARGV[@]}) #which ${BASH_SOURCE[0]} echo $THIS_DIR |grep "\.app" && test -d $THIS_DIR/Contents/Frameworks/QtAV.framework && { find . -name "QtAV*.framework" AV_FRAMEWORKS=(`find $THIS_DIR -name "QtAV*.framework"`) } echo "AV_FRAMEWORKS: ${AV_FRAMEWORKS[@]}" for av in ${AV_FRAMEWORKS[@]}; do test -f $av/QtAV && { QTAV_VER=`otool -L $av/QtAV | grep -v ":" |grep "QtAV\.framework" |sed 's,.*current version \(.*\)),\1,'` QTAV_VER_MAJOR=`echo $QTAV_VER |cut -d "." -f 1` QTAV_VER_MINOR=`echo $QTAV_VER |cut -d "." -f 2` QTAV_VER_PATCH=`echo $QTAV_VER |cut -d "." -f 3` break } done echo QtAV version: $QTAV_VER for av in ${AV_FRAMEWORKS[@]}; do F=${av##*/} name=${F%\.framework} rm -rf $QT_LIBS/$F cp -af $av $QT_LIBS find $QT_LIBS/$F -name "*.prl" -delete install_name_tool -id $QT_LIBS/$F/Versions/$QTAV_VER_MAJOR/$name $QT_LIBS/$F/Versions/$QTAV_VER_MAJOR/$name fix_qt $QT_LIBS/$F/Versions/$QTAV_VER_MAJOR/$name done if [ -d $THIS_DIR/Contents/Resources/qml/QtAV ]; then echo "deploying QtAV qml module..." cp -af $THIS_DIR/Contents/Resources/qml/QtAV $QT_LIBS/../qml install_name_tool -id $QT_LIBS/../qml/QtAV/libQmlAV.dylib $QT_LIBS/../qml/QtAV/libQmlAV.dylib fi echo "deploying mkspecs..." tolower(){ echo "$@" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz } write_module_pri() { local M=$1 local m=`tolower $M` local dep=$2 echo $QT_LIBS/../mkspecs/modules/qt_lib_${m}.pri cat >$QT_LIBS/../mkspecs/modules/qt_lib_${m}.pri<$QT_LIBS/../mkspecs/modules/qt_lib_${m}_private.pri<$QT_LIBS/../mkspecs/features/${m}.prf< * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ QtAV-1.12.0/tools/templates/base.cpp000066400000000000000000000002341312235004300171520ustar00rootroot00000000000000 #include "QtAV/%CLASS%.h" #include "private/%CLASS%_p.h" namespace QtAV { %CLASS%::%CLASS%(%CLASS%Private &d): DPTR_INIT(&d) { } } //namespace QtAV QtAV-1.12.0/tools/templates/base.h000066400000000000000000000005251312235004300166220ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_H #define QTAV_%CLASS:u%_H #include namespace QtAV { class %CLASS%Private; class Q_AV_EXPORT %CLASS% { DPTR_DECLARE_PRIVATE(%CLASS%) public: virtual ~%CLASS%() = 0; protected: %CLASS%(%CLASS%Private &d); DPTR_DECLARE(%CLASS%) }; } //namespace QtAV #endif // QTAV_%CLASS:u%_H QtAV-1.12.0/tools/templates/base_p.h000066400000000000000000000004351312235004300171410ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_P_H #define QTAV_%CLASS:u%_P_H #include namespace QtAV { class %CLASS%; class Q_AV_PRIVATE_EXPORT %CLASS%Private : public DPtrPrivate<%CLASS%> { public: virtual ~%CLASS%Private() {} }; } //namespace QtAV #endif // QTAV_%CLASS%_P_H QtAV-1.12.0/tools/templates/derived.cpp000066400000000000000000000002611312235004300176620ustar00rootroot00000000000000 #include "QtAV/%CLASS%.h" #include "private/%CLASS%_p.h" namespace QtAV { %CLASS%::%CLASS%(%CLASS%Private &d): %BASE%(d) { } %CLASS%::~%CLASS%() { } } //namespace QtAV QtAV-1.12.0/tools/templates/derived.h000066400000000000000000000005021312235004300173250ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_H #define QTAV_%CLASS:u%_H #include namespace QtAV { class %CLASS%Private; class Q_AV_EXPORT %CLASS% : public %BASE% { DPTR_DECLARE_PRIVATE(%CLASS%) public: virtual ~%CLASS%(); protected: %CLASS%(%CLASS%Private &d); }; } //namespace QtAV #endif // QTAV_%CLASS:u%_H QtAV-1.12.0/tools/templates/derived_p.h000066400000000000000000000004071312235004300176500ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_P_H #define QTAV_%CLASS:u%_P_H #include "private/%BASE%_p.h" namespace QtAV { class Q_AV_PRIVATE_EXPORT %CLASS%Private : public %BASE%Private { public: virtual ~%CLASS%Private() {} }; } //namespace QtAV #endif // QTAV_%CLASS%_P_H QtAV-1.12.0/tools/templates/err.txt000066400000000000000000000030501312235004300170640ustar00rootroot00000000000000__init_VideoRendererWidget id=0 instance = 00000000 Singleton 00697B88 created... instance = 00697B88 __init_VideoRendererGLWidget id=1 instance = 00697B88 instance = 00697B88 __init_VideoRendererGDI id=2 instance = 00697B88 instance = 00697B88 __init_VideoRendererDirect2D id=3 instance = 00697B88 instance = 00697B88 __init_ImageConverterIPP id=1 instance = 00000000 Singleton 00698E68 created... instance = 00698E68 __init_ImageConverterFF id=0 instance = 00698E68 instance = 00698E68 Build with libavcodec-54.86.100 Build with libavformat-54.59.106 Build with libavutil-52.13.100 Build with libswscale-2.1.103 Warning: avutil runtime version 52.19.100 mismatch! Warning: swscale runtime version 2.2.100 mismatch! Debug: vo: d2d instance = 00697B88 Debug: Resize bitmap to 0 x 0 Debug: QtAV 1.1.10(Mar 14 2013, 12:03:58) Qt FFmpeg Ƶſ. Distributed under the terms of LGPLv2.1 or later. Copyright (C) 2012-2013 Wang Bin (aka. Lucas Wang) wbsecg1@gmail.com ϺѧйϺ https://github.com/wang-bin/QtAV https://sourceforge.net/projects/qtav Debug: audio device 2: MME: Microsoft ӳ - Output Debug: audio device 3: MME: / (SigmaTel High Defi Debug: audio device 4: MME: 豸 (SPDIF ) (Sigm Debug: audio device: / (SigmaTel High Defi instance = 00698E68 Debug: setCapture 0x1e02c50 Debug: setOSDFilter 0x0 Debug: loading: e:/video/XTM.mkv ... Debug: all closed and reseted Debug: avformat_open_input: format_context:'0x1e05380', url:'e:/video/XTM.mkv'... Debug: avformat_open_input: url:'e:/video/XTM.mkv' ret:0 QtAV-1.12.0/tools/templates/final.cpp000066400000000000000000000004461312235004300173360ustar00rootroot00000000000000 #include "QtAV/%CLASS%.h" #include "private/%BASE%_p.h" namespace QtAV { class %CLASS%Private : public %BASE%Private { public: %CLASS%Private() {} virtual ~%CLASS%Private() {} }; %CLASS%::%CLASS%(): %BASE%(*new %CLASS%Private()) { } %CLASS%::~%CLASS%() { } } //namespace QtAV QtAV-1.12.0/tools/templates/final.h000066400000000000000000000004461312235004300170030ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_H #define QTAV_%CLASS:u%_H #include namespace QtAV { class %CLASS%Private; class Q_AV_EXPORT %CLASS% : public %BASE% { DPTR_DECLARE_PRIVATE(%CLASS%) public: %CLASS%(); virtual ~%CLASS%(); }; } //namespace QtAV #endif // QTAV_%CLASS:u%_H QtAV-1.12.0/tools/templates/mkclass.sh000077500000000000000000000032211312235004300175270ustar00rootroot00000000000000. ../scripts/functions.sh #TODO: auto add virtual functions help(){ cecho green "Usage: $0 [-class Class] [-base Base] [-template Template]" cecho green "Or $0 [--class=Class] [--base=Base] [--template=Template]" exit 0 } while [ $# -gt 0 ]; do VAR= case "$1" in --*=*) PAIR=${1##--} VAR=${PAIRE%=*} VAL=${PAIR##*=} ;; -*) VAR=${1##-} shift VAL=$1 ;; *) break ;; esac if [ -n "VAR" ]; then VAR_U=`echo $VAR | tr "[:lower:]" "[:upper:]"` echo "$VAR_U" eval $VAR_U=$VAL fi shift done echo "class: $CLASS, template: $TEMPLATE" COPY=COPYRIGHT.h [ -n "$CLASS" -a -n "$TEMPLATE" ] || help [ -f $COPY ] || die 'NO copyright template found!' CLASS_U=`echo $CLASS | tr "[:lower:]" "[:upper:]"` YEAR=`date +%Y` #TODO: author info echo "generating ${CLASS}.h..." cat $COPY | sed "s/%YEAR%/$YEAR/g" > ${CLASS}.h cat ${TEMPLATE}.h |sed "s/%CLASS%/$CLASS/g" | sed "s/%CLASS:u%/$CLASS_U/g" >>${CLASS}.h [ -n "$BASE" ] && cat ${CLASS}.h |sed "s/%BASE%/$BASE/g" >${CLASS}_.h && mv ${CLASS}_.h ${CLASS}.h echo "generating ${CLASS}.cpp..." cat $COPY | sed "s/%YEAR%/$YEAR/g" > ${CLASS}.cpp cat ${TEMPLATE}.cpp | sed "s/%CLASS%/$CLASS/g" | sed "s/%CLASS:u%/$CLASS_U/g" >>${CLASS}.cpp [ -n "$BASE" ] && cat ${CLASS}.cpp |sed "s/%BASE%/$BASE/g" >${CLASS}_.cpp && mv ${CLASS}_.cpp ${CLASS}.cpp if [ -f ${TEMPLATE}_p.h ]; then echo "generating ${CLASS}_p.h..." cat $COPY | sed "s/%YEAR%/$YEAR/g" > ${CLASS}_p.h cat ${TEMPLATE}_p.h |sed "s/%CLASS%/$CLASS/g" | sed "s/%CLASS:u%/$CLASS_U/g" >>${CLASS}_p.h [ -n "$BASE" ] && cat ${CLASS}_p.h |sed "s/%BASE%/$BASE/g" >${CLASS}_p_.h && mv ${CLASS}_p_.h ${CLASS}_p.h fi QtAV-1.12.0/tools/templates/vo.cpp000066400000000000000000000045231312235004300166710ustar00rootroot00000000000000 #include "QtAV/%CLASS%.h" #include "private/VideoRenderer_p.h" #include namespace QtAV { class %CLASS%Private : public VideoRendererPrivate { public: DPTR_DECLARE_PUBLIC(%CLASS%) %CLASS%Private() { } ~%CLASS%Private() { } virtual void setupQuality() {} }; %CLASS%::%CLASS%(QWidget *parent, Qt::WindowFlags f): QWidget(parent, f) , VideoRenderer(*new %CLASS%Private()) { DPTR_INIT_PRIVATE(%CLASS%); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); //setAttribute(Qt::WA_OpaquePaintEvent); //setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(false); setAttribute(Qt::WA_PaintOnScreen, true); } %CLASS%::~%CLASS%() { } QPaintEngine* %CLASS%::paintEngine() const { return 0; //use native engine } void %CLASS%::convertData(const QByteArray &data) { DPTR_D(%CLASS%); //TODO: if date is deep copied, mutex can be avoided QMutexLocker locker(&d.img_mutex); Q_UNUSED(locker); } ool %CLASS%::needUpdateBackground() const { return VideoRenderer::needUpdateBackground(); } void %CLASS%::drawBackground() { } bool %CLASS%::needDrawFrame() const { return VideoRenderer::needDrawFrame(); } void %CLASS%::drawFrame() { //assume that the image data is already scaled to out_size(NOT renderer size!) if (d.src_width == d.out_rect.width() && d.src_height == d.out_rect.height()) { //you may copy data to video buffer directly } else { //paint with scale } } void %CLASS%::paintEvent(QPaintEvent *) { //DPTR_D(%CLASS%); //d.painter->begin(this); //Widget painting can only begin as a result of a paintEvent handlePaintEvent(); //end paint. how about QPainter::endNativePainting()? //d.painter->end(); } void %CLASS%::resizeEvent(QResizeEvent *e) { DPTR_D(%CLASS%); d.update_background = true; resizeRenderer(e->size()); update(); } void %CLASS%::showEvent(QShowEvent *event) { Q_UNUSED(event); DPTR_D(%CLASS%); d.update_background = true; /* * Do something that depends on widget below! e.g. recreate render target for direct2d. * When Qt::WindowStaysOnTopHint changed, window will hide first then show. If you * don't do anything here, the widget content will never be updated. */ } bool %CLASS%::write() { update(); return true; } } //namespace QtAV QtAV-1.12.0/tools/templates/vo.h000066400000000000000000000037211312235004300163350ustar00rootroot00000000000000 #ifndef QTAV_%CLASS:u%_H #define QTAV_%CLASS:u%_H #include #include namespace QtAV { class %CLASS%Private; class Q_AV_EXPORT %CLASS% : public QWidget, public VideoRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(%CLASS%) public: %CLASS%(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~%CLASS%(); /* WA_PaintOnScreen: To render outside of Qt's paint system, e.g. If you require * native painting primitives, you need to reimplement QWidget::paintEngine() to * return 0 and set this flag * If paintEngine != 0, the window will flicker when drawing without QPainter. * Make sure that paintEngine returns 0 to avoid flicking. */ virtual QPaintEngine* paintEngine() const; /*http://lists.trolltech.com/qt4-preview-feedback/2005-04/thread00609-0.html * true: paintEngine is QPainter. Painting with QPainter support double buffer * false: no double buffer, should reimplement paintEngine() to return 0 to avoid flicker */ virtual QWidget* widget() { return this; } protected: virtual void convertData(const QByteArray &data); virtual bool needUpdateBackground() const; //TODO: abstract draw image and font. too many drawXXX() //called in paintEvent before drawFrame() when required virtual void drawBackground(); virtual bool needDrawFrame() const; //TODO: no virtual func. it's a solution for temporary //draw the current frame using the current paint engine. called by paintEvent() virtual void drawFrame(); /*usually you don't need to reimplement paintEvent, just drawXXX() is ok. unless you want do all *things yourself totally*/ virtual void paintEvent(QPaintEvent *); virtual void resizeEvent(QResizeEvent *); //stay on top will change parent, hide then show(windows). we need GetDC() again virtual void showEvent(QShowEvent *); virtual bool write(); }; //typedef %CLASS% VideoRenderer?? } //namespace QtAV #endif // QTAV_%CLASS%_H QtAV-1.12.0/tools/tools.pro000066400000000000000000000000511312235004300154150ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = install_sdk QtAV-1.12.0/widgets/000077500000000000000000000000001312235004300140455ustar00rootroot00000000000000QtAV-1.12.0/widgets/CMakeLists.txt000066400000000000000000000054561312235004300166170ustar00rootroot00000000000000set(MODULE QtAVWidgets) set(EXTRA_DEFS -DBUILD_QTAVWIDGETS_LIB) list(APPEND EXTRA_DEFS -DQTAV_HAVE_GL=1) set(HAVE_QT5OPENGL 0) if(BUILD_QT5OPENGL) find_package(Qt5OpenGL) if(Qt5OpenGL_FOUND) set(HAVE_QT5OPENGL 1) endif() endif() set(SDK_HEADERS QtAVWidgets/global.h QtAVWidgets/GraphicsItemRenderer.h QtAVWidgets/OpenGLWidgetRenderer.h QtAVWidgets/QtAVWidgets QtAVWidgets/QtAVWidgets.h QtAVWidgets/version.h QtAVWidgets/VideoPreviewWidget.h QtAVWidgets/WidgetRenderer.h ) file(GLOB SDK_PRIVATE_HEADERS QtAVWidgets/*.h) set(SOURCES global.cpp GraphicsItemRenderer.cpp OpenGLWidgetRenderer.cpp VideoPreviewWidget.cpp WidgetRenderer.cpp ) if(HAVE_QT5OPENGL) list(APPEND SDK_HEADERS QtAVWidgets/GLWidgetRenderer2.h) list(APPEND SOURCES GLWidgetRenderer2.cpp) endif() # TODO: Qt5Gui_OPENGL_IMPLEMENTATION? if(Qt5Gui_VERSION VERSION_LESS 5.4.0) list(APPEND SDK_HEADERS QtAVWidgets/QOpenGLWidget.h) list(APPEND SOURCES QOpenGLWidget.cpp) endif() set(HEADERS ${SDK_HEADERS}) if(WIN32) set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}.rc) configure_file(${CMAKE_SOURCE_DIR}/cmake/QtAV.rc.in ${RC_FILE}) endif() # add HEADERS for moc add_library(${MODULE} SHARED ${SOURCES} ${RESOURCES_SOURCES} ${HEADERS} ${RC_FILE}) target_link_libraries(${MODULE} LINK_PRIVATE ${EXTRA_LIBS} LINK_PUBLIC QtAV Qt5::Widgets ) if(HAVE_QT5OPENGL) target_link_libraries(${MODULE} LINK_PUBLIC Qt5::OpenGL) endif() set_target_properties(${MODULE} PROPERTIES MACOSX_RPATH ON FRAMEWORK ON VERSION ${PROJECT_VERSION} SOVERSION ${SO_VERSION} OUTPUT_NAME ${MODULE} CLEAN_DIRECT_OUTPUT 1 #LINK_SEARCH_START_STATIC 1 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) #http://stackoverflow.com/questions/19866424/cmake-and-qt5-automoc-error #http://doc.qt.io/qt-5/cmake-manual.html set(CMAKE_INCLUDE_CURRENT_DIR ON) # for .moc target_include_directories(${MODULE} PUBLIC $ PUBLIC $ PUBLIC $ PUBLIC $ PRIVATE ${EXTRA_INCLUDE} ) target_compile_definitions(${MODULE} PRIVATE ${EXTRA_DEFS}) install(FILES ${SDK_HEADERS} DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE} ) install(FILES ${SDK_PRIVATE_HEADERS} DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE}/private ) install(TARGETS ${MODULE} EXPORT ${MODULE}-targets #PUBLIC_HEADER DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE} #PRIVATE_HEADER DESTINATION ${QTAV_INSTALL_HEADERS}/${MODULE}/private RUNTIME DESTINATION ${QTAV_INSTALL_BINS} LIBRARY DESTINATION ${QTAV_INSTALL_LIBS} ARCHIVE DESTINATION ${QTAV_INSTALL_LIBS} FRAMEWORK DESTINATION ${QTAV_INSTALL_LIBS} ) install(EXPORT ${MODULE}-targets DESTINATION ${QTAV_INSTALL_LIBS}/cmake/${MODULE} FILE ${MODULE}-config.cmake ) QtAV-1.12.0/widgets/Direct2DRenderer.cpp000066400000000000000000000373131312235004300176470ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoRenderer.h" #include #include #include #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/private/factory.h" //#define CINTERFACE //http://rxlib.ru/faqs/faqc_en/15596.html #include #ifndef _Out_writes_bytes_opt_ #define _Out_writes_bytes_opt_(s) #endif #include //IID_ID2D1Factory #include //steps: http://msdn.microsoft.com/zh-cn/library/dd317121(v=vs.85).aspx //performance: http://msdn.microsoft.com/en-us/library/windows/desktop/dd372260(v=vs.85).aspx //vlc is helpful //layer(opacity): http://www.cnblogs.com/graphics/archive/2013/04/15/2781969.html namespace QtAV { template inline void SafeRelease(Interface **ppInterfaceToRelease) { if (*ppInterfaceToRelease != NULL){ (*ppInterfaceToRelease)->Release(); (*ppInterfaceToRelease) = NULL; } } class Direct2DRendererPrivate; class Direct2DRenderer : public QWidget, public VideoRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(Direct2DRenderer) public: Direct2DRenderer(QWidget* parent = 0, Qt::WindowFlags f = 0); VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; /* WA_PaintOnScreen: To render outside of Qt's paint system, e.g. If you require * native painting primitives, you need to reimplement QWidget::paintEngine() to * return 0 and set this flag */ QPaintEngine* paintEngine() const Q_DECL_OVERRIDE; QWidget* widget() Q_DECL_OVERRIDE { return this; } protected: bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; void drawBackground() Q_DECL_OVERRIDE; void drawFrame() Q_DECL_OVERRIDE; /*usually you don't need to reimplement paintEvent, just drawXXX() is ok. unless you want do all *things yourself totally*/ void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; //stay on top will change parent, hide then show(windows) void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef Direct2DRenderer VideoRendererDirect2D; extern VideoRendererId VideoRendererId_Direct2D; #if 0 FACTORY_REGISTER_ID_AUTO(VideoRenderer, Direct2D, "Direct2D") #else void RegisterVideoRendererDirect2D_Man() { VideoRenderer::Register(VideoRendererId_Direct2D, "Direct2D"); } #endif VideoRendererId Direct2DRenderer::id() const { return VideoRendererId_Direct2D; } class Direct2DRendererPrivate : public VideoRendererPrivate { public: DPTR_DECLARE_PUBLIC(Direct2DRenderer) Direct2DRendererPrivate(): VideoRendererPrivate() , d2d_factory(0) , render_target(0) , bitmap(0) , bitmap_width(0) , bitmap_height(0) , interpolation(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR) { dll.setFileName(QStringLiteral("d2d1")); if (!dll.load()) { available = false; qWarning("Direct2D is disabled. Failed to load 'd2d1.dll': %s", dll.errorString().toUtf8().constData()); return; } typedef HRESULT (WINAPI *D2D1CreateFactory_t)(D2D1_FACTORY_TYPE, REFIID, const D2D1_FACTORY_OPTIONS *, void **ppIFactory); D2D1CreateFactory_t D2D1CreateFactory; D2D1CreateFactory = (D2D1CreateFactory_t)dll.resolve("D2D1CreateFactory"); if (!D2D1CreateFactory) { available = false; qWarning("Direct2D is disabled. Failed to resolve symble 'D2D1CreateFactory': %s", dll.errorString().toUtf8().constData()); return; } D2D1_FACTORY_OPTIONS factory_opt = { D2D1_DEBUG_LEVEL_NONE }; /* * d2d is accessed by AVThread and GUI thread, so we use D2D1_FACTORY_TYPE_MULTI_THREADED * and let d2d to deal with the thread safe problems. otherwise, if we use * D2D1_FACTORY_TYPE_SINGLE_THREADED, we must use lock when copying ID2D1Bitmap and calling EndDraw. */ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd368104%28v=vs.85%29.aspx HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED , (REFIID)IID_ID2D1Factory , &factory_opt , (void**)&d2d_factory); if (FAILED(hr)) { available = false; qWarning("Direct2D is disabled. Create d2d factory failed"); return; } FLOAT dpiX, dpiY; d2d_factory->GetDesktopDpi(&dpiX, &dpiY); //gcc: extended initializer lists only available with -std=c++11 or -std=gnu++11 //vc: http://msdn.microsoft.com/zh-cn/library/t8xe60cf(v=vs.80).aspx /*pixel_format = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE };*/ pixel_format.format = DXGI_FORMAT_B8G8R8A8_UNORM; pixel_format.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;//D2D1_ALPHA_MODE_IGNORE; /*bitmap_properties = { pixel_format, dpiX, dpiY };*/ bitmap_properties.pixelFormat = pixel_format; bitmap_properties.dpiX = dpiX; bitmap_properties.dpiY = dpiY; } ~Direct2DRendererPrivate() { destroyDeviceResource(); SafeRelease(&d2d_factory);//vlc does not call this. why? bug? dll.unload(); } bool createDeviceResource() { DPTR_P(Direct2DRenderer); update_background = true; destroyDeviceResource(); // // This method creates resources which are bound to a particular // Direct3D device. It's all centralized here, in case the resources // need to be recreated in case of Direct3D device loss (eg. display // change, remoting, removal of video card, etc). // //TODO: move to prepare(), or private. how to call less times D2D1_RENDER_TARGET_PROPERTIES rtp = { D2D1_RENDER_TARGET_TYPE_DEFAULT, pixel_format, 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT }; D2D1_SIZE_U size = { (UINT32)p.width(), (UINT32)p.height() };//d.renderer_width, d.renderer_height? // Create a Direct2D render target. D2D1_HWND_RENDER_TARGET_PROPERTIES hwnd_rtp = { (HWND)p.winId(), size, //TODO: what do these mean? D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS //D2D1_PRESENT_OPTIONS_IMMEDIATELY /* this might need fiddling */ }; HRESULT hr = d2d_factory->CreateHwndRenderTarget(&rtp//D2D1::RenderTargetProperties() //TODO: vlc set properties , &hwnd_rtp//D2D1::HwndRenderTargetProperties(, size) , &render_target); if (FAILED(hr)) { qWarning("Direct2D is disabled. CreateHwndRenderTarget() failed: %d", (int)GetLastError()); render_target = 0; return false; } SafeRelease(&bitmap); prepareBitmap(src_width, src_height); //bitmap depends on render target return hr == S_OK; } void destroyDeviceResource() { SafeRelease(&render_target); SafeRelease(&bitmap); } void recreateDeviceResource() { qDebug("D2DERR_RECREATE_TARGET"); QMutexLocker locker(&img_mutex); Q_UNUSED(locker); update_background = true; destroyDeviceResource(); createDeviceResource(); } //create an empty bitmap with given size. if size is equal as current and bitmap already exists, do nothing bool prepareBitmap(int w, int h) { if (w == bitmap_width && h == bitmap_height && bitmap) return true; if (!render_target) { qWarning("No render target, bitmap will not be created!!!"); return false; } bitmap_width = w; bitmap_height = h; qDebug("Resize bitmap to %d x %d", w, h); SafeRelease(&bitmap); if (w ==0 || h == 0) return true; D2D1_SIZE_U s = {(UINT32)w, (UINT32)h}; HRESULT hr = render_target->CreateBitmap(s , NULL , 0 , &bitmap_properties , &bitmap); if (FAILED(hr)) { qWarning("Failed to create ID2D1Bitmap (%ld)", hr); SafeRelease(&bitmap); SafeRelease(&render_target); return false; } return true; } //it seems that only D2D1_BITMAP_INTERPOLATION_MODE(used in DrawBitmap) matters when drawing an image void setupQuality() { switch (quality) { case VideoRenderer::QualityFastest: interpolation = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; render_target->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); render_target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); break; case VideoRenderer::QualityBest: interpolation = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; render_target->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); render_target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); break; default: interpolation = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; render_target->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); render_target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT); break; } } ID2D1Factory *d2d_factory; ID2D1HwndRenderTarget *render_target; D2D1_PIXEL_FORMAT pixel_format; D2D1_BITMAP_PROPERTIES bitmap_properties; ID2D1Bitmap *bitmap; int bitmap_width, bitmap_height; //can not use src_width, src height because bitmap not update when they changes D2D1_BITMAP_INTERPOLATION_MODE interpolation; QLibrary dll; }; Direct2DRenderer::Direct2DRenderer(QWidget *parent, Qt::WindowFlags f): QWidget(parent, f),VideoRenderer(*new Direct2DRendererPrivate()) { DPTR_INIT_PRIVATE(Direct2DRenderer); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); //setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(false); setAttribute(Qt::WA_PaintOnScreen, true); } bool Direct2DRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { return VideoFormat::isRGB(pixfmt); } bool Direct2DRenderer::receiveFrame(const VideoFrame& frame) { DPTR_D(Direct2DRenderer); if (!frame.isValid()) { //d.prepareBitmap(0, 0); return false; } if (!d.prepareBitmap(frame.width(), frame.height())) return false; HRESULT hr = S_OK; //if d2d factory is D2D1_FACTORY_TYPE_SINGLE_THREADED, we need to lock //already locked if (frame.constBits(0)) d.video_frame = frame; else d.video_frame = frame.to(frame.pixelFormat()); //TODO: if CopyFromMemory() is deep copy, mutex can be avoided /*if lock is required, do not use locker in if() scope, it will unlock outside the scope*/ //TODO: d2d often crash, should we always lock? How about other renderer? hr = d.bitmap->CopyFromMemory(NULL //&D2D1::RectU(0, 0, image.width(), image.height()) /*&dstRect, NULL?*/, , frame.constBits(0) //data.constData() //msdn: const void* , frame.bytesPerLine(0)); if (hr != S_OK) { qWarning("Failed to copy from memory to bitmap (%ld)", hr); } updateUi(); return true; } QPaintEngine* Direct2DRenderer::paintEngine() const { return 0; //use native engine } void Direct2DRenderer::drawBackground() { const QRegion bgRegion(backgroundRegion()); if (bgRegion.isEmpty()) return; DPTR_D(Direct2DRenderer); const QColor bc(backgroundColor()); D2D1_COLOR_F c = {(float)bc.red(), (float)bc.green(), (float)bc.blue(), (float)bc.alpha()}; d.render_target->Clear(&c); //const D2D1_COlOR_F&? //http://msdn.microsoft.com/en-us/library/windows/desktop/dd535473(v=vs.85).aspx //ID2D1SolidColorBrush *brush; //d.render_target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &brush); //d.render_target->FillRectangle(D2D1::RectF(0, 0, width(), height()), brush); } void Direct2DRenderer::drawFrame() { DPTR_D(Direct2DRenderer); if (!d.bitmap || d.out_rect.isEmpty()) return; D2D1_RECT_F out_rect = { (FLOAT)d.out_rect.x(), (FLOAT)d.out_rect.y(), (FLOAT)(d.out_rect.x() + d.out_rect.width()), // QRect.right() is x+width-1 for historical reason (FLOAT)(d.out_rect.y() + d.out_rect.height()) }; QRect roi = realROI(); D2D1_RECT_F roi_d2d = { (FLOAT)roi.x(), (FLOAT)roi.y(), (FLOAT)(roi.x() + roi.width()), (FLOAT)(roi.y() + roi.height()) }; //d.render_target->SetTransform d.render_target->DrawBitmap(d.bitmap , &out_rect , 1 //opacity , d.interpolation , &roi_d2d); } void Direct2DRenderer::paintEvent(QPaintEvent *) { DPTR_D(Direct2DRenderer); if (!d.render_target) { qWarning("No render target!!!"); return; } //http://www.daimakuai.net/?page_id=1574 d.render_target->BeginDraw(); handlePaintEvent(); HRESULT hr = S_OK; { //if d2d factory is D2D1_FACTORY_TYPE_SINGLE_THREADED, we need to lock //QMutexLocker locker(&d.img_mutex); //Q_UNUSED(locker); hr = d.render_target->EndDraw(NULL, NULL); //TODO: why it need lock? otherwise crash } if (hr == D2DERR_RECREATE_TARGET) { d.recreateDeviceResource(); } } void Direct2DRenderer::resizeEvent(QResizeEvent *e) { resizeRenderer(e->size()); DPTR_D(Direct2DRenderer); d.update_background = true; if (d.render_target) { D2D1_SIZE_U size = { (UINT32)e->size().width(), (UINT32)e->size().height() }; // Note: This method can fail, but it's okay to ignore the // error here -- it will be repeated on the next call to // EndDraw. d.render_target->Resize(&size); //D2D1_SIZE_U&? } update(); } void Direct2DRenderer::showEvent(QShowEvent *) { DPTR_D(Direct2DRenderer); d.recreateDeviceResource(); } } //namespace QtAV #include "Direct2DRenderer.moc" QtAV-1.12.0/widgets/GDIRenderer.cpp000066400000000000000000000233261312235004300166510ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAV/VideoRenderer.h" #include "QtAV/private/VideoRenderer_p.h" #include #include //GetDC() #include #include #include "QtAV/private/factory.h" #define USE_GRAPHICS 0 //http://msdn.microsoft.com/en-us/library/ms927613.aspx //#pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; namespace QtAV { class GDIRendererPrivate; class GDIRenderer : public QWidget, public VideoRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(GDIRenderer) public: GDIRenderer(QWidget* parent = 0, Qt::WindowFlags f = 0); //offscreen? VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; /* WA_PaintOnScreen: To render outside of Qt's paint system, e.g. If you require * native painting primitives, you need to reimplement QWidget::paintEngine() to * return 0 and set this flag */ QPaintEngine* paintEngine() const Q_DECL_OVERRIDE; /*http://lists.trolltech.com/qt4-preview-feedback/2005-04/thread00609-0.html * true: paintEngine.getDC(), double buffer is enabled by defalut. * false: GetDC(winId()), no double buffer, should reimplement paintEngine() */ QWidget* widget() Q_DECL_OVERRIDE { return this; } protected: bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; void drawBackground() Q_DECL_OVERRIDE; void drawFrame() Q_DECL_OVERRIDE; /*usually you don't need to reimplement paintEvent, just drawXXX() is ok. unless you want do all *things yourself totally*/ void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; //stay on top will change parent, hide then show(windows). we need GetDC() again void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef GDIRenderer VideoRendererGDI; extern VideoRendererId VideoRendererId_GDI; #if 0 FACTORY_REGISTER_ID_AUTO(VideoRenderer, GDI, "GDI") #else void RegisterVideoRendererGDI_Man() { VideoRenderer::Register(VideoRendererId_GDI, "GDI"); } #endif VideoRendererId GDIRenderer::id() const { return VideoRendererId_GDI; } class GDIRendererPrivate : public VideoRendererPrivate { public: DPTR_DECLARE_PUBLIC(GDIRenderer) GDIRendererPrivate(): VideoRendererPrivate() , support_bitblt(true) , gdiplus_token(0) , device_context(0) #if USE_GRAPHICS , graphics(0) #endif //USE_GRAPHICS { GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&gdiplus_token, &gdiplusStartupInput, NULL); } ~GDIRendererPrivate() { if (device_context) { DPTR_P(GDIRenderer); ReleaseDC((HWND)p.winId(), device_context); /*Q5: must cast WID to HWND*/ #if !USE_GRAPHICS DeleteDC(off_dc); #endif //USE_GRAPHICS device_context = 0; } GdiplusShutdown(gdiplus_token); } void prepare() { update_background = true; DPTR_P(GDIRenderer); device_context = GetDC((HWND)p.winId()); /*Q5: must cast WID to HWND*/ #if USE_GRAPHICS if (graphics) { delete graphics; } graphics = new Graphics(device_context); #endif //USE_GRAPHICS //TODO: check bitblt support int ret = GetDeviceCaps(device_context, RC_BITBLT); qDebug("bitblt=%d", ret); //TODO: wingapi? vlc #if 0 BITMAPINFOHEADER bih; bih.biSize = sizeof(BITMAPINFOHEADER); bih.biSizeImage = 0; bih.biPlanes = 1; bih.biCompression = BI_RGB; //vlc: 16bpp=>BI_RGB, 15bpp=>BI_BITFIELDS bih.biBitCount = 32; bih.biWidth = src_width; bih.biHeight = src_height; bih.biClrImportant = 0; bih.biClrUsed = 0; bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; off_bitmap = CreateDIBSection(device_context, , (BITMAPINFO*)&bih , DIB_RGB_COLORS , &p_pic_buffer, NULL, 0); #endif //0 #if !USE_GRAPHICS off_dc = CreateCompatibleDC(device_context); #endif //USE_GRAPHICS } void setupQuality() { //http://www.codeproject.com/Articles/9184/Custom-AntiAliasing-with-GDI //http://msdn.microsoft.com/en-us/library/windows/desktop/ms533836%28v=vs.85%29.aspx /* *Graphics.DrawImage, Graphics.InterpolationMode * bitblit? */ #if USE_GRAPHICS if (!graphics) return; switch (quality) { case VideoRenderer::QualityBest: graphics->SetInterpolationMode(InterpolationModeHighQualityBicubic); break; case VideoRenderer::QualityFastest: graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); break; default: graphics->SetInterpolationMode(InterpolationModeDefault); break; } #endif //USE_GRAPHICS } bool support_bitblt; ULONG_PTR gdiplus_token; /* * GetDC(winID()): will flick * QPainter.paintEngine()->getDC() in paintEvent: doc says it's for internal use */ HDC device_context; /* Our offscreen bitmap and its framebuffer */ #if USE_GRAPHICS Graphics *graphics; #else HDC off_dc; HBITMAP off_bitmap; #endif //USE_GRAPHICS }; GDIRenderer::GDIRenderer(QWidget *parent, Qt::WindowFlags f): QWidget(parent, f),VideoRenderer(*new GDIRendererPrivate()) { DPTR_INIT_PRIVATE(GDIRenderer); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); //setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(false); //setDCFromPainter(false); //changeEvent will be called on show() setAttribute(Qt::WA_PaintOnScreen, true); } bool GDIRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { return VideoFormat::isRGB(pixfmt); } QPaintEngine* GDIRenderer::paintEngine() const { return 0; } bool GDIRenderer::receiveFrame(const VideoFrame& frame) { DPTR_D(GDIRenderer); if (frame.constBits(0)) d.video_frame = frame; else d.video_frame = frame.to(frame.pixelFormat()); updateUi(); return true; } void GDIRenderer::drawBackground() { const QRegion bgRegion(backgroundRegion()); if (bgRegion.isEmpty()) return; const QColor bc(backgroundColor()); DPTR_D(GDIRenderer); //HDC hdc = d.device_context; Graphics g(d.device_context); SolidBrush brush(Color(bc.alpha(), bc.red(), bc.green(), bc.blue())); //argb const QVector bg(bgRegion.rects()); foreach (const QRect& r, bg) { g.FillRectangle(&brush, r.x(), r.y(), r.width(), r.height()); } } void GDIRenderer::drawFrame() { DPTR_D(GDIRenderer); /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms533829%28v=vs.85%29.aspx * Improving Performance by Avoiding Automatic Scaling * TODO: How about QPainter? */ //steps to use BitBlt: http://bbs.csdn.net/topics/60183502 Bitmap bitmap(d.video_frame.width(), d.video_frame.height(), d.video_frame.bytesPerLine(0) , PixelFormat32bppRGB, (BYTE*)d.video_frame.constBits(0)); #if USE_GRAPHICS if (d.graphics) d.graphics->DrawImage(&bitmap, d.out_rect.x(), d.out_rect.y(), d.out_rect.width(), d.out_rect.height()); #else if (FAILED(bitmap.GetHBITMAP(Color(), &d.off_bitmap))) { qWarning("Failed GetHBITMAP"); return; } HDC hdc = d.device_context; HBITMAP hbmp_old = (HBITMAP)SelectObject(d.off_dc, d.off_bitmap); QRect roi = realROI(); // && image.size() != size() //assume that the image data is already scaled to out_size(NOT renderer size!) StretchBlt(hdc , d.out_rect.left(), d.out_rect.top() , d.out_rect.width(), d.out_rect.height() , d.off_dc , roi.x(), roi.y() , roi.width(), roi.height() , SRCCOPY); SelectObject(d.off_dc, hbmp_old); DeleteObject(d.off_bitmap); //avoid mem leak #endif //end paint } void GDIRenderer::paintEvent(QPaintEvent *) { handlePaintEvent(); } void GDIRenderer::resizeEvent(QResizeEvent *e) { d_func().update_background = true; resizeRenderer(e->size()); update(); } void GDIRenderer::showEvent(QShowEvent *) { DPTR_D(GDIRenderer); d.update_background = true; d_func().prepare(); } } //namespace QtAV #include "GDIRenderer.moc" QtAV-1.12.0/widgets/GLWidgetRenderer.cpp000066400000000000000000001225651312235004300177210ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/GLWidgetRenderer.h" #include "QtAV/private/VideoRenderer_p.h" #include "utils/OpenGLHelper.h" #include #include #include #include #include #include #include #define NO_QGL_SHADER (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) //TODO: vsync http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl //TODO: check gl errors //glEGLImageTargetTexture2DOES:http://software.intel.com/en-us/articles/using-opengl-es-to-accelerate-apps-with-legacy-2d-guis //#ifdef GL_EXT_unpack_subimage #ifndef GL_UNPACK_ROW_LENGTH #ifdef GL_UNPACK_ROW_LENGTH_EXT #define GL_UNPACK_ROW_LENGTH GL_UNPACK_ROW_LENGTH_EXT #endif //GL_UNPACK_ROW_LENGTH_EXT #endif //GL_UNPACK_ROW_LENGTH #include "QtAV/ColorTransform.h" #include "QtAV/FilterContext.h" #define UPLOAD_ROI 0 #define ROI_TEXCOORDS 1 #define AVALIGN(x, a) (((x)+(a)-1)&~((a)-1)) //TODO: QGLfunctions? namespace QtAV { const GLfloat kVertices[] = { -1, 1, 1, 1, 1, -1, -1, -1, }; static void checkGlError(const char* op = 0) { GLenum error = glGetError(); if (error == GL_NO_ERROR) return; qWarning("GL error %s (%#x): %s", op, error, glGetString(error)); } #define CHECK_GL_ERROR(FUNC) \ FUNC; \ checkGlError(#FUNC); static const char kVertexShader[] = "attribute vec4 a_Position;\n" "attribute vec2 a_TexCoords;\n" "uniform mat4 u_MVP_matrix;\n" "varying vec2 v_TexCoords;\n" "void main() {\n" " gl_Position = u_MVP_matrix * a_Position;\n" " v_TexCoords = a_TexCoords; \n" "}\n"; class GLWidgetRendererPrivate : public VideoRendererPrivate #if QTAV_HAVE(QGLFUNCTIONS) , public QGLFunctions #endif //QTAV_HAVE(QGLFUNCTIONS) { public: GLWidgetRendererPrivate(): VideoRendererPrivate() , hasGLSL(true) , update_texcoords(true) , effective_tex_width_ratio(1) , shader_program(0) , program(0) , vert(0) , frag(0) , a_Position(-1) , a_TexCoords(-1) , u_matrix(-1) , u_bpp(-1) , u_opacity(-1) , painter(0) , video_format(VideoFormat::Format_Invalid) , material_type(0) { if (QGLFormat::openGLVersionFlags() == QGLFormat::OpenGL_Version_None) { available = false; return; } colorTransform.setOutputColorSpace(ColorTransform::RGB); } ~GLWidgetRendererPrivate() { releaseShaderProgram(); if (!textures.isEmpty()) { glDeleteTextures(textures.size(), textures.data()); textures.clear(); } if (shader_program) { delete shader_program; shader_program = 0; } if (painter) { delete painter; painter= 0; } } bool initWithContext(const QGLContext *ctx) { Q_UNUSED(ctx); #if !NO_QGL_SHADER shader_program = new QGLShaderProgram(ctx, 0); #endif return true; } GLuint loadShader(GLenum shaderType, const char* pSource); GLuint createProgram(const char* pVertexSource, const char* pFragmentSource); bool releaseShaderProgram(); QString getShaderFromFile(const QString& fileName); bool prepareShaderProgram(const VideoFormat& fmt, ColorTransform::ColorSpace cs); bool initTexture(GLuint tex, GLint internal_format, GLenum format, GLenum dataType, int width, int height); bool initTextures(const VideoFormat& fmt); void updateTexturesIfNeeded(); void upload(const QRect& roi); void uploadPlane(int p, GLint internal_format, GLenum format, const QRect& roi); //GL 4.x: GL_FRAGMENT_SHADER_DERIVATIVE_HINT,GL_TEXTURE_COMPRESSION_HINT //GL_DONT_CARE(default), GL_FASTEST, GL_NICEST /* * it seems that only glTexParameteri matters when drawing an image * MAG_FILTER/MIN_FILTER combinations: http://gregs-blog.com/2008/01/17/opengl-texture-filter-parameters-explained/ * TODO: understand what each parameter means. GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T * what is MIPMAP? */ void setupQuality() { switch (quality) { case VideoRenderer::QualityBest: //FIXME: GL_LINEAR_MIPMAP_LINEAR+GL_LINEAR=white screen? //texture zoom out glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //texture zoom in glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #ifndef QT_OPENGL_ES_2 if (!hasGLSL) { glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); //glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } #endif //QT_OPENGL_ES_2 break; case VideoRenderer::QualityFastest: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); #ifndef QT_OPENGL_ES_2 if (!hasGLSL) { glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); //glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); } #endif //QT_OPENGL_ES_2 break; default: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //GL_NEAREST #ifndef QT_OPENGL_ES_2 if (!hasGLSL) { glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE); glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); //glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE); } #endif //QT_OPENGL_ES_2 break; } } void setupAspectRatio() { mpv_matrix.setToIdentity(); mpv_matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); if (orientation) mpv_matrix.rotate(orientation, 0, 0, 1); // Z axis } class VideoMaterialType {}; VideoMaterialType* materialType(const VideoFormat& fmt) const { static VideoMaterialType rgbType; static VideoMaterialType packedType; // TODO: uyuy, yuy2 static VideoMaterialType planar16leType; static VideoMaterialType planar16beType; static VideoMaterialType yuv8Type; static VideoMaterialType invalidType; if (fmt.isRGB() && !fmt.isPlanar()) return &rgbType; if (!fmt.isPlanar()) return &packedType; if (fmt.bytesPerPixel(0) == 1) return &yuv8Type; if (fmt.isBigEndian()) return &planar16beType; else return &planar16leType; return &invalidType; } void updateShaderIfNeeded(); bool hasGLSL; bool update_texcoords; QVector textures; //texture ids. size is plane count QVector texture_size; /* * actually if render a full frame, only plane 0 is enough. other planes are the same as texture size. * because linesize[0]>=linesize[1] * uploade size is required when * 1. y/u is not an integer because of alignment. then padding size of y < padding size of u, and effective size y/u != texture size y/u * 2. odd size. enlarge y */ QVector texture_upload_size; QVector effective_tex_width; //without additional width for alignment qreal effective_tex_width_ratio; QVector internal_format; QVector data_format; QVector data_type; QGLShaderProgram *shader_program; GLuint program; GLuint vert, frag; GLint a_Position; GLint a_TexCoords; QVector u_Texture; //u_TextureN uniform. size is channel count GLint u_matrix; GLint u_colorMatrix; GLint u_bpp; GLint u_opacity; QPainter *painter; VideoFormat video_format; QSize plane0Size; // width is in bytes. different alignments may result in different plane 1 linesize even if plane 0 are the same int plane1_linesize; ColorTransform colorTransform; QMatrix4x4 mpv_matrix; VideoMaterialType *material_type; }; //http://www.opengl.org/wiki/GLSL#Error_Checking // TODO: use QGLShaderProgram for better compatiblity GLuint GLWidgetRendererPrivate::loadShader(GLenum shaderType, const char* pSource) { if (!hasGLSL) return 0; GLuint shader = glCreateShader(shaderType); if (shader) { glShaderSource(shader, 1, &pSource, NULL); glCompileShader(shader); GLint compiled = GL_FALSE; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (compiled == GL_FALSE) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*)malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); qWarning("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } } glDeleteShader(shader); shader = 0; } } return shader; } GLuint GLWidgetRendererPrivate::createProgram(const char* pVertexSource, const char* pFragmentSource) { if (!hasGLSL) return 0; program = glCreateProgram(); //TODO: var name conflict. temp var is better if (!program) return 0; GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); if (!vertexShader) { return 0; } GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); if (!pixelShader) { glDeleteShader(vertexShader); return 0; } glAttachShader(program, vertexShader); glAttachShader(program, pixelShader); glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*)malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, NULL, buf); qWarning("Could not link program:\n%s\n", buf); free(buf); } } glDetachShader(program, vertexShader); glDeleteShader(vertexShader); glDetachShader(program, pixelShader); glDeleteShader(pixelShader); glDeleteProgram(program); program = 0; return 0; } //Always detach shaders after a successful link. glDetachShader(program, vertexShader); glDetachShader(program, pixelShader); vert = vertexShader; frag = pixelShader; return program; } bool GLWidgetRendererPrivate::releaseShaderProgram() { video_format.setPixelFormat(VideoFormat::Format_Invalid); #if NO_QGL_SHADER if (vert) { if (program) glDetachShader(program, vert); glDeleteShader(vert); } if (frag) { if (program) glDetachShader(program, frag); glDeleteShader(frag); } if (program) { glDeleteProgram(program); program = 0; } #else shader_program->removeAllShaders(); #endif return true; } QString GLWidgetRendererPrivate::getShaderFromFile(const QString &fileName) { QFile f(qApp->applicationDirPath() + "/" + fileName); if (!f.exists()) { f.setFileName(":/" + fileName); } if (!f.open(QIODevice::ReadOnly)) { qWarning("Can not load shader %s: %s", f.fileName().toUtf8().constData(), f.errorString().toUtf8().constData()); return QString(); } QString src = f.readAll(); f.close(); return src; } bool GLWidgetRendererPrivate::prepareShaderProgram(const VideoFormat &fmt, ColorTransform::ColorSpace cs) { // isSupported(pixfmt) if (!fmt.isValid()) return false; releaseShaderProgram(); video_format.setPixelFormatFFmpeg(fmt.pixelFormatFFmpeg()); colorTransform.setInputColorSpace(cs); // TODO: only to kinds, packed.glsl, planar.glsl QString frag; if (fmt.isPlanar()) { frag = getShaderFromFile("shaders/planar.f.glsl"); } else { frag = getShaderFromFile("shaders/rgb.f.glsl"); } if (frag.isEmpty()) return false; if (fmt.isRGB()) { frag.prepend("#define INPUT_RGB\n"); } else { frag.prepend(QString("#define YUV%1P\n").arg(fmt.bitsPerPixel(0))); } if (fmt.isPlanar() && fmt.bytesPerPixel(0) == 2) { if (fmt.isBigEndian()) frag.prepend("#define LA_16BITS_BE\n"); else frag.prepend("#define LA_16BITS_LE\n"); } if (cs == ColorTransform::BT601) { frag.prepend("#define CS_BT601\n"); } else if (cs == ColorTransform::BT709) { frag.prepend("#define CS_BT709\n"); } #if NO_QGL_SHADER program = createProgram(kVertexShader, frag.toUtf8().constData()); if (!program) { qWarning("Could not create shader program."); return false; } // vertex shader. we can set attribute locations calling glBindAttribLocation a_Position = glGetAttribLocation(program, "a_Position"); a_TexCoords = glGetAttribLocation(program, "a_TexCoords"); u_matrix = glGetUniformLocation(program, "u_MVP_matrix"); u_bpp = glGetUniformLocation(program, "u_bpp"); u_opacity = glGetUniformLocation(program, "u_opacity"); // fragment shader u_colorMatrix = glGetUniformLocation(program, "u_colorMatrix"); #else if (!shader_program->addShaderFromSourceCode(QGLShader::Vertex, kVertexShader)) { qWarning("Failed to add vertex shader: %s", shader_program->log().toUtf8().constData()); return false; } if (!shader_program->addShaderFromSourceCode(QGLShader::Fragment, frag)) { qWarning("Failed to add fragment shader: %s", shader_program->log().toUtf8().constData()); return false; } if (!shader_program->link()) { qWarning("Failed to link shader program...%s", shader_program->log().toUtf8().constData()); return false; } // vertex shader a_Position = shader_program->attributeLocation("a_Position"); a_TexCoords = shader_program->attributeLocation("a_TexCoords"); u_matrix = shader_program->uniformLocation("u_MVP_matrix"); u_bpp = shader_program->uniformLocation("u_bpp"); u_opacity = shader_program->uniformLocation("u_opacity"); // fragment shader u_colorMatrix = shader_program->uniformLocation("u_colorMatrix"); #endif //NO_QGL_SHADER qDebug("glGetAttribLocation(\"a_Position\") = %d\n", a_Position); qDebug("glGetAttribLocation(\"a_TexCoords\") = %d\n", a_TexCoords); qDebug("glGetUniformLocation(\"u_MVP_matrix\") = %d\n", u_matrix); qDebug("glGetUniformLocation(\"u_bpp\") = %d\n", u_bpp); qDebug("glGetUniformLocation(\"u_opacity\") = %d\n", u_opacity); qDebug("glGetUniformLocation(\"u_colorMatrix\") = %d\n", u_colorMatrix); if (fmt.isRGB()) u_Texture.resize(1); else u_Texture.resize(fmt.channels()); for (int i = 0; i < u_Texture.size(); ++i) { QString tex_var = QString("u_Texture%1").arg(i); #if NO_QGL_SHADER u_Texture[i] = glGetUniformLocation(program, tex_var.toUtf8().constData()); #else u_Texture[i] = shader_program->uniformLocation(tex_var); #endif qDebug("glGetUniformLocation(\"%s\") = %d\n", tex_var.toUtf8().constData(), u_Texture[i]); } return true; } bool GLWidgetRendererPrivate::initTexture(GLuint tex, GLint internalFormat, GLenum format, GLenum dataType, int width, int height) { glBindTexture(GL_TEXTURE_2D, tex); setupQuality(); // This is necessary for non-power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D , 0 //level , internalFormat //internal format. 4? why GL_RGBA? GL_RGB? , width , height , 0 //border, ES not support , format //format, must the same as internal format? , dataType , NULL); glBindTexture(GL_TEXTURE_2D, 0); return true; } bool GLWidgetRendererPrivate::initTextures(const VideoFormat &fmt) { // isSupported(pixfmt) if (!fmt.isValid()) return false; video_format.setPixelFormatFFmpeg(fmt.pixelFormatFFmpeg()); //http://www.berkelium.com/OpenGL/GDC99/internalformat.html //NV12: UV is 1 plane. 16 bits as a unit. GL_LUMINANCE4, 8, 16, ... 32? //GL_LUMINANCE, GL_LUMINANCE_ALPHA are deprecated in GL3, removed in GL3.1 //replaced by GL_RED, GL_RG, GL_RGB, GL_RGBA? for 1, 2, 3, 4 channel image //http://www.gamedev.net/topic/634850-do-luminance-textures-still-exist-to-opengl/ //https://github.com/kivy/kivy/issues/1738: GL_LUMINANCE does work on a Galaxy Tab 2. LUMINANCE_ALPHA very slow on Linux //ALPHA: vec4(1,1,1,A), LUMINANCE: (L,L,L,1), LUMINANCE_ALPHA: (L,L,L,A) /* * To support both planar and packed use GL_ALPHA and in shader use r,g,a like xbmc does. * or use Swizzle_mask to layout the channels: http://www.opengl.org/wiki/Texture#Swizzle_mask * GL ES2 support: GL_RGB, GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA * http://stackoverflow.com/questions/18688057/which-opengl-es-2-0-texture-formats-are-color-depth-or-stencil-renderable */ if (!fmt.isPlanar()) { GLint internal_fmt; GLenum data_fmt; GLenum data_t; if (!OpenGLHelper::videoFormatToGL(fmt, &internal_fmt, &data_fmt, &data_t)) { qWarning("no opengl format found"); return false; } internal_format = QVector(fmt.planeCount(), internal_fmt); data_format = QVector(fmt.planeCount(), data_fmt); data_type = QVector(fmt.planeCount(), data_t); } else { internal_format.resize(fmt.planeCount()); data_format.resize(fmt.planeCount()); data_type = QVector(fmt.planeCount(), GL_UNSIGNED_BYTE); if (fmt.isPlanar()) { /*! * GLES internal_format == data_format, GL_LUMINANCE_ALPHA is 2 bytes * so if NV12 use GL_LUMINANCE_ALPHA, YV12 use GL_ALPHA */ qDebug("///////////bpp %d", fmt.bytesPerPixel()); internal_format[0] = data_format[0] = GL_LUMINANCE; //or GL_RED for GL if (fmt.planeCount() == 2) { internal_format[1] = data_format[1] = GL_LUMINANCE_ALPHA; } else { if (fmt.bytesPerPixel(1) == 2) { // read 16 bits and compute the real luminance in shader internal_format.fill(GL_LUMINANCE_ALPHA); //vec4(L,L,L,A) data_format.fill(GL_LUMINANCE_ALPHA); } else { internal_format[1] = data_format[1] = GL_LUMINANCE; //vec4(L,L,L,1) internal_format[2] = data_format[2] = GL_ALPHA;//GL_ALPHA; } } for (int i = 0; i < internal_format.size(); ++i) { // xbmc use bpp not bpp(plane) //internal_format[i] = GetGLInternalFormat(data_format[i], fmt.bytesPerPixel(i)); //data_format[i] = internal_format[i]; } } else { //glPixelStorei(GL_UNPACK_ALIGNMENT, fmt.bytesPerPixel()); // TODO: if no alpha, data_fmt is not GL_BGRA. align at every upload? } } for (int i = 0; i < fmt.planeCount(); ++i) { //qDebug("format: %#x GL_LUMINANCE_ALPHA=%#x", data_format[i], GL_LUMINANCE_ALPHA); if (fmt.bytesPerPixel(i) == 2 && fmt.planeCount() == 3) { //data_type[i] = GL_UNSIGNED_SHORT; } int bpp_gl = OpenGLHelper::bytesOfGLFormat(data_format[i], data_type[i]); int pad = qCeil((qreal)(texture_size[i].width() - effective_tex_width[i])/(qreal)bpp_gl); texture_size[i].setWidth(qCeil((qreal)texture_size[i].width()/(qreal)bpp_gl)); texture_upload_size[i].setWidth(qCeil((qreal)texture_upload_size[i].width()/(qreal)bpp_gl)); effective_tex_width[i] /= bpp_gl; //fmt.bytesPerPixel(i); //effective_tex_width_ratio = qDebug("texture width: %d - %d = pad: %d. bpp(gl): %d", texture_size[i].width(), effective_tex_width[i], pad, bpp_gl); } /* * there are 2 fragment shaders: rgb and yuv. * only 1 texture for packed rgb. planar rgb likes yuv * To support both planar and packed yuv, and mixed yuv(NV12), we give a texture sample * for each channel. For packed, each (channel) texture sample is the same. For planar, * packed channels has the same texture sample. * But the number of actural textures we upload is plane count. * Which means the number of texture id equals to plane count */ if (textures.size() != fmt.planeCount()) { glDeleteTextures(textures.size(), textures.data()); qDebug("delete %d textures", textures.size()); textures.clear(); textures.resize(fmt.planeCount()); glGenTextures(textures.size(), textures.data()); } if (!hasGLSL) { initTexture(textures[0], internal_format[0], data_format[0], data_type[0], texture_size[0].width(), texture_size[0].height()); // more than 1? qWarning("Does not support GLSL!"); return false; } qDebug("init textures..."); for (int i = 0; i < textures.size(); ++i) { initTexture(textures[i], internal_format[i], data_format[i], data_type[i], texture_size[i].width(), texture_size[i].height()); } return true; } void GLWidgetRendererPrivate::updateTexturesIfNeeded() { const VideoFormat &fmt = video_frame.format(); bool update_textures = false; if (fmt != video_format) { update_textures = true; //FIXME qDebug("updateTexturesIfNeeded pixel format changed: %s => %s", qPrintable(video_format.name()), qPrintable(fmt.name())); } // effective size may change even if plane size not changed if (update_textures || video_frame.bytesPerLine(0) != plane0Size.width() || video_frame.height() != plane0Size.height() || (plane1_linesize > 0 && video_frame.bytesPerLine(1) != plane1_linesize)) { // no need to check height if plane 0 sizes are equal? update_textures = true; qDebug("---------------------update texture: %dx%d, %s", video_frame.width(), video_frame.height(), video_frame.format().name().toUtf8().constData()); const int nb_planes = fmt.planeCount(); texture_size.resize(nb_planes); texture_upload_size.resize(nb_planes); effective_tex_width.resize(nb_planes); for (int i = 0; i < nb_planes; ++i) { qDebug("plane linesize %d: padded = %d, effective = %d", i, video_frame.bytesPerLine(i), video_frame.effectiveBytesPerLine(i)); qDebug("plane width %d: effective = %d", video_frame.planeWidth(i), video_frame.effectivePlaneWidth(i)); qDebug("planeHeight %d = %d", i, video_frame.planeHeight(i)); // we have to consider size of opengl format. set bytesPerLine here and change to width later texture_size[i] = QSize(video_frame.bytesPerLine(i), video_frame.planeHeight(i)); texture_upload_size[i] = texture_size[i]; effective_tex_width[i] = video_frame.effectiveBytesPerLine(i); //store bytes here, modify as width later // TODO: ratio count the GL_UNPACK_ALIGN? //effective_tex_width_ratio = qMin((qreal)1.0, (qreal)video_frame.effectiveBytesPerLine(i)/(qreal)video_frame.bytesPerLine(i)); } plane1_linesize = 0; if (nb_planes > 1) { texture_size[0].setWidth(texture_size[1].width() * effective_tex_width[0]/effective_tex_width[1]); // height? how about odd? plane1_linesize = video_frame.bytesPerLine(1); } effective_tex_width_ratio = (qreal)video_frame.effectiveBytesPerLine(nb_planes-1)/(qreal)video_frame.bytesPerLine(nb_planes-1); qDebug("effective_tex_width_ratio=%f", effective_tex_width_ratio); plane0Size.setWidth(video_frame.bytesPerLine(0)); plane0Size.setHeight(video_frame.height()); } if (update_textures) { initTextures(fmt); } } void GLWidgetRendererPrivate::updateShaderIfNeeded() { const VideoFormat& fmt(video_frame.format()); if (fmt != video_format) { qDebug("pixel format changed: %s => %s", qPrintable(video_format.name()), qPrintable(fmt.name())); } VideoMaterialType *newType = materialType(fmt); if (material_type == newType) return; material_type = newType; // http://forum.doom9.org/archive/index.php/t-160211.html ColorTransform::ColorSpace cs = ColorTransform::RGB; if (fmt.isRGB()) { if (fmt.isPlanar()) cs = ColorTransform::GBR; } else { if (video_frame.width() >= 1280 || video_frame.height() > 576) //values from mpv cs = ColorTransform::BT709; else cs = ColorTransform::BT601; } if (!prepareShaderProgram(fmt, cs)) { qWarning("shader program create error..."); return; } else { qDebug("shader program created!!!"); } } void GLWidgetRendererPrivate::upload(const QRect &roi) { for (int i = 0; i < video_frame.planeCount(); ++i) { uploadPlane(i, internal_format[i], data_format[i], roi); } } void GLWidgetRendererPrivate::uploadPlane(int p, GLint internalFormat, GLenum format, const QRect& roi) { // FIXME: why happens on win? if (video_frame.bytesPerLine(p) <= 0) return; OpenGLHelper::glActiveTexture(GL_TEXTURE0 + p); //xbmc: only for es, not for desktop? glBindTexture(GL_TEXTURE_2D, textures[p]); ////nv12: 2 //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);//GetAlign(video_frame.bytesPerLine(p))); #if defined(GL_UNPACK_ROW_LENGTH) // glPixelStorei(GL_UNPACK_ROW_LENGTH, video_frame.bytesPerLine(p)); #endif //qDebug("bpl[%d]=%d width=%d", p, video_frame.bytesPerLine(p), video_frame.planeWidth(p)); // This is necessary for non-power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //uploading part of image eats less gpu memory, but may be more cpu(gles) //FIXME: more cpu usage then qpainter. FBO, VBO? //roi for planes? if (ROI_TEXCOORDS || roi.size() == video_frame.size()) { glTexSubImage2D(GL_TEXTURE_2D , 0 //level , 0 // xoffset , 0 // yoffset , texture_upload_size[p].width() , texture_upload_size[p].height() , format //format, must the same as internal format? , data_type[p] , video_frame.bits(p)); } else { int roi_x = roi.x(); int roi_y = roi.y(); int roi_w = roi.width(); int roi_h = roi.height(); int plane_w = video_frame.planeWidth(p); VideoFormat fmt = video_frame.format(); if (p == 0) { plane0Size = QSize(roi_w, roi_h); // } else { roi_x = fmt.chromaWidth(roi_x); roi_y = fmt.chromaHeight(roi_y); roi_w = fmt.chromaWidth(roi_w); roi_h = fmt.chromaHeight(roi_h); } qDebug("roi: %d, %d %dx%d", roi_x, roi_y, roi_w, roi_h); #if 0// defined(GL_UNPACK_ROW_LENGTH) && defined(GL_UNPACK_SKIP_PIXELS) // http://stackoverflow.com/questions/205522/opengl-subtexturing glPixelStorei(GL_UNPACK_ROW_LENGTH, plane_w); //glPixelStorei or compute pointer glPixelStorei(GL_UNPACK_SKIP_PIXELS, roi_x); glPixelStorei(GL_UNPACK_SKIP_ROWS, roi_y); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, roi_w, roi_h, 0, format, GL_UNSIGNED_BYTE, video_frame.bits(p)); //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, roi_w, roi_h, format, GL_UNSIGNED_BYTE, video_frame.bits(p)); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); #else // GL ES //define it? or any efficient way? //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, roi_w, roi_h, 0, format, GL_UNSIGNED_BYTE, NULL); const char *src = (char*)video_frame.bits(p) + roi_y*plane_w + roi_x*fmt.bytesPerPixel(p); #define UPLOAD_LINE 1 #if UPLOAD_LINE for (int y = 0; y < roi_h; y++) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, roi_w, 1, format, GL_UNSIGNED_BYTE, src); } #else int line_size = roi_w*fmt.bytesPerPixel(p); char *sub = (char*)malloc(roi_h*line_size); char *dst = sub; for (int y = 0; y < roi_h; y++) { memcpy(dst, src, line_size); src += video_frame.bytesPerLine(p); dst += line_size; } // FIXME: crash glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, roi_w, roi_h, format, GL_UNSIGNED_BYTE, sub); free(sub); #endif //UPLOAD_LINE #endif //GL_UNPACK_ROW_LENGTH } #if defined(GL_UNPACK_ROW_LENGTH) // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif //glPixelStorei(GL_UNPACK_ALIGNMENT, 4); } GLWidgetRenderer::GLWidgetRenderer(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFlags f): QGLWidget(parent, shareWidget, f),VideoRenderer(*new GLWidgetRendererPrivate()) { DPTR_INIT_PRIVATE(GLWidgetRenderer); DPTR_D(GLWidgetRenderer); setPreferredPixelFormat(VideoFormat::Format_YUV420P); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); //default: swap in qpainter dtor. we should swap before QPainter.endNativePainting() setAutoBufferSwap(false); setAutoFillBackground(false); d.painter = new QPainter(); d.filter_context = VideoFilterContext::create(VideoFilterContext::QtPainter); d.filter_context->paint_device = this; d.filter_context->painter = d.painter; } VideoRendererId GLWidgetRenderer::id() const { return VideoRendererId_GLWidget; } bool GLWidgetRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { Q_UNUSED(pixfmt); return pixfmt != VideoFormat::Format_YUYV && pixfmt != VideoFormat::Format_UYVY; } bool GLWidgetRenderer::receiveFrame(const VideoFrame& frame) { DPTR_D(GLWidgetRenderer); d.video_frame = frame; update(); //can not call updateGL() directly because no event and paintGL() will in video thread return true; } bool GLWidgetRenderer::needUpdateBackground() const { return true; } void GLWidgetRenderer::drawBackground() { glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void GLWidgetRenderer::drawFrame() { DPTR_D(GLWidgetRenderer); d.updateTexturesIfNeeded(); d.updateShaderIfNeeded(); QRect roi = realROI(); const int nb_planes = d.video_frame.planeCount(); //number of texture id int mapped = 0; for (int i = 0; i < nb_planes; ++i) { if (d.video_frame.map(GLTextureSurface, &d.textures[i])) { OpenGLHelper::glActiveTexture(GL_TEXTURE0 + i); // if mapped by SurfaceInterop, the texture may be not bound glBindTexture(GL_TEXTURE_2D, d.textures[i]); //we've bind 0 after upload() mapped++; } } if (mapped < nb_planes) { d.upload(roi); } //TODO: compute kTexCoords only if roi changed #if ROI_TEXCOORDS /*! tex coords: ROI/frameRect()*effective_tex_width_ratio */ const GLfloat kTexCoords[] = { (GLfloat)roi.x()*(GLfloat)d.effective_tex_width_ratio/(GLfloat)d.video_frame.width(), (GLfloat)roi.y()/(GLfloat)d.video_frame.height(), (GLfloat)(roi.x() + roi.width())*(GLfloat)d.effective_tex_width_ratio/(GLfloat)d.video_frame.width(), (GLfloat)roi.y()/(GLfloat)d.video_frame.height(), (GLfloat)(roi.x() + roi.width())*(GLfloat)d.effective_tex_width_ratio/(GLfloat)d.video_frame.width(), (GLfloat)(roi.y()+roi.height())/(GLfloat)d.video_frame.height(), (GLfloat)roi.x()*(GLfloat)d.effective_tex_width_ratio/(GLfloat)d.video_frame.width(), (GLfloat)(roi.y()+roi.height())/(GLfloat)d.video_frame.height(), }; #else const GLfloat kTexCoords[] = { 0, 0, 1, 0, 1, 1, 0, 1, }; #endif //ROI_TEXCOORDS if (d.video_format.hasAlpha()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); } #ifndef QT_OPENGL_ES_2 //GL_XXX may not defined in ES2. so macro is required if (!d.hasGLSL) { //qpainter will reset gl state, so need glMatrixMode and clear color(in drawBackground()) //TODO: study what state will be reset glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glPushMatrix(); glScalef((float)d.out_rect.width()/(float)d.renderer_width, (float)d.out_rect.height()/(float)d.renderer_height, 0); glVertexPointer(2, GL_FLOAT, 0, kVertices); glEnableClientState(GL_VERTEX_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, kTexCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glPopMatrix(); glDisable(GL_BLEND); for (int i = 0; i < d.textures.size(); ++i) { d.video_frame.unmap(&d.textures[i]); } return; } #endif //QT_OPENGL_ES_2 // uniforms begin // shader program may not ready before upload #if NO_QGL_SHADER glUseProgram(d.program); //for glUniform #else d.shader_program->bind(); #endif //NO_QGL_SHADER // all texture ids should be binded when renderering even for packed plane! for (int i = 0; i < nb_planes; ++i) { // use glUniform1i to swap planes. swap uv: i => (3-i)%3 // TODO: in shader, use uniform sample2D u_Texture[], and use glUniform1iv(u_Texture, 3, {...}) #if NO_QGL_SHADER glUniform1i(d.u_Texture[i], i); #else d.shader_program->setUniformValue(d.u_Texture[i], (GLint)i); #endif } if (nb_planes < d.u_Texture.size()) { for (int i = nb_planes; i < d.u_Texture.size(); ++i) { #if NO_QGL_SHADER glUniform1i(d.u_Texture[i], nb_planes - 1); #else d.shader_program->setUniformValue(d.u_Texture[i], (GLint)(nb_planes - 1)); #endif } } /* * in Qt4 QMatrix4x4 stores qreal (double), while GLfloat may be float * QShaderProgram deal with this case. But compares sizeof(QMatrix4x4) and (GLfloat)*16 * which seems not correct because QMatrix4x4 has a flag var */ GLfloat *mat = (GLfloat*)d.colorTransform.matrixRef().data(); GLfloat glm[16]; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) if (sizeof(qreal) != sizeof(GLfloat)) { #else if (sizeof(float) != sizeof(GLfloat)) { #endif d.colorTransform.matrixData(glm); mat = glm; } //QMatrix4x4 stores value in Column-major order to match OpenGL. so transpose is not required in glUniformMatrix4fv #if NO_QGL_SHADER glUniformMatrix4fv(d.u_colorMatrix, 1, GL_FALSE, mat); glUniformMatrix4fv(d.u_matrix, 1, GL_FALSE/*transpose or not*/, d.mpv_matrix.constData()); glUniform1f(d.u_bpp, (GLfloat)d.video_format.bitsPerPixel(0)); glUniform1f(d.u_opacity, (GLfloat)1.0); #else d.shader_program->setUniformValue(d.u_colorMatrix, d.colorTransform.matrixRef()); d.shader_program->setUniformValue(d.u_matrix, d.mpv_matrix); d.shader_program->setUniformValue(d.u_bpp, (GLfloat)d.video_format.bitsPerPixel(0)); d.shader_program->setUniformValue(d.u_opacity, (GLfloat)1.0); #endif // uniforms done. attributes begin //qpainter need. TODO: VBO? #if NO_QGL_SHADER glVertexAttribPointer(d.a_Position, 2, GL_FLOAT, GL_FALSE, 0, kVertices); glVertexAttribPointer(d.a_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, kTexCoords); glEnableVertexAttribArray(d.a_Position); glEnableVertexAttribArray(d.a_TexCoords); #else d.shader_program->setAttributeArray(d.a_Position, GL_FLOAT, kVertices, 2); d.shader_program->setAttributeArray(d.a_TexCoords, GL_FLOAT, kTexCoords, 2); d.shader_program->enableAttributeArray(d.a_Position); d.shader_program->enableAttributeArray(d.a_TexCoords); #endif glDrawArrays(GL_TRIANGLE_FAN, 0, 4); #if NO_QGL_SHADER //glUseProgram(0); glDisableVertexAttribArray(d.a_TexCoords); glDisableVertexAttribArray(d.a_Position); #else d.shader_program->release(); d.shader_program->disableAttributeArray(d.a_TexCoords); d.shader_program->disableAttributeArray(d.a_Position); #endif glDisable(GL_BLEND); for (int i = 0; i < d.textures.size(); ++i) { d.video_frame.unmap(&d.textures[i]); } } void GLWidgetRenderer::initializeGL() { DPTR_D(GLWidgetRenderer); makeCurrent(); //const QByteArray extensions(reinterpret_cast(glGetString(GL_EXTENSIONS))); d.hasGLSL = QGLShaderProgram::hasOpenGLShaderPrograms(); qDebug("OpenGL version: %d.%d hasGLSL: %d", format().majorVersion(), format().minorVersion(), d.hasGLSL); #if QTAV_HAVE(QGLFUNCTIONS) initializeGLFunctions(); d.initializeGLFunctions(); #endif //QTAV_HAVE(QGLFUNCTIONS) glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); if (!d.hasGLSL) { #ifndef QT_OPENGL_ES_2 glShadeModel(GL_SMOOTH); //setupQuality? glClearDepth(1.0f); #endif //QT_OPENGL_ES_2 } else { d.initWithContext(context()); } glClearColor(0.0, 0.0, 0.0, 0.0); } void GLWidgetRenderer::paintGL() { DPTR_D(GLWidgetRenderer); /* we can mix gl and qpainter. * QPainter painter(this); * painter.beginNativePainting(); * gl functions... * painter.endNativePainting(); * swapBuffers(); */ handlePaintEvent(); swapBuffers(); if (d.painter && d.painter->isActive()) d.painter->end(); } void GLWidgetRenderer::resizeGL(int w, int h) { DPTR_D(GLWidgetRenderer); //qDebug("%s @%d %dx%d", __FUNCTION__, __LINE__, d.out_rect.width(), d.out_rect.height()); glViewport(0, 0, w, h); d.setupAspectRatio(); #ifndef QT_OPENGL_ES_2 //?? if (!d.hasGLSL) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } #endif //QT_OPENGL_ES_2 } void GLWidgetRenderer::resizeEvent(QResizeEvent *e) { DPTR_D(GLWidgetRenderer); d.update_background = true; resizeRenderer(e->size()); QGLWidget::resizeEvent(e); //will call resizeGL(). TODO:will call paintEvent()? } //TODO: out_rect not correct when top level changed void GLWidgetRenderer::showEvent(QShowEvent *) { DPTR_D(GLWidgetRenderer); d.update_background = true; /* * Do something that depends on widget below! e.g. recreate render target for direct2d. * When Qt::WindowStaysOnTopHint changed, window will hide first then show. If you * don't do anything here, the widget content will never be updated. */ } void GLWidgetRenderer::onSetOutAspectRatio(qreal ratio) { Q_UNUSED(ratio); d_func().setupAspectRatio(); } void GLWidgetRenderer::onSetOutAspectRatioMode(OutAspectRatioMode mode) { Q_UNUSED(mode); d_func().setupAspectRatio(); } bool GLWidgetRenderer::onSetOrientation(int value) { Q_UNUSED(value); d_func().setupAspectRatio(); return true; } bool GLWidgetRenderer::onSetBrightness(qreal b) { DPTR_D(GLWidgetRenderer); if (!d.hasGLSL) return false; d.colorTransform.setBrightness(b); return true; } bool GLWidgetRenderer::onSetContrast(qreal c) { DPTR_D(GLWidgetRenderer); if (!d.hasGLSL) return false; d.colorTransform.setContrast(c); return true; } bool GLWidgetRenderer::onSetHue(qreal h) { DPTR_D(GLWidgetRenderer); if (!d.hasGLSL) return false; d.colorTransform.setHue(h); return true; } bool GLWidgetRenderer::onSetSaturation(qreal s) { DPTR_D(GLWidgetRenderer); if (!d.hasGLSL) return false; d.colorTransform.setSaturation(s); return true; } } //namespace QtAV QtAV-1.12.0/widgets/GLWidgetRenderer2.cpp000066400000000000000000000061101312235004300177660ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/GLWidgetRenderer2.h" #include "QtAV/private/OpenGLRendererBase_p.h" #include namespace QtAV { class GLWidgetRenderer2Private : public OpenGLRendererBasePrivate { public: GLWidgetRenderer2Private(QPaintDevice *pd) : OpenGLRendererBasePrivate(pd) {} }; VideoRendererId GLWidgetRenderer2::id() const { return VideoRendererId_GLWidget2; } GLWidgetRenderer2::GLWidgetRenderer2(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFlags f): QGLWidget(parent, shareWidget, f) , OpenGLRendererBase(*new GLWidgetRenderer2Private(this)) { setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_NoSystemBackground); //default: swap in qpainter dtor. we should swap before QPainter.endNativePainting() setAutoBufferSwap(false); setAutoFillBackground(false); } void GLWidgetRenderer2::initializeGL() { // already current onInitializeGL(); } void GLWidgetRenderer2::paintGL() { DPTR_D(GLWidgetRenderer2); /* we can mix gl and qpainter. * QPainter painter(this); * painter.beginNativePainting(); * gl functions... * painter.endNativePainting(); * swapBuffers(); */ handlePaintEvent(); swapBuffers(); if (d.painter && d.painter->isActive()) d.painter->end(); } void GLWidgetRenderer2::resizeGL(int w, int h) { onResizeGL(w, h); } void GLWidgetRenderer2::resizeEvent(QResizeEvent *e) { onResizeEvent(e->size().width(), e->size().height()); QGLWidget::resizeEvent(e); //will call resizeGL(). TODO:will call paintEvent()? } void GLWidgetRenderer2::showEvent(QShowEvent *) { onShowEvent(); resizeGL(width(), height()); } } //namespace QtAV QtAV-1.12.0/widgets/GraphicsItemRenderer.cpp000066400000000000000000000203001312235004300206120ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/GraphicsItemRenderer.h" #include "QtAV/private/QPainterRenderer_p.h" #include "QtAV/FilterContext.h" #define QTAV_HAVE_OPENGL (!defined QT_NO_OPENGL && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) || defined(QT_OPENGL_LIB))) #if QTAV_HAVE(OPENGL) #include "QtAV/OpenGLVideo.h" #else typedef float GLfloat; #endif #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif namespace QtAV { class GraphicsItemRendererPrivate : public QPainterRendererPrivate { public: GraphicsItemRendererPrivate() : frame_changed(false) , opengl(false) {} virtual ~GraphicsItemRendererPrivate(){} void setupAspectRatio() { matrix.setToIdentity(); matrix.scale((GLfloat)out_rect.width()/(GLfloat)renderer_width, (GLfloat)out_rect.height()/(GLfloat)renderer_height, 1); if (orientation) matrix.rotate(orientation, 0, 0, 1); // Z axis } // return true if opengl is enabled and context is ready. may called by non-rendering thread bool checkGL() { #if QTAV_HAVE(OPENGL) if (!opengl) { glv.setOpenGLContext(0); // it's for Qt4. may not in rendering thread return false; } if (!glv.openGLContext()) { //qWarning("no opengl context! set current"); // null if not called from renderering thread; QOpenGLContext *ctx = const_cast(QOpenGLContext::currentContext()); if (!ctx) return false; glv.setOpenGLContext(ctx); } return true; #endif return false; } bool frame_changed; bool opengl; #if QTAV_HAVE(OPENGL) OpenGLVideo glv; #endif QMatrix4x4 matrix; }; VideoRendererId GraphicsItemRenderer::id() const { return VideoRendererId_GraphicsItem; } GraphicsItemRenderer::GraphicsItemRenderer(QGraphicsItem * parent) :GraphicsWidget(parent),QPainterRenderer(*new GraphicsItemRendererPrivate()) { setFlag(ItemIsFocusable); //receive key events //setAcceptHoverEvents(true); #if CONFIG_GRAPHICSWIDGET setFocusPolicy(Qt::ClickFocus); //for widget #endif //CONFIG_GRAPHICSWIDGET } GraphicsItemRenderer::GraphicsItemRenderer(GraphicsItemRendererPrivate &d, QGraphicsItem *parent) :GraphicsWidget(parent),QPainterRenderer(d) { setFlag(ItemIsFocusable); //receive key events //setAcceptHoverEvents(true); #if CONFIG_GRAPHICSWIDGET setFocusPolicy(Qt::ClickFocus); //for widget #endif //CONFIG_GRAPHICSWIDGET } bool GraphicsItemRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { if (isOpenGL()) return true; return QPainterRenderer::isSupported(pixfmt); } bool GraphicsItemRenderer::receiveFrame(const VideoFrame& frame) { #if QTAV_HAVE(OPENGL) DPTR_D(GraphicsItemRenderer); if (isOpenGL()) { d.video_frame = frame; d.frame_changed = true; } else #endif { preparePixmap(frame); } scene()->update(sceneBoundingRect()); //TODO: thread? //update(); //does not cause an immediate paint. my not redraw. return true; } QRectF GraphicsItemRenderer::boundingRect() const { return QRectF(0, 0, rendererWidth(), rendererHeight()); } bool GraphicsItemRenderer::isOpenGL() const { #if QTAV_HAVE(OPENGL) return d_func().opengl; #endif return false; } void GraphicsItemRenderer::setOpenGL(bool o) { DPTR_D(GraphicsItemRenderer); if (d.opengl == o) return; d.opengl = o; Q_EMIT openGLChanged(); } OpenGLVideo* GraphicsItemRenderer::opengl() const { #if QTAV_HAVE(OPENGL) return const_cast(&d_func().glv); #endif return NULL; } void GraphicsItemRenderer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); DPTR_D(GraphicsItemRenderer); d.painter = painter; QPainterFilterContext *ctx = static_cast(d.filter_context); if (ctx) { ctx->painter = d.painter; } else { qWarning("FilterContext not available!"); } handlePaintEvent(); d.painter = 0; //painter may be not available outside this function if (ctx) ctx->painter = 0; } void GraphicsItemRenderer::drawBackground() { DPTR_D(GraphicsItemRenderer); #if QTAV_HAVE(OPENGL) if (d.checkGL()) { // d.glv.fill(QColor(0, 0, 0)); //FIXME: fill boundingRect return; } else #endif { QPainterRenderer::drawBackground(); } } void GraphicsItemRenderer::drawFrame() { DPTR_D(GraphicsItemRenderer); if (!d.painter) return; #if QTAV_HAVE(OPENGL) if (d.checkGL()) { if (d.frame_changed) { d.glv.setCurrentFrame(d.video_frame); d.frame_changed = false; } d.glv.render(boundingRect(), realROI(), d.matrix*sceneTransform()); return; } #endif QPainterRenderer::drawFrame(); } void GraphicsItemRenderer::onSetOutAspectRatio(qreal ratio) { Q_UNUSED(ratio); DPTR_D(GraphicsItemRenderer); d.setupAspectRatio(); } bool GraphicsItemRenderer::onSetOrientation(int value) { Q_UNUSED(value); d_func().setupAspectRatio(); update(); //TODO: thread? return true; } void GraphicsItemRenderer::onSetOutAspectRatioMode(OutAspectRatioMode mode) { Q_UNUSED(mode); DPTR_D(GraphicsItemRenderer); d.setupAspectRatio(); } bool GraphicsItemRenderer::onSetBrightness(qreal b) { if (!isOpenGL()) return false; Q_UNUSED(b); #if QTAV_HAVE(OPENGL) d_func().glv.setBrightness(b); update(); return true; #endif return false; } bool GraphicsItemRenderer::onSetContrast(qreal c) { if (!isOpenGL()) return false; Q_UNUSED(c); #if QTAV_HAVE(OPENGL) d_func().glv.setContrast(c); update(); return true; #endif return false; } bool GraphicsItemRenderer::onSetHue(qreal h) { if (!isOpenGL()) return false; Q_UNUSED(h); #if QTAV_HAVE(OPENGL) d_func().glv.setHue(h); update(); return true; #endif return false; } bool GraphicsItemRenderer::onSetSaturation(qreal s) { if (!isOpenGL()) return false; Q_UNUSED(s); #if QTAV_HAVE(OPENGL) d_func().glv.setSaturation(s); update(); return true; #endif return false; } //GraphicsWidget will lose focus forever if focus out. Why? #if CONFIG_GRAPHICSWIDGET bool GraphicsItemRenderer::event(QEvent *event) { setFocus(); //WHY: Force focus QEvent::Type type = event->type(); qDebug("GraphicsItemRenderer event type = %d", type); if (type == QEvent::KeyPress) { qDebug("KeyPress Event. key=%d", static_cast(event)->key()); } return true; } #else /*simply passes event to QGraphicsWidget::event(). you should not have to *reimplement sceneEvent() in a subclass of QGraphicsWidget. */ /* bool GraphicsItemRenderer::sceneEvent(QEvent *event) { QEvent::Type type = event->type(); qDebug("sceneEvent type = %d", type); if (type == QEvent::KeyPress) { qDebug("KeyPress Event. key=%d", static_cast(event)->key()); } return true; } */ #endif //!CONFIG_GRAPHICSWIDGET } //namespace QtAV QtAV-1.12.0/widgets/OpenGLWidgetRenderer.cpp000066400000000000000000000057411312235004300205370ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/OpenGLWidgetRenderer.h" #include "QtAV/private/OpenGLRendererBase_p.h" #include #include #include namespace QtAV { class OpenGLWidgetRendererPrivate : public OpenGLRendererBasePrivate { public: OpenGLWidgetRendererPrivate(QPaintDevice *pd) : OpenGLRendererBasePrivate(pd) {} }; VideoRendererId OpenGLWidgetRenderer::id() const { return VideoRendererId_OpenGLWidget; } OpenGLWidgetRenderer::OpenGLWidgetRenderer(QWidget *parent, Qt::WindowFlags f): QOpenGLWidget(parent, f) , OpenGLRendererBase(*new OpenGLWidgetRendererPrivate(this)) { setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); } void OpenGLWidgetRenderer::initializeGL() { onInitializeGL(); } void OpenGLWidgetRenderer::paintGL() { onPaintGL(); } void OpenGLWidgetRenderer::resizeGL(int w, int h) { // QGLWidget uses window()->windowHandle()->devicePixelRatio() for resizeGL(), while QOpenGLWidget does not, so scale here #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) if (!context()) return; const qreal dpr = context()->screen()->devicePixelRatio(); #elif QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) // qApp->devicePixelRatio() is global, window()->windowHandle()->devicePixelRatio() depends on screen, check window() and windowHandle() is required. // QWidget.devicePixelRatio() is int, but float value is not implemented in old qt, so just use int is fine. const qreal dpr = devicePixelRatio(); #else const qreal dpr = qApp->devicePixelRatio(); #endif onResizeGL(w*dpr, h*dpr); } void OpenGLWidgetRenderer::resizeEvent(QResizeEvent *e) { onResizeEvent(e->size().width(), e->size().height()); QOpenGLWidget::resizeEvent(e); //will call resizeGL(). TODO:will call paintEvent()? } void OpenGLWidgetRenderer::showEvent(QShowEvent *e) { onShowEvent(); // TODO: onShowEvent(w, h)? resizeGL(width(), height()); } } //namespace QtAV QtAV-1.12.0/widgets/QOpenGLWidget.cpp000066400000000000000000000132411312235004300171630ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/QOpenGLWidget.h" #include #include // for dynamicgl. qglfunctions before qt5.3 does not have portable gl functions #ifdef QT_OPENGL_DYNAMIC #define DYGL(glFunc) QOpenGLContext::currentContext()->functions()->glFunc #else #define DYGL(glFunc) glFunc #endif namespace QtAV { // TODO: is QOpenGLWidgetPaintDevice required? class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice { public: QOpenGLWidgetPaintDevice(QOpenGLWidget *widget) : QOpenGLPaintDevice() , w(widget) { } void ensureActiveTarget() Q_DECL_OVERRIDE { if (QOpenGLContext::currentContext() != w->context()) { w->makeCurrent(); } } private: QOpenGLWidget *w; }; QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_initialized(false) , m_fakeHidden(false) , m_context(0) , m_paintDevice(0) { setAttribute(Qt::WA_NativeWindow); // ensure windowHandle() is not null // WA_PaintOnScreen: QWidget::paintEngine: Should no longer be called. This flag is only supported on X11 and it disables double buffering //setAttribute(Qt::WA_PaintOnScreen); // enforce native window, so windowHandle() is not null setAttribute(Qt::WA_NoSystemBackground); //setAutoFillBackground(true); // for compatibility // FIXME: why setSurfaceType crash? //windowHandle()->setSurfaceType(QWindow::OpenGLSurface); } QOpenGLWidget::~QOpenGLWidget() { delete m_paintDevice; } void QOpenGLWidget::setFormat(const QSurfaceFormat &format) { m_requestedFormat = format; } QSurfaceFormat QOpenGLWidget::format() const { return m_requestedFormat; } bool QOpenGLWidget::isValid() const { return m_initialized && m_context->isValid(); } void QOpenGLWidget::makeCurrent() { if (!m_initialized) { qWarning("QOpenGLWidget: Cannot make uninitialized widget current"); return; } if (!windowHandle()) { qWarning("QOpenGLWidget: No window handle"); return; } m_context->makeCurrent((QSurface*)windowHandle()); } void QOpenGLWidget::doneCurrent() { if (!m_initialized) return; m_context->doneCurrent(); } QOpenGLContext *QOpenGLWidget::context() const { return m_context; } QPaintDevice* QOpenGLWidget::redirected(QPoint *offset) const { Q_UNUSED(offset); // TODO: check backing store like Qt does return m_paintDevice; } void QOpenGLWidget::initializeGL() { } void QOpenGLWidget::paintGL() { } void QOpenGLWidget::resizeGL(int w, int h) { Q_UNUSED(w); Q_UNUSED(h); } void QOpenGLWidget::paintEvent(QPaintEvent *e) { Q_UNUSED(e); if (!m_initialized) return; if (updatesEnabled()) render(); } void QOpenGLWidget::resizeEvent(QResizeEvent *e) { if (e->size().isEmpty()) { m_fakeHidden = true; return; } m_fakeHidden = false; initialize(); if (!m_initialized) return; //recreateFbo(); resizeGL(width(), height()); invokeUserPaint(); //resolveSamples(); } void QOpenGLWidget::initialize() { if (m_initialized) return; QWindow *win = windowHandle(); if (!win) { qWarning("QOpenGLWidget: No window handle"); return; } m_context = new QOpenGLContext(this); // TODO: shareContext() m_context->setFormat(m_requestedFormat); if (!m_context->create()) { qWarning("QOpenGLWidget: Failed to create context"); return; } //m_context = QOpenGLContext::currentContext(); if (!m_context) { qWarning("QOpenGLWidget: QOpenGLContext is null"); return; } if (!m_context->makeCurrent(win)) { qWarning("QOpenGLWidget: Failed to make context current"); return; } m_paintDevice = new QOpenGLWidgetPaintDevice(this); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) m_paintDevice->setSize(size() * devicePixelRatio()); m_paintDevice->setDevicePixelRatio(devicePixelRatio()); #else m_paintDevice->setSize(size()); #endif m_initialized = true; initializeGL(); } void QOpenGLWidget::render() { if (m_fakeHidden || !m_initialized) return; // QOpenGLContext::swapBuffers() called with non-exposed window, behavior is undefined QWindow *win = windowHandle(); if (!win || !win->isExposed()) return; makeCurrent(); invokeUserPaint(); m_context->swapBuffers(win); } void QOpenGLWidget::invokeUserPaint() { #if QT_VERSION >= QT_VERSION_CHECK(5, 1 , 0) DYGL(glViewport(0, 0, width()*devicePixelRatio(), height()*devicePixelRatio())); #else DYGL(glViewport(0, 0, width(), height())); #endif paintGL(); DYGL(glFlush()); } } //namespace QtAV QtAV-1.12.0/widgets/QtAVWidgets.rc000066400000000000000000000020731312235004300165370ustar00rootroot00000000000000#include "winver.h" #include "QtAVWidgets/version.h" VS_VERSION_INFO VERSIONINFO FILEVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 PRODUCTVERSION QTAV_MAJOR,QTAV_MINOR,QTAV_PATCH,0 #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEFLAGSMASK 0x3fL FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000004b0" BEGIN VALUE "CompanyName", "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" VALUE "FileDescription", "QtAVWidgets module. QtAV Multimedia framework. http://qtav.org" VALUE "FileVersion", QTAVWIDGETS_VERSION_STR ".0" VALUE "LegalCopyright", "Copyright (C) 2012-2016 WangBin, wbsecg1@gmail.com" VALUE "InternalName", "QtAV Widgets" VALUE "OriginalFilename", "QtAVWidgets.dll" VALUE "ProductName", "QtAV Widgets" VALUE "ProductVersion", QTAVWIDGETS_VERSION_STR ".0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0, 1200 END END QtAV-1.12.0/widgets/QtAVWidgets/000077500000000000000000000000001312235004300162075ustar00rootroot00000000000000QtAV-1.12.0/widgets/QtAVWidgets/GLWidgetRenderer.h000066400000000000000000000060601312235004300215170ustar00rootroot00000000000000/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GLWIDGETRENDERER_H #define QTAV_GLWIDGETRENDERER_H #include #include #include // TODO: QGLFunctions is in Qt4.8+. meego is 4.7 #define QTAV_HAVE_QGLFUNCTIONS QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) #if QTAV_HAVE(QGLFUNCTIONS) #include #endif namespace QtAV { class GLWidgetRendererPrivate; class Q_AVWIDGETS_EXPORT GLWidgetRenderer : public QGLWidget, public VideoRenderer #if QTAV_HAVE(QGLFUNCTIONS) //TODO: why use QT_VERSION will result in moc error? , public QGLFunctions #endif //QTAV_HAVE(QGLFUNCTIONS) { Q_OBJECT DPTR_DECLARE_PRIVATE(GLWidgetRenderer) public: GLWidgetRenderer(QWidget* parent = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0); virtual VideoRendererId id() const; virtual bool isSupported(VideoFormat::PixelFormat pixfmt) const; virtual QWidget* widget() { return this; } protected: virtual bool receiveFrame(const VideoFrame& frame); virtual bool needUpdateBackground() const; //called in paintEvent before drawFrame() when required virtual void drawBackground(); //draw the current frame using the current paint engine. called by paintEvent() virtual void drawFrame(); virtual void initializeGL(); virtual void paintGL(); virtual void resizeGL(int w, int h); virtual void resizeEvent(QResizeEvent *); virtual void showEvent(QShowEvent *); private: virtual void onSetOutAspectRatioMode(OutAspectRatioMode mode); virtual void onSetOutAspectRatio(qreal ratio); virtual bool onSetOrientation(int value); /*! * \brief onSetBrightness * only works for GLSL. otherwise return false, means that do nothing, brightness() does not change. * \return */ virtual bool onSetBrightness(qreal b); virtual bool onSetContrast(qreal c); virtual bool onSetHue(qreal h); virtual bool onSetSaturation(qreal s); }; typedef GLWidgetRenderer VideoRendererGLWidget; } //namespace QtAV #endif // QTAV_GLWidgetRenderer_H QtAV-1.12.0/widgets/QtAVWidgets/GLWidgetRenderer2.h000066400000000000000000000102611312235004300215770ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_GLWIDGETRENDERER2_H #define QTAV_GLWIDGETRENDERER2_H #ifndef QT_NO_OPENGL #include #include #include namespace QtAV { class GLWidgetRenderer2Private; /*! * \brief The GLWidgetRenderer2 class * Renderering video frames using GLSL. A more generic high level class OpenGLVideo is used internally. * TODO: for Qt5, no QtOpenGL, use QWindow instead. */ class Q_AVWIDGETS_EXPORT GLWidgetRenderer2 : public QGLWidget, public OpenGLRendererBase { Q_OBJECT DPTR_DECLARE_PRIVATE(GLWidgetRenderer2) Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: GLWidgetRenderer2(QWidget* parent = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0); virtual VideoRendererId id() const Q_DECL_OVERRIDE; virtual QWidget* widget() Q_DECL_OVERRIDE { return this; } Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; protected: virtual void initializeGL() Q_DECL_OVERRIDE; virtual void paintGL() Q_DECL_OVERRIDE; virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; // not virtual in QGLWidget (Qt<5.5) virtual void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef GLWidgetRenderer2 VideoRendererGLWidget2; } //namespace QtAV #endif //QT_NO_OPENGL #endif // QTAV_GLWIDGETRENDERER2_H QtAV-1.12.0/widgets/QtAVWidgets/GraphicsItemRenderer.h000066400000000000000000000126431312235004300224340ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QAV_GRAPHICSITEMRENDERER_H #define QAV_GRAPHICSITEMRENDERER_H #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif //QGraphicsWidget will lose focus forever if TextItem inserted text. Why? #define CONFIG_GRAPHICSWIDGET 0 #if CONFIG_GRAPHICSWIDGET #define GraphicsWidget QGraphicsWidget #else #define GraphicsWidget QGraphicsObject #endif namespace QtAV { class GraphicsItemRendererPrivate; class Q_AVWIDGETS_EXPORT GraphicsItemRenderer : public GraphicsWidget, public QPainterRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(GraphicsItemRenderer) Q_PROPERTY(bool opengl READ isOpenGL WRITE setOpenGL NOTIFY openGLChanged) Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: GraphicsItemRenderer(QGraphicsItem * parent = 0); VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; QRectF boundingRect() const Q_DECL_OVERRIDE; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE; QGraphicsItem* graphicsItem() Q_DECL_OVERRIDE { return this; } /*! * \brief isOpenGL * true: user set to enabling opengl renderering. if viewport is not GLWidget, nothing will be rendered * false: otherwise. opengl resources in QtAV (e.g. shader manager) will be released later */ bool isOpenGL() const; void setOpenGL(bool o); OpenGLVideo* opengl() const Q_DECL_OVERRIDE; Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; void openGLChanged(); protected: GraphicsItemRenderer(GraphicsItemRendererPrivate& d, QGraphicsItem *parent); bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; void drawBackground() Q_DECL_OVERRIDE; //draw the current frame using the current paint engine. called by paintEvent() void drawFrame() Q_DECL_OVERRIDE; #if CONFIG_GRAPHICSWIDGET bool event(QEvent *event) Q_DECL_OVERRIDE; #else //bool sceneEvent(QEvent *event) Q_DECL_OVERRIDE; #endif //CONFIG_GRAPHICSWIDGET private: void onSetOutAspectRatioMode(OutAspectRatioMode mode) Q_DECL_OVERRIDE; void onSetOutAspectRatio(qreal ratio) Q_DECL_OVERRIDE; bool onSetOrientation(int value) Q_DECL_OVERRIDE; bool onSetBrightness(qreal b) Q_DECL_OVERRIDE; bool onSetContrast(qreal c) Q_DECL_OVERRIDE; bool onSetHue(qreal h) Q_DECL_OVERRIDE; bool onSetSaturation(qreal s) Q_DECL_OVERRIDE; }; typedef GraphicsItemRenderer VideoRendererGraphicsItem; } #endif // QAV_GRAPHICSITEMRENDERER_H QtAV-1.12.0/widgets/QtAVWidgets/OpenGLWidgetRenderer.h000066400000000000000000000101561312235004300223420ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_OPENGLWIDGETRENDERER_H #define QTAV_OPENGLWIDGETRENDERER_H #include #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) #include #else #include #endif //QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) #include namespace QtAV { // do not define QOpenGLWidget here with ifdef to avoid moc error class OpenGLWidgetRendererPrivate; class Q_AVWIDGETS_EXPORT OpenGLWidgetRenderer : public QOpenGLWidget, public OpenGLRendererBase { Q_OBJECT DPTR_DECLARE_PRIVATE(OpenGLWidgetRenderer) Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged) Q_PROPERTY(qreal contrast READ contrast WRITE setContrast NOTIFY contrastChanged) Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged) Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY saturationChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: explicit OpenGLWidgetRenderer(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual VideoRendererId id() const Q_DECL_OVERRIDE; virtual QWidget* widget() Q_DECL_OVERRIDE { return this; } Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; protected: virtual void initializeGL() Q_DECL_OVERRIDE; virtual void paintGL() Q_DECL_OVERRIDE; virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; virtual void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef OpenGLWidgetRenderer VideoRendererOpenGLWidget; } //namespace QtAV #endif // QTAV_OPENGLWIDGETRENDERER_H QtAV-1.12.0/widgets/QtAVWidgets/QOpenGLWidget.h000066400000000000000000000046561312235004300210040ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_QOPENGLWIDGET_H #define QTAV_QOPENGLWIDGET_H #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #error "Qt5 is required!" #endif #include #include #include #include namespace QtAV { /*! * \brief The QOpenGLWidget class * A widget for rendering OpenGL graphics without QtOpenGL module */ class Q_AVWIDGETS_EXPORT QOpenGLWidget : public QWidget { Q_OBJECT Q_DISABLE_COPY(QOpenGLWidget) public: explicit QOpenGLWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~QOpenGLWidget(); void setFormat(const QSurfaceFormat &format); QSurfaceFormat format() const; bool isValid() const; void makeCurrent(); void doneCurrent(); QOpenGLContext *context() const; protected: QPaintDevice* redirected(QPoint *offset) const Q_DECL_OVERRIDE; virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; private: void initialize(); void render(); void invokeUserPaint(); bool m_initialized; bool m_fakeHidden; QOpenGLContext *m_context; QOpenGLPaintDevice *m_paintDevice; QSurfaceFormat m_requestedFormat; }; } //namespace QtAV #endif //QTAV_QOPENGLWIDGET_H QtAV-1.12.0/widgets/QtAVWidgets/QtAVWidgets000066400000000000000000000000311312235004300202660ustar00rootroot00000000000000#include "QtAVWidgets.h" QtAV-1.12.0/widgets/QtAVWidgets/QtAVWidgets.h000066400000000000000000000026111312235004300205220ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAVWIDGETS_H #define QTAVWIDGETS_H #include #include #include #include //#include #include #include #endif // QTAVWIDGETS_H QtAV-1.12.0/widgets/QtAVWidgets/VideoPreviewWidget.h000066400000000000000000000041641312235004300221410ustar00rootroot00000000000000/****************************************************************************** VideoRendererTypes: type id and manually id register function Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_VIDEOPREVIEWWIDGET_H #define QTAV_VIDEOPREVIEWWIDGET_H #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif namespace QtAV { class VideoFrame; class VideoOutput; class VideoFrameExtractor; class Q_AVWIDGETS_EXPORT VideoPreviewWidget : public QWidget { Q_OBJECT public: explicit VideoPreviewWidget(QWidget *parent = 0); void setTimestamp(qint64 msec); qint64 timestamp() const; void preview(); void setFile(const QString& file); QString file() const; // default is false void setKeepAspectRatio(bool value = true); bool isKeepAspectRatio() const; Q_SIGNALS: void timestampChanged(); void fileChanged(); protected: virtual void resizeEvent(QResizeEvent *); private Q_SLOTS: void displayFrame(const QtAV::VideoFrame& frame); //parameter VideoFrame void displayNoFrame(); private: bool m_keep_ar; QString m_file; VideoFrameExtractor *m_extractor; VideoOutput *m_out; }; } //namespace QtAV #endif // QTAV_VIDEOPREVIEWWIDGET_H QtAV-1.12.0/widgets/QtAVWidgets/WidgetRenderer.h000066400000000000000000000075221312235004300213000ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_WIDGETRENDERER_H #define QTAV_WIDGETRENDERER_H #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif namespace QtAV { class WidgetRendererPrivate; class Q_AVWIDGETS_EXPORT WidgetRenderer : public QWidget, public QPainterRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(WidgetRenderer) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QRectF regionOfInterest READ regionOfInterest WRITE setRegionOfInterest NOTIFY regionOfInterestChanged) Q_PROPERTY(qreal sourceAspectRatio READ sourceAspectRatio NOTIFY sourceAspectRatioChanged) Q_PROPERTY(qreal outAspectRatio READ outAspectRatio WRITE setOutAspectRatio NOTIFY outAspectRatioChanged) //fillMode // TODO: how to use enums in base class as property or Q_ENUM Q_PROPERTY(OutAspectRatioMode outAspectRatioMode READ outAspectRatioMode WRITE setOutAspectRatioMode NOTIFY outAspectRatioModeChanged) Q_ENUMS(OutAspectRatioMode) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QRect videoRect READ videoRect NOTIFY videoRectChanged) Q_PROPERTY(QSize videoFrameSize READ videoFrameSize NOTIFY videoFrameSizeChanged) Q_ENUMS(Quality) public: explicit WidgetRenderer(QWidget *parent = 0, Qt::WindowFlags f = 0); virtual VideoRendererId id() const Q_DECL_OVERRIDE; virtual QWidget* widget() Q_DECL_OVERRIDE { return this; } Q_SIGNALS: void sourceAspectRatioChanged(qreal value) Q_DECL_OVERRIDE Q_DECL_FINAL; void regionOfInterestChanged() Q_DECL_OVERRIDE; void outAspectRatioChanged() Q_DECL_OVERRIDE; void outAspectRatioModeChanged() Q_DECL_OVERRIDE; void brightnessChanged(qreal value) Q_DECL_OVERRIDE; void contrastChanged(qreal) Q_DECL_OVERRIDE; void hueChanged(qreal) Q_DECL_OVERRIDE; void saturationChanged(qreal) Q_DECL_OVERRIDE; void backgroundColorChanged() Q_DECL_OVERRIDE; void orientationChanged() Q_DECL_OVERRIDE; void videoRectChanged() Q_DECL_OVERRIDE; void videoFrameSizeChanged() Q_DECL_OVERRIDE; QTAVWIDGETS_DEPRECATED void imageReady(); // add frameReady() in the future? protected: bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; /*usually you don't need to reimplement paintEvent, just drawXXX() is ok. unless you want do all *things yourself totally*/ void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; bool onSetOrientation(int value) Q_DECL_OVERRIDE; protected: WidgetRenderer(WidgetRendererPrivate& d, QWidget *parent, Qt::WindowFlags f); }; typedef WidgetRenderer VideoRendererWidget; } //namespace QtAV #endif // QTAV_WIDGETRENDERER_H QtAV-1.12.0/widgets/QtAVWidgets/global.h000066400000000000000000000054321312235004300176240ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAVWIDGETS_GLOBAL_H #define QTAVWIDGETS_GLOBAL_H #include #ifdef BUILD_QTAVWIDGETS_STATIC #define Q_AVWIDGETS_EXPORT #else #if defined(BUILD_QTAVWIDGETS_LIB) # undef Q_AVWIDGETS_EXPORT # define Q_AVWIDGETS_EXPORT Q_DECL_EXPORT #else # undef Q_AVWIDGETS_EXPORT # define Q_AVWIDGETS_EXPORT Q_DECL_IMPORT //only for vc? #endif #endif //BUILD_QTAVWIDGETS_STATIC #define Q_AVWIDGETS_PRIVATE_EXPORT Q_AVWIDGETS_EXPORT #if defined(BUILD_QTAVWIDGETS_LIB) #define QTAVWIDGETS_DEPRECATED #else #define QTAVWIDGETS_DEPRECATED Q_DECL_DEPRECATED #endif namespace QtAV { namespace Widgets { /*! * \brief registerRenderers * register built-in renderers. * If you do not explicitly use any var, function or class in this module in your code, * QtAVWidgets module maybe not linked to your program and renderers will not be available. * Then you have to call registerRenderers() to ensure QtAVWidgets module is linked. */ Q_AVWIDGETS_EXPORT void registerRenderers(); } // namespace Widgets extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_Widget; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_GraphicsItem; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_GLWidget; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_GDI; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_Direct2D; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_XV; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_X11; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_GLWidget2; extern Q_AVWIDGETS_EXPORT VideoRendererId VideoRendererId_OpenGLWidget; //popup a dialog Q_AVWIDGETS_EXPORT void about(); Q_AVWIDGETS_EXPORT void aboutFFmpeg(); Q_AVWIDGETS_EXPORT void aboutQtAV(); } // namespace QtAV #endif //QTAVWIDGETS_GLOBAL_H QtAV-1.12.0/widgets/QtAVWidgets/version.h000066400000000000000000000035371312235004300200550ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAVWIDGETS_VERSION_H #define QTAVWIDGETS_VERSION_H #include #define QTAVWIDGETS_MAJOR QTAV_MAJOR //((QTAV_VERSION&0xff0000)>>16) #define QTAVWIDGETS_MINOR QTAV_MAJOR //((QTAV_VERSION&0xff00)>>8) #define QTAVWIDGETS_PATCH QTAV_MAJOR //(QTAV_VERSION&0xff) #define QTAVWIDGETS_VERSION_MAJOR(V) ((V & 0xff0000) >> 16) #define QTAVWIDGETS_VERSION_MINOR(V) ((V & 0xff00) >> 8) #define QTAVWIDGETS_VERSION_PATCH(V) (V & 0xff) #define QTAVWIDGETS_VERSION QTAV_VERSION_CHK(QTAVWIDGETS_MAJOR, QTAVWIDGETS_MINOR, QTAVWIDGETS_PATCH) /* the following are compile time version */ /* C++11 requires a space between literal and identifier */ #define QTAVWIDGETS_VERSION_STR TOSTR(QTAVWIDGETS_MAJOR) "." TOSTR(QTAVWIDGETS_MINOR) "." TOSTR(QTAVWIDGETS_PATCH) #endif // QTAVWIDGETS_VERSION_H QtAV-1.12.0/widgets/VideoPreviewWidget.cpp000066400000000000000000000065251312235004300203350ustar00rootroot00000000000000/****************************************************************************** VideoRendererTypes: type id and manually id register function Copyright (C) 2015 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/VideoPreviewWidget.h" #include "QtAV/VideoFrameExtractor.h" #include "QtAV/VideoOutput.h" #include namespace QtAV { VideoPreviewWidget::VideoPreviewWidget(QWidget *parent) : QWidget(parent) , m_keep_ar(false) , m_extractor(new VideoFrameExtractor(this)) , m_out(new VideoOutput(VideoRendererId_Widget, this)) // FIXME: opengl may crash, so use software renderer here { setWindowFlags(Qt::FramelessWindowHint); Q_ASSERT_X(m_out->widget(), "VideoPreviewWidget()", "widget based renderer is not found"); m_out->widget()->setParent(this); connect(m_extractor, SIGNAL(positionChanged()), this, SIGNAL(timestampChanged())); connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame))); connect(m_extractor, SIGNAL(error()), SLOT(displayNoFrame())); connect(this, SIGNAL(fileChanged()), SLOT(displayNoFrame())); m_extractor->setAutoExtract(false); } void VideoPreviewWidget::resizeEvent(QResizeEvent *e) { m_out->widget()->resize(e->size()); } void VideoPreviewWidget::setTimestamp(qint64 value) { m_extractor->setPosition(value); } qint64 VideoPreviewWidget::timestamp() const { return m_extractor->position(); } void VideoPreviewWidget::preview() { m_extractor->extract(); } void VideoPreviewWidget::setFile(const QString &value) { if (m_file == value) return; m_file = value; m_extractor->setSource(m_file); emit fileChanged(); } QString VideoPreviewWidget::file() const { return m_file; } void VideoPreviewWidget::displayFrame(const QtAV::VideoFrame &frame) { int diff = qAbs(qint64(frame.timestamp()*1000.0) - m_extractor->position()); if (diff > m_extractor->precision()) { //qWarning("timestamp difference (%d/%lld) is too large! ignore", diff); } if (m_out->isSupported(frame.format().pixelFormat())) { m_out->receive(frame); return; } QSize s = m_out->widget()->rect().size(); if (m_keep_ar) { QSize fs(frame.size()); fs.scale(s, Qt::KeepAspectRatio); s = fs; } VideoFrame f(frame.to(VideoFormat::Format_RGB32, s)); if (!f.isValid()) return; m_out->receive(f); } void VideoPreviewWidget::displayNoFrame() { m_out->receive(VideoFrame()); } } //namespace QtAV QtAV-1.12.0/widgets/WidgetRenderer.cpp000066400000000000000000000072061312235004300174700ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/WidgetRenderer.h" #include "QtAV/private/QPainterRenderer_p.h" #include #include #include #include #include "QtAV/Filter.h" namespace QtAV { class WidgetRendererPrivate : public QPainterRendererPrivate { public: virtual ~WidgetRendererPrivate(){} }; VideoRendererId WidgetRenderer::id() const { return VideoRendererId_Widget; } WidgetRenderer::WidgetRenderer(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f),QPainterRenderer(*new WidgetRendererPrivate()) { DPTR_D(WidgetRenderer); d.painter = new QPainter(); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); setAutoFillBackground(false); QPainterFilterContext *ctx = static_cast(d.filter_context); if (ctx) { ctx->painter = d.painter; } else { qWarning("FilterContext not available!"); } } WidgetRenderer::WidgetRenderer(WidgetRendererPrivate &d, QWidget *parent, Qt::WindowFlags f) :QWidget(parent, f),QPainterRenderer(d) { d.painter = new QPainter(); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setAutoFillBackground(false); QPainterFilterContext *ctx = static_cast(d.filter_context); if (ctx) { ctx->painter = d.painter; } else { qWarning("FilterContext not available!"); } } bool WidgetRenderer::receiveFrame(const VideoFrame &frame) { preparePixmap(frame); updateUi(); /* * workaround for the widget not updated if has parent. don't know why it works and why update() can't * Thanks to Vito Covito and Carlo Scarpato * Now it's fixed by posting a QUpdateLaterEvent */ Q_EMIT imageReady(); return true; } void WidgetRenderer::resizeEvent(QResizeEvent *e) { DPTR_D(WidgetRenderer); d.update_background = true; resizeRenderer(e->size()); update(); } void WidgetRenderer::paintEvent(QPaintEvent *) { DPTR_D(WidgetRenderer); d.painter->begin(this); //Widget painting can only begin as a result of a paintEvent handlePaintEvent(); if (d.painter->isActive()) d.painter->end(); } bool WidgetRenderer::onSetOrientation(int value) { Q_UNUSED(value); update(); return true; } } //namespace QtAV QtAV-1.12.0/widgets/X11Renderer.cpp000066400000000000000000000500311312235004300166100ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ //TODO: ROI (xsubimage?), rotation. slow scale because of alignment? no xsync for shm /* * X11 headers define 'Bool' type which is used in qmetatype.h. we must include X11 files at last, i.e. X11Renderer_p.h. otherwise compile error */ #include "QtAV/VideoRenderer.h" #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/FilterContext.h" #include #include #include #include //#error qtextstream.h must be included before any header file that defines Status. Xlib.h defines Status #include //build error #include #include #include #include #include //usleep //#include "QtAV/private/factory.h" //scale: http://www.opensource.apple.com/source/X11apps/X11apps-14/xmag/xmag-X11R7.0-1.0.1/Scale.c #define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1)) static const int kPoolSize = 2; namespace QtAV { class X11RendererPrivate; class X11Renderer: public QWidget, public VideoRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(X11Renderer) public: X11Renderer(QWidget* parent = 0, Qt::WindowFlags f = 0); VideoRendererId id() const Q_DECL_OVERRIDE; bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; /* WA_PaintOnScreen: To render outside of Qt's paint system, e.g. If you require * native painting primitives, you need to reimplement QWidget::paintEngine() to * return 0 and set this flag * If paintEngine != 0, the window will flicker when drawing without QPainter. * Make sure that paintEngine returns 0 to avoid flicking. */ QPaintEngine* paintEngine() const Q_DECL_OVERRIDE; /*http://lists.trolltech.com/qt4-preview-feedback/2005-04/thread00609-0.html * true: paintEngine is QPainter. Painting with QPainter support double buffer * false: no double buffer, should reimplement paintEngine() to return 0 to avoid flicker */ QWidget* widget() Q_DECL_OVERRIDE { return this; } protected: bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; //called in paintEvent before drawFrame() when required void drawBackground() Q_DECL_OVERRIDE; //draw the current frame using the current paint engine. called by paintEvent() void drawFrame() Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; //stay on top will change parent, hide then show(windows) void showEvent(QShowEvent *) Q_DECL_OVERRIDE; }; typedef X11Renderer VideoRendererX11; extern VideoRendererId VideoRendererId_X11; #if 0 FACTORY_REGISTER_ID_AUTO(VideoRenderer, X11, "X11") #else void RegisterVideoRendererX11_Man() { VideoRenderer::Register(VideoRendererId_X11, "X11"); } #endif VideoRendererId X11Renderer::id() const { return VideoRendererId_X11; } #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN #define BO_NATIVE LSBFirst #define BO_NONNATIVE MSBFirst #else #define BO_NATIVE MSBFirst #define BO_NONNATIVE LSBFirst #endif static const struct fmt2Xfmtentry { VideoFormat::PixelFormat fmt; int byte_order; unsigned red_mask; unsigned green_mask; unsigned blue_mask; } fmt2Xfmt[] = { {VideoFormat::Format_BGR555, BO_NATIVE, 0x0000001F, 0x000003E0, 0x00007C00}, {VideoFormat::Format_BGR555, BO_NATIVE, 0x00007C00, 0x000003E0, 0x0000001F}, {VideoFormat::Format_BGR565, BO_NATIVE, 0x0000001F, 0x000007E0, 0x0000F800}, {VideoFormat::Format_RGB565, BO_NATIVE, 0x0000F800, 0x000007E0, 0x0000001F}, {VideoFormat::Format_RGB24, MSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF}, {VideoFormat::Format_RGB24, LSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000}, {VideoFormat::Format_BGR24, MSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000}, {VideoFormat::Format_BGR24, LSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF}, {VideoFormat::Format_BGR32, BO_NATIVE, 0x000000FF, 0x0000FF00, 0x00FF0000}, {VideoFormat::Format_BGR32, BO_NONNATIVE, 0xFF000000, 0x00FF0000, 0x0000FF00}, //abgr rgba {VideoFormat::Format_RGB32, BO_NATIVE, 0x00FF0000, 0x0000FF00, 0x000000FF}, //argb bgra {VideoFormat::Format_RGB32, BO_NONNATIVE, 0x0000FF00, 0x00FF0000, 0xFF000000}, {VideoFormat::Format_ARGB32, MSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF}, {VideoFormat::Format_ARGB32, LSBFirst, 0x0000FF00, 0x00FF0000, 0xFF000000}, {VideoFormat::Format_ABGR32, MSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000}, {VideoFormat::Format_ABGR32, LSBFirst, 0xFF000000, 0x00FF0000, 0x0000FF00}, {VideoFormat::Format_RGBA32, MSBFirst, 0xFF000000, 0x00FF0000, 0x0000FF00}, {VideoFormat::Format_RGBA32, LSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000}, {VideoFormat::Format_BGRA32, MSBFirst, 0x0000FF00, 0x00FF0000, 0xFF000000}, {VideoFormat::Format_BGRA32, LSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF}, {VideoFormat::Format_Invalid, BO_NATIVE, 0, 0, 0} }; VideoFormat::PixelFormat pixelFormat(XImage* xi) { const struct fmt2Xfmtentry *fmte = fmt2Xfmt; while (fmte->fmt != VideoFormat::Format_Invalid) { int depth = VideoFormat(fmte->fmt).bitsPerPixel(); // 15->16? mpv if (depth == xi->bits_per_pixel && fmte->byte_order == xi->byte_order && fmte->red_mask == xi->red_mask && fmte->green_mask == xi->green_mask && fmte->blue_mask == xi->blue_mask) break; //qDebug() << fmte->fmt; fmte++; } return fmte->fmt; } class X11RendererPrivate : public VideoRendererPrivate { public: DPTR_DECLARE_PUBLIC(X11Renderer) X11RendererPrivate(): use_shm(true) , warn_bad_pitch(true) , num_adaptors(0) , ShmCompletionEvent(0) , ShmCompletionWaitCount(0) , current_index(0) , next_index(0) , gc(NULL) , pixfmt(VideoFormat::Format_Invalid) , frame_changed(false) { XInitThreads(); memset(ximage_pool, 0, sizeof(ximage_pool)); #ifndef _XSHM_H_ use_shm = false; #endif //_XSHM_H_ //XvQueryExtension() char* dispName = XDisplayName(NULL); qDebug("X11 open display: %s", dispName); display = XOpenDisplay(dispName); if (!display) { available = false; qWarning("Open X11 display error"); return; } XWindowAttributes attribs; XGetWindowAttributes(display, DefaultRootWindow(display), &attribs); depth = attribs.depth; if (!XMatchVisualInfo(display, DefaultScreen(display), depth, TrueColor, &vinfo)) { qWarning("XMatchVisualInfo error"); available = false; return; } XImage *ximg = NULL; if (depth != 15 && depth != 16 && depth != 24 && depth != 32) { //?? Visual *vs; //depth = //find_depth_from_visuals } else { ximg = XGetImage(display, DefaultRootWindow(display), 0, 0, 1, 1, AllPlanes, ZPixmap); } int ximage_depth = depth; unsigned int mask = 0; if (ximg) { bpp = ximg->bits_per_pixel; if ((ximage_depth+7)/8 != (bpp+7)/8) //24bpp use 32 depth ximage_depth = bpp; mask = ximg->red_mask | ximg->green_mask | ximg->blue_mask; qDebug("color mask: %X R:%1X G:%1X B:%1X", mask, ximg->red_mask, ximg->green_mask, ximg->blue_mask); XDestroyImage(ximg); } if (((ximage_depth + 7) / 8) == 2) { if (mask == 0x7FFF) ximage_depth = 15; else if (mask == 0xFFFF) ximage_depth = 16; } } ~X11RendererPrivate() { for (int i = 0; i < kPoolSize; ++i) destroyX11Image(i); XCloseDisplay(display); } void destroyX11Image(int index) { if (use_shm) { XShmSegmentInfo &shm = shm_pool[index]; if (shm.shmaddr) { XShmDetach(display, &shm); shmctl(shm.shmid, IPC_RMID, 0); shmdt(shm.shmaddr); } } XImage* ximage = ximage_pool[index]; if (ximage) { if (!ximage_data[index].isEmpty()) ximage->data = NULL; // we point it to our own data if shm is not used XDestroyImage(ximage); } ximage_pool[index] = NULL; ximage_data[index].clear(); } bool prepareDeviceResource() { if (gc) { XFreeGC(display, gc); gc = 0; } gc = XCreateGC(display, q_func().winId(), 0, 0); //DefaultRootWindow if (!gc) { available = false; qCritical("Create GC failed!"); return false; } XSetBackground(display, gc, BlackPixel(display, DefaultScreen(display))); if (filter_context) ((X11FilterContext*)filter_context)->resetX11((X11FilterContext::Display*)display, (X11FilterContext::GC)gc, (X11FilterContext::Drawable)q_func().winId()); return true; } bool ensureImage(int w, int h) { for (int i = 0; i < kPoolSize; ++i) { if (!ensureImage(i, w, h)) return false; } return true; } bool ensureImage(int index, int w, int h) { XImage* &ximage = ximage_pool[index]; if (ximage && ximage->width == w && ximage->height == h) return true; warn_bad_pitch = true; destroyX11Image(index); use_shm = XShmQueryExtension(display); XShmSegmentInfo &shm = shm_pool[index]; qDebug("use x11 shm: %d", use_shm); if (!use_shm) goto no_shm; ShmCompletionEvent = XShmGetEventBase(display) + ShmCompletion; // data seems not aligned ximage = XShmCreateImage(display, vinfo.visual, depth, ZPixmap, NULL, &shm, w, h); if (!ximage) { qWarning("XShmCreateImage error"); goto no_shm; } shm.shmid = shmget(IPC_PRIVATE, ximage->bytes_per_line*ximage->height, IPC_CREAT | 0777); if (shm.shmid < 0) { qWarning("shmget error"); goto no_shm; } shm.shmaddr = (char *)shmat(shm.shmid, 0, 0); if (shm.shmaddr == (char*)-1) { if (!ximage_data[index].isEmpty()) ximage->data = NULL; XDestroyImage(ximage); ximage = NULL; ximage_data[index].clear(); qWarning("Shared memory error,disabling ( seg id error )"); goto no_shm; } ximage->data = shm.shmaddr; shm.readOnly = False; if (!XShmAttach(display, &shm)) { qWarning("Attach to shm failed! try to use none shm"); goto no_shm; } XSync(display, False); shmctl(shm.shmid, IPC_RMID, 0); pixfmt = pixelFormat(ximage); return true; no_shm: ShmCompletionEvent = 0; ximage = XCreateImage(display, vinfo.visual, depth, ZPixmap, 0, NULL, w, h, 8, 0); if (!ximage) return false; pixfmt = pixelFormat(ximage); ximage->data = NULL; XSync(display, False); // TODO: align 16 or? ximage_data[index].resize(ximage->bytes_per_line*ximage->height + 32); return true; } int resizeXImage(int index); bool use_shm; //TODO: set by user bool warn_bad_pitch; unsigned int num_adaptors; int bpp; int depth; int ShmCompletionEvent; int ShmCompletionWaitCount; XVisualInfo vinfo; Display *display; int current_index; int next_index; XImage *ximage_pool[kPoolSize]; GC gc; XShmSegmentInfo shm_pool[kPoolSize]; VideoFormat::PixelFormat pixfmt; // if the incoming image pitchs are different from ximage ones, use ximage pitchs and copy data in ximage_data QByteArray ximage_data[kPoolSize]; VideoFrame frame_orig; // if renderer is resized, scale the original frame bool frame_changed; }; X11Renderer::X11Renderer(QWidget *parent, Qt::WindowFlags f): QWidget(parent, f) , VideoRenderer(*new X11RendererPrivate()) { DPTR_INIT_PRIVATE(X11Renderer); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); //setAttribute(Qt::WA_NoSystemBackground); //setAutoFillBackground(false); setAttribute(Qt::WA_PaintOnScreen, true); d_func().filter_context = VideoFilterContext::create(VideoFilterContext::X11); if (!d_func().filter_context) { qWarning("No filter context for X11"); } else { d_func().filter_context->paint_device = this; } } bool X11Renderer::isSupported(VideoFormat::PixelFormat pixfmt) const { Q_UNUSED(pixfmt); // if always return true, then convert to x11 format and scale in receiveFrame() only once. no need to convert format first then scale return true;//pixfmt == d_func().pixfmt && d_func().pixfmt != VideoFormat::Format_Invalid; } bool X11Renderer::receiveFrame(const VideoFrame& frame) { DPTR_D(X11Renderer); d.frame_changed = true; if (!frame.isValid()) { d.video_frame = VideoFrame(); // fill background update(); return true; } d.frame_orig = frame; d.video_frame = frame; // must be set because it will be check isValid() somewhere else updateUi(); return true; } int X11RendererPrivate::resizeXImage(int index) { if (!frame_orig.isValid()) return false; if (!frame_changed) return -1; // force align to 8(16?) because xcreateimage will not do alignment // GAL vmem is 16 aligned if (!ensureImage(index, FFALIGN(out_rect.width(), 16), FFALIGN(out_rect.height(), 16))) // we can also call it in onResizeRenderer, onSetOutAspectXXX return false; frame_changed = false; XImage* &ximage = ximage_pool[index]; video_frame = frame_orig; // set before map! VideoFrame interopFrame; if (!frame_orig.constBits(0)) { interopFrame = VideoFrame(ximage->width, ximage->height, pixelFormat(ximage)); interopFrame.setBits(use_shm ? (quint8*)ximage->data : (quint8*)ximage_data[index].constData()); interopFrame.setBytesPerLine(ximage->bytes_per_line); } if (frame_orig.constBits(0) || !video_frame.map(UserSurface, &interopFrame, VideoFormat(VideoFormat::Format_RGB32)) //check pixel format and scale to ximage size&line_size ) { if (!frame_orig.constBits(0) //always convert hw frames || frame_orig.pixelFormat() != pixfmt || frame_orig.width() != ximage->width || frame_orig.height() != ximage->height) video_frame = frame_orig.to(pixfmt, QSize(ximage->width, ximage->height)); else video_frame = frame_orig; if (video_frame.bytesPerLine(0) == ximage->bytes_per_line) { if (use_shm) { memcpy(ximage->data, video_frame.constBits(0), ximage->bytes_per_line*ximage->height); } else { ximage->data = (char*)video_frame.constBits(0); } } else { //copy line by line if (warn_bad_pitch) { warn_bad_pitch = false; qDebug("bad pitch: %d - % ximage_data[%d].size: %d", ximage->bytes_per_line, video_frame.bytesPerLine(0), index, ximage_data[index].size()); } quint8* dst = (quint8*)ximage->data; if (!use_shm) { dst = (quint8*)ximage_data[index].constData(); ximage->data = (char*)ximage_data[index].constData(); } VideoFrame::copyPlane(dst, ximage->bytes_per_line, (const quint8*)video_frame.constBits(0), video_frame.bytesPerLine(0), ximage->bytes_per_line, ximage->height); } } else { if (!use_shm) ximage->data = (char*)ximage_data[index].constData(); } return true; } QPaintEngine* X11Renderer::paintEngine() const { return 0; //use native engine } void X11Renderer::drawBackground() { const QRegion bgRegion(backgroundRegion()); if (bgRegion.isEmpty()) return; DPTR_D(X11Renderer); // TODO: fill once each resize? mpv // TODO: set color //XSetBackground(d.display, d.gc, BlackPixel(d.display, DefaultScreen(d.display))); const QVector bg(bgRegion.rects()); foreach (const QRect& r, bg) { XFillRectangle(d.display, winId(), d.gc, r.x(), r.y(), r.width(), r.height()); } XFlush(d.display); // apply the color } void X11Renderer::drawFrame() { // TODO: interop DPTR_D(X11Renderer); int ret = d.resizeXImage(d.next_index); // -1: image no change if (!ret) return; if (preferredPixelFormat() != d.pixfmt) { qDebug() << "x11 preferred pixel format: " << d.pixfmt; setPreferredPixelFormat(d.pixfmt); } if (d.use_shm) { int wait_count = 0; while (d.ShmCompletionWaitCount >= kPoolSize) { if (wait_count++ > 100) { qDebug("reset ShmCompletionWaitCount"); d.ShmCompletionWaitCount = 0; break; } while (XPending(d.display)) { XEvent ev; XNextEvent(d.display, &ev); if (ev.type == d.ShmCompletionEvent) { if (d.ShmCompletionWaitCount > 0) d.ShmCompletionWaitCount--; } } usleep(1000); } } QRect roi = realROI(); int idx = d.current_index; // ret<0, frame/vo no change. if host frame, no filters; >0: filters if (ret > 0) { // next ximage is ready idx = d.next_index; d.next_index = (d.next_index+1)%kPoolSize; } XImage* ximage = d.ximage_pool[idx]; if (d.use_shm) { XShmPutImage(d.display, winId(), d.gc, ximage , roi.x(), roi.y()//, roi.width(), roi.height() , d.out_rect.x(), d.out_rect.y(), d.out_rect.width(), d.out_rect.height() , True /*true: send event*/); d.ShmCompletionWaitCount++; } else { XPutImage(d.display, winId(), d.gc, ximage , roi.x(), roi.y()//, roi.width(), roi.height() , d.out_rect.x(), d.out_rect.y(), d.out_rect.width(), d.out_rect.height()); XSync(d.display, False); // update immediately } } void X11Renderer::paintEvent(QPaintEvent *) { handlePaintEvent(); } void X11Renderer::resizeEvent(QResizeEvent *e) { DPTR_D(X11Renderer); d.frame_changed = true; resizeRenderer(e->size()); update(); //update background } void X11Renderer::showEvent(QShowEvent *event) { Q_UNUSED(event); DPTR_D(X11Renderer); /* * Do something that depends on widget below! e.g. recreate render target for direct2d. * When Qt::WindowStaysOnTopHint changed, window will hide first then show. If you * don't do anything here, the widget content will never be updated. */ d.prepareDeviceResource(); } } //namespace QtAV #include "X11Renderer.moc" QtAV-1.12.0/widgets/XVRenderer.cpp000066400000000000000000000472571312235004300166140ustar00rootroot00000000000000/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ /* * X11 headers define 'Bool' type which is used in qmetatype.h. we must include X11 files at last, i.e. XVRenderer_p.h. otherwise compile error */ #include "QtAV/VideoRenderer.h" #include "QtAV/private/VideoRenderer_p.h" #include "QtAV/FilterContext.h" #include #include #include //#error qtextstream.h must be included before any header file that defines Status. Xlib.h defines Status #include //build error #include #include #include #include "QtAV/private/factory.h" //http://huangbster.i.sohu.com/blog/view/256490057.htm namespace QtAV { inline int scaleEQValue(int val, int min, int max) { // max-min? return (val + 100)*((qAbs(min) + qAbs(max)))/200 - qAbs(min); } class XVRendererPrivate; class XVRenderer: public QWidget, public VideoRenderer { Q_OBJECT DPTR_DECLARE_PRIVATE(XVRenderer) public: XVRenderer(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual VideoRendererId id() const Q_DECL_OVERRIDE; virtual bool isSupported(VideoFormat::PixelFormat pixfmt) const Q_DECL_OVERRIDE; /* WA_PaintOnScreen: To render outside of Qt's paint system, e.g. If you require * native painting primitives, you need to reimplement QWidget::paintEngine() to * return 0 and set this flag * If paintEngine != 0, the window will flicker when drawing without QPainter. * Make sure that paintEngine returns 0 to avoid flicking. */ virtual QPaintEngine* paintEngine() const Q_DECL_OVERRIDE; /*http://lists.trolltech.com/qt4-preview-feedback/2005-04/thread00609-0.html * true: paintEngine is QPainter. Painting with QPainter support double buffer * false: no double buffer, should reimplement paintEngine() to return 0 to avoid flicker */ virtual QWidget* widget() Q_DECL_OVERRIDE { return this; } protected: virtual bool receiveFrame(const VideoFrame& frame) Q_DECL_OVERRIDE; virtual void drawBackground() Q_DECL_OVERRIDE; virtual void drawFrame() Q_DECL_OVERRIDE; virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; //stay on top will change parent, hide then show(windows) virtual void showEvent(QShowEvent *) Q_DECL_OVERRIDE; private: virtual bool onSetBrightness(qreal b) Q_DECL_OVERRIDE; virtual bool onSetContrast(qreal c) Q_DECL_OVERRIDE; virtual bool onSetHue(qreal h) Q_DECL_OVERRIDE; virtual bool onSetSaturation(qreal s) Q_DECL_OVERRIDE; }; typedef XVRenderer VideoRendererXV; extern VideoRendererId VideoRendererId_XV; #if 0 FACTORY_REGISTER_ID_AUTO(VideoRenderer, XV, "XVideo") #else void RegisterVideoRendererXV_Man() { VideoRenderer::Register(VideoRendererId_XV, "XVideo"); } #endif VideoRendererId XVRenderer::id() const { return VideoRendererId_XV; } #define FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((unsigned)(d)<<24)) static const struct xv_format_entry_t { VideoFormat::PixelFormat format; int fourcc; } xv_fmt[] = { { VideoFormat::Format_YUV420P, FOURCC('Y', 'V', '1', '2')}, { VideoFormat::Format_YUV420P, FOURCC('I', '4', '2', '0')}, { VideoFormat::Format_UYVY, FOURCC('U', 'Y', 'V', 'Y')}, { VideoFormat::Format_YUYV, FOURCC('Y', 'U', 'Y', '2')}, // split nv12 in xv to reduce copy times(no need to convert in sws) { VideoFormat::Format_NV12, FOURCC('Y', 'V', '1', '2')}, { VideoFormat::Format_NV21, FOURCC('Y', 'V', '1', '2')}, }; #undef FOURCC int pixelFormatToXv(VideoFormat::PixelFormat fmt) { for (size_t i = 0; i < sizeof(xv_fmt)/sizeof(xv_fmt[0]); ++i) { if (xv_fmt[i].format == fmt) return xv_fmt[i].fourcc; } return 0; } int xvFormatInPort(Display* disp, XvPortID port, VideoFormat::PixelFormat fmt) { const int xv_id = pixelFormatToXv(fmt); if (!xv_id) return 0; const int xv_type = VideoFormat::isRGB(fmt) ? XvRGB : XvYUV; const int xv_plane = VideoFormat::isPlanar(fmt) ? XvPlanar : XvPacked; int count = 0; XvImageFormatValues *xvifmts = XvListImageFormats(disp, port, &count); for (const XvImageFormatValues *xvifmt = xvifmts; xvifmt < xvifmts+count; ++xvifmt) { qDebug("XvImageFormatValues: %s", xvifmt->guid); if (xvifmt->type == xv_type && xvifmt->format == xv_plane && xvifmt->id == xv_id ) { if (XvGrabPort(disp, port, 0) == Success) { XFree(xvifmts); return xv_id; } } } XFree(xvifmts); return 0; } class XVRendererPrivate : public VideoRendererPrivate { public: DPTR_DECLARE_PUBLIC(XVRenderer) XVRendererPrivate(): use_shm(true) , num_adaptors(0) , xv_image(0) , format_id(0x32315659) /*YV12*/ , xv_image_width(0) , xv_image_height(0) , xv_port(0) , gc(NULL) , format(VideoFormat::Format_Invalid) { XInitThreads(); #ifndef _XSHM_H_ use_shm = false; #endif //_XSHM_H_ //XvQueryExtension() display = XOpenDisplay(NULL); if (XvQueryAdaptors(display, DefaultRootWindow(display), &num_adaptors, &xv_adaptor_info) != Success) { available = false; qCritical("Query adaptors failed!"); return; } if (num_adaptors < 1) { available = false; qCritical("No adaptor found!"); return; } } ~XVRendererPrivate() { if (xv_adaptor_info) { XvFreeAdaptorInfo(xv_adaptor_info); xv_adaptor_info = 0; } destroyXVImage(); if (gc) { XFreeGC(display, gc); gc = 0; } if (xv_port) { XvUngrabPort(display, xv_port, 0); xv_port = 0; } XCloseDisplay(display); } void destroyXVImage() { if (!xv_image) return; #ifdef _XSHM_H_ if (use_shm) { if (shm.shmaddr) { XShmDetach(display, &shm); shmctl(shm.shmid, IPC_RMID, 0); shmdt(shm.shmaddr); } } else #endif //_XSHM_H_ { // free if use copy (e.g. shm) free(xv_image->data); } XFree(xv_image); xv_image_width = 0; xv_image_height = 0; } bool prepareDeviceResource() { if (gc) { XFreeGC(display, gc); gc = 0; } gc = XCreateGC(display, q_func().winId(), 0, 0); if (!gc) { available = false; qCritical("Create GC failed!"); return false; } XSetBackground(display, gc, BlackPixel(display, DefaultScreen(display))); if (filter_context) ((X11FilterContext*)filter_context)->resetX11((X11FilterContext::Display*)display, (X11FilterContext::GC)gc, (X11FilterContext::Drawable)q_func().winId()); return true; } bool ensureImage(int w, int h, VideoFormat::PixelFormat pixfmt) { if (xv_image_width == w && xv_image_height == h && xv_image && format == pixfmt) return true; destroyXVImage(); qDebug("port count: %d", num_adaptors); for (uint i = 0; i < num_adaptors; ++i) { //?? if ((xv_adaptor_info[i].type & (XvInputMask | XvImageMask)) == (XvInputMask | XvImageMask)) { for (XvPortID p = xv_adaptor_info[i].base_id; p < xv_adaptor_info[i].base_id + xv_adaptor_info[i].num_ports; ++p) { qDebug("XvAdaptorInfo: %s", xv_adaptor_info[i].name); format_id = xvFormatInPort(display, p, pixfmt); if (format_id) { xv_port = p; break; } } } if (xv_port) break; } if (!xv_port) { qWarning("xv port not found!"); } format = pixfmt; xv_image_width = w; xv_image_height = h; #ifdef _XSHM_H_ use_shm = XShmQueryExtension(display); qDebug("use xv shm: %d", use_shm); if (!use_shm) goto no_shm; xv_image = XvShmCreateImage(display, xv_port, format_id, 0, xv_image_width, xv_image_height, &shm); if (!xv_image) goto no_shm; shm.shmid = shmget(IPC_PRIVATE, xv_image->data_size, IPC_CREAT | 0777); qDebug("shmid: %d xv_image->data_size: %d, %dx%d", shm.shmid, xv_image->data_size, xv_image_width, xv_image_height); if (shm.shmid < 0) { qWarning("get shm failed. try to use none shm"); goto no_shm; } shm.shmaddr = (char *)shmat(shm.shmid, 0, 0); if (shm.shmaddr == (char*)-1) { XFree(xv_image); qWarning("Shared memory error,disabling ( seg id error )"); goto no_shm; } xv_image->data = shm.shmaddr; shm.readOnly = False; if (!XShmAttach(display, &shm)) { qWarning("Attach to shm failed! try to use none shm"); goto no_shm; } XSync(display, False); shmctl(shm.shmid, IPC_RMID, 0); return true; #endif //_XSHM_H_ no_shm: xv_image = XvCreateImage(display, xv_port, format_id, 0, xv_image_width, xv_image_height); if (!xv_image) return false; // malloc if use copy (e.g. shm) xv_image->data = (char*)malloc(xv_image->data_size); if (!xv_image->data) return false; XSync(display, False); return true; } bool XvSetPortAttributeIfExists(const char *key, int value); bool use_shm; //TODO: set by user unsigned int num_adaptors; XvAdaptorInfo *xv_adaptor_info; Display *display; XvImage *xv_image; int format_id; int xv_image_width, xv_image_height; XvPortID xv_port; GC gc; #ifdef _XSHM_H_ XShmSegmentInfo shm; #endif //_XSHM_H_ VideoFormat::PixelFormat format; }; bool XVRendererPrivate::XvSetPortAttributeIfExists(const char *key, int value) { int nb_attributes; XvAttribute *attributes = XvQueryPortAttributes(display, xv_port, &nb_attributes); if (!attributes) { qWarning("XvQueryPortAttributes error"); return false; } for (int i = 0; i < nb_attributes; ++i) { const XvAttribute &attribute = ((XvAttribute*)attributes)[i]; if (!qstrcmp(attribute.name, key) && (attribute.flags & XvSettable)) { XvSetPortAttribute(display, xv_port, XInternAtom(display, key, false), scaleEQValue(value, attribute.min_value, attribute.max_value)); return true; } } qWarning("Can not set Xv attribute at key '%s'", key); return false; } XVRenderer::XVRenderer(QWidget *parent, Qt::WindowFlags f): QWidget(parent, f) , VideoRenderer(*new XVRendererPrivate()) { setPreferredPixelFormat(VideoFormat::Format_YUV420P); DPTR_INIT_PRIVATE(XVRenderer); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); /* To rapidly update custom widgets that constantly paint over their entire areas with * opaque content, e.g., video streaming widgets, it is better to set the widget's * Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead associated with repainting the * widget's background */ setAttribute(Qt::WA_OpaquePaintEvent); //setAttribute(Qt::WA_NoSystemBackground); //setAutoFillBackground(false); setAttribute(Qt::WA_PaintOnScreen, true); d_func().filter_context = VideoFilterContext::create(VideoFilterContext::X11); if (!d_func().filter_context) { qWarning("No filter context for X11"); } else { d_func().filter_context->paint_device = this; } } bool XVRenderer::isSupported(VideoFormat::PixelFormat pixfmt) const { // TODO: rgb use copyplane return pixfmt == VideoFormat::Format_YUV420P || pixfmt == VideoFormat::Format_YV12 || pixfmt == VideoFormat::Format_NV12|| pixfmt == VideoFormat::Format_NV21 || pixfmt == VideoFormat::Format_UYVY || pixfmt == VideoFormat::Format_YUYV ; } static void SplitPlanes(quint8 *dstu, size_t dstu_pitch, quint8 *dstv, size_t dstv_pitch, const quint8 *src, size_t src_pitch, unsigned width, unsigned height) { for (unsigned y = 0; y < height; y++) { for (unsigned x = 0; x < width; x++) { dstu[x] = src[2*x+0]; dstv[x] = src[2*x+1]; } src += src_pitch; dstu += dstu_pitch; dstv += dstv_pitch; } } void CopyFromNv12(quint8 *dst[], size_t dst_pitch[], const quint8 *src[2], size_t src_pitch[2], unsigned width, unsigned height) { VideoFrame::copyPlane(dst[0], dst_pitch[0], src[0], src_pitch[0], width, height); SplitPlanes(dst[2], dst_pitch[2], dst[1], dst_pitch[1], src[1], src_pitch[1], width/2, height/2); } void CopyFromYv12(quint8 *dst[], size_t dst_pitch[], const quint8 *src[3], size_t src_pitch[3], unsigned width, unsigned height) { VideoFrame::copyPlane(dst[0], dst_pitch[0], src[0], src_pitch[0], width, height); VideoFrame::copyPlane(dst[1], dst_pitch[1], src[1], src_pitch[1], width/2, height/2); VideoFrame::copyPlane(dst[2], dst_pitch[2], src[2], src_pitch[2], width/2, height/2); } void CopyFromYv12_2(quint8 *dst[], size_t dst_pitch[], const quint8 *src[3], size_t src_pitch[3], unsigned width, unsigned height) { VideoFrame::copyPlane(dst[0], dst_pitch[0], src[0], src_pitch[0], width, height); width /= 2; height /= 2; if (width == dst_pitch[1] && dst_pitch[1] == src_pitch[1]) { VideoFrame::copyPlane(dst[1], dst_pitch[1], src[1], src_pitch[1], width, height); VideoFrame::copyPlane(dst[2], dst_pitch[2], src[2], src_pitch[2], width, height); } else { for (unsigned i = 0; i < height; ++i) { memcpy(dst[2], src[2], width); memcpy(dst[1], src[1], width); src[1] += src_pitch[1]; src[2] += src_pitch[2]; dst[1] += dst_pitch[1]; dst[2] += dst_pitch[2]; } } } bool XVRenderer::receiveFrame(const VideoFrame& frame) { DPTR_D(XVRenderer); if (!frame.isValid()) { d.update_background = true; d.video_frame = VideoFrame(); // fill background updateUi(); return true; } if (!d.ensureImage(frame.width(), frame.height(), frame.format().pixelFormat())) return false; if (frame.constBits(0)) d.video_frame = frame; else // FIXME: not work d.video_frame = frame.to(frame.pixelFormat()); // assume frame format is supported int nb_planes = d.video_frame.planeCount(); QVector src_linesize(nb_planes); QVector src(nb_planes); for (int i = 0; i < nb_planes; ++i) { src[i] = d.video_frame.constBits(i); src_linesize[i] = d.video_frame.bytesPerLine(i); } //swap UV quint8* dst[] = { (quint8*)(d.xv_image->data + d.xv_image->offsets[0]), (quint8*)(d.xv_image->data + d.xv_image->offsets[2]), (quint8*)(d.xv_image->data + d.xv_image->offsets[1]) }; size_t dst_linesize[] = { (size_t)d.xv_image->pitches[0], (size_t)d.xv_image->pitches[2], (size_t)d.xv_image->pitches[1] }; // TODO: if not using shm and linesizes match, no copy is required. but seems no benefit switch (d.video_frame.pixelFormat()) { case VideoFormat::Format_YUV420P: case VideoFormat::Format_YV12: CopyFromYv12_2(dst, dst_linesize, src.data(), src_linesize.data(), dst_linesize[0], d.xv_image->height); break; case VideoFormat::Format_NV12: std::swap(dst[1], dst[2]); std::swap(dst_linesize[1], dst_linesize[2]); CopyFromNv12(dst, dst_linesize, src.data(), src_linesize.data(), dst_linesize[0], d.xv_image->height); break; case VideoFormat::Format_NV21: CopyFromNv12(dst, dst_linesize, src.data(), src_linesize.data(), dst_linesize[0], d.xv_image->height); break; case VideoFormat::Format_UYVY: case VideoFormat::Format_YUYV: VideoFrame::copyPlane(dst[0], dst_linesize[0], src[0], src_linesize[0], dst_linesize[0], d.xv_image->height); break; case VideoFormat::Format_BGR24: break; default: break; } update(); return true; } QPaintEngine* XVRenderer::paintEngine() const { return 0; //use native engine } void XVRenderer::drawBackground() { const QRegion bgRegion(backgroundRegion()); if (bgRegion.isEmpty()) return; DPTR_D(XVRenderer); // TODO: set color const QVector bg(bgRegion.rects()); foreach (const QRect& r, bg) { XFillRectangle(d.display, winId(), d.gc, r.x(), r.y(), r.width(), r.height()); } XFlush(d.display); } void XVRenderer::drawFrame() { DPTR_D(XVRenderer); QRect roi = realROI(); #ifdef _XSHM_H_ if (d.use_shm) { XvShmPutImage(d.display, d.xv_port, winId(), d.gc, d.xv_image , roi.x(), roi.y(), roi.width(), roi.height() , d.out_rect.x(), d.out_rect.y(), d.out_rect.width(), d.out_rect.height() , false /*true: send event*/); XSync(d.display, False); // update immediately return; } #endif XvPutImage(d.display, d.xv_port, winId(), d.gc, d.xv_image , roi.x(), roi.y(), roi.width(), roi.height() , d.out_rect.x(), d.out_rect.y(), d.out_rect.width(), d.out_rect.height()); XSync(d.display, False); } void XVRenderer::paintEvent(QPaintEvent *) { handlePaintEvent(); } void XVRenderer::resizeEvent(QResizeEvent *e) { DPTR_D(XVRenderer); d.update_background = true; resizeRenderer(e->size()); update(); //update background } void XVRenderer::showEvent(QShowEvent *event) { Q_UNUSED(event); DPTR_D(XVRenderer); d.update_background = true; /* * Do something that depends on widget below! e.g. recreate render target for direct2d. * When Qt::WindowStaysOnTopHint changed, window will hide first then show. If you * don't do anything here, the widget content will never be updated. */ d.prepareDeviceResource(); } bool XVRenderer::onSetBrightness(qreal b) { DPTR_D(XVRenderer); return d.XvSetPortAttributeIfExists("XV_BRIGHTNESS", b*100); } bool XVRenderer::onSetContrast(qreal c) { DPTR_D(XVRenderer); return d.XvSetPortAttributeIfExists("XV_CONTRAST",c*100); } bool XVRenderer::onSetHue(qreal h) { DPTR_D(XVRenderer); return d.XvSetPortAttributeIfExists("XV_HUE", h*100); } bool XVRenderer::onSetSaturation(qreal s) { DPTR_D(XVRenderer); return d.XvSetPortAttributeIfExists("XV_SATURATION", s*100); } } //namespace QtAV #include "XVRenderer.moc" QtAV-1.12.0/widgets/global.cpp000066400000000000000000000141611312235004300160140ustar00rootroot00000000000000/****************************************************************************** VideoRendererTypes: type id and manually id register function Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2015) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "QtAVWidgets/global.h" #include #include #include #include #include #include #include "QtAVWidgets/WidgetRenderer.h" #include "QtAVWidgets/GraphicsItemRenderer.h" #if QTAV_HAVE(GL) #include "QtAVWidgets/GLWidgetRenderer2.h" #endif //QTAV_HAVE(GL) #if QTAV_HAVE(GL1) #include "QtAVWidgets/GLWidgetRenderer.h" #endif //QTAV_HAVE(GL1) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #ifndef QT_NO_OPENGL #include "QtAVWidgets/OpenGLWidgetRenderer.h" #endif //QT_NO_OPENGL #endif #include "QtAV/private/factory.h" #include "QtAV/private/mkid.h" namespace QtAV { VideoRendererId VideoRendererId_Widget = mkid::id32base36_6<'W', 'i', 'd', 'g', 'e', 't'>::value; VideoRendererId VideoRendererId_OpenGLWidget = mkid::id32base36_6<'Q', 'O', 'G', 'L', 'W', 't'>::value; VideoRendererId VideoRendererId_GLWidget2 = mkid::id32base36_6<'Q', 'G', 'L', 'W', 't', '2'>::value; VideoRendererId VideoRendererId_GLWidget = mkid::id32base36_6<'Q', 'G', 'L', 'W', 't', '1'>::value; VideoRendererId VideoRendererId_GraphicsItem = mkid::id32base36_6<'Q', 'G', 'r', 'a', 'p', 'h'>::value; VideoRendererId VideoRendererId_GDI = mkid::id32base36_3<'G', 'D', 'I'>::value; VideoRendererId VideoRendererId_Direct2D = mkid::id32base36_3<'D', '2', 'D'>::value; VideoRendererId VideoRendererId_XV = mkid::id32base36_6<'X', 'V', 'i', 'd', 'e', 'o'>::value; VideoRendererId VideoRendererId_X11 = mkid::id32base36_3<'X', '1', '1'>::value; //QPainterRenderer is abstract. So can not register(operator new will needed) #if AUTO_REGISTER FACTORY_REGISTER(VideoRenderer, Widget, "QWidegt") FACTORY_REGISTER(VideoRenderer, GraphicsItem, "QGraphicsItem") #if QTAV_HAVE(GL) #if QTAV_HAVE(GL1) FACTORY_REGISTER(VideoRenderer, GLWidget, "QGLWidegt") #endif //QTAV_HAVE(GL1) FACTORY_REGISTER(VideoRenderer, GLWidget2, "QGLWidegt2") #endif //QTAV_HAVE(GL) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #ifndef QT_NO_OPENGL FACTORY_REGISTER(VideoRenderer, OpenGLWidget, "OpenGLWidget") #endif //QT_NO_OPENGL #endif #endif extern void RegisterVideoRendererGDI_Man(); extern void RegisterVideoRendererDirect2D_Man(); extern void RegisterVideoRendererXV_Man(); extern void RegisterVideoRendererX11_Man(); namespace Widgets { void registerRenderers() { #if !defined(QT_NO_DEBUG) qDebug("registerRenderers..........."); #endif // check whether it is called static bool initialized = false; if (initialized) return; initialized = true; // factory.h does not check whether an id is registered if (VideoRenderer::name(VideoRendererId_Widget)) return; #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) #ifndef QT_NO_OPENGL VideoRenderer::Register(VideoRendererId_OpenGLWidget, "OpenGLWidget"); #endif //QT_NO_OPENGL #endif #if QTAV_HAVE(GL) VideoRenderer::Register(VideoRendererId_GLWidget2, "QGLWidget2"); #endif //QTAV_HAVE(GL) #if QTAV_HAVE(GL1) VideoRenderer::Register(VideoRendererId_GLWidget, "QGLWidget"); #endif //QTAV_HAVE(GL1) VideoRenderer::Register(VideoRendererId_Widget, "Widget"); #if QTAV_HAVE(GDIPLUS) RegisterVideoRendererGDI_Man(); #endif //QTAV_HAVE(GDIPLUS) #if QTAV_HAVE(DIRECT2D) RegisterVideoRendererDirect2D_Man(); #endif //QTAV_HAVE(DIRECT2D) #if QTAV_HAVE(XV) RegisterVideoRendererXV_Man(); #endif //QTAV_HAVE(XV) #if QTAV_HAVE(X11) RegisterVideoRendererX11_Man(); #endif //QTAV_HAVE(XV) VideoRenderer::Register(VideoRendererId_GraphicsItem, "GraphicsItem"); } } //namespace Widgets namespace { static const struct register_renderers { inline register_renderers() { QtAV::Widgets::registerRenderers(); } } sRegisterVO; } void about() { //we should use new because a qobject will delete it's children QTextBrowser *viewQtAV = new QTextBrowser; QTextBrowser *viewFFmpeg = new QTextBrowser; viewQtAV->setOpenExternalLinks(true); viewFFmpeg->setOpenExternalLinks(true); viewQtAV->setHtml(aboutQtAV_HTML()); viewFFmpeg->setHtml(aboutFFmpeg_HTML()); QTabWidget *tab = new QTabWidget; tab->addTab(viewQtAV, QStringLiteral("QtAV")); tab->addTab(viewFFmpeg, QStringLiteral("FFmpeg")); QPushButton *qbtn = new QPushButton(QObject::tr("About Qt")); QPushButton *btn = new QPushButton(QObject::tr("Ok")); QHBoxLayout *btnLayout = new QHBoxLayout; btnLayout->addWidget(btn); btnLayout->addStretch(); btnLayout->addWidget(qbtn); btn->setFocus(); QDialog dialog; dialog.setWindowTitle(QObject::tr("About") + QStringLiteral(" QtAV")); QVBoxLayout *layout = new QVBoxLayout; dialog.setLayout(layout); layout->addWidget(tab); layout->addLayout(btnLayout); QObject::connect(qbtn, SIGNAL(clicked()), qApp, SLOT(aboutQt())); QObject::connect(btn, SIGNAL(clicked()), &dialog, SLOT(accept())); dialog.exec(); } void aboutFFmpeg() { QMessageBox::about(0, QObject::tr("About FFmpeg"), aboutFFmpeg_HTML()); } void aboutQtAV() { QMessageBox::about(0, QObject::tr("About QtAV"), aboutQtAV_HTML()); } }//namespace QtAV QtAV-1.12.0/widgets/libQtAVWidgets.pri000066400000000000000000000111231312235004300174100ustar00rootroot00000000000000# qmake library building template pri file # Copyright (C) 2011-2015 Wang Bin # Shanghai, China. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # ############################## HOW TO ################################## # Suppose the library name is XX # Usually what you need to change are: staticlink, LIB_VERSION, NAME and DLLDESTDIR. # And rename xx-buildlib and LIBXX_PRI_INCLUDED # the contents of libXX.pro is: # TEMPLATE = lib # QT -= gui # CONFIG *= xx-buildlib # STATICLINK = 1 #optional. default is 0, i.e. dynamically link # PROJECTROOT = $$PWD/.. # include(libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # ... # the content of other pro using this library is: # TEMPLATE = app # PROJECTROOT = $$PWD/.. # STATICLINK = 1 #or 0 # include(dir_of_XX/libXX.pri) # preparePaths($$OUT_PWD/../out) # HEADERS = ... # SOURCES = ... # NAME = QtAVWidgets !isEmpty(LIB$$upper($$NAME)_PRI_INCLUDED): { error("lib$${NAME}.pri already included") unset(NAME) } eval(LIB$$upper($$NAME)_PRI_INCLUDED = 1) LIB_VERSION = $$QTAV_VERSION #0.x.y may be wrong for dll # If user haven't supplied STATICLINK, then auto-detect isEmpty(STATICLINK) { static|contains(CONFIG, staticlib) { STATICLINK = 1 } else { STATICLINK = 0 } # Override for ios. Dynamic link is only supported # in iOS 8.1. ios:STATICLINK = 1 } isEqual(STATICLINK, 1):DEFINES += BUILD_$$upper($$NAME)_STATIC TEMPLATE += fakelib PROJECT_TARGETNAME = $$qtLibraryTarget($$NAME) TEMPLATE -= fakelib isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/.. include($${PROJECTROOT}/common.pri) preparePaths($$OUT_PWD/../out) CONFIG += depend_includepath #? mac_framework: PROJECT_TARGETNAME = $$NAME PROJECT_SRCPATH = $$PWD PROJECT_LIBDIR = $$qtLongName($$BUILD_DIR/lib) INCLUDEPATH *= $$PROJECT_SRCPATH $$PROJECT_SRCPATH/.. $$PROJECT_SRCPATH/$$NAME DEPENDPATH *= $$PROJECT_SRCPATH #QMAKE_LFLAGS_RPATH += #will append to rpath dir #eval() ? !contains(CONFIG, $$lower($$NAME)-buildlib) { #The following may not need to change CONFIG *= link_prl mac_framework { LIBS += -F$$PROJECT_LIBDIR -framework $$PROJECT_TARGETNAME } else { LIBS *= -L$$PROJECT_LIBDIR -l$$qtLibName($$NAME) isEqual(STATICLINK, 1) { PRE_TARGETDEPS += $$PROJECT_LIBDIR/$$qtStaticLib($$NAME) } else { win32 { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME, $$LIB_VERSION) } else { PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) } } } } else { #Add your additional configuration first. e.g. # win32: LIBS += -lUser32 # The following may not need to change !CONFIG(plugin) { #TEMPLATE = lib VERSION = $$LIB_VERSION DESTDIR= $$PROJECT_LIBDIR } TARGET = $$PROJECT_TARGETNAME ##I commented out this before, why? CONFIG *= create_prl # DEFINES += BUILD_$$upper($$NAME)_LIB #win32-msvc* isEqual(STATICLINK, 1) { CONFIG -= shared dll ##otherwise the following shared is true, why? CONFIG *= staticlib } else { CONFIG *= shared #shared includes dll } shared { !CONFIG(plugin) { !isEqual(DESTDIR, $$BUILD_DIR/bin): DLLDESTDIR = $$BUILD_DIR/bin #copy shared lib there } CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP):!mac_framework: QMAKE_POST_LINK = -$$QMAKE_STRIP $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) #copy from the pro creator creates. symbian { MMP_RULES += EXPORTUNFROZEN TARGET.UID3 = 0xE4CC8061 TARGET.CAPABILITY = TARGET.EPOCALLOWDLLDATA = 1 addFiles.sources = $$qtSharedLib($$NAME, $$LIB_VERSION) addFiles.path = !:/sys/bin DEPLOYMENT += addFiles } } unix:!symbian { maemo5 { target.path = /opt/usr/lib } else { target.path = /usr/lib } INSTALLS += target } } !no_rpath:!cross_compile:set_rpath($$PROJECT_LIBDIR) unset(LIB_VERSION) unset(PROJECT_SRCPATH) unset(PROJECT_LIBDIR) unset(PROJECT_TARGETNAME) QtAV-1.12.0/widgets/libQtAVWidgets.pro000066400000000000000000000141061312235004300174220ustar00rootroot00000000000000TEMPLATE = lib MODULE_INCNAME = QtAVWidgets # for mac framework. also used in install_sdk.pro TARGET = QtAVWidgets QT += gui config_gl: QT += opengl greaterThan(QT_MAJOR_VERSION, 4) { # qtHaveModule does not exist in Qt5.0 qtHaveModule(widgets) { QT *= widgets } qtHaveModule(opengl) { QT *= opengl } } CONFIG *= qtavwidgets-buildlib INCLUDEPATH += $$[QT_INSTALL_HEADERS] #release: DEFINES += QT_NO_DEBUG_OUTPUT #var with '_' can not pass to pri? PROJECTROOT = $$PWD/.. !include($$PROJECTROOT/src/libQtAV.pri): error("could not find libQtAV.pri") !include(libQtAVWidgets.pri): error("could not find libQtAVWidgets.pri") preparePaths($$OUT_PWD/../out) !rc_file { RC_ICONS = $$PROJECTROOT/src/QtAV.ico QMAKE_TARGET_COMPANY = "Shanghai University->S3 Graphics->Deepin | wbsecg1@gmail.com" QMAKE_TARGET_DESCRIPTION = "QtAVWidgets module. QtAV Multimedia framework. http://qtav.org" QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2012-2017 WangBin, wbsecg1@gmail.com" QMAKE_TARGET_PRODUCT = "QtAV Widgets" } else:win32 { RC_FILE = QtAVWidgets.rc #no depends for rc file by default, even if rc includes a header. Makefile target use '/' as default, so not works iwth win cmd rc.target = $$clean_path($$RC_FILE) #rc obj depends on clean path target rc.depends = QtAVWidgets/version.h #why use multiple rule failed? i.e. add a rule without command isEmpty(QMAKE_SH) { rc.commands = @copy /B $$system_path($$RC_FILE)+,, #change file time } else { rc.commands = @touch $$RC_FILE #change file time } QMAKE_EXTRA_TARGETS += rc } OTHER_FILES += $$RC_FILE win32 { #dynamicgl: __impl__GetDC __impl_ReleaseDC !winrt:LIBS *= -luser32 } SDK_HEADERS *= \ QtAVWidgets/QtAVWidgets \ QtAVWidgets/QtAVWidgets.h \ QtAVWidgets/global.h \ QtAVWidgets/version.h \ QtAVWidgets/VideoPreviewWidget.h \ QtAVWidgets/GraphicsItemRenderer.h \ QtAVWidgets/WidgetRenderer.h SOURCES *= \ global.cpp \ VideoPreviewWidget.cpp \ GraphicsItemRenderer.cpp \ WidgetRenderer.cpp contains(QT_CONFIG, opengl):greaterThan(QT_MAJOR_VERSION, 4) { SDK_HEADERS *= QtAVWidgets/OpenGLWidgetRenderer.h SOURCES *= OpenGLWidgetRenderer.cpp isEqual(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 4) { SDK_HEADERS *= QtAVWidgets/QOpenGLWidget.h SOURCES *= QOpenGLWidget.cpp } } config_gl { DEFINES *= QTAV_HAVE_GL=1 SOURCES += GLWidgetRenderer2.cpp SDK_HEADERS += QtAVWidgets/GLWidgetRenderer2.h !contains(QT_CONFIG, dynamicgl) { #dynamicgl does not support old gl1 functions which used in GLWidgetRenderer #GLWidgetRenderer depends on internal functions of QtAV #DEFINES *= QTAV_HAVE_GL1 #SOURCES += GLWidgetRenderer.cpp #SDK_HEADERS += QtAVWidgets/GLWidgetRenderer.h } } config_gdiplus { DEFINES *= QTAV_HAVE_GDIPLUS=1 SOURCES += GDIRenderer.cpp LIBS *= -lgdiplus -lgdi32 } config_direct2d { DEFINES *= QTAV_HAVE_DIRECT2D=1 !*msvc*: INCLUDEPATH += $$PROJECTROOT/contrib/d2d1headers SOURCES += Direct2DRenderer.cpp #LIBS += -lD2d1 } config_xv { DEFINES *= QTAV_HAVE_XV=1 SOURCES += XVRenderer.cpp LIBS *= -lXv -lX11 -lXext } config_x11 { DEFINES *= QTAV_HAVE_X11=1 SOURCES *= X11Renderer.cpp LIBS *= -lX11 } # QtAV/private/* may be used by developers to extend QtAV features without changing QtAV library # headers not in QtAV/ and it's subdirs are used only by QtAV internally HEADERS *= \ $$SDK_HEADERS \ $$SDK_PRIVATE_HEADERS # from mkspecs/features/qt_module.prf # OS X and iOS frameworks mac_framework { # from common.pri #QMAKE_FRAMEWORK_VERSION = 4.0 CONFIG += lib_bundle sliced_bundle qt_framework CONFIG -= qt_install_headers #no need to install these as well !debug_and_release|!build_all|CONFIG(release, debug|release) { FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$SDK_HEADERS FRAMEWORK_HEADERS.path = Headers # 5.4(beta) workaround for wrong include path # TODO: why can be found? qtAtLeast(5,3): FRAMEWORK_HEADERS.path = Headers/$$MODULE_INCNAME FRAMEWORK_PRIVATE_HEADERS.version = Versions FRAMEWORK_PRIVATE_HEADERS.files = $$SDK_PRIVATE_HEADERS FRAMEWORK_PRIVATE_HEADERS.path = Headers/$$VERSION/$$MODULE_INCNAME/private QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS FRAMEWORK_PRIVATE_HEADERS } } mac { CONFIG += explicitlib macx-g++ { QMAKE_CFLAGS += -fconstant-cfstrings QMAKE_CXXFLAGS += -fconstant-cfstrings } } unix:!android:!mac { #debian DEB_INSTALL_LIST = .$$[QT_INSTALL_LIBS]/libQt*AVWidgets.so.* libqtavwidgets.target = libqtavwidgets.install libqtavwidgets.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >$$PROJECTROOT/debian/$${libqtavwidgets.target} QMAKE_EXTRA_TARGETS += libqtavwidgets target.depends *= $${libqtavwidgets.target} DEB_INSTALL_LIST = $$join(SDK_HEADERS, \\n.$$[QT_INSTALL_HEADERS]/, .$$[QT_INSTALL_HEADERS]/) DEB_INSTALL_LIST += .$$[QT_INSTALL_LIBS]/libQt*AVWidgets.prl .$$[QT_INSTALL_LIBS]/libQt*AVWidgets.so MKSPECS_DIR=$$[QT_HOST_DATA]/mkspecs DEB_INSTALL_LIST += .$${MKSPECS_DIR}/features/avwidgets.prf .$${MKSPECS_DIR}/modules/qt_lib_avwidgets.pri qtavwidgets_dev.target = qtav-dev.install #like qtmultimedia5-dev, contains widgets headers qtavwidgets_dev.commands = echo \"$$join(DEB_INSTALL_LIST, \\n)\" >>$$PROJECTROOT/debian/$${qtavwidgets_dev.target} QMAKE_EXTRA_TARGETS += qtavwidgets_dev target.depends *= $${qtavwidgets_dev.target} greaterThan(QT_MAJOR_VERSION, 4) { qtavwidgets_dev_links.target = qtav-dev.links #like qtmultimedia5-dev, contains widgets .so qtavwidgets_dev_links.commands = echo \"$$[QT_INSTALL_LIBS]/libQtAVWidgets.so $$[QT_INSTALL_LIBS]/libQt$${QT_MAJOR_VERSION}AVWidgets.so\" >>$$PROJECTROOT/debian/$${qtavwidgets_dev_links.target} QMAKE_EXTRA_TARGETS *= qtavwidgets_dev_links target.depends *= $${qtavwidgets_dev_links.target} } #Qt>=5 } #debian MODULE_INCNAME = QtAVWidgets MODULE_VERSION = $$VERSION #use Qt version. limited by qmake # windows: Qt5AV.dll, not Qt1AV.dll !mac_framework: MODULE_VERSION = $${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} !contains(QMAKE_HOST.os, Windows):include($$PROJECTROOT/deploy.pri)