Skip to content

Commit e651812

Browse files
andrewlewisicbaker
authored andcommitted
Add frame processor based on MediaPipe to demo
PiperOrigin-RevId: 446432695
1 parent 9ca0f78 commit e651812

File tree

9 files changed

+333
-5
lines changed

9 files changed

+333
-5
lines changed

Diff for: demos/transformer/BUILD.bazel

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Build targets for a demo MediaPipe graph.
2+
# See README.md for instructions on using MediaPipe in the demo.
3+
4+
load("//mediapipe/java/com/google/mediapipe:mediapipe_aar.bzl", "mediapipe_aar")
5+
load(
6+
"//mediapipe/framework/tool:mediapipe_graph.bzl",
7+
"mediapipe_binary_graph",
8+
)
9+
10+
mediapipe_aar(
11+
name = "edge_detector_mediapipe_aar",
12+
calculators = [
13+
"//mediapipe/calculators/image:luminance_calculator",
14+
"//mediapipe/calculators/image:sobel_edges_calculator",
15+
],
16+
)
17+
18+
mediapipe_binary_graph(
19+
name = "edge_detector_binary_graph",
20+
graph = "edge_detector_mediapipe_graph.pbtxt",
21+
output_name = "edge_detector_mediapipe_graph.binarypb",
22+
)

Diff for: demos/transformer/README.md

+56
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,60 @@ example by removing audio or video.
66
See the [demos README](../README.md) for instructions on how to build and run
77
this demo.
88

9+
## MediaPipe frame processing demo
10+
11+
Building the demo app with [MediaPipe][] integration enabled requires some extra
12+
manual steps.
13+
14+
1. Follow the
15+
[instructions](https://google.github.io/mediapipe/getting_started/install.html)
16+
to install MediaPipe.
17+
1. Copy the Transformer demo's build configuration and MediaPipe graph text
18+
protocol buffer under the MediaPipe source tree. This makes it easy to
19+
[build an AAR][] with bazel by reusing MediaPipe's workspace.
20+
21+
```sh
22+
cd "<path to MediaPipe checkout>"
23+
MEDIAPIPE_ROOT="$(pwd)"
24+
MEDIAPIPE_TRANSFORMER_ROOT="${MEDIAPIPE_ROOT}/mediapipe/java/com/google/mediapipe/transformer"
25+
cd "<path to the transformer demo (containing this readme)>"
26+
TRANSFORMER_DEMO_ROOT="$(pwd)"
27+
mkdir -p "${MEDIAPIPE_TRANSFORMER_ROOT}"
28+
mkdir -p "${TRANSFORMER_DEMO_ROOT}/libs"
29+
cp ${TRANSFORMER_DEMO_ROOT}/BUILD.bazel ${MEDIAPIPE_TRANSFORMER_ROOT}/BUILD
30+
cp ${TRANSFORMER_DEMO_ROOT}/src/withMediaPipe/assets/edge_detector_mediapipe_graph.pbtxt \
31+
${MEDIAPIPE_TRANSFORMER_ROOT}
32+
```
33+
34+
1. Build the AAR and the binary proto for the demo's MediaPipe graph, then copy
35+
them to Transformer.
36+
37+
```sh
38+
cd ${MEDIAPIPE_ROOT}
39+
bazel build -c opt --strip=ALWAYS \
40+
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
41+
--fat_apk_cpu=arm64-v8a,armeabi-v7a \
42+
--legacy_whole_archive=0 \
43+
--features=-legacy_whole_archive \
44+
--copt=-fvisibility=hidden \
45+
--copt=-ffunction-sections \
46+
--copt=-fdata-sections \
47+
--copt=-fstack-protector \
48+
--copt=-Oz \
49+
--copt=-fomit-frame-pointer \
50+
--copt=-DABSL_MIN_LOG_LEVEL=2 \
51+
--linkopt=-Wl,--gc-sections,--strip-all \
52+
mediapipe/java/com/google/mediapipe/transformer:edge_detector_mediapipe_aar.aar
53+
cp bazel-bin/mediapipe/java/com/google/mediapipe/transformer/edge_detector_mediapipe_aar.aar \
54+
${TRANSFORMER_DEMO_ROOT}/libs
55+
bazel build mediapipe/java/com/google/mediapipe/transformer:edge_detector_binary_graph
56+
cp bazel-bin/mediapipe/java/com/google/mediapipe/transformer/edge_detector_mediapipe_graph.binarypb \
57+
${TRANSFORMER_DEMO_ROOT}/src/withMediaPipe/assets
58+
```
59+
60+
1. Select the `withMediaPipe` build variant in Android Studio, then build and
61+
run the demo app and select a MediaPipe-based effect.
62+
963
[Transformer]: https://exoplayer.dev/transforming-media.html
64+
[MediaPipe]: https://google.github.io/mediapipe/
65+
[build an AAR]: https://google.github.io/mediapipe/getting_started/android_archive_library.html

Diff for: demos/transformer/build.gradle

+18
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ android {
4545
// This demo app isn't indexed and doesn't have translations.
4646
disable 'GoogleAppIndexingWarning','MissingTranslation'
4747
}
48+
49+
flavorDimensions "mediaPipe"
50+
51+
productFlavors {
52+
noMediaPipe {
53+
dimension "mediaPipe"
54+
}
55+
withMediaPipe {
56+
dimension "mediaPipe"
57+
}
58+
}
4859
}
4960

5061
dependencies {
@@ -59,4 +70,11 @@ dependencies {
5970
implementation project(modulePrefix + 'lib-exoplayer-dash')
6071
implementation project(modulePrefix + 'lib-transformer')
6172
implementation project(modulePrefix + 'lib-ui')
73+
74+
// For MediaPipe and its dependencies:
75+
withMediaPipeImplementation fileTree(dir: 'libs', include: ['*.aar'])
76+
withMediaPipeImplementation 'com.google.flogger:flogger:latest.release'
77+
withMediaPipeImplementation 'com.google.flogger:flogger-system-backend:latest.release'
78+
withMediaPipeImplementation 'com.google.code.findbugs:jsr305:latest.release'
79+
withMediaPipeImplementation 'com.google.protobuf:protobuf-javalite:3.19.1'
6280
}

Diff for: demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,14 @@ public final class ConfigurationActivity extends AppCompatActivity {
9292
"MP4 with HDR (HDR10) H265 video (encoding may fail)",
9393
};
9494
private static final String[] DEMO_EFFECTS = {
95-
"Dizzy crop", "Periodic vignette", "3D spin", "Overlay logo & timer", "Zoom in start"
95+
"Dizzy crop",
96+
"Edge detector (Media Pipe)",
97+
"Periodic vignette",
98+
"3D spin",
99+
"Overlay logo & timer",
100+
"Zoom in start",
96101
};
97-
private static final int PERIODIC_VIGNETTE_INDEX = 1;
102+
private static final int PERIODIC_VIGNETTE_INDEX = 2;
98103
private static final String SAME_AS_INPUT_OPTION = "same as input";
99104
private static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2);
100105

Diff for: demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java

+33-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import android.widget.TextView;
3232
import android.widget.Toast;
3333
import androidx.annotation.Nullable;
34+
import androidx.annotation.StringRes;
3435
import androidx.appcompat.app.AppCompatActivity;
3536
import androidx.media3.common.C;
3637
import androidx.media3.common.MediaItem;
@@ -41,6 +42,7 @@
4142
import androidx.media3.transformer.DefaultEncoderFactory;
4243
import androidx.media3.transformer.EncoderSelector;
4344
import androidx.media3.transformer.GlEffect;
45+
import androidx.media3.transformer.GlFrameProcessor;
4446
import androidx.media3.transformer.ProgressHolder;
4547
import androidx.media3.transformer.TransformationException;
4648
import androidx.media3.transformer.TransformationRequest;
@@ -54,6 +56,7 @@
5456
import com.google.common.collect.ImmutableList;
5557
import java.io.File;
5658
import java.io.IOException;
59+
import java.lang.reflect.Constructor;
5760
import java.util.concurrent.CountDownLatch;
5861
import java.util.concurrent.TimeUnit;
5962
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -249,6 +252,29 @@ private Transformer createTransformer(@Nullable Bundle bundle, String filePath)
249252
effects.add(MatrixTransformationFactory.createDizzyCropEffect());
250253
}
251254
if (selectedEffects[1]) {
255+
try {
256+
Class<?> clazz =
257+
Class.forName("androidx.media3.demo.transformer.MediaPipeFrameProcessor");
258+
Constructor<?> constructor =
259+
clazz.getConstructor(String.class, String.class, String.class);
260+
effects.add(
261+
() -> {
262+
try {
263+
return (GlFrameProcessor)
264+
constructor.newInstance(
265+
/* graphName= */ "edge_detector_mediapipe_graph.binarypb",
266+
/* inputStreamName= */ "input_video",
267+
/* outputStreamName= */ "output_video");
268+
} catch (Exception e) {
269+
showToast(R.string.no_media_pipe_error);
270+
throw new RuntimeException("Failed to load MediaPipe processor", e);
271+
}
272+
});
273+
} catch (Exception e) {
274+
showToast(R.string.no_media_pipe_error);
275+
}
276+
}
277+
if (selectedEffects[2]) {
252278
effects.add(
253279
() ->
254280
new PeriodicVignetteFrameProcessor(
@@ -260,13 +286,13 @@ private Transformer createTransformer(@Nullable Bundle bundle, String filePath)
260286
ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS),
261287
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS)));
262288
}
263-
if (selectedEffects[2]) {
289+
if (selectedEffects[3]) {
264290
effects.add(MatrixTransformationFactory.createSpin3dEffect());
265291
}
266-
if (selectedEffects[3]) {
292+
if (selectedEffects[4]) {
267293
effects.add(BitmapOverlayFrameProcessor::new);
268294
}
269-
if (selectedEffects[4]) {
295+
if (selectedEffects[5]) {
270296
effects.add(MatrixTransformationFactory.createZoomInTransition());
271297
}
272298
transformerBuilder.setVideoFrameEffects(effects.build());
@@ -363,6 +389,10 @@ private void requestTransformerPermission() {
363389
}
364390
}
365391

392+
private void showToast(@StringRes int messageResource) {
393+
Toast.makeText(getApplicationContext(), getString(messageResource), Toast.LENGTH_LONG).show();
394+
}
395+
366396
private final class DemoDebugViewProvider implements Transformer.DebugViewProvider {
367397

368398
@Nullable

Diff for: demos/transformer/src/main/res/values/strings.xml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string>
3232
<string name="select_demo_effects" translatable="false">Add demo effects</string>
3333
<string name="periodic_vignette_options" translatable="false">Periodic vignette options</string>
34+
<string name="no_media_pipe_error" translatable="false">Failed to load MediaPipe processor. Check the README for instructions.</string>
3435
<string name="transform" translatable="false">Transform</string>
3536
<string name="debug_preview" translatable="false">Debug preview:</string>
3637
<string name="debug_preview_not_available" translatable="false">No debug preview available.</string>
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<manifest package="androidx.media3.demo.transformer">
18+
<uses-sdk />
19+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Demo MediaPipe graph that shows edges using a SobelEdgesCalculator.
2+
input_stream: "input_video"
3+
output_stream: "output_video"
4+
node: {
5+
calculator: "LuminanceCalculator"
6+
input_stream: "input_video"
7+
output_stream: "luma_video"
8+
}
9+
node: {
10+
calculator: "SobelEdgesCalculator"
11+
input_stream: "luma_video"
12+
output_stream: "output_video"
13+
}

0 commit comments

Comments
 (0)