diff --git a/android/build.gradle b/android/build.gradle index a17296a177..ba30405656 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -164,6 +164,8 @@ dependencies { implementation "androidx.media3:media3-exoplayer-dash:$media3_version" // For HLS playback support with ExoPlayer implementation "androidx.media3:media3-exoplayer-hls:$media3_version" + + implementation "androidx.media3:media3-exoplayer-rtsp:$media3_version" // For ad insertion using the Interactive Media Ads SDK with ExoPlayer if (useExoplayerIMA) { implementation "androidx.media3:media3-exoplayer-ima:$media3_version" diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index fef6d2479a..e88064aeb2 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -3,6 +3,7 @@ import static androidx.media3.common.C.CONTENT_TYPE_DASH; import static androidx.media3.common.C.CONTENT_TYPE_HLS; import static androidx.media3.common.C.CONTENT_TYPE_OTHER; +import static androidx.media3.common.C.CONTENT_TYPE_RTSP; import static androidx.media3.common.C.CONTENT_TYPE_SS; import static androidx.media3.common.C.TIME_END_OF_SOURCE; @@ -67,6 +68,7 @@ import androidx.media3.exoplayer.ima.ImaAdsLoader; import androidx.media3.exoplayer.mediacodec.MediaCodecInfo; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil; +import androidx.media3.exoplayer.rtsp.RtspMediaSource; import androidx.media3.exoplayer.smoothstreaming.DefaultSsChunkSource; import androidx.media3.exoplayer.smoothstreaming.SsMediaSource; import androidx.media3.exoplayer.source.ClippingMediaSource; @@ -783,8 +785,13 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi if (uri == null) { throw new IllegalStateException("Invalid video uri"); } - int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension - : uri.getLastPathSegment()); + int type; + if ("rtsp".equals(overrideExtension)) { + type = C.TYPE_RTSP; + } else { + type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension + : uri.getLastPathSegment()); + } config.setDisableDisconnectError(this.disableDisconnectError); MediaItem.Builder mediaItemBuilder = new MediaItem.Builder().setUri(uri); @@ -827,6 +834,9 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi mediaDataSourceFactory ); break; + case CONTENT_TYPE_RTSP: + mediaSourceFactory = new RtspMediaSource.Factory(); + break; default: { throw new IllegalStateException("Unsupported type: " + type); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 6a0b198f4e..2b8f1f506b 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -439,6 +439,7 @@ private boolean startsWithValidScheme(String uriString) { || lowerCaseUri.startsWith("https://") || lowerCaseUri.startsWith("content://") || lowerCaseUri.startsWith("file://") + || lowerCaseUri.startsWith("rtsp://") || lowerCaseUri.startsWith("asset://"); } } diff --git a/examples/basic/android/app/build.gradle b/examples/basic/android/app/build.gradle index f173269a45..9bb1f1f671 100644 --- a/examples/basic/android/app/build.gradle +++ b/examples/basic/android/app/build.gradle @@ -104,6 +104,18 @@ android { configurations.all { resolutionStrategy { force 'androidx.core:core:1.9.0' } } + + packagingOptions { + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/LICENSE' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/license.txt' + exclude 'META-INF/NOTICE' + exclude 'META-INF/NOTICE.txt' + exclude 'META-INF/notice.txt' + exclude 'META-INF/ASL2.0' + exclude("META-INF/*.kotlin_module") + } } diff --git a/examples/basic/android/gradle.properties b/examples/basic/android/gradle.properties index a3b2fa1249..fa87402f7c 100644 --- a/examples/basic/android/gradle.properties +++ b/examples/basic/android/gradle.properties @@ -9,8 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m +# Default value: -Xmx10248m -XX:MaxPermSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/src/Video.tsx b/src/Video.tsx index 41d2aa6497..8e60922f91 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -131,7 +131,7 @@ const Video = forwardRef( if (!uri) { console.log('Trying to load empty source'); } - const isNetwork = !!(uri && uri.match(/^https?:/)); + const isNetwork = !!(uri && uri.match(/^(rtp|rtsp|http|https):/)); const isAsset = !!( uri && uri.match(