android原生MediaPlayer播放本地的m3u8
背景
遇到某个需求,需要播放百度网盘的视频。但是通过百度网盘的sdk获取到的视频url是由若干个ts片段组成的m3u8播放源。然后把这个m3u8的播放源放在本地,也就是说m3u8是一个本地播放源。那么如何去播放呢?这边引进一个http轻量级的服务器NanoHttpd。
选择播放器
目前市场上流行的播放器,比如ijkPlay,是可以直接播放本地的m3u8,但是android原生的播放器却不能。那么如何去播放呢?这边引进一个http轻量级的服务器NanoHttpd。
什么是NanoHttpd?
NanoHTTPD是一个免费、轻量级的HTTP服务器,可以很好地嵌入到Java程序中。支持 GET, POST, PUT, HEAD 和 DELETE 请求,支持文件上传,占用内存很小。可轻松定制临时文件使用和线程模型。
git地址:
https://github.com/NanoHttpd/nanohttpd
下面看看NanoHttpd的使用情况
1.在build.gradle文件中引入nanohttpd
implementation ‘org.nanohttpd:nanohttpd:2.3.1’
2.由于9.0以上禁止使用未加密的连接,所以在res下添加xml文件夹,并把network_security_config.xml放到该文件夹下,并在AndroidManifest.xml的application中添加android:networkSecurityConfig="@xml/network_security_config"network_security_config文件的具体内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">127.0.0.1</domain>
<domain includeSubdomains="true">localhost</domain>
</domain-config>
</network-security-config>
3.如果.m3u8的文件是放在sdcard中需要在AndroidManifest.xml下添加相关的读写权限,放在data/data中则不需要
<!--写sdcard权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- 添加完以上配置后,就可以使用了,具体使用可参考LocalServer:
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import fi.iki.elonen.NanoHTTPD;
public class LocalServer extends NanoHTTPD {
private static String TAG = "CcLocalServer";
/**
* m3u8的文件格式
*/
private static final String MIME_TYPE_M3U8 = "video/x-mpegURL";
// 端口号
public static final int PORT = 9999;
/**
* 构造方法
*/
public LocalServer() {
// 端口号
super(PORT);
Log.d(TAG, "---CcLocalServer---");
}
/**
* 重写 serve 方法,获取本地视频文件
*
* @param session The HTTP session
* @return
*/
@Override
public Response serve(IHTTPSession session) {
// 获取请求的url
String url = session.getUri();
Log.d(TAG, "请求URL:" + url);
try {
File file = new File(url);
//判断本地的文件是否存在
if (file.exists()) {
//直接返回m3u8的文件格式
return newChunkedResponse(Response.Status.OK, MIME_TYPE_M3U8, new FileInputStream(file));
} else {
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/html", "文件不存在:" + url);
}
} catch (Exception e) {
e.printStackTrace();
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/html", "文件不存在:" + url);
}
}
}
activity中如何使用呢?
注意:m3u8文件和文件的存放位置由使用者自己决定
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.example.mediaplaydome.server.LocalServer;
public class MediaPlayerActivity extends AppCompatActivity {
private static final String TAG = "MediaPlayerActivity";
private SurfaceHolder surfaceHolder;
private SurfaceView surfaceView;
private MediaPlayer mediaPlayer;
private LocalServer mMyServer = new LocalServer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_media_player);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
startLocalServer();
startPlayVideo();
}
/**
* 开启本地服务器
*/
private void startLocalServer() {
try {
// 开启本地代理
mMyServer.start();
} catch (Exception e) {
Log.e(TAG, "server start failed", e);
}
}
/**
* 开始播放
*/
private void startPlayVideo() {
String url = getHttpUrl(Environment.getExternalStorageDirectory().getPath() + "/local_m3u8/test.m3u8");
Log.i(TAG, "当前的url:" + url);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(url);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
mediaPlayer.setDisplay(surfaceHolder);
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
}
});
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
mediaPlayer.setLooping(true);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//关闭服务
if (mMyServer != null) {
mMyServer.closeAllConnections();
mMyServer = null;
Log.e(TAG, "app destory, so server close");
}
}
/**
* 通过本地文件的地址,获取本地http的地址
* @param localUrl
* @return
*/
public static String getHttpUrl(String localUrl) {
if (TextUtils.isEmpty(localUrl)) {
return localUrl;
}
return String.format("http://127.0.0.1:%d%s", LocalServer.PORT, localUrl);
}
}