├── .gitignore ├── CONTRIBUTING.md ├── Doxyfile ├── LICENSE.md ├── README.md ├── examples ├── AnimationWriter │ ├── .gitignore │ ├── AnimationWriter.jucer │ ├── Builds │ │ ├── MacOSX │ │ │ ├── AnimationWriter.xcodeproj │ │ │ │ └── project.pbxproj │ │ │ ├── Info-App.plist │ │ │ └── RecentFilesMenuTemplate.nib │ │ └── VisualStudio2015 │ │ │ ├── AnimationWriter - App.vcxproj │ │ │ ├── AnimationWriter.sln │ │ │ ├── AnimationWriter_App.vcxproj │ │ │ ├── AnimationWriter_App.vcxproj.filters │ │ │ └── resources.rc │ └── Source │ │ ├── Main.cpp │ │ └── MainComponent.cpp └── VideoPlayer │ ├── .gitignore │ ├── Builds │ ├── MacOSX │ │ ├── Info-App.plist │ │ ├── RecentFilesMenuTemplate.nib │ │ └── VideoPlayer.xcodeproj │ │ │ └── project.pbxproj │ └── VisualStudio2015 │ │ ├── VideoPlayer (App).vcxproj │ │ ├── VideoPlayer - App.vcxproj │ │ ├── VideoPlayer.sln │ │ ├── VideoPlayer.vcxproj │ │ ├── VideoPlayer.vcxproj.filters │ │ ├── VideoPlayer_App.vcxproj │ │ ├── VideoPlayer_App.vcxproj.filters │ │ └── resources.rc │ ├── Source │ ├── Main.cpp │ ├── MainComponent.cpp │ └── OSDComponent.h │ └── VideoPlayer.jucer └── modules ├── filmstro_audiohelpers ├── filmstro_audiohelpers.h ├── filmstro_audiohelpers_AudioBufferFIFO.h ├── filmstro_audiohelpers_AudioProcessorPlayerSource.h ├── filmstro_audiohelpers_OutputSourcePlayer.h └── filmstro_audiohelpers_SharedFormatManager.h └── filmstro_ffmpeg ├── filmstro_ffmpeg.h ├── filmstro_ffmpeg_FFmpegVideoComponent.cpp ├── filmstro_ffmpeg_FFmpegVideoComponent.h ├── filmstro_ffmpeg_FFmpegVideoListener.h ├── filmstro_ffmpeg_FFmpegVideoReader.cpp ├── filmstro_ffmpeg_FFmpegVideoReader.h ├── filmstro_ffmpeg_FFmpegVideoScaler.h ├── filmstro_ffmpeg_FFmpegVideoWriter.cpp └── filmstro_ffmpeg_FFmpegVideoWriter.h /.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | examples/VideoPlayer/Builds/VisualStudio2015/Win32/ 3 | *.db 4 | *.opendb 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | filmstro_ffmpeg 2 | =============== 3 | 4 | This JUCE wrapper for the ffMpeg library was written by Daniel Walz (ffAudio), 5 | begun while working at Filmstro Ltd. in Brighton, UK. 6 | 7 | We welcome all contributions, suggestions and bug reports, as this is the strength of 8 | Open Source. 9 | 10 | However, we will decide at our discretion to accept or ignore pull requests. 11 | A pull request will not form any ownership of any of the code. 12 | 13 | 14 | Contributions so far: 15 | 16 | - Ian (ianatdolby): Added support for interleaved audio formats and use ffmpegs audio resampler 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | filmstro_ffmpeg - license 2 | ========================= 3 | 4 | ``` 5 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 28 | OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ``` 30 | 31 | Brighton, June 2017 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | filmstro_ffmpeg 3 | =============== 4 | 5 | by Daniel Walz / Filmstro Ltd. 6 | Published under the BSD License (3 clause) 7 | 8 | This is a module to read video files and play/display it using the JUCE 9 | framework (juce.com). The audio will be available as a juce::AudioSource and 10 | can be processed through a regular processing chain. 11 | 12 | Find the [API documentation here](https://filmstro.github.io/filmstro_ffmpeg/) 13 | 14 | Dependencies 15 | ============ 16 | 17 | The reading is done using ffMpeg (ffmpeg.org). You will have to build ffmpeg 18 | and link your project to it, if it's not already installed. We use this flags: 19 | 20 | OSX: 21 | 22 | ./configure --cc=CC --arch=x86_64 --disable-static --enable-shared --disable-stripping --disable-debug --install-name-dir='@loader_path' 23 | 24 | Windows: 25 | 26 | ./configure --toolchain=msvc --disable-static --enable-shared --prefix=../build/windows --arch=x86 27 | 28 | To make 29 | 30 | make -j10 31 | make install 32 | 33 | You can add an audio meter simply by adding the module https://github.com/ffAudio/ff_meters to the projucer project. 34 | 35 | 36 | Current State 37 | ============= 38 | 39 | It works for us with mp4 files. However, there are many formats that theoretically should work, but some create 40 | problems, probably to do with the sequence of packets / frames in the stream. To 41 | improve this any insights and findings are very welcome. 42 | 43 | There is an example how to write e.g. an Animation to a file, see examples/AnimationWriter. The animation is 44 | simply copied from the JUCE examples and the writer is added. 45 | 46 | ******************************************************************************** 47 | 48 | We hope it is of any use, let us know of any problems or improvements you may 49 | come up with... 50 | 51 | Brighton, 9th February 2017 52 | 53 | ******************************************************************************** 54 | -------------------------------------------------------------------------------- /examples/AnimationWriter/.gitignore: -------------------------------------------------------------------------------- 1 | Builds/MacOSX/AnimationWriter.xcodeproj/xcuserdata/ 2 | Builds/MacOSX/AnimationWriter.xcodeproj/project.xcworkspace/ 3 | Builds/MacOSX/build/Debug/ 4 | Builds/MacOSX/build/Release/ 5 | Builds/MacOSX/build/AnimationWriter.build/ 6 | JuceLibraryCode/ 7 | 8 | -------------------------------------------------------------------------------- /examples/AnimationWriter/AnimationWriter.jucer: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 46 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/MacOSX/AnimationWriter.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | 84126614E38094C3A6753F77 = {isa = PBXBuildFile; fileRef = 8DF8A1F530B4D5B24DB2131C; }; 10 | CE615F8434ABFDB021B30FFB = {isa = PBXBuildFile; fileRef = CFB3210F0790D39592202590; }; 11 | 593329D60E6758C4146C518C = {isa = PBXBuildFile; fileRef = 662BA9AE35EC1C3CACE89D0F; }; 12 | 48E26FFCF88747E719A998D3 = {isa = PBXBuildFile; fileRef = 279D2FC78D87D6F7C2A5FB6F; }; 13 | C05FDDE9AC9700481D429AA9 = {isa = PBXBuildFile; fileRef = 1CA0B5B547A04CBEDC73EA29; }; 14 | 77B10A2FC144AE8105F3E3CC = {isa = PBXBuildFile; fileRef = 2B017EBADD3C1142DC5B4583; }; 15 | 90FFC256DA2692235C34AC47 = {isa = PBXBuildFile; fileRef = 9E0769B33582C0556942253C; }; 16 | 2C84E713BD4E8A790B6FB7D0 = {isa = PBXBuildFile; fileRef = 97BF18FDF6295096BAFF514A; }; 17 | 2AAB862BBA1269374C8CBF37 = {isa = PBXBuildFile; fileRef = D56051ED1536EE645AF154C4; }; 18 | 383EB94E9E953681C7AFE5BE = {isa = PBXBuildFile; fileRef = 30C0A0262206EA700AAE1FC1; }; 19 | B6354D10CFCB76715D00518B = {isa = PBXBuildFile; fileRef = 3E9B3AB19191B41F738629F3; }; 20 | AB3CF6D773A815C9F590561A = {isa = PBXBuildFile; fileRef = BE4E0356AA8265B8866DB6A2; }; 21 | E6E13134807C2B30FC82E9CC = {isa = PBXBuildFile; fileRef = A74DAC0C8D5107ABF6AAEF2D; }; 22 | 1DA2ACDDC4DA4D016015ED11 = {isa = PBXBuildFile; fileRef = AED04C702F5B55957D5C445F; }; 23 | 35913D20399BA1526162387C = {isa = PBXBuildFile; fileRef = 0CD2392379DE0F9F87A0107E; }; 24 | 2B01C40B1DBA960DB3A55B66 = {isa = PBXBuildFile; fileRef = 607CE387A481957CDD59D90E; }; 25 | 74E55A83A5367F9ED189BC1F = {isa = PBXBuildFile; fileRef = 7D47134AB08DF97F9DA79300; }; 26 | A8F0A4402B93EF710C262E32 = {isa = PBXBuildFile; fileRef = D9FCEBF3117A0F32EAC498D4; }; 27 | 0BD92437D8A2325D5F38D88C = {isa = PBXBuildFile; fileRef = B81BF1239BCF78413757DC1C; }; 28 | D0E4A6DDEB4FA8ACFAAC9196 = {isa = PBXBuildFile; fileRef = 52B267CE2126115014DA1748; }; 29 | CF4B2BDF07BEAD43D3C74000 = {isa = PBXBuildFile; fileRef = FE8AE66E95BB7A2A89FB2302; }; 30 | 86215578F641BD4CD768B944 = {isa = PBXBuildFile; fileRef = 3BCCEDFD2C3A07C096624889; }; 31 | 8513F4FA617B7FAA2A417744 = {isa = PBXBuildFile; fileRef = 1EBA5D3E157DDDDA5B423EF4; }; 32 | 699ECA9670F19F9DED971A6B = {isa = PBXBuildFile; fileRef = 2DD60BD7849677BF1AF4BC18; }; 33 | 0774292A8A728CFB5C6EBE7B = {isa = PBXBuildFile; fileRef = 89D541C1C81041585FB7F620; }; 34 | 0DF9E6ABDC25C19DEA206B1B = {isa = PBXBuildFile; fileRef = 3D8C99E65316150252498CC8; }; 35 | 11DE783EFABFD48CCDC4F4BB = {isa = PBXBuildFile; fileRef = B05227A3BD24ACFAE688B147; }; 36 | 9F82D1B651088851DF81A384 = {isa = PBXBuildFile; fileRef = CA32A431DE2B3AB5B4BBA6D7; }; 37 | F44A81EEE6FC217D134DBB90 = {isa = PBXBuildFile; fileRef = 9D7082639AE07EFC4448FD63; }; 38 | 7EBB9EB20F6A6EB1E07A2BA1 = {isa = PBXBuildFile; fileRef = 07C37447DC474934AA6113D0; }; 39 | 930E5950447B5A2245709B21 = {isa = PBXBuildFile; fileRef = 66280D8E82D2DD905497DB4B; }; 40 | 7CD5E6EE983829CD82BC5FFE = {isa = PBXBuildFile; fileRef = A905989DD8CE5F62C0C3E373; }; 41 | 07C37447DC474934AA6113D0 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_gui_extra.mm"; path = "../../JuceLibraryCode/include_juce_gui_extra.mm"; sourceTree = "SOURCE_ROOT"; }; 42 | 0CD2392379DE0F9F87A0107E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MainComponent.cpp; path = ../../Source/MainComponent.cpp; sourceTree = "SOURCE_ROOT"; }; 43 | 1CA0B5B547A04CBEDC73EA29 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 44 | 1EBA5D3E157DDDDA5B423EF4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_processors.mm"; path = "../../JuceLibraryCode/include_juce_audio_processors.mm"; sourceTree = "SOURCE_ROOT"; }; 45 | 237FDBE0A0B24D510E72F1FD = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_basics"; path = "$(HOME)/Developer/JUCE5/modules/juce_gui_basics"; sourceTree = ""; }; 46 | 279D2FC78D87D6F7C2A5FB6F = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 47 | 2B017EBADD3C1142DC5B4583 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 48 | 2DD60BD7849677BF1AF4BC18 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_core.mm"; path = "../../JuceLibraryCode/include_juce_core.mm"; sourceTree = "SOURCE_ROOT"; }; 49 | 30C0A0262206EA700AAE1FC1 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 50 | 3BCCEDFD2C3A07C096624889 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_formats.mm"; path = "../../JuceLibraryCode/include_juce_audio_formats.mm"; sourceTree = "SOURCE_ROOT"; }; 51 | 3D8C99E65316150252498CC8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_data_structures.mm"; path = "../../JuceLibraryCode/include_juce_data_structures.mm"; sourceTree = "SOURCE_ROOT"; }; 52 | 3E9B3AB19191B41F738629F3 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; 53 | 438284FE3877E1977AF27441 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_opengl"; path = "$(HOME)/Developer/JUCE5/modules/juce_opengl"; sourceTree = ""; }; 54 | 52B267CE2126115014DA1748 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_basics.mm"; path = "../../JuceLibraryCode/include_juce_audio_basics.mm"; sourceTree = "SOURCE_ROOT"; }; 55 | 52E88916F57D26B99A94EC2B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = "SOURCE_ROOT"; }; 56 | 607CE387A481957CDD59D90E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Main.cpp; path = ../../Source/Main.cpp; sourceTree = "SOURCE_ROOT"; }; 57 | 66280D8E82D2DD905497DB4B = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_opengl.mm"; path = "../../JuceLibraryCode/include_juce_opengl.mm"; sourceTree = "SOURCE_ROOT"; }; 58 | 662BA9AE35EC1C3CACE89D0F = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 59 | 6EC2D41E236228B2D0F1C127 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_processors"; path = "$(HOME)/Developer/JUCE5/modules/juce_audio_processors"; sourceTree = ""; }; 60 | 72BB758EA230626FC98EDD2B = {isa = PBXFileReference; lastKnownFileType = file; name = "filmstro_audiohelpers"; path = "../../../../modules/filmstro_audiohelpers"; sourceTree = "SOURCE_ROOT"; }; 61 | 7D47134AB08DF97F9DA79300 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_filmstro_ffmpeg_FFmpegVideoComponent.cpp"; path = "../../JuceLibraryCode/include_filmstro_ffmpeg_FFmpegVideoComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; 62 | 7EA266B152B5E71634EE7E90 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_events"; path = "$(HOME)/Developer/JUCE5/modules/juce_events"; sourceTree = ""; }; 63 | 87AA51F8ABC360ED4BE5A3D5 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_basics"; path = "$(HOME)/Developer/JUCE5/modules/juce_audio_basics"; sourceTree = ""; }; 64 | 883ADA1A8E18370962E49FCD = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_data_structures"; path = "$(HOME)/Developer/JUCE5/modules/juce_data_structures"; sourceTree = ""; }; 65 | 89D541C1C81041585FB7F620 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_cryptography.mm"; path = "../../JuceLibraryCode/include_juce_cryptography.mm"; sourceTree = "SOURCE_ROOT"; }; 66 | 8B684384CABF6CA6E885CF81 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JuceHeader.h; path = ../../JuceLibraryCode/JuceHeader.h; sourceTree = "SOURCE_ROOT"; }; 67 | 8DF8A1F530B4D5B24DB2131C = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnimationWriter.app; sourceTree = "BUILT_PRODUCTS_DIR"; }; 68 | 97BF18FDF6295096BAFF514A = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; 69 | 9D7082639AE07EFC4448FD63 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_gui_basics.mm"; path = "../../JuceLibraryCode/include_juce_gui_basics.mm"; sourceTree = "SOURCE_ROOT"; }; 70 | 9E0769B33582C0556942253C = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 71 | A283AB3BA5E5BB13A672D29F = {isa = PBXFileReference; lastKnownFileType = file; name = "filmstro_ffmpeg"; path = "../../../../modules/filmstro_ffmpeg"; sourceTree = "SOURCE_ROOT"; }; 72 | A59C895A18329C62AD9A039B = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_cryptography"; path = "$(HOME)/Developer/JUCE5/modules/juce_cryptography"; sourceTree = ""; }; 73 | A74DAC0C8D5107ABF6AAEF2D = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 74 | A905989DD8CE5F62C0C3E373 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_video.mm"; path = "../../JuceLibraryCode/include_juce_video.mm"; sourceTree = "SOURCE_ROOT"; }; 75 | AED04C702F5B55957D5C445F = {isa = PBXFileReference; lastKnownFileType = file.nib; name = RecentFilesMenuTemplate.nib; path = RecentFilesMenuTemplate.nib; sourceTree = "SOURCE_ROOT"; }; 76 | B05227A3BD24ACFAE688B147 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_events.mm"; path = "../../JuceLibraryCode/include_juce_events.mm"; sourceTree = "SOURCE_ROOT"; }; 77 | B13037D3D34D3E0805769B4F = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_graphics"; path = "$(HOME)/Developer/JUCE5/modules/juce_graphics"; sourceTree = ""; }; 78 | B5497809C3BE72F221C205AB = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-App.plist"; path = "Info-App.plist"; sourceTree = "SOURCE_ROOT"; }; 79 | B73FD37DB7C051B88D354E30 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_core"; path = "$(HOME)/Developer/JUCE5/modules/juce_core"; sourceTree = ""; }; 80 | B81BF1239BCF78413757DC1C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_filmstro_ffmpeg_FFmpegVideoWriter.cpp"; path = "../../JuceLibraryCode/include_filmstro_ffmpeg_FFmpegVideoWriter.cpp"; sourceTree = "SOURCE_ROOT"; }; 81 | B96262CDB7EC68FB4A7B15C1 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_devices"; path = "$(HOME)/Developer/JUCE5/modules/juce_audio_devices"; sourceTree = ""; }; 82 | BE4E0356AA8265B8866DB6A2 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 83 | CA32A431DE2B3AB5B4BBA6D7 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_graphics.mm"; path = "../../JuceLibraryCode/include_juce_graphics.mm"; sourceTree = "SOURCE_ROOT"; }; 84 | CFB3210F0790D39592202590 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 85 | D56051ED1536EE645AF154C4 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = System/Library/Frameworks/CoreMIDI.framework; sourceTree = SDKROOT; }; 86 | D9FCEBF3117A0F32EAC498D4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_filmstro_ffmpeg_FFmpegVideoReader.cpp"; path = "../../JuceLibraryCode/include_filmstro_ffmpeg_FFmpegVideoReader.cpp"; sourceTree = "SOURCE_ROOT"; }; 87 | DE0D306E8BA00467337E9D82 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_video"; path = "$(HOME)/Developer/JUCE5/modules/juce_video"; sourceTree = ""; }; 88 | DF6B2F765E7761DB1C5DC083 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_extra"; path = "$(HOME)/Developer/JUCE5/modules/juce_gui_extra"; sourceTree = ""; }; 89 | FDEAA1C503C7808DD969B2A4 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_formats"; path = "$(HOME)/Developer/JUCE5/modules/juce_audio_formats"; sourceTree = ""; }; 90 | FE8AE66E95BB7A2A89FB2302 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_devices.mm"; path = "../../JuceLibraryCode/include_juce_audio_devices.mm"; sourceTree = "SOURCE_ROOT"; }; 91 | 945A878486F49E9C89244D90 = {isa = PBXGroup; children = ( 92 | 0CD2392379DE0F9F87A0107E, 93 | 607CE387A481957CDD59D90E, ); name = Source; sourceTree = ""; }; 94 | 78F0CD72045EA38F6D2A47A6 = {isa = PBXGroup; children = ( 95 | 945A878486F49E9C89244D90, ); name = AnimationWriter; sourceTree = ""; }; 96 | 6ED9A120F41C32CDFA5A6700 = {isa = PBXGroup; children = ( 97 | 72BB758EA230626FC98EDD2B, 98 | A283AB3BA5E5BB13A672D29F, 99 | 87AA51F8ABC360ED4BE5A3D5, 100 | B96262CDB7EC68FB4A7B15C1, 101 | FDEAA1C503C7808DD969B2A4, 102 | 6EC2D41E236228B2D0F1C127, 103 | B73FD37DB7C051B88D354E30, 104 | A59C895A18329C62AD9A039B, 105 | 883ADA1A8E18370962E49FCD, 106 | 7EA266B152B5E71634EE7E90, 107 | B13037D3D34D3E0805769B4F, 108 | 237FDBE0A0B24D510E72F1FD, 109 | DF6B2F765E7761DB1C5DC083, 110 | 438284FE3877E1977AF27441, 111 | DE0D306E8BA00467337E9D82, ); name = "Juce Modules"; sourceTree = ""; }; 112 | 41C5DD571C74F64C5CA70811 = {isa = PBXGroup; children = ( 113 | 52E88916F57D26B99A94EC2B, 114 | 7D47134AB08DF97F9DA79300, 115 | D9FCEBF3117A0F32EAC498D4, 116 | B81BF1239BCF78413757DC1C, 117 | 52B267CE2126115014DA1748, 118 | FE8AE66E95BB7A2A89FB2302, 119 | 3BCCEDFD2C3A07C096624889, 120 | 1EBA5D3E157DDDDA5B423EF4, 121 | 2DD60BD7849677BF1AF4BC18, 122 | 89D541C1C81041585FB7F620, 123 | 3D8C99E65316150252498CC8, 124 | B05227A3BD24ACFAE688B147, 125 | CA32A431DE2B3AB5B4BBA6D7, 126 | 9D7082639AE07EFC4448FD63, 127 | 07C37447DC474934AA6113D0, 128 | 66280D8E82D2DD905497DB4B, 129 | A905989DD8CE5F62C0C3E373, 130 | 8B684384CABF6CA6E885CF81, ); name = "Juce Library Code"; sourceTree = ""; }; 131 | D8669A6265CA1F15C34E3164 = {isa = PBXGroup; children = ( 132 | B5497809C3BE72F221C205AB, 133 | AED04C702F5B55957D5C445F, ); name = Resources; sourceTree = ""; }; 134 | 02EA7C7CB17F53C195545442 = {isa = PBXGroup; children = ( 135 | CFB3210F0790D39592202590, 136 | 662BA9AE35EC1C3CACE89D0F, 137 | 279D2FC78D87D6F7C2A5FB6F, 138 | 1CA0B5B547A04CBEDC73EA29, 139 | 2B017EBADD3C1142DC5B4583, 140 | 9E0769B33582C0556942253C, 141 | 97BF18FDF6295096BAFF514A, 142 | D56051ED1536EE645AF154C4, 143 | 30C0A0262206EA700AAE1FC1, 144 | 3E9B3AB19191B41F738629F3, 145 | BE4E0356AA8265B8866DB6A2, 146 | A74DAC0C8D5107ABF6AAEF2D, ); name = Frameworks; sourceTree = ""; }; 147 | 8BFE04FF8097C999BF6B1964 = {isa = PBXGroup; children = ( 148 | 8DF8A1F530B4D5B24DB2131C, ); name = Products; sourceTree = ""; }; 149 | 31511396049F550DED41F41D = {isa = PBXGroup; children = ( 150 | 78F0CD72045EA38F6D2A47A6, 151 | 6ED9A120F41C32CDFA5A6700, 152 | 41C5DD571C74F64C5CA70811, 153 | D8669A6265CA1F15C34E3164, 154 | 02EA7C7CB17F53C195545442, 155 | 8BFE04FF8097C999BF6B1964, ); name = Source; sourceTree = ""; }; 156 | 066983D19FEF06B6D9D88FA9 = {isa = XCBuildConfiguration; buildSettings = { 157 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 158 | CLANG_LINK_OBJC_RUNTIME = NO; 159 | COMBINE_HIDPI_IMAGES = YES; 160 | CONFIGURATION_BUILD_DIR = "$(PROJECT_DIR)/build/$(CONFIGURATION)"; 161 | COPY_PHASE_STRIP = NO; 162 | GCC_DYNAMIC_NO_PIC = NO; 163 | GCC_OPTIMIZATION_LEVEL = 0; 164 | GCC_PREPROCESSOR_DEFINITIONS = ( 165 | "_DEBUG=1", 166 | "DEBUG=1", 167 | "FFMPEG_ROOT=/usr/local", 168 | "JUCER_XCODE_MAC_F6D2F4CF=1", 169 | "JUCE_APP_VERSION=1.0.0", 170 | "JUCE_APP_VERSION_HEX=0x10000", 171 | "JucePlugin_Build_VST=0", 172 | "JucePlugin_Build_VST3=0", 173 | "JucePlugin_Build_AU=0", 174 | "JucePlugin_Build_AUv3=0", 175 | "JucePlugin_Build_RTAS=0", 176 | "JucePlugin_Build_AAX=0", 177 | "JucePlugin_Build_Standalone=0", ); 178 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 179 | HEADER_SEARCH_PATHS = ("../../JuceLibraryCode", "../../../../modules", "~/Developer/JUCE5/modules", "$(inherited)"); 180 | INFOPLIST_FILE = Info-App.plist; 181 | INFOPLIST_PREPROCESS = NO; 182 | INSTALL_PATH = "$(HOME)/Applications"; 183 | MACOSX_DEPLOYMENT_TARGET = 10.11; 184 | MACOSX_DEPLOYMENT_TARGET_ppc = 10.4; 185 | OTHER_CPLUSPLUSFLAGS = "-Wno-reserved-user-defined-literal -I/usr/local/include"; 186 | OTHER_LDFLAGS = "-L/usr/local/lib -lavformat -lavutil -lavcodec -lswscale -lswresample"; 187 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.AnimationWriter; 188 | SDKROOT_ppc = macosx10.5; 189 | USE_HEADERMAP = NO; }; name = Debug; }; 190 | F99DD5D54406C943444B6911 = {isa = XCBuildConfiguration; buildSettings = { 191 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 192 | CLANG_LINK_OBJC_RUNTIME = NO; 193 | COMBINE_HIDPI_IMAGES = YES; 194 | CONFIGURATION_BUILD_DIR = "$(PROJECT_DIR)/build/$(CONFIGURATION)"; 195 | DEAD_CODE_STRIPPING = YES; 196 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 197 | GCC_OPTIMIZATION_LEVEL = 3; 198 | GCC_PREPROCESSOR_DEFINITIONS = ( 199 | "_NDEBUG=1", 200 | "NDEBUG=1", 201 | "FFMPEG_ROOT=/usr/local", 202 | "JUCER_XCODE_MAC_F6D2F4CF=1", 203 | "JUCE_APP_VERSION=1.0.0", 204 | "JUCE_APP_VERSION_HEX=0x10000", 205 | "JucePlugin_Build_VST=0", 206 | "JucePlugin_Build_VST3=0", 207 | "JucePlugin_Build_AU=0", 208 | "JucePlugin_Build_AUv3=0", 209 | "JucePlugin_Build_RTAS=0", 210 | "JucePlugin_Build_AAX=0", 211 | "JucePlugin_Build_Standalone=0", ); 212 | GCC_SYMBOLS_PRIVATE_EXTERN = YES; 213 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 214 | HEADER_SEARCH_PATHS = ("../../JuceLibraryCode", "../../../../modules", "~/Developer/JUCE5/modules", "$(inherited)"); 215 | INFOPLIST_FILE = Info-App.plist; 216 | INFOPLIST_PREPROCESS = NO; 217 | INSTALL_PATH = "$(HOME)/Applications"; 218 | MACOSX_DEPLOYMENT_TARGET = 10.11; 219 | MACOSX_DEPLOYMENT_TARGET_ppc = 10.4; 220 | OTHER_CPLUSPLUSFLAGS = "-Wno-reserved-user-defined-literal -I/usr/local/include"; 221 | OTHER_LDFLAGS = "-L/usr/local/lib -lavformat -lavutil -lavcodec -lswscale -lswresample"; 222 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.AnimationWriter; 223 | SDKROOT_ppc = macosx10.5; 224 | USE_HEADERMAP = NO; }; name = Release; }; 225 | F9941B8D7DFDAB3B819AB248 = {isa = XCBuildConfiguration; buildSettings = { 226 | ALWAYS_SEARCH_USER_PATHS = NO; 227 | DEBUG_INFORMATION_FORMAT = "dwarf"; 228 | ENABLE_TESTABILITY = YES; 229 | GCC_C_LANGUAGE_STANDARD = c11; 230 | GCC_INLINES_ARE_PRIVATE_EXTERN = YES; 231 | GCC_MODEL_TUNING = G5; 232 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 233 | GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; 234 | GCC_WARN_MISSING_PARENTHESES = YES; 235 | GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; 236 | GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; 237 | GCC_WARN_UNUSED_VARIABLE = YES; 238 | ONLY_ACTIVE_ARCH = YES; 239 | PRODUCT_NAME = "AnimationWriter"; 240 | WARNING_CFLAGS = -Wreorder; 241 | ZERO_LINK = NO; }; name = Debug; }; 242 | 460AD3503A3223BEF2698BE2 = {isa = XCBuildConfiguration; buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | DEBUG_INFORMATION_FORMAT = "dwarf"; 245 | GCC_C_LANGUAGE_STANDARD = c11; 246 | GCC_INLINES_ARE_PRIVATE_EXTERN = YES; 247 | GCC_MODEL_TUNING = G5; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 249 | GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; 250 | GCC_WARN_MISSING_PARENTHESES = YES; 251 | GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; 252 | GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; 253 | GCC_WARN_UNUSED_VARIABLE = YES; 254 | PRODUCT_NAME = "AnimationWriter"; 255 | WARNING_CFLAGS = -Wreorder; 256 | ZERO_LINK = NO; }; name = Release; }; 257 | 5E8742E6ADED0EC90F6C38AA = {isa = PBXTargetDependency; target = 5D6BC08B04394F552E02BFF9; }; 258 | 94EAD700A7D3033D1BFEDAF0 = {isa = XCConfigurationList; buildConfigurations = ( 259 | F9941B8D7DFDAB3B819AB248, 260 | 460AD3503A3223BEF2698BE2, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; 261 | 56F62D5302E537E6E8679767 = {isa = XCConfigurationList; buildConfigurations = ( 262 | 066983D19FEF06B6D9D88FA9, 263 | F99DD5D54406C943444B6911, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; 264 | 0FF969CA22DF317AC1372AE5 = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 265 | 1DA2ACDDC4DA4D016015ED11, ); runOnlyForDeploymentPostprocessing = 0; }; 266 | C0373C521259D11A862870DC = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 267 | 35913D20399BA1526162387C, 268 | 2B01C40B1DBA960DB3A55B66, 269 | 74E55A83A5367F9ED189BC1F, 270 | A8F0A4402B93EF710C262E32, 271 | 0BD92437D8A2325D5F38D88C, 272 | D0E4A6DDEB4FA8ACFAAC9196, 273 | CF4B2BDF07BEAD43D3C74000, 274 | 86215578F641BD4CD768B944, 275 | 8513F4FA617B7FAA2A417744, 276 | 699ECA9670F19F9DED971A6B, 277 | 0774292A8A728CFB5C6EBE7B, 278 | 0DF9E6ABDC25C19DEA206B1B, 279 | 11DE783EFABFD48CCDC4F4BB, 280 | 9F82D1B651088851DF81A384, 281 | F44A81EEE6FC217D134DBB90, 282 | 7EBB9EB20F6A6EB1E07A2BA1, 283 | 930E5950447B5A2245709B21, 284 | 7CD5E6EE983829CD82BC5FFE, ); runOnlyForDeploymentPostprocessing = 0; }; 285 | 7226A6A93265F6BC225430EF = {isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 286 | CE615F8434ABFDB021B30FFB, 287 | 593329D60E6758C4146C518C, 288 | 48E26FFCF88747E719A998D3, 289 | C05FDDE9AC9700481D429AA9, 290 | 77B10A2FC144AE8105F3E3CC, 291 | 90FFC256DA2692235C34AC47, 292 | 2C84E713BD4E8A790B6FB7D0, 293 | 2AAB862BBA1269374C8CBF37, 294 | 383EB94E9E953681C7AFE5BE, 295 | B6354D10CFCB76715D00518B, 296 | AB3CF6D773A815C9F590561A, 297 | E6E13134807C2B30FC82E9CC, ); runOnlyForDeploymentPostprocessing = 0; }; 298 | 5D6BC08B04394F552E02BFF9 = {isa = PBXNativeTarget; buildConfigurationList = 56F62D5302E537E6E8679767; buildPhases = ( 299 | 0FF969CA22DF317AC1372AE5, 300 | C0373C521259D11A862870DC, 301 | 7226A6A93265F6BC225430EF, ); buildRules = ( ); dependencies = ( ); name = "AnimationWriter - App"; productName = AnimationWriter; productReference = 8DF8A1F530B4D5B24DB2131C; productType = "com.apple.product-type.application"; }; 302 | 8B6A3CB88895A77F238B08B6 = {isa = PBXProject; buildConfigurationList = 94EAD700A7D3033D1BFEDAF0; attributes = { LastUpgradeCheck = 0830; TargetAttributes = { 5D6BC08B04394F552E02BFF9 = { SystemCapabilities = {com.apple.InAppPurchase = { enabled = 0; }; com.apple.InterAppAudio = { enabled = 0; }; com.apple.Sandbox = { enabled = 0; }; }; }; }; }; compatibilityVersion = "Xcode 3.2"; hasScannedForEncodings = 0; mainGroup = 31511396049F550DED41F41D; projectDirPath = ""; projectRoot = ""; targets = (5D6BC08B04394F552E02BFF9); }; 303 | }; 304 | rootObject = 8B6A3CB88895A77F238B08B6; 305 | } 306 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/MacOSX/Info-App.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CFBundleExecutable 7 | ${EXECUTABLE_NAME} 8 | CFBundleIconFile 9 | 10 | CFBundleIdentifier 11 | $(PRODUCT_BUNDLE_IDENTIFIER) 12 | CFBundleName 13 | AnimationWriter 14 | CFBundleDisplayName 15 | AnimationWriter 16 | CFBundlePackageType 17 | APPL 18 | CFBundleSignature 19 | ???? 20 | CFBundleShortVersionString 21 | 1.0.0 22 | CFBundleVersion 23 | 1.0.0 24 | NSHumanReadableCopyright 25 | 26 | NSHighResolutionCapable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/MacOSX/RecentFilesMenuTemplate.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filmstro/filmstro_ffmpeg/ebb79f7713742311d09ac3f2215d807e88e3a517/examples/AnimationWriter/Builds/MacOSX/RecentFilesMenuTemplate.nib -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/VisualStudio2015/AnimationWriter - App.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Debug 9 | x64 10 | 11 | 12 | Release 13 | x64 14 | 15 | 16 | 17 | {91D32CDA-AB1B-359D-8A4D-F196B0B23693} 18 | v140 19 | 20 | 21 | 23 | Application 24 | false 25 | v140 26 | v140 27 | 28 | 30 | Application 31 | false 32 | true 33 | v140 34 | v140 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | v140 45 | 46 | 47 | <_ProjectFileVersion>10.0.30319.1 48 | .exe 49 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 50 | $(Platform)\$(Configuration)\App\ 51 | AnimationWriter 52 | true 53 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 54 | $(Platform)\$(Configuration)\App\ 55 | AnimationWriter 56 | true 57 | v140 58 | 59 | 60 | 61 | _DEBUG;%(PreprocessorDefinitions) 62 | true 63 | true 64 | Win32 65 | 66 | 67 | 68 | Disabled 69 | ProgramDatabase 70 | ..\..\JuceLibraryCode;..\..\..\..\modules;..\..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 71 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 72 | MultiThreadedDebug 73 | true 74 | 75 | $(IntDir)\ 76 | $(IntDir)\ 77 | $(IntDir)\ 78 | Level4 79 | true 80 | true 81 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 82 | 83 | 84 | _DEBUG;%(PreprocessorDefinitions) 85 | 86 | 87 | $(OutDir)\AnimationWriter.exe 88 | true 89 | libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries) 90 | true 91 | $(IntDir)\AnimationWriter.pdb 92 | Windows 93 | true 94 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 95 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 96 | 97 | 98 | true 99 | $(IntDir)\AnimationWriter.bsc 100 | 101 | 102 | 103 | 104 | NDEBUG;%(PreprocessorDefinitions) 105 | true 106 | true 107 | Win32 108 | 109 | 110 | 111 | Full 112 | ..\..\JuceLibraryCode;..\..\..\..\modules;..\..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 113 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 114 | MultiThreaded 115 | true 116 | 117 | $(IntDir)\ 118 | $(IntDir)\ 119 | $(IntDir)\ 120 | Level4 121 | true 122 | true 123 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 124 | 125 | 126 | NDEBUG;%(PreprocessorDefinitions) 127 | 128 | 129 | $(OutDir)\AnimationWriter.exe 130 | true 131 | %(IgnoreSpecificDefaultLibraries) 132 | false 133 | $(IntDir)\AnimationWriter.pdb 134 | Windows 135 | true 136 | true 137 | true 138 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 139 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 140 | 141 | 142 | true 143 | $(IntDir)\AnimationWriter.bsc 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/VisualStudio2015/AnimationWriter.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 11.00 2 | # Visual Studio 2015 3 | 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnimationWriter - App", "AnimationWriter_App.vcxproj", "{91D32CDA-AB1B-359D-8A4D-F196B0B23693}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x64 = Debug|x64 9 | Release|x64 = Release|x64 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {91D32CDA-AB1B-359D-8A4D-F196B0B23693}.Debug|x64.Build.0 = Debug|x64 13 | {91D32CDA-AB1B-359D-8A4D-F196B0B23693}.Debug|x64.ActiveCfg = Debug|x64 14 | {91D32CDA-AB1B-359D-8A4D-F196B0B23693}.Release|x64.Build.0 = Release|x64 15 | {91D32CDA-AB1B-359D-8A4D-F196B0B23693}.Release|x64.ActiveCfg = Release|x64 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Builds/VisualStudio2015/resources.rc: -------------------------------------------------------------------------------- 1 | #ifdef JUCE_USER_DEFINED_RC_FILE 2 | #include JUCE_USER_DEFINED_RC_FILE 3 | #else 4 | 5 | #undef WIN32_LEAN_AND_MEAN 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | VS_VERSION_INFO VERSIONINFO 10 | FILEVERSION 1,0,0,0 11 | BEGIN 12 | BLOCK "StringFileInfo" 13 | BEGIN 14 | BLOCK "040904E4" 15 | BEGIN 16 | VALUE "FileDescription", "AnimationWriter\0" 17 | VALUE "FileVersion", "1.0.0\0" 18 | VALUE "ProductName", "AnimationWriter\0" 19 | VALUE "ProductVersion", "1.0.0\0" 20 | END 21 | END 22 | 23 | BLOCK "VarFileInfo" 24 | BEGIN 25 | VALUE "Translation", 0x409, 1252 26 | END 27 | END 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Source/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | It contains the basic startup code for a Juce application. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "../JuceLibraryCode/JuceHeader.h" 12 | 13 | Component* createMainContentComponent(); 14 | 15 | //============================================================================== 16 | class AnimationWriterApplication : public JUCEApplication 17 | { 18 | public: 19 | //============================================================================== 20 | AnimationWriterApplication() {} 21 | 22 | const String getApplicationName() override { return ProjectInfo::projectName; } 23 | const String getApplicationVersion() override { return ProjectInfo::versionString; } 24 | bool moreThanOneInstanceAllowed() override { return true; } 25 | 26 | //============================================================================== 27 | void initialise (const String& commandLine) override 28 | { 29 | // This method is where you should put your application's initialisation code.. 30 | 31 | mainWindow = new MainWindow (getApplicationName()); 32 | } 33 | 34 | void shutdown() override 35 | { 36 | // Add your application's shutdown code here.. 37 | 38 | mainWindow = nullptr; // (deletes our window) 39 | } 40 | 41 | //============================================================================== 42 | void systemRequestedQuit() override 43 | { 44 | // This is called when the app is being asked to quit: you can ignore this 45 | // request and let the app carry on running, or call quit() to allow the app to close. 46 | quit(); 47 | } 48 | 49 | void anotherInstanceStarted (const String& commandLine) override 50 | { 51 | // When another instance of the app is launched while this one is running, 52 | // this method is invoked, and the commandLine parameter tells you what 53 | // the other instance's command-line arguments were. 54 | } 55 | 56 | //============================================================================== 57 | /* 58 | This class implements the desktop window that contains an instance of 59 | our MainContentComponent class. 60 | */ 61 | class MainWindow : public DocumentWindow 62 | { 63 | public: 64 | MainWindow (String name) : DocumentWindow (name, 65 | Desktop::getInstance().getDefaultLookAndFeel() 66 | .findColour (ResizableWindow::backgroundColourId), 67 | DocumentWindow::allButtons) 68 | { 69 | setUsingNativeTitleBar (true); 70 | setContentOwned (createMainContentComponent(), true); 71 | setResizable (true, true); 72 | 73 | centreWithSize (getWidth(), getHeight()); 74 | setVisible (true); 75 | } 76 | 77 | void closeButtonPressed() override 78 | { 79 | // This is called when the user tries to close this window. Here, we'll just 80 | // ask the app to quit when this happens, but you can change this to do 81 | // whatever you need. 82 | JUCEApplication::getInstance()->systemRequestedQuit(); 83 | } 84 | 85 | /* Note: Be careful if you override any DocumentWindow methods - the base 86 | class uses a lot of them, so by overriding you might break its functionality. 87 | It's best to do all your work in your content component instead, but if 88 | you really have to override any DocumentWindow methods, make sure your 89 | subclass also calls the superclass's method. 90 | */ 91 | 92 | private: 93 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) 94 | }; 95 | 96 | private: 97 | ScopedPointer mainWindow; 98 | }; 99 | 100 | //============================================================================== 101 | // This macro generates the main() routine that launches the app. 102 | START_JUCE_APPLICATION (AnimationWriterApplication) 103 | -------------------------------------------------------------------------------- /examples/AnimationWriter/Source/MainComponent.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated! 5 | 6 | ============================================================================== 7 | */ 8 | 9 | #include "../JuceLibraryCode/JuceHeader.h" 10 | 11 | //============================================================================== 12 | /* 13 | This component lives inside our window, and this is where you should put all 14 | your controls and content. 15 | */ 16 | class MainContentComponent : public AnimatedAppComponent 17 | { 18 | public: 19 | //============================================================================== 20 | MainContentComponent() 21 | { 22 | counter = -1; 23 | setSize (800, 600); 24 | setFramesPerSecond (25); 25 | FFmpegVideoWriter writer; 26 | DBG (FFmpegVideoWriter::getOutputFormatNames().joinIntoString (";")); 27 | } 28 | 29 | ~MainContentComponent() 30 | { 31 | } 32 | 33 | void mouseDoubleClick (const MouseEvent&) override 34 | { 35 | if (!videoWriter) { 36 | FileChooser chooser ("Save Video File"); 37 | if (chooser.browseForFileToSave (true)) { 38 | videoCanvas = Image(Image::RGB, getWidth(), getHeight(), false); 39 | videoWriter = new FFmpegVideoWriter (); 40 | videoWriter->setVideoSize (getWidth(), getHeight()); 41 | videoWriter->setVideoCodec (AV_CODEC_ID_PROBE); 42 | videoWriter->setPixelAspect (1, 1); 43 | videoWriter->setPixelFormat (AV_PIX_FMT_YUV420P); 44 | videoWriter->setTimeBase (AVMEDIA_TYPE_VIDEO, av_make_q (1, 25)); 45 | videoWriter->openMovieFile (chooser.getResult()); 46 | counter = 0; 47 | } 48 | } 49 | } 50 | 51 | void update() override 52 | { 53 | // This function is called at the frequency specified by the setFramesPerSecond() call 54 | // in the constructor. You can use it to update counters, animate values, etc. 55 | if (videoWriter && counter >= 0) { 56 | if (counter > 240) { 57 | videoWriter->closeMovieFile(); 58 | videoWriter = nullptr; 59 | counter = -1; 60 | return; 61 | } 62 | Graphics g(videoCanvas); 63 | paint (g); 64 | // videoCanvas = createComponentSnapshot (getLocalBounds()); 65 | videoWriter->writeNextVideoFrame (videoCanvas, counter); 66 | 67 | ++counter; 68 | } 69 | } 70 | 71 | void paint (Graphics& g) override 72 | { 73 | // (Our component is opaque, so we must completely fill the background with a solid colour) 74 | g.fillAll (Colours::black); 75 | 76 | g.setColour (Colours::white); 77 | const int fishLength = 15; 78 | 79 | Path spinePath; 80 | 81 | for (int i = 0; i < fishLength; ++i) 82 | { 83 | const float radius = 100 + 10 * std::sin (getFrameCounter() * 0.1f + i * 0.5f); 84 | 85 | Point p (getWidth() / 2.0f + 1.5f * radius * std::sin (getFrameCounter() * 0.02f + i * 0.12f), 86 | getHeight() / 2.0f + 1.0f * radius * std::cos (getFrameCounter() * 0.04f + i * 0.12f)); 87 | 88 | // draw the circles along the fish 89 | g.fillEllipse (p.x - i, p.y - i, 2.0f + 2.0f * i, 2.0f + 2.0f * i); 90 | 91 | if (i == 0) 92 | spinePath.startNewSubPath (p); // if this is the first point, start a new path.. 93 | else 94 | spinePath.lineTo (p); // ...otherwise add the next point 95 | } 96 | 97 | // draw an outline around the path that we have created 98 | g.strokePath (spinePath, PathStrokeType (4.0f)); 99 | 100 | if (!videoWriter) 101 | g.setFont (14.0f); 102 | g.setColour (Colours::red); 103 | g.drawFittedText ("Double-click to start writing", getLocalBounds(), 104 | Justification::bottom, 1); 105 | } 106 | 107 | void resized() override 108 | { 109 | // This is called when the MainContentComponent is resized. 110 | // If you add any child components, this is where you should 111 | // update their positions. 112 | } 113 | 114 | 115 | private: 116 | //============================================================================== 117 | 118 | // Your private member variables go here... 119 | ScopedPointer videoWriter; 120 | Image videoCanvas; 121 | int64 counter; 122 | 123 | 124 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) 125 | }; 126 | 127 | 128 | // (This function is called by the app startup code to create our main component) 129 | Component* createMainContentComponent() { return new MainContentComponent(); } 130 | -------------------------------------------------------------------------------- /examples/VideoPlayer/.gitignore: -------------------------------------------------------------------------------- 1 | Builds/MacOSX/VideoPlayer.xcodeproj/xcuserdata/ 2 | Builds/MacOSX/VideoPlayer.xcodeproj/project.xcworkspace/ 3 | Builds/MacOSX/build/Debug/ 4 | Builds/MacOSX/build/Release/ 5 | Builds/MacOSX/build/VideoPlayer.build/ 6 | JuceLibraryCode/ 7 | 8 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/MacOSX/Info-App.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CFBundleExecutable 7 | ${EXECUTABLE_NAME} 8 | CFBundleIconFile 9 | 10 | CFBundleIdentifier 11 | com.yourcompany.VideoLibTest 12 | CFBundleName 13 | VideoPlayer 14 | CFBundleDisplayName 15 | VideoPlayer 16 | CFBundlePackageType 17 | APPL 18 | CFBundleSignature 19 | ???? 20 | CFBundleShortVersionString 21 | 1.0.0 22 | CFBundleVersion 23 | 1.0.0 24 | NSHumanReadableCopyright 25 | 26 | NSHighResolutionCapable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/MacOSX/RecentFilesMenuTemplate.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filmstro/filmstro_ffmpeg/ebb79f7713742311d09ac3f2215d807e88e3a517/examples/VideoPlayer/Builds/MacOSX/RecentFilesMenuTemplate.nib -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/VisualStudio2015/VideoPlayer (App).vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Debug 9 | Win32 10 | 11 | 12 | Release 13 | Win32 14 | 15 | 16 | 17 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6} 18 | v140 19 | 20 | 21 | 23 | Application 24 | false 25 | v140 26 | 27 | 29 | Application 30 | false 31 | true 32 | v140 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | v140 43 | 44 | 45 | <_ProjectFileVersion>10.0.30319.1 46 | .exe 47 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 48 | $(Platform)\$(Configuration)\App\ 49 | VideoLibTest 50 | true 51 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 52 | $(Platform)\$(Configuration)\App\ 53 | VideoLibTest 54 | true 55 | v140 56 | 57 | 58 | 59 | _DEBUG;%(PreprocessorDefinitions) 60 | true 61 | true 62 | Win32 63 | 64 | 65 | 66 | Disabled 67 | EditAndContinue 68 | ..\..\JuceLibraryCode;..\..\..\..\modules;..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 69 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 70 | MultiThreadedDebug 71 | true 72 | 73 | $(IntDir)\ 74 | $(IntDir)\ 75 | $(IntDir)\ 76 | Level4 77 | true 78 | true 79 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 80 | 81 | 82 | _DEBUG;%(PreprocessorDefinitions) 83 | 84 | 85 | $(OutDir)\VideoLibTest.exe 86 | true 87 | libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries) 88 | true 89 | $(IntDir)\VideoLibTest.pdb 90 | Windows 91 | MachineX86 92 | false 93 | true 94 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 95 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 96 | 97 | 98 | true 99 | $(IntDir)\VideoLibTest.bsc 100 | 101 | 102 | 103 | 104 | NDEBUG;%(PreprocessorDefinitions) 105 | true 106 | true 107 | Win32 108 | 109 | 110 | 111 | Full 112 | ..\..\JuceLibraryCode;..\..\..\..\modules;..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 113 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 114 | MultiThreaded 115 | true 116 | 117 | $(IntDir)\ 118 | $(IntDir)\ 119 | $(IntDir)\ 120 | Level4 121 | true 122 | true 123 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 124 | 125 | 126 | NDEBUG;%(PreprocessorDefinitions) 127 | 128 | 129 | $(OutDir)\VideoLibTest.exe 130 | true 131 | %(IgnoreSpecificDefaultLibraries) 132 | false 133 | $(IntDir)\VideoLibTest.pdb 134 | Windows 135 | MachineX86 136 | true 137 | true 138 | true 139 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 140 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 141 | 142 | 143 | true 144 | $(IntDir)\VideoLibTest.bsc 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/VisualStudio2015/VideoPlayer - App.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Debug 9 | Win32 10 | 11 | 12 | Release 13 | Win32 14 | 15 | 16 | 17 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6} 18 | v140 19 | 20 | 21 | 23 | Application 24 | false 25 | v140 26 | 27 | 29 | Application 30 | false 31 | true 32 | v140 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | v140 43 | 44 | 45 | <_ProjectFileVersion>10.0.30319.1 46 | .exe 47 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 48 | $(Platform)\$(Configuration)\App\ 49 | VideoLibTest 50 | true 51 | $(SolutionDir)\$(Platform)\$(Configuration)\App\ 52 | $(Platform)\$(Configuration)\App\ 53 | VideoLibTest 54 | true 55 | v140 56 | 57 | 58 | 59 | _DEBUG;%(PreprocessorDefinitions) 60 | true 61 | true 62 | Win32 63 | 64 | 65 | 66 | Disabled 67 | EditAndContinue 68 | ..\..\JuceLibraryCode;..\..\..\..\..\..\OpenSource;..\..\..\..\modules;..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 69 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 70 | MultiThreadedDebug 71 | true 72 | 73 | $(IntDir)\ 74 | $(IntDir)\ 75 | $(IntDir)\ 76 | Level4 77 | true 78 | true 79 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 80 | 81 | 82 | _DEBUG;%(PreprocessorDefinitions) 83 | 84 | 85 | $(OutDir)\VideoLibTest.exe 86 | true 87 | libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries) 88 | true 89 | $(IntDir)\VideoLibTest.pdb 90 | Windows 91 | MachineX86 92 | false 93 | true 94 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 95 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 96 | 97 | 98 | true 99 | $(IntDir)\VideoLibTest.bsc 100 | 101 | 102 | 103 | 104 | NDEBUG;%(PreprocessorDefinitions) 105 | true 106 | true 107 | Win32 108 | 109 | 110 | 111 | Full 112 | ..\..\JuceLibraryCode;..\..\..\..\..\..\OpenSource;..\..\..\..\modules;..\..\..\..\..\..\JUCE\modules;%(AdditionalIncludeDirectories) 113 | _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions) 114 | MultiThreaded 115 | true 116 | 117 | $(IntDir)\ 118 | $(IntDir)\ 119 | $(IntDir)\ 120 | Level4 121 | true 122 | true 123 | -I../../../../../../FFmpeg/build/windows/include %(AdditionalOptions) 124 | 125 | 126 | NDEBUG;%(PreprocessorDefinitions) 127 | 128 | 129 | $(OutDir)\VideoLibTest.exe 130 | true 131 | %(IgnoreSpecificDefaultLibraries) 132 | false 133 | $(IntDir)\VideoLibTest.pdb 134 | Windows 135 | MachineX86 136 | true 137 | true 138 | true 139 | avformat.lib;avutil.lib;avcodec.lib;swscale.lib;swresample.lib;%(AdditionalDependencies) 140 | -LIBPATH:../../../../../../FFmpeg/build/windows/bin %(AdditionalOptions) 141 | 142 | 143 | true 144 | $(IntDir)\VideoLibTest.bsc 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/VisualStudio2015/VideoPlayer.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 11.00 2 | # Visual Studio 2015 3 | 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoPlayer - App", "VideoPlayer_App.vcxproj", "{1F76C5AB-42A2-1C31-68E7-71A7C91907F6}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6}.Debug|Win32.Build.0 = Debug|Win32 14 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6}.Release|Win32.ActiveCfg = Release|Win32 15 | {1F76C5AB-42A2-1C31-68E7-71A7C91907F6}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Builds/VisualStudio2015/resources.rc: -------------------------------------------------------------------------------- 1 | #ifdef JUCE_USER_DEFINED_RC_FILE 2 | #include JUCE_USER_DEFINED_RC_FILE 3 | #else 4 | 5 | #undef WIN32_LEAN_AND_MEAN 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | VS_VERSION_INFO VERSIONINFO 10 | FILEVERSION 1,0,0,0 11 | BEGIN 12 | BLOCK "StringFileInfo" 13 | BEGIN 14 | BLOCK "040904E4" 15 | BEGIN 16 | VALUE "FileDescription", "VideoPlayer\0" 17 | VALUE "FileVersion", "1.0.0\0" 18 | VALUE "ProductName", "VideoPlayer\0" 19 | VALUE "ProductVersion", "1.0.0\0" 20 | END 21 | END 22 | 23 | BLOCK "VarFileInfo" 24 | BEGIN 25 | VALUE "Translation", 0x409, 1252 26 | END 27 | END 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Source/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 27 | OF THE POSSIBILITY OF SUCH DAMAGE. 28 | ============================================================================== 29 | 30 | This contains the basic startup code for a Juce application. 31 | 32 | ============================================================================== 33 | */ 34 | 35 | #include "../JuceLibraryCode/JuceHeader.h" 36 | 37 | Component* createMainContentComponent(); 38 | 39 | //============================================================================== 40 | class VideoPlayerApplication : public JUCEApplication 41 | { 42 | public: 43 | //============================================================================== 44 | VideoPlayerApplication() {} 45 | 46 | const String getApplicationName() override { return ProjectInfo::projectName; } 47 | const String getApplicationVersion() override { return ProjectInfo::versionString; } 48 | bool moreThanOneInstanceAllowed() override { return true; } 49 | 50 | //============================================================================== 51 | void initialise (const String& commandLine) override 52 | { 53 | // This method is where you should put your application's initialisation code.. 54 | 55 | mainWindow = new MainWindow (getApplicationName()); 56 | } 57 | 58 | void shutdown() override 59 | { 60 | // Add your application's shutdown code here.. 61 | 62 | mainWindow = nullptr; // (deletes our window) 63 | } 64 | 65 | //============================================================================== 66 | void systemRequestedQuit() override 67 | { 68 | // This is called when the app is being asked to quit: you can ignore this 69 | // request and let the app carry on running, or call quit() to allow the app to close. 70 | quit(); 71 | } 72 | 73 | void anotherInstanceStarted (const String& commandLine) override 74 | { 75 | // When another instance of the app is launched while this one is running, 76 | // this method is invoked, and the commandLine parameter tells you what 77 | // the other instance's command-line arguments were. 78 | } 79 | 80 | //============================================================================== 81 | /* 82 | This class implements the desktop window that contains an instance of 83 | our MainContentComponent class. 84 | */ 85 | class MainWindow : public DocumentWindow 86 | { 87 | public: 88 | MainWindow (String name) : DocumentWindow (name, 89 | Colours::lightgrey, 90 | DocumentWindow::allButtons) 91 | { 92 | setUsingNativeTitleBar (true); 93 | setContentOwned (createMainContentComponent(), true); 94 | setResizable (true, true); 95 | 96 | centreWithSize (getWidth(), getHeight()); 97 | setVisible (true); 98 | } 99 | 100 | void closeButtonPressed() override 101 | { 102 | // This is called when the user tries to close this window. Here, we'll just 103 | // ask the app to quit when this happens, but you can change this to do 104 | // whatever you need. 105 | JUCEApplication::getInstance()->systemRequestedQuit(); 106 | } 107 | 108 | /* Note: Be careful if you override any DocumentWindow methods - the base 109 | class uses a lot of them, so by overriding you might break its functionality. 110 | It's best to do all your work in your content component instead, but if 111 | you really have to override any DocumentWindow methods, make sure your 112 | subclass also calls the superclass's method. 113 | */ 114 | 115 | private: 116 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) 117 | }; 118 | 119 | private: 120 | ScopedPointer mainWindow; 121 | }; 122 | 123 | //============================================================================== 124 | // This macro generates the main() routine that launches the app. 125 | START_JUCE_APPLICATION (VideoPlayerApplication) 126 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Source/MainComponent.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | 29 | Demo to play back a movie using ffmpeg and JUCE 30 | 31 | ============================================================================== 32 | */ 33 | 34 | #ifndef MAINCOMPONENT_H_INCLUDED 35 | #define MAINCOMPONENT_H_INCLUDED 36 | 37 | #include "../JuceLibraryCode/JuceHeader.h" 38 | 39 | #include "OSDComponent.h" 40 | 41 | //============================================================================== 42 | /* 43 | This component lives inside our window, and this is where you should put all 44 | your controls and content. 45 | */ 46 | class VideoComponentWithDropper : public FFmpegVideoComponent, 47 | public FileDragAndDropTarget 48 | { 49 | public: 50 | VideoComponentWithDropper (FFmpegVideoReader* readerToOpenFiles) 51 | { 52 | setWantsKeyboardFocus (false); 53 | setVideoReader (readerToOpenFiles); 54 | } 55 | 56 | virtual ~VideoComponentWithDropper () {} 57 | 58 | bool isInterestedInFileDrag (const StringArray &files) override 59 | { 60 | return true; 61 | } 62 | 63 | void filesDropped (const StringArray &files, int x, int y) override 64 | { 65 | if (FFmpegVideoReader* reader = getVideoReader()) { 66 | File fileToOpen (files [0]); 67 | reader->loadMovieFile (fileToOpen); 68 | Process::makeForegroundProcess (); 69 | } 70 | } 71 | }; 72 | 73 | //============================================================================== 74 | /* 75 | This component lives inside our window, and this is where you should put all 76 | your controls and content. 77 | */ 78 | class MainContentComponent : public AudioAppComponent, 79 | public FFmpegVideoListener 80 | { 81 | public: 82 | //============================================================================== 83 | MainContentComponent() 84 | { 85 | setWantsKeyboardFocus (true); 86 | videoAspectRatio = 1.77; 87 | 88 | videoReader = new FFmpegVideoReader (384000, 30); 89 | videoReader->addVideoListener (this); 90 | 91 | transportSource = new AudioTransportSource (); 92 | transportSource->setSource (videoReader, 0, nullptr); 93 | 94 | videoComponent = new VideoComponentWithDropper (videoReader); 95 | addAndMakeVisible (videoComponent); 96 | 97 | osdComponent = new OSDComponent (videoReader, transportSource); 98 | addAndMakeVisible (osdComponent); 99 | 100 | // specify the number of input and output channels that we want to open 101 | setAudioChannels (0, 2); 102 | 103 | #ifdef DEBUG 104 | if (AudioIODevice* device = deviceManager.getCurrentAudioDevice()) { 105 | DBG ("Current Samplerate: " + String (device->getCurrentSampleRate())); 106 | DBG ("Current Buffersize: " + String (device->getCurrentBufferSizeSamples())); 107 | DBG ("Current Bitdepth: " + String (device->getCurrentBitDepth())); 108 | } 109 | #endif /* DEBUG */ 110 | 111 | #ifdef USE_FF_AUDIO_METERS 112 | meter = new LevelMeter (); 113 | meter->getLookAndFeel()->setMeterColour (LevelMeterLookAndFeel::lmBackgroundColour, 114 | Colour::fromFloatRGBA (0.0f, 0.0f, 0.0f, 0.6f)); 115 | meter->setMeterSource (&meterSource); 116 | addAndMakeVisible (meter); 117 | #endif 118 | 119 | setSize (800, 600); 120 | } 121 | 122 | ~MainContentComponent() 123 | { 124 | shutdownAudio(); 125 | } 126 | 127 | //============================================================================== 128 | void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override 129 | { 130 | // This function will be called when the audio device is started, or when 131 | // its settings (i.e. sample rate, block size, etc) are changed. 132 | if (videoReader) videoReader->prepareToPlay (samplesPerBlockExpected, sampleRate); 133 | if (transportSource) transportSource->prepareToPlay (samplesPerBlockExpected, sampleRate); 134 | 135 | readBuffer.setSize (2, samplesPerBlockExpected); 136 | } 137 | 138 | void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override 139 | { 140 | auto numInputChannels = videoReader->getVideoChannels(); 141 | 142 | AudioSourceChannelInfo info (&readBuffer, 143 | bufferToFill.startSample, 144 | bufferToFill.numSamples); 145 | // the AudioTransportSource takes care of start, stop and resample 146 | transportSource->getNextAudioBlock (info); 147 | 148 | #ifdef USE_FF_AUDIO_METERS 149 | meterSource.measureBlock (readBuffer); 150 | #endif 151 | 152 | if (numInputChannels > 0) 153 | { 154 | for (int i=0; i < bufferToFill.buffer->getNumChannels(); ++i) { 155 | 156 | bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, 157 | readBuffer.getReadPointer (i % numInputChannels), 158 | bufferToFill.numSamples); 159 | if (bufferToFill.buffer->getNumChannels() == 2 && 160 | readBuffer.getNumChannels() > 2) { 161 | // add center to left and right 162 | bufferToFill.buffer->addFrom (i, bufferToFill.startSample, 163 | readBuffer.getReadPointer (2), 164 | bufferToFill.numSamples, 0.7); 165 | } 166 | } 167 | } 168 | else{ 169 | bufferToFill.clearActiveBufferRegion(); 170 | } 171 | } 172 | 173 | void releaseResources() override 174 | { 175 | transportSource->releaseResources (); 176 | videoReader->releaseResources (); 177 | } 178 | 179 | //============================================================================== 180 | /** Reset gui when a new file is loaded */ 181 | void videoFileChanged (const juce::File& video) override 182 | { 183 | String abbrev (video.getFullPathName()); 184 | if (abbrev.length() > 30) 185 | abbrev = "(...)" + abbrev.substring (abbrev.length() - 30); 186 | DBG ("===================================================="); 187 | DBG ("Loaded file : " + abbrev); 188 | DBG ("Channels: " + String (videoReader->getVideoChannels())); 189 | DBG ("Duration (sec): " + String (videoReader->getVideoDuration())); 190 | DBG ("Framerate (1/sec): " + String (videoReader->getFramesPerSecond())); 191 | DBG ("SampleRate: " + String (videoReader->getVideoSamplingRate())); 192 | DBG ("SampleFormat: " + String (av_get_sample_fmt_name (videoReader->getSampleFormat()))); 193 | DBG ("Width: " + String (videoReader->getVideoWidth())); 194 | DBG ("Height: " + String (videoReader->getVideoHeight())); 195 | DBG ("Pixel format: " + String (av_get_pix_fmt_name (videoReader->getPixelFormat()))); 196 | DBG ("Pixel aspect ratio:" + String (videoReader->getVideoPixelAspect())); 197 | DBG ("===================================================="); 198 | 199 | osdComponent->setVideoLength (videoReader->getVideoDuration ()); 200 | 201 | transportSource->setSource (videoReader, 0, nullptr, videoReader->getVideoSamplingRate(), videoReader->getVideoChannels()); 202 | readBuffer.setSize (videoReader->getVideoChannels(), readBuffer.getNumSamples()); 203 | 204 | videoAspectRatio = videoReader->getVideoAspectRatio (); 205 | resized (); 206 | 207 | if (AudioIODevice* device = deviceManager.getCurrentAudioDevice()) { 208 | videoReader->prepareToPlay (device->getCurrentBufferSizeSamples(), 209 | device->getCurrentSampleRate()); 210 | readBuffer.setSize (videoReader->getVideoChannels(), 211 | device->getCurrentBufferSizeSamples()); 212 | 213 | } 214 | } 215 | 216 | void presentationTimestampChanged (const double pts) override 217 | { 218 | MessageManager::callAsync (std::bind (&OSDComponent::setCurrentTime, 219 | osdComponent.get(), 220 | videoReader->getCurrentTimeStamp())); 221 | } 222 | 223 | void paint (Graphics& g) override 224 | { 225 | g.fillAll (Colours::black); 226 | } 227 | 228 | void resized() override 229 | { 230 | videoComponent->setBounds (getBounds()); 231 | osdComponent->setBounds (getBounds()); 232 | 233 | #ifdef USE_FF_AUDIO_METERS 234 | const int w = 30 + 20 * videoReader->getVideoChannels(); 235 | meter->setBounds (getWidth() - w, getHeight() - 240, w, 200); 236 | #endif 237 | } 238 | 239 | bool keyPressed (const KeyPress &key) override 240 | { 241 | if (key == KeyPress::spaceKey) { 242 | if (transportSource->isPlaying()) { 243 | transportSource->stop(); 244 | return true; 245 | } 246 | transportSource->start(); 247 | return true; 248 | } 249 | return false; 250 | } 251 | 252 | private: 253 | //============================================================================== 254 | 255 | // Your private member variables go here... 256 | ScopedPointer videoComponent; 257 | ScopedPointer videoReader; 258 | ScopedPointer osdComponent; 259 | ScopedPointer transportSource; 260 | 261 | #ifdef USE_FF_AUDIO_METERS 262 | ScopedPointer meter; 263 | LevelMeterSource meterSource; 264 | #endif 265 | 266 | AudioSampleBuffer readBuffer; 267 | 268 | double videoAspectRatio; 269 | 270 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) 271 | }; 272 | 273 | 274 | // (This function is called by the app startup code to create our main component) 275 | Component* createMainContentComponent() { return new MainContentComponent(); } 276 | 277 | 278 | #endif // MAINCOMPONENT_H_INCLUDED 279 | -------------------------------------------------------------------------------- /examples/VideoPlayer/Source/OSDComponent.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | 29 | Overlay component 30 | 31 | ============================================================================== 32 | */ 33 | 34 | 35 | #ifndef OSDCOMPONENT_H_INCLUDED 36 | #define OSDCOMPONENT_H_INCLUDED 37 | 38 | #include "../JuceLibraryCode/JuceHeader.h" 39 | 40 | //============================================================================== 41 | /* 42 | */ 43 | class OSDComponent : public Component, 44 | public Slider::Listener, 45 | public Button::Listener 46 | { 47 | public: 48 | OSDComponent (FFmpegVideoReader* readerToControl, AudioTransportSource* transportToControl) 49 | : videoReader (readerToControl), transport (transportToControl) 50 | { 51 | ffwdSpeed = 2; 52 | 53 | setInterceptsMouseClicks (false, true); 54 | setWantsKeyboardFocus (false); 55 | 56 | openFile = new TextButton ("Open", "Open"); 57 | openFile->addListener (this); 58 | openFile->setWantsKeyboardFocus (false); 59 | addAndMakeVisible (openFile); 60 | flexBox.items.add (FlexItem (*openFile).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 61 | 62 | saveFile = new TextButton ("Save", "Save"); 63 | saveFile->addListener (this); 64 | saveFile->setWantsKeyboardFocus (false); 65 | addAndMakeVisible (saveFile); 66 | flexBox.items.add (FlexItem (*saveFile).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 67 | 68 | seekBar = new Slider (Slider::LinearHorizontal, Slider::NoTextBox); 69 | addAndMakeVisible (seekBar); 70 | seekBar->addListener (this); 71 | seekBar->setWantsKeyboardFocus (false); 72 | flexBox.items.add (FlexItem (*seekBar).withFlex (6.0, 1.0, 0.5).withHeight (20.0)); 73 | 74 | stop = new TextButton ("Stop", "Stop"); 75 | stop->addListener (this); 76 | stop->setWantsKeyboardFocus (false); 77 | addAndMakeVisible (stop); 78 | flexBox.items.add (FlexItem (*stop).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 79 | 80 | pause = new TextButton ("Pause", "Pause"); 81 | pause->addListener (this); 82 | pause->setWantsKeyboardFocus (false); 83 | addAndMakeVisible (pause); 84 | flexBox.items.add (FlexItem (*pause).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 85 | 86 | play = new TextButton ("Play", "Play"); 87 | play->addListener (this); 88 | play->setWantsKeyboardFocus (false); 89 | addAndMakeVisible (play); 90 | flexBox.items.add (FlexItem (*play).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 91 | 92 | ffwd = new TextButton ("FFWD", "FFWD"); 93 | ffwd->addListener (this); 94 | ffwd->setWantsKeyboardFocus (false); 95 | addAndMakeVisible (ffwd); 96 | flexBox.items.add (FlexItem (*ffwd).withFlex (1.0, 1.0, 0.5).withHeight (20.0)); 97 | 98 | openFile->setConnectedEdges (TextButton::ConnectedOnRight); 99 | saveFile->setConnectedEdges (TextButton::ConnectedOnLeft); 100 | 101 | stop->setConnectedEdges (TextButton::ConnectedOnRight); 102 | pause->setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft); 103 | play->setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft); 104 | ffwd->setConnectedEdges (TextButton::ConnectedOnLeft); 105 | 106 | idle = new MouseIdle (*this); 107 | } 108 | 109 | ~OSDComponent() 110 | { 111 | } 112 | 113 | void paint (Graphics& g) override 114 | { 115 | if (videoReader && videoReader->getVideoDuration() > 0) { 116 | g.setColour (Colours::white); 117 | g.setFont (24); 118 | String dim = String (videoReader->getVideoWidth()) + " x " + String (videoReader->getVideoHeight()); 119 | g.drawFittedText (dim, getLocalBounds(), Justification::topLeft, 1); 120 | g.drawFittedText (FFmpegVideoReader::formatTimeCode (videoReader->getCurrentTimeStamp ()), 121 | getLocalBounds(), Justification::topRight, 1); 122 | } 123 | } 124 | 125 | void resized() override 126 | { 127 | Rectangle bounds = getBounds().withTop (getHeight() - 40); 128 | flexBox.performLayout (bounds); 129 | } 130 | 131 | void setCurrentTime (const double time) 132 | { 133 | seekBar->setValue (time, dontSendNotification); 134 | } 135 | 136 | void setVideoLength (const double length) 137 | { 138 | seekBar->setRange (0.0, length); 139 | } 140 | 141 | /** React to slider changes with seeking */ 142 | void sliderValueChanged (juce::Slider* slider) override 143 | { 144 | if (slider == seekBar) { 145 | videoReader->setNextReadPosition (slider->getValue() * videoReader->getVideoSamplingRate()); 146 | } 147 | } 148 | 149 | void buttonClicked (Button* b) override 150 | { 151 | if (b == openFile) { 152 | transport->stop(); 153 | FileChooser chooser ("Open Video File"); 154 | if (chooser.browseForFileToOpen()) { 155 | File video = chooser.getResult(); 156 | videoReader->loadMovieFile (video); 157 | 158 | } 159 | } 160 | else if (b == play) { 161 | if (ffwdSpeed != 2) { 162 | int64 lastPos = videoReader->getNextReadPosition(); 163 | ffwdSpeed = 2; 164 | double factor = 0.5 + (ffwdSpeed / 4.0); 165 | transport->setSource (videoReader, 0, nullptr, videoReader->getVideoSamplingRate() * factor, videoReader->getVideoChannels()); 166 | videoReader->setNextReadPosition (lastPos); 167 | } 168 | transport->start(); 169 | } 170 | else if (b == stop) { 171 | transport->stop(); 172 | videoReader->setNextReadPosition (0); 173 | } 174 | else if (b == pause) { 175 | transport->stop(); 176 | } 177 | else if (b == ffwd) { 178 | int64 lastPos = videoReader->getNextReadPosition(); 179 | ffwdSpeed = ++ffwdSpeed % 7; 180 | double factor = 0.5 + (ffwdSpeed / 4.0); 181 | transport->setSource (videoReader, 0, nullptr, videoReader->getVideoSamplingRate() * factor, videoReader->getVideoChannels()); 182 | videoReader->setNextReadPosition (lastPos); 183 | transport->start (); 184 | 185 | } 186 | else if (b == saveFile) { 187 | transport->stop(); 188 | FileChooser chooser ("Save Video File"); 189 | if (chooser.browseForFileToSave (true)) { 190 | File saveFileName = chooser.getResult(); 191 | 192 | FFmpegVideoReader copyReader; 193 | 194 | FFmpegVideoWriter writer; 195 | copyReader.addVideoListener (&writer); 196 | copyReader.loadMovieFile (videoReader->getVideoFileName()); 197 | copyReader.prepareToPlay (1024, videoReader->getVideoSamplingRate()); 198 | 199 | AVRational videoTimeBase = copyReader.getVideoTimeBase(); 200 | if (videoTimeBase.num > 0) { 201 | writer.setTimeBase (AVMEDIA_TYPE_VIDEO, videoTimeBase); 202 | } 203 | writer.setVideoCodec (AV_CODEC_ID_PROBE); 204 | writer.setAudioCodec (AV_CODEC_ID_PROBE); 205 | writer.setVideoSize (copyReader.getVideoWidth(), copyReader.getVideoHeight()); 206 | writer.setPixelFormat (copyReader.getPixelFormat()); 207 | writer.openMovieFile (saveFileName); 208 | 209 | AudioBuffer buffer; 210 | buffer.setSize (2, 1024); 211 | 212 | while (copyReader.getCurrentTimeStamp() < copyReader.getVideoDuration()) { 213 | AudioSourceChannelInfo info (&buffer, 0, 1024); 214 | copyReader.waitForNextAudioBlockReady (info, 500); 215 | copyReader.getNextAudioBlock (info); 216 | 217 | writer.writeNextAudioBlock (info); 218 | } 219 | writer.closeMovieFile (); 220 | 221 | copyReader.removeVideoListener (&writer); 222 | } 223 | } 224 | } 225 | 226 | class MouseIdle : public MouseListener, public Timer 227 | { 228 | public: 229 | MouseIdle (Component& c) : 230 | component (c), 231 | lastMovement (Time::getMillisecondCounter()) 232 | { 233 | Desktop::getInstance().addGlobalMouseListener (this); 234 | startTimerHz (20); 235 | } 236 | 237 | void timerCallback () override 238 | { 239 | const int64 relTime = Time::getMillisecondCounter() - lastMovement; 240 | if (relTime < 2000) { 241 | component.setVisible (true); 242 | component.setAlpha (1.0); 243 | if (Component* parent = component.getParentComponent()) 244 | parent->setMouseCursor (MouseCursor::StandardCursorType::NormalCursor); 245 | } 246 | else if (relTime < 2300) { 247 | component.setAlpha (1.0 - jmax (0.0, (relTime - 2000.0) / 300.0)); 248 | } 249 | else { 250 | component.setVisible (false); 251 | if (Component* parent = component.getParentComponent()) { 252 | parent->setMouseCursor (MouseCursor::StandardCursorType::NoCursor); 253 | Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); 254 | } 255 | } 256 | } 257 | 258 | void mouseMove (const MouseEvent &event) override 259 | { 260 | if (event.position.getDistanceFrom (lastPosition) > 3.0) { 261 | lastMovement = Time::getMillisecondCounter(); 262 | lastPosition = event.position; 263 | } 264 | } 265 | private: 266 | Component& component; 267 | int64 lastMovement; 268 | Point lastPosition; 269 | }; 270 | 271 | private: 272 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSDComponent) 273 | 274 | FlexBox flexBox; 275 | 276 | ScopedPointer idle; 277 | ScopedPointer seekBar; 278 | ScopedPointer openFile; 279 | ScopedPointer saveFile; 280 | ScopedPointer play; 281 | ScopedPointer pause; 282 | ScopedPointer stop; 283 | ScopedPointer ffwd; 284 | int ffwdSpeed; 285 | FFmpegVideoReader* videoReader; 286 | 287 | AudioTransportSource* transport; 288 | }; 289 | 290 | 291 | #endif // OSDCOMPONENT_H_INCLUDED 292 | -------------------------------------------------------------------------------- /examples/VideoPlayer/VideoPlayer.jucer: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /modules/filmstro_audiohelpers/filmstro_audiohelpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017 Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | 29 | BEGIN_JUCE_MODULE_DECLARATION 30 | 31 | ID: filmstro_audiohelpers 32 | vendor: Filmstro Ltd. 33 | version: 0.9.0 34 | name: Basic audio helper classes 35 | description: Audio helper classes of general purpose 36 | dependencies: juce_audio_basics, juce_audio_formats, juce_audio_devices, juce_audio_processors 37 | website: http://www.filmstro.com/ 38 | license: BSD V2 3-clause 39 | 40 | END_JUCE_MODULE_DECLARATION 41 | 42 | ============================================================================== 43 | */ 44 | 45 | 46 | #ifndef FILMSTRO_AUDIOHELPERS_INCLUDED_H 47 | #define FILMSTRO_AUDIOHELPERS_INCLUDED_H 48 | 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "filmstro_audiohelpers/filmstro_audiohelpers_AudioBufferFIFO.h" 56 | #include "filmstro_audiohelpers/filmstro_audiohelpers_AudioProcessorPlayerSource.h" 57 | #include "filmstro_audiohelpers/filmstro_audiohelpers_OutputSourcePlayer.h" 58 | #include "filmstro_audiohelpers/filmstro_audiohelpers_SharedFormatManager.h" 59 | 60 | #endif /* FILMSTRO_AUDIOHELPERS_INCLUDED_H */ 61 | 62 | -------------------------------------------------------------------------------- /modules/filmstro_audiohelpers/filmstro_audiohelpers_AudioBufferFIFO.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class AudioBufferFIFO 29 | \file filmstro_audiobasics_AudioBufferFIFO.h 30 | \brief A FIFO for audio samples 31 | 32 | \author Daniel Walz / Filmstro Ltd. 33 | \date September 7th 2016 34 | 35 | \description A FIFO for AudioBuffer / samples 36 | 37 | ============================================================================== 38 | */ 39 | 40 | #ifndef FSPRO_AUDIOBASICS_AUDIOBUFFERFIFO_H_INCLUDED 41 | #define FSPRO_AUDIOBASICS_AUDIOBUFFERFIFO_H_INCLUDED 42 | 43 | /** 44 | The AudioBufferFIFO implements an actual sample buffer using JUCEs AbstractFIFO 45 | class. You can add samples from the various kind of formats, like float pointers 46 | or AudioBuffers. Then you can read into float arrays, AudioBuffers or even 47 | AudioSourceChannelInfo to be used directly in AudioSources. 48 | */ 49 | template 50 | class AudioBufferFIFO : public juce::AbstractFifo 51 | { 52 | public: 53 | /*< Creates a FIFO with a buffer of given number of channels and given number of samples */ 54 | AudioBufferFIFO (int channels, int buffersize) : 55 | AbstractFifo (buffersize) 56 | { 57 | buffer.setSize (channels, buffersize); 58 | } 59 | 60 | /*< Resize the buffer with new number of channels and new number of samples */ 61 | void setSize (const int channels, const int newBufferSize) 62 | { 63 | buffer.setSize (channels, newBufferSize); 64 | setTotalSize (newBufferSize); 65 | reset (); 66 | } 67 | 68 | /*< Push samples into the FIFO from raw float arrays */ 69 | void addToFifo (const FloatType** samples, int numSamples) 70 | { 71 | jassert (getFreeSpace() >= numSamples); 72 | int start1, size1, start2, size2; 73 | prepareToWrite (numSamples, start1, size1, start2, size2); 74 | if (size1 > 0) 75 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 76 | buffer.copyFrom (channel, start1, samples[channel], size1); 77 | if (size2 > 0) 78 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 79 | buffer.copyFrom (channel, start2, samples[channel] + size1, size2); 80 | finishedWrite (size1 + size2); 81 | } 82 | 83 | /*< Push samples from an AudioBuffer into the FIFO */ 84 | void addToFifo (const juce::AudioBuffer& samples, int numSamples = -1, int sourceOffset = 0) 85 | { 86 | const int addSamples = (numSamples < 0 ? samples.getNumSamples() : numSamples) - sourceOffset; 87 | jassert (getFreeSpace() >= addSamples); 88 | 89 | int start1, size1, start2, size2; 90 | prepareToWrite (addSamples, start1, size1, start2, size2); 91 | if (size1 > 0) 92 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 93 | buffer.copyFrom (channel, start1, samples.getReadPointer (channel, sourceOffset), size1); 94 | if (size2 > 0) 95 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 96 | buffer.copyFrom (channel, start2, samples.getReadPointer (channel, sourceOffset + size1), size2); 97 | finishedWrite (size1 + size2); 98 | 99 | } 100 | 101 | /*< Read samples from the FIFO into raw float arrays */ 102 | void readFromFifo (FloatType** samples, int numSamples) 103 | { 104 | jassert (getNumReady() >= numSamples); 105 | int start1, size1, start2, size2; 106 | prepareToRead (numSamples, start1, size1, start2, size2); 107 | if (size1 > 0) 108 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 109 | juce::FloatVectorOperations::copy (samples [channel], 110 | buffer.getReadPointer (channel, start1), 111 | size1); 112 | if (size2 > 0) 113 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 114 | juce::FloatVectorOperations::copy (samples [channel] + size1, 115 | buffer.getReadPointer (channel, start2), 116 | size2); 117 | finishedRead (size1 + size2); 118 | } 119 | 120 | /*< Read samples from the FIFO into AudioBuffers */ 121 | void readFromFifo (juce::AudioBuffer& samples, int numSamples=-1) 122 | { 123 | const int readSamples = numSamples > 0 ? numSamples : samples.getNumSamples(); 124 | jassert (getNumReady() >= readSamples); 125 | 126 | int start1, size1, start2, size2; 127 | prepareToRead (readSamples, start1, size1, start2, size2); 128 | if (size1 > 0) 129 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 130 | samples.copyFrom (channel, 0, buffer.getReadPointer (channel, start1), size1); 131 | if (size2 > 0) 132 | for (int channel = 0; channel < buffer.getNumChannels(); ++channel) 133 | samples.copyFrom (channel, size1, buffer.getReadPointer (channel, start2), size2); 134 | finishedRead (size1 + size2); 135 | } 136 | 137 | /*< Read samples from the FIFO into AudioSourceChannelInfo buffers to be used in AudioSources getNextAudioBlock */ 138 | void readFromFifo (const juce::AudioSourceChannelInfo& info, int numSamples=-1) 139 | { 140 | const int readSamples = numSamples > 0 ? numSamples : info.numSamples; 141 | jassert (getNumReady() >= readSamples); 142 | 143 | int start1, size1, start2, size2; 144 | prepareToRead (readSamples, start1, size1, start2, size2); 145 | if (size1 > 0) 146 | for (int channel = 0; channel < info.buffer->getNumChannels(); ++channel) 147 | info.buffer->copyFrom (channel, info.startSample, buffer.getReadPointer (channel, start1), size1); 148 | if (size2 > 0) 149 | for (int channel = 0; channel < info.buffer->getNumChannels(); ++channel) 150 | info.buffer->copyFrom (channel, info.startSample + size1, buffer.getReadPointer (channel, start2), size2); 151 | finishedRead (size1 + size2); 152 | } 153 | 154 | /*< Returns the number of channels of the underlying buffer */ 155 | int getNumChannels () const { 156 | return buffer.getNumChannels(); 157 | } 158 | 159 | /*< Clears all samples and sets the FIFO state to empty */ 160 | void clear () { 161 | buffer.clear (); 162 | reset(); 163 | } 164 | 165 | private: 166 | /*< The actual audio buffer */ 167 | juce::AudioBuffer buffer; 168 | }; 169 | 170 | 171 | 172 | 173 | #endif /* FSPRO_AUDIOBASICS_AUDIOBUFFERFIFO_H_INCLUDED */ 174 | 175 | -------------------------------------------------------------------------------- /modules/filmstro_audiohelpers/filmstro_audiohelpers_AudioProcessorPlayerSource.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class AudioProcessorPlayerSource 29 | \file filmstro_audiobasics_AudioProcessorPlayerSource.h 30 | \brief An AudioSource to playback audioProcessors 31 | 32 | \author Daniel Walz / Filmstro Ltd. 33 | \date December 15th 2016 34 | 35 | \description An AudioSource playing back an AudioProcessor 36 | 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef filmstro_audiobasics_AudioProcessorPlayerSource_h 42 | #define filmstro_audiobasics_AudioProcessorPlayerSource_h 43 | 44 | class AudioProcessorPlayerSource : public juce::AudioSource 45 | { 46 | public: 47 | AudioProcessorPlayerSource (juce::AudioProcessor* proc, const int channels = 2, 48 | const bool deleteProcessor = true) 49 | : processor (proc, deleteProcessor), 50 | numChannels (channels), 51 | fifo (channels, 4096) 52 | { 53 | 54 | } 55 | 56 | virtual ~AudioProcessorPlayerSource () 57 | { 58 | } 59 | 60 | void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override 61 | { 62 | buffer.setSize (numChannels, samplesPerBlockExpected); 63 | if (processor) { 64 | buffer.setSize (numChannels, samplesPerBlockExpected); 65 | processor->prepareToPlay (sampleRate, samplesPerBlockExpected); 66 | } 67 | } 68 | 69 | void releaseResources () override 70 | { 71 | if (processor) { 72 | processor->releaseResources (); 73 | } 74 | } 75 | 76 | void setProcessor (juce::AudioProcessor* proc, const bool deleteProcessor) 77 | { 78 | fifo.clear (); 79 | processor.set (proc, deleteProcessor); 80 | } 81 | 82 | juce::AudioProcessor* getCurrentProcessor () const 83 | { 84 | return processor; 85 | } 86 | 87 | void getNextAudioBlock (const juce::AudioSourceChannelInfo &bufferToFill) override 88 | { 89 | if (processor) { 90 | juce::MidiBuffer midiBuffer; 91 | while (fifo.getNumReady() < bufferToFill.numSamples) { 92 | processor->processBlock (buffer, midiBuffer); 93 | fifo.addToFifo (buffer); 94 | } 95 | const int ready = fifo.getNumReady (); 96 | if (ready >= bufferToFill.numSamples) 97 | { 98 | fifo.readFromFifo (bufferToFill); 99 | } 100 | else if (ready > 0) { 101 | fifo.readFromFifo (bufferToFill, ready); 102 | bufferToFill.buffer->clear (bufferToFill.startSample + ready, bufferToFill.numSamples - ready); 103 | } 104 | else { 105 | bufferToFill.clearActiveBufferRegion (); 106 | } 107 | } 108 | else { 109 | bufferToFill.clearActiveBufferRegion(); 110 | } 111 | } 112 | 113 | private: 114 | 115 | juce::OptionalScopedPointer processor; 116 | 117 | int numChannels; 118 | 119 | // buffer some audio data 120 | AudioBufferFIFO fifo; 121 | 122 | // processor buffer 123 | juce::AudioBuffer buffer; 124 | }; 125 | 126 | 127 | 128 | #endif /* filmstro_audiobasics_AudioProcessorPlayerSource_h */ 129 | -------------------------------------------------------------------------------- /modules/filmstro_audiohelpers/filmstro_audiohelpers_OutputSourcePlayer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class OutputSourcePlayer 29 | \file filmstro_audiobasics_OutputSourcePlayer.h 30 | \brief An AudioSourcePlayer which handles changes of the sample rate 31 | 32 | \author Daniel Walz / Filmstro Ltd. 33 | \date December 19th 2016 34 | 35 | \description An AudioSourcePlayer which handles changes of the sample rate 36 | 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef filmstro_audiobasics_OutputSourcePlayer_h 42 | #define filmstro_audiobasics_OutputSourcePlayer_h 43 | 44 | /** 45 | This class provides an AudioSourcePlayer, which will adapt its resampler whenever 46 | the device changes the sampling rate 47 | */ 48 | class OutputSourcePlayer : public juce::AudioSourcePlayer 49 | { 50 | public: 51 | /** 52 | Constructs an OutputSourcePlayer. This makes the processing independent from the devices playback rate. 53 | \param sampleRate is the sample rate the playing AudioSource would expect 54 | \param channels is the number of channels for the resampler to provide. Defaults to 2 (stereo) 55 | */ 56 | OutputSourcePlayer (double sampleRate, int channels=2) 57 | : internalSampleRate (sampleRate), 58 | deviceSampleRate (0.0), 59 | numChannels (channels), 60 | outputDevice (nullptr) 61 | { 62 | 63 | } 64 | 65 | /** 66 | Set or replace the source of the AudioSourcePlayer 67 | */ 68 | void setSource (juce::AudioSource* newSource) 69 | { 70 | const juce::ScopedLock sl (readLock); 71 | juce::AudioSourcePlayer::setSource (newSource); 72 | } 73 | 74 | /** 75 | Callback from the AudioIODevice. This drives the playback of the AudioSource 76 | */ 77 | void audioDeviceIOCallback (const float **inputChannelData, int totalNumInputChannels, 78 | float **outputChannelData, int totalNumOutputChannels, 79 | int numSamples) override 80 | { 81 | if (getCurrentSource()) { 82 | const juce::ScopedLock sl (readLock); 83 | 84 | if (outputDevice != nullptr) { 85 | if (fabs (deviceSampleRate - outputDevice->getCurrentSampleRate ()) > 1.0) { 86 | deviceSampleRate = outputDevice->getCurrentSampleRate(); 87 | resampler->setResamplingRatio (deviceSampleRate != 0 ? 88 | internalSampleRate / deviceSampleRate : 89 | 1.0); 90 | resampler->flushBuffers (); 91 | } 92 | } 93 | juce::AudioBuffer buffer (outputChannelData, 94 | totalNumOutputChannels, 95 | numSamples); 96 | juce::AudioSourceChannelInfo bufferToFill (&buffer, 0, numSamples); 97 | resampler->getNextAudioBlock (bufferToFill); 98 | } 99 | else { 100 | juce::AudioSourcePlayer::audioDeviceIOCallback (inputChannelData, 101 | totalNumInputChannels, 102 | outputChannelData, 103 | totalNumOutputChannels, 104 | numSamples); 105 | } 106 | } 107 | 108 | /** 109 | callback when the device wants to start using the AudioDeviceIOCallback 110 | */ 111 | void audioDeviceAboutToStart (juce::AudioIODevice *device) override 112 | { 113 | outputDevice = device; 114 | resampler = new juce::ResamplingAudioSource (getCurrentSource(), false); 115 | resampler->prepareToPlay (device->getCurrentBufferSizeSamples(), 116 | device->getCurrentSampleRate()); 117 | juce::AudioSourcePlayer::audioDeviceAboutToStart (device); 118 | } 119 | 120 | /** 121 | Callback when the AudioIODevice just stopped using the AudioDeviceIOCallback 122 | */ 123 | void audioDeviceStopped () override 124 | { 125 | juce::AudioSourcePlayer::audioDeviceStopped(); 126 | resampler = nullptr; 127 | outputDevice = nullptr; 128 | } 129 | 130 | 131 | 132 | private: 133 | double internalSampleRate; 134 | 135 | double deviceSampleRate; 136 | 137 | int numChannels; 138 | 139 | juce::AudioIODevice* outputDevice; 140 | 141 | juce::ScopedPointer resampler; 142 | 143 | juce::CriticalSection readLock; 144 | }; 145 | 146 | 147 | #endif /* filmstro_audiobasics_OutputSourcePlayer_h */ 148 | -------------------------------------------------------------------------------- /modules/filmstro_audiohelpers/filmstro_audiohelpers_SharedFormatManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class SharedFormatManager 29 | \file filmstro_audiobasics_SharedFormatManager.h 30 | \brief A FormatManager that registers audio formats by default 31 | 32 | \author Daniel Walz / Filmstro Ltd. 33 | \date October 10th 2016 34 | 35 | \description A FormatManager that registers audio formats by default, so it 36 | can be used as SharedResourcePointer 37 | 38 | ============================================================================== 39 | */ 40 | 41 | #ifndef filmstro_audiobasics_SharedFormatManager_h 42 | #define filmstro_audiobasics_SharedFormatManager_h 43 | 44 | /** 45 | This is a AudioFormatManager that registers all basic formats when created. 46 | Made to be used with JUCE's SharedResourcePointer 47 | */ 48 | class SharedFormatManager : public juce::AudioFormatManager 49 | { 50 | public: 51 | SharedFormatManager() 52 | { 53 | registerBasicFormats(); 54 | } 55 | }; 56 | 57 | 58 | #endif /* filmstro_audiobasics_SharedFormatManager_h */ 59 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Copyright (c) 2017, Daniel Walz 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | ============================================================================== 33 | 34 | BEGIN_JUCE_MODULE_DECLARATION 35 | 36 | ID: filmstro_ffmpeg 37 | vendor: Filmstro Ltd. 38 | version: 0.9.0 39 | name: FFmpeg wrapping classes for use in JUCE 40 | description: Provides classes to read audio streams from video files or to 41 | mux audio into an existing video 42 | dependencies: juce_audio_basics, juce_audio_formats, juce_core, filmstro_audiohelpers 43 | website: http://www.filmstro.com/ 44 | license: BSD V2 3-clause 45 | 46 | END_JUCE_MODULE_DECLARATION 47 | 48 | ============================================================================== 49 | */ 50 | 51 | 52 | #ifndef FSPRO_FFMPEG_INCLUDED_H 53 | #define FSPRO_FFMPEG_INCLUDED_H 54 | 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include "filmstro_audiohelpers/filmstro_audiohelpers_AudioBufferFIFO.h" 61 | 62 | #ifndef FILMSTRO_USE_FFMPEG 63 | #define FILMSTRO_USE_FFMPEG 1 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | 78 | #ifdef __cplusplus 79 | } 80 | #endif 81 | 82 | #include "filmstro_ffmpeg_FFmpegVideoListener.h" 83 | #include "filmstro_ffmpeg_FFmpegVideoScaler.h" 84 | #include "filmstro_ffmpeg_FFmpegVideoReader.h" 85 | #include "filmstro_ffmpeg_FFmpegVideoWriter.h" 86 | #include "filmstro_ffmpeg_FFmpegVideoComponent.h" 87 | 88 | 89 | #endif /* FSPRO_FFMPEG_INCLUDED_H */ 90 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoComponent.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoComponent 29 | \file filmstro_ffmpeg_FFmpegVideoComponent.cpp 30 | \brief A component to view a video decoded by FFmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date August 31st 2016 34 | 35 | \description This component will display the movie in the gui. 36 | It has a FFmpegVideoSource, which will send changeMessages when 37 | a new Video frame arrived in the stream to be displayed. 38 | 39 | ============================================================================== 40 | 41 | Ripped from example: 42 | http://ffmpeg.org/doxygen/trunk/demuxing_decoding_8c-example.html 43 | 44 | ============================================================================== 45 | */ 46 | 47 | 48 | #include "../JuceLibraryCode/JuceHeader.h" 49 | 50 | // ============================================================================== 51 | // FFmpegVideoComponent 52 | // ============================================================================== 53 | 54 | FFmpegVideoComponent::FFmpegVideoComponent () 55 | : currentFrame (nullptr), 56 | dirty (true) 57 | { 58 | setOpaque (true); 59 | startTimerHz (80); 60 | } 61 | 62 | FFmpegVideoComponent::~FFmpegVideoComponent () 63 | { 64 | } 65 | 66 | void FFmpegVideoComponent::resized () 67 | { 68 | if (videoSource) { 69 | double aspectRatio = videoSource->getVideoAspectRatio() * videoSource->getVideoPixelAspect(); 70 | if (aspectRatio > 0) { 71 | double w = getWidth(); 72 | double h = getHeight(); 73 | if (w / h > aspectRatio) { 74 | w = h * aspectRatio; 75 | } 76 | else { 77 | h = w / aspectRatio; 78 | } 79 | frameBuffer = Image (Image::PixelFormat::ARGB, static_cast (w), static_cast (h), true); 80 | videoScaler.setupScaler (videoSource->getVideoWidth(), 81 | videoSource->getVideoHeight(), 82 | videoSource->getPixelFormat(), 83 | frameBuffer.getWidth(), 84 | frameBuffer.getHeight (), 85 | AV_PIX_FMT_BGR0); 86 | } 87 | } 88 | } 89 | 90 | void FFmpegVideoComponent::timerCallback () 91 | { 92 | if (dirty) { 93 | dirty = false; 94 | repaint (); 95 | } 96 | } 97 | 98 | void FFmpegVideoComponent::paint (juce::Graphics& g) 99 | { 100 | g.fillAll (Colours::black); 101 | if (videoSource && currentFrame && frameBuffer.isValid()) { 102 | videoScaler.convertFrameToImage (frameBuffer, currentFrame); 103 | g.drawImageAt (frameBuffer, 104 | (getWidth() - frameBuffer.getWidth()) * 0.5, 105 | (getHeight() - frameBuffer.getHeight()) * 0.5); 106 | } 107 | else { 108 | g.drawFittedText ("No VideoSource connected", getLocalBounds(), juce::Justification::centred, 1); 109 | } 110 | } 111 | 112 | /** callback from FFmpegVideoReader to display a new frame */ 113 | void FFmpegVideoComponent::displayNewFrame (const AVFrame* frame) 114 | { 115 | if (dirty) { 116 | DBG ("Frame not painted: " + String (av_frame_get_best_effort_timestamp (currentFrame))); 117 | } 118 | currentFrame = frame; 119 | dirty = true; 120 | } 121 | 122 | /** This is called whenever the size changed, so a framebuffer can be resized */ 123 | void FFmpegVideoComponent::videoSizeChanged (const int width, const int height, const AVPixelFormat format) 124 | { 125 | resized(); 126 | dirty = true; 127 | } 128 | 129 | void FFmpegVideoComponent::setVideoReader (FFmpegVideoReader* source) 130 | { 131 | if (videoSource) 132 | videoSource->removeVideoListener (this); 133 | 134 | videoSource = source; 135 | 136 | if (videoSource) 137 | videoSource->addVideoListener(this); 138 | 139 | dirty = true; 140 | } 141 | 142 | FFmpegVideoReader* FFmpegVideoComponent::getVideoReader () const 143 | { 144 | return videoSource; 145 | } 146 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoComponent.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoComponent 29 | \file filmstro_ffmpeg_FFmpegVideoComponent.h 30 | \brief A component to view a video decoded by FFmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date August 31st 2016 34 | 35 | \description This component will display the movie in the gui. 36 | It has a FFmpegVideoSource, which will send changeMessages when 37 | a new Video frame arrived in the stream to be displayed. 38 | 39 | ============================================================================== 40 | */ 41 | 42 | 43 | #ifndef FILMSTRO_FFMPEG_FFMPEGVIDEOCOMPONENT_H_INCLUDED 44 | #define FILMSTRO_FFMPEG_FFMPEGVIDEOCOMPONENT_H_INCLUDED 45 | 46 | class FFmpegVideoSource; 47 | 48 | /** 49 | \class FFmpegVideoComponent 50 | \description Component to display a video read by FFmpegVideoReader 51 | */ 52 | class FFmpegVideoComponent : public juce::Component, 53 | public juce::Timer, 54 | public FFmpegVideoListener 55 | { 56 | public: 57 | FFmpegVideoComponent (); 58 | virtual ~FFmpegVideoComponent (); 59 | 60 | /** Callback when the component is resized */ 61 | void resized () override; 62 | 63 | void paint (juce::Graphics& g) override; 64 | 65 | /** triggers repaint on message thread */ 66 | void timerCallback () override; 67 | 68 | /** callback from FFmpegVideoReader to display a new frame */ 69 | void displayNewFrame (const AVFrame*) override; 70 | 71 | /** This is called whenever the size changed, so a framebuffer can be resized */ 72 | void videoSizeChanged (const int width, const int height, const AVPixelFormat) override; 73 | 74 | /** Set a video source for the component */ 75 | void setVideoReader (FFmpegVideoReader* source); 76 | 77 | /** Get the video source for the component */ 78 | FFmpegVideoReader* getVideoReader () const; 79 | 80 | private: 81 | /** Format the timecode in seconds */ 82 | juce::String formatTimeCode (const double tc); 83 | 84 | /** Reference to the FFmpegVideoReader to provide video frames */ 85 | juce::WeakReference videoSource; 86 | 87 | const AVFrame* currentFrame; 88 | 89 | juce::Image frameBuffer; 90 | 91 | FFmpegVideoScaler videoScaler; 92 | 93 | bool dirty; 94 | 95 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFmpegVideoComponent) 96 | }; 97 | 98 | #endif /* FILMSTRO_FFMPEG_FFMPEGVIDEOCOMPONENT_H_INCLUDED */ 99 | 100 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoListener.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoListener 29 | \file filmstro_ffmpeg_FFmpegVideoListener.h 30 | \brief Interface for video related callbacks from FFmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date January 25th 2017 34 | 35 | \description This is an interface for objects, that want to receive video frames 36 | 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef FILMSTRO_FFMPEG_FFMPEGVIDEOLISTENER_H_INCLUDED 42 | #define FILMSTRO_FFMPEG_FFMPEGVIDEOLISTENER_H_INCLUDED 43 | 44 | class FFmpegVideoListener { 45 | 46 | public: 47 | 48 | FFmpegVideoListener() {} 49 | 50 | virtual ~FFmpegVideoListener() {} 51 | 52 | /** This will notify about advancing the presentation timestamp */ 53 | virtual void presentationTimestampChanged (const double) {} 54 | 55 | /** This is called whenever the size changed, so a framebuffer can be resized */ 56 | virtual void videoSizeChanged (const int width, const int height, const AVPixelFormat) {} 57 | 58 | /** This is called when a frame is read from the video stream. It will be 59 | called in the correct sequence, but not synchronised to the audio stream. 60 | You can use that for transcoding a video stream */ 61 | virtual void readRawFrame (const AVFrame*) {} 62 | 63 | /** This is called when a frame is due to be displayed according to audio's 64 | presentation timestamp PTS as raw frame */ 65 | virtual void displayNewFrame (const AVFrame*) {} 66 | 67 | /** This is called when the video source file has changed */ 68 | virtual void videoFileChanged (const juce::File& newSource) {} 69 | 70 | }; 71 | 72 | #endif /* FILMSTRO_FFMPEG_FFMPEGVIDEOLISTENER_H_INCLUDED */ 73 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoReader.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoReader 29 | \file filmstro_ffmpeg_FFmpegVideoReader.h 30 | \brief A component to view a video decoded by FFmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date August 31st 2016 34 | 35 | \description This class will read a video file using ffmpeg 36 | 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef FILMSTRO_FFMPEG_FFMPEGVIDEOREADER_H_INCLUDED 42 | #define FILMSTRO_FFMPEG_FFMPEGVIDEOREADER_H_INCLUDED 43 | 44 | #include 45 | 46 | /** 47 | \class FFmpegVideoReader 48 | \description Reads a ffmpeg video file 49 | 50 | The FFmpegVideoReader opens a ffmpeg stream for reading. It acts as regular 51 | juce::AudioSource and can be played like that. Additionally it provides a 52 | FFmpegVideoSource which can be used to display the video frames or to write 53 | the file back using a FFmpegVideoWriter. 54 | */ 55 | class FFmpegVideoReader : public juce::PositionableAudioSource 56 | { 57 | public: 58 | 59 | /** Constructs a FFmpegVideoReader. Because usually audio and video frames may 60 | be in arbitrary order, the reader provides a FIFO for audio samples and a FIFO 61 | for video frames. */ 62 | FFmpegVideoReader (const int audioFifoSize=192000, const int videoFifoSize=20); 63 | virtual ~FFmpegVideoReader(); 64 | 65 | // ============================================================================== 66 | // video decoder thread 67 | // ============================================================================== 68 | /** 69 | \class FFmpegVideoReader::DecoderThread 70 | \description class for FFmpegReader to decode audio and images asynchronously 71 | This is to keep the audio thread as fast as possible 72 | */ 73 | class DecoderThread : public juce::Thread 74 | { 75 | public: 76 | DecoderThread (AudioBufferFIFO& fifo, const int videoFifoSize); 77 | 78 | virtual ~DecoderThread (); 79 | 80 | bool loadMovieFile (const juce::File& inputFile); 81 | 82 | void closeMovieFile (); 83 | 84 | void addVideoListener (FFmpegVideoListener* listener); 85 | 86 | void removeVideoListener (FFmpegVideoListener* listener); 87 | 88 | /** working loop */ 89 | void run() override; 90 | 91 | /** set the currently played PTS according to the audio stream */ 92 | void setCurrentPTS (const double pts, bool seek = false); 93 | 94 | /** returns the presentation timestamp the video is synchronised to */ 95 | double getCurrentPTS () const; 96 | 97 | /** get the width of the video images according to decoder */ 98 | int getVideoWidth () const; 99 | 100 | /** get the height of the video images according to decoder */ 101 | int getVideoHeight () const; 102 | 103 | /** get the pixel format of the video images according to decoder, 104 | will be converted to BGR0 to be displayed as juce::Image */ 105 | enum AVPixelFormat getPixelFormat () const; 106 | 107 | /** This will return the aspect ratio of each pixel */ 108 | double getPixelAspect () const; 109 | 110 | enum AVSampleFormat getSampleFormat () const; 111 | 112 | /** Return the framerate. If framerate in the decoder context is not set, 113 | this will return the timebase of the video stream. */ 114 | double getFramesPerSecond () const; 115 | 116 | /** This will return the time_base from the video stream */ 117 | AVRational getVideoTimeBase () const; 118 | 119 | /** Give access to the context to set up writers */ 120 | AVFormatContext* getVideoReaderContext(); 121 | 122 | double getSampleRate () const; 123 | 124 | double getDuration () const; 125 | 126 | int getNumChannels () const; 127 | 128 | AVCodecContext* getVideoContext () const; 129 | AVCodecContext* getAudioContext () const; 130 | AVCodecContext* getSubtitleContext () const; 131 | 132 | private: 133 | 134 | int openCodecContext (AVCodecContext** decoderContext, 135 | enum AVMediaType type, 136 | bool refCounted); 137 | 138 | /** Returns the number of added samples to the audio FIFO */ 139 | int decodeAudioPacket (AVPacket packet); 140 | 141 | /** Returns the presentation timecode PTS of the decoded frame */ 142 | double decodeVideoPacket (AVPacket packet); 143 | 144 | 145 | // ============================================================================== 146 | 147 | 148 | /** has access to the audio sources fifo to fill it */ 149 | AudioBufferFIFO& audioFifo; 150 | 151 | juce::WaitableEvent waitForPacket; 152 | 153 | /** vector of PTS -> AVFrame tuples */ 154 | std::vector > videoFrames; 155 | std::atomic videoFifoRead; 156 | std::atomic videoFifoWrite; 157 | 158 | AVFormatContext* formatContext; 159 | AVCodecContext* videoContext; 160 | AVCodecContext* audioContext; 161 | AVCodecContext* subtitleContext; 162 | SwrContext* audioConverterContext; 163 | 164 | int videoStreamIdx; 165 | int audioStreamIdx; 166 | int subtitleStreamIdx; 167 | 168 | AVFrame* audioFrame; 169 | juce::AudioBuffer audioConvertBuffer; 170 | 171 | std::atomic currentPTS; 172 | 173 | juce::ListenerList videoListeners; 174 | 175 | /** Buffer for reading */ 176 | juce::AudioBuffer buffer; 177 | 178 | }; 179 | 180 | // ============================================================================== 181 | // video methods 182 | // ============================================================================== 183 | 184 | bool loadMovieFile (const juce::File& inputFile); 185 | 186 | void closeMovieFile (); 187 | 188 | /** Returns the currently opened video file */ 189 | juce::File getVideoFileName () const; 190 | 191 | /** Return the framerate. If framerate in the decoder context is not set, 192 | this will return the timebase of the video stream. */ 193 | double getFramesPerSecond () const; 194 | 195 | /** Get current presentation timecode according to audio stream */ 196 | double getCurrentTimeStamp() const; 197 | 198 | /** returns the duration of the video in seconds according to the container context */ 199 | double getVideoDuration () const; 200 | 201 | /** returns the sampling rate as specified in the video file. This can be different 202 | from the samplingrate the prepareToPlay was called with. 203 | The FFmpegVideoReader will not resample. */ 204 | int getVideoSamplingRate () const; 205 | 206 | /** returns the number of audio channels in the video file. Make sure you call 207 | getNextAudioBuffer with the same number of channels */ 208 | int getVideoChannels () const; 209 | 210 | /** returns the audio sample format in the video file. 211 | The FFmpegVideoReader will convert into discrete channels of float values */ 212 | enum AVSampleFormat getSampleFormat () const; 213 | 214 | /** add a listener to receive video frames for displaying and to get timestamp 215 | notifications. The video frames will happen synchronously to getNextAudioBlock */ 216 | void addVideoListener (FFmpegVideoListener* listener); 217 | 218 | /** remove a video listener */ 219 | void removeVideoListener (FFmpegVideoListener* listener); 220 | 221 | double getLastVideoPTS () const; 222 | 223 | /** get the width of the video images according to decoder */ 224 | int getVideoWidth () const; 225 | 226 | /** get the height of the video images according to decoder */ 227 | int getVideoHeight () const; 228 | 229 | /** returns the aspect ratio video frame */ 230 | double getVideoAspectRatio () const; 231 | 232 | /** This will return the aspect ratio of each pixel */ 233 | double getVideoPixelAspect () const; 234 | 235 | /** get the pixel format of the video images according to decoder, 236 | will be converted to BGR0 to be displayed as juce::Image */ 237 | enum AVPixelFormat getPixelFormat () const; 238 | 239 | /** This will return the time_base from the video stream */ 240 | AVRational getVideoTimeBase () const; 241 | 242 | static juce::String formatTimeCode (const double tc); 243 | 244 | 245 | // ============================================================================== 246 | // from PositionableAudioSource 247 | // ============================================================================== 248 | 249 | void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; 250 | 251 | void releaseResources () override; 252 | 253 | /** decodes packets to fill the audioFifo. If a video packet is found it will be forwarded to VideoDecoderThread */ 254 | void getNextAudioBlock (const juce::AudioSourceChannelInfo &bufferToFill) override; 255 | 256 | /** Wait until the decoder thread has finished enough data. This is needed for non-realtime processing. */ 257 | bool waitForNextAudioBlockReady (const juce::AudioSourceChannelInfo &bufferToFill, const int msecs) const; 258 | 259 | /** Seeks in the stream */ 260 | void setNextReadPosition (juce::int64 newPosition) override; 261 | 262 | /** Returns the sample count of the next sample to be returned by getNextAudioBlock */ 263 | juce::int64 getNextReadPosition () const override; 264 | 265 | /** Returns the total length in samples. May not be accurate, because it uses ffmpegs 266 | duration property multiplied with the samplerate */ 267 | juce::int64 getTotalLength () const override; 268 | 269 | /** Returns true if this source is actually playing in a loop. */ 270 | bool isLooping() const override; 271 | 272 | /** Tells the source whether you'd like it to play in a loop. */ 273 | void setLooping (bool shouldLoop) override; 274 | 275 | // ============================================================================== 276 | // FFmpeg low level 277 | // ============================================================================== 278 | 279 | AVCodecContext* getVideoContext () const; 280 | AVCodecContext* getAudioContext () const; 281 | AVCodecContext* getSubtitleContext () const; 282 | 283 | 284 | // ============================================================================== 285 | private: 286 | 287 | juce::File videoFileName; 288 | 289 | bool fileReadyToRead; 290 | 291 | bool looping; 292 | 293 | int sampleRate; 294 | 295 | double framesPerSec; 296 | 297 | double resampleFactor; 298 | 299 | double currentTimeStamp; 300 | 301 | juce::int64 nextReadPos; 302 | 303 | AudioBufferFIFO audioFifo; 304 | 305 | DecoderThread decoder; 306 | 307 | // use WeakReference so that removing a source doesn't lead to disaster 308 | juce::WeakReference::Master masterReference; 309 | friend class juce::WeakReference; 310 | 311 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFmpegVideoReader) 312 | }; 313 | 314 | #endif /* FILMSTRO_FFMPEG_FFMPEGVIDEOREADER_H_INCLUDED */ 315 | 316 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoScaler.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoScaler 29 | \file filmstro_ffmpeg_FFmpegVideoScaler.h 30 | \brief Converts and scales FFmpeg frames into juce format 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date January 25th 2017 34 | 35 | \description Converts and scales FFmpeg frames into juce format 36 | 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef FILMSTRO_FFMPEG_FFMPEGVIDEOSCALER_H_INCLUDED 42 | #define FILMSTRO_FFMPEG_FFMPEGVIDEOSCALER_H_INCLUDED 43 | 44 | class FFmpegVideoScaler { 45 | public: 46 | /** Creates a scaler object. It does nothing before you call setupScaler */ 47 | FFmpegVideoScaler () : scalerContext (nullptr) {} 48 | 49 | ~FFmpegVideoScaler () 50 | { 51 | if (scalerContext) 52 | sws_freeContext (scalerContext); 53 | } 54 | 55 | /** Setup a scaler to scale video frames and to convert pixel formats */ 56 | void setupScaler (const int in_width, const int in_height, const AVPixelFormat in_format, 57 | const int out_width, const int out_height, const AVPixelFormat out_format) 58 | { 59 | if (scalerContext) { 60 | sws_freeContext (scalerContext); 61 | scalerContext = nullptr; 62 | } 63 | 64 | const AVPixFmtDescriptor* in_descriptor = av_pix_fmt_desc_get (in_format); 65 | if (!in_descriptor) { 66 | DBG ("No description for input pixel format"); 67 | return; 68 | } 69 | const int in_bitsPerPixel = av_get_padded_bits_per_pixel (in_descriptor); 70 | for (int i=0; i < 4; ++i) 71 | inLinesizes [i] = i < in_descriptor->nb_components ? in_width * in_bitsPerPixel >> 3 : 0; 72 | 73 | const AVPixFmtDescriptor* out_descriptor = av_pix_fmt_desc_get (out_format); 74 | if (!out_descriptor) { 75 | DBG ("No description for output pixel format"); 76 | return; 77 | } 78 | const int out_bitsPerPixel = av_get_padded_bits_per_pixel (out_descriptor); 79 | for (int i=0; i < 4; ++i) 80 | outLinesizes [i] = i < out_descriptor->nb_components ? out_width * out_bitsPerPixel >> 3 : 0; 81 | 82 | /* create scaling context */ 83 | scalerContext = sws_getContext (in_width, in_height, in_format, 84 | out_width, out_height, out_format, 85 | SWS_BILINEAR, NULL, NULL, NULL); 86 | if (!scalerContext) { 87 | DBG ("Impossible to create scale context for the conversion"); 88 | } 89 | } 90 | 91 | 92 | /** takes an AVFrame from ffmpeg and converts it to a JUCE Image. Image is in a format 93 | matching to the platform */ 94 | void convertFrameToImage (juce::Image& image, const AVFrame* frame) 95 | { 96 | if (scalerContext) { 97 | juce::Image::BitmapData data (image, 0, 0, 98 | image.getWidth(), 99 | image.getHeight(), 100 | juce::Image::BitmapData::writeOnly); 101 | 102 | uint8_t* destination[4] = {data.data, nullptr, nullptr, nullptr}; 103 | 104 | sws_scale (scalerContext, 105 | frame->data, 106 | frame->linesize, 107 | 0, 108 | frame->height, 109 | destination, 110 | outLinesizes); 111 | } 112 | } 113 | 114 | 115 | /** Converts a JUCE Image into a ffmpeg AVFrame to be written into a video stream */ 116 | void convertImageToFrame (AVFrame* frame, const juce::Image& image) 117 | { 118 | if (scalerContext) { 119 | juce::Image::BitmapData data (image, 0, 0, 120 | image.getWidth(), 121 | image.getHeight()); 122 | 123 | uint8_t* source[4] = {data.data, nullptr, nullptr, nullptr}; 124 | 125 | sws_scale (scalerContext, 126 | source, 127 | inLinesizes, 128 | 0, 129 | image.getHeight(), 130 | frame->data, 131 | frame->linesize); 132 | } 133 | } 134 | 135 | 136 | private: 137 | SwsContext* scalerContext; 138 | 139 | int inLinesizes[4]; 140 | int outLinesizes[4]; 141 | 142 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFmpegVideoScaler) 143 | }; 144 | 145 | #endif /* FILMSTRO_FFMPEG_FFMPEGVIDEOSCALER_H_INCLUDED */ 146 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoWriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoWriter 29 | \file filmstro_ffmpeg_FFmpegVideoWriter.cpp 30 | \brief A component to write a video stream with ffmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date September 19th 2016 34 | 35 | \description Use this writer to write audio frames and video frames in a 36 | video file using ffmpeg 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #include "../JuceLibraryCode/JuceHeader.h" 42 | 43 | FFmpegVideoWriter::FFmpegVideoWriter (const juce::String& format) 44 | : audioWritePosition (0), 45 | formatContext (nullptr), 46 | videoContext (nullptr), 47 | audioContext (nullptr), 48 | subtitleContext (nullptr), 49 | videoCodec (AV_CODEC_ID_NONE), 50 | audioCodec (AV_CODEC_ID_NONE), 51 | subtitleCodec (AV_CODEC_ID_NONE), 52 | videoStreamIdx (-1), 53 | audioStreamIdx (-1), 54 | subtitleStreamIdx (-1), 55 | sampleRate (48000), 56 | channelLayout (AV_CH_LAYOUT_STEREO), 57 | videoWidth (0), 58 | videoHeight (0), 59 | pixelFormat (AV_PIX_FMT_NONE), 60 | pixelAspect (av_make_q (1, 1)), 61 | audioFifo (2, 8192) 62 | { 63 | videoTimeBase = av_make_q (1, 24); 64 | audioTimeBase = av_make_q (1, sampleRate); 65 | subtitleTimeBase = av_make_q (1, AV_TIME_BASE); 66 | 67 | av_register_all(); 68 | if (format.isNotEmpty()) { 69 | formatContext = avformat_alloc_context(); 70 | AVOutputFormat* oformat = av_guess_format (format.toRawUTF8(), nullptr, nullptr); 71 | if (oformat) { 72 | formatContext->oformat = oformat; 73 | } 74 | } 75 | } 76 | 77 | FFmpegVideoWriter::~FFmpegVideoWriter() 78 | { 79 | } 80 | 81 | juce::StringArray FFmpegVideoWriter::getOutputFormatNames () 82 | { 83 | StringArray names; 84 | AVOutputFormat* format = av_oformat_next (nullptr); 85 | while (format) { 86 | names.add (format->name); 87 | format = av_oformat_next (format); 88 | } 89 | return names; 90 | } 91 | 92 | void FFmpegVideoWriter::setVideoCodec (const AVCodecID codec) 93 | { 94 | videoCodec = codec; 95 | } 96 | 97 | void FFmpegVideoWriter::setAudioCodec (const AVCodecID codec) 98 | { 99 | audioCodec = codec; 100 | } 101 | 102 | void FFmpegVideoWriter::setSubtitleCodec (const AVCodecID codec) 103 | { 104 | subtitleCodec = codec; 105 | } 106 | 107 | void FFmpegVideoWriter::setSampleRate (const int newSampleRate) 108 | { 109 | sampleRate = newSampleRate; 110 | audioTimeBase = av_make_q (1, newSampleRate); 111 | } 112 | 113 | void FFmpegVideoWriter::setVideoSize (const int width, const int height) 114 | { 115 | videoWidth = width; 116 | videoHeight = height; 117 | } 118 | 119 | void FFmpegVideoWriter::setPixelFormat (const AVPixelFormat format) 120 | { 121 | pixelFormat = format; 122 | } 123 | 124 | void FFmpegVideoWriter::setPixelAspect (const int num, const int den) 125 | { 126 | pixelAspect = av_make_q (num, den); 127 | } 128 | 129 | void FFmpegVideoWriter::setTimeBase (AVMediaType type, AVRational timebase) 130 | { 131 | switch (type) { 132 | case AVMEDIA_TYPE_VIDEO: 133 | videoTimeBase = timebase; 134 | break; 135 | case AVMEDIA_TYPE_AUDIO: 136 | audioTimeBase = timebase; 137 | break; 138 | case AVMEDIA_TYPE_SUBTITLE: 139 | subtitleTimeBase = timebase; 140 | break; 141 | default: 142 | break; 143 | } 144 | } 145 | 146 | void FFmpegVideoWriter::copySettingsFromContext (const AVCodecContext* context) 147 | { 148 | if (context) { 149 | if (context->codec_type == AVMEDIA_TYPE_VIDEO) { 150 | videoCodec = context->codec_id; 151 | videoWidth = context->width; 152 | videoHeight = context->height; 153 | pixelFormat = context->pix_fmt; 154 | pixelAspect = context->sample_aspect_ratio; 155 | if (context->framerate.num != 0) { 156 | // preferred way 157 | videoTimeBase = av_inv_q (context->framerate); 158 | } 159 | else if (context->time_base.num > 0) { 160 | // some decoders don't set framerate but might provide a time_base 161 | videoTimeBase = context->time_base; 162 | } 163 | } 164 | else if (context->codec_type == AVMEDIA_TYPE_AUDIO) { 165 | audioCodec = context->codec_id; 166 | channelLayout = context->channel_layout; 167 | // we don't copy audio, it's coming from JUCE so it's gonna be AV_SAMPLE_FMT_FLTP 168 | //sampleRate = context->sample_rate; 169 | } 170 | else if (context->codec_type == AVMEDIA_TYPE_SUBTITLE) { 171 | // FIXME: TODO 172 | } 173 | } 174 | } 175 | 176 | bool FFmpegVideoWriter::openMovieFile (const juce::File& outputFile, const juce::String& format) 177 | { 178 | videoStreamIdx = -1; 179 | if (videoContext) av_free (&videoContext); 180 | audioStreamIdx = -1; 181 | if (audioContext) av_free (&audioContext); 182 | subtitleStreamIdx = -1; 183 | if (subtitleContext) av_free (&subtitleContext); 184 | 185 | audioWritePosition = 0; 186 | 187 | if (formatContext) { 188 | String name (outputFile.getFullPathName ()); 189 | memcpy (formatContext->filename, name.toRawUTF8 (), jmin (name.getNumBytesAsUTF8 () + 1, sizeof (formatContext->filename))); 190 | } 191 | else { 192 | if (format.isEmpty()) 193 | avformat_alloc_output_context2 (&formatContext, NULL, NULL, outputFile.getFullPathName().toRawUTF8()); 194 | else 195 | avformat_alloc_output_context2 (&formatContext, NULL, format.toRawUTF8(), outputFile.getFullPathName().toRawUTF8()); 196 | } 197 | if (!formatContext) { 198 | DBG ("Could not open output with format " + format); 199 | return false; 200 | } 201 | 202 | if (videoCodec == AV_CODEC_ID_PROBE) { 203 | videoCodec = av_guess_codec (formatContext->oformat, 204 | nullptr, outputFile.getFullPathName().toRawUTF8(), 205 | nullptr, 206 | AVMEDIA_TYPE_VIDEO); 207 | } 208 | if (audioCodec == AV_CODEC_ID_PROBE) { 209 | audioCodec = av_guess_codec (formatContext->oformat, 210 | nullptr, outputFile.getFullPathName().toRawUTF8(), 211 | nullptr, 212 | AVMEDIA_TYPE_AUDIO); 213 | } 214 | if (subtitleCodec == AV_CODEC_ID_PROBE) { 215 | subtitleCodec = av_guess_codec (formatContext->oformat, 216 | nullptr, outputFile.getFullPathName().toRawUTF8(), 217 | nullptr, 218 | AVMEDIA_TYPE_SUBTITLE); 219 | } 220 | 221 | if (videoCodec > AV_CODEC_ID_NONE) { 222 | AVStream* stream = avformat_new_stream (formatContext, NULL); 223 | if (!stream) { 224 | DBG ("Failed allocating video output stream\n"); 225 | } 226 | else { 227 | videoStreamIdx = formatContext->nb_streams - 1; 228 | AVCodec* encoder = avcodec_find_encoder (videoCodec); 229 | if (encoder) { 230 | stream->time_base = videoTimeBase; 231 | videoContext = avcodec_alloc_context3 (encoder); 232 | videoContext->time_base = videoTimeBase; 233 | videoContext->pix_fmt = pixelFormat; 234 | videoContext->sample_aspect_ratio = pixelAspect; 235 | videoContext->codec_type = encoder->type; 236 | videoContext->color_range = AVCOL_RANGE_JPEG; 237 | videoContext->codec_id = videoCodec; 238 | videoContext->width = videoWidth; 239 | videoContext->height = videoHeight; 240 | videoContext->bit_rate = 480000; 241 | videoContext->gop_size = 10; 242 | videoContext->max_b_frames = 1; 243 | avcodec_parameters_from_context (stream->codecpar, videoContext); 244 | 245 | AVDictionary* options = nullptr; 246 | 247 | if (encoder->id == AV_CODEC_ID_H264) { 248 | av_dict_set (&options, "preset", "slow", 0); 249 | av_dict_set (&options, "tune", "film", 0); 250 | av_opt_set (videoContext->priv_data, "profile", "baseline", AV_OPT_SEARCH_CHILDREN); 251 | } 252 | 253 | int ret = avcodec_open2 (videoContext, encoder, &options); 254 | if (ret < 0) { 255 | DBG ("Cannot open video encoder"); 256 | videoStreamIdx = -1; 257 | videoCodec = AV_CODEC_ID_NONE; 258 | } 259 | av_dict_free (&options); 260 | } 261 | else { 262 | DBG ("Video encoder not found for " + String (avcodec_get_name (videoCodec))); 263 | } 264 | } 265 | } 266 | 267 | if (audioCodec > AV_CODEC_ID_NONE) { 268 | AVStream* stream = avformat_new_stream (formatContext, NULL); 269 | if (!stream) { 270 | DBG ("Failed allocating audio output stream\n"); 271 | } 272 | else { 273 | audioStreamIdx = formatContext->nb_streams - 1; 274 | AVCodec* encoder = avcodec_find_encoder (audioCodec); 275 | if (encoder) { 276 | audioContext = avcodec_alloc_context3 (encoder); 277 | audioContext->time_base = audioTimeBase; 278 | audioContext->sample_rate = audioTimeBase.den; 279 | audioContext->sample_fmt = AV_SAMPLE_FMT_FLTP; 280 | audioContext->channel_layout = channelLayout; 281 | audioContext->channels = av_get_channel_layout_nb_channels (channelLayout); 282 | audioContext->bit_rate = 64000; 283 | audioContext->frame_size = 1024; 284 | audioContext->bits_per_raw_sample = 32; 285 | avcodec_parameters_from_context (stream->codecpar, audioContext); 286 | 287 | int ret = avcodec_open2 (audioContext, encoder, NULL); 288 | if (ret < 0) { 289 | char codecName [256]; 290 | av_get_codec_tag_string (codecName, 256, audioCodec); 291 | DBG ("Cannot open audio encoder for format: " + String::fromUTF8 (codecName)); 292 | audioStreamIdx = -1; 293 | audioCodec = AV_CODEC_ID_NONE; 294 | } 295 | } 296 | else { 297 | DBG ("Audio encoder not found for " + String (avcodec_get_name (audioCodec))); 298 | } 299 | } 300 | } 301 | 302 | //if (subtitleCodec > AV_CODEC_ID_NONE) { 303 | // // FIXME: TODO 304 | //} 305 | 306 | av_dump_format (formatContext, 0, outputFile.getFullPathName().toRawUTF8(), 1); 307 | 308 | if (!(formatContext->oformat->flags & AVFMT_NOFILE)) { 309 | if (avio_open (&formatContext->pb, outputFile.getFullPathName().toRawUTF8(), AVIO_FLAG_WRITE) < 0) { 310 | DBG ("Could not open output file '" + outputFile.getFullPathName() + "'"); 311 | closeContexts(); 312 | return false; 313 | } 314 | } 315 | 316 | if (formatContext->oformat->flags & AVFMT_GLOBALHEADER) { 317 | if (videoContext) videoContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 318 | if (audioContext) audioContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 319 | if (subtitleContext) subtitleContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 320 | } 321 | 322 | if (!videoContext && !audioContext && !subtitleContext) { 323 | DBG ("No stream provided to encode"); 324 | closeContexts(); 325 | return false; 326 | } 327 | 328 | if (!(formatContext->oformat->flags & AVFMT_NOFILE)) { 329 | int ret = avio_open(&formatContext->pb, outputFile.getFullPathName().toRawUTF8(), AVIO_FLAG_WRITE); 330 | if (ret < 0) { 331 | av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", outputFile.getFullPathName().toRawUTF8()); 332 | closeContexts(); 333 | return false; 334 | } 335 | } 336 | 337 | int ret = avformat_write_header (formatContext, NULL); 338 | if (ret < 0) { 339 | DBG ("Error occurred when opening output file"); 340 | closeContexts(); 341 | return false; 342 | } 343 | 344 | return true; 345 | } 346 | 347 | void FFmpegVideoWriter::closeMovieFile () 348 | { 349 | finishWriting (); 350 | } 351 | 352 | void FFmpegVideoWriter::finishWriting () 353 | { 354 | if (formatContext) { 355 | AVMediaType mediaType; 356 | for (int idx=0; idx < formatContext->nb_streams; ++idx) { 357 | if (idx == videoStreamIdx) { 358 | if (!(videoContext->codec->capabilities & 359 | AV_CODEC_CAP_DELAY)) 360 | break; 361 | mediaType = AVMEDIA_TYPE_VIDEO; 362 | } 363 | else if (idx == audioStreamIdx) { 364 | if (!(audioContext->codec->capabilities & 365 | AV_CODEC_CAP_DELAY)) 366 | break; 367 | mediaType = AVMEDIA_TYPE_AUDIO; 368 | } 369 | else { 370 | break; 371 | } 372 | 373 | av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", idx); 374 | while (encodeWriteFrame (NULL, mediaType)); 375 | } 376 | 377 | av_write_trailer (formatContext); 378 | } 379 | closeContexts(); 380 | } 381 | 382 | void FFmpegVideoWriter::closeContexts () 383 | { 384 | avformat_free_context (formatContext); 385 | videoStreamIdx = -1; 386 | audioStreamIdx = -1; 387 | subtitleStreamIdx = -1; 388 | videoContext = nullptr; 389 | audioContext = nullptr; 390 | subtitleContext = nullptr; 391 | avcodec_free_context (&videoContext); 392 | avcodec_free_context (&audioContext); 393 | avcodec_free_context (&subtitleContext); 394 | formatContext = nullptr; 395 | inVideoScaler = nullptr; 396 | outVideoScaler = nullptr; 397 | audioWritePosition = 0; 398 | } 399 | 400 | void FFmpegVideoWriter::writeNextAudioBlock (juce::AudioSourceChannelInfo& info) 401 | { 402 | audioFifo.addToFifo (*info.buffer, info.numSamples); 403 | writeAudioFrame (false); 404 | } 405 | 406 | void FFmpegVideoWriter::writeNextVideoFrame (const juce::Image& image, const juce::int64 timestamp) 407 | { 408 | // use scaler and add interface for image processing / e.g. branding 409 | if (videoContext) { 410 | if (! outVideoScaler) { 411 | outVideoScaler = new FFmpegVideoScaler (); 412 | outVideoScaler->setupScaler (image.getWidth(), 413 | image.getHeight(), 414 | AV_PIX_FMT_BGR0, 415 | videoContext->width, 416 | videoContext->height, 417 | videoContext->pix_fmt); 418 | } 419 | AVFrame* frame = av_frame_alloc (); 420 | frame->width = videoContext->width; 421 | frame->height = videoContext->height; 422 | frame->format = videoContext->pix_fmt; 423 | frame->pts = timestamp; 424 | av_frame_set_color_range (frame, videoContext->color_range); 425 | int ret = av_image_alloc(frame->data, frame->linesize, 426 | videoContext->width, 427 | videoContext->height, 428 | videoContext->pix_fmt, 32); 429 | if (ret < 0) { 430 | DBG ("Could not allocate raw picture buffer"); 431 | av_free (&frame); 432 | return; 433 | } 434 | outVideoScaler->convertImageToFrame (frame, image); 435 | encodeWriteFrame (frame, AVMEDIA_TYPE_VIDEO); 436 | } 437 | } 438 | 439 | bool FFmpegVideoWriter::writeAudioFrame (const bool flush) 440 | { 441 | if (formatContext && audioContext && 442 | isPositiveAndBelow (audioStreamIdx, static_cast (formatContext->nb_streams))) 443 | { 444 | 445 | int numFrameSamples = audioContext->frame_size; 446 | 447 | if (audioFifo.getNumReady() >= numFrameSamples || flush) { 448 | const uint64_t channelLayout = AV_CH_LAYOUT_STEREO; 449 | const int numChannels = av_get_channel_layout_nb_channels (channelLayout); 450 | AVFrame* frame = av_frame_alloc (); 451 | frame->nb_samples = numFrameSamples; 452 | frame->format = AV_SAMPLE_FMT_FLTP; 453 | frame->channel_layout = channelLayout; 454 | frame->channels = numChannels; 455 | frame->pts = audioWritePosition; 456 | DBG ("Start writing audio frame, pts: " + String (audioWritePosition)); 457 | int bufferSize = av_samples_get_buffer_size (nullptr, frame->channels, frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0); 458 | float* samples = static_cast (av_malloc (bufferSize)); 459 | avcodec_fill_audio_frame (frame, 460 | frame->channels, 461 | static_cast (frame->format), 462 | (const uint8_t*) (samples), 463 | bufferSize, 464 | 0); 465 | 466 | float** sampleData = new float*[numChannels]; 467 | for (int i=0; i < frame->channels; ++i) { 468 | sampleData[i] = samples + i * frame->nb_samples; 469 | } 470 | audioFifo.readFromFifo (sampleData, numFrameSamples); 471 | 472 | encodeWriteFrame (frame, AVMEDIA_TYPE_AUDIO); 473 | 474 | delete[] sampleData; 475 | //av_freep (buffer); 476 | 477 | audioWritePosition += numFrameSamples; 478 | } 479 | 480 | return true; 481 | } 482 | return false; 483 | } 484 | 485 | int FFmpegVideoWriter::encodeWriteFrame (AVFrame *frame, AVMediaType type) { 486 | int got_frame = 0; 487 | if (formatContext) { 488 | int ret; 489 | AVPacket packet; 490 | packet.data = NULL; 491 | packet.size = 0; 492 | av_init_packet (&packet); 493 | if (type == AVMEDIA_TYPE_VIDEO) { 494 | if (frame) 495 | av_frame_set_color_range (frame, AVCOL_RANGE_JPEG); 496 | ret = avcodec_encode_video2 (videoContext, &packet, frame, &got_frame); 497 | packet.stream_index = videoStreamIdx; 498 | av_packet_rescale_ts (&packet, 499 | videoContext->time_base, 500 | formatContext->streams [videoStreamIdx]->time_base); 501 | av_frame_free (&frame); 502 | if (ret < 0) { 503 | char error[255]; 504 | av_strerror (ret, error, 255); 505 | DBG (String ("Error when encoding video data: ") += error); 506 | return false; 507 | } 508 | } 509 | else if (type == AVMEDIA_TYPE_AUDIO) { 510 | ret = avcodec_encode_audio2 (audioContext, &packet, frame, &got_frame); 511 | packet.stream_index = audioStreamIdx; 512 | av_packet_rescale_ts (&packet, 513 | audioContext->time_base, 514 | formatContext->streams [audioStreamIdx]->time_base); 515 | av_frame_free (&frame); 516 | if (ret < 0) { 517 | char error[255]; 518 | av_strerror (ret, error, 255); 519 | DBG (String ("Error when encoding audio data: ") += error); 520 | return false; 521 | } 522 | } 523 | else { 524 | // the AVFrame only holds audio or video data, so you shouldn't call that 525 | // function with a type other than AVMEDIA_TYPE_VIDEO or AVMEDIA_TYPE_AUDIO 526 | jassertfalse; 527 | } 528 | 529 | if (got_frame == 1) { 530 | if (av_interleaved_write_frame (formatContext, &packet) < 0) { 531 | DBG ("Error when writing data"); 532 | return false; 533 | } 534 | } 535 | } 536 | else { 537 | DBG ("No writer open, did not write frame"); 538 | av_frame_free (&frame); 539 | } 540 | return got_frame == 1; 541 | } 542 | 543 | void FFmpegVideoWriter::videoSizeChanged (const int width, const int height, const AVPixelFormat format) 544 | { 545 | // force a reset of scaler 546 | inVideoScaler = nullptr; 547 | } 548 | 549 | void FFmpegVideoWriter::displayNewFrame (const AVFrame* frame) 550 | { 551 | if (!inVideoScaler) { 552 | inVideoScaler = new FFmpegVideoScaler(); 553 | inVideoScaler->setupScaler (frame->width, frame->height, static_cast (frame->format), 554 | videoWidth, videoHeight, AV_PIX_FMT_BGR0); 555 | } 556 | DBG ("Write video frame, pts: " + String (frame->pts)); 557 | Image picture (Image::RGB, videoWidth, videoHeight, true); 558 | inVideoScaler->convertFrameToImage (picture, frame); 559 | writeNextVideoFrame (picture, frame->pts); 560 | } 561 | 562 | 563 | -------------------------------------------------------------------------------- /modules/filmstro_ffmpeg/filmstro_ffmpeg_FFmpegVideoWriter.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2017, Filmstro Ltd. - Daniel Walz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | ============================================================================== 28 | \class FFmpegVideoWriter 29 | \file filmstro_ffmpeg_FFmpegVideoWriter.h 30 | \brief A class to write a video stream using FFmpeg 31 | 32 | \author Daniel Walz @ filmstro.com 33 | \date September 19th 2016 34 | 35 | \description Use this writer to write audio frames and video frames in a 36 | video file using ffmpeg 37 | ============================================================================== 38 | */ 39 | 40 | 41 | #ifndef FILMSTRO_FFMPEG_FFMPEGVIDEOWRITER_H_INCLUDED 42 | #define FILMSTRO_FFMPEG_FFMPEGVIDEOWRITER_H_INCLUDED 43 | 44 | class FFmpegVideoSource; 45 | 46 | 47 | class FFmpegVideoWriter : public FFmpegVideoListener 48 | { 49 | public: 50 | 51 | FFmpegVideoWriter (const juce::String& format = juce::String()); 52 | ~FFmpegVideoWriter(); 53 | 54 | /** Set the requested video codec before opening a file */ 55 | void setVideoCodec (AVCodecID codec = AV_CODEC_ID_PROBE); 56 | /** Set the requested audio codec before opening a file */ 57 | void setAudioCodec (AVCodecID codec = AV_CODEC_ID_PROBE); 58 | /** Set the requested subtitle codec before opening a file */ 59 | void setSubtitleCodec (AVCodecID codec = AV_CODEC_ID_PROBE); 60 | 61 | /** Set the audio sample rate before opening a file */ 62 | void setSampleRate (const int newSampleRate); 63 | 64 | /** Set the video size before opening a file */ 65 | void setVideoSize (const int width, const int height); 66 | 67 | /** Set the pixel format before opening a file */ 68 | void setPixelFormat (const AVPixelFormat format); 69 | 70 | /** Set the pixel aspect ratio as fraction before opening a file */ 71 | void setPixelAspect (const int num, const int den); 72 | 73 | /** Set the timebase for the stream, provide either 74 | AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE */ 75 | void setTimeBase (AVMediaType type, AVRational timebase); 76 | 77 | /** copies settings from a context (e.g. FFmpegVideoReader) to the writer */ 78 | void copySettingsFromContext (const AVCodecContext* context); 79 | 80 | /** Opens a file for writing audio, video and subtitles. The settings like 81 | encoders, samplerate etc. has to be set first. */ 82 | bool openMovieFile (const juce::File& outputFile, const juce::String& format=juce::String()); 83 | 84 | /** Closes the movie file. Also flushes all left over samples and frames */ 85 | void closeMovieFile (); 86 | 87 | /** Append a chunk of audio data. It will call writeAudioFrame to get rid of the data */ 88 | void writeNextAudioBlock (juce::AudioSourceChannelInfo& info); 89 | 90 | /** Write the next video frame from juce image */ 91 | void writeNextVideoFrame (const juce::Image& image, const juce::int64 timestamp); 92 | 93 | void videoSizeChanged (const int width, const int height, const AVPixelFormat) override; 94 | 95 | /** This callback receives frames from e.g. the FFmpegVideoReader to be written to the video file. 96 | The timestamp has to be set in the frame. */ 97 | void displayNewFrame (const AVFrame*) override; 98 | 99 | /** Returns the names of available output formats */ 100 | static juce::StringArray getOutputFormatNames (); 101 | 102 | // ============================================================================== 103 | private: 104 | 105 | void closeContexts (); 106 | 107 | void finishWriting (); 108 | 109 | /** Write audio data to frame, if there is enough. If flush is set to true, it will append silence to fill the last frame. */ 110 | bool writeAudioFrame (const bool flush=false); 111 | 112 | int encodeWriteFrame (AVFrame *frame, AVMediaType type); 113 | 114 | // ============================================================================== 115 | 116 | /** This is the samplecode of the next sample to be written */ 117 | juce::int64 audioWritePosition; 118 | 119 | AVFormatContext* formatContext; 120 | 121 | AVCodecContext* videoContext; 122 | AVCodecContext* audioContext; 123 | AVCodecContext* subtitleContext; 124 | 125 | AVCodecID videoCodec; 126 | AVCodecID audioCodec; 127 | AVCodecID subtitleCodec; 128 | 129 | int videoStreamIdx; 130 | int audioStreamIdx; 131 | int subtitleStreamIdx; 132 | 133 | AVRational videoTimeBase; 134 | AVRational audioTimeBase; 135 | AVRational subtitleTimeBase; 136 | 137 | int sampleRate; 138 | int64_t channelLayout; 139 | 140 | int videoWidth; 141 | int videoHeight; 142 | AVPixelFormat pixelFormat; 143 | AVRational pixelAspect; 144 | 145 | // buffer audio to match the video's audio frame size 146 | AudioBufferFIFO audioFifo; 147 | 148 | juce::ScopedPointer outVideoScaler; 149 | juce::ScopedPointer inVideoScaler; 150 | 151 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFmpegVideoWriter) 152 | }; 153 | 154 | #endif /* FILMSTRO_FFMPEG_FFMPEGVIDEOWRITER_H_INCLUDED */ 155 | 156 | --------------------------------------------------------------------------------