此文可用于学习MediaCodec的使用,没有涉及到转码所需的格式修改。
与低配版文件转码 https://blog.csdn.net/h2948203216/article/details/102838871 类似,都是多线程跑的转码。大致流程也没什么修改,代码注释都有写,也就不多说了。
唯一的臭毛病就是都在每一段结束后塞EOF阻塞编解码器,导致需要初始化codec或者重置codec。
替换initXXXCodec的方式,根据MediaCodec的状态转换图,我尝试了flush刷新,无效;尝试了stop,Configure,start,无效。
最终使用了reset,configure,start,成功。
也就是调用如下:
initxxxCodec()
->
xxxcodec.reset();
xxxcodec.configure(,,,);
xxxcodec.start();
如此Codec可以再次使用。
话不多说,上代码
public class TranscodeWrapperDemo3 {
/private //
private List<TailTimer> fileList;
private MediaExtractor extractor,audioExtractor;
private MediaCodec decodec,encodec,audioDecodec,audioEncodec;
private MediaMuxer muxer;
private String filePath;
private AssetFileDescriptor srcFilePath = null;
private AssetFileDescriptor srcFilePath2 = null;
private int isMuxed = 0;
private int audioTrackIndex = -1 ;
private int videoTrackIndex = -1 ;
private int videoIndex = -1;
private int audioIndex = -1;
private boolean isMuxerStarted = false;
private double assignSizeRate = 1.0;
private double durationTotal = 0;
private long TIME_US = 70000L;
private long startTime1 = 0;
private long endTime1 = 0;
private long startTime2 = 0;
private long endTime2 = 0;
private boolean isNeedTailed = false;
private long timeA = 0;
private long timeV = 0;
private long startsTime1 = 0;
// private long startsTime2 = 0;
/**
* @param startTime 视频起始解码时间戳
* @param endTime 视频终止解码时间戳
*/
private void setTailTimeVideo(long startTime,long endTime){
long s = 0;
long e = (long)durationTotal;
startTime *= 1000000;
endTime *= 1000000;
this.startTime1 = startTime > s ? startTime : s;
this.endTime1 = (endTime < e) && (endTime != 0) ? endTime : e;
this.isNeedTailed = (startTime > 0) || (endTime < e);
}
/**
*
* @param startTime 音频起始解码时间戳
* @param endTime 音频终止解码时间戳
*/
private void setTailTimeAudio(long startTime , long endTime){
long s = 0;
long e = (long)durationTotal;
startTime *= 1000000;
endTime *= 1000000;
this.startTime2 = startTime > s ? startTime : s;
this.endTime2 = (endTime < e) && (endTime != 0) ? endTime : e;
this.isNeedTailed = (startTime > 0) || (endTime < e);
}
TranscodeWrapperDemo3(String filePath , List<TailTimer> fileList){
this.filePath = filePath;
this.fileList = fileList;
initMediaMuxer();
initVideoExtractor();
initAudioExtractor();
initVideoDecodec();
initVideoEncodec();
initAudioDecodec();
initAudioEncodec();
}
/**
* pauseAudio 控制音频比视频慢,保证音频线程每一段可以取到同一段视频的基准也就是第一帧关键帧
*/
private boolean pauseAudio = true;
/**
* 以下总是先初始化codec再执行操作的原因是因为,后一段使用codec的时候,因为第一段接受了EOS指令导致codec停止工作,所以需要重置
* 而我这里没有重置,而是新建一个
* 替代方案:
* codec.reset()
* codec.configure(format, null, null, 0|MediaCodec.CONFIGURE_FLAG_ENCODE)
* codec.start()
* 以上方案是根据codec状态得到的解决方案,
* 下面是解释:
* codec(uninitiated) -> codec(configured) -> codec(flushed) -> codec(running) -> codec(EOS) -> codec(released)
* 这是完整的状态,其中存在的闭环:
* codec(running) -> codec(flushed)
* codec(EOS) -> codec(flushed)
* codec(EOS) -> codec(uninitiated) -> codec(configured) -> codec(flushed) -> codec(running) -> codec(EOS)
* 有几个状态转换需要调用的方法,就说比较陌生的地方,比如codec(flushed) 调用了dequeueInputBuffer() 就会进入到codec(running)
* 那running 或 EOS 回到 flushed 就调用flush()即可,不过这里我试过,没用。
* 所以我就用了第三个闭环,EOS 回到 uninitiated 就调用了reset(),然后就是重置codec正常使用。
*/
private Thread inputThread = new Thread(new Runnable() {
@Override
public void run() {
if (fileList != null){
for (int i = 0; i < fileList.size(); i++){
initVideoDecodec();
TailTimer tailTimer = fileList.get(i);
setTailTimeVideo(tailTimer.getStartTime(),tailTimer.getEndTime());
inputLoop();
Log.d("tag","执行到fileListVideo的"+i);
}
}else{
inputLoop();
}
extractor.release();
decodec.stop();
decodec.release();
Log.v("tag","released decode");
}
});
private Thread outputThread = new Thread(new Runnable() {
@Override
public void run() {
if (fileList != null){