Skip to content

Commit

Permalink
Refactoring and add copyright text
Browse files Browse the repository at this point in the history
  • Loading branch information
rohitjoins committed Oct 23, 2024
1 parent 59c1f2b commit c835c68
Show file tree
Hide file tree
Showing 18 changed files with 443 additions and 227 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
* Smooth Streaming Extension:
* RTSP Extension:
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
* Add the MPEG-H decoder module which uses the native MPEG-H decoder
module to decode MPEG-H audio
([#1826](https://github.com/androidx/media/pull/1826)).
* MIDI extension:
* Leanback extension:
* Cast Extension:
Expand Down
13 changes: 4 additions & 9 deletions demos/main/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
[
{
"name": "MPEG-H",
"samples": [
{
"name": "HD (MP4, H265)",
"uri": "https://media.githubusercontent.com/media/Fraunhofer-IIS/mpegh-test-content/main/TRI_Fileset_17_514H_D1_D2_D3_O1_24bit1080p50.mp4"
}
]
},
{
"name": "Clear DASH",
"samples": [
Expand Down Expand Up @@ -771,6 +762,10 @@
{
"name": "Immersive Audio Format Sample (MP4, IAMF)",
"uri": "https://github.com/AOMediaCodec/libiamf/raw/main/tests/test_000036_s.mp4"
},
{
"name": "MPEG-H HD (MP4, H265)",
"uri": "https://media.githubusercontent.com/media/Fraunhofer-IIS/mpegh-test-content/main/TRI_Fileset_17_514H_D1_D2_D3_O1_24bit1080p50.mp4"
}
]
},
Expand Down
44 changes: 20 additions & 24 deletions libraries/decoder_mpegh/README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
# MPEG-H decoder module

The MPEG-H module provides `MpeghAudioRenderer`, which uses [mpeghdec
(the Fraunhofer MPEG-H decoding library)](https://github.com/Fraunhofer-IIS/mpeghdec) to decode MPEG-H audio.

The MPEG-H module provides `MpeghAudioRenderer`, which uses the libmpegh native
library to decode MPEG-H audio.

## License note

Please note that whilst this code for extending ExoPlayer for MPEG-H 3D Audio
Decoder functionality is licensed under Apache 2.0, using this extension requires
checkout and compile the [Fraunhofer GitHub MPEG-H decoding library](https://github.com/Fraunhofer-IIS/mpeghdec)
as described below. The [Fraunhofer GitHub MPEG-H decoder](https://github.com/Fraunhofer-IIS/mpeghdec) is not licensed
under Apache 2.0, but under the license of the Fraunhofer GitHub MPEG-H
decoder project.
Please note that whilst the code in this repository is licensed under
[Apache 2.0][], using this module also requires building and including the
[Fraunhofer GitHub MPEG-H decoder][] which is licensed under the Fraunhofer
GitHub MPEG-H decoder project.

[Apache 2.0]: ../../LICENSE
[Fraunhofer GitHub MPEG-H decoder]: https://github.com/Fraunhofer-IIS/mpeghdec

## Build instructions (Linux, macOS)

To use the module you need to clone this GitHub project and depend on its
modules locally. Instructions for doing this can be found in the
[top level README][].

In addition, it's necessary to fetch mpeghdec library with its
dependencies as follows:
In addition, it's necessary to fetch libmpegh library as follows:

* Set the following environment variables:
* Set the following environment variables:

```
cd "<path to project checkout>"
MPEGH_MODULE_PATH="$(pwd)/libraries/decoder_mpegh/src/main"
```

* Fetch mpeghdec library:
* Fetch libmpegh library:

```
cd "${MPEGH_MODULE_PATH}/jni" && \
git clone https://github.com/Fraunhofer-IIS/mpeghdec.git --branch r2.0.0
git clone https://github.com/Fraunhofer-IIS/mpeghdec.git --branch r2.0.0 libmpegh
```

* [Install CMake][].
* [Install CMake][].

Having followed these steps, gradle will build the module automatically when run
on the command line or via Android Studio, using [CMake][] and [Ninja][] to
Expand All @@ -60,31 +57,30 @@ be possible to follow the Linux instructions in [Windows PowerShell][].
## Using the module with ExoPlayer

Once you've followed the instructions above to check out, build and depend on
the module, the next step is to tell ExoPlayer to use `MpeghAudioRenderer`.
How you do this depends on which player API you're using:
the module, the next step is to tell ExoPlayer to use `MpeghAudioRenderer`. How
you do this depends on which player API you're using:

* If you're passing a `DefaultRenderersFactory` to `ExoPlayer.Builder`, you
can enable using the module by setting the `extensionRendererMode` parameter
of the `DefaultRenderersFactory` constructor to
`EXTENSION_RENDERER_MODE_ON`. This will use `MpeghAudioRenderer` for
playback if `MediaCodecAudioRenderer` doesn't support the input format. Pass
`EXTENSION_RENDERER_MODE_PREFER` to give `MpeghAudioRenderer` priority
over `MediaCodecAudioRenderer`.
`EXTENSION_RENDERER_MODE_PREFER` to give `MpeghAudioRenderer` priority over
`MediaCodecAudioRenderer`.
* If you've subclassed `DefaultRenderersFactory`, add a `MpeghAudioRenderer`
to the output list in `buildAudioRenderers`. ExoPlayer will use the first
`Renderer` in the list that supports the input media format.
* If you've implemented your own `RenderersFactory`, return a
`MpeghAudioRenderer` instance from `createRenderers`. ExoPlayer will use
the first `Renderer` in the returned array that supports the input media
format.
`MpeghAudioRenderer` instance from `createRenderers`. ExoPlayer will use the
first `Renderer` in the returned array that supports the input media format.
* If you're using `ExoPlayer.Builder`, pass a `MpeghAudioRenderer` in the
array of `Renderer`s. ExoPlayer will use the first `Renderer` in the list
that supports the input media format.

Note: These instructions assume you're using `DefaultTrackSelector`. If you have
a custom track selector the choice of `Renderer` is up to your implementation,
so you need to make sure you are passing an `MpeghAudioRenderer` to the
player, then implement your own logic to use the renderer for a given track.
so you need to make sure you are passing an `MpeghAudioRenderer` to the player,
then implement your own logic to use the renderer for a given track.

## Links

Expand Down
57 changes: 50 additions & 7 deletions libraries/decoder_mpegh/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,56 @@
// Copyright 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle"

android.namespace 'androidx.media3.decoder.mpegh'
android {
namespace 'androidx.media3.decoder.mpegh'

// Configure the native build only if ffmpeg is present to avoid gradle sync
// failures if ffmpeg hasn't been built according to the README instructions.
if (project.file('src/main/jni/mpeghdec').exists()) {
android.externalNativeBuild.cmake.path = 'src/main/jni/CMakeLists.txt'
// Should match cmake_minimum_required.
android.externalNativeBuild.cmake.version = '3.21.0+'
sourceSets {
androidTest.assets.srcDir '../test_data/src/test/assets'
}

defaultConfig {
externalNativeBuild {
cmake {
targets "mpeghJNI"
}
}
}

// TODO(Internal: b/372449691): Remove packagingOptions once AGP is updated
// to version 8.5.1 or higher.
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}

// Configure the native build only if libmpegh is present to avoid gradle sync
// failures if libmpegh hasn't been built according to the README instructions.
if (project.file('src/main/jni/libmpegh').exists()) {
android.externalNativeBuild.cmake {
path = 'src/main/jni/CMakeLists.txt'
version = '3.21.0+'
if (project.hasProperty('externalNativeBuildDir')) {
if (!new File(externalNativeBuildDir).isAbsolute()) {
ext.externalNativeBuildDir =
new File(rootDir, it.externalNativeBuildDir)
}
buildStagingDirectory = "${externalNativeBuildDir}/${project.name}"
}
}
}

dependencies {
Expand Down
15 changes: 15 additions & 0 deletions libraries/decoder_mpegh/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<manifest package="androidx.media3.decoder.mpegh"/>
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.decoder.mpegh;

import android.os.Handler;
Expand All @@ -6,16 +21,16 @@
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.TraceUtil;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.decoder.CryptoConfig;
import androidx.media3.exoplayer.DecoderReuseEvaluation;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DecoderAudioRenderer;
import java.util.Objects;

/**
* Decodes and renders audio using the native MPEG-H decoder.
*/
/** Decodes and renders audio using the native MPEG-H decoder. */
@UnstableApi
public final class MpeghAudioRenderer extends DecoderAudioRenderer<MpeghDecoder> {

Expand All @@ -24,23 +39,29 @@ public final class MpeghAudioRenderer extends DecoderAudioRenderer<MpeghDecoder>
/** The number of input and output buffers. */
private static final int NUM_BUFFERS = 16;

/* Creates a new instance. */
public MpeghAudioRenderer() {
this(null, null);
this(/* eventHandler= */ null, /* eventListener= */ null);
}

/**
* Creates a new instance.
*
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public MpeghAudioRenderer(
Handler eventHandler, AudioRendererEventListener eventListener,
Handler eventHandler,
AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
super(eventHandler, eventListener, audioProcessors);
}

/**
* Creates a new instance.
*
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
Expand All @@ -57,15 +78,15 @@ public String getName() {
}

@Override
protected int supportsFormatInternal(Format format) {
// check if JNI library is available
protected @C.FormatSupport int supportsFormatInternal(Format format) {
// Check if JNI library is available.
if (!MpeghLibrary.isAvailable()) {
return C.FORMAT_UNSUPPORTED_TYPE;
}

// check if MIME type is supported
if (!(MimeTypes.AUDIO_MPEGH_MHM1.equalsIgnoreCase(format.sampleMimeType)
|| MimeTypes.AUDIO_MPEGH_MHA1.equalsIgnoreCase(format.sampleMimeType))) {
// Check if MIME type is supported.
if (!(Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_MPEGH_MHM1)
|| Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_MPEGH_MHA1))) {
return C.FORMAT_UNSUPPORTED_TYPE;
}
return C.FORMAT_HANDLED;
Expand All @@ -74,29 +95,34 @@ protected int supportsFormatInternal(Format format) {
@Override
protected DecoderReuseEvaluation canReuseDecoder(
@NonNull String decoderName, Format oldFormat, Format newFormat) {

if (oldFormat.sampleMimeType.equals(newFormat.sampleMimeType)
&& oldFormat.sampleMimeType.equals(MimeTypes.AUDIO_MPEGH_MHM1)) {
if (Objects.equals(oldFormat.sampleMimeType, newFormat.sampleMimeType)
&& Objects.equals(oldFormat.sampleMimeType, MimeTypes.AUDIO_MPEGH_MHM1)) {
return new DecoderReuseEvaluation(
decoderName, oldFormat, newFormat,
DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, 0);
decoderName,
oldFormat,
newFormat,
DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION,
/* discardReasons= */ 0);
}
return super.canReuseDecoder(decoderName, oldFormat, newFormat);
}

@Override
protected MpeghDecoder createDecoder(@NonNull Format format, CryptoConfig cryptoConfig)
throws MpeghException {

// initialize the decoder
return new MpeghDecoder(NUM_BUFFERS, NUM_BUFFERS, format);
throws MpeghDecoderException {
TraceUtil.beginSection("createMpeghDecoder");
MpeghDecoder decoder = new MpeghDecoder(format, NUM_BUFFERS, NUM_BUFFERS);
TraceUtil.endSection();
return decoder;
}

@Override
protected Format getOutputFormat(MpeghDecoder decoder) {
Format.Builder formatBuilder = new Format.Builder();
formatBuilder.setChannelCount(decoder.getChannelCount()).setSampleRate(decoder.getSampleRate());
formatBuilder.setSampleMimeType(MimeTypes.AUDIO_RAW).setPcmEncoding(C.ENCODING_PCM_16BIT);
return formatBuilder.build();
return new Format.Builder()
.setChannelCount(decoder.getChannelCount())
.setSampleRate(decoder.getSampleRate())
.setSampleMimeType(MimeTypes.AUDIO_RAW)
.setPcmEncoding(C.ENCODING_PCM_16BIT)
.build();
}
}
Loading

0 comments on commit c835c68

Please sign in to comment.