欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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());
    }

}

Over

相关标签: Android