diff --git a/media3-migration.sh b/media3-migration.sh new file mode 100644 index 00000000000..01a7df93bab --- /dev/null +++ b/media3-migration.sh @@ -0,0 +1,386 @@ +#!/bin/bash +# Copyright (C) 2022 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. +## +shopt -s extglob + +PACKAGE_MAPPINGS='com.google.android.exoplayer2 com.google.android.exoplayer2 +com.google.android.exoplayer2.analytics com.google.android.exoplayer2.analytics +com.google.android.exoplayer2.audio com.google.android.exoplayer2.audio +com.google.android.exoplayer2.castdemo com.google.android.exoplayer2.castdemo +com.google.android.exoplayer2.database com.google.android.exoplayer2.database +com.google.android.exoplayer2.decoder com.google.android.exoplayer2.decoder +com.google.android.exoplayer2.demo com.google.android.exoplayer2.demo +com.google.android.exoplayer2.drm com.google.android.exoplayer2.drm +com.google.android.exoplayer2.ext.av1 com.google.android.exoplayer2.ext.av1 +com.google.android.exoplayer2.ext.cast com.google.android.exoplayer2.ext.cast +com.google.android.exoplayer2.ext.cronet com.google.android.exoplayer2.ext.cronet +com.google.android.exoplayer2.ext.ffmpeg com.google.android.exoplayer2.ext.ffmpeg +com.google.android.exoplayer2.ext.flac com.google.android.exoplayer2.ext.flac +com.google.android.exoplayer2.ext.ima com.google.android.exoplayer2.ext.ima +com.google.android.exoplayer2.ext.leanback com.google.android.exoplayer2.ext.leanback +com.google.android.exoplayer2.ext.okhttp com.google.android.exoplayer2.ext.okhttp +com.google.android.exoplayer2.ext.opus com.google.android.exoplayer2.ext.opus +com.google.android.exoplayer2.ext.rtmp com.google.android.exoplayer2.ext.rtmp +com.google.android.exoplayer2.ext.vp9 com.google.android.exoplayer2.ext.vp9 +com.google.android.exoplayer2.ext.workmanager com.google.android.exoplayer2.ext.workmanager +com.google.android.exoplayer2.extractor com.google.android.exoplayer2.extractor +com.google.android.exoplayer2.gldemo com.google.android.exoplayer2.gldemo +com.google.android.exoplayer2.mediacodec com.google.android.exoplayer2.mediacodec +com.google.android.exoplayer2.metadata com.google.android.exoplayer2.metadata +com.google.android.exoplayer2.offline com.google.android.exoplayer2.offline +com.google.android.exoplayer2.playbacktests com.google.android.exoplayer2.playbacktests +com.google.android.exoplayer2.robolectric com.google.android.exoplayer2.robolectric +com.google.android.exoplayer2.scheduler com.google.android.exoplayer2.scheduler +com.google.android.exoplayer2.source com.google.android.exoplayer2.source +com.google.android.exoplayer2.source.dash com.google.android.exoplayer2.source.dash +com.google.android.exoplayer2.source.hls com.google.android.exoplayer2.source.hls +com.google.android.exoplayer2.source.rtsp com.google.android.exoplayer2.source.rtsp +com.google.android.exoplayer2.source.smoothstreaming com.google.android.exoplayer2.source.smoothstreaming +com.google.android.exoplayer2.surfacedemo com.google.android.exoplayer2.surfacedemo +com.google.android.exoplayer2.testdata com.google.android.exoplayer2.testdata +com.google.android.exoplayer2.testutil com.google.android.exoplayer2.testutil +com.google.android.exoplayer2.text com.google.android.exoplayer2.text +com.google.android.exoplayer2.trackselection com.google.android.exoplayer2.trackselection +com.google.android.exoplayer2.transformer com.google.android.exoplayer2.transformer +com.google.android.exoplayer2.transformerdemo com.google.android.exoplayer2.transformerdemo +com.google.android.exoplayer2.ui com.google.android.exoplayer2.ui +com.google.android.exoplayer2.upstream com.google.android.exoplayer2.upstream +com.google.android.exoplayer2.upstream.cache com.google.android.exoplayer2.upstream.cache +com.google.android.exoplayer2.upstream.crypto com.google.android.exoplayer2.upstream.crypto +com.google.android.exoplayer2.util com.google.android.exoplayer2.util +com.google.android.exoplayer2.util com.google.android.exoplayer2.util +com.google.android.exoplayer2.video com.google.android.exoplayer2.video' + + +CLASS_RENAMINGS='com.google.android.exoplayer2.ui.StyledPlayerView com.google.android.exoplayer2.ui.PlayerView +StyledPlayerView PlayerView +com.google.android.exoplayer2.ui.StyledPlayerControlView com.google.android.exoplayer2.ui.PlayerControlView +StyledPlayerControlView PlayerControlView +com.google.android.exoplayer2.ExoPlayerLibraryInfo androidx.media3.common.MediaLibraryInfo +ExoPlayerLibraryInfo MediaLibraryInfo +com.google.android.exoplayer2.SimpleExoPlayer com.google.android.exoplayer2.ExoPlayer +SimpleExoPlayer ExoPlayer' + +CLASS_MAPPINGS='com.google.android.exoplayer2.text.span androidx.media3.common.text HorizontalTextInVerticalContextSpan LanguageFeatureSpan RubySpan SpanUtil TextAnnotation TextEmphasisSpan +com.google.android.exoplayer2.text androidx.media3.common.text CueGroup Cue +com.google.android.exoplayer2.text com.google.android.exoplayer2.text ExoplayerCuesDecoder SubtitleDecoderFactory TextOutput TextRenderer +com.google.android.exoplayer2.upstream.crypto com.google.android.exoplayer2.upstream AesCipherDataSource AesCipherDataSink AesFlushingCipher +com.google.android.exoplayer2.util com.google.android.exoplayer2.util AtomicFile Assertions BundleableUtil BundleUtil Clock ClosedSource CodecSpecificDataUtil ColorParser ConditionVariable Consumer CopyOnWriteMultiset EGLSurfaceTexture GlProgram GlUtil HandlerWrapper LibraryLoader ListenerSet Log LongArray MediaFormatUtil NetworkTypeObserver NonNullApi NotificationUtil ParsableBitArray ParsableByteArray RepeatModeUtil RunnableFutureTask SystemClock SystemHandlerWrapper TimedValueQueue TimestampAdjuster TraceUtil UnknownNull UnstableApi UriUtil Util XmlPullParserUtil +com.google.android.exoplayer2.util androidx.media3.common ErrorMessageProvider FlagSet FileTypes MimeTypes PriorityTaskManager +com.google.android.exoplayer2.metadata androidx.media3.common Metadata +com.google.android.exoplayer2.metadata com.google.android.exoplayer2.metadata MetadataDecoderFactory MetadataOutput MetadataRenderer +com.google.android.exoplayer2.audio androidx.media3.common AudioAttributes AuxEffectInfo +com.google.android.exoplayer2.ui androidx.media3.common AdOverlayInfo AdViewProvider +com.google.android.exoplayer2.source.ads androidx.media3.common AdPlaybackState +com.google.android.exoplayer2.source androidx.media3.common MediaPeriodId TrackGroup +com.google.android.exoplayer2.offline androidx.media3.common StreamKey +com.google.android.exoplayer2.ui com.google.android.exoplayer2.offline DownloadNotificationHelper +com.google.android.exoplayer2.trackselection androidx.media3.common TrackSelectionParameters TrackSelectionOverride +com.google.android.exoplayer2.video androidx.media3.common ColorInfo VideoSize +com.google.android.exoplayer2.upstream androidx.media3.common DataReader +com.google.android.exoplayer2.upstream com.google.android.exoplayer2.upstream Allocation Allocator BandwidthMeter CachedRegionTracker DefaultAllocator DefaultBandwidthMeter DefaultLoadErrorHandlingPolicy Loader LoaderErrorThrower ParsingLoadable SlidingPercentile TimeToFirstByteEstimator +com.google.android.exoplayer2.audio com.google.android.exoplayer2.extractor AacUtil Ac3Util Ac4Util DtsUtil MpegAudioUtil OpusUtil WavUtil +com.google.android.exoplayer2.util com.google.android.exoplayer2.extractor NalUnitUtil ParsableNalUnitBitArray +com.google.android.exoplayer2.video com.google.android.exoplayer2.extractor AvcConfig DolbyVisionConfig HevcConfig +com.google.android.exoplayer2.decoder com.google.android.exoplayer2 DecoderCounters DecoderReuseEvaluation +com.google.android.exoplayer2.util com.google.android.exoplayer2 MediaClock StandaloneMediaClock +com.google.android.exoplayer2 com.google.android.exoplayer2 FormatHolder PlayerMessage +com.google.android.exoplayer2 androidx.media3.common BasePlayer BundleListRetriever Bundleable ControlDispatcher C DefaultControlDispatcher DeviceInfo ErrorMessageProvider ExoPlayerLibraryInfo Format ForwardingPlayer HeartRating IllegalSeekPositionException MediaItem MediaMetadata ParserException PercentageRating PlaybackException PlaybackParameters Player PositionInfo Rating StarRating ThumbRating Timeline Tracks +com.google.android.exoplayer2.drm androidx.media3.common DrmInitData' + +DEPENDENCY_MAPPINGS='exoplayer media3-exoplayer +exoplayer-common media3-common +exoplayer-core media3-exoplayer +exoplayer-dash media3-exoplayer-dash +exoplayer-database media3-database +exoplayer-datasource media-datasource +exoplayer-decoder media3-decoder +exoplayer-extractor media3-extractor +exoplayer-hls media3-exoplayer-hls +exoplayer-robolectricutils media3-test-utils-robolectric +exoplayer-rtsp media3-exoplayer-rtsp +exoplayer-smoothstreaming media3-exoplayer-smoothstreaming +exoplayer-testutils media3-test-utils +exoplayer-transformer media3-transformer +exoplayer-ui media3-ui +extension-cast media3-cast +extension-cronet media3-datasource-cronet +extension-ima media3-exoplayer-ima +extension-leanback media3-ui-leanback +extension-okhttp media3-datasource-okhttp +extension-rtmp media3-datasource-rtmp +extension-workmanager media3-exoplayer-workmanager' + +# Rewrites classes, packages and dependencies from the legacy ExoPlayer package structure +# to androidx.media3 structure. + +MEDIA3_VERSION="1.0.0-beta02" +LEGACY_PEER_VERSION="2.18.1" + +function usage() { + echo "usage: $0 [-p|-c|-d|-v]|[-m|-l [-x ] [-f] PROJECT_ROOT]" + echo " PROJECT_ROOT: path to your project root (location of 'gradlew')" + echo " -p: list package mappings and then exit" + echo " -c: list class mappings (precedence over package mappings) and then exit" + echo " -d: list dependency mappings and then exit" + echo " -m: migrate packages, classes and dependencies to AndroidX Media3" + echo " -l: list files that will be considered for rewrite and then exit" + echo " -x: exclude the path from the list of file to be changed: 'app/src/test'" + echo " -f: force the action even when validation fails" + echo " -v: print the exoplayer2/media3 version strings of this script and exit" + echo " --noclean : Do not call './gradlew clean' in project directory." + echo " -h, --help: show this help text" +} + +function print_pairs { + while read -r line; + do + IFS=' ' read -ra PAIR <<< "$line" + printf "%-55s %-30s\n" "${PAIR[0]}" "${PAIR[1]}" + done <<< "$(echo "$@")" +} + +function print_class_mappings { + while read -r mapping; + do + old=$(echo "$mapping" | cut -d ' ' -f1) + new=$(echo "$mapping" | cut -d ' ' -f2) + classes=$(echo "$mapping" | cut -d ' ' -f3-) + for clazz in $classes; + do + printf "%-80s %-30s\n" "$old.$clazz" "$new.$clazz" + done + done <<< "$(echo "$CLASS_MAPPINGS" | sort)" +} + +ERROR_COUNTER=0 +VALIDATION_ERRORS='' + +function add_validation_error { + let ERROR_COUNTER++ + VALIDATION_ERRORS+="\033[31m[$ERROR_COUNTER] ->\033[0m ${1}" +} + +function validate_exoplayer_version() { + has_exoplayer_dependency='' + while read -r file; + do + local version + version=$(grep -m 1 "com\.google\.android\.exoplayer:" "$file" | cut -d ":" -f3 | tr -d \" | tr -d \') + if [[ ! -z $version ]] && [[ ! "$version" =~ $LEGACY_PEER_VERSION ]]; + then + add_validation_error "The version does not match '$LEGACY_PEER_VERSION'. \ +Update to '$LEGACY_PEER_VERSION' or use the migration script matching your \ +current version. Current version '$version' found in\n $file\n" + fi + done <<< "$(find . -type f -name "build.gradle")" +} + +function validate_string_not_contained { + local pattern=$1 # regex + local failure_message=$2 + while read -r file; + do + if grep -q -e "$pattern" "$file"; + then + add_validation_error "$failure_message:\n $file\n" + fi + done <<< "$files" +} + +function validate_string_patterns { + validate_string_not_contained \ + 'com\.google\.android\.exoplayer2\..*\*' \ + 'Replace wildcard import statements with fully qualified import statements'; + validate_string_not_contained \ + 'com\.google\.android\.exoplayer2\.ui\.PlayerView' \ + 'Migrate PlayerView to StyledPlayerView before migrating'; + validate_string_not_contained \ + 'LegacyPlayerView' \ + 'Migrate LegacyPlayerView to StyledPlayerView before migrating'; + validate_string_not_contained \ + 'com\.google\.android\.exoplayer2\.ext\.mediasession' \ + 'The MediaSessionConnector is integrated in androidx.media3.session.MediaSession' +} + +SED_CMD_INPLACE='sed -i ' +if [[ "$OSTYPE" == "darwin"* ]]; then + SED_CMD_INPLACE="sed -i '' " +fi + +MIGRATE_FILES='1' +LIST_FILES_ONLY='1' +PRINT_CLASS_MAPPINGS='1' +PRINT_PACKAGE_MAPPINGS='1' +PRINT_DEPENDENCY_MAPPINGS='1' +PRINT_VERSION='1' +NO_CLEAN='1' +FORCE='1' +IGNORE_VERSION='1' +EXCLUDED_PATHS='' + +while [[ $1 =~ ^-.* ]]; +do + case "$1" in + -m ) MIGRATE_FILES='';; + -l ) LIST_FILES_ONLY='';; + -c ) PRINT_CLASS_MAPPINGS='';; + -p ) PRINT_PACKAGE_MAPPINGS='';; + -d ) PRINT_DEPENDENCY_MAPPINGS='';; + -v ) PRINT_VERSION='';; + -f ) FORCE='';; + -x ) shift; EXCLUDED_PATHS="$(printf "%s\n%s" $EXCLUDED_PATHS $1)";; + --noclean ) NO_CLEAN='';; + * ) usage && exit 1;; + esac + shift +done + +if [[ -z $PRINT_DEPENDENCY_MAPPINGS ]]; +then + print_pairs "$DEPENDENCY_MAPPINGS" + exit 0 +elif [[ -z $PRINT_PACKAGE_MAPPINGS ]]; +then + print_pairs "$PACKAGE_MAPPINGS" + exit 0 +elif [[ -z $PRINT_CLASS_MAPPINGS ]]; +then + print_class_mappings + exit 0 +elif [[ -z $PRINT_VERSION ]]; +then + echo "$LEGACY_PEER_VERSION -> $MEDIA3_VERSION. This script is written to migrate from ExoPlayer $LEGACY_PEER_VERSION to AndroidX Media3 $MEDIA3_VERSION" + exit 0 +elif [[ -z $1 ]]; +then + usage + exit 1 +fi + +if [[ ! -f $1/gradlew ]]; +then + echo "directory seems not to exist or is not a gradle project (missing 'gradlew')" + usage + exit 1 +fi + +PROJECT_ROOT=$1 +cd "$PROJECT_ROOT" + +# Create the set of files to transform +exclusion="/build/|/.idea/|/res/drawable|/res/color|/res/mipmap|/res/values|" +if [[ ! -z $EXCLUDED_PATHS ]]; +then + while read -r path; + do + exclusion="$exclusion./$path|" + done <<< "$EXCLUDED_PATHS" +fi +files=$(find . -name '*\.java' -o -name '*\.kt' -o -name '*\.xml' | grep -Ev "'$exclusion'") + +# Validate project and exit in case of validation errors +validate_string_patterns +validate_exoplayer_version "$PROJECT_ROOT" +if [[ ! -z $FORCE && ! -z "$VALIDATION_ERRORS" ]]; +then + echo "=============================================" + echo "Validation errors (use -f to force execution)" + echo "---------------------------------------------" + echo -e "$VALIDATION_ERRORS" + exit 1 +fi + +if [[ -z $LIST_FILES_ONLY ]]; +then + echo "$files" | cut -c 3- + find . -type f -name 'build\.gradle' | cut -c 3- + exit 0 +fi + +# start migration after successful validation or when forced to disregard validation +# errors + +if [[ ! -z "$MIGRATE_FILES" ]]; +then + echo "nothing to do" + usage + exit 0 +fi + +PWD=$(pwd) +if [[ ! -z $NO_CLEAN ]]; +then + cd "$PROJECT_ROOT" + ./gradlew clean + cd "$PWD" +fi + +# create expressions for class renamings +renaming_expressions='' +while read -r renaming; +do + src=$(echo "$renaming" | cut -d ' ' -f1 | sed -e 's/\./\\\./g') + dest=$(echo "$renaming" | cut -d ' ' -f2) + renaming_expressions+="-e s/$src/$dest/g " +done <<< "$CLASS_RENAMINGS" + +# create expressions for class mappings +classes_expressions='' +while read -r mapping; +do + src=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g') + dest=$(echo "$mapping" | cut -d ' ' -f2) + classes=$(echo "$mapping" | cut -d ' ' -f3-) + for clazz in $classes; + do + classes_expressions+="-e s/$src\.$clazz/$dest.$clazz/g " + done +done <<< "$CLASS_MAPPINGS" + +# create expressions for package mappings +packages_expressions='' +while read -r mapping; +do + src=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g') + dest=$(echo "$mapping" | cut -d ' ' -f2) + packages_expressions+="-e s/$src/$dest/g " +done <<< "$PACKAGE_MAPPINGS" + +# do search and replace with expressions in each selected file +while read -r file; +do + echo "migrating $file" + expr="$renaming_expressions $classes_expressions $packages_expressions" + $SED_CMD_INPLACE $expr $file +done <<< "$files" + +# create expressions for dependencies in gradle files +EXOPLAYER_GROUP="com\.google\.android\.exoplayer" +MEDIA3_GROUP="androidx.media3" +dependency_expressions="" +while read -r mapping +do + OLD=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g') + NEW=$(echo "$mapping" | cut -d ' ' -f2) + dependency_expressions="$dependency_expressions -e s/$EXOPLAYER_GROUP:$OLD:.*\"/$MEDIA3_GROUP:$NEW:$MEDIA3_VERSION\"/g -e s/$EXOPLAYER_GROUP:$OLD:.*'/$MEDIA3_GROUP:$NEW:$MEDIA3_VERSION'/" +done <<< "$DEPENDENCY_MAPPINGS" + +## do search and replace for dependencies in gradle files +while read -r build_file; +do + echo "migrating build file $build_file" + $SED_CMD_INPLACE $dependency_expressions $build_file +done <<< "$(find . -type f -name 'build\.gradle')"