Clean up rtmp extension
This commit is contained in:
parent
1279b7dcf1
commit
7524228160
@ -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].
|
||||
|
||||
## Using the extension ##
|
||||
|
||||
When building [MediaSource][], inject `RtmpDataSourceFactory` like this:
|
||||
|
||||
```java
|
||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
||||
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri)
|
||||
: Util.inferContentType("." + overrideExtension);
|
||||
switch (type) {
|
||||
|
||||
// ... other types cases
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The RTMP Extension is a [DataSource][] implementation for playing [RTMP][]
|
||||
streams using [LibRtmp Client for Android][].
|
||||
|
||||
[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
|
||||
[LibRtmp Client for Android]: https://github.com/ant-media/LibRtmp-Client-for-Android
|
||||
|
||||
## Using the extension ##
|
||||
|
||||
The easiest way to use the extension is to add it as a gradle dependency:
|
||||
|
||||
```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.
|
||||
|
||||
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'
|
||||
}
|
||||
compile 'net.butterflytv.utils:rtmp-client:0.2.7.1'
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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,24 +16,72 @@
|
||||
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() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public RtmpDataSource(@Nullable TransferListener<? super RtmpDataSource> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 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 void close() {
|
||||
if (uri != null) {
|
||||
uri = null;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
}
|
||||
if (rtmpClient != null) {
|
||||
rtmpClient.close();
|
||||
rtmpClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -41,30 +89,4 @@ public final class RtmpDataSource implements DataSource {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
uri = dataSpec.uri;
|
||||
int result = rtmpClient.open(dataSpec.uri.toString(), false);
|
||||
if (result < 0) {
|
||||
return 0;
|
||||
}
|
||||
return C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
rtmpClient.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||
return rtmpClient.read(buffer, offset, readLength);
|
||||
}
|
||||
|
||||
public final static class RtmpDataSourceFactory implements DataSource.Factory {
|
||||
@Override
|
||||
public DataSource createDataSource() {
|
||||
return new RtmpDataSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user