MediaCodec+MediaExtractor实现选取本地视频文件进行解码 kotlin代码实现
程序员文章站
2022-07-05 16:19:28
...
mediacodec视频硬解码
不喜欢多比比 直接上代码
需要配置provider 适配7.0+ 具体怎么配置自行google
package com.cyber.app_test.ui.aty
import android.app.Activity
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.util.Log
import android.view.Surface
import android.view.SurfaceHolder
import android.view.View
import com.cyber.app_test.R
import com.cyber.app_test.base.BaseActivity
import kotlinx.android.synthetic.main.aty_videoplay.*
import kotlinx.android.synthetic.main.include_title.*
import android.R.attr.start
import android.R.attr.configure
import android.R.attr.mimeType
import android.content.Context
import android.media.MediaCodec
import android.media.MediaCodec.createByCodecName
import android.media.MediaFormat.createVideoFormat
import android.media.MediaFormat.MIMETYPE_VIDEO_AVC
import android.media.MediaCodec.createDecoderByType
import android.media.MediaExtractor
import android.media.MediaFormat
import android.net.Uri
import android.os.Build
import android.support.v4.content.FileProvider
import com.cyber.app_test.BuildConfig
import com.cyber.app_test.utils.FileUtils
import java.nio.ByteBuffer
/***
* 作者 : 于德海
* 时间 : 2019/12/25 0025 15:05
* 描述 :
*/
class VideoPlayActivity : BaseActivity() {
private val TAG = VideoPlayActivity::class.java.simpleName
private var mSurface : Surface?=null
private var mMediaCodec : MediaCodec? = null
private var mContext : Context? = null
private var isDestory = false
override fun initParam(bundle: Bundle?) {
}
override fun initLayout(): Int {
return R.layout.aty_videoplay
}
override fun initView() {
tv_title.text = TAG
mSurface = surfaceView.holder.surface
surfaceView.holder.addCallback(callback)
mContext = this
}
var callback : SurfaceHolder.Callback = object : SurfaceHolder.Callback{
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
Log.i(TAG,"surfaceChanged")
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
Log.i(TAG,"surfaceDestroyed")
}
override fun surfaceCreated(holder: SurfaceHolder?) {
}
}
override fun initData() {
}
override fun initListener() {
btn_select.setOnClickListener(View.OnClickListener { v ->kotlin.run {
var intent = Intent()
intent.type = "*/*"
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(intent,1)
} })
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.i(TAG,"onActivityResult:$requestCode;$resultCode")
if(requestCode == 1 && resultCode == Activity.RESULT_OK){
Log.i(TAG,"data:"+data?.data)
MediaThread(data?.data).start()
}
}
override fun onClick(v: View?) {
}
inner class MediaThread : Thread{
var framecount =0L
var inputErrorCount = 0
var outputErrorCount = 0
var videoUri : Uri? = null
constructor(uri : Uri?) : super(){
videoUri = uri
}
override fun run() {
super.run()
var mediaExtractor = MediaExtractor()
val fileUrl = FileUtils.getFilePathByUri([email protected], videoUri)
Log.i(TAG,"fileURl:$fileUrl")
mediaExtractor.setDataSource(fileUrl)
var index = getMediaTrackIndex(mediaExtractor)
Log.i(TAG,"视频渠道:$index")
if(index >= 0){
var mediaFormat = mediaExtractor.getTrackFormat(index)
var width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
var height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
// var duration = mediaFormat.getLong(MediaFormat.KEY_DURATION)/1000000
Log.i(TAG,"widht:$width;height:$height;mime:"+mediaFormat.getString(MediaFormat.KEY_MIME))
mediaExtractor.selectTrack(index)
mMediaCodec = createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME))
mMediaCodec?.configure(mediaFormat,mSurface,null,0)
mMediaCodec?.start()
var bufferInfo = MediaCodec.BufferInfo()
var inputbuffer = mMediaCodec?.inputBuffers
while (!isDestory){
inputData(mediaExtractor,inputbuffer)
outputData(bufferInfo)
}
}
}
fun outputData(buffer: MediaCodec.BufferInfo){
var output = mMediaCodec?.dequeueOutputBuffer(buffer,20000L)!!
if(output>=0){
Log.i(TAG,"out time:$framecount")
mMediaCodec?.releaseOutputBuffer(output,true)
outputErrorCount = 0
return
}
if(outputErrorCount>10){
outputErrorCount =0
Log.e(TAG,"输出超过错误上限")
return
}
outputErrorCount++
outputData(buffer)
}
fun inputData(extractor: MediaExtractor,inputBuffer : Array<ByteBuffer>?){
var index:Int = mMediaCodec?.dequeueInputBuffer(10000L)!!
if(index >= 0){
framecount++
var input : ByteBuffer? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
input = mMediaCodec?.getInputBuffer(index)
} else {
input = inputBuffer?.get(index)
}
var size = extractor.readSampleData(input,0)
Log.i(TAG,"inject:$framecount;size:$size")
var flag = 0
if(size < 0){
flag = MediaCodec.BUFFER_FLAG_END_OF_STREAM
Log.e(TAG,"视频结束")
isDestory = true
return
}
inputErrorCount = 0
mMediaCodec?.queueInputBuffer(index,0,size,framecount,flag)
extractor.advance()
return
}
if(inputErrorCount>10){
inputErrorCount =0
Log.e(TAG,"注入超过错误上限")
return
}
outputErrorCount++
inputData(extractor,inputBuffer)
}
fun getMediaTrackIndex(mediaExtractor: MediaExtractor) : Int{
for (i in 0 until mediaExtractor.trackCount){
var mediaFormat = mediaExtractor.getTrackFormat(i)
if (mediaFormat.getString(MediaFormat.KEY_MIME).startsWith("video/")){
return i
}
}
return -1
}
}
override fun onDestroy() {
isDestory = true
super.onDestroy()
}
}
FileUtils类
此类是参考的别人的文章 具体连接找不到了 有认领的可@我
package com.cyber.app_test.utils;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
/***
* 作者 : 于德海
* 时间 : 2019/12/25 0025 16:07
* 描述 :
*/
public final class FileUtils {
public static String getFilePathByUri(Context context, Uri uri) {
String path = null;
// 以 file:// 开头的
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
path = uri.getPath();
return path;
}
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (columnIndex > -1) {
path = cursor.getString(columnIndex);
}
}
cursor.close();
}
return path;
}
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
// ExternalStorageProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
path = Environment.getExternalStorageDirectory() + "/" + split[1];
return path;
}
} else if (isDownloadsDocument(uri)) {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
path = getDataColumn(context, contentUri, null, null);
return path;
} else if (isMediaDocument(uri)) {
// MediaProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
path = getDataColumn(context, contentUri, selection, selectionArgs);
return path;
}
}
}
return null;
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
}