企业建站模板价格,大城 网站建设,html小清新类型网站,大连网站制作机构文章目录 背景加密流程音视频解密音视频播放结语 背景
当涉及App内部视频的时候#xff0c;我们不希望被别人以抓包的形式来爬取我们的视频大视频文件以文件方式整个加密的话需要完全下载后才能进行解密当前m3u8格式虽然支持加密#xff0c;但是ts格式的小视频可以独立播放的… 文章目录 背景加密流程音视频解密音视频播放结语 背景
当涉及App内部视频的时候我们不希望被别人以抓包的形式来爬取我们的视频大视频文件以文件方式整个加密的话需要完全下载后才能进行解密当前m3u8格式虽然支持加密但是ts格式的小视频可以独立播放的也就是ts文件本身没有被加密或者加密方法过于复杂
根据以上我通过修改ExoPlayer的源代码实现以下功能这里不讨论其他视频流加密解密的方法
大文件分段加密后应用分段解密m3u8高度自定义你可以实现任何你需要的加密方法甚至每一个ts都有自己的解码方式ts加密不允许独立播放
加密流程 PS使用ffmpeg进行音视频分割后使用Java代码进行加密
音视频分割 代码就是通过java执行ffmpeg的命令即可请确保环境变量中安装了ffmpeg内部的代码可以自己通过需求来修改其中音频与视频的分割方式差不多 private static String encryptVideoWithFFmpeg(String videoFilePath, String outputDirPath) {File outputDir new File(outputDirPath);if (!outputDir.exists()) {outputDir.mkdirs();}String outputFileName output; // 输出文件名这里可以根据需要自定义String tsOutputPath outputDirPath File.separator outputFileName .ts;String m3u8OutputPath outputDirPath File.separator outputFileName .m3u8;try {ProcessBuilder processBuilder new ProcessBuilder(ffmpeg,-i, videoFilePath,-c:v, libx264,-c:a, aac,-f, hls,-hls_time, 5,-hls_list_size, 0,-hls_segment_filename, outputDirPath File.separator output%03d.ts,m3u8OutputPath);// 设置工作目录可以防止某些情况下找不到 ffmpeg 命令的问题Process process processBuilder.start();// 获取 ffmpeg 命令执行的输出信息可选如果需要查看 ffmpeg 执行日志BufferedReader reader new BufferedReader(new InputStreamReader(process.getErrorStream()));String line;while ((line reader.readLine()) ! null) {System.out.println(line);}int exitCode process.waitFor();if (exitCode 0) {System.out.println(FFmpeg command executed successfully.);} else {System.err.println(Error executing FFmpeg command. Exit code: exitCode);}} catch (IOException | InterruptedException e) {e.printStackTrace();}return tsOutputPath;}
private static String splitAudioWithFFmpeg(String audioFilePath, String outputDirPath) {File outputDir new File(outputDirPath);if (!outputDir.exists()) {outputDir.mkdirs();}String outputFileName output; // 输出文件名这里可以根据需要自定义String tsOutputPath outputDirPath File.separator outputFileName .ts;String m3u8OutputPath outputDirPath File.separator outputFileName .m3u8;try {ProcessBuilder processBuilder new ProcessBuilder(ffmpeg,-i, audioFilePath,-c:a, aac,-f, hls,-hls_time, 10,-hls_list_size, 0,-hls_segment_filename, outputDirPath File.separator output%03d.ts,m3u8OutputPath);Process process processBuilder.start();BufferedReader reader new BufferedReader(new InputStreamReader(process.getErrorStream()));String line;while ((line reader.readLine()) ! null) {System.out.println(line);}int exitCode process.waitFor();if (exitCode 0) {System.out.println(FFmpeg command executed successfully.);} else {System.err.println(Error executing FFmpeg command. Exit code: exitCode);}} catch (IOException | InterruptedException e) {e.printStackTrace();}return tsOutputPath;}音视频加密 这里的视频加密使用的是AES加密是将ts结尾的所有文件进行加密后面的方法是解密一般用不到 private static void encryptTSSegmentsWithAES(String outputDirPath, String aesKey) {File outputDir new File(outputDirPath);File[] tsFiles outputDir.listFiles((dir, name) - name.endsWith(.ts));if (tsFiles ! null) {try {byte[] keyBytes aesKey.getBytes();Key aesKeySpec new SecretKeySpec(keyBytes, AES_ALGORITHM);Cipher cipher Cipher.getInstance(AES_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec);for (File tsFile : tsFiles) {byte[] tsData Files.readAllBytes(Paths.get(tsFile.getPath()));byte[] encryptedData cipher.doFinal(tsData);Files.write(Paths.get(tsFile.getPath()), encryptedData);}} catch (Exception e) {e.printStackTrace();}}}public static void decryptTSSegmentsWithAES(String outputDirPath, String aesKey) {File outputDir new File(outputDirPath);File[] tsFiles outputDir.listFiles((dir, name) - name.endsWith(.ts));if (tsFiles ! null) {try {byte[] keyBytes aesKey.getBytes();Key aesKeySpec new SecretKeySpec(keyBytes, AES);Cipher cipher Cipher.getInstance(AES_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, aesKeySpec);for (File tsFile : tsFiles) {byte[] tsData Files.readAllBytes(Paths.get(tsFile.getPath()));byte[] encryptedData cipher.doFinal(tsData);Files.write(Paths.get(tsFile.getPath()), encryptedData);}} catch (Exception e) {e.printStackTrace();}}}加密完成之后将m3u8放在服务器上并且分割的文件也要在同一目录或者切片的时候手动设置保证切片后的视频可以正常播放即可
音视频解密
这里使用的是修改ExoPlayer的源代码来实现的因为在Android手机上面播放视频的选择有很多大家也可以根据我的方法修改其他播放器本次按照ExoPlayer进行演示教学 PS:因为Google把ExoPlayer整合到MediaPlayer3里了所以如果不使用纯源代码来修改的话也会跟我的演示一样有删除线但是无伤大雅
引入依赖直接在App层的Build.gradle引入ExoPlayer2的依赖其中我们要使用的视频流为hls格式所以需要引入hls模块 implementation com.google.android.exoplayer:exoplayer-core:2.19.0implementation com.google.android.exoplayer:exoplayer-dash:2.19.0implementation com.google.android.exoplayer:exoplayer-ui:2.19.0implementation com.google.android.exoplayer:exoplayer-hls:2.19.0准备修改代码我们需要修改的类如下
DefaultDataSourceDefaultDataSourceFactoryDefaultHttpDataSourceHttpDataSource
我们只需要复制其源码然后进行修改后使用ExoPlayer播放视频的时候使用我们自己的类即可如果你不想这样那么可以直接下载ExoPlayer2的源代码进行修改这样的话还能去除废弃的表示没有那么多删除线接下来我们正式开始修改 修改类“DefaultHttpDataSource” 我将以注释的方式来讲解代码注意这里只是演示一个简单的自定义加解密的切入方式所以按照文件名末尾为ts的文件进行暴力判断精细化的处理方式可以有很多拓展比如仅加密视频的中间部分作为会员视频这样只需要单一视频流就可以解决试看的问题而且不怕应用内部修改VIP标志位对于修改源码等暴力破解的方法无效毕竟源码都给你扒出来了
//定义解密流主要使用此流来进行解密
private CipherInputStream cipherInputStream;
//修改open方法代码最后的try代码块中增加如下内容用来解密流
Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
....
try {inputStream connection.getInputStream();if (isCompressed) {inputStream new GZIPInputStream(inputStream);}//新增代码块这里的解密方法可以按照自己的需求编写----------------------------------if (dataSpec.uri.getPath().endsWith(.ts)) {Cipher cipher;try {cipher Cipher.getInstance(AES);} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new RuntimeException(e);}Key aesKeySpec new SecretKeySpec(1234567890abcdef.getBytes(), AES);try {cipher.init(Cipher.DECRYPT_MODE, aesKeySpec);} catch (InvalidKeyException e) {throw new RuntimeException(e);}cipherInputStream new CipherInputStream(inputStream, cipher);}//新增代码块结束------------------------------} catch (IOException e) {closeConnectionQuietly();throw new HttpDataSourceException(e,dataSpec,PlaybackException.ERROR_CODE_IO_UNSPECIFIED,HttpDataSourceException.TYPE_OPEN);}....
}//修改read方法如下如果判断是需要解密的文件则走cipherInputStreamOverridepublic final int read(byte[] buffer, int offset, int length) throws IOException {if (dataSpec.uri.getPath().endsWith(.ts)) {Assertions.checkNotNull(cipherInputStream);int bytesRead cipherInputStream.read(buffer, offset, length);if (bytesRead 0) {return C.RESULT_END_OF_INPUT;}return bytesRead;} else {try {return readInternal(buffer, offset, length);} catch (IOException e) {throw HttpDataSourceException.createForIOException(e, castNonNull(dataSpec), HttpDataSourceException.TYPE_READ);}}}
//最后释放资源Overridepublic void close() throws HttpDataSourceException {try {Nullable InputStream inputStream this.inputStream;if (inputStream ! null) {long bytesRemaining bytesToRead C.LENGTH_UNSET ? C.LENGTH_UNSET : bytesToRead - bytesRead;maybeTerminateInputStream(connection, bytesRemaining);try {inputStream.close();} catch (IOException e) {throw new HttpDataSourceException(e,castNonNull(dataSpec),PlaybackException.ERROR_CODE_IO_UNSPECIFIED,HttpDataSourceException.TYPE_CLOSE);}}if (cipherInputStream ! null) {cipherInputStream.close();}} catch (IOException e) {throw new HttpDataSourceException(e,castNonNull(dataSpec),PlaybackException.ERROR_CODE_IO_UNSPECIFIED,HttpDataSourceException.TYPE_CLOSE);} finally {inputStream null;cipherInputStream null;closeConnectionQuietly();if (opened) {opened false;transferEnded();}}}修改类“DefaultDataSourceFactory” 此类只需要修改一点那就是将DefaultDataSource的create过程引导到我们自己写的DefaultDataSource也就是删除原来的ExoPlayer2的依赖引入引入刚刚讲到的DefaultHttpDataSource不需要修改代码只需要切换依赖即可 public DefaultDataSourceFactory(Context context, Nullable String userAgent, Nullable TransferListener listener) {this(context, listener, new DefaultHttpDataSource.Factory().setUserAgent(userAgent));} 音视频播放
因为ExoPlayer2同时支持音频和视频的播放所以均可使用下列方式完成
public class PlayerActivity extends AppCompatActivity {private PlayerView playerView;private SimpleExoPlayer player;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_player);// Initialize PlayerViewplayerView findViewById(R.id.player);// Create a DefaultTrackSelector to enable tracksDefaultTrackSelector trackSelector new DefaultTrackSelector(this);// Create an instance of ExoPlayerplayer new SimpleExoPlayer.Builder(this).setTrackSelector(trackSelector).build();// Attach the player to the PlayerViewplayerView.setPlayer(player);String userAgent Util.getUserAgent(this, ExoPlayerDemo);DefaultDataSourceFactory dataSourceFactory new DefaultDataSourceFactory(this, userAgent);String videoUrl http://zhangzhiao.top/missit/aa/output.m3u8;// Create an HlsMediaSourceHlsMediaSource mediaSource new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)));// Prepare the player with the media sourceplayer.prepare(mediaSource);}Overrideprotected void onDestroy() {super.onDestroy();// Release the player when the activity is destroyedplayer.release();}
}
源码下载
结语
代码里给大家提供了一个小视频如果按照流程编写应该是可以顺利播放的如果需要还可以把m3u8文件进行加密处理一切处理方法都可以实现如果对您有帮助不妨点个赞