Commit 75242281 by Oliver Woodman

Clean up rtmp extension

parent 1279b7dc
......@@ -31,6 +31,7 @@ include modulePrefix + 'extension-ima'
include modulePrefix + 'extension-okhttp'
include modulePrefix + 'extension-opus'
include modulePrefix + 'extension-vp9'
include modulePrefix + 'extension-rtmp'
project(modulePrefix + 'library').projectDir = new File(rootDir, 'library/all')
project(modulePrefix + 'library-core').projectDir = new File(rootDir, 'library/core')
......@@ -46,6 +47,7 @@ project(modulePrefix + 'extension-ima').projectDir = new File(rootDir, 'extensio
project(modulePrefix + 'extension-okhttp').projectDir = new File(rootDir, 'extensions/okhttp')
project(modulePrefix + 'extension-opus').projectDir = new File(rootDir, 'extensions/opus')
project(modulePrefix + 'extension-vp9').projectDir = new File(rootDir, 'extensions/vp9')
project(modulePrefix + 'extension-rtmp').projectDir = new File(rootDir, 'extensions/rtmp')
if (gradle.ext.has('exoplayerIncludeCronetExtension')
&& gradle.ext.exoplayerIncludeCronetExtension) {
......
......@@ -2,33 +2,26 @@
## Description ##
The RTMP Extension is an [DataSource][] implementation for playing [RTMP][] streaming using
[Librtmp Client for Android].
The RTMP Extension is a [DataSource][] implementation for playing [RTMP][]
streams using [LibRtmp Client for Android][].
## Using the extension ##
When building [MediaSource][], inject `RtmpDataSourceFactory` like this:
[DataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/DataSource.html
[RTMP]: https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol
[LibRtmp Client for Android]: https://github.com/ant-media/LibRtmp-Client-for-Android
```java
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri)
: Util.inferContentType("." + overrideExtension);
switch (type) {
## Using the extension ##
// ... other types cases
The easiest way to use the extension is to add it as a gradle dependency:
case C.TYPE_OTHER:
DataSource.Factory factory = uri.getScheme().equals("rtmp") ? new RtmpDataSourceFactory() : mediaDataSourceFactory;
return new ExtractorMediaSource(uri, factory, new DefaultExtractorsFactory(), mainHandler, eventLogger);
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
```gradle
compile 'com.google.android.exoplayer:extension-rtmp:rX.X.X'
```
where `rX.X.X` is the version, which must match the version of the ExoPlayer
library being used.
[DataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/DataSource.html
[RTMP]: https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol
[Librtmp Client for Android]: https://github.com/ant-media/LibRtmp-Client-for-Android
[MediaSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/source/MediaSource.html
Alternatively, you can clone the ExoPlayer repository and depend on the module
locally. Instructions for doing this can be found in ExoPlayer's
[top level README][].
[top level README]: https://github.com/google/ExoPlayer/blob/release-v2/README.md
......@@ -11,6 +11,7 @@
// 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: '../../constants.gradle'
apply plugin: 'com.android.library'
android {
......@@ -18,12 +19,14 @@ android {
buildToolsVersion project.ext.buildToolsVersion
defaultConfig {
minSdkVersion 16
// TODO: Lower minSdkVersion as much as possible once this issue in LibRtmp is fixed:
// https://github.com/ant-media/LibRtmp-Client-for-Android/issues/39
minSdkVersion 21
targetSdkVersion project.ext.targetSdkVersion
}
}
dependencies {
compile project(':library-core')
compile 'net.butterflytv.utils:rtmp-client:0.2.6.1'
}
\ No newline at end of file
compile 'net.butterflytv.utils:rtmp-client:0.2.7.1'
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
<!-- Copyright (C) 2017 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.
......
/*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2017 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.
......@@ -16,55 +16,77 @@
package com.google.android.exoplayer2.ext.rtmp;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import net.butterflytv.rtmp_client.RtmpClient;
import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
import net.butterflytv.rtmp_client.RtmpClient;
import net.butterflytv.rtmp_client.RtmpClient.RtmpIOException;
/**
* A Real-Time Messaging Protocol (RTMP) {@link DataSource}.
*/
public final class RtmpDataSource implements DataSource {
private final RtmpClient rtmpClient;
@Nullable private final TransferListener<? super RtmpDataSource> listener;
private RtmpClient rtmpClient;
private Uri uri;
public RtmpDataSource() {
rtmpClient = new RtmpClient();
this(null);
}
@Override
public Uri getUri() {
return uri;
/**
* @param listener An optional listener.
*/
public RtmpDataSource(@Nullable TransferListener<? super RtmpDataSource> listener) {
this.listener = listener;
}
@Override
public long open(DataSpec dataSpec) throws IOException {
uri = dataSpec.uri;
int result = rtmpClient.open(dataSpec.uri.toString(), false);
if (result < 0) {
return 0;
public long open(DataSpec dataSpec) throws RtmpIOException {
rtmpClient = new RtmpClient();
rtmpClient.open(dataSpec.uri.toString(), false);
this.uri = dataSpec.uri;
if (listener != null) {
listener.onTransferStart(this, dataSpec);
}
return C.LENGTH_UNSET;
}
@Override
public void close() throws IOException {
rtmpClient.close();
public int read(byte[] buffer, int offset, int readLength) throws IOException {
int bytesRead = rtmpClient.read(buffer, offset, readLength);
if (bytesRead == -1) {
return C.RESULT_END_OF_INPUT;
}
if (listener != null) {
listener.onBytesTransferred(this, bytesRead);
}
return bytesRead;
}
@Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
return rtmpClient.read(buffer, offset, readLength);
public void close() {
if (uri != null) {
uri = null;
if (listener != null) {
listener.onTransferEnd(this);
}
}
if (rtmpClient != null) {
rtmpClient.close();
rtmpClient = null;
}
}
public final static class RtmpDataSourceFactory implements DataSource.Factory {
@Override
public DataSource createDataSource() {
return new RtmpDataSource();
}
@Override
public Uri getUri() {
return uri;
}
}
/*
* Copyright (C) 2017 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 com.google.android.exoplayer2.ext.rtmp;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.upstream.TransferListener;
/**
* A {@link Factory} that produces {@link RtmpDataSource}.
*/
public final class RtmpDataSourceFactory implements DataSource.Factory {
@Nullable
private final TransferListener<? super RtmpDataSource> listener;
public RtmpDataSourceFactory() {
this(null);
}
/**
* @param listener An optional listener.
*/
public RtmpDataSourceFactory(@Nullable TransferListener<? super RtmpDataSource> listener) {
this.listener = listener;
}
@Override
public DataSource createDataSource() {
return new RtmpDataSource(listener);
}
}
......@@ -17,9 +17,11 @@ package com.google.android.exoplayer2.upstream;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/**
* A {@link DataSource} that supports multiple URI schemes. The supported schemes are:
......@@ -30,6 +32,8 @@ import java.io.IOException;
* local file URI).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
* explicit dependency on ExoPlayer's RTMP extension.</li>
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4), if
* constructed using {@link #DefaultDataSource(Context, TransferListener, String, boolean)}, or
* any other schemes supported by a base data source if constructed using
......@@ -38,13 +42,22 @@ import java.io.IOException;
*/
public final class DefaultDataSource implements DataSource {
private static final String TAG = "DefaultDataSource";
private static final String SCHEME_ASSET = "asset";
private static final String SCHEME_CONTENT = "content";
private static final String SCHEME_RTMP = "rtmp";
private final Context context;
private final TransferListener<? super DataSource> listener;
private final DataSource baseDataSource;
private final DataSource fileDataSource;
private final DataSource assetDataSource;
private final DataSource contentDataSource;
// Lazily initialized.
private DataSource fileDataSource;
private DataSource assetDataSource;
private DataSource contentDataSource;
private DataSource rtmpDataSource;
private DataSource dataSource;
......@@ -95,10 +108,9 @@ public final class DefaultDataSource implements DataSource {
*/
public DefaultDataSource(Context context, TransferListener<? super DataSource> listener,
DataSource baseDataSource) {
this.context = context.getApplicationContext();
this.listener = listener;
this.baseDataSource = Assertions.checkNotNull(baseDataSource);
this.fileDataSource = new FileDataSource(listener);
this.assetDataSource = new AssetDataSource(context, listener);
this.contentDataSource = new ContentDataSource(context, listener);
}
@Override
......@@ -108,14 +120,16 @@ public final class DefaultDataSource implements DataSource {
String scheme = dataSpec.uri.getScheme();
if (Util.isLocalFileUri(dataSpec.uri)) {
if (dataSpec.uri.getPath().startsWith("/android_asset/")) {
dataSource = assetDataSource;
dataSource = getAssetDataSource();
} else {
dataSource = fileDataSource;
dataSource = getFileDataSource();
}
} else if (SCHEME_ASSET.equals(scheme)) {
dataSource = assetDataSource;
dataSource = getAssetDataSource();
} else if (SCHEME_CONTENT.equals(scheme)) {
dataSource = contentDataSource;
dataSource = getContentDataSource();
} else if (SCHEME_RTMP.equals(scheme)) {
dataSource = getRtmpDataSource();
} else {
dataSource = baseDataSource;
}
......@@ -144,4 +158,48 @@ public final class DefaultDataSource implements DataSource {
}
}
private DataSource getFileDataSource() {
if (fileDataSource == null) {
fileDataSource = new FileDataSource(listener);
}
return fileDataSource;
}
private DataSource getAssetDataSource() {
if (assetDataSource == null) {
assetDataSource = new AssetDataSource(context, listener);
}
return assetDataSource;
}
private DataSource getContentDataSource() {
if (contentDataSource == null) {
contentDataSource = new ContentDataSource(context, listener);
}
return contentDataSource;
}
private DataSource getRtmpDataSource() {
if (rtmpDataSource == null) {
try {
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.rtmp.RtmpDataSource");
rtmpDataSource = (DataSource) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
Log.w(TAG, "Attempting to play RTMP stream without depending on the RTMP extension");
} catch (InstantiationException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} catch (NoSuchMethodException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
}
if (rtmpDataSource == null) {
rtmpDataSource = baseDataSource;
}
}
return rtmpDataSource;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment