/*
* Copyright (C) 2016 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.like.zza.missit.video;
import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.min;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.upstream.BaseDataSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceException;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
import com.google.android.exoplayer2.upstream.HttpUtil;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Predicate;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.net.HttpHeaders;
import com.like.zza.missit.utils.HttpDataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
*
* <p>By default this implementation will not follow cross-protocol redirects (i.e. redirects from
* HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by passing {@code true} to
* {@link Factory#setAllowCrossProtocolRedirects(boolean)}.
*
* <p>Note: HTTP request headers will be set using all parameters passed via (in order of decreasing
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default properties that can
* be passed to {@link HttpDataSource.Factory#setDefaultRequestProperties(Map)}.
*
* @deprecated com.google.android.exoplayer2 is deprecated. Please migrate to androidx.media3 (which
* contains the same ExoPlayer code). See <a
* href="https://developer.android.com/guide/topics/media/media3/getting-started/migration-guide">the
* migration guide</a> for more details, including a script to help with the migration.
*/
public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource {
/**
* {@link DataSource.Factory} for {@link DefaultHttpDataSource} instances.
*/
public static final class Factory implements HttpDataSource.Factory {
private final RequestProperties defaultRequestProperties;
@Nullable
private TransferListener transferListener;
@Nullable
private Predicate<String> contentTypePredicate;
@Nullable
private String userAgent;
private int connectTimeoutMs;
private int readTimeoutMs;
private boolean allowCrossProtocolRedirects;
private boolean keepPostFor302Redirects;
/**
* Creates an instance.
*/
public Factory() {
defaultRequestProperties = new RequestProperties();
connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS;
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
}
@Override
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
return this;
}
/**
* Sets the user agent that will be used.
*
* <p>The default is {@code null}, which causes the default user agent of the underlying
* platform to be used.
*
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
* @return This factory.
*/
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
return this;
}
/**
* Sets the connect timeout, in milliseconds.
*
* <p>The default is {@link DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}.
*
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
public Factory setConnectTimeoutMs(int connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
return this;
}
/**
* Sets the read timeout, in milliseconds.
*
* <p>The default is {@link DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS}.
*
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
public Factory setReadTimeoutMs(int readTimeoutMs) {
this.readTimeoutMs = readTimeoutMs;
return this;
}
/**
* Sets whether to allow cross protocol redirects.
*
* <p>The default is {@code false}.
*
* @param allowCrossProtocolRedirects Whether to allow cross protocol redirects.
* @return This factory.
*/
public Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) {
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
return this;
}
/**
* Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a
* {@link InvalidContentTypeException} is thrown from {@link
* DefaultHttpDataSource#open(DataSpec)}.
*
* <p>The default is {@code null}.
*
* @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a
* predicate that was previously set.
* @return This factory.
*/
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
return this;
}
/**
* Sets the {@link TransferListener} that will be used.
*
* <p>The default is {@code null}.
*
* <p>See {@link DataSource#addTransferListener(TransferListener)}.
*
* @param transferListener The listener that will be used.
* @return This factory.
*/
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = tr
Android 自定义加解密播放音视频(m3u8独立加密)伴随源码
需积分: 0 149 浏览量
更新于2023-09-17
收藏 10KB ZIP 举报
在Android平台上,开发音视频应用时,为了保护内容的安全性和防止未经授权的访问,开发者常常需要实现自定义的加解密机制。本主题聚焦于如何在Android应用中实现针对m3u8格式的音视频流的独立加密播放,并提供相关的源码文件。m3u8是一种基于HTTP的媒体播放列表格式,常用于流媒体服务,如HLS(HTTP Live Streaming)。
我们需要了解m3u8的工作原理。m3u8文件本质上是一个文本文件,它包含了音视频片段的URL列表以及播放的相关信息,如时间切片、编码参数等。在播放过程中,播放器会根据m3u8文件中的指示下载相应的媒体片段并进行播放。
为了实现加密播放,我们需要对m3u8中列出的媒体片段进行加密处理。这通常涉及到以下步骤:
1. **数据加密**:在服务器端,使用特定的加密算法(如AES-128)对每个媒体片段进行加密,生成加密后的二进制数据。加密过程应确保只有持有正确密钥的客户端才能解密并播放。
2. **密钥管理**:加密后的媒体片段与一个或多个密钥相关联,这些密钥存储在单独的key文件中,或者通过HTTP响应头传递。在Android应用中,你需要创建一个获取密钥的逻辑,可能需要通过网络请求获取。
3. **自定义数据源**:在Android播放器(如ExoPlayer)中,你需要自定义`DataSource`来处理加密的数据。例如,`DefaultHttpDataSource.java`和`DefaultDataSourceFactory.java`可能是这样的自定义数据源的实现,它们负责读取加密的媒体片段,并在传输过程中解密数据。
4. **播放器集成**:在`PlayerActivity.java`中,你需要集成自定义的数据源到播放器实例,确保在播放每个片段时,能够正确地解密数据并将其传递给解码器。
5. **错误处理与安全策略**:在实现过程中,需要考虑到错误处理,如密钥获取失败、解密错误等。同时,为了增加安全性,可以添加额外的安全策略,比如限制密钥的使用次数,或者实施动态密钥更新。
6. **性能优化**:由于加密和解密操作会消耗计算资源,因此在实现时需要考虑性能优化,如使用硬件加速解密库,减少内存拷贝,以及合理调度任务以避免阻塞UI线程。
通过以上步骤,我们可以创建一个安全的、自定义加解密的m3u8音视频播放系统。这样的系统既保证了内容的安全,又提供了流畅的用户体验。然而,实际开发过程中还需要关注版权法律、跨平台兼容性以及用户隐私等问题。对于初学者,理解并实现这样的系统可能会有一定难度,但通过学习提供的源码,可以深入理解加密播放的实现细节。