MediaCodec+MediaExtractor实现选取本地视频文件进行解码 kotlin代码实现

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(this@VideoPlayActivity, 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());
    }

}

Over

猜你喜欢

转载自blog.csdn.net/a940659387/article/details/103704133