Android中使用MediaExtractor+MediaCodec+MediaMuxer实现将本地视频解码截取再和另外的视频进行拼接编码合成一个视频,主要有两个类。

下面是解码相关的类VideoDecoder.java

package com.audiovideo.camera.decoder;import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.Toast;import com.audiovideo.camera.MyApplication;
import com.audiovideo.camera.constant.Constants;import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;/*** This class use for Decode Video Frame Data and show to SurfaceTexture* Created by fenghaitao on 2019年10月10日09:28:15*/
public class VideoDecoder {private final static String TAG = "VideoEncoder";private final static int CONFIGURE_FLAG_DECODE = 0;private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Videoprivate MediaCodec mMediaCodec;private MediaFormat mMediaFormat;private Handler mVideoDecoderHandler;private HandlerThread mVideoDecoderHandlerThread = new HandlerThread("VideoDecoder");private MediaExtractor videoExtractor = null;private VideoEncoderFromBuffer videoEncoder = null;private int mPreviewWidth = 640;private int mPreviewHeight = 480;private long duration;private Date startTime;private boolean flag;  //是否需要合并视频private boolean hasMerge = true;  //是否为第二段视频private String input_path1;private MediaCodec.Callback mCallback = new MediaCodec.Callback() {@Overridepublic void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int id) {ByteBuffer inputBuffer = mediaCodec.getInputBuffer(id);inputBuffer.clear();int samplesize = videoExtractor.readSampleData(inputBuffer,0);if (samplesize < 0) {mediaCodec.queueInputBuffer(id,0, 0,0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);//需要合并视频if (flag) {hasMerge = false;// 覆盖新的分离器videoExtractor.release();videoExtractor = getVideoExtractor(input_path1,0);// 覆盖新的解码器mMediaCodec.stop();mMediaCodec.release();try {mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);} catch (IOException e) {e.printStackTrace();}//开始解码startDecoder();}}else{Log.e("fht","SampleTime 为:" + videoExtractor.getSampleTime());if (hasMerge) { //判断是否为第二段视频,TRUE为第一段视频mediaCodec.queueInputBuffer(id,0, samplesize,videoExtractor.getSampleTime(),0);videoExtractor.advance();}else{//只取视频前8秒,大于8秒则结束if (videoExtractor.getSampleTime() > 6 * 1000000) {mediaCodec.queueInputBuffer(id,0, 0,0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);}else{mediaCodec.queueInputBuffer(id,0, samplesize,videoExtractor.getSampleTime(),0);videoExtractor.advance();}}}}@Overridepublic void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int id, @NonNull MediaCodec.BufferInfo bufferInfo) {ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(id);MediaFormat outputFormat = mMediaCodec.getOutputFormat(id);if(outputBuffer != null && bufferInfo.size > 0){byte [] buffer = new byte[outputBuffer.remaining()];outputBuffer.get(buffer);videoEncoder.encodeFrame(buffer);mMediaCodec.releaseOutputBuffer(id, false);}else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Date endDate = new Date(System.currentTimeMillis());long diff = (endDate.getTime() - startTime.getTime())/1000;Log.e("fht","编解码完成!用时:" + diff + "秒");Toast.makeText(MyApplication.getContext(),"编解码完成!用时:" + diff + "秒", Toast.LENGTH_LONG).show();release();}}@Overridepublic void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {Log.d(TAG, "------> onError");}@Overridepublic void onOutputFormatChanged(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat) {Log.d(TAG, "------> onOutputFormatChanged");}};/*** * @param input_path  第一个视频路径* @param input_path1  需要拼接的视频路径* @param mimeType  编码格式* @param time  第一个视频开始截取的时间点* @param flag  是否有合并的视频*/public VideoDecoder(String input_path ,String input_path1 , String mimeType, int time, boolean flag){this.flag = flag;this.input_path1 = input_path1;try {getPlayTime(input_path);mMediaCodec = MediaCodec.createDecoderByType(mimeType);if (videoEncoder == null) {videoEncoder = new VideoEncoderFromBuffer(mPreviewWidth, mPreviewHeight);}} catch (IOException e) {Log.e(TAG, Log.getStackTraceString(e));mMediaCodec = null;return;}mVideoDecoderHandlerThread.start();mVideoDecoderHandler = new Handler(mVideoDecoderHandlerThread.getLooper());videoExtractor = getVideoExtractor(input_path,time);}//初始化分离器MediaExtractor,并分离文件视频信道private MediaExtractor getVideoExtractor(String input_path, int time){videoExtractor = new MediaExtractor();try {videoExtractor.setDataSource(input_path);} catch (IOException e) {e.printStackTrace();}int videoTrackIndex;//获取视频所在轨道videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");if (videoTrackIndex >= 0) {MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);mMediaFormat = videoExtractor.getTrackFormat(videoTrackIndex);
//            mMediaFormat = MediaFormat.createVideoFormat(mimeType, mViewWidth, mViewHeight);
//            mMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, videoEncoder.selectColorFormat(codecInfo,MIME_TYPE));mMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar);mMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mPreviewWidth*mPreviewHeight);mMediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);mMediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);videoExtractor.selectTrack(videoTrackIndex);//截取该段视频n秒之后的视频if (time > 0 && (time * 1000000)<duration ) { //判断开始截取的时间是否超过视总时长videoExtractor.seekTo(time * 1000000, MediaExtractor.SEEK_TO_CLOSEST_SYNC);}}return videoExtractor;}//获取视频分辨率和时长private void  getPlayTime(String mUri) {android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();try {if (mUri != null) {HashMap<String, String> headers = null;if (headers == null) {headers = new HashMap<String, String>();headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 4.4.2; zh-CN; MW-KW-001 Build/JRO03C) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/1.0.0.001 U4/0.8.0 Mobile Safari/533.1");}mmr.setDataSource(mUri);} else {//mmr.setDataSource(mFD, mOffset, mLength);}//            duration = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_DURATION);//时长(毫秒)mPreviewWidth = Integer.parseInt(mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));//宽mPreviewHeight = Integer.parseInt(mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));//高} catch (Exception ex) {Log.e("TAG", "MediaMetadataRetriever exception " + ex);} finally {mmr.release();}}//获取指定类型媒体文件所在轨道private int getMediaTrackIndex(MediaExtractor videoExtractor, String MEDIA_TYPE) {int trackIndex = -1;for (int i = 0; i < videoExtractor.getTrackCount(); i++) {MediaFormat mediaFormat = videoExtractor.getTrackFormat(i);
//            mPreviewWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
//            mPreviewHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);Log.e("fht","mPreviewWidth:" + mPreviewWidth + "; mPreviewHeight:" + mPreviewHeight + "; duration:" + duration);String mime = mediaFormat.getString(MediaFormat.KEY_MIME);if (mime.startsWith(MEDIA_TYPE)) {trackIndex = i;break;}}return trackIndex;}public void startDecoder(){if(mMediaCodec != null){mMediaCodec.setCallback(mCallback, mVideoDecoderHandler);mMediaCodec.configure(mMediaFormat, null,null,CONFIGURE_FLAG_DECODE);mMediaCodec.start();if (hasMerge) {startTime = new Date(System.currentTimeMillis());}}else{throw new IllegalArgumentException("startDecoder failed, please check the MediaCodec is init correct");}}public void stopDecoder(){if(mMediaCodec != null){mMediaCodec.stop();}}/*** release all resource that used in Encoder*/public void release(){if(mMediaCodec != null){mMediaCodec.stop();mMediaCodec.release();mMediaCodec = null;}if (videoExtractor != null) {videoExtractor.release();videoExtractor = null;}if (mVideoDecoderHandler != null) {mVideoDecoderHandler = null;}if (videoEncoder != null) {videoEncoder.close();}}/*** Returns the first codec capable of encoding the specified MIME type, or* null if no match was found.*/private static MediaCodecInfo selectCodec(String mimeType) {int numCodecs = MediaCodecList.getCodecCount();for (int i = 0; i < numCodecs; i++) {MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);if (!codecInfo.isEncoder()) {continue;}String[] types = codecInfo.getSupportedTypes();for (int j = 0; j < types.length; j++) {if (types[j].equalsIgnoreCase(mimeType)) {return codecInfo;}}}return null;}
}

下面是将解码数据编码合成MP4文件的类 VideoEncoderFromBuffer.java

import android.annotation.SuppressLint;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;import com.audiovideo.camera.constant.Constants;import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;/*** 将YUV数据编码合成MP4文件* create by  fenghaitao 2019年10月10日09:29:37*/public class VideoEncoderFromBuffer {private static final String TAG = "VideoEncoderFromBuffer";private static final boolean VERBOSE = true; // lots of logging// parameters for the encoderprivate static final String MIME_TYPE = "video/avc"; // H.264 Advanced Videoprivate static final int FRAME_RATE = 25; // 15fpsprivate static final int IFRAME_INTERVAL = 10; // 10 between// I-framesprivate static final int TIMEOUT_USEC = 10000;private static final int COMPRESS_RATIO = 256;private int mWidth;private int mHeight;private MediaCodec mMediaCodec;private MediaMuxer mMuxer;private BufferInfo mBufferInfo;private int mTrackIndex = -1;private boolean mMuxerStarted;byte[] mFrameData;private int mColorFormat;private long mStartTime = 0;@SuppressLint("NewApi")public VideoEncoderFromBuffer(int width, int height) {Log.i(TAG, "VideoEncoder()");this.mWidth = width;this.mHeight = height;mFrameData = new byte[this.mWidth * this.mHeight * 3 / 2];mBufferInfo = new BufferInfo();MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);if (codecInfo == null) {// Don't fail CTS if they don't have an AVC codec (not here,// anyway).Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);return;}if (VERBOSE)Log.d(TAG, "found codec: " + codecInfo.getName());mColorFormat = selectColorFormat(codecInfo, MIME_TYPE);if (VERBOSE)Log.d(TAG, "found colorFormat: " + mColorFormat);MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,this.mWidth, this.mHeight);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,IFRAME_INTERVAL);if (VERBOSE)Log.d(TAG, "format: " + mediaFormat);try {mMediaCodec = MediaCodec.createByCodecName(codecInfo.getName());} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}mMediaCodec.configure(mediaFormat, null, null,MediaCodec.CONFIGURE_FLAG_ENCODE);mMediaCodec.start();String fileName = Constants.output3 + new Random().nextInt() + ".mp4";
//    File f = new File(fileName);
//    boolean s = false;
//    if(!f.exists())
//       try {
//          s = f.createNewFile();
//       } catch (IOException e) {
//          // TODO Auto-generated catch block
//          e.printStackTrace();
//       }Log.i(TAG, "videofile: " + fileName);mStartTime = System.nanoTime();// Create a MediaMuxer.  We can't add the video track and start() the muxer here,// because our MediaFormat doesn't have the Magic Goodies.  These can only be// obtained from the encoder after it has started processing data.//// We're not actually interested in multiplexing audio.  We just want to convert// the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.try {mMuxer = new MediaMuxer(fileName,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);} catch (IOException ioe) {throw new RuntimeException("MediaMuxer creation failed", ioe);}mTrackIndex = -1;mMuxerStarted = false;}public void encodeFrame(byte[] input/* , byte[] output */) {Log.i(TAG, "encodeFrame()");
//    Log.e("fht", "数据大小为:" + input.length);long encodedSize = 0;NV21toI420SemiPlanar(input, mFrameData, this.mWidth, this.mHeight);ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);if (VERBOSE)Log.i(TAG, "inputBufferIndex-->" + inputBufferIndex);if (inputBufferIndex >= 0) {long endTime = System.nanoTime();long ptsUsec = (endTime - mStartTime) / 1000;Log.i(TAG, "resentationTime: " + ptsUsec);ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];inputBuffer.clear();inputBuffer.put(mFrameData);mMediaCodec.queueInputBuffer(inputBufferIndex, 0,mFrameData.length, getPTSUs(), 0);} else {// either all in use, or we timed out during initial setupif (VERBOSE)Log.d(TAG, "input buffer not available");}int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);Log.i(TAG, "outputBufferIndex-->" + outputBufferIndex);do {if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {// no output available yetif (VERBOSE)Log.d(TAG, "no output from encoder available");} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {// not expected for an encoderoutputBuffers = mMediaCodec.getOutputBuffers();if (VERBOSE)Log.d(TAG, "encoder output buffers changed");} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// not expected for an encoderMediaFormat newFormat = mMediaCodec.getOutputFormat();Log.d(TAG, "encoder output format changed: " + newFormat);// now that we have the Magic Goodies, start the muxermTrackIndex = mMuxer.addTrack(newFormat);mMuxer.start();mMuxerStarted = true;} else if (outputBufferIndex < 0) {Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +outputBufferIndex);// let's ignore it} else {if (VERBOSE)Log.d(TAG, "perform encoding");ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];if (outputBuffer == null) {throw new RuntimeException("encoderOutputBuffer " + outputBufferIndex +" was null");}if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// The codec config data was pulled out and fed to the muxer when we got// the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");mBufferInfo.size = 0;}if (mBufferInfo.size != 0) {if (!mMuxerStarted) {
//                throw new RuntimeException("muxer hasn't started");MediaFormat newFormat = mMediaCodec.getOutputFormat();mTrackIndex = mMuxer.addTrack(newFormat);mMuxer.start();mMuxerStarted = true;}// adjust the ByteBuffer values to match BufferInfo (not needed?)outputBuffer.position(mBufferInfo.offset);outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);mMuxer.writeSampleData(mTrackIndex, outputBuffer, mBufferInfo);if (VERBOSE) {Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer");}}mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);}outputBufferIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);} while (outputBufferIndex >= 0);}@SuppressLint("NewApi")public void close() {Log.i(TAG, "close()");try {if (mMediaCodec != null) {mMediaCodec.stop();mMediaCodec.release();mMediaCodec = null;}} catch (Exception e) {e.printStackTrace();}if (mMuxer != null) {// TODO: stop() throws an exception if you haven't fed it any data.  Keep track//       of frames submitted, and don't call stop() if we haven't written anything.mMuxer.stop();mMuxer.release();mMuxer = null;}}/*** NV21 is a 4:2:0 YCbCr, For 1 NV21 pixel: YYYYYYYY VUVU I420YUVSemiPlanar* is a 4:2:0 YUV, For a single I420 pixel: YYYYYYYY UVUV Apply NV21 to* I420YUVSemiPlanar(NV12) Refer to https://wiki.videolan.org/YUV/*/private void NV21toI420SemiPlanar(byte[] nv21bytes, byte[] i420bytes,int width, int height) {int totle = width * height; //Y数据的长度int nLen = totle / 4;  //U、V数据的长度System.arraycopy(nv21bytes, 0, i420bytes, 0, totle);for (int i = 0; i < nLen; i++) {i420bytes[totle + i] = nv21bytes[totle + 2 * i];i420bytes[totle + nLen + i] = nv21bytes[totle + 2 * i + 1];}}/*** Returns a color format that is supported by the codec and by this test* code. If no match is found, this throws a test failure -- the set of* formats known to the test should be expanded for new platforms.*/public static int selectColorFormat(MediaCodecInfo codecInfo,String mimeType) {MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);for (int i = 0; i < capabilities.colorFormats.length; i++) {int colorFormat = capabilities.colorFormats[i];if (isRecognizedFormat(colorFormat)) {return colorFormat;}}Log.e(TAG,"couldn't find a good color format for " + codecInfo.getName()+ " / " + mimeType);return 0; // not reached}/*** Returns true if this is a color format that this test code understands* (i.e. we know how to read and generate frames in this format).*/private static boolean isRecognizedFormat(int colorFormat) {switch (colorFormat) {// these are the formats we know how to handle for this testcase MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:return true;default:return false;}}/*** Returns the first codec capable of encoding the specified MIME type, or* null if no match was found.*/private static MediaCodecInfo selectCodec(String mimeType) {int numCodecs = MediaCodecList.getCodecCount();for (int i = 0; i < numCodecs; i++) {MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);if (!codecInfo.isEncoder()) {continue;}String[] types = codecInfo.getSupportedTypes();for (int j = 0; j < types.length; j++) {if (types[j].equalsIgnoreCase(mimeType)) {return codecInfo;}}}return null;}/*** Generates the presentation time for frame N, in microseconds.*/private static long computePresentationTime(int frameIndex) {return 132 + frameIndex * 1000000 / FRAME_RATE;}/*** Returns true if the specified color format is semi-planar YUV. Throws an* exception if the color format is not recognized (e.g. not YUV).*/private static boolean isSemiPlanarYUV(int colorFormat) {switch (colorFormat) {case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:return false;case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:return true;default:throw new RuntimeException("unknown format " + colorFormat);}}/*** 上一个用于书写的演示时间*/private long prevOutputPTSUs = 0;/*** 获取下一个presentationTimeUs* @return*/protected long getPTSUs() {long result = System.nanoTime() / 1000L;// presentationTimeUs 应该是单调的// 否则muxer无法写入if (result < prevOutputPTSUs)result = (prevOutputPTSUs - result) + result;return result;}private static final float BPP = 0.25f;private int calcBitRate() {final int bitrate = (int)(BPP * 25 * mWidth * mHeight);return bitrate;}
}

调用方法也很简单,如下:

VideoDecoder mVideoDecoder = new VideoDecoder(Constants.test_path, MediaFormat.MIMETYPE_VIDEO_AVC,0,false);
mVideoDecoder.startDecoder();

到时候注意把里面的视频文件路径替换一下就可以了

使用MediaExtractor+MediaCodec+MediaMuxer实现视频截取和拼接相关推荐

  1. 【Android】使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据

    1 简介 本文以 mp4 文件为例,讲解去音频操作.mp4 是一种视频封装的容器,里面包含音频(audio)和视频(video)数据,对应的数据编码格式分别为 aac 和 h264.在去音频过程中,主 ...

  2. Android音视频API - MediaCodec/MediaMuxer/MediaStore/MediaController等

    AudioTrack播放音频PCM.[Android] 混音器AudioMixer. MediaPlayer/MediaRecorder, AudioTrack/AudioRecorder, Medi ...

  3. Android音视频开发(三)——MediaExtractor和MediaMuxer的使用

    了解了音视频的编解码过程,我们接下来使用一下经常跟MediaCodec一起搭配的MediaExtractor和MediaMuxer.最后会使用一个简单的demo来了解具体了解这两个工具类的使用过程.这 ...

  4. 使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件

    一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor.MediaMuxer把音频或视频给单独抽取出来,抽取出来的音频和视频能单独播放: 一.MediaExtractor API ...

  5. 使用MediaMuxer裁剪视频

    MediaPlayer的使用 使用MediaMuxer裁剪视频 1.解封装,获取不同tracks上的format 2.配置MediaMuxer 3.向MediaMuxer中写入数据 4.释放资源 使用 ...

  6. Android直播开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)

    Android直播开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer) (码字不易,转载请声明出处:http://blog.csdn.net/andrexp ...

  7. ffmpeg开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)

    ffmpeg开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer) (原文链接:http://blog.csdn.net/andrexpert/article ...

  8. Android音频学习之MediaExtractor,提取音频视频轨道数据(从视频中分离音频视频数据)

    一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor.MediaMuxer把音频或视频给单独抽取出来,抽取出来的音频和视频能单独播放: 1 MediaExtractor 说明 ...

  9. MediaCodec在Android视频硬解码组件的应用

    https://yq.aliyun.com/articles/632892 云栖社区> 博客列表> 正文 MediaCodec在Android视频硬解码组件的应用 cheenc 2018- ...

最新文章

  1. JSONObject和JSONArray的关系
  2. Perforce使用指南_forP4V
  3. 美国下注15亿美元重点搞芯片!电子复兴5年计划首批入围项目曝光
  4. sklearn机器学习之特征工程
  5. VMware ESXi虚拟机安装Windows7
  6. 计算机实验excel总结,EXCEL实验报告
  7. 什么是有监督学习?看这里。
  8. ens2sym <- select(org.Hs.eg.db, keys=row.names(pc9_removebatch_b12),columns=c(“ENTREZID“,“SYMBOL“),
  9. 中国农业大学计算机考研拟录取名单,2019中国农业大学硕士考研研究生拟录取名单...
  10. xiaoxin juju needs help - 组合公式
  11. 华为开发者联盟Severless解决方案为鸿蒙生态构建发力
  12. python基础之实现max函数
  13. 学废了吗?2022年我的GTD工作流
  14. 从零开始用人工智能预测股票(三、初步实现)
  15. 微信公众号运营错误的四个方式
  16. IC design的初期阶段(floorplan和place初期)的检查清单checklist
  17. 将对称分量转换为abc相量
  18. java编程基础学习需要多久的时间
  19. 电脑故障维修判断指导大全(2)
  20. 最快速的OpenCV安装教程

热门文章

  1. 开源公告|腾讯云代码分析(TCA)对外开源
  2. 分销积分商城小程序开发方案php开发语言
  3. 计算机毕设Python+Vue智能居家养老服务平台(程序+LW+部署)
  4. vue身份证号、手机号、姓名脱敏处理
  5. VREP学习路路径 | Learning path of V-REP
  6. 计算机毕业设计基于asp.net的KTV信息管理系统
  7. 美团王兴将参与王慧文光年之外A轮投资;海康机器人拟募资60亿元创业板上市;赛富时启动2.5亿美元生成式AI基金丨每日大事件...
  8. Delphi-双色球分析软件(1)
  9. 双臂协作机器人或是机器人管家的雏形,那他还在等什么?
  10. 纯硬件简单人体感应灯(无需编程)