Add CompositionPlayer to the Transformer demo app
PiperOrigin-RevId: 571284291
This commit is contained in:
parent
f8d2e362a5
commit
d9cf350eb0
@ -77,6 +77,7 @@ dependencies {
|
||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||
implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion
|
||||
implementation 'androidx.constraintlayout:constraintlayout:' + androidxConstraintLayoutVersion
|
||||
implementation 'androidx.recyclerview:recyclerview:' + androidxRecyclerViewVersion
|
||||
implementation 'androidx.multidex:multidex:' + androidxMultidexVersion
|
||||
implementation 'com.google.android.material:material:' + androidxMaterialVersion
|
||||
implementation project(modulePrefix + 'lib-effect')
|
||||
|
@ -64,5 +64,11 @@
|
||||
android:label="@string/app_name"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar"/>
|
||||
<activity android:name=".CompositionPreviewActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTop"
|
||||
android:label="@string/app_name"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
package androidx.media3.demo.transformer;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link RecyclerView.Adapter} that displays assets in a sequence in a {@link RecyclerView}. */
|
||||
public final class AssetItemAdapter extends RecyclerView.Adapter<AssetItemAdapter.ViewHolder> {
|
||||
private static final String TAG = "AssetItemAdapter";
|
||||
|
||||
private final List<String> dataSet;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param data A list of items to populate RecyclerView with.
|
||||
*/
|
||||
public AssetItemAdapter(List<String> data) {
|
||||
this.dataSet = new ArrayList<>(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.preset_item, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.getTextView().setText(dataSet.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dataSet.size();
|
||||
}
|
||||
|
||||
/** A {@link RecyclerView.ViewHolder} used to build {@link AssetItemAdapter}. */
|
||||
public static final class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView textView;
|
||||
|
||||
private ViewHolder(View view) {
|
||||
super(view);
|
||||
textView = view.findViewById(R.id.preset_name_text);
|
||||
}
|
||||
|
||||
private TextView getTextView() {
|
||||
return textView;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
package androidx.media3.demo.transformer;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.transformer.Composition;
|
||||
import androidx.media3.transformer.CompositionPlayer;
|
||||
import androidx.media3.transformer.EditedMediaItem;
|
||||
import androidx.media3.transformer.EditedMediaItemSequence;
|
||||
import androidx.media3.ui.PlayerView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* An {@link Activity} that previews compositions, using {@link
|
||||
* androidx.media3.transformer.CompositionPlayer}.
|
||||
*/
|
||||
public final class CompositionPreviewActivity extends AppCompatActivity {
|
||||
private static final String TAG = "CompPreviewActivity";
|
||||
private static final ImmutableList<Integer> SEQUENCE_FILE_INDICES = ImmutableList.of(0, 2);
|
||||
|
||||
private @MonotonicNonNull PlayerView playerView;
|
||||
private @MonotonicNonNull RecyclerView presetList;
|
||||
private @MonotonicNonNull AppCompatButton previewButton;
|
||||
|
||||
@Nullable private CompositionPlayer compositionPlayer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.composition_preview_activity);
|
||||
|
||||
String[] presetFileURIs = getResources().getStringArray(R.array.preset_uris);
|
||||
String[] presetFileDescriptions = getResources().getStringArray(R.array.preset_descriptions);
|
||||
|
||||
playerView = findViewById(R.id.composition_player_view);
|
||||
presetList = findViewById(R.id.composition_preset_list);
|
||||
previewButton = findViewById(R.id.preview_button);
|
||||
previewButton.setOnClickListener(view -> previewComposition(view, presetFileURIs));
|
||||
|
||||
LinearLayoutManager layoutManager =
|
||||
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, /* reverseLayout= */ false);
|
||||
presetList.setLayoutManager(layoutManager);
|
||||
ArrayList<String> sequenceFiles = new ArrayList<>();
|
||||
for (int i = 0; i < SEQUENCE_FILE_INDICES.size(); i++) {
|
||||
if (SEQUENCE_FILE_INDICES.get(i) < presetFileDescriptions.length) {
|
||||
sequenceFiles.add(presetFileDescriptions[SEQUENCE_FILE_INDICES.get(i)]);
|
||||
}
|
||||
}
|
||||
AssetItemAdapter adapter = new AssetItemAdapter(sequenceFiles);
|
||||
presetList.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
checkStateNotNull(playerView).onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
checkStateNotNull(playerView).onPause();
|
||||
releasePlayer();
|
||||
}
|
||||
|
||||
private Composition prepareComposition(String[] presetFileURIs) {
|
||||
List<EditedMediaItem> mediaItems = new ArrayList<>();
|
||||
for (int i = 0; i < SEQUENCE_FILE_INDICES.size(); i++) {
|
||||
mediaItems.add(
|
||||
new EditedMediaItem.Builder(
|
||||
MediaItem.fromUri(presetFileURIs[SEQUENCE_FILE_INDICES.get(i)]))
|
||||
.build());
|
||||
}
|
||||
EditedMediaItemSequence videoSequence =
|
||||
new EditedMediaItemSequence(Collections.unmodifiableList(mediaItems));
|
||||
return new Composition.Builder(ImmutableList.of(videoSequence)).build();
|
||||
}
|
||||
|
||||
private void previewComposition(View view, String[] presetFileURIs) {
|
||||
releasePlayer();
|
||||
Composition composition = prepareComposition(presetFileURIs);
|
||||
checkStateNotNull(playerView).setPlayer(null);
|
||||
|
||||
CompositionPlayer player = new CompositionPlayer(getApplicationContext(), /* looper= */ null);
|
||||
this.compositionPlayer = player;
|
||||
checkStateNotNull(playerView).setPlayer(compositionPlayer);
|
||||
checkStateNotNull(playerView).setControllerAutoShow(false);
|
||||
player.setComposition(composition);
|
||||
player.prepare();
|
||||
player.play();
|
||||
}
|
||||
|
||||
private void releasePlayer() {
|
||||
if (compositionPlayer != null) {
|
||||
compositionPlayer.release();
|
||||
compositionPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2023 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.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/composition_preview_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/input_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:text="@string/preview_single_sequence" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp" >
|
||||
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/composition_player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/sequence_header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/single_sequence_items"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/composition_preview_card_view"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/composition_preset_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sequence_header_text"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/preview_button"
|
||||
android:text="@string/preview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
30
demos/transformer/src/main/res/layout/preset_item.xml
Normal file
30
demos/transformer/src/main/res/layout/preset_item.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2023 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.
|
||||
-->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/preset_name_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2021 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
56
demos/transformer/src/main/res/values/arrays.xml
Normal file
56
demos/transformer/src/main/res/values/arrays.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2023 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.
|
||||
-->
|
||||
<resources>
|
||||
<string-array name="preset_descriptions">
|
||||
<item>720p H264 video and AAC audio</item>
|
||||
<item>1080p H265 video and AAC audio</item>
|
||||
<item>360p H264 video and AAC audio</item>
|
||||
<item>360p VP8 video and Vorbis audio</item>
|
||||
<item>4K H264 video and AAC audio (portrait, no B-frames)</item>
|
||||
<item>8k H265 video and AAC audio</item>
|
||||
<item>Short 1080p H265 video and AAC audio</item>
|
||||
<item>Long 180p H264 video and AAC audio</item>
|
||||
<item>H264 video and AAC audio (portrait, H > W, 0°)</item>
|
||||
<item>H264 video and AAC audio (portrait, H < W, 90°)</item>
|
||||
<item>London JPG image (Plays for 5secs at 30fps)</item>
|
||||
<item>Tokyo JPG image (Portrait, Plays for 5secs at 30fps)</item>
|
||||
<item>SEF slow motion with 240 fps</item>
|
||||
<item>480p DASH (non-square pixels)</item>
|
||||
<item>HDR (HDR10) H265 limited range video (encoding may fail)</item>
|
||||
<item>HDR (HLG) H265 limited range video (encoding may fail)</item>
|
||||
<item>720p H264 video with no audio</item>
|
||||
</string-array>
|
||||
<string-array name="preset_uris">
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4</item>
|
||||
<item>https://html5demos.com/assets/dizzy.mp4</item>
|
||||
<item>https://html5demos.com/assets/dizzy.webm</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/portrait_4k60.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/8k24fps_4s.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/1920w_1080h_4s.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/portrait_avc_aac.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/portrait_rotated_avc_aac.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/jpg/london.jpg</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/jpg/tokyo.jpg</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/slow-motion/slowMotion_stopwatch_240fps_long.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/gen/screens/dash-vod-single-segment/manifest-baseline.mpd</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/Pixel7Pro_HLG_1080P.mp4</item>
|
||||
<item>https://storage.googleapis.com/exoplayer-test-media-1/mp4/sample_video_track_only.mp4</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -80,4 +80,7 @@
|
||||
<string name="overlay_text">Text</string>
|
||||
<string name="overlay_text_color">Text color</string>
|
||||
<string name="text_overlay_settings">Specify text overlay settings</string>
|
||||
<string name="preview" translatable="false">Preview</string>
|
||||
<string name="preview_single_sequence" translatable="false">Single sequence preview</string>
|
||||
<string name="single_sequence_items" translatable="false">Single sequence items:</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user