├── .github
└── workflows
│ ├── deploy-snapshot.yml
│ ├── gradle-wrapper-validation.yml
│ ├── merge-check.yml
│ └── publish-maven-central.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── RELEASING.md
├── build.gradle
├── gradle.properties
├── gradle
├── publishing.gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── litr-demo
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── linkedin
│ │ └── android
│ │ └── litr
│ │ ├── demo
│ │ ├── BaseTransformationFragment.java
│ │ ├── DemoCase.java
│ │ ├── DemoCasesAdapter.java
│ │ ├── InfoActivity.java
│ │ ├── MainActivity.java
│ │ ├── MainFragment.java
│ │ ├── MediaPickerListener.java
│ │ ├── data
│ │ │ ├── AudioTrackFormat.java
│ │ │ ├── AudioVolumeConfig.java
│ │ │ ├── Converter.java
│ │ │ ├── EmptyVideoPresenter.kt
│ │ │ ├── FreeTransformVideoGlPresenter.kt
│ │ │ ├── GenericTrackFormat.java
│ │ │ ├── MediaTrackFormat.java
│ │ │ ├── MediaTransformationListener.java
│ │ │ ├── MuxVideoAndAudioPresenter.kt
│ │ │ ├── RecordAudioPresenter.kt
│ │ │ ├── RecordCameraPresenter.kt
│ │ │ ├── SharedMediaStoragePublisher.kt
│ │ │ ├── SourceMedia.java
│ │ │ ├── SquareCenterCropPresenter.kt
│ │ │ ├── TargetAudioTrack.java
│ │ │ ├── TargetMedia.java
│ │ │ ├── TargetTrack.java
│ │ │ ├── TargetVideoConfiguration.java
│ │ │ ├── TargetVideoTrack.java
│ │ │ ├── TranscodeAudioPresenter.kt
│ │ │ ├── TranscodeToVp9Presenter.kt
│ │ │ ├── TranscodeVideoGlPresenter.kt
│ │ │ ├── TranscodingConfigPresenter.java
│ │ │ ├── TransformationPresenter.kt
│ │ │ ├── TransformationState.java
│ │ │ ├── TrimConfig.java
│ │ │ ├── VideoFiltersPresenter.kt
│ │ │ ├── VideoTrackFormat.java
│ │ │ └── VideoWatermarkPresenter.kt
│ │ ├── fragment
│ │ │ ├── CameraSizes.kt
│ │ │ ├── DemoFilter.java
│ │ │ ├── EmptyVideoFragment.kt
│ │ │ ├── ExtractFramesFragment.kt
│ │ │ ├── ExtractedFramesAdapter.kt
│ │ │ ├── FreeTransformVideoGlFragment.java
│ │ │ ├── MediaTrackAdapter.java
│ │ │ ├── MockTranscodeFragment.java
│ │ │ ├── MuxVideoAndAudioFragment.kt
│ │ │ ├── NativeMuxerCameraFragment.kt
│ │ │ ├── NativeMuxerTranscodeFragment.kt
│ │ │ ├── RecordAudioFragment.kt
│ │ │ ├── RecordCamera2Fragment.kt
│ │ │ ├── SquareCenterCropFragment.java
│ │ │ ├── TranscodeAudioFragment.kt
│ │ │ ├── TranscodeToVp9Fragment.kt
│ │ │ ├── TranscodeVideoGlFragment.java
│ │ │ ├── VideoFilmStripView.kt
│ │ │ ├── VideoFilterPreviewFragment.java
│ │ │ ├── VideoFiltersFragment.java
│ │ │ └── VideoWatermarkFragment.java
│ │ └── view
│ │ │ ├── AudioTrackViewHolder.java
│ │ │ ├── AutoFitSurfaceView.kt
│ │ │ ├── GenericTrackViewHolder.java
│ │ │ └── VideoTrackViewHolder.java
│ │ └── utils
│ │ ├── DeviceUtil.java
│ │ ├── TrackMetadataUtil.java
│ │ └── TransformationUtil.java
│ └── res
│ ├── layout
│ ├── activity_info.xml
│ ├── activity_main.xml
│ ├── fragment_audio_record.xml
│ ├── fragment_camera2_record.xml
│ ├── fragment_empty_video.xml
│ ├── fragment_extract_frames.xml
│ ├── fragment_main.xml
│ ├── fragment_mock_transcode.xml
│ ├── fragment_mux_video_audio.xml
│ ├── fragment_square_center_crop.xml
│ ├── fragment_transcode_audio.xml
│ ├── fragment_transcode_video_gl.xml
│ ├── fragment_video_filter_preview.xml
│ ├── fragment_video_filters.xml
│ ├── fragment_video_overlay_gl.xml
│ ├── fragment_video_vp9.xml
│ ├── fragment_video_watermark.xml
│ ├── item_audio_track.xml
│ ├── item_frame.xml
│ ├── item_generic_track.xml
│ ├── item_video_track.xml
│ ├── section_audio_volume.xml
│ ├── section_pick_audio.xml
│ ├── section_pick_video.xml
│ ├── section_target_video_configuration.xml
│ ├── section_transformation_progress.xml
│ └── section_trim.xml
│ ├── menu
│ └── main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── file_provider_paths.xml
├── litr-ffmpeg
├── .gitignore
├── README.md
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ ├── CMakeLists.txt
│ ├── FFmpeg.h
│ ├── Logging.h
│ ├── MediaMuxer.cpp
│ ├── MediaMuxer.h
│ ├── NativeLogger.cpp
│ ├── NativeMediaMuxer.cpp
│ ├── build_ffmpeg.sh
│ └── ffmpeg_bundled
│ │ ├── README.md
│ │ ├── android-libs
│ │ ├── arm64-v8a
│ │ │ ├── libavcodec.so
│ │ │ ├── libavformat.so
│ │ │ └── libavutil.so
│ │ ├── armeabi-v7a
│ │ │ ├── libavcodec.so
│ │ │ ├── libavformat.so
│ │ │ └── libavutil.so
│ │ ├── x86
│ │ │ ├── libavcodec.so
│ │ │ ├── libavformat.so
│ │ │ └── libavutil.so
│ │ └── x86_64
│ │ │ ├── libavcodec.so
│ │ │ ├── libavformat.so
│ │ │ └── libavutil.so
│ │ └── extra
│ │ └── libavutil
│ │ └── avconfig.h
│ └── java
│ └── com
│ └── linkedin
│ └── android
│ └── litr
│ └── muxers
│ ├── MediaFormatEx.kt
│ ├── NativeLogger.kt
│ ├── NativeMediaMuxer.kt
│ ├── NativeMediaMuxerMediaTarget.kt
│ ├── NativeMuxersLib.kt
│ └── NativeOutputFormats.kt
├── litr-filters
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── linkedin
│ └── android
│ └── litr
│ └── filter
│ ├── audio
│ ├── AudioOverlayFilter.kt
│ └── VolumeFilter.kt
│ └── video
│ └── gl
│ ├── AnimationFrameProvider.java
│ ├── BaseOverlayGlFilter.java
│ ├── BilateralFilter.java
│ ├── BitmapOverlayFilter.java
│ ├── BrightnessFilter.java
│ ├── BulgeDistortionFilter.java
│ ├── CgaColorspaceFilter.java
│ ├── ColorBalanceFilter.java
│ ├── ColorMatrixFilter.java
│ ├── ColorMonochromeFilter.java
│ ├── ContrastFilter.java
│ ├── CrossHatchFilter.java
│ ├── ExposureFilter.java
│ ├── FalseColorFilter.java
│ ├── FrameSequenceAnimationOverlayFilter.java
│ ├── GammaFilter.java
│ ├── GaussianBlurFilter.java
│ ├── GlassSphereFilter.java
│ ├── GrayscaleFilter.java
│ ├── HalftoneFilter.java
│ ├── HazeFilter.java
│ ├── HueFilter.java
│ ├── InversionFilter.java
│ ├── KuwaharaFilter.java
│ ├── LaplacianFilter.java
│ ├── LevelsFilter.java
│ ├── LocalBinaryPatternFilter.java
│ ├── OpacityFilter.java
│ ├── PixelationFilter.java
│ ├── PosterizationFilter.java
│ ├── RgbFilter.java
│ ├── SaturationFilter.java
│ ├── SepiaFilter.java
│ ├── ShadowsHighlightsFilter.java
│ ├── SharpenFilter.java
│ ├── SolarizeFilter.java
│ ├── SolidBackgroundColorFilter.java
│ ├── SphereRefractionFilter.java
│ ├── SwirlFilter.java
│ ├── ToonFilter.java
│ ├── VibranceFilter.java
│ ├── VignetteFilter.java
│ ├── WeakPixelInclusionFilter.java
│ ├── WhiteBalanceFilter.java
│ └── ZoomBlurFilter.java
├── litr
├── build.gradle
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ ├── audio-processor.cpp
│ │ └── oboe_resampler
│ │ │ ├── CMakeLists.txt
│ │ │ ├── HyperbolicCosineWindow.h
│ │ │ ├── IntegerRatio.cpp
│ │ │ ├── IntegerRatio.h
│ │ │ ├── KaiserWindow.h
│ │ │ ├── LinearResampler.cpp
│ │ │ ├── LinearResampler.h
│ │ │ ├── MultiChannelResampler.cpp
│ │ │ ├── MultiChannelResampler.h
│ │ │ ├── PolyphaseResampler.cpp
│ │ │ ├── PolyphaseResampler.h
│ │ │ ├── PolyphaseResamplerMono.cpp
│ │ │ ├── PolyphaseResamplerMono.h
│ │ │ ├── PolyphaseResamplerStereo.cpp
│ │ │ ├── PolyphaseResamplerStereo.h
│ │ │ ├── README.md
│ │ │ ├── SincResampler.cpp
│ │ │ ├── SincResampler.h
│ │ │ ├── SincResamplerStereo.cpp
│ │ │ └── SincResamplerStereo.h
│ └── java
│ │ └── com
│ │ └── linkedin
│ │ └── android
│ │ └── litr
│ │ ├── Annotations.kt
│ │ ├── MarshallingTransformationListener.java
│ │ ├── MediaTransformer.java
│ │ ├── MimeType.java
│ │ ├── TrackTransform.java
│ │ ├── TransformationJob.java
│ │ ├── TransformationListener.java
│ │ ├── TransformationOptions.java
│ │ ├── analytics
│ │ ├── TrackTransformationInfo.java
│ │ └── TransformationStatsCollector.java
│ │ ├── codec
│ │ ├── Decoder.java
│ │ ├── Encoder.java
│ │ ├── Frame.java
│ │ ├── MediaCodecDecoder.java
│ │ ├── MediaCodecEncoder.java
│ │ ├── PassthroughBufferEncoder.kt
│ │ └── PassthroughDecoder.kt
│ │ ├── exception
│ │ ├── InsufficientDiskSpaceException.java
│ │ ├── MediaSourceException.java
│ │ ├── MediaTargetException.java
│ │ ├── MediaTransformationException.java
│ │ └── TrackTranscoderException.java
│ │ ├── filter
│ │ ├── BufferFilter.kt
│ │ ├── GlFilter.java
│ │ ├── GlFrameRenderFilter.java
│ │ ├── Transform.java
│ │ ├── util
│ │ │ └── GlFilterUtil.java
│ │ └── video
│ │ │ └── gl
│ │ │ ├── DefaultVideoFrameRenderFilter.java
│ │ │ ├── VideoFrameRenderFilter.java
│ │ │ ├── parameter
│ │ │ ├── ShaderParameter.java
│ │ │ ├── Uniform1f.java
│ │ │ ├── Uniform1fv.java
│ │ │ ├── Uniform1i.java
│ │ │ ├── Uniform1iv.java
│ │ │ ├── Uniform2f.java
│ │ │ ├── Uniform2fv.java
│ │ │ ├── Uniform2i.java
│ │ │ ├── Uniform2iv.java
│ │ │ ├── Uniform3f.java
│ │ │ ├── Uniform3fv.java
│ │ │ ├── Uniform3i.java
│ │ │ ├── Uniform3iv.java
│ │ │ ├── Uniform4f.java
│ │ │ ├── Uniform4fv.java
│ │ │ ├── Uniform4i.java
│ │ │ ├── Uniform4iv.java
│ │ │ ├── UniformMatrix2fv.java
│ │ │ ├── UniformMatrix3fv.java
│ │ │ └── UniformMatrix4fv.java
│ │ │ └── shader
│ │ │ └── VertexShader.java
│ │ ├── frameextract
│ │ ├── FrameExtractJob.kt
│ │ ├── FrameExtractListener.kt
│ │ ├── FrameExtractMode.kt
│ │ ├── FrameExtractParameters.kt
│ │ ├── VideoFrameExtractor.kt
│ │ ├── behaviors
│ │ │ ├── FrameExtractBehavior.kt
│ │ │ └── MediaMetadataExtractBehavior.kt
│ │ └── queue
│ │ │ ├── ComparableFutureTask.kt
│ │ │ └── PriorityExecutorUtil.kt
│ │ ├── io
│ │ ├── AudioRecordMediaSource.kt
│ │ ├── Camera2MediaSource.kt
│ │ ├── CaptureMediaSource.kt
│ │ ├── MediaExtractorMediaSource.java
│ │ ├── MediaMuxerMediaTarget.java
│ │ ├── MediaRange.java
│ │ ├── MediaSource.java
│ │ ├── MediaTarget.java
│ │ ├── MediaTargetSample.kt
│ │ ├── MockVideoMediaSource.kt
│ │ └── WavMediaTarget.kt
│ │ ├── preview
│ │ ├── PreviewEglConfigChooser.java
│ │ ├── PreviewEglContextFactory.java
│ │ ├── VideoFilterPreviewView.java
│ │ └── VideoPreviewRenderer.java
│ │ ├── render
│ │ ├── AudioProcessor.kt
│ │ ├── AudioProcessorFactory.kt
│ │ ├── AudioRenderer.kt
│ │ ├── FrameDropper.kt
│ │ ├── GlFramebuffer.kt
│ │ ├── GlRenderUtils.java
│ │ ├── GlSingleFrameRenderer.kt
│ │ ├── GlTexture.kt
│ │ ├── GlVideoRenderer.java
│ │ ├── OboeAudioProcessor.kt
│ │ ├── PassthroughAudioProcessor.kt
│ │ ├── Renderer.java
│ │ ├── SingleFrameRenderer.kt
│ │ ├── VideoRenderInputSurface.java
│ │ └── VideoRenderOutputSurface.java
│ │ ├── test
│ │ ├── MockMediaTransformer.java
│ │ └── TransformationEvent.java
│ │ ├── transcoder
│ │ ├── AudioTrackTranscoder.java
│ │ ├── PassthroughTranscoder.java
│ │ ├── TrackTranscoder.java
│ │ ├── TrackTranscoderFactory.java
│ │ └── VideoTrackTranscoder.java
│ │ └── utils
│ │ ├── ByteBufferPool.kt
│ │ ├── CodecUtils.java
│ │ ├── DiskUtil.java
│ │ ├── MediaFormatUtils.kt
│ │ ├── TimeUtils.java
│ │ └── TranscoderUtils.java
│ └── test
│ └── java
│ └── com
│ └── linkedin
│ └── android
│ └── litr
│ ├── TransformationJobShould.java
│ ├── codec
│ ├── PassthroughBufferEncoderShould.kt
│ └── PassthroughDecoderShould.kt
│ ├── io
│ └── MediaMuxerMediaTargetShould.java
│ ├── render
│ └── AudioRendererShould.kt
│ ├── transcoder
│ ├── AudioTrackTranscoderShould.java
│ ├── FrameDropperShould.kt
│ ├── PassthroughTranscoderShould.java
│ └── VideoTrackTranscoderShould.java
│ └── utils
│ └── TranscoderUtilsShould.java
└── settings.gradle
/.github/workflows/deploy-snapshot.yml:
--------------------------------------------------------------------------------
1 | name: Deploy snapshot
2 | on:
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | publish:
8 | runs-on: ubuntu-latest
9 | if: ${{ !contains(github.event.head_commit.message, 'Prepare for release') }}
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Cache Gradle Files
14 | uses: actions/cache@v2
15 | with:
16 | path: |
17 | ~/.gradle/caches/
18 | ~/.gradle/wrapper/
19 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
20 | restore-keys: |
21 | ${{ runner.os }}-gradle-
22 |
23 | - name: Set up Java
24 | uses: actions/setup-java@v1
25 | with:
26 | java-version: 11
27 |
28 | - name: Build
29 | run: ./gradlew build
30 |
31 | - name: Publish package
32 | run: ./gradlew publishAllPublicationsToSonatypeSnapshotRepository
33 | env:
34 | SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
35 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/gradle-wrapper-validation.yml:
--------------------------------------------------------------------------------
1 | name: "Validate Gradle Wrapper"
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - '*'
9 |
10 | jobs:
11 | validation:
12 | name: "Validation"
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: gradle/wrapper-validation-action@v1
17 |
18 |
--------------------------------------------------------------------------------
/.github/workflows/merge-check.yml:
--------------------------------------------------------------------------------
1 | name: Merge checks
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - '*'
10 |
11 | jobs:
12 | build:
13 | name: Build project
14 | runs-on: ubuntu-latest
15 | if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
16 | steps:
17 | - name: Checkout Repo
18 | uses: actions/checkout@v2
19 |
20 | - name: Cache Gradle Files
21 | uses: actions/cache@v2
22 | with:
23 | path: |
24 | ~/.gradle/caches/
25 | ~/.gradle/wrapper/
26 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
27 | restore-keys: |
28 | ${{ runner.os }}-gradle-
29 |
30 | - name: Run Gradle tasks
31 | run: ./gradlew build
32 |
--------------------------------------------------------------------------------
/.github/workflows/publish-maven-central.yml:
--------------------------------------------------------------------------------
1 | name: Publish package to the Maven Central Repository
2 | on:
3 | release:
4 | types: [published]
5 | branches:
6 | - main
7 | jobs:
8 | publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Set up Java
14 | uses: actions/setup-java@v1
15 | with:
16 | java-version: 11
17 |
18 | - name: Build
19 | run: ./gradlew build
20 |
21 | - name: Publish package
22 | run: ./gradlew publishAllPublicationsToMavenCentralRepository
23 | env:
24 | SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
25 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
26 | LITR_GPG_PRIVATE_KEY: ${{ secrets.LITR_GPG_PRIVATE_KEY }}
27 | LITR_GPG_PRIVATE_KEY_PASSWORD: ${{ secrets.LITR_GPG_PRIVATE_KEY_PASSWORD }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .idea
5 | .idea/caches
6 | .idea/libraries
7 | .idea/modules.xml
8 | .idea/workspace.xml
9 | .idea/navEditor.xml
10 | .idea/assetWizardSettings.xml
11 | .DS_Store
12 | build
13 | captures
14 | .externalNativeBuild
15 | .cxx
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to LiTr
2 |
3 | LiTr is designed to encourage contribution. Transformation process is divided into five distinct steps, and components for each step are pluggable:
4 | - Overriding `MediaSource` allows reading data from sources Android's MediaExtractor can't, such as stream from camera or unsupported container formats
5 | - Implementing custom `Decoder` and/or `Encoder` allows experimentation with not yet supported codecs (for example, AV1)
6 | - Overriding `MediaTarget` allows writing data using custom muxer (MKV, for instance)
7 | - Custom `Renderer` can do things beyond simple resizing - ML based frame modification, audio mixing, etc.
8 |
9 | In addition, it should be quite easy to develop and contribute new filters by implementing `GlFilter` interface. Please contribute filters into `litr-filters` library.
10 |
11 | ## Contribution Agreement
12 |
13 | As a contributor, you represent that the code you submit is your original work or that of your employer
14 | (in which case you represent you have the right to bind your employer). By submitting code, you
15 | (and, if applicable, your employer) are licensing the submitted code to LinkedIn and the open source
16 | community subject to the BSD 2-Clause license.
17 |
18 | ## Responsible Disclosure of Security Vulnerabilities
19 |
20 | Please do not file reports on Github for security issues. Please send vulnerability reports to
21 | security@linkedin.com preferably with the title "Github linkedin/ - ".
22 |
23 | ## Tips for Getting Your Pull Request Accepted
24 |
25 | - Make sure all new features are tested and the tests pass.
26 | - Bug fixes must include a test case demonstrating the error that it fixes.
27 | - Open an issue first and seek advice for your change before submitting a pull request. Large features which have never been discussed are unlikely to be accepted. You have been warned.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-CLAUSE LICENSE
2 | Copyright 2019 LinkedIn Corporation
3 | All Rights Reserved.
4 | Redistribution and use in source and binary forms, with or
5 | without modification, are permitted provided that the following
6 | conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following
11 | disclaimer in the documentation and/or other materials provided
12 | with the distribution.
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/RELEASING.md:
--------------------------------------------------------------------------------
1 | # Releasing
2 |
3 | 1. Change the version in `gradle.properties` to a non-SNAPSHOT version.
4 | 2. Update the `CHANGELOG.md` for the impending release.
5 | 3. Update the `README.md` with the new version.
6 | 4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version)
7 | 5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version)
8 | 6. Update the `gradle.properties` to the next SNAPSHOT version.
9 | 7. `git commit -am "Prepare next development version."`
10 | 8. `git push && git push --tags`
11 | 9. Create a new release in the releases tab on GitHub
12 | 10. Wait for the [publish-maven-central.yml](.github/workflows/publish-maven-central.yml) action to complete.
13 | 11. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact.
14 |
15 | ## How it works
16 |
17 | The [deploy-snapshot.yml](.github/workflows/deploy-snapshot.yml) workflow runs on every
18 | push to the main branch as long as the commit message does not contain `Prepare for release`. This
19 | workflow calls Gradle to publish to the Sonatype snapshot repository.
20 |
21 | For actual releases, there is a separate [publish-maven-central.yml](.github/workflows/publish-maven-central.yml)
22 | workflow which runs after a new release is created in the GitHub UI. This will call Gradle on the
23 | tagged release commit and upload to the staging repository. After that completes, you will need to
24 | go and promote the artifacts to production.
25 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.20'
3 |
4 | repositories {
5 | google()
6 | mavenCentral()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 |
20 | group = GROUP_ID
21 | version = VERSION_NAME
22 | }
23 |
24 | ext {
25 | minSdkVersion = 18
26 | targetSdkVersion = 30
27 | compileSdkVersion = 33
28 | buildToolsVersion = "33.0.1"
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
19 | # AndroidX package structure to make it clearer which packages are bundled with the
20 | # Android operating system, and which are packaged with your app's APK
21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
22 | android.useAndroidX=true
23 |
24 | GROUP_ID=com.linkedin.android.litr
25 | VERSION_NAME=1.5.8-SNAPSHOT
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Feb 19 05:51:03 UZT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/litr-demo/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 | android {
8 | compileSdkVersion rootProject.ext.compileSdkVersion
9 | buildToolsVersion rootProject.ext.buildToolsVersion
10 |
11 | namespace 'com.linkedin.android.litr.demo'
12 |
13 | defaultConfig {
14 | applicationId "com.linkedin.android.litr.demo"
15 | minSdkVersion rootProject.ext.minSdkVersion
16 | targetSdkVersion rootProject.ext.targetSdkVersion
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | multiDexEnabled true
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 |
26 | lint {
27 | abortOnError true
28 | checkDependencies true
29 | checkTestSources true
30 | checkReleaseBuilds false
31 | }
32 |
33 | buildFeatures {
34 | dataBinding true
35 | }
36 |
37 | kotlinOptions {
38 | freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation project(':litr')
44 | implementation project(':litr-filters')
45 |
46 | // uncomment to experiment with ffmpeg
47 | // implementation project(':litr-ffmpeg')
48 |
49 | implementation 'androidx.appcompat:appcompat:1.2.0'
50 | implementation 'androidx.recyclerview:recyclerview:1.2.0'
51 | implementation 'androidx.multidex:multidex:2.0.1'
52 | implementation 'com.github.bumptech.glide:glide:4.14.2'
53 | implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3'
54 | implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
55 | implementation 'com.google.android.material:material:1.3.0'
56 | implementation 'androidx.core:core-ktx:1.3.2'
57 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
58 |
59 | kapt 'com.github.bumptech.glide:compiler:4.14.2'
60 | }
61 |
--------------------------------------------------------------------------------
/litr-demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
42 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/DemoCasesAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo;
9 |
10 | import android.content.Context;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.ArrayAdapter;
15 | import android.widget.TextView;
16 | import androidx.annotation.NonNull;
17 | import androidx.annotation.Nullable;
18 |
19 | class DemoCasesAdapter extends ArrayAdapter {
20 |
21 | DemoCasesAdapter(@NonNull Context context, int resource) {
22 | super(context, resource);
23 |
24 | addAll(DemoCase.values());
25 | }
26 |
27 | @Override
28 | public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
29 | View view = convertView;
30 | if (view == null) {
31 | view = LayoutInflater.from(getContext()).inflate(android.R.layout.simple_list_item_1, null);
32 | }
33 |
34 | TextView textView = view.findViewById(android.R.id.text1);
35 | textView.setText(getItem(position).displayName);
36 |
37 | return textView;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/MainFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo;
9 |
10 | import android.os.Bundle;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.AdapterView;
15 | import android.widget.ArrayAdapter;
16 | import androidx.annotation.NonNull;
17 | import androidx.fragment.app.ListFragment;
18 |
19 | public class MainFragment extends ListFragment implements AdapterView.OnItemClickListener {
20 | private static final String KEY_DEMO_CASE = "demoCase";
21 |
22 | private DemoCase demoCase;
23 |
24 | @Override
25 | public View onCreateView(LayoutInflater inflater,
26 | ViewGroup container, Bundle savedInstanceState) {
27 | View view = inflater.inflate(R.layout.fragment_main, container, false);
28 | return view;
29 | }
30 |
31 | @Override
32 | public void onActivityCreated(Bundle savedInstanceState) {
33 | super.onActivityCreated(savedInstanceState);
34 | ArrayAdapter adapter = new DemoCasesAdapter(getContext(), android.R.layout.simple_list_item_1);
35 | setListAdapter(adapter);
36 | getListView().setOnItemClickListener(this);
37 |
38 | if (savedInstanceState != null) {
39 | demoCase = (DemoCase) savedInstanceState.getSerializable(KEY_DEMO_CASE);
40 | if (demoCase != null) {
41 | startDemoCase(demoCase);
42 | }
43 | }
44 | }
45 |
46 | @Override
47 | public void onResume() {
48 | super.onResume();
49 |
50 | demoCase = null;
51 | }
52 |
53 | @Override
54 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
55 | demoCase = (DemoCase) getListAdapter().getItem(position);
56 | startDemoCase(demoCase);
57 | }
58 |
59 | @Override
60 | public void onSaveInstanceState(@NonNull Bundle outState) {
61 | super.onSaveInstanceState(outState);
62 |
63 | outState.putSerializable(KEY_DEMO_CASE, demoCase);
64 | }
65 |
66 | private void startDemoCase(@NonNull DemoCase demoCase) {
67 | getActivity().getSupportFragmentManager().beginTransaction()
68 | .replace(R.id.fragment_container, demoCase.fragment, demoCase.fragmentTag)
69 | .addToBackStack(null)
70 | .commit();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/MediaPickerListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo;
9 |
10 | import android.net.Uri;
11 | import androidx.annotation.NonNull;
12 |
13 | public interface MediaPickerListener {
14 |
15 | void onMediaPicked(@NonNull Uri uri);
16 | }
17 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/AudioTrackFormat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | public class AudioTrackFormat extends MediaTrackFormat {
13 |
14 | public int channelCount;
15 | public int samplingRate;
16 | public int bitrate;
17 | public long duration;
18 |
19 | public AudioTrackFormat(int index, @NonNull String mimeType) {
20 | super(index, mimeType);
21 | }
22 |
23 | public AudioTrackFormat(@NonNull AudioTrackFormat audioTrackFormat) {
24 | super(audioTrackFormat);
25 | this.channelCount = audioTrackFormat.channelCount;
26 | this.samplingRate = audioTrackFormat.samplingRate;
27 | this.bitrate = audioTrackFormat.bitrate;
28 | this.duration = audioTrackFormat.duration;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/AudioVolumeConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.databinding.BaseObservable;
13 | import androidx.databinding.Bindable;
14 | import androidx.databinding.BindingAdapter;
15 |
16 | import com.google.android.material.slider.Slider;
17 |
18 | public class AudioVolumeConfig extends BaseObservable {
19 |
20 | public final Slider.OnChangeListener onValueChangeListener = (slider, value, fromUser) -> {
21 | this.value = slider.getValue();
22 | };
23 |
24 | public boolean enabled;
25 | public Float value = 1.0f;
26 |
27 | @Bindable
28 | public Boolean getEnabled() {
29 | return enabled;
30 | }
31 |
32 | @BindingAdapter(value = "onChangeListener")
33 | public static void setOnChangeListener(@NonNull Slider slider, @Nullable Slider.OnChangeListener onChangeListener) {
34 | slider.addOnChangeListener(onChangeListener);
35 | }
36 |
37 | public void setEnabled(Boolean enabled) {
38 | this.enabled = enabled;
39 | notifyChange();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/Converter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import android.text.TextUtils;
11 | import androidx.annotation.NonNull;
12 | import androidx.databinding.InverseMethod;
13 |
14 | public class Converter {
15 |
16 | @InverseMethod("stringToInteger")
17 | public static String integerToString(int value) {
18 | return Integer.toString(value);
19 | }
20 |
21 | public static int stringToInteger(@NonNull String string) {
22 | if (!TextUtils.isEmpty(string)) {
23 | return Integer.parseInt(string);
24 | }
25 | return 0;
26 | }
27 |
28 | @InverseMethod("stringToVideoBitrate")
29 | public static String videoBitrateToString(int bitrate) {
30 | if (bitrate > 0) {
31 | return Float.toString((float) bitrate / 1000000);
32 | }
33 | return Integer.toString(bitrate);
34 | }
35 |
36 | public static int stringToVideoBitrate(@NonNull String string) {
37 | if (!TextUtils.isEmpty(string)) {
38 | return (int) (Float.parseFloat(string) * 1000000);
39 | }
40 | return 0;
41 | }
42 |
43 | @InverseMethod("stringToAudioBitrate")
44 | public static String audioBitrateToString(int bitrate) {
45 | return Integer.toString(bitrate / 1000);
46 | }
47 |
48 | public static int stringToAudioBitrate(@NonNull String string) {
49 | if (!TextUtils.isEmpty(string)) {
50 | return Integer.parseInt(string) * 1000;
51 | }
52 | return 0;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/GenericTrackFormat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | public class GenericTrackFormat extends MediaTrackFormat {
13 |
14 | public GenericTrackFormat(int index, @NonNull String mimeType) {
15 | super(index, mimeType);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/MediaTrackFormat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | public class MediaTrackFormat {
13 |
14 | public int index;
15 | public String mimeType;
16 |
17 | MediaTrackFormat(int index, @NonNull String mimeType) {
18 | this.index = index;
19 | this.mimeType = mimeType;
20 | }
21 |
22 | MediaTrackFormat(@NonNull MediaTrackFormat mediaTrackFormat) {
23 | this.index = mediaTrackFormat.index;
24 | this.mimeType = mediaTrackFormat.mimeType;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/SourceMedia.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import android.net.Uri;
11 | import androidx.databinding.BaseObservable;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class SourceMedia extends BaseObservable {
17 |
18 | public Uri uri;
19 | public long size;
20 | public float duration;
21 |
22 | public List tracks = new ArrayList<>();
23 |
24 | public boolean hasAudio() {
25 | for (MediaTrackFormat trackFormat : tracks) {
26 | if (trackFormat instanceof AudioTrackFormat) {
27 | return true;
28 | }
29 | }
30 | return false;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TargetAudioTrack.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | public class TargetAudioTrack extends TargetTrack {
11 | public TargetAudioTrack(int sourceTrackIndex,
12 | boolean shouldInclude,
13 | boolean shouldTranscode,
14 | AudioTrackFormat format) {
15 | super(sourceTrackIndex, shouldInclude, shouldTranscode, format);
16 | }
17 |
18 | public AudioTrackFormat getTrackFormat() {
19 | return (AudioTrackFormat) format;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TargetTrack.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import android.net.Uri;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.databinding.BaseObservable;
14 |
15 | public class TargetTrack extends BaseObservable {
16 | public int sourceTrackIndex;
17 | public boolean shouldInclude;
18 | public boolean shouldTranscode;
19 | public boolean shouldApplyOverlay;
20 | public Uri overlay;
21 | public MediaTrackFormat format;
22 |
23 | public TargetTrack(int sourceTrackIndex, boolean shouldInclude, boolean shouldTranscode, @NonNull MediaTrackFormat format) {
24 | this.sourceTrackIndex = sourceTrackIndex;
25 | this.shouldInclude = shouldInclude;
26 | this.shouldTranscode = shouldTranscode;
27 | this.format = format;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TargetVideoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.demo.data;
2 |
3 | import android.view.View;
4 | import android.widget.AdapterView;
5 |
6 | import androidx.databinding.BaseObservable;
7 |
8 | public class TargetVideoConfiguration extends BaseObservable {
9 |
10 | public int rotation;
11 |
12 | public void onRotationSelected(AdapterView> parent, View view, int pos, long id) {
13 | switch (pos) {
14 | case 0:
15 | // landscape
16 | rotation = 0;
17 | break;
18 | case 1:
19 | // portrait
20 | rotation = 90;
21 | break;
22 | case 2:
23 | // reverse landscape
24 | rotation = 180;
25 | break;
26 | case 3:
27 | // reverse portrait
28 | rotation = 270;
29 | break;
30 | default:
31 | // nor handled yet
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TargetVideoTrack.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import android.view.View;
11 | import android.widget.AdapterView;
12 |
13 | public class TargetVideoTrack extends TargetTrack {
14 |
15 | public TargetVideoTrack(int sourceTrackIndex,
16 | boolean shouldInclude,
17 | boolean shouldTranscode,
18 | VideoTrackFormat format) {
19 | super(sourceTrackIndex, shouldInclude, shouldTranscode, format);
20 | }
21 |
22 | public VideoTrackFormat getTrackFormat() {
23 | return (VideoTrackFormat) format;
24 | }
25 |
26 | public void onMimeTypeSelected(AdapterView> parent, View view, int pos, long id) {
27 | format.mimeType = (String) parent.getAdapter().getItem(pos);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TranscodingConfigPresenter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import android.content.Context;
11 | import android.widget.ImageView;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.databinding.BindingAdapter;
15 |
16 | import com.bumptech.glide.Glide;
17 | import com.linkedin.android.litr.demo.BaseTransformationFragment;
18 | import com.linkedin.android.litr.demo.MediaPickerListener;
19 |
20 | public class TranscodingConfigPresenter {
21 |
22 | private BaseTransformationFragment fragment;
23 | private TargetMedia targetMedia;
24 |
25 | public TranscodingConfigPresenter(@NonNull BaseTransformationFragment fragment, @NonNull TargetMedia targetMedia) {
26 | this.fragment = fragment;
27 | this.targetMedia = targetMedia;
28 | }
29 |
30 | public void onIncludeTrackChanged(@NonNull TargetTrack targetTrack, boolean include) {
31 | targetTrack.shouldInclude = include;
32 | targetTrack.notifyChange();
33 | targetMedia.notifyChange();
34 | }
35 |
36 | public void onTranscodeTrackChanged(@NonNull TargetTrack targetTrack, boolean transcode) {
37 | if (targetTrack instanceof TargetVideoTrack) {
38 | ((TargetVideoTrack) targetTrack).shouldTranscode = transcode;
39 | } else if (targetTrack instanceof TargetAudioTrack) {
40 | ((TargetAudioTrack) targetTrack).shouldTranscode = transcode;
41 | }
42 | targetTrack.notifyChange();
43 | }
44 |
45 | public void onApplyOverlayChanged(@NonNull TargetTrack targetTrack, boolean applyOverlay) {
46 | targetTrack.shouldApplyOverlay = applyOverlay;
47 | targetTrack.notifyChange();
48 | }
49 |
50 | public void onPickOverlayClicked(@NonNull MediaPickerListener mediaPickerListener) {
51 | fragment.pickOverlay(mediaPickerListener);
52 | }
53 |
54 | public void onPickAudioOverlayClicked(@NonNull MediaPickerListener mediaPickerListener) {
55 | fragment.pickAudio(mediaPickerListener);
56 | }
57 |
58 | public Context getContext() {
59 | return fragment.getContext();
60 | }
61 |
62 | @BindingAdapter("overlayThumbnail")
63 | public static void loadImage(ImageView view, String imageUrl) {
64 | Glide.with(view.getContext())
65 | .load(imageUrl)
66 | .into(view);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TransformationState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.IntDef;
11 | import androidx.annotation.Nullable;
12 | import androidx.databinding.BaseObservable;
13 |
14 | import java.lang.annotation.Retention;
15 | import java.lang.annotation.RetentionPolicy;
16 |
17 | public class TransformationState extends BaseObservable {
18 |
19 | public static final int MAX_PROGRESS = 100;
20 |
21 | public static final int STATE_IDLE = 0;
22 | public static final int STATE_RUNNING = 1;
23 | public static final int STATE_COMPLETED = 3;
24 | public static final int STATE_CANCELLED = 4;
25 | public static final int STATE_ERROR = 5;
26 |
27 | @Retention(RetentionPolicy.SOURCE)
28 | @IntDef({ STATE_IDLE, STATE_RUNNING, STATE_COMPLETED, STATE_CANCELLED, STATE_ERROR})
29 | @interface State {}
30 |
31 | public String requestId;
32 |
33 | public int state;
34 | public int progress;
35 | public String stats;
36 |
37 | public TransformationState() {
38 | state = STATE_IDLE;
39 | progress = 0;
40 | stats = null;
41 | }
42 |
43 | public void setState(@State int state) {
44 | this.state = state;
45 | notifyChange();
46 | }
47 |
48 | public void setProgress(int progress) {
49 | this.progress = progress;
50 | notifyChange();
51 | }
52 |
53 | public void setStats(@Nullable String stats) {
54 | this.stats = stats;
55 | notifyChange();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/TrimConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.databinding.BaseObservable;
13 | import androidx.databinding.Bindable;
14 | import androidx.databinding.BindingAdapter;
15 |
16 | import com.google.android.material.slider.RangeSlider;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | public class TrimConfig extends BaseObservable {
22 |
23 | public final RangeSlider.OnChangeListener onValueChangeListener = (slider, value, fromUser) -> {
24 | range = slider.getValues();
25 | };
26 |
27 | public boolean enabled;
28 | public List range = new ArrayList<>(2);
29 |
30 | public TrimConfig() {
31 | range.add(0f);
32 | range.add(1f);
33 | }
34 |
35 | @Bindable
36 | public Boolean getEnabled() {
37 | return enabled;
38 | }
39 |
40 | @BindingAdapter(value = "onChangeListener")
41 | public static void setOnChangeListener(@NonNull RangeSlider rangeSlider, @Nullable RangeSlider.OnChangeListener onChangeListener) {
42 | rangeSlider.addOnChangeListener(onChangeListener);
43 | }
44 |
45 | public void setEnabled(Boolean enabled) {
46 | this.enabled = enabled;
47 | notifyChange();
48 | }
49 |
50 | public void setTrimEnd(float trimEnd) {
51 | range.set(1, trimEnd);
52 | notifyChange();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/VideoFiltersPresenter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data
9 |
10 | import android.content.Context
11 | import com.linkedin.android.litr.MediaTransformer
12 | import com.linkedin.android.litr.TransformationOptions
13 | import java.util.UUID
14 |
15 | class VideoFiltersPresenter(
16 | private val context: Context,
17 | private val mediaTransformer: MediaTransformer
18 | ) : TransformationPresenter(context, mediaTransformer) {
19 |
20 | fun applyFilter(
21 | sourceMedia: SourceMedia,
22 | targetMedia: TargetMedia,
23 | transformationState: TransformationState
24 | ) {
25 | if (targetMedia.targetFile.exists()) {
26 | targetMedia.targetFile.delete()
27 | }
28 |
29 | transformationState.requestId = UUID.randomUUID().toString()
30 | val transformationListener = MediaTransformationListener(
31 | context,
32 | transformationState.requestId,
33 | transformationState,
34 | targetMedia
35 | )
36 |
37 | val transformationOptions = TransformationOptions.Builder()
38 | .setGranularity(MediaTransformer.GRANULARITY_DEFAULT)
39 | .setVideoFilters(listOf(targetMedia.filter))
40 | .build()
41 |
42 | mediaTransformer.transform(
43 | transformationState.requestId,
44 | sourceMedia.uri,
45 | targetMedia.targetFile.path,
46 | null,
47 | null,
48 | transformationListener,
49 | transformationOptions
50 | )
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/data/VideoTrackFormat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.data;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | public class VideoTrackFormat extends MediaTrackFormat {
13 |
14 | public int width;
15 | public int height;
16 | public int bitrate;
17 | public int frameRate;
18 | public int keyFrameInterval;
19 | public long duration;
20 | public int rotation;
21 |
22 | public VideoTrackFormat(int index, @NonNull String mimeType) {
23 | super(index, mimeType);
24 | }
25 |
26 | public VideoTrackFormat(@NonNull VideoTrackFormat videoTrackFormat) {
27 | super(videoTrackFormat);
28 | this.width = videoTrackFormat.width;
29 | this.height = videoTrackFormat.height;
30 | this.bitrate = videoTrackFormat.bitrate;
31 | this.frameRate = videoTrackFormat.frameRate;
32 | this.keyFrameInterval = videoTrackFormat.keyFrameInterval;
33 | this.duration = videoTrackFormat.duration;
34 | this.rotation = videoTrackFormat.rotation;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/fragment/NativeMuxerCameraFragment.kt:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.demo.fragment
2 |
3 | import android.os.Build
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.annotation.RequiresApi
9 |
10 | @RequiresApi(Build.VERSION_CODES.M)
11 | class NativeMuxerCameraFragment: RecordCamera2Fragment() {
12 | override fun onCreateView(
13 | inflater: LayoutInflater,
14 | container: ViewGroup?,
15 | savedInstanceState: Bundle?
16 | ): View {
17 | return super.onCreateView(inflater, container, savedInstanceState).also {
18 | binding.enableNativeMuxer = true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/fragment/NativeMuxerTranscodeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.demo.fragment
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.linkedin.android.litr.demo.MediaPickerListener
8 |
9 | class NativeMuxerTranscodeFragment: TranscodeVideoGlFragment(), MediaPickerListener {
10 | override fun onCreateView(
11 | inflater: LayoutInflater,
12 | container: ViewGroup?,
13 | savedInstanceState: Bundle?
14 | ): View? {
15 | return super.onCreateView(inflater, container, savedInstanceState).also {
16 | binding.enableNativeMuxer = true
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/fragment/VideoFilmStripView.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.fragment
9 |
10 | import android.content.Context
11 | import android.graphics.Bitmap
12 | import android.graphics.Canvas
13 | import android.util.AttributeSet
14 | import android.view.View
15 | import androidx.annotation.NonNull
16 |
17 |
18 | class VideoFilmStripView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
19 |
20 | private var bitmapList: MutableList? = null
21 |
22 | fun setFrameList(list: List) {
23 | bitmapList = list.toMutableList()
24 | invalidate()
25 | }
26 |
27 | override fun onDraw(@NonNull canvas: Canvas) {
28 | super.onDraw(canvas)
29 | var x = 0f
30 | bitmapList?.filterNotNull()?.forEach { bitmap ->
31 | canvas.drawBitmap(bitmap, x, 0f, null)
32 | x += bitmap.width
33 | }
34 | }
35 |
36 | fun setFrameAt(index: Int, bitmap: Bitmap?) {
37 | bitmapList?.set(index, bitmap)
38 | invalidate()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/view/AudioTrackViewHolder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.view;
9 |
10 | import android.content.Context;
11 | import android.content.Intent;
12 | import android.net.Uri;
13 |
14 | import androidx.annotation.NonNull;
15 | import androidx.recyclerview.widget.RecyclerView;
16 |
17 | import com.linkedin.android.litr.demo.MediaPickerListener;
18 | import com.linkedin.android.litr.demo.data.AudioTrackFormat;
19 | import com.linkedin.android.litr.demo.data.TargetAudioTrack;
20 | import com.linkedin.android.litr.demo.data.TranscodingConfigPresenter;
21 | import com.linkedin.android.litr.demo.databinding.ItemAudioTrackBinding;
22 |
23 | public class AudioTrackViewHolder extends RecyclerView.ViewHolder implements MediaPickerListener {
24 |
25 | private ItemAudioTrackBinding binding;
26 |
27 | public AudioTrackViewHolder(@NonNull ItemAudioTrackBinding binding) {
28 | super(binding.getRoot());
29 | this.binding = binding;
30 | }
31 |
32 | public void bind(@NonNull TranscodingConfigPresenter presenter,
33 | @NonNull AudioTrackFormat sourceTrackFormat,
34 | @NonNull TargetAudioTrack targetTrack) {
35 | binding.setTargetTrack(targetTrack);
36 | binding.setPresenter(presenter);
37 | binding.setAudioTrack(sourceTrackFormat);
38 | binding.executePendingBindings();
39 |
40 | binding.buttonPickAudioOverlay.setOnClickListener( view ->
41 | presenter.onPickAudioOverlayClicked(AudioTrackViewHolder.this)
42 | );
43 |
44 | binding.playAudioOverlay.setOnClickListener( view -> {
45 | Context context = presenter.getContext();
46 | Uri audioOverlayUri = binding.getTargetTrack().overlay;
47 |
48 | if (context != null && audioOverlayUri != null) {
49 | Intent playIntent = new Intent(Intent.ACTION_VIEW);
50 | playIntent.setDataAndType(audioOverlayUri, context.getContentResolver().getType(audioOverlayUri));
51 | context.startActivity(playIntent);
52 | }
53 | });
54 | }
55 |
56 | @Override
57 | public void onMediaPicked(@NonNull Uri uri) {
58 | binding.getTargetTrack().overlay = uri;
59 | binding.getTargetTrack().notifyChange();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/view/AutoFitSurfaceView.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 | package com.linkedin.android.litr.demo.view
11 |
12 | import android.content.Context
13 | import android.util.AttributeSet
14 | import android.util.Log
15 | import android.view.SurfaceView
16 | import kotlin.math.roundToInt
17 |
18 | private const val TAG = "AutoFitSurfaceView"
19 |
20 | /**
21 | * A [SurfaceView] that can be adjusted to a specified aspect ratio and performs center-crop
22 | * transformation of input frames.
23 | */
24 | class AutoFitSurfaceView @JvmOverloads constructor(
25 | context: Context,
26 | attrs: AttributeSet? = null,
27 | defStyle: Int = 0
28 | ) : SurfaceView(context, attrs, defStyle) {
29 |
30 | private var aspectRatio = 0f
31 |
32 | /**
33 | * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
34 | * calculated from the parameters.
35 | *
36 | * @param width Camera resolution horizontal size
37 | * @param height Camera resolution vertical size
38 | */
39 | fun setAspectRatio(width: Int, height: Int) {
40 | require(width > 0 && height > 0) { "Size cannot be negative" }
41 | aspectRatio = width.toFloat() / height.toFloat()
42 | holder.setFixedSize(width, height)
43 | requestLayout()
44 | }
45 |
46 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
47 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
48 | val width = MeasureSpec.getSize(widthMeasureSpec)
49 | val height = MeasureSpec.getSize(heightMeasureSpec)
50 | if (aspectRatio == 0f) {
51 | setMeasuredDimension(width, height)
52 | } else {
53 | // Performs center-crop transformation of the camera frames
54 | val newWidth: Int
55 | val newHeight: Int
56 | val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
57 | if (width < height * actualRatio) {
58 | newHeight = height
59 | newWidth = (height * actualRatio).roundToInt()
60 | } else {
61 | newWidth = width
62 | newHeight = (width / actualRatio).roundToInt()
63 | }
64 |
65 | Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
66 | setMeasuredDimension(newWidth, newHeight)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/view/GenericTrackViewHolder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.view;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.recyclerview.widget.RecyclerView;
12 | import com.linkedin.android.litr.demo.data.MediaTrackFormat;
13 | import com.linkedin.android.litr.demo.data.TargetTrack;
14 | import com.linkedin.android.litr.demo.data.TranscodingConfigPresenter;
15 | import com.linkedin.android.litr.demo.databinding.ItemGenericTrackBinding;
16 |
17 | public class GenericTrackViewHolder extends RecyclerView.ViewHolder {
18 |
19 | private ItemGenericTrackBinding binding;
20 |
21 | public GenericTrackViewHolder(@NonNull ItemGenericTrackBinding binding) {
22 | super(binding.getRoot());
23 | this.binding = binding;
24 | }
25 |
26 | public void bind(@NonNull TranscodingConfigPresenter presenter,
27 | @NonNull MediaTrackFormat mediaTrackFormat,
28 | @NonNull TargetTrack targetTrack) {
29 | binding.setPresenter(presenter);
30 | binding.setMediaTrack(mediaTrackFormat);
31 | binding.setTargetTrack(targetTrack);
32 | binding.executePendingBindings();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/litr-demo/src/main/java/com/linkedin/android/litr/demo/view/VideoTrackViewHolder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.demo.view;
9 |
10 | import android.net.Uri;
11 | import android.view.View;
12 | import androidx.annotation.NonNull;
13 | import androidx.recyclerview.widget.RecyclerView;
14 | import com.linkedin.android.litr.demo.MediaPickerListener;
15 | import com.linkedin.android.litr.demo.data.TargetVideoTrack;
16 | import com.linkedin.android.litr.demo.data.TranscodingConfigPresenter;
17 | import com.linkedin.android.litr.demo.data.VideoTrackFormat;
18 | import com.linkedin.android.litr.demo.databinding.ItemVideoTrackBinding;
19 |
20 | public class VideoTrackViewHolder extends RecyclerView.ViewHolder implements MediaPickerListener {
21 |
22 | private ItemVideoTrackBinding binding;
23 |
24 | public VideoTrackViewHolder(@NonNull ItemVideoTrackBinding binding) {
25 | super(binding.getRoot());
26 | this.binding = binding;
27 | }
28 |
29 | public void bind(@NonNull final TranscodingConfigPresenter presenter,
30 | @NonNull VideoTrackFormat videoTrackFormat,
31 | @NonNull TargetVideoTrack targetTrack) {
32 | binding.setPresenter(presenter);
33 | binding.setVideoTrack(videoTrackFormat);
34 | binding.setTargetTrack(targetTrack);
35 | binding.executePendingBindings();
36 |
37 | binding.buttonPickVideoOverlay.setOnClickListener(new View.OnClickListener() {
38 | @Override
39 | public void onClick(View view) {
40 | presenter.onPickOverlayClicked(VideoTrackViewHolder.this);
41 | }
42 | });
43 | }
44 |
45 | @Override
46 | public void onMediaPicked(@NonNull Uri uri) {
47 | binding.getTargetTrack().overlay = uri;
48 | binding.getTargetTrack().notifyChange();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/activity_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/fragment_video_filter_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
20 |
21 |
22 |
23 |
27 |
28 |
33 |
34 |
38 |
39 |
40 |
41 |
47 |
48 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/item_frame.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
18 |
19 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/item_generic_track.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
20 |
21 |
22 |
23 |
27 |
28 |
33 |
34 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_audio_volume.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
13 |
14 |
15 |
20 |
21 |
32 |
33 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_pick_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
21 |
22 |
28 |
29 |
34 |
35 |
40 |
41 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_pick_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
21 |
22 |
28 |
29 |
34 |
35 |
40 |
41 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_target_video_configuration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
18 |
19 |
20 |
24 |
25 |
31 |
32 |
36 |
37 |
44 |
45 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_transformation_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
19 |
20 |
21 |
26 |
27 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/layout/section_trim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
13 |
14 |
17 |
18 |
19 |
24 |
25 |
36 |
37 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/litr-demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/litr-demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/litr-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/litr-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/litr-demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | #3F51B5
9 | #303F9F
10 | #FF4081
11 |
12 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 5dp
9 | 10dp
10 | 4dp
11 | 10dp
12 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/litr-demo/src/main/res/xml/file_provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/litr-ffmpeg/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 | # FFmpeg (downloaded via cmake)
4 | src/main/cpp/ffmpeg_bundled/ffmpeg-*
5 |
6 | # FFmpeg symlink
7 | src/main/cpp/ffmpeg
--------------------------------------------------------------------------------
/litr-ffmpeg/README.md:
--------------------------------------------------------------------------------
1 | # Litr Muxers module
2 |
3 | The Litr FFmpeg module provides integration with ffmpeg. Currently, it offers
4 | `NativeMediaMuxerMediaTarget`, which uses FFmpeg for muxing
5 | individual streams into a target file container.
6 |
7 | ## Build instructions (Linux, macOS)
8 |
9 | It is necessary to manually build the FFmpeg library, so that gradle can bundle the FFmpeg binaries
10 | in the APK:
11 |
12 | * Set the following shell variable:
13 |
14 | ```
15 | cd ""
16 | FFMPEG_MODULE_PATH="$(pwd)/litr-muxers/src/main"
17 | ```
18 |
19 | * Download the [Android NDK][] and set its location in a shell variable.
20 | This build configuration has been tested on NDK r22b.
21 |
22 | ```
23 | NDK_PATH=""
24 | ```
25 |
26 | * Set the host platform (use "darwin-x86_64" for Mac OS X):
27 |
28 | ```
29 | HOST_PLATFORM="linux-x86_64"
30 | ```
31 |
32 | * Fetch FFmpeg and checkout an appropriate branch. We cannot guarantee
33 | compatibility with all versions of FFmpeg. We currently recommend version 4.2:
34 |
35 | ```
36 | cd "" && \
37 | git clone git://source.ffmpeg.org/ffmpeg && \
38 | cd ffmpeg && \
39 | git checkout release/4.2 && \
40 | FFMPEG_PATH="$(pwd)"
41 | ```
42 |
43 | * Add a link to the FFmpeg source code in the FFmpeg module `cpp` directory.
44 |
45 | ```
46 | cd "${FFMPEG_MODULE_PATH}/cpp" && \
47 | ln -s "$FFMPEG_PATH" ffmpeg
48 | ```
49 |
50 | * Execute `build_ffmpeg.sh` to build FFmpeg for `armeabi-v7a`, `arm64-v8a`,
51 | `x86` and `x86_64`. The script can be edited if you need to build for
52 | different architectures:
53 |
54 | ```
55 | cd "${FFMPEG_MODULE_PATH}/cpp" && \
56 | chmod +x build_ffmpeg.sh && \
57 | ./build_ffmpeg.sh \
58 | "${FFMPEG_MODULE_PATH}" "${NDK_PATH}" "${HOST_PLATFORM}"
59 | ```
60 |
--------------------------------------------------------------------------------
/litr-ffmpeg/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | namespace 'com.linkedin.android.litr.muxers'
8 |
9 | compileSdkVersion rootProject.ext.compileSdkVersion
10 | buildToolsVersion rootProject.ext.buildToolsVersion
11 |
12 | defaultConfig {
13 | minSdkVersion rootProject.ext.minSdkVersion
14 | targetSdkVersion rootProject.ext.targetSdkVersion
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility JavaVersion.VERSION_1_8
19 | targetCompatibility JavaVersion.VERSION_1_8
20 | }
21 |
22 | kotlinOptions {
23 | freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
24 | }
25 |
26 | externalNativeBuild {
27 | cmake {
28 | path file('src/main/cpp/CMakeLists.txt')
29 | version '3.18.1'
30 | }
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation project(':litr')
36 | }
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/FFmpeg.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 |
11 | #ifndef LITR_FFMPEG_H
12 | #define LITR_FFMPEG_H
13 |
14 | extern "C" {
15 | #include "libavutil/log.h"
16 | #include "libavformat/avformat.h"
17 | }
18 |
19 | #endif //LITR_FFMPEG_H
20 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/Logging.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 |
11 | #ifndef LITR_LOGGING_H
12 | #define LITR_LOGGING_H
13 |
14 | #include
15 |
16 | #define LOG_TAG "LiTrMuxers_JNI"
17 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
18 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
19 | #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
20 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
21 |
22 | #endif //LITR_LOGGING_H
23 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/MediaMuxer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 |
11 | #ifndef LITR_MEDIAMUXER_H
12 | #define LITR_MEDIAMUXER_H
13 |
14 | #define STATUS_OK 0
15 | #define STATUS_ERROR -1
16 |
17 | #include "FFmpeg.h"
18 |
19 | class MediaMuxer
20 | {
21 | public:
22 | MediaMuxer();
23 | ~MediaMuxer();
24 |
25 | int init(const char *path, const char *formatName);
26 | int start(const char **keys, const char **values, int optionSize);
27 | int stop();
28 | int addVideoStream(const char * codec_name, int64_t bitrate, int width, int height,
29 | uint8_t *extradata, int extradata_size);
30 | int addAudioStream(const char * codec_name, int64_t bitrate, int channels, int sample_rate,
31 | int frame_size, uint8_t *extradata, int extradata_size);
32 | int writeSampleData(int stream_index, uint8_t *buffer, int size, int64_t ptsUs, int flags);
33 |
34 | private:
35 | AVFormatContext *mContext = nullptr;
36 |
37 | AVStream* addStream(const char * codec_name, int64_t bitrate, uint8_t *extradata, int extradata_size);
38 | static void logErrorCode(const char * error, int errorCode);
39 | };
40 |
41 | #endif //LITR_MEDIAMUXER_H
42 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/NativeLogger.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 |
11 | #include
12 |
13 | #include "FFmpeg.h"
14 | #include "Logging.h"
15 |
16 | static void av_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
17 | // Check to see if we care about the log.
18 | if (level > av_log_get_level())
19 | return;
20 |
21 | va_list vl2;
22 | char line[1024];
23 | static int print_prefix = 1;
24 |
25 | // Extract and format the log line.
26 | va_copy(vl2, vl);
27 | av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
28 | va_end(vl2);
29 |
30 | // Log it via Android with the relevant logcat equivalent level.
31 | if (level <= AV_LOG_ERROR) {
32 | LOGE("FFMPEG: %s", line);
33 | } else if (level <= AV_LOG_WARNING) {
34 | LOGW("FFMPEG: %s", line);
35 | } else if (level <= AV_LOG_INFO) {
36 | LOGI("FFMPEG: %s", line);
37 | } else {
38 | LOGD("FFMPEG: %s", line);
39 | }
40 | }
41 |
42 | extern "C" JNIEXPORT void
43 | Java_com_linkedin_android_litr_muxers_NativeLogger_nativeSetup(
44 | JNIEnv *env,
45 | jobject /* this */,
46 | jint level
47 | ) {
48 | // Configure the log level and attach a callback.
49 | av_log_set_level(level);
50 | av_log_set_callback(av_log_callback);
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/README.md:
--------------------------------------------------------------------------------
1 | # Litr Muxers / Bundled FFmpeg
2 |
3 | This folder contains a pre-built version of the required ffmpeg dependencies, along with the headers
4 | required to build against it. This is purely provided for simplicity to consume this module, however
5 | it is still preferable for the consumers to build and use their own binaries (and source) rather
6 | than what is provided here.
7 |
8 | The binaries/source included here are copied from git://source.ffmpeg.org/ffmpeg (release/4.2)
9 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavcodec.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavcodec.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavformat.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavformat.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavutil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/arm64-v8a/libavutil.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavcodec.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavcodec.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavformat.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavformat.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavutil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/armeabi-v7a/libavutil.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavcodec.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavcodec.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavformat.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavformat.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavutil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86/libavutil.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavcodec.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavcodec.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavformat.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavformat.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavutil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linkedin/LiTr/070479ada51176a4f2014bf799bb569448ed0027/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/android-libs/x86_64/libavutil.so
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/cpp/ffmpeg_bundled/extra/libavutil/avconfig.h:
--------------------------------------------------------------------------------
1 | /* Generated by ffmpeg configure */
2 | #ifndef AVUTIL_AVCONFIG_H
3 | #define AVUTIL_AVCONFIG_H
4 | #define AV_HAVE_BIGENDIAN 0
5 | #define AV_HAVE_FAST_UNALIGNED 0
6 | #endif /* AVUTIL_AVCONFIG_H */
7 |
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/java/com/linkedin/android/litr/muxers/NativeLogger.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 | package com.linkedin.android.litr.muxers
11 |
12 | /**
13 | * Allows configuration of the logging performed by the native components.
14 | */
15 | object NativeLogger {
16 | // Level's defined in avutil/log.h
17 | const val LEVEL_TRACE = 56
18 | const val LEVEL_DEBUG = 48
19 | const val LEVEL_VERBOSE = 40
20 | const val LEVEL_INFO = 32
21 | const val LEVEL_WARNING = 24
22 | const val LEVEL_ERROR = 16
23 | const val LEVEL_FATAL = 8
24 | const val LEVEL_PANIC = 0
25 | const val LEVEL_QUIET = -8
26 |
27 | /**
28 | * Configures the native logger with the given level. These log messages will be written to
29 | * the application's logcat messages.
30 | */
31 | fun setup(level: Int) = nativeSetup(level)
32 |
33 | private external fun nativeSetup(level: Int)
34 | }
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/java/com/linkedin/android/litr/muxers/NativeMuxersLib.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 | package com.linkedin.android.litr.muxers
11 |
12 | import android.util.Log
13 |
14 | private const val TAG = "NativeMuxersLib"
15 | private val FFMPEG_LIBRARIES = listOf(
16 | "avutil",
17 | "avcodec",
18 | "avformat",
19 | "litr-muxers"
20 | )
21 |
22 | /**
23 | * Helper method for loading all required ffmpeg binaries and dependencies.
24 | */
25 | object NativeMuxersLib {
26 | /**
27 | * Loads all required libraries for the Native Muxer.
28 | */
29 | fun loadLibraries() {
30 | FFMPEG_LIBRARIES.forEach {
31 | loadLibrary(it)
32 | }
33 | }
34 |
35 | private fun loadLibrary(libraryName: String) {
36 | try {
37 | System.loadLibrary(libraryName)
38 | Log.i(TAG, "Loaded: lib$libraryName")
39 | } catch (e: UnsatisfiedLinkError) {
40 | Log.e(TAG, "Unable to load: lib$libraryName")
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/litr-ffmpeg/src/main/java/com/linkedin/android/litr/muxers/NativeOutputFormats.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 | package com.linkedin.android.litr.muxers
11 |
12 | import android.media.MediaMuxer
13 |
14 | /**
15 | * This object contains the known (and supported) output formats for the NativeMediaMuxer.
16 | */
17 | object NativeOutputFormats {
18 | const val FORMAT_MPEG4 = "mp4"
19 | const val FORMAT_MKV = "matroska"
20 |
21 | const val FORMAT_SEGMENT = "stream_segment"
22 |
23 | /**
24 | * Converts constants defined by MediaMuxer.OutputFormat into their equivalent NativeMediaMuxer
25 | * constant.
26 | */
27 | fun fromOutputFormat(outputFormat: Int): String {
28 | return when (outputFormat) {
29 | MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 -> FORMAT_MPEG4
30 | MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM -> FORMAT_MKV
31 | else -> error("Unsupported output format")
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/litr-filters/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/litr-filters/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | apply from: "$rootDir/gradle/publishing.gradle"
7 |
8 | description 'Video/Audio filter pack for LiTr'
9 |
10 | android {
11 | compileSdkVersion rootProject.ext.compileSdkVersion
12 | buildToolsVersion rootProject.ext.buildToolsVersion
13 |
14 | namespace 'com.linkedin.android.litr.filter'
15 |
16 | defaultConfig {
17 | minSdkVersion rootProject.ext.minSdkVersion
18 | targetSdkVersion rootProject.ext.targetSdkVersion
19 |
20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
21 | consumerProguardFiles 'consumer-rules.pro'
22 | }
23 |
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 |
29 | buildTypes {
30 | release {
31 | minifyEnabled false
32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
33 | }
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation project(':litr')
39 |
40 | implementation 'androidx.annotation:annotation:1.2.0'
41 | implementation 'androidx.core:core-ktx:1.3.2'
42 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
43 | }
44 |
--------------------------------------------------------------------------------
/litr-filters/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/litr-filters/src/main/java/com/linkedin/android/litr/filter/audio/VolumeFilter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.audio
9 |
10 | import android.media.MediaFormat
11 | import androidx.annotation.FloatRange
12 | import com.linkedin.android.litr.codec.Frame
13 | import com.linkedin.android.litr.filter.BufferFilter
14 | import kotlin.math.pow
15 |
16 | private const val SAMPLE_SIZE = 2 // audio sample size on Android is 2 bytes per channel
17 | private const val BASE = 10.0 // volume is logarithmic, we will use base 10
18 |
19 | /**
20 | * An audio filter that changes the audio track volume:
21 | * - 1 will keep the volume "as is"
22 | * - 0 will silence the track
23 | * - value <1 will lower the volume
24 | * - value >1 will increase it. One has to be careful with these, since large values may result in distortion.
25 | */
26 | class VolumeFilter(@FloatRange(from = 0.0) private val volume: Double) : BufferFilter{
27 |
28 | override fun init(mediaFormat: MediaFormat?) {}
29 |
30 | override fun apply(frame: Frame) {
31 | frame.buffer?.asShortBuffer()?.let { buffer ->
32 | val sampleCount = frame.bufferInfo.size / SAMPLE_SIZE
33 | repeat(sampleCount) { index ->
34 | // replace sample at index with volume adjusted value
35 | buffer.put(
36 | index,
37 | (buffer.get(index) * (BASE.pow(volume) -1 ) / (BASE - 1)).toInt().toShort()
38 | )
39 | }
40 | }
41 | }
42 |
43 | override fun release() {}
44 | }
45 |
--------------------------------------------------------------------------------
/litr-filters/src/main/java/com/linkedin/android/litr/filter/video/gl/AnimationFrameProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl;
9 |
10 | import android.graphics.Bitmap;
11 | import androidx.annotation.Nullable;
12 |
13 | /**
14 | * Interface that provides animation frames to be overlaid onto video frames.
15 | */
16 | public interface AnimationFrameProvider {
17 |
18 | /**
19 | * Get total number of frames in animation
20 | * @return total frame count
21 | */
22 | int getFrameCount();
23 |
24 | /**
25 | * Get next frame content
26 | * @return frame bitmap, null if not available
27 | */
28 | @Nullable
29 | Bitmap getNextFrame();
30 |
31 | /**
32 | * Get next frame delay, AKA duration frame is visible before replaced by next one
33 | * @return frame delay in nanoseconds
34 | */
35 | long getNextFrameDurationNs();
36 |
37 | /**
38 | * Advance animation to next frame
39 | */
40 | void advance();
41 | }
42 |
--------------------------------------------------------------------------------
/litr-filters/src/main/java/com/linkedin/android/litr/filter/video/gl/GrayscaleFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl;
9 |
10 | import androidx.annotation.Nullable;
11 |
12 | import com.linkedin.android.litr.filter.Transform;
13 |
14 | public class GrayscaleFilter extends VideoFrameRenderFilter {
15 |
16 | private static final String FRAGMENT_SHADER =
17 | "#extension GL_OES_EGL_image_external : require\n" +
18 |
19 | "precision mediump float;\n" +
20 | "varying vec2 vTextureCoord;\n" +
21 | "uniform samplerExternalOES sTexture;\n" +
22 | "const highp vec3 weight = vec3(0.2125, 0.7154, 0.0721);\n" +
23 |
24 | "void main()\n" +
25 | "{\n" +
26 | "float luminance = dot(texture2D(sTexture, vTextureCoord).rgb, weight);\n" +
27 | "gl_FragColor = vec4(vec3(luminance), 1.0);\n" +
28 | "}";
29 |
30 | public GrayscaleFilter() {
31 | this(null);
32 | }
33 |
34 | /**
35 | * Create frame render filter with source video frame, then scale, then position and then rotate the bitmap around its center as specified.
36 | * @param transform {@link Transform} that defines positioning of source video frame within target video frame
37 | */
38 | public GrayscaleFilter(@Nullable Transform transform) {
39 | super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER, null, transform);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/litr-filters/src/main/java/com/linkedin/android/litr/filter/video/gl/SolidBackgroundColorFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl;
9 |
10 | import android.graphics.Color;
11 | import android.opengl.GLES20;
12 |
13 | import androidx.annotation.ColorInt;
14 | import androidx.annotation.FloatRange;
15 | import androidx.annotation.NonNull;
16 |
17 | import com.linkedin.android.litr.filter.GlFilter;
18 |
19 | /**
20 | * Filter to set video background to solid color
21 | */
22 | public class SolidBackgroundColorFilter implements GlFilter {
23 |
24 | private final float red;
25 | private final float green;
26 | private final float blue;
27 |
28 | /**
29 | * Create using Android integer RGBA color format
30 | * @param color color value
31 | */
32 | public SolidBackgroundColorFilter(@ColorInt int color) {
33 | this.red = Color.red(color) / 255f;
34 | this.green = Color.green(color) / 255f;
35 | this.blue = Color.blue(color) / 255f;
36 | }
37 |
38 | /**
39 | * Create using OpenGL color format
40 | * @param red red value
41 | * @param green green value
42 | * @param blue blue value
43 | */
44 | public SolidBackgroundColorFilter(@FloatRange(from = 0, to = 1) float red,
45 | @FloatRange(from = 0, to = 1) float green,
46 | @FloatRange(from = 0, to = 1) float blue) {
47 | this.red = red;
48 | this.green = green;
49 | this.blue = blue;
50 | }
51 |
52 | @Override
53 | public void init() {}
54 |
55 | @Override
56 | public void setVpMatrix(@NonNull float[] vpMatrix, int vpMatrixOffset) {}
57 |
58 | @Override
59 | public void apply(long presentationTimeNs) {
60 | GLES20.glClearColor(red, green, blue, 1.0f);
61 | GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
62 | }
63 |
64 | @Override
65 | public void release() {}
66 | }
67 |
--------------------------------------------------------------------------------
/litr/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | apply from: "$rootDir/gradle/publishing.gradle"
7 |
8 | description 'LiTr media transformation library for Android'
9 |
10 | android {
11 | compileSdkVersion rootProject.ext.compileSdkVersion
12 | buildToolsVersion rootProject.ext.buildToolsVersion
13 |
14 | namespace 'com.linkedin.android.litr'
15 |
16 | defaultConfig {
17 | minSdkVersion rootProject.ext.minSdkVersion
18 | targetSdkVersion rootProject.ext.targetSdkVersion
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 |
26 | testOptions {
27 | unitTests.returnDefaultValues = true
28 | }
29 |
30 | lint {
31 | abortOnError true
32 | checkDependencies true
33 | checkTestSources true
34 | checkReleaseBuilds false
35 | }
36 |
37 | kotlinOptions {
38 | freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
39 | }
40 |
41 | externalNativeBuild {
42 | cmake {
43 | path file('src/main/cpp/CMakeLists.txt')
44 | version '3.10.2'
45 | }
46 | }
47 | }
48 |
49 | dependencies {
50 | implementation 'androidx.annotation:annotation:1.2.0'
51 | implementation 'androidx.core:core-ktx:1.3.2'
52 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
53 |
54 | testImplementation 'junit:junit:4.13.2'
55 | testImplementation 'org.mockito:mockito-core:2.28.2'
56 | testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
57 | testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
58 | }
59 |
--------------------------------------------------------------------------------
/litr/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10.2)
2 |
3 | project("LiTrNative")
4 |
5 | add_library(litr-jni SHARED
6 | audio-processor.cpp)
7 |
8 | add_subdirectory(oboe_resampler)
9 |
10 | find_library(log-lib log)
11 |
12 | target_link_libraries(litr-jni
13 | ${log-lib}
14 | oboe-resampler)
15 |
16 | #[[To support compiling 16 KB-aligned shared libraries with Android NDK version r26 or lower]]
17 | set_target_properties(litr-jni PROPERTIES
18 | LINK_FLAGS "-Wl,-z,max-page-size=16384"
19 | )
20 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10.2)
2 |
3 | project("OboeResampler")
4 |
5 | add_library(oboe-resampler STATIC
6 | IntegerRatio.cpp
7 | LinearResampler.cpp
8 | MultiChannelResampler.cpp
9 | PolyphaseResampler.cpp
10 | PolyphaseResamplerMono.cpp
11 | PolyphaseResamplerStereo.cpp
12 | SincResampler.cpp
13 | SincResamplerStereo.cpp
14 | )
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/HyperbolicCosineWindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H
18 | #define RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H
19 |
20 | #include
21 |
22 | namespace resampler {
23 |
24 | /**
25 | * Calculate a HyperbolicCosineWindow window centered at 0.
26 | * This can be used in place of a Kaiser window.
27 | *
28 | * The code is based on an anonymous contribution by "a concerned citizen":
29 | * https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation
30 | */
31 | class HyperbolicCosineWindow {
32 | public:
33 | HyperbolicCosineWindow() {
34 | setStopBandAttenuation(60);
35 | }
36 |
37 | /**
38 | * @param attenuation typical values range from 30 to 90 dB
39 | * @return beta
40 | */
41 | double setStopBandAttenuation(double attenuation) {
42 | double alpha = ((-325.1e-6 * attenuation + 0.1677) * attenuation) - 3.149;
43 | setAlpha(alpha);
44 | return alpha;
45 | }
46 |
47 | void setAlpha(double alpha) {
48 | mAlpha = alpha;
49 | mInverseCoshAlpha = 1.0 / cosh(alpha);
50 | }
51 |
52 | /**
53 | * @param x ranges from -1.0 to +1.0
54 | */
55 | double operator()(double x) {
56 | double x2 = x * x;
57 | if (x2 >= 1.0) return 0.0;
58 | double w = mAlpha * sqrt(1.0 - x2);
59 | return cosh(w) * mInverseCoshAlpha;
60 | }
61 |
62 | private:
63 | double mAlpha = 0.0;
64 | double mInverseCoshAlpha = 1.0;
65 | };
66 |
67 | } // namespace resampler
68 | #endif //RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H
69 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/IntegerRatio.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "IntegerRatio.h"
18 |
19 | using namespace resampler;
20 |
21 | // Enough primes to cover the common sample rates.
22 | static const int kPrimes[] = {
23 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
24 | 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
25 | 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
26 | 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199};
27 |
28 | void IntegerRatio::reduce() {
29 | for (int prime : kPrimes) {
30 | if (mNumerator < prime || mDenominator < prime) {
31 | break;
32 | }
33 |
34 | // Find biggest prime factor for numerator.
35 | while (true) {
36 | int top = mNumerator / prime;
37 | int bottom = mDenominator / prime;
38 | if ((top >= 1)
39 | && (bottom >= 1)
40 | && (top * prime == mNumerator) // divided evenly?
41 | && (bottom * prime == mDenominator)) {
42 | mNumerator = top;
43 | mDenominator = bottom;
44 | } else {
45 | break;
46 | }
47 | }
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/IntegerRatio.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_INTEGER_RATIO_H
18 | #define OBOE_INTEGER_RATIO_H
19 |
20 | #include
21 |
22 | namespace resampler {
23 |
24 | /**
25 | * Represent the ratio of two integers.
26 | */
27 | class IntegerRatio {
28 | public:
29 | IntegerRatio(int32_t numerator, int32_t denominator)
30 | : mNumerator(numerator), mDenominator(denominator) {}
31 |
32 | /**
33 | * Reduce by removing common prime factors.
34 | */
35 | void reduce();
36 |
37 | int32_t getNumerator() {
38 | return mNumerator;
39 | }
40 |
41 | int32_t getDenominator() {
42 | return mDenominator;
43 | }
44 |
45 | private:
46 | int32_t mNumerator;
47 | int32_t mDenominator;
48 | };
49 |
50 | }
51 |
52 | #endif //OBOE_INTEGER_RATIO_H
53 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/LinearResampler.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "LinearResampler.h"
18 |
19 | using namespace resampler;
20 |
21 | LinearResampler::LinearResampler(const MultiChannelResampler::Builder &builder)
22 | : MultiChannelResampler(builder) {
23 | mPreviousFrame = std::make_unique(getChannelCount());
24 | mCurrentFrame = std::make_unique(getChannelCount());
25 | }
26 |
27 | void LinearResampler::writeFrame(const float *frame) {
28 | memcpy(mPreviousFrame.get(), mCurrentFrame.get(), sizeof(float) * getChannelCount());
29 | memcpy(mCurrentFrame.get(), frame, sizeof(float) * getChannelCount());
30 | }
31 |
32 | void LinearResampler::readFrame(float *frame) {
33 | float *previous = mPreviousFrame.get();
34 | float *current = mCurrentFrame.get();
35 | float phase = (float) getIntegerPhase() / mDenominator;
36 | // iterate across samples in the frame
37 | for (int channel = 0; channel < getChannelCount(); channel++) {
38 | float f0 = *previous++;
39 | float f1 = *current++;
40 | *frame++ = f0 + (phase * (f1 - f0));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/LinearResampler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_LINEAR_RESAMPLER_H
18 | #define OBOE_LINEAR_RESAMPLER_H
19 |
20 | #include
21 | #include
22 | #include
23 | #include "MultiChannelResampler.h"
24 |
25 | namespace resampler {
26 |
27 | /**
28 | * Simple resampler that uses bi-linear interpolation.
29 | */
30 | class LinearResampler : public MultiChannelResampler {
31 | public:
32 | LinearResampler(const MultiChannelResampler::Builder &builder);
33 |
34 | void writeFrame(const float *frame) override;
35 |
36 | void readFrame(float *frame) override;
37 |
38 | private:
39 | std::unique_ptr mPreviousFrame;
40 | std::unique_ptr mCurrentFrame;
41 | };
42 |
43 | } // namespace resampler
44 | #endif //OBOE_LINEAR_RESAMPLER_H
45 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/PolyphaseResampler.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include "IntegerRatio.h"
20 | #include "PolyphaseResampler.h"
21 |
22 | using namespace resampler;
23 |
24 | PolyphaseResampler::PolyphaseResampler(const MultiChannelResampler::Builder &builder)
25 | : MultiChannelResampler(builder)
26 | {
27 | assert((getNumTaps() % 4) == 0); // Required for loop unrolling.
28 |
29 | int32_t inputRate = builder.getInputRate();
30 | int32_t outputRate = builder.getOutputRate();
31 |
32 | int32_t numRows = mDenominator;
33 | double phaseIncrement = (double) inputRate / (double) outputRate;
34 | generateCoefficients(inputRate, outputRate,
35 | numRows, phaseIncrement,
36 | builder.getNormalizedCutoff());
37 | }
38 |
39 | void PolyphaseResampler::readFrame(float *frame) {
40 | // Clear accumulator for mixing.
41 | std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0);
42 |
43 | // Multiply input times windowed sinc function.
44 | float *coefficients = &mCoefficients[mCoefficientCursor];
45 | float *xFrame = &mX[mCursor * getChannelCount()];
46 | for (int i = 0; i < mNumTaps; i++) {
47 | float coefficient = *coefficients++;
48 | for (int channel = 0; channel < getChannelCount(); channel++) {
49 | mSingleFrame[channel] += *xFrame++ * coefficient;
50 | }
51 | }
52 |
53 | // Advance and wrap through coefficients.
54 | mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size();
55 |
56 | // Copy accumulator to output.
57 | for (int channel = 0; channel < getChannelCount(); channel++) {
58 | frame[channel] = mSingleFrame[channel];
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/PolyphaseResampler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_POLYPHASE_RESAMPLER_H
18 | #define OBOE_POLYPHASE_RESAMPLER_H
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include "MultiChannelResampler.h"
25 |
26 | namespace resampler {
27 | /**
28 | * Resampler that is optimized for a reduced ratio of sample rates.
29 | * All of the coefficients for each possible phase value are pre-calculated.
30 | */
31 | class PolyphaseResampler : public MultiChannelResampler {
32 | public:
33 | /**
34 | *
35 | * @param builder containing lots of parameters
36 | */
37 | explicit PolyphaseResampler(const MultiChannelResampler::Builder &builder);
38 |
39 | virtual ~PolyphaseResampler() = default;
40 |
41 | void readFrame(float *frame) override;
42 |
43 | protected:
44 |
45 | int32_t mCoefficientCursor = 0;
46 |
47 | };
48 |
49 | }
50 |
51 | #endif //OBOE_POLYPHASE_RESAMPLER_H
52 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/PolyphaseResamplerMono.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include "PolyphaseResamplerMono.h"
19 |
20 | using namespace resampler;
21 |
22 | #define MONO 1
23 |
24 | PolyphaseResamplerMono::PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder)
25 | : PolyphaseResampler(builder) {
26 | assert(builder.getChannelCount() == MONO);
27 | }
28 |
29 | void PolyphaseResamplerMono::writeFrame(const float *frame) {
30 | // Move cursor before write so that cursor points to last written frame in read.
31 | if (--mCursor < 0) {
32 | mCursor = getNumTaps() - 1;
33 | }
34 | float *dest = &mX[mCursor * MONO];
35 | const int offset = mNumTaps * MONO;
36 | // Write each channel twice so we avoid having to wrap when running the FIR.
37 | const float sample = frame[0];
38 | // Put ordered writes together.
39 | dest[0] = sample;
40 | dest[offset] = sample;
41 | }
42 |
43 | void PolyphaseResamplerMono::readFrame(float *frame) {
44 | // Clear accumulator.
45 | float sum = 0.0;
46 |
47 | // Multiply input times precomputed windowed sinc function.
48 | const float *coefficients = &mCoefficients[mCoefficientCursor];
49 | float *xFrame = &mX[mCursor * MONO];
50 | const int numLoops = mNumTaps >> 2; // n/4
51 | for (int i = 0; i < numLoops; i++) {
52 | // Manual loop unrolling, might get converted to SIMD.
53 | sum += *xFrame++ * *coefficients++;
54 | sum += *xFrame++ * *coefficients++;
55 | sum += *xFrame++ * *coefficients++;
56 | sum += *xFrame++ * *coefficients++;
57 | }
58 |
59 | mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size();
60 |
61 | // Copy accumulator to output.
62 | frame[0] = sum;
63 | }
64 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/PolyphaseResamplerMono.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_POLYPHASE_RESAMPLER_MONO_H
18 | #define OBOE_POLYPHASE_RESAMPLER_MONO_H
19 |
20 | #include
21 | #include
22 | #include "PolyphaseResampler.h"
23 |
24 | namespace resampler {
25 |
26 | class PolyphaseResamplerMono : public PolyphaseResampler {
27 | public:
28 | explicit PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder);
29 |
30 | virtual ~PolyphaseResamplerMono() = default;
31 |
32 | void writeFrame(const float *frame) override;
33 |
34 | void readFrame(float *frame) override;
35 | };
36 |
37 | }
38 |
39 | #endif //OBOE_POLYPHASE_RESAMPLER_MONO_H
40 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/PolyphaseResamplerStereo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_POLYPHASE_RESAMPLER_STEREO_H
18 | #define OBOE_POLYPHASE_RESAMPLER_STEREO_H
19 |
20 | #include
21 | #include
22 | #include "PolyphaseResampler.h"
23 |
24 | namespace resampler {
25 |
26 | class PolyphaseResamplerStereo : public PolyphaseResampler {
27 | public:
28 | explicit PolyphaseResamplerStereo(const MultiChannelResampler::Builder &builder);
29 |
30 | virtual ~PolyphaseResamplerStereo() = default;
31 |
32 | void writeFrame(const float *frame) override;
33 |
34 | void readFrame(float *frame) override;
35 | };
36 |
37 | }
38 |
39 | #endif //OBOE_POLYPHASE_RESAMPLER_STEREO_H
40 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/SincResampler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_SINC_RESAMPLER_H
18 | #define OBOE_SINC_RESAMPLER_H
19 |
20 | #include
21 | #include
22 | #include
23 | #include "MultiChannelResampler.h"
24 |
25 | namespace resampler {
26 |
27 | /**
28 | * Resampler that can interpolate between coefficients.
29 | * This can be used to support arbitrary ratios.
30 | */
31 | class SincResampler : public MultiChannelResampler {
32 | public:
33 | explicit SincResampler(const MultiChannelResampler::Builder &builder);
34 |
35 | virtual ~SincResampler() = default;
36 |
37 | void readFrame(float *frame) override;
38 |
39 | protected:
40 |
41 | std::vector mSingleFrame2; // for interpolation
42 | int32_t mNumRows = 0;
43 | double mPhaseScaler = 1.0;
44 | };
45 |
46 | }
47 | #endif //OBOE_SINC_RESAMPLER_H
48 |
--------------------------------------------------------------------------------
/litr/src/main/cpp/oboe_resampler/SincResamplerStereo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef OBOE_SINC_RESAMPLER_STEREO_H
18 | #define OBOE_SINC_RESAMPLER_STEREO_H
19 |
20 | #include
21 | #include
22 | #include "SincResampler.h"
23 |
24 | namespace resampler {
25 |
26 | class SincResamplerStereo : public SincResampler {
27 | public:
28 | explicit SincResamplerStereo(const MultiChannelResampler::Builder &builder);
29 |
30 | virtual ~SincResamplerStereo() = default;
31 |
32 | void writeFrame(const float *frame) override;
33 |
34 | void readFrame(float *frame) override;
35 |
36 | };
37 |
38 | }
39 | #endif //OBOE_SINC_RESAMPLER_STEREO_H
40 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/Annotations.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr
9 |
10 | @Retention(value = AnnotationRetention.BINARY)
11 | @RequiresOptIn(
12 | level = RequiresOptIn.Level.WARNING,
13 | message = "The frame extract APIs are experimental in LiTr, and may be changed or removed in the future."
14 | )
15 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY)
16 | annotation class ExperimentalFrameExtractorApi
17 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/MimeType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr;
9 |
10 | import androidx.annotation.Nullable;
11 |
12 | public class MimeType {
13 | private static final String BASE_TYPE_AUDIO = "audio";
14 | private static final String BASE_TYPE_VIDEO = "video";
15 |
16 | public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
17 | public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw";
18 | public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
19 | public static final String AUDIO_VORBIS = BASE_TYPE_AUDIO + "/vorbis";
20 |
21 | public static final String VIDEO_AVC = BASE_TYPE_VIDEO + "/avc";
22 | public static final String VIDEO_HEVC = BASE_TYPE_VIDEO + "/hevc";
23 | public static final String VIDEO_VP8 = BASE_TYPE_VIDEO + "/x-vnd.on2.vp8";
24 | public static final String VIDEO_VP9 = BASE_TYPE_VIDEO + "/x-vnd.on2.vp9";
25 | public static final String VIDEO_RAW = BASE_TYPE_VIDEO + "/raw";
26 |
27 | public static boolean isVideo(@Nullable String mimeType) {
28 | return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType));
29 | }
30 |
31 | public static boolean isAudio(@Nullable String mimeType) {
32 | return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType));
33 | }
34 |
35 | private static String getTopLevelType(@Nullable String mimeType) {
36 | if (mimeType == null) {
37 | return null;
38 | }
39 |
40 | int indexOfSlash = mimeType.indexOf('/');
41 | if (indexOfSlash == -1) {
42 | return null;
43 | }
44 |
45 | return mimeType.substring(0, indexOfSlash);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/TransformationListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr;
9 |
10 | import androidx.annotation.FloatRange;
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import com.linkedin.android.litr.analytics.TrackTransformationInfo;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * A listener interface, to notify client about transformation progress/result
19 | * Callbacks are made on UI thread, to make it safe to update UI in listener implementations
20 | */
21 | public interface TransformationListener {
22 |
23 | /**
24 | * Transformation started successfully
25 | * @param id request id
26 | */
27 | void onStarted(@NonNull String id);
28 |
29 | /**
30 | * Transformation progress update
31 | * @param id request id
32 | * @param progress progress, from 0 to 1, with client specified granularity
33 | */
34 | void onProgress(@NonNull String id, @FloatRange(from = 0, to = 1) float progress);
35 |
36 | /**
37 | * Transformation completed
38 | * @param id request id
39 | */
40 | void onCompleted(@NonNull String id, @Nullable List trackTransformationInfos);
41 |
42 | /**
43 | * Transformation was cancelled
44 | * @param id request id
45 | */
46 | void onCancelled(@NonNull String id, @Nullable List trackTransformationInfos);
47 |
48 | /**
49 | * Transformation error
50 | * @param id request id
51 | * @param cause error cause
52 | */
53 | void onError(@NonNull String id, @Nullable Throwable cause, @Nullable List trackTransformationInfos);
54 | }
55 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/analytics/TrackTransformationInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.analytics;
9 |
10 | import android.media.MediaFormat;
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | /**
15 | * A class which stores information about transformation for a particular track
16 | */
17 | public class TrackTransformationInfo {
18 | public static final long UNKNOWN_VALUE = -1;
19 |
20 | @NonNull private MediaFormat sourceFormat;
21 | @Nullable private MediaFormat targetFormat;
22 | @Nullable private String decoderCodec;
23 | @Nullable private String encoderCodec;
24 | private long duration = UNKNOWN_VALUE;
25 |
26 | @NonNull
27 | public MediaFormat getSourceFormat() {
28 | return sourceFormat;
29 | }
30 |
31 | @Nullable
32 | public MediaFormat getTargetFormat() {
33 | return targetFormat;
34 | }
35 |
36 | @Nullable
37 | public String getDecoderCodec() {
38 | return decoderCodec;
39 | }
40 |
41 | @Nullable
42 | public String getEncoderCodec() {
43 | return encoderCodec;
44 | }
45 |
46 | public long getDuration() {
47 | return duration;
48 | }
49 |
50 | public void setSourceFormat(@NonNull MediaFormat sourceFormat) {
51 | this.sourceFormat = sourceFormat;
52 | }
53 |
54 | public void setTargetFormat(@Nullable MediaFormat targetFormat) {
55 | this.targetFormat = targetFormat;
56 | }
57 |
58 | public void setDecoderCodec(@Nullable String decoderCodec) {
59 | this.decoderCodec = decoderCodec;
60 | }
61 |
62 | public void setEncoderCodec(@Nullable String encoderCodec) {
63 | this.encoderCodec = encoderCodec;
64 | }
65 |
66 | public void setDuration(long duration) {
67 | this.duration = duration;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/analytics/TransformationStatsCollector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.analytics;
9 |
10 | import android.media.MediaFormat;
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import androidx.annotation.RestrictTo;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | @RestrictTo(RestrictTo.Scope.LIBRARY)
19 | public class TransformationStatsCollector {
20 | private List trackTransformationInfos;
21 |
22 | public TransformationStatsCollector() {
23 | trackTransformationInfos = new ArrayList<>(2);
24 | }
25 |
26 | @NonNull
27 | public List getStats() {
28 | return trackTransformationInfos;
29 | }
30 |
31 | public void addSourceTrack(@NonNull MediaFormat sourceMediaFormat) {
32 | TrackTransformationInfo trackTransformationInfo = new TrackTransformationInfo();
33 | trackTransformationInfo.setSourceFormat(sourceMediaFormat);
34 |
35 | trackTransformationInfos.add(trackTransformationInfo);
36 | }
37 |
38 | public void setTrackCodecs(int track, @Nullable String decoderCodec, @Nullable String encoderCodec) {
39 | TrackTransformationInfo trackTransformationInfo = trackTransformationInfos.get(track);
40 | trackTransformationInfo.setDecoderCodec(decoderCodec);
41 | trackTransformationInfo.setEncoderCodec(encoderCodec);
42 | }
43 |
44 | public void setTargetFormat(int track, @Nullable MediaFormat mediaFormat) {
45 | trackTransformationInfos.get(track).setTargetFormat(mediaFormat);
46 | }
47 |
48 | public void increaseTrackProcessingDuration(int track, long frameProcessingDuration) {
49 | TrackTransformationInfo trackTransformationInfo = trackTransformationInfos.get(track);
50 | long duration = trackTransformationInfo.getDuration();
51 | trackTransformationInfo.setDuration(duration + frameProcessingDuration);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/codec/Frame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.codec;
9 |
10 | import android.media.MediaCodec;
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | import java.nio.ByteBuffer;
15 |
16 | /**
17 | * A container class for frame data and metadata
18 | */
19 | public class Frame {
20 | public final int tag;
21 | @Nullable public final ByteBuffer buffer;
22 | @NonNull public final MediaCodec.BufferInfo bufferInfo;
23 |
24 | public Frame(int tag, @Nullable ByteBuffer buffer, @Nullable MediaCodec.BufferInfo bufferInfo) {
25 | this.tag = tag;
26 | this.buffer = buffer;
27 |
28 | if (bufferInfo == null) {
29 | this.bufferInfo = new MediaCodec.BufferInfo();
30 | } else {
31 | this.bufferInfo = bufferInfo;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/exception/InsufficientDiskSpaceException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.exception;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | import java.util.Locale;
13 |
14 | public class InsufficientDiskSpaceException extends MediaTransformationException {
15 | private final long estimatedTargetFileSizeInBytes;
16 | private final long availableDiskSpaceInBytes;
17 |
18 | public InsufficientDiskSpaceException(long estimatedTargetFileSizeInBytes, long availableDiskSpaceInBytes) {
19 | this(estimatedTargetFileSizeInBytes, availableDiskSpaceInBytes, new Throwable());
20 | }
21 |
22 | public InsufficientDiskSpaceException(long estimatedTargetFileSizeInBytes, long availableDiskSpaceInBytes, @NonNull Throwable cause) {
23 | super(cause);
24 | this.estimatedTargetFileSizeInBytes = estimatedTargetFileSizeInBytes;
25 | this.availableDiskSpaceInBytes = availableDiskSpaceInBytes;
26 | }
27 |
28 | @Override
29 | @NonNull
30 | public String getMessage() {
31 | return String.format(Locale.ENGLISH, "Insufficient disk space, estimated file size in bytes %d, available disk space in bytes %d",
32 | estimatedTargetFileSizeInBytes,
33 | availableDiskSpaceInBytes);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/exception/MediaSourceException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.exception;
9 |
10 | import android.net.Uri;
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | public class MediaSourceException extends MediaTransformationException {
15 |
16 | private static final String DATA_SOURCE_ERROR_TEXT = "data source error";
17 | private static final String MEDIA_EXTRACTOR_CREATION_ERROR_TEXT = "Failed to create media source due to a ";
18 |
19 | @NonNull private final Error error;
20 | @Nullable private final Uri inputUri;
21 |
22 | public MediaSourceException(@NonNull Error error,
23 | @Nullable Uri inputUri,
24 | @NonNull Throwable throwable) {
25 | super(throwable);
26 | this.error = error;
27 | this.inputUri = inputUri;
28 | }
29 |
30 | public enum Error {
31 | DATA_SOURCE(DATA_SOURCE_ERROR_TEXT);
32 |
33 | private final String text;
34 |
35 | Error(String message) {
36 | this.text = message;
37 | }
38 | }
39 |
40 | @NonNull
41 | public Error getError() {
42 | return error;
43 | }
44 |
45 | @Override
46 | @NonNull
47 | public String getMessage() {
48 | return MEDIA_EXTRACTOR_CREATION_ERROR_TEXT + error.text;
49 | }
50 |
51 | @Override
52 | @NonNull
53 | public String toString() {
54 | return super.toString() + '\n'
55 | + MEDIA_EXTRACTOR_CREATION_ERROR_TEXT + error.text + '\n'
56 | + "Uri: " + inputUri;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/exception/MediaTargetException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.exception;
9 |
10 | import android.net.Uri;
11 | import androidx.annotation.IntRange;
12 | import androidx.annotation.NonNull;
13 |
14 | public class MediaTargetException extends MediaTransformationException {
15 |
16 | private static final String INVALID_PARAMS_TEXT = "Invalid parameters";
17 | private static final String IO_FAILURE_TEXT = "Failed to open the media target for write.";
18 | private static final String UNSUPPORTED_URI_TYPE_TEXT = "URI type not supported at API level below 26";
19 | private static final String NO_OUTPUT_TRACKS_TEXT = "No output tracks";
20 |
21 | private final Error error;
22 | private final String outputFilePath;
23 | private final String outputFormat;
24 |
25 | public MediaTargetException(@NonNull Error error, @NonNull Uri outputFileUri, @IntRange(from=0, to=2) int outputFormat, @NonNull Throwable cause) {
26 | this(error, outputFileUri.toString(), outputFormat, cause);
27 | }
28 |
29 | public MediaTargetException(@NonNull Error error, @NonNull String outputFilePath, @IntRange(from=0, to=2) int outputFormat, @NonNull Throwable cause) {
30 | this(error, outputFilePath, String.valueOf(outputFormat), cause);
31 | }
32 |
33 | public MediaTargetException(@NonNull Error error, @NonNull String outputFilePath, String outputFormat, @NonNull Throwable cause) {
34 | super(cause);
35 | this.error = error;
36 | this.outputFilePath = outputFilePath;
37 | this.outputFormat = outputFormat;
38 | }
39 |
40 | public enum Error {
41 | INVALID_PARAMS(INVALID_PARAMS_TEXT),
42 | IO_FAILUE(IO_FAILURE_TEXT),
43 | UNSUPPORTED_URI_TYPE(UNSUPPORTED_URI_TYPE_TEXT),
44 | NO_OUTPUT_TRACKS(NO_OUTPUT_TRACKS_TEXT);
45 |
46 |
47 | private final String text;
48 | Error(String text) {
49 | this.text = text;
50 | }
51 | }
52 |
53 | @NonNull
54 | public Error getError() {
55 | return error;
56 | }
57 |
58 | @Override
59 | @NonNull
60 | public String toString() {
61 | return super.toString() + '\n'
62 | + error.text + '\n'
63 | + "Output file path or Uri encoded string: " + outputFilePath + '\n'
64 | + "MediaMuxer output format: " + outputFormat;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/exception/MediaTransformationException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.exception;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 |
13 | public abstract class MediaTransformationException extends Exception {
14 |
15 | @NonNull private String jobId;
16 |
17 | public MediaTransformationException(@Nullable Throwable cause) {
18 | super(cause);
19 | }
20 |
21 | public void setJobId(@NonNull String jobId) {
22 | this.jobId = jobId;
23 | }
24 |
25 | @Override
26 | @NonNull
27 | public String toString() {
28 | return super.toString() + "Media transformation failed for job id: " + jobId;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/BufferFilter.kt:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.filter
2 |
3 | import android.media.MediaFormat
4 | import com.linkedin.android.litr.codec.Frame
5 |
6 | /**
7 | * A filter used with a renderer operating in buffer mode, like [AudioRenderer]
8 | */
9 | interface BufferFilter {
10 |
11 | /**
12 | * Initialize the filter
13 | * @param mediaFormat renderer's target [MediaFormat]
14 | */
15 | fun init(mediaFormat: MediaFormat?)
16 |
17 | /**
18 | * Apply a filter to a [Frame]. Frame.bufferInfo will provide necessary metadata.
19 | * Frame.buffer is expected to be modified. Buffer contents are in target format.
20 | * For example, for audio buffer those will be Short (2 byte LE) values with target
21 | * sample rate and channel count.
22 | */
23 | fun apply(frame: Frame)
24 |
25 | /**
26 | * Release the filter.
27 | */
28 | fun release()
29 | }
30 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/GlFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | /**
13 | * Interface to allow clients modify individual frames as they are being transcoded. This type of filters are used
14 | * in Surface based encoding, so modifications must be written in OpenGL.
15 | */
16 | public interface GlFilter {
17 |
18 | /**
19 | * Initialize filter. This will be called during renderer initialization on a thread with GL context,
20 | * after GL surface has been created and set current.
21 | * Filter is expected to do all its GL initialization (compiling shaders, loading textures, etc.) in this method.
22 | */
23 | void init();
24 |
25 | /**
26 | * Set VP (projection + view) matrix on a filter. VP matrix is created and configured by a renderer
27 | * and pass copies of it are passed into to filters using this method, so they can use it to
28 | * configure their own MVP matrices. Model matrix not present. View matrix is configured to have its camera angle match video's.
29 | * Projection matrix is configured to orthogonal projection to account for video frame's aspect ratio: (-aspectRatio, aspectRatio, -1, 1, -1, 1)
30 | * Filters can set up their model matrix and then multiply it by renderer's VP matrix to match their geometry to video frame's.
31 | *
32 | * @param vpMatrix VP (projection * view) matrix configured by the renderer, usually
33 | * @param vpMatrixOffset offset into VP matrix
34 | */
35 | void setVpMatrix(@NonNull float[] vpMatrix, int vpMatrixOffset);
36 |
37 | /**
38 | * Apply GL rendering to a frame
39 | * @param presentationTimeNs presentation time of a frame, in nanoseconds
40 | */
41 | void apply(long presentationTimeNs);
42 |
43 | /**
44 | * Release all GL resources, such as shaders, textures, etc.
45 | */
46 | void release();
47 | }
48 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/GlFrameRenderFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | /**
13 | * Interface for filters that implement rendering source video frame onto target video frame.
14 | * In addition to target frame geometry (size, rotation, aspect ratio, etc.) frame renderer
15 | * should also have access to source video frame texture.
16 | */
17 | public interface GlFrameRenderFilter extends GlFilter {
18 |
19 | /**
20 | * Initialize texture associated with {@link android.graphics.SurfaceTexture} if input video frames
21 | * @param textureHandle texture handle of input video texture
22 | * @param transformMatrix transform matrix of input video texture
23 | */
24 | void initInputFrameTexture(int textureHandle, @NonNull float[] transformMatrix);
25 | }
26 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/Transform.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter;
9 |
10 | import android.graphics.PointF;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * A data class that defines geometric transform of a drawable object (video frame, bitmap overlay)
16 | * within a target video frame. Definition and transformation order matches positioning of an Android
17 | * {@link android.view.View} in a parent {@link android.view.View}: object is first scaled to size,
18 | * then moved into position, then rotated around its center.
19 | */
20 | public class Transform {
21 |
22 | @NonNull public final PointF size;
23 | @NonNull public final PointF position;
24 | public final float rotation;
25 |
26 | /**
27 | * Create a geometric transform
28 | * @param size size in X and Y direction, relative to target video frame
29 | * @param position position of source video frame center, in relative coordinate in 0 - 1 range
30 | * in fourth quadrant (0,0 is top left corner)
31 | * @param rotation rotation angle of overlay, relative to target video frame, counter-clockwise, in degrees
32 | **/
33 | public Transform(@NonNull PointF size, @NonNull PointF position, float rotation) {
34 | this.size = size;
35 | this.position = position;
36 | this.rotation = rotation;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/DefaultVideoFrameRenderFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl;
9 |
10 | import androidx.annotation.Nullable;
11 |
12 | import com.linkedin.android.litr.filter.Transform;
13 |
14 | /**
15 | * Most basic frame render filter that doesn't modify video pixels but can modify placement of a source frame within a target frame
16 | */
17 | public class DefaultVideoFrameRenderFilter extends VideoFrameRenderFilter {
18 |
19 | /**
20 | * Create most basic filter, which scales source frame to fit target frame, with no pixel modification.
21 | */
22 | public DefaultVideoFrameRenderFilter() {
23 | this(null);
24 | }
25 |
26 | /**
27 | * Create frame render filter with source video frame, then scale, then position and then rotate the bitmap around its center as specified.
28 | * No pixel data is modified.
29 | * @param transform {@link Transform} that defines positioning of source video frame within target video frame
30 | */
31 | public DefaultVideoFrameRenderFilter(@Nullable Transform transform) {
32 | super(DEFAULT_VERTEX_SHADER,
33 | DEFAULT_FRAGMENT_SHADER,
34 | null,
35 | transform);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/ShaderParameter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.IntDef;
13 | import androidx.annotation.NonNull;
14 |
15 | import java.lang.annotation.Retention;
16 | import java.lang.annotation.RetentionPolicy;
17 |
18 | /**
19 | * Base class for all shader attributes
20 | */
21 | public abstract class ShaderParameter {
22 |
23 | public static final int TYPE_UNIFORM = 0;
24 | public static final int TYPE_ATTRIBUTE = 1;
25 |
26 | @Retention(RetentionPolicy.SOURCE)
27 | @IntDef({ TYPE_UNIFORM, TYPE_ATTRIBUTE})
28 | @interface Type {}
29 |
30 | @Type public int type;
31 | @NonNull public String name;
32 |
33 | protected ShaderParameter(@Type int type, @NonNull String name) {
34 | this.type = type;
35 | this.name = name;
36 | }
37 |
38 | /**
39 | * Apply the parameter to GL program. This is called at each frame render.
40 | * @param glProgram handle of a GL program containing the parameter
41 | */
42 | abstract public void apply(int glProgram);
43 |
44 | protected int getLocation(int glProgram) {
45 | switch (type) {
46 | case TYPE_UNIFORM:
47 | return GLES20.glGetUniformLocation(glProgram, name);
48 | case TYPE_ATTRIBUTE:
49 | return GLES20.glGetAttribLocation(glProgram, name);
50 | default:
51 | return -1;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform1f.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * One float value shader parameter
16 | */
17 | public class Uniform1f extends ShaderParameter {
18 |
19 | private float value;
20 |
21 | /**
22 | * Create shader parameter
23 | * @param name parameter name, as defined in shader code
24 | * @param value parameter value
25 | */
26 | public Uniform1f(@NonNull String name, float value) {
27 | super(TYPE_UNIFORM, name);
28 |
29 | this.value = value;
30 | }
31 |
32 | @Override
33 | public void apply(int glProgram) {
34 | GLES20.glUniform1f(getLocation(glProgram), value);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform1fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * One float element vector shader parameter
18 | */
19 | public class Uniform1fv extends ShaderParameter {
20 |
21 | private int count;
22 | private FloatBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform1fv(@NonNull String name, int count, @NonNull float[] buffer) {
31 | this(name, count, FloatBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform1fv(@NonNull String name, int count, @NonNull FloatBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform1fv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform1i.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * One integer value shader parameter
16 | */
17 | public class Uniform1i extends ShaderParameter {
18 |
19 | private int value;
20 |
21 | /**
22 | * Create shader parameter
23 | * @param name parameter name, as defined in shader code
24 | * @param value parameter value
25 | */
26 | public Uniform1i(@NonNull String name, int value) {
27 | super(TYPE_UNIFORM, name);
28 |
29 | this.value = value;
30 | }
31 |
32 | @Override
33 | public void apply(int glProgram) {
34 | GLES20.glUniform1i(getLocation(glProgram), value);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform1iv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.IntBuffer;
15 |
16 | /**
17 | * One integer element vector shader parameter
18 | */
19 | public class Uniform1iv extends ShaderParameter {
20 |
21 | private int count;
22 | private IntBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform1iv(@NonNull String name, int count, @NonNull int[] buffer) {
31 | this(name, count, IntBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform1iv(@NonNull String name, int count, @NonNull IntBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform1iv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform2f.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Two float value shader parameter
16 | */
17 | public class Uniform2f extends ShaderParameter {
18 |
19 | private float value1;
20 | private float value2;
21 |
22 | /**
23 | * Create shader parameter
24 | * @param name parameter name, as defined in shader code
25 | * @param value1 first parameter value
26 | * @param value2 second parameter value
27 | */
28 | public Uniform2f(@NonNull String name, float value1, float value2) {
29 | super(TYPE_UNIFORM, name);
30 |
31 | this.value1 = value1;
32 | this.value2 = value2;
33 | }
34 |
35 | @Override
36 | public void apply(int glProgram) {
37 | GLES20.glUniform2f(getLocation(glProgram), value1, value2);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform2fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * Two float element vector shader parameter
18 | */
19 | public class Uniform2fv extends ShaderParameter {
20 |
21 | private int count;
22 | private FloatBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform2fv(@NonNull String name, int count, @NonNull float[] buffer) {
31 | this(name, count, FloatBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform2fv(@NonNull String name, int count, @NonNull FloatBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform2fv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform2i.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Two integer value shader parameter
16 | */
17 | public class Uniform2i extends ShaderParameter {
18 |
19 | private int value1;
20 | private int value2;
21 |
22 | /**
23 | * Create shader parameter
24 | * @param name parameter name, as defined in shader code
25 | * @param value1 first parameter value
26 | * @param value2 second parameter value
27 | */
28 | public Uniform2i(@NonNull String name, int value1, int value2) {
29 | super(TYPE_UNIFORM, name);
30 |
31 | this.value1 = value1;
32 | this.value2 = value2;
33 | }
34 |
35 | @Override
36 | public void apply(int glProgram) {
37 | GLES20.glUniform2i(getLocation(glProgram), value1, value2);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform2iv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.IntBuffer;
15 |
16 | /**
17 | * Two integer element vector shader parameter
18 | */
19 | public class Uniform2iv extends ShaderParameter {
20 |
21 | private int count;
22 | private IntBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform2iv(@NonNull String name, int count, @NonNull int[] buffer) {
31 | this(name, count, IntBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform2iv(@NonNull String name, int count, @NonNull IntBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform2iv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform3f.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Three float value shader parameter
16 | */
17 | public class Uniform3f extends ShaderParameter {
18 |
19 | private float value1;
20 | private float value2;
21 | private float value3;
22 |
23 | /**
24 | * Create shader parameter
25 | * @param name parameter name, as defined in shader code
26 | * @param value1 first parameter value
27 | * @param value2 second parameter value
28 | * @param value3 third parameter value
29 | */
30 | public Uniform3f(@NonNull String name, float value1, float value2, float value3) {
31 | super(TYPE_UNIFORM, name);
32 |
33 | this.value1 = value1;
34 | this.value2 = value2;
35 | this.value3 = value3;
36 | }
37 |
38 | @Override
39 | public void apply(int glProgram) {
40 | GLES20.glUniform3f(getLocation(glProgram), value1, value2, value3);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform3fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * Three float element vector shader parameter
18 | */
19 | public class Uniform3fv extends ShaderParameter {
20 |
21 | private int count;
22 | private FloatBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform3fv(@NonNull String name, int count, @NonNull float[] buffer) {
31 | this(name, count, FloatBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform3fv(@NonNull String name, int count, @NonNull FloatBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform3fv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform3i.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Three integer value shader parameter
16 | */
17 | public class Uniform3i extends ShaderParameter {
18 |
19 | private int value1;
20 | private int value2;
21 | private int value3;
22 |
23 | /**
24 | * Create shader parameter
25 | * @param name parameter name, as defined in shader code
26 | * @param value1 first parameter value
27 | * @param value2 second parameter value
28 | * @param value3 third parameter value
29 | */
30 | public Uniform3i(@NonNull String name, int value1, int value2, int value3) {
31 | super(TYPE_UNIFORM, name);
32 |
33 | this.value1 = value1;
34 | this.value2 = value2;
35 | this.value3 = value3;
36 | }
37 |
38 | @Override
39 | public void apply(int glProgram) {
40 | GLES20.glUniform3i(getLocation(glProgram), value1, value2, value3);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform3iv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.IntBuffer;
15 |
16 | /**
17 | * Three integer element vector shader parameter
18 | */
19 | public class Uniform3iv extends ShaderParameter {
20 |
21 | private int count;
22 | private IntBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform3iv(@NonNull String name, int count, @NonNull int[] buffer) {
31 | this(name, count, IntBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform3iv(@NonNull String name, int count, @NonNull IntBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform3iv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform4f.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Four float value shader parameter
16 | */
17 | public class Uniform4f extends ShaderParameter {
18 |
19 | private float value1;
20 | private float value2;
21 | private float value3;
22 | private float value4;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param value1 first parameter value
28 | * @param value2 second parameter value
29 | * @param value3 third parameter value
30 | * @param value4 fourth parameter value
31 | */
32 | public Uniform4f(@NonNull String name, float value1, float value2, float value3, float value4) {
33 | super(TYPE_UNIFORM, name);
34 |
35 | this.value1 = value1;
36 | this.value2 = value2;
37 | this.value3 = value3;
38 | this.value4 = value4;
39 | }
40 |
41 | @Override
42 | public void apply(int glProgram) {
43 | GLES20.glUniform4f(getLocation(glProgram), value1, value2, value3, value4);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform4fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * Four float element vector shader parameter
18 | */
19 | public class Uniform4fv extends ShaderParameter {
20 |
21 | private int count;
22 | private FloatBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform4fv(@NonNull String name, int count, @NonNull float[] buffer) {
31 | this(name, count, FloatBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform4fv(@NonNull String name, int count, @NonNull FloatBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform4fv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform4i.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | /**
15 | * Four integer value shader parameter
16 | */
17 | public class Uniform4i extends ShaderParameter {
18 |
19 | private int value1;
20 | private int value2;
21 | private int value3;
22 | private int value4;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param value1 first parameter value
28 | * @param value2 second parameter value
29 | * @param value3 third parameter value
30 | * @param value4 fourth parameter value
31 | */
32 | public Uniform4i(@NonNull String name, int value1, int value2, int value3, int value4) {
33 | super(TYPE_UNIFORM, name);
34 |
35 | this.value1 = value1;
36 | this.value2 = value2;
37 | this.value3 = value3;
38 | this.value4 = value4;
39 | }
40 |
41 | @Override
42 | public void apply(int glProgram) {
43 | GLES20.glUniform4i(getLocation(glProgram), value1, value2, value3, value4);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/Uniform4iv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.IntBuffer;
15 |
16 | /**
17 | * Four integer element vector shader parameter
18 | */
19 | public class Uniform4iv extends ShaderParameter {
20 |
21 | private int count;
22 | private IntBuffer buffer;
23 |
24 | /**
25 | * Create shader parameter
26 | * @param name parameter name, as defined in shader code
27 | * @param count number of vectors
28 | * @param buffer buffer containing new vector values
29 | */
30 | public Uniform4iv(@NonNull String name, int count, @NonNull int[] buffer) {
31 | this(name, count, IntBuffer.wrap(buffer));
32 | }
33 |
34 | /**
35 | * Create shader parameter
36 | * @param name parameter name, as defined in shader code
37 | * @param count number of vectors
38 | * @param buffer buffer containing new vector values
39 | */
40 | public Uniform4iv(@NonNull String name, int count, @NonNull IntBuffer buffer) {
41 | super(TYPE_UNIFORM, name);
42 |
43 | this.count = count;
44 | this.buffer = buffer;
45 | }
46 |
47 | @Override
48 | public void apply(int glProgram) {
49 | GLES20.glUniform4iv(getLocation(glProgram), count, buffer);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/UniformMatrix2fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * 2x2 float value matrix shader parameter
18 | */
19 | public class UniformMatrix2fv extends ShaderParameter {
20 |
21 | private int count;
22 | private boolean transpose;
23 | private float[] matrix;
24 | private int offset;
25 |
26 | private FloatBuffer buffer;
27 |
28 | /**
29 | * Create shader parameter
30 | * @param name parameter name, as defined in shader code
31 | * @param count number of matrices
32 | * @param transpose flag indicating if matrix is transposed
33 | * @param matrix matrix values
34 | * @param offset matrix offset
35 | */
36 | public UniformMatrix2fv(@NonNull String name, int count, boolean transpose, @NonNull float[] matrix, int offset) {
37 | super(TYPE_UNIFORM, name);
38 |
39 | this.count = count;
40 | this.transpose = transpose;
41 | this.matrix = matrix;
42 | this.offset = offset;
43 | }
44 |
45 | /**
46 | * Create shader parameter
47 | * @param name parameter name, as defined in shader code
48 | * @param count number of matrices
49 | * @param transpose flag indicating if matrix is transposed
50 | * @param buffer buffer containing matrix values
51 | */
52 | public UniformMatrix2fv(@NonNull String name, int count, boolean transpose, @NonNull FloatBuffer buffer) {
53 | super(TYPE_UNIFORM, name);
54 |
55 | this.count = count;
56 | this.transpose = transpose;
57 | this.buffer = buffer;
58 | }
59 |
60 | @Override
61 | public void apply(int glProgram) {
62 | if (buffer != null) {
63 | GLES20.glUniformMatrix2fv(getLocation(glProgram), count, transpose, buffer);
64 | } else {
65 | GLES20.glUniformMatrix2fv(getLocation(glProgram), count, transpose, matrix, offset);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/UniformMatrix3fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * 3x3 float value matrix shader parameter
18 | */
19 | public class UniformMatrix3fv extends ShaderParameter {
20 |
21 | private int count;
22 | private boolean transpose;
23 | private float[] matrix;
24 | private int offset;
25 | private FloatBuffer buffer;
26 |
27 | /**
28 | * Create shader parameter
29 | * @param name parameter name, as defined in shader code
30 | * @param count number of matrices
31 | * @param transpose flag indicating if matrix is transposed
32 | * @param matrix matrix values
33 | * @param offset matrix offset
34 | */
35 | public UniformMatrix3fv(@NonNull String name, int count, boolean transpose, @NonNull float[] matrix, int offset) {
36 | super(TYPE_UNIFORM, name);
37 |
38 | this.count = count;
39 | this.transpose = transpose;
40 | this.matrix = matrix;
41 | this.offset = offset;
42 | }
43 |
44 | /**
45 | * Create shader parameter
46 | * @param name parameter name, as defined in shader code
47 | * @param count number of matrices
48 | * @param transpose flag indicating if matrix is transposed
49 | * @param buffer buffer containing matrix values
50 | */
51 | public UniformMatrix3fv(@NonNull String name, int count, boolean transpose, @NonNull FloatBuffer buffer) {
52 | super(TYPE_UNIFORM, name);
53 |
54 | this.count = count;
55 | this.transpose = transpose;
56 | this.buffer = buffer;
57 | }
58 |
59 | @Override
60 | public void apply(int glProgram) {
61 | if (buffer != null) {
62 | GLES20.glUniformMatrix3fv(getLocation(glProgram), count, transpose, buffer);
63 | } else {
64 | GLES20.glUniformMatrix3fv(getLocation(glProgram), count, transpose, matrix, offset);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/parameter/UniformMatrix4fv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.parameter;
9 |
10 | import android.opengl.GLES20;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.nio.FloatBuffer;
15 |
16 | /**
17 | * 4x4 float value matrix shader parameter
18 | */
19 | public class UniformMatrix4fv extends ShaderParameter {
20 |
21 | private int count;
22 | private boolean transpose;
23 | private float[] matrix;
24 | private int offset;
25 | private FloatBuffer buffer;
26 |
27 | /**
28 | * Create shader parameter
29 | * @param name parameter name, as defined in shader code
30 | * @param count number of matrices
31 | * @param transpose flag indicating if matrix is transposed
32 | * @param matrix matrix values
33 | * @param offset matrix offset
34 | */
35 | public UniformMatrix4fv(@NonNull String name, int count, boolean transpose, @NonNull float[] matrix, int offset) {
36 | super(TYPE_UNIFORM, name);
37 |
38 | this.count = count;
39 | this.transpose = transpose;
40 | this.matrix = matrix;
41 | this.offset = offset;
42 | }
43 |
44 | /**
45 | * Create shader parameter
46 | * @param name parameter name, as defined in shader code
47 | * @param count number of matrices
48 | * @param transpose flag indicating if matrix is transposed
49 | * @param buffer buffer containing matrix values
50 | */
51 | public UniformMatrix4fv(@NonNull String name, int count, boolean transpose, @NonNull FloatBuffer buffer) {
52 | super(TYPE_UNIFORM, name);
53 |
54 | this.count = count;
55 | this.transpose = transpose;
56 | this.buffer = buffer;
57 | }
58 |
59 | @Override
60 | public void apply(int glProgram) {
61 | if (buffer != null) {
62 | GLES20.glUniformMatrix4fv(getLocation(glProgram), count, transpose, buffer);
63 | } else {
64 | GLES20.glUniformMatrix4fv(getLocation(glProgram), count, transpose, matrix, offset);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/filter/video/gl/shader/VertexShader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.filter.video.gl.shader;
9 |
10 | public class VertexShader {
11 |
12 | public static final String THREE_X_THREE_TEXTURE_SAMPLING_VERTEX_SHADER =
13 | "uniform mat4 uMVPMatrix;\n" +
14 | "uniform mat4 uSTMatrix;\n" +
15 |
16 | "attribute vec4 aPosition;\n" +
17 | "attribute vec4 aTextureCoord;\n" +
18 |
19 | "uniform highp float texelWidth;\n" +
20 | "uniform highp float texelHeight;\n" +
21 |
22 | "varying highp vec2 textureCoordinate;\n" +
23 | "varying highp vec2 leftTextureCoordinate;\n" +
24 | "varying highp vec2 rightTextureCoordinate;\n" +
25 |
26 | "varying highp vec2 topTextureCoordinate;\n" +
27 | "varying highp vec2 topLeftTextureCoordinate;\n" +
28 | "varying highp vec2 topRightTextureCoordinate;\n" +
29 |
30 | "varying highp vec2 bottomTextureCoordinate;\n" +
31 | "varying highp vec2 bottomLeftTextureCoordinate;\n" +
32 | "varying highp vec2 bottomRightTextureCoordinate;\n" +
33 |
34 | "void main()\n" +
35 | "{\n" +
36 | "gl_Position = uMVPMatrix * aPosition;\n" +
37 |
38 | "vec2 widthStep = vec2(texelWidth, 0.0);\n" +
39 | "vec2 heightStep = vec2(0.0, texelHeight);\n" +
40 | "vec2 widthHeightStep = vec2(texelWidth, texelHeight);\n" +
41 | "vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);\n" +
42 |
43 | "textureCoordinate = (uSTMatrix * aTextureCoord).xy;\n" +
44 | "leftTextureCoordinate = textureCoordinate - widthStep;\n" +
45 | "rightTextureCoordinate = textureCoordinate + widthStep;\n" +
46 |
47 | "topTextureCoordinate = textureCoordinate - heightStep;\n" +
48 | "topLeftTextureCoordinate = textureCoordinate - widthHeightStep;\n" +
49 | "topRightTextureCoordinate = textureCoordinate + widthNegativeHeightStep;\n" +
50 |
51 | "bottomTextureCoordinate = textureCoordinate + heightStep;\n" +
52 | "bottomLeftTextureCoordinate = textureCoordinate - widthNegativeHeightStep;\n" +
53 | "bottomRightTextureCoordinate = textureCoordinate + widthHeightStep;\n" +
54 | "}";
55 | }
56 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/FrameExtractListener.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract
9 |
10 | import android.graphics.Bitmap
11 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
12 |
13 | /**
14 | * Listener for frame extraction events.
15 | */
16 | @ExperimentalFrameExtractorApi
17 | interface FrameExtractListener {
18 | /**
19 | * Occurs when the specified job has started.
20 | */
21 | fun onStarted(id: String, timestampUs: Long) {}
22 |
23 | /**
24 | * Occurs when a frame is extracted. This method is not guaranteed to be called if there is an error.
25 | */
26 | fun onExtracted(id: String, timestampUs: Long, bitmap: Bitmap) {}
27 |
28 | /**
29 | * Extraction was aborted at some point.
30 | */
31 | fun onCancelled(id: String, timestampUs: Long) {}
32 |
33 | /**
34 | * An error occurred during extraction.
35 | */
36 | fun onError(id: String, timestampUs: Long, cause: Throwable?) {}
37 | }
38 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/FrameExtractMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract
9 |
10 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
11 |
12 | @ExperimentalFrameExtractorApi
13 | enum class FrameExtractMode {
14 | /**
15 | * Extract just the sync frames (typically this is fastest).
16 | */
17 | Fast,
18 |
19 | /**
20 | * Extract just the closest frames (attempts to find exact video frame, closest to specified timestamp).
21 | */
22 | Exact
23 | }
24 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/FrameExtractParameters.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract
9 |
10 | import android.graphics.Point
11 | import android.net.Uri
12 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
13 | import com.linkedin.android.litr.render.SingleFrameRenderer
14 |
15 | /**
16 | * Request parameters for frame extraction.
17 | */
18 | @ExperimentalFrameExtractorApi
19 | data class FrameExtractParameters @JvmOverloads constructor(
20 | /**
21 | * The URI of the media from which to extract the frame.
22 | */
23 | val mediaUri: Uri,
24 | /**
25 | * The timestamp, in microseconds, for which to extract the frame. If the frame can't be extracted for a specified timestamp, the nearest frame may be
26 | * returned: this frame selection behavior is defined in [mode].
27 | */
28 | val timestampUs: Long,
29 | /**
30 | * The renderer to use to process each of the frames before they're returned.
31 | *
32 | * One instance of the renderer may be shared between requests, as long as the dimensions of the video frames (for media specified in [mediaUri]) and
33 | * the [destSize] remain the same.
34 | */
35 | val renderer: SingleFrameRenderer,
36 | /**
37 | * A hint to the [ExtractionBehavior] about what approach to use for extracting video frames.
38 | */
39 | val mode: FrameExtractMode = FrameExtractMode.Fast,
40 | /**
41 | * Optional size of the generated frame Bitmap. The video frame will be resized and scaled to fill this [destSize], preserving the video frame's
42 | * original aspect ratio.
43 | */
44 | val destSize: Point? = null,
45 | /**
46 | * The optional priority of the request. Lower value indicates higher priority. Requests with equal priority will be handled in FIFO order.
47 | */
48 | val priority: Long = 0L
49 | )
50 |
51 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/behaviors/FrameExtractBehavior.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract.behaviors
9 |
10 | import android.graphics.Bitmap
11 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
12 | import com.linkedin.android.litr.frameextract.FrameExtractParameters
13 |
14 | /**
15 | * An interface used by [FrameExtractBehavior] to notify the job of the status of individual frame extraction.
16 | */
17 | interface FrameExtractBehaviorFrameListener {
18 | fun onFrameExtracted(bitmap: Bitmap)
19 | fun onFrameFailed()
20 | }
21 |
22 | /**
23 | * Provides a way to customize frame extraction behavior.
24 | *
25 | * All methods are guaranteed to be called on the same thread, but the thread will not be the main thread.
26 | */
27 | @ExperimentalFrameExtractorApi
28 | interface FrameExtractBehavior {
29 | /**
30 | * Perform frame extraction work here, and notify [listener] for each frame extracted. This method is called only once.
31 | *
32 | * For long-running operations, [Thread.isInterrupted] should be checked periodically. If interrupted, implementation should return false from this method.
33 | *
34 | * @return Return true if extraction is completed/scheduled, false if it was canceled.
35 | */
36 | fun extract(params: FrameExtractParameters, listener: FrameExtractBehaviorFrameListener): Boolean
37 |
38 | /**
39 | * Called when this behavior should clean up any associated resources.
40 | */
41 | fun release()
42 | }
43 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/queue/ComparableFutureTask.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract.queue
9 |
10 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
11 | import com.linkedin.android.litr.frameextract.FrameExtractJob
12 | import java.util.concurrent.*
13 | import java.util.concurrent.atomic.AtomicLong
14 |
15 | /**
16 | * A [FutureTask] that is has a priority, so that it can be e.g. used in a priority queue with other tasks.
17 | *
18 | * This task maintains a FIFO order for priority.
19 | */
20 | @ExperimentalFrameExtractorApi
21 | internal class ComparableFutureTask(private val job: FrameExtractJob?, result: T, private var priority: Long) : FutureTask(job, result),
22 | Comparable> {
23 | private val sequenceNumber = sharedSequence.getAndIncrement()
24 |
25 | val isStarted: Boolean
26 | get() = job?.isStarted ?: false
27 |
28 | override fun compareTo(other: ComparableFutureTask): Int {
29 | var res = priority.compareTo(other.priority)
30 |
31 | if (res == 0) {
32 | res = if (sequenceNumber < other.sequenceNumber) -1 else 1
33 | }
34 |
35 | return res
36 | }
37 |
38 | companion object {
39 | val sharedSequence = AtomicLong()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/frameextract/queue/PriorityExecutorUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.frameextract.queue
9 |
10 | import java.util.concurrent.*
11 |
12 | /**
13 | * Helpers for constructing an [ExecutorService] backed by a [PriorityBlockingQueue] for prioritizing tasks.
14 | */
15 | internal object PriorityExecutorUtil {
16 | fun newSingleThreadPoolPriorityExecutor() = ThreadPoolExecutor(
17 | 1,
18 | 1,
19 | 0L,
20 | TimeUnit.MILLISECONDS,
21 | PriorityBlockingQueue()
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/io/MediaRange.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.io;
9 |
10 | /**
11 | * Data class used to define a range (start, stop) of media. For example, it can be used to
12 | * define a "selection" in a MediaSource
13 | */
14 | public class MediaRange {
15 |
16 | private final long start;
17 | private final long end;
18 |
19 | /**
20 | * Create an instance of MediaRange
21 | * @param start range start, in microseconds
22 | * @param end range end, in microseconds, greater than start
23 | */
24 | public MediaRange(long start, long end) {
25 | this.start = start;
26 | this.end = end;
27 | }
28 |
29 | /**
30 | * Get range start, in microseconds
31 | */
32 | public long getStart() {
33 | return start;
34 | }
35 |
36 | /**
37 | * Get range end, in microseconds
38 | */
39 | public long getEnd() {
40 | return end;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/io/MediaTarget.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.io;
9 |
10 | import android.media.MediaCodec;
11 | import android.media.MediaFormat;
12 | import androidx.annotation.IntRange;
13 | import androidx.annotation.NonNull;
14 |
15 | import java.nio.ByteBuffer;
16 |
17 | /**
18 | * Common interface for MediaTarget
19 | */
20 | public interface MediaTarget {
21 |
22 | /**
23 | * Adds a track with the specified format.
24 | * @param mediaFormat The media format for the track. This must not be an empty MediaFormat.
25 | * @param targetTrack target track index
26 | * @return index of a newly added track
27 | */
28 | int addTrack(@NonNull MediaFormat mediaFormat, @IntRange(from = 0) int targetTrack);
29 |
30 | /**
31 | * Writes an encoded sample into the muxer.
32 | * @param targetTrack target track index
33 | * @param buffer encoded data
34 | * @param info metadata
35 | */
36 | void writeSampleData(int targetTrack, @NonNull ByteBuffer buffer, @NonNull MediaCodec.BufferInfo info);
37 |
38 | /**
39 | * Release all resources. Make sure to call this when MediaTarget is no longer needed
40 | */
41 | void release();
42 |
43 | /**
44 | * Get output file path
45 | * @return output file path
46 | */
47 | @NonNull
48 | String getOutputFilePath();
49 | }
50 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/io/MediaTargetSample.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | *
8 | * Author: Ian Bird
9 | */
10 | package com.linkedin.android.litr.io
11 |
12 | import android.media.MediaCodec
13 | import java.nio.ByteBuffer
14 |
15 | /**
16 | * If a {@link MediaTarget} needs to temporarily queue up samples, this class can provide a deep
17 | * copy of the sample to allow the original to be returned (e.g. to the encoder).
18 | */
19 | class MediaTargetSample(
20 | val targetTrack: Int,
21 | buffer: ByteBuffer,
22 | info: MediaCodec.BufferInfo
23 | ) {
24 | val buffer: ByteBuffer = ByteBuffer.allocate(buffer.capacity())
25 | val info : MediaCodec.BufferInfo = MediaCodec.BufferInfo()
26 |
27 | init {
28 | this.info.set(0, info.size, info.presentationTimeUs, info.flags)
29 |
30 | // We want to make a deep copy so that we can release the incoming buffer back to the
31 | // encoder immediately.
32 | this.buffer.put(buffer)
33 | this.buffer.flip()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/preview/VideoFilterPreviewView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.preview;
9 |
10 | import android.content.Context;
11 | import android.opengl.GLSurfaceView;
12 | import android.util.AttributeSet;
13 |
14 | import androidx.annotation.NonNull;
15 |
16 | public class VideoFilterPreviewView extends GLSurfaceView {
17 |
18 | public VideoFilterPreviewView(Context context) {
19 | this(context, null);
20 | }
21 |
22 | public VideoFilterPreviewView(Context context, AttributeSet attributeSet) {
23 | super(context, attributeSet);
24 |
25 | setEGLContextFactory(new PreviewEglContextFactory());
26 | setEGLConfigChooser(new PreviewEglConfigChooser());
27 | }
28 |
29 | @Override
30 | public void setRenderer(Renderer renderer) {
31 | super.setRenderer(renderer);
32 |
33 | if (renderer instanceof VideoPreviewRenderer) {
34 | ((VideoPreviewRenderer) renderer).setPreviewRenderListener(new VideoPreviewRenderer.PreviewRenderListener() {
35 | @Override
36 | public void onRenderRequested() {
37 | requestRender();
38 | }
39 |
40 | @Override
41 | public void onEventQueued(@NonNull Runnable runnable) {
42 | queueEvent(runnable);
43 | }
44 | });
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/AudioProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.render
9 |
10 | import com.linkedin.android.litr.codec.Frame
11 |
12 | /**
13 | * Common interface for all audio processors
14 | */
15 | interface AudioProcessor {
16 |
17 | /**
18 | * Process (resample, mix channels, etc.) an audio frame from source to target format.
19 | * @param sourceFrame input frame that needs to be processed
20 | * @param targetFrame output frame, which will have a pre-allocated buffer to put processed data into
21 | */
22 | fun processFrame(sourceFrame: Frame, targetFrame: Frame)
23 |
24 | /**
25 | * Release processor. After this method is called, processor can no longer be used.
26 | * New instance of [AudioProcessor] must be initialized if necessary.
27 | */
28 | fun release()
29 | }
30 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/AudioProcessorFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.render
9 |
10 | import android.media.MediaFormat
11 |
12 | class AudioProcessorFactory {
13 |
14 | fun createAudioProcessor(sourceMediaFormat: MediaFormat?, targetMediaFormat: MediaFormat?): AudioProcessor {
15 | return if (sourceMediaFormat != null &&
16 | targetMediaFormat != null &&
17 | sourceMediaFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) &&
18 | targetMediaFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) &&
19 | sourceMediaFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) &&
20 | targetMediaFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) &&
21 | (sourceMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) != targetMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)
22 | || sourceMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) != targetMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT))) {
23 | OboeAudioProcessor(
24 | sourceMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT),
25 | sourceMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE),
26 | targetMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT),
27 | targetMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)
28 | )
29 | } else {
30 | PassthroughAudioProcessor()
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/FrameDropper.kt:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.render
2 |
3 | /**
4 | * A simple video frame dropper which provides a [shouldRender] function. May be used when the
5 | * target video should have a lower frame rate than its source video.
6 | *
7 | * @see Suggested by @alexvasilkov
8 | */
9 | internal interface FrameDropper {
10 |
11 | fun shouldRender(): Boolean
12 | }
13 |
14 | class DefaultFrameDropper(
15 | inputFps: Int,
16 | outputFps: Int,
17 | ) : FrameDropper {
18 |
19 | private val inputSpf = 1.0 / inputFps
20 | private val outputSpf = 1.0 / outputFps
21 | private var currentSpf = 0.0
22 | private var frameCount = 0
23 |
24 | override fun shouldRender(): Boolean {
25 | currentSpf += inputSpf
26 |
27 | return when {
28 | frameCount++ == 0 -> true
29 | currentSpf > outputSpf -> true.also { currentSpf -= outputSpf }
30 | else -> false
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/PassthroughAudioProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.render
9 |
10 | import com.linkedin.android.litr.codec.Frame
11 | import java.lang.IllegalArgumentException
12 |
13 | /**
14 | * Simple implementation of [AudioProcessor] that duplicates a source frame into target frame.
15 | */
16 |
17 | internal class PassthroughAudioProcessor : AudioProcessor {
18 |
19 | override fun processFrame(sourceFrame: Frame, targetFrame: Frame) {
20 | if (sourceFrame.buffer != null && targetFrame.buffer != null) {
21 | targetFrame.buffer.put(sourceFrame.buffer)
22 | targetFrame.buffer.flip()
23 |
24 | targetFrame.bufferInfo.apply {
25 | offset = 0
26 | size = sourceFrame.bufferInfo.size
27 | presentationTimeUs = sourceFrame.bufferInfo.presentationTimeUs
28 | flags = sourceFrame.bufferInfo.flags
29 | }
30 | } else {
31 | throw IllegalArgumentException("Source or target frame doesn't have a buffer, cannot process it!")
32 | }
33 | }
34 |
35 | override fun release() {}
36 | }
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/Renderer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.render;
9 |
10 | import android.media.MediaCodec;
11 | import android.media.MediaFormat;
12 | import android.view.Surface;
13 |
14 | import androidx.annotation.Nullable;
15 |
16 | import com.linkedin.android.litr.codec.Frame;
17 |
18 | /**
19 | * Common interface for renderer
20 | */
21 | public interface Renderer {
22 |
23 | /**
24 | * Initialize the renderer. Called during track transformer initialization.
25 | * @param outputSurface {@link Surface} to render onto, null for non OpenGL renderer
26 | * @param sourceMediaFormat source {@link MediaFormat}
27 | * @param targetMediaFormat target {@link MediaFormat}
28 | */
29 | void init(@Nullable Surface outputSurface, @Nullable MediaFormat sourceMediaFormat, @Nullable MediaFormat targetMediaFormat);
30 |
31 | /**
32 | * This should be called every time the {@link MediaCodec#INFO_OUTPUT_FORMAT_CHANGED} is returned
33 | * @param sourceMediaFormat source {@link MediaFormat}
34 | * @param targetMediaFormat target {@link MediaFormat}
35 | */
36 | void onMediaFormatChanged(@Nullable MediaFormat sourceMediaFormat, @Nullable MediaFormat targetMediaFormat);
37 |
38 | /**
39 | * Get renderer's input surface. Renderer creates it internally.
40 | * @return {@link Surface} to get pixels from, null for non OpenGL renderer
41 | */
42 | @Nullable Surface getInputSurface();
43 |
44 | /**
45 | * Render a frame
46 | * @param inputFrame {@link Frame} to operate with. Non-null ror non OpenGL renderer, will contain raw pixels.
47 | * null for GL renderer, which should assume that environment has been set and just invoke Gl calls.
48 | * @param presentationTimeNs frame presentation time in nanoseconds
49 | */
50 | void renderFrame(@Nullable Frame inputFrame, long presentationTimeNs);
51 |
52 | /**
53 | * Release the renderer and all it resources.
54 | */
55 | void release();
56 |
57 | /**
58 | * Check if renderer has user provided filters
59 | * @return true if has, false otherwise
60 | */
61 | boolean hasFilters();
62 | }
63 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/render/SingleFrameRenderer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.render
9 |
10 | import android.graphics.Bitmap
11 | import com.linkedin.android.litr.ExperimentalFrameExtractorApi
12 |
13 | @ExperimentalFrameExtractorApi
14 | interface SingleFrameRenderer {
15 | fun renderFrame(input: Bitmap?, presentationTimeNs: Long): Bitmap?
16 | }
17 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/test/TransformationEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.test;
9 |
10 | import androidx.annotation.FloatRange;
11 | import androidx.annotation.IntDef;
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 |
15 | import java.lang.annotation.Retention;
16 | import java.lang.annotation.RetentionPolicy;
17 |
18 | public class TransformationEvent {
19 |
20 | public static final int TYPE_START = 0;
21 | public static final int TYPE_PROGRESS = 1;
22 | public static final int TYPE_COMPLETED = 2;
23 | public static final int TYPE_ERROR = 3;
24 | public static final int TYPE_CANCELLED = 4;
25 |
26 | @Retention(RetentionPolicy.SOURCE)
27 | @IntDef({ TYPE_START, TYPE_PROGRESS, TYPE_COMPLETED, TYPE_ERROR, TYPE_CANCELLED})
28 | @interface EventType {}
29 |
30 | @NonNull public final String id;
31 | @EventType public final int type;
32 | public final float progress;
33 | @Nullable public final Throwable cause;
34 |
35 | public TransformationEvent(@NonNull String id,
36 | @EventType int type,
37 | @FloatRange(from = 0, to = 1) float progress,
38 | @Nullable Throwable cause) {
39 | this.id = id;
40 | this.type = type;
41 | this.progress = progress;
42 | this.cause = cause;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/utils/ByteBufferPool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.utils
9 |
10 | import java.nio.ByteBuffer
11 | import java.nio.ByteOrder
12 | import java.util.concurrent.LinkedBlockingQueue
13 |
14 | /**
15 | * A helper thread safe class that manages a [ByteBuffer] pool, to increase buffer reuse.
16 | * This class is very useful in classes like renderers because they work with sequences of same sized buffers.
17 | */
18 | class ByteBufferPool(private val isDirect: Boolean = false) {
19 |
20 | private val bufferQueue = LinkedBlockingQueue()
21 |
22 | /**
23 | * Get a buffer from the pool. If buffer of at least requested capacity is available in the pool,
24 | * it will be returned. Otherwise, new buffer of requested capacity will be created.
25 | * Returned buffer will be ready to receive data.
26 | */
27 | fun get(capacity: Int): ByteBuffer {
28 | return bufferQueue.poll()?.let { byteBuffer ->
29 | if (byteBuffer.capacity() >= capacity) {
30 | byteBuffer
31 | } else {
32 | allocateByteBuffer(capacity)
33 | }
34 | } ?: allocateByteBuffer(capacity)
35 | }
36 |
37 | /**
38 | * Put a buffer back in the pool. Buffer will be cleared: position will be set to 0, and limit to capacity.
39 | * Contents of a buffer must be consumed before calling this method.
40 | */
41 | fun put(byteBuffer: ByteBuffer) {
42 | byteBuffer.clear()
43 | bufferQueue.put(byteBuffer)
44 | }
45 |
46 | /**
47 | * Clear the pool, all entries in it will be removed.
48 | */
49 | fun clear() {
50 | bufferQueue.clear()
51 | }
52 |
53 | private fun allocateByteBuffer(capacity: Int): ByteBuffer {
54 | return if (isDirect) {
55 | ByteBuffer.allocateDirect(capacity).order(ByteOrder.LITTLE_ENDIAN)
56 | } else {
57 | ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/utils/DiskUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 LinkedIn Corporation
3 | * All Rights Reserved.
4 | *
5 | * Licensed under the BSD 2-Clause License (the "License"). See License in the project root for
6 | * license information.
7 | */
8 | package com.linkedin.android.litr.utils;
9 |
10 | import android.os.Build;
11 | import android.os.Environment;
12 | import android.os.StatFs;
13 | import android.util.Log;
14 |
15 | public class DiskUtil {
16 | private static final String TAG = DiskUtil.class.getSimpleName();
17 |
18 | public static final long FREE_DISK_SPACE_CHECK_FAILED = -1;
19 |
20 | /**
21 | * This method returns the available disk space in data directory, measured in bytes,
22 | * for the application.
23 | *
24 | * @return free disk space in bytes, or FREE_DISK_SPACE_CHECK_FAILED if cannot be determined.
25 | */
26 | @SuppressWarnings("IllegalCatch")
27 | public long getAvailableDiskSpaceInDataDirectory() {
28 | try {
29 | StatFs statFs = new StatFs(Environment.getDataDirectory().getAbsolutePath());
30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
31 | return statFs.getAvailableBytes();
32 | } else {
33 | return (long) statFs.getAvailableBlocks() * statFs.getBlockSize();
34 | }
35 | } catch (Exception e) {
36 | Log.e(TAG, "Could not get Available Disk Space");
37 | return FREE_DISK_SPACE_CHECK_FAILED;
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/utils/MediaFormatUtils.kt:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.utils
2 |
3 | import android.media.MediaFormat
4 | import android.os.Build
5 |
6 | class MediaFormatUtils {
7 | companion object {
8 | @JvmStatic
9 | fun getIFrameInterval(format: MediaFormat, defaultValue: Number): Number {
10 | return getNumber(format, MediaFormat.KEY_I_FRAME_INTERVAL) ?: defaultValue
11 | }
12 |
13 | @JvmStatic
14 | fun getFrameRate(format: MediaFormat, defaultValue: Number): Number {
15 | return getNumber(format, MediaFormat.KEY_FRAME_RATE) ?: defaultValue
16 | }
17 |
18 | @JvmStatic
19 | fun getChannelCount(format: MediaFormat, defaultValue: Number): Number {
20 | return getNumber(format, MediaFormat.KEY_CHANNEL_COUNT) ?: defaultValue
21 | }
22 |
23 | @JvmStatic
24 | fun getSampleRate(format: MediaFormat, defaultValue: Number): Number {
25 | return getNumber(format, MediaFormat.KEY_SAMPLE_RATE) ?: defaultValue
26 | }
27 |
28 | @JvmStatic
29 | fun getNumber(format: MediaFormat, key: String): Number? {
30 | return when {
31 | !format.containsKey(key) -> null
32 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> format.getNumber(key)
33 | else -> runCatching {
34 | format.getInteger(key)
35 | }.recoverCatching {
36 | format.getFloat(key)
37 | }.getOrNull()
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/litr/src/main/java/com/linkedin/android/litr/utils/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.linkedin.android.litr.utils;
2 |
3 | class TimeUtils {
4 |
5 | /**
6 | * Convert the time from Micros(Long) to seconds(float) to keep the fractions of a second
7 | *
8 | * @param timeUs time in Microseconds
9 | * @return the time as float to keep the fractions of a second
10 | */
11 | public static float microsToSeconds(long timeUs) {
12 | return timeUs / 1_000_000F;
13 | }
14 |
15 | /**
16 | * Convert the time from Millis(Long) to seconds(float) to keep the fractions of a second
17 | *
18 | * @param timeMs time in Milliseconds
19 | * @return the time as float to keep the fractions of a second
20 | */
21 | public static float millisToSeconds(long timeMs) {
22 | return timeMs / 1_000F;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':litr-demo', ':litr', ':litr-filters'
2 | // uncomment if experimenting with ffmpeg
3 | // include ':litr-ffmpeg'
4 |
--------------------------------------------------------------------------------