MediaCodec完成低配版文件转码(中间不生成文件)

文件转码:demux -> decode -> encode -> mux(解封装->解码->编码->封装)

对了,这里我并没有修改格式,只是走一个流程,中间的格式修改由于涉及很多东西并没有写出来,因此算的上是阉割版的,辅助学习的。

分成两步骤,两渠道。

两步骤:

解封装->解码  inputThread

编码->封装  outputThread

两渠道:自然就是视频和音频了。

设计模式用的建造者,不知道纯不纯。

因此我将转码视为一个主体,视音频转码的线程类作为子对象,因为我在不断的修改转码优化这个转码,后面陆续做了视频裁剪和暂停转码,所以这些多余的就全注释掉了。

package com.example.myapplication3.TranscodeBuildUtils;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaMuxer;

/**
 * Created by weizheng.huang on 2019-10-30.
 */
public class Transcode {
    private InputThread audioInputThread;
    private OutputThread audioOutputThread;
    private InputThread videoInputThread;
    private OutputThread videoOutputThread;

    void setAudioInputThread(MediaExtractor extractor,MediaCodec decodec,MediaCodec encodec,int formatIndex , double totalMS) {
        this.audioInputThread = new InputThread(extractor,decodec,encodec,formatIndex,totalMS);
    }

    void setAudioOutputThread(MediaMuxer muxer,MediaCodec encodec , String MIME ,double totalMS) {
        this.audioOutputThread = new OutputThread(muxer,encodec,MIME,totalMS);
    }

    void setVideoInputThread(MediaExtractor extractor,MediaCodec decodec , MediaCodec encodec,int formatIndex,double totalMS) {
        this.videoInputThread = new InputThread(extractor,decodec,encodec,formatIndex,totalMS);
    }

    void setVideoOutputThread(MediaMuxer muxer, MediaCodec encodec , String MIME , double totalMS) {
        this.videoOutputThread = new OutputThread(muxer,encodec,MIME,totalMS);
    }

    public void setTimeUS(long TIME_US){
        audioInputThread.setTIME_US(TIME_US);
        audioOutputThread.setTIME_US(TIME_US);
        videoInputThread.setTIME_US(TIME_US);
        videoOutputThread.setTIME_US(TIME_US);
    }

//    public void setPauseTranscode(boolean TRUEORFALSE){
//        audioOutputThread.setPauseOutput(TRUEORFALSE);
//        audioInputThread.setPauseInput(TRUEORFALSE);
//        videoInputThread.setPauseInput(TRUEORFALSE);
//        videoOutputThread.setPauseOutput(TRUEORFALSE);
//    }
//
//    public void setTailTime(long startTime ,long endTime){
//        videoInputThread.setTailTime(startTime,endTime);
//        audioInputThread.setTailTime(startTime,endTime);
//    }

    public void start(){
        videoOutputThread.start();
        audioOutputThread.start();
        videoInputThread.start();
        audioInputThread.start();
    }

}

接下来就是对两步骤的讲解:

inputThread: 解封装->解码 

核心方法是inputLoop()

package com.example.myapplication3.TranscodeBuildUtils;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.util.Log;

import androidx.annotation.Nullable;

import java.nio.ByteBuffer;

/**
 * Created by weizheng.huang on 2019-10-30.
 */
class InputThread extends  Thread{

//    private boolean isNeedTailed = false;
//    private boolean pauseInput = false;
    private int formatIndex;
//    private double totalMS;
//    private long startTime = 0;
//    private long endTime = 0;
    private long TIME_US = 50000l;
    private MediaExtractor extractor;
    private MediaCodec decodec;
    private MediaCodec encodec;

    /**
     * 
     * @param extractor  这里都懂吧,就是传入一个解封装器
     * @param decodec    传入解码器
     * @param encodec    传入编码器
     * @param formatIndex   传入解码器获取的渠道
     * @param totalMS     传入播放的总时间 用于视频裁剪可忽略
     */
    public InputThread(@Nullable MediaExtractor extractor , @Nullable  MediaCodec decodec , @Nullable  MediaCodec encodec , int formatIndex ,double totalMS){
        this.extractor = extractor;
        this.decodec = decodec;
        this.encodec = encodec;
        this.formatIndex = formatIndex;
//        this.totalMS = totalMS;
    }

//    public void setPauseInput(boolean pauseInput) {
//        this.pauseInput = pauseInput;
//    }

    public void setTIME_US(long TIME_US) {
        this.TIME_US = TIME_US;
    }

//
//    public void setTailTime(long startTime,long endTime){
//        long s = 0;
//        long e = (long)totalMS;
//        startTime *= 1000000;
//        endTime *= 1000000;
//        this.startTime = startTime > s ? startTime : s;
//
//        this.endTime = (endTime < e) && (endTime != 0) ? endTime : e;
//        this.isNeedTailed = (startTime > 0) && (endTime < e);
//
//    }

    @Override
    public void run() {
        inputLoop();
        extractor.release();
        decodec.stop();
        decodec.release();
        Log.v("tag","released decode");
    }



    private void inputLoop(){
        extractor.selectTrack(formatIndex);
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        boolean closeExtractor = false;
//        if (isNeedTailed){
//            extractor.seekTo(startTime,MediaExtractor.SEEK_TO_CLOSEST_SYNC);
//        }

        while(!Thread.interrupted()){
            if (!closeExtractor){
                int inputIndex = decodec.dequeueInputBuffer(TIME_US);
                if (inputIndex >= 0){
                    ByteBuffer inputBuffer = decodec.getInputBuffer(inputIndex);
                    int size = extractor.readSampleData(inputBuffer,0);
                    if (size < 0){
                        decodec.queueInputBuffer(inputIndex,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        closeExtractor = true;
                    }else{
//                        if (extractor.getSampleTime() > endTime && isNeedTailed){
//                            decodec.queueInputBuffer(inputIndex,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);
//                            closeExtractor = true;
//                        }else{
                            decodec.queueInputBuffer(inputIndex,0,size,extractor.getSampleTime(),extractor.getSampleFlags());
                            extractor.advance();
//                        }
                    }
                }
            }

            int outputIndex = decodec.dequeueOutputBuffer(info,TIME_US);
            if (outputIndex >= 0){
                ByteBuffer outputBuffer = decodec.getOutputBuffer(outputIndex);
                int inputBufferEncodeIndex = encodec.dequeueInputBuffer(TIME_US);
                if (inputBufferEncodeIndex >= 0){
                    if (info.size < 0){
                        encodec.queueInputBuffer(inputBufferEncodeIndex,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }else{
                        ByteBuffer inputEncodeBuffer = encodec.getInputBuffer(inputBufferEncodeIndex);
                        inputEncodeBuffer.put(outputBuffer);
                        encodec.queueInputBuffer(inputBufferEncodeIndex,0,info.size,info.presentationTimeUs,info.flags);
                    }
                }
                decodec.releaseOutputBuffer(outputIndex,false);
            }
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
                Log.d("tag","start release Decode");
                break;
            }
//            while(pauseInput){
//                try {
//                    Thread.sleep(10l);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
        }
    }
}

同样的

OutputThread:编码和封装

核心方法OutputLoop();

package com.example.myapplication3.TranscodeBuildUtils;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;

import androidx.annotation.Nullable;

import java.nio.ByteBuffer;

/**
 * Created by weizheng.huang on 2019-10-30.
 */
class OutputThread extends Thread{
//    private boolean pauseOutput = false;
    private static boolean isMuxerStarted = false;
    private static int videoTrackIndex = -1;
    private static int audioTrackIndex = -1;
    private static int isMuxed = 0;
    private long TIME_US;
//    private double totalMS = 0;
    private String MIME;
    private MediaCodec encodec;
    private static MediaMuxer muxer;
//    private ProgressBarDialog.MyHandler handler = ProgressBarDialog.getHandler();

    /**
     * 
     * @param muxer   新鲜的封装器
     * @param encodec  编码器
     * @param MIME     对应渠道的类型
     * @param totalMS  播放总时间
     */
    public OutputThread(@Nullable MediaMuxer muxer , @Nullable MediaCodec encodec ,@Nullable String MIME , double totalMS){
        this.muxer = muxer;
        this.encodec = encodec;
        this.MIME = MIME;
//        this.totalMS = totalMS;
    }

//    public void setPauseOutput(boolean pauseOutput) {
//        this.pauseOutput = pauseOutput;
//    }

    public void setTIME_US(long TIME_US) {
        this.TIME_US = TIME_US;
    }


    @Override
    public void run() {
        outputLoop();
        encodec.stop();
        encodec.release();
        Log.v("tag","released encodec" + MIME);
        isMuxed++;
        releaseMuxer();
    }


    private void outputLoop(){
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        while(!Thread.interrupted()) {
            int outputIndex = encodec.dequeueOutputBuffer(info, TIME_US);
            switch (outputIndex){
                case MediaCodec.INFO_TRY_AGAIN_LATER:{
                  //  Log.d("tag","try again Later");
                    break;
                }
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                {
                    Log.d("tag","format changed " +MIME);
                    MediaFormat format = encodec.getOutputFormat();
                    MIME = format.getString(MediaFormat.KEY_MIME);
                    if (videoTrackIndex < 0 && MIME.startsWith("video"))
                        videoTrackIndex = muxer.addTrack(format);
                    if (audioTrackIndex < 0 && MIME.startsWith("audio"))
                        audioTrackIndex = muxer.addTrack(format);

                    break;
                }
                default:{
                    ByteBuffer outputBuffer = encodec.getOutputBuffer(outputIndex);
                    if (!isMuxerStarted){
                        startMuxer();
                    }
                    if (info.size >= 0 && isMuxerStarted){
//                        if (0 < info.presentationTimeUs){
//                            double currentMS = info.presentationTimeUs;
//                            Bundle bundle = new Bundle();
//                            if (MIME.startsWith("video")){
//                                bundle.putString("videoProgress",new DecimalFormat(".0").format((currentMS / totalMS) * 100));
//                            }
//                            if (MIME.startsWith("audio")){
//                                bundle.putString("audioProgress",new DecimalFormat(".0").format((currentMS / totalMS) * 100));
//                            }
//                            Message message = new Message();
//                            message.setData(bundle);
//                            message.setTarget(handler);
//                            handler.sendMessage(message);
//                        }
                        int trackIndex = MIME.startsWith("video") ? videoTrackIndex : audioTrackIndex;
                        muxer.writeSampleData(trackIndex , outputBuffer , info);
                        Log.d("tag",MIME + " muxing");
                    }
                    encodec.releaseOutputBuffer(outputIndex,false);
                }
            }
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
                Log.d("tag","start release " + MIME + " Encode");
                break;
            }
//            while(pauseOutput){
//                try {
//                    Thread.sleep(10l);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
        }
    }

    private static synchronized void startMuxer(){
        if ((0 <= audioTrackIndex ) && (0 <= videoTrackIndex ) && (!isMuxerStarted)){
            muxer.start();
            isMuxerStarted = true;
        }
    }
    private static synchronized void releaseMuxer(){
        if (isMuxed == 2){
            isMuxed++;
            muxer.stop();
            muxer.release();
            Log.v("tag","released muxer");
        }
    }
}

建造者模式:Builder是不可少滴

package com.example.myapplication3.TranscodeBuildUtils;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaMuxer;

import androidx.annotation.Nullable;

/**
 * Created by weizheng.huang on 2019-10-30.
 */
public class TranscodeBuilder {
    private Transcode transcode = new Transcode();

    public void buildVideoInputThread(@Nullable MediaExtractor extractor , @Nullable MediaCodec decodec , @Nullable  MediaCodec encodec , int formatIndex , double totalMS){
        transcode.setVideoInputThread(extractor, decodec, encodec, formatIndex, totalMS);
    }
    public void buildAudioInputThread(@Nullable MediaExtractor extractor , @Nullable  MediaCodec decodec , @Nullable  MediaCodec encodec , int formatIndex ,double totalMS){
        transcode.setAudioInputThread(extractor, decodec, encodec, formatIndex, totalMS);
    }
    public void buildVideoOutputThread(@Nullable MediaMuxer muxer , @Nullable MediaCodec encodec , @Nullable String MIME , double totalMS){
        transcode.setVideoOutputThread(muxer, encodec, MIME, totalMS);
    }
    public void buildAudioOutputThread(@Nullable MediaMuxer muxer , @Nullable MediaCodec encodec ,@Nullable String MIME , double totalMS){
        transcode.setAudioOutputThread(muxer, encodec, MIME, totalMS);
    }
    public void setTIMEUS(long TIME_US){
        transcode.setTimeUS(TIME_US);
    }

    public Transcode build(){
        return transcode;
    }
}

接下来基础都做完了,可以用了

package com.example.myapplication3;

import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;

import com.example.myapplication3.TranscodeBuildUtils.Transcode;
import com.example.myapplication3.TranscodeBuildUtils.TranscodeBuilder;

import java.io.IOException;

/**
 * Created by weizheng.huang on 2019-10-30.
 */
public class TranscodeWrapperDemo2 {
    private int audioIndex = -1;
    private int videoIndex = -1;
    private double assignSizeRate = 1;
    private double durationTotal = 0;
    private String audioFormatType;
    private String videoFormatType;
    private String filePath;
    private AssetFileDescriptor srcFilePath;
    private AssetFileDescriptor srcFilePath2;
    private MediaExtractor extractor,audioExtractor;
    private MediaCodec decodec,encodec;
    private MediaCodec audioDecodec,audioEncodec;
    private MediaMuxer muxer;

    private Transcode transcode;



    ///public

    public void setAssignSize(double assignSizeRate) {
        this.assignSizeRate = assignSizeRate;
    }

    public void setPauseTranscode(boolean pauseTranscode) {
        transcode.setPauseTranscode(pauseTranscode);
    }
    public void setTailTime(long startTime ,long endTime){
        transcode.setTailTime(startTime,endTime);
    }

    public TranscodeWrapperDemo2(String filePath, AssetFileDescriptor srcFilePath, AssetFileDescriptor srcFilePath2) {
        this.filePath = filePath;
        this.srcFilePath = srcFilePath;
        this.srcFilePath2 = srcFilePath2;
    }


    public boolean startTranscode(){
        transcode.start();
        return true;
    }
    public  void init(){
        initMediaExtractor();
        initMediaCodec();
        initMediaMuxer();
        initTranscode();
    }

    ///private //

    private void initTranscode(){
        TranscodeBuilder transcodeBuilder = new TranscodeBuilder();
        transcodeBuilder.buildAudioInputThread(audioExtractor,audioDecodec,audioEncodec,audioIndex,durationTotal);
        transcodeBuilder.buildVideoInputThread(extractor,decodec,encodec,videoIndex,durationTotal);
        transcodeBuilder.buildAudioOutputThread(muxer,audioEncodec,audioFormatType,durationTotal);
        transcodeBuilder.buildVideoOutputThread(muxer,encodec,videoFormatType,durationTotal);
        transcodeBuilder.setTIMEUS(50000l);
        transcode = transcodeBuilder.build();
    }
    private void initMediaExtractor(){
        extractor = new MediaExtractor();
        try {
            extractor.setDataSource(srcFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        audioExtractor = new MediaExtractor();
        try {
            audioExtractor.setDataSource(srcFilePath2);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private int frameRate;
    private int bitRate;
    private int width;
    private int height;
    private int audioBitRate;
    private int sampleRate;
    private int channelCount;

    private void initMediaCodec(){


        decode///
        for (int i = 0; i < extractor.getTrackCount(); i++){
            MediaFormat format = extractor.getTrackFormat(i);
            String formatType = format.getString(MediaFormat.KEY_MIME);

            if (formatType.startsWith("video")){
                videoIndex = i;
                try {
                    decodec = MediaCodec.createDecoderByType(formatType);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                videoFormatType = formatType;
                frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
                bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
                width = format.getInteger(MediaFormat.KEY_WIDTH);
                height = format.getInteger(MediaFormat.KEY_HEIGHT);
                durationTotal = format.getLong(MediaFormat.KEY_DURATION);
                decodec.configure(format,null,null,0);
                decodec.start();
                continue;
            }
            if (formatType.startsWith("audio")){
                audioIndex = i;
                try {
                    audioDecodec = MediaCodec.createDecoderByType(formatType);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                audioFormatType = formatType;
                audioBitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
                sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                audioDecodec.configure(format,null,null,0);
                audioDecodec.start();
            }
        }


        MediaFormat videoFormat = MediaFormat.createVideoFormat(videoFormatType, width, height);

        videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
        videoFormat.setInteger(MediaFormat.KEY_BIT_RATE,(int)(bitRate * assignSizeRate ));
        videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
        videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,frameRate * 2);

        MediaFormat audioFormat = MediaFormat.createAudioFormat(audioFormatType, sampleRate, channelCount);
        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE,(int)(audioBitRate * assignSizeRate ));




        try {
            encodec = MediaCodec.createEncoderByType(videoFormatType);
        } catch (IOException e) {
            e.printStackTrace();
        }
        encodec.configure(videoFormat, null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
        encodec.start();

        try {
            audioEncodec = MediaCodec.createEncoderByType(audioFormatType);
        } catch (IOException e) {
            e.printStackTrace();
        }
        audioEncodec.configure(audioFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
        audioEncodec.start();
    }
    private void initMediaMuxer(){

        try {
            muxer = new MediaMuxer(filePath,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

最后总结:最好浏览完所有代码再来看,就知道我总结的是哪里了。😊

1.TIME_US的设置问题

在修改这个值的时候,我换过-1、0、100000l等等,发现-1会导致线程阻塞,也许是因为一旦出现申请不到的操作的话,一直等待会直接阻塞某一线程,但是呢输入和输出必须挂钩,一旦有一者阻塞了,另一者势必因为缓冲区无法清除或无法申请而挂掉不能塞数据从而抛出IllegalArgumentException。我换成大于0的时候,那么必然意味着丢帧,于是一点点调整,发现50000l以上基本没变化了(其实不过是作者懒得往细调了,直接50000l,可能小一点的丢帧也相差不大,这些就是随心了)

2.为什么要用两个extractor和两个视频文件

看到inputLoop没,我selectTrack也放在里边了,两个线程不能只selectTrack一个吧,另一个总会覆盖掉的。

所以用两个才是明智的选择。然后就是这里还会有问题,不过我就不贴出来了,初始化有点多。extractor我setdatasource这里用的是两个视频文件,一份原始视频文件,另一份则是原始文件的拷贝文件。为什么这么做的原因是因为,如果两个extractor同时setdatasource设置一个视频文件,读取时会抛出read on track x error with x ,这里就是读取时容易互相产生干扰,对于这里作者也是头疼的狠,暂时无法想出更好的方法。

3.关于format取渠道时,设置encodec、muxer的问题

关于videoEncodec,这里的format一定得保证width、height、formatType、colorFormat、frameRate、IFrameInteval这几个属性要设置好。audioEncodec的话就是sampleRate、channelCount、formatType。bitRate都是可选的

muxer这里就有点意思了,网上找的基本都是瞎鸡儿设置,按官网写的配在outputIndex这里,当outputIndex为MediaCodec.INFO_OUTPUT_FORMAT_CHANGED的时候,这里添加渠道就行了。为什么呢,因为编码器启动后,第一帧(没数据的)或者说OutputIndex第一次取一定会是这个值,如果不是那就肯定编码器失误了。

4.将解码输出和编码输入放在一起确实方便,就是不用生成文件了很舒服。

5.MainActivity我就不给了,提供的几个public方法依次调用就行了。

6.新增此文大部分使用还是正确,但是已经不合适了建议往后看后更新的转码demo。

https://blog.csdn.net/h2948203216/article/details/103694508

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里有一个简单的MediaCodec C++例子,可以将MP4文件解码为YUV格式: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <media/NdkMediaCodec.h> #include <media/NdkMediaExtractor.h> #include <media/NdkMediaFormat.h> #define TIMEOUT_US 10000 int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "Usage: %s <input.mp4> <output.yuv>\n", argv[0]); return -1; } // 创建MediaExtractor对象 AMediaExtractor *extractor = AMediaExtractor_new(); if (!extractor) { fprintf(stderr, "Failed to create MediaExtractor\n"); return -1; } // 设置数据源 if (AMediaExtractor_setDataSource(extractor, argv[1]) != AMEDIA_OK) { fprintf(stderr, "Failed to set data source '%s'\n", argv[1]); return -1; } // 获取视频轨道 AMediaFormat *format = NULL; int trackIndex = -1; for (int i = 0; i < AMediaExtractor_getTrackCount(extractor); i++) { format = AMediaExtractor_getTrackFormat(extractor, i); const char *mime; AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime); if (!strncmp(mime, "video/", 6)) { trackIndex = i; break; } } if (trackIndex == -1) { fprintf(stderr, "No video track found\n"); return -1; } // 选择视频轨道 AMediaExtractor_selectTrack(extractor, trackIndex); // 创建MediaCodec对象 AMediaCodec *decoder = AMediaCodec_createDecoderByType(AMediaFormat_toString(format)); if (!decoder) { fprintf(stderr, "Failed to create decoder for '%s'\n", AMediaFormat_toString(format)); return -1; } // 配置MediaCodec对象 AMediaCodec_configure(decoder, format, NULL, NULL, 0); // 启动MediaCodec对象 AMediaCodec_start(decoder); // 创建输出文件 FILE *fpOut = fopen(argv[2], "wb"); if (!fpOut) { fprintf(stderr, "Failed to create output file '%s'\n", argv[2]); return -1; } // 解码视频数据 ssize_t sampleSize = 0; uint8_t *inputBuffer = NULL; size_t inputBufferSize = 0; AMediaCodecBufferInfo inputBufferInfo; memset(&inputBufferInfo, 0, sizeof(inputBufferInfo)); bool sawInputEOS = false; bool sawOutputEOS = false; while (!sawOutputEOS) { // 从Extractor中获取输入数据 if (!sawInputEOS) { int inputBufferIndex = AMediaCodec_dequeueInputBuffer(decoder, TIMEOUT_US); if (inputBufferIndex >= 0) { inputBuffer = AMediaCodec_getInputBuffer(decoder, inputBufferIndex, &inputBufferSize); sampleSize = AMediaExtractor_readSampleData(extractor, inputBuffer, inputBufferSize); if (sampleSize < 0) { // 输入结束,发送EOS标志 AMediaCodec_queueInputBuffer(decoder, inputBufferIndex, 0, 0, 0, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); sawInputEOS = true; } else { // 将数据提交给MediaCodec对象进行解码 AMediaCodec_queueInputBuffer(decoder, inputBufferIndex, 0, sampleSize, AMediaExtractor_getSampleTime(extractor), 0); AMediaExtractor_advance(extractor); } } } // 从MediaCodec中获取输出数据 AMediaCodecBufferInfo outputBufferInfo; uint8_t *outputBuffer = AMediaCodec_getOutputBuffer(decoder, &outputBufferInfo, TIMEOUT_US); if (outputBuffer) { // 将YUV数据保存到文件中 fwrite(outputBuffer, 1, outputBufferInfo.size, fpOut); // 释放输出缓冲区 AMediaCodec_releaseOutputBuffer(decoder, AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED, false); if ((outputBufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) { sawOutputEOS = true; } } else if (outputBuffer == NULL && sawInputEOS) { break; } } // 关闭输出文件 fclose(fpOut); // 停止并释放MediaCodec对象 AMediaCodec_stop(decoder); AMediaCodec_delete(decoder); // 释放MediaExtractor对象 AMediaExtractor_delete(extractor); return 0; } ``` 请注意,此示例仅用于演示如何使用MediaCodec C++将MP4文件解码为YUV格式。实际应用中,您需要根据您的具体需求进行适当的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值