【Android】MediaCodec详解[通俗易懂]

【Android】MediaCodec详解[通俗易懂]1前言MediaCodec主要用于视频解码和编码操作,可以实现视频倍速播放、全关键帧转换、视频倒放等功能。MediaCodec的工作原理图如下:

大家好,欢迎来到IT知识分享网。

1 前言

        MediaCodec 主要用于视频解码和编码操作,可以实现视频倍速播放、全关键帧转换、视频倒放等功能。

        MediaCodec 的工作原理图如下:

【Android】MediaCodec详解[通俗易懂]

        MediaCodec 的主要接口如下:

//创建解码器(type为mime或name)
public static MediaCodec createDecoderByType(String type)

//创建编码器(type为mime或name)
public static MediaCodec createEncoderByType(String type)

//配置解码器和编码器(flag为0表示解码器,1表示编码器)
public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) 

//启动编码器或解码器
public final void start()

//获取输入队列的一个空闲索引(timeoutUs:最多等待时间,-1表示一直等待,单位:微秒us)
public final int dequeueInputBuffer(long timeoutUs)

//获取输入队列的一个空闲缓存区(index:dequeueInputBuffer方法的返回值)
public ByteBuffer getInputBuffer(int index)

//提醒解码器或编码器处理数据(index:dequeueInputBuffer方法的返回值)
public final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags)

//创建BufferInfo类,用于存储解码或编码后的缓存数据的格式信息
public final static class BufferInfo

//获取输出队列的一个缓存区的索引,并将格式信息保存在info中(timeoutUs:最多等待时间,-1表示一直等待,单位:微秒us)
public final int dequeueOutputBuffer(BufferInfo info, long timeoutUs)

//获取输出队列的一个缓存区(index:dequeueOutputBuffer方法的返回值)
public ByteBuffer getOutputBuffer(int index)

//清除index指向的缓存区中的数据
public final void releaseOutputBuffer(int index, boolean render)

//结束解码或编码会话
public final void stop()

//释放资源
public final void release()

        本文将以全关键帧转换为例,讲解 MediaCodec 的应用。

2 项目目录

【Android】MediaCodec详解[通俗易懂]

3 代码

        在 AndroidManifest.xml 中配置权限,如下:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

        MainActivity.java

package com.example.codec;

import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        String[] permissions = {
                "android.permission.WRITE_EXTERNAL_STORAGE",
                "android.permission.READ_EXTERNAL_STORAGE"};
        if(Build.VERSION.SDK_INT>=23){
            requestPermissions(permissions,1);
        }
    }

    public void onclick(View v) {
        Manager manager = new Manager("/sdcard/Pictures/WeiXin/a.mp4", "/sdcard/a.mp4");
        manager.prepare();
        manager.start();
        manager.transform(); //转换为全关键帧
    }
}

        Manager.java

package com.example.codec;

import android.media.MediaFormat;
import android.os.Handler;
import android.os.Message;

import java.util.LinkedList;

public class Manager {
    private Decoder decoder;
    private Encoder encoder;
    private LinkedList<byte[]> de2EnQue;
    private String input_path;
    private String output_path;

    public static final int DECODE_ONE_FRAME = 1; //decoder解码了1帧
    public static final int DECODER_RELEASE = 2; //decoder释放了资源
    public static final int ENCODE_ONE_FRAME = 3; //encoder编码了1帧
    public static final int ENCODER_RELEASE = 4; //encoder释放了资源

    public Manager(String input_path, String output_path) {
        this.input_path = input_path;
        this.output_path = output_path;
        de2EnQue = new LinkedList<>();
        decoder = new Decoder(mainHandler, de2EnQue);
        encoder = new Encoder(mainHandler, de2EnQue);
    }

    public void prepare() {
        decoder.setPath(input_path);
        encoder.setPath(output_path);
        MediaFormat format = decoder.config();
        encoder.config(format);
    }

    public void start() {
        decoder.start();
        encoder.start();
    }

    public void transform() {
        decoder.decode();
    }

    private Handler mainHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DECODE_ONE_FRAME:
                    encoder.encode(); //通知编码器编码
                    break;
                case DECODER_RELEASE:
                    encoder.release(); //通知编码器释放资源
                    break;
                case ENCODE_ONE_FRAME:
                    break;
                case ENCODER_RELEASE:
                    break;
            }
        }
    };
}

        Decoder.java

package com.example.codec;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;

public class Decoder {
    private MediaCodec decoder;
    private MediaExtractor extractor;
    private MediaFormat format;
    private LinkedList<byte[]> de2EnQue;
    private Handler mainHandler;
    private String input_path;
    private int outputSize;
    private final int timeout = 500;

    public Decoder(Handler mainHandler, LinkedList<byte[]> de2EnQue) {
        this.mainHandler = mainHandler;
        this.de2EnQue = de2EnQue;
        extractor = new MediaExtractor();
    }

    public void setPath(String input_path) {
        this.input_path = input_path;
    }

    public MediaFormat config() {
        try {
            extractor.setDataSource(input_path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String mime = "";
        int count = extractor.getTrackCount(); //获取轨道数
        for (int i = 0; i < count; i++) {
            format = extractor.getTrackFormat(i);
            mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) { // mp4为“video/avc”
                extractor.selectTrack(i);
                break;
            }
        }
        int width = format.getInteger(MediaFormat.KEY_WIDTH);
        int height = format.getInteger(MediaFormat.KEY_HEIGHT);
        outputSize = width*height*3/2;
        try {
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            decoder = MediaCodec.createDecoderByType(mime);
            decoder.configure(format,null,null,0);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return format;
    }

    public void start() {
        decoder.start();
    }

    public void decode() {
        new Thread() {
            @Override
            public void run() {
                execute();
                release();
            }
        }.start();
    }

    public void execute() {
        boolean isInputFinish = false; //输入结束标志
        boolean isOutPutFinish = false; //输出结束标志
        while (!isOutPutFinish) {
            if (!isInputFinish) {
                int inputIndex = decoder.dequeueInputBuffer(timeout);
                if (inputIndex>=0) {
                    ByteBuffer inputBuffer = decoder.getInputBuffer(inputIndex);
                    inputBuffer.clear(); //清除以前数据
                    int sampleSize = extractor.readSampleData(inputBuffer, 0);
                    if (sampleSize>0) {
                        decoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), 0, 0);
                        extractor.advance();
                    } else {
                        isInputFinish = true;
                        decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }
                }
            }
            while (!isOutPutFinish) {
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, timeout);
                if (outputIndex<0) {
                    break;
                }
                if (bufferInfo.flags==MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                    if (bufferInfo.size==0) {
                        if (isInputFinish) {
                            isOutPutFinish = true;
                        }
                        break;
                    }
                    bufferInfo.flags = 0;
                }
                if (bufferInfo.size>0) {
                    ByteBuffer outputBuffer = decoder.getOutputBuffer(outputIndex);
                    byte[] tempBuffer = new byte[outputSize];
                    outputBuffer.get(tempBuffer, 0, outputSize);
                    de2EnQue.addLast(tempBuffer);
                    mainHandler.sendEmptyMessage(Manager.DECODE_ONE_FRAME);
                    decoder.releaseOutputBuffer(outputIndex, false);
                }
            }
        }
    }

    public void release() {
        decoder.stop();
        decoder.release();
        mainHandler.sendEmptyMessage(Manager.DECODER_RELEASE);
    }
}

        Encoder.java

package com.example.codec;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Handler;
import android.os.HandlerThread;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;

public class Encoder {
    private MediaCodec encoder;
    private MediaMuxer muxer;
    private LinkedList<byte[]> de2EnQue;
    private Handler mainHandler;
    private Handler handler;
    private String output_path;
    private int interval = 34483;
    private volatile long pts = 0;
    private final int timeout = 500;
    private int trackIndex;

    public Encoder(Handler mainHandler, LinkedList<byte[]> de2EnQue) {
        this.mainHandler = mainHandler;
        this.de2EnQue = de2EnQue;
    }

    public void setPath(String output_path) {
        this.output_path = output_path;
    }

    public void config(MediaFormat mediaFormat) {
        int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
        int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
        int frame_rate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0); //全I帧
        format.setInteger(MediaFormat.KEY_FRAME_RATE, frame_rate);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 1500000);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
        try {
            encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            encoder.configure(format, null,null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            muxer = new MediaMuxer(output_path,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        encoder.start();
        HandlerThread handlerThread = new HandlerThread("encoder");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
    }

    public void encode() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                execute();
            }
        });
    }

    private void execute() {
        int inputIndex = encoder.dequeueInputBuffer(timeout);
        if (inputIndex>=0) {
            if (!de2EnQue.isEmpty()) {
                ByteBuffer inputBuffer = encoder.getInputBuffer(inputIndex);
                inputBuffer.clear(); //清除以前数据
                byte[] tempBuffer = de2EnQue.removeFirst();
                inputBuffer.put(tempBuffer, 0, tempBuffer.length);
                encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), pts, 0);
                pts += interval;
            }
        }
        while(true) {
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputIndex = encoder.dequeueOutputBuffer(bufferInfo, timeout);
            if (outputIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                MediaFormat outputFormat =  encoder.getOutputFormat();
                trackIndex = muxer.addTrack(outputFormat);
                muxer.start();
                break;
            }
            if (outputIndex<0) {
                break;
            }
            if (bufferInfo.size>0) {
                ByteBuffer outputBuffer = encoder.getOutputBuffer(outputIndex);
                //这里可以通过修改 bufferInfo.presentationTimeUs 实现倍速播放
                muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);
                mainHandler.sendEmptyMessage(Manager.ENCODE_ONE_FRAME);
                encoder.releaseOutputBuffer(outputIndex, false);
            }
        }
    }

    public void release() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                encoder.stop();
                encoder.release();
                handler.getLooper().quit();
                mainHandler.sendEmptyMessage(Manager.ENCODER_RELEASE);
            }
        });
    }
}

4 拓展

        可以通过修改 bufferInfo.presentationTimeUs 实现倍速播放,如下:

float speed = 3.0f; //播放速度
bufferInfo.presentationTimeUs = (long)(bufferInfo.presentationTimeUs/speed)
muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);

        详见 → 使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据 中拓展。

        另外,也可以通过以下方式实现倍速播放视频:

float speed = 3.0f; //播放速度
encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), (long)(pts/speed), 0);

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/25831.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信