温馨提示:这篇文章已超过425天没有更新,请注意相关的内容是否还可用!
摘要:,,本内容主要介绍了Android开发中的视频压缩技术。详细介绍了如何进行视频压缩,包括压缩工具的选择、压缩参数的设置以及压缩过程中的注意事项等。通过学习和实践,开发者可以掌握视频压缩的技巧,优化视频文件大小,节省存储空间,提高应用性能。这对于需要处理大量视频数据的Android应用来说,具有重要的实用价值。
视频压缩是一个有关视频类项目必不可少的环节,选择一个合适且稳定的压缩工具更是领开发者比较头疼的一件事情,网上压缩工具比比皆是,一旦入坑,如果出问题后期出现问题,各种成本更是令人畏惧,这篇文章或许可以让你少走一些“弯路”。

首先这里的视频压缩使用的是 VideoProcessor 介意者勿扰~,并且是音视频类实战项目长期稳定之后才写的此文章,压缩比基本保持在 7:3 左右。
接下来开始实战使用,以及遇到的问题。

1.导入依赖
com.github.yellowcath:VideoProcessor:2.4.2
2.调用方法
VideoProcessor.processor(mPresenter) .input(url) .outWidth(1600) .outHeight(1200) .output(outputUrl) .bitrate(mBitrate) .frameRate(10) .process()
方法介绍
.processor(mPresenter) - mPresenter 传入当前引用 .input(url) - url本地视频地址 .outWidth(1600) - 压缩后的宽度 .outHeight(1200) - 压缩后的高度 .output(outputUrl) - 压缩后的地址 .bitrate(mBitrate) - 比特率 .frameRate(10) - 帧速率
比特率会影响到压缩视频之后的效果,可以动态去设置比特率和帧速率去调整压缩效果
//默认三百万,有数据后拿数据的百分之四十 var mBitrate = 3000000 try { //拿到视频的比特率 var media = MediaMetadataRetriever() media.setDataSource(locationVideoUrl) val extractMetadata = media.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE) Log.e(ContentValues.TAG, "当前视频的大小242412412 比特率->:${extractMetadata}") if (extractMetadata != null && extractMetadata.isNotEmpty()) { mBitrate = (extractMetadata.toInt() * 0.4).toInt() } } catch (e: Exception) { e.printStackTrace() }
以上就是压缩视频的使用步骤
以下是出现的问题
首先上述代码中很明显的错误就是压缩后的宽高是写死的,这样当用户传入不同形状的大小肯定会变形,所以我们可以根据原视频宽高进行压缩
基本我们会去本地拿资源会经过 onActivityResult 回调并且拿到 Intent data
可以通过
public static List obtainMultipleResult(Intent data) { List result = new ArrayList(); if (data != null) { result = (List) data.getSerializableExtra(PictureConfig.EXTRA_RESULT_SELECTION); if (result == null) { result = new ArrayList(); } return result; } return result; }
LocalMedia
public class LocalMedia implements Parcelable { private String path; private String compressPath; private String cutPath; private long duration; private boolean isChecked; private boolean isCut; public int position; private int num; private int mimeType; private String pictureType; private boolean compressed; private int width; private int height; public String ossUrl;//记录上传成功后的图片地址 public boolean isFail;//新增业务字段 是否是违规 public LocalMedia() { } public LocalMedia(String path, long duration, int mimeType, String pictureType) { this.path = path; this.duration = duration; this.mimeType = mimeType; this.pictureType = pictureType; } public LocalMedia(String path, long duration, int mimeType, String pictureType, int width, int height) { this.path = path; this.duration = duration; this.mimeType = mimeType; this.pictureType = pictureType; this.width = width; this.height = height; } public LocalMedia(String path, long duration, boolean isChecked, int position, int num, int mimeType) { this.path = path; this.duration = duration; this.isChecked = isChecked; this.position = position; this.num = num; this.mimeType = mimeType; } public String getPictureType() { if (TextUtils.isEmpty(pictureType)) { pictureType = "image/jpeg"; } return pictureType; } public void setPictureType(String pictureType) { this.pictureType = pictureType; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getCompressPath() { return compressPath; } public void setCompressPath(String compressPath) { this.compressPath = compressPath; } public String getCutPath() { return cutPath; } public void setCutPath(String cutPath) { this.cutPath = cutPath; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; } public boolean isChecked() { return isChecked; } public void setChecked(boolean checked) { isChecked = checked; } public boolean isCut() { return isCut; } public void setCut(boolean cut) { isCut = cut; } public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public int getMimeType() { return mimeType; } public void setMimeType(int mimeType) { this.mimeType = mimeType; } public boolean isCompressed() { return compressed; } public void setCompressed(boolean compressed) { this.compressed = compressed; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.path); dest.writeString(this.compressPath); dest.writeString(this.cutPath); dest.writeLong(this.duration); dest.writeByte(this.isChecked ? (byte) 1 : (byte) 0); dest.writeByte(this.isCut ? (byte) 1 : (byte) 0); dest.writeInt(this.position); dest.writeInt(this.num); dest.writeInt(this.mimeType); dest.writeString(this.pictureType); dest.writeByte(this.compressed ? (byte) 1 : (byte) 0); dest.writeInt(this.width); dest.writeInt(this.height); } protected LocalMedia(Parcel in) { this.path = in.readString(); this.compressPath = in.readString(); this.cutPath = in.readString(); this.duration = in.readLong(); this.isChecked = in.readByte() != 0; this.isCut = in.readByte() != 0; this.position = in.readInt(); this.num = in.readInt(); this.mimeType = in.readInt(); this.pictureType = in.readString(); this.compressed = in.readByte() != 0; this.width = in.readInt(); this.height = in.readInt(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LocalMedia that = (LocalMedia) o; return path != null && path.equals(that.path); } @Override public int hashCode() { if (path != null) { return path.hashCode(); } else { return 0; } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public LocalMedia createFromParcel(Parcel source) { return new LocalMedia(source); } @Override public LocalMedia[] newArray(int size) { return new LocalMedia[size]; } }; }
将拿到的data 转成 LocalMedia 的集合,默认取第一个 result.get(0)
此时我们就拿到了LocalMedia 这里面有我们需要的宽、高、时间、本地路径等信息
此时我们就可以将上面代码改成
Int videoWith = 1600 Int videoHeight = 1200 if (videoMedia.width != null && videoMedia.width != 0){ videoWith = videoMedia.width } if (videoMedia.height != null && videoMedia.height != 0){ videoHeight = videoMedia.height } VideoProcessor.processor(mPresenter) .input(url) .outWidth(videoWith ) .outHeight(videoHeight ) .output(outputUrl) .bitrate(mBitrate) .frameRate(10) .process()
以上,视频压缩之后变形的问题就解决了
我们压缩之后会出现一个新的压缩后的路径,如果不及时删除,用户手机上就会多一个压缩之后的文件,会影响用户的使用体验,当我们使用之后要及时去删除压缩后的文件(这里不贴代码了,百度一大堆,如有需要请留言~)
删除的时候,部分机型会报错,此时需要注意了!安卓现在已经不允许对 /0 文件也就是系统默认文件进行操作了,所以我们设置的压缩后的路径不要放在 /0 目录下
可以这样
//压缩视频,使用完后别忘了把压缩后的视频删除掉 val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" var outputUrl = mPresenter.getExternalFilesDir("").toString() + "/Kome" + SimpleDateFormat( FILENAME_FORMAT, Locale.CHINA ).format(System.currentTimeMillis()) + ".mp4"
这样基本都压缩步骤已经完成了,不出意外的话就要发版了,但是发上去之后就会发现部分机型会出现空指针的问题,经排查问题发生在底层源码里面 …processVideo()方法里面报错
可能最新版本已经修复此问题,如果没有可以直接重写 VideoProcessor 类,加一下防护,类似于 默认数据都是原有参数,尽量自己不要乱改
int originWidth = 1600; if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH) != null){ originWidth = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); }
最后贴上加防护后的VideoProcessor 代码
import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.media.AudioFormat; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaExtractor; import android.media.MediaFormat; import android.media.MediaMetadataRetriever; import android.media.MediaMuxer; import android.media.MediaRecorder; import android.net.Uri; import android.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import static com.hw.videoprocessor.util.AudioUtil.getAudioBitrate; import com.hw.videoprocessor.AudioProcessThread; import com.hw.videoprocessor.VideoDecodeThread; import com.hw.videoprocessor.VideoEncodeThread; import com.hw.videoprocessor.VideoUtil; import com.hw.videoprocessor.util.AudioFadeUtil; import com.hw.videoprocessor.util.AudioUtil; import com.hw.videoprocessor.util.CL; import com.hw.videoprocessor.util.PcmToWavUtil; import com.hw.videoprocessor.util.VideoMultiStepProgress; import com.hw.videoprocessor.util.VideoProgressAve; import com.hw.videoprocessor.util.VideoProgressListener; @TargetApi(21) public class VideoProcessor { final static String TAG = "VideoProcessor"; final static String OUTPUT_MIME_TYPE = "video/avc"; public static int DEFAULT_FRAME_RATE = 20; /** * 只有关键帧距为0的才能方便做逆序 */ public final static int DEFAULT_I_FRAME_INTERVAL = 1; public final static int DEFAULT_AAC_BITRATE = 192 * 1000; /** * 控制音频合成时,如果输入的音频文件长度不够,是否重复填充 */ public static boolean AUDIO_MIX_REPEAT = true; final static int TIMEOUT_USEC = 2500; public static void scaleVideo(Context context, Uri input, String output, int outWidth, int outHeight) throws Exception { processor(context) .input(input) .output(output) .outWidth(outWidth) .outHeight(outHeight) .process(); } public static void cutVideo(Context context, Uri input, String output, int startTimeMs, int endTimeMs) throws Exception { processor(context) .input(input) .output(output) .startTimeMs(startTimeMs) .endTimeMs(endTimeMs) .process(); } public static void changeVideoSpeed(Context context, Uri input, String output, float speed) throws Exception { processor(context) .input(input) .output(output) .speed(speed) .process(); } /** * 对视频先检查,如果不是全关键帧,先处理成所有帧都是关键帧,再逆序 */ public static void reverseVideo(Context context, com.hw.videoprocessor.VideoProcessor.MediaSource input, String output, boolean reverseAudio, @Nullable VideoProgressListener listener) throws Exception { File tempFile = new File(context.getCacheDir(), System.currentTimeMillis() + ".temp"); File temp2File = new File(context.getCacheDir(), System.currentTimeMillis() + ".temp2"); try { MediaExtractor extractor = new MediaExtractor(); input.setDataSource(extractor); int trackIndex = VideoUtil.selectTrack(extractor, false); extractor.selectTrack(trackIndex); int keyFrameCount = 0; int frameCount = 0; List frameTimeStamps = new ArrayList(); while (true) { int flags = extractor.getSampleFlags(); if (flags > 0 && (flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) { keyFrameCount++; } long sampleTime = extractor.getSampleTime(); if (sampleTime = 0) { MediaFormat audioTrackFormat = extractor.getTrackFormat(audioIndex); String audioMimeType = MediaFormat.MIMETYPE_AUDIO_AAC; int bitrate = getAudioBitrate(audioTrackFormat); int channelCount = audioTrackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); int sampleRate = audioTrackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); int maxBufferSize = AudioUtil.getAudioMaxBufferSize(audioTrackFormat); MediaFormat audioEncodeFormat = MediaFormat.createAudioFormat(audioMimeType, sampleRate, channelCount);//参数对应-> mime type、采样率、声道数 audioEncodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);//比特率 audioEncodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); audioEncodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxBufferSize); if (shouldChangeAudioSpeed) { if (processor.startTimeMs != null || processor.endTimeMs != null || processor.speed != null) { long durationUs = audioTrackFormat.getLong(MediaFormat.KEY_DURATION); if (processor.startTimeMs != null && processor.endTimeMs != null) { durationUs = (processor.endTimeMs - processor.startTimeMs) * 1000; } if (processor.speed != null) { durationUs /= processor.speed; } audioEncodeFormat.setLong(MediaFormat.KEY_DURATION, durationUs); } } else { long videoDurationUs = durationMs * 1000; long audioDurationUs = audioTrackFormat.getLong(MediaFormat.KEY_DURATION); if (processor.startTimeMs != null || processor.endTimeMs != null || processor.speed != null) { if (processor.startTimeMs != null && processor.endTimeMs != null) { videoDurationUs = (processor.endTimeMs - processor.startTimeMs) * 1000; } if (processor.speed != null) { videoDurationUs /= processor.speed; } long avDurationUs = videoDurationUs 0) { for (int i = videoFrameTimeStamps.size() - 1; i >= 0; i--) { extractor.seekTo(videoFrameTimeStamps.get(i), MediaExtractor.SEEK_TO_CLOSEST_SYNC); long sampleTime = extractor.getSampleTime(); if (lastFrameTimeUs == -1) { lastFrameTimeUs = sampleTime; } info.presentationTimeUs = lastFrameTimeUs - sampleTime; info.size = extractor.readSampleData(buffer, 0); info.flags = extractor.getSampleFlags(); if (info.size 1 ? 1 : videoProgress; videoProgress *= 0.7f; listener.onProgress(videoProgress); } } } else { while (true) { long sampleTime = extractor.getSampleTime(); if (lastFrameTimeUs == -1) { lastFrameTimeUs = sampleTime; } info.presentationTimeUs = lastFrameTimeUs - sampleTime; info.size = extractor.readSampleData(buffer, 0); info.flags = extractor.getSampleFlags(); if (info.size 1 ? 1 : videoProgress; videoProgress *= 0.7f; listener.onProgress(videoProgress); } long seekTime = sampleTime - MIN_FRAME_INTERVAL; if (seekTime = 0; i--) { extractor.seekTo(audioFrameStamps.get(i), MediaExtractor.SEEK_TO_CLOSEST_SYNC); long sampleTime = extractor.getSampleTime(); if (lastFrameTimeUs == -1) { lastFrameTimeUs = sampleTime; } info.presentationTimeUs = lastFrameTimeUs - sampleTime; info.size = extractor.readSampleData(buffer, 0); info.flags = extractor.getSampleFlags(); if (info.size 1 ? 1 : audioProgress; audioProgress = 0.7f + audioProgress * 0.3f; listener.onProgress(audioProgress); } } } else { extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); while (true) { long sampleTime = extractor.getSampleTime(); if (sampleTime == -1) { break; } info.presentationTimeUs = sampleTime; info.size = extractor.readSampleData(buffer, 0); info.flags = extractor.getSampleFlags(); if (info.size 1 ? 1 : audioProgress; audioProgress = 0.7f + audioProgress * 0.3f; listener.onProgress(audioProgress); } extractor.advance(); } } } if (listener != null) { listener.onProgress(1f); } } catch (Exception e) { CL.e(e); } finally { extractor.release(); mediaMuxer.release(); } } static List getFrameTimeStampsList(MediaExtractor extractor){ List frameTimeStamps = new ArrayList(); while (true) { long sampleTime = extractor.getSampleTime(); if (sampleTime 0 || fadeOutSec > 0) { AudioFadeUtil.audioFade(videoPcmAdjustedFile.getAbsolutePath(), sampleRate, channelCount, faceInSec, fadeOutSec); } new PcmToWavUtil(sampleRate, channelConfig, channelCount, AudioFormat.ENCODING_PCM_16BIT).pcmToWav(videoPcmAdjustedFile.getAbsolutePath(), videoWavFile.getAbsolutePath()); final int TIMEOUT_US = 2500; //重新将速率变化过后的pcm写入 int audioBitrate = getAudioBitrate(audioTrackFormat); int oriVideoIndex = VideoUtil.selectTrack(oriExtrator, false); MediaFormat oriVideoFormat = oriExtrator.getTrackFormat(oriVideoIndex); int rotation = oriVideoFormat.containsKey(MediaFormat.KEY_ROTATION) ? oriVideoFormat.getInteger(MediaFormat.KEY_ROTATION) : 0; MediaMuxer mediaMuxer = new MediaMuxer(output, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); mediaMuxer.setOrientationHint(rotation); int muxerVideoIndex = mediaMuxer.addTrack(oriVideoFormat); int muxerAudioIndex = mediaMuxer.addTrack(audioTrackFormat); //重新写入音频 mediaMuxer.start(); MediaExtractor pcmExtrator = new MediaExtractor(); pcmExtrator.setDataSource(videoWavFile.getAbsolutePath()); int audioTrack = VideoUtil.selectTrack(pcmExtrator, true); pcmExtrator.selectTrack(audioTrack); MediaFormat pcmTrackFormat = pcmExtrator.getTrackFormat(audioTrack); int maxBufferSize = AudioUtil.getAudioMaxBufferSize(pcmTrackFormat); ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);//参数对应-> mime type、采样率、声道数 encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, audioBitrate);//比特率 encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxBufferSize); MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); encoder.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); boolean encodeInputDone = false; boolean encodeDone = false; long lastAudioFrameTimeUs = -1; final int AAC_FRAME_TIME_US = 1024 * 1000 * 1000 / sampleRate; boolean detectTimeError = false; try { while (!encodeDone) { int inputBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_US); if (!encodeInputDone && inputBufferIndex >= 0) { long sampleTime = pcmExtrator.getSampleTime(); if (sampleTime = 0) { oriExtrator.unselectTrack(oriAudioIndex); } oriExtrator.selectTrack(oriVideoIndex); oriExtrator.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); maxBufferSize = oriVideoFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE); int frameRate = oriVideoFormat.containsKey(MediaFormat.KEY_FRAME_RATE) ? oriVideoFormat.getInteger(MediaFormat.KEY_FRAME_RATE) : (int) Math.ceil(VideoUtil.getAveFrameRate(mediaSource)); buffer = ByteBuffer.allocateDirect(maxBufferSize); final int VIDEO_FRAME_TIME_US = (int) (1000 * 1000f / frameRate); long lastVideoFrameTimeUs = -1; detectTimeError = false; while (true) { long sampleTimeUs = oriExtrator.getSampleTime(); if (sampleTimeUs == -1) { break; } info.presentationTimeUs = sampleTimeUs; info.flags = oriExtrator.getSampleFlags(); info.size = oriExtrator.readSampleData(buffer, 0); if (info.size = 0) { long s1 = System.currentTimeMillis(); final CountDownLatch latch = new CountDownLatch(2); //音频转化为PCM new Thread(new Runnable() { @Override public void run() { try { AudioUtil.decodeToPCM(videoInput, videoPcmFile.getAbsolutePath(), startTimeUs, endTimeUs); } catch (IOException e) { throw new RuntimeException(e); } finally { latch.countDown(); } } }).start(); final File finalAacPcmFile = aacPcmFile; new Thread(new Runnable() { @Override public void run() { try { AudioUtil.decodeToPCM(audioInput, finalAacPcmFile.getAbsolutePath(), 0, aacDurationMs > videoDurationMs ? videoDurationMs * 1000 : aacDurationMs * 1000); } catch (IOException e) { throw new RuntimeException(e); } finally { latch.countDown(); } } }).start(); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long s2 = System.currentTimeMillis(); //检查两段音频格式是否一致,不一致则统一转换为单声道+44100 Pair resultPair = AudioUtil.checkAndAdjustAudioFormat(videoPcmFile.getAbsolutePath(), aacPcmFile.getAbsolutePath(), oriExtrator.getTrackFormat(oriAudioIndex), audioExtractor.getTrackFormat(aacAudioIndex) ); channelCount = resultPair.first; sampleRate = resultPair.second; audioExtractor.release(); long s3 = System.currentTimeMillis(); //检查音频长度是否需要重复填充 if (AUDIO_MIX_REPEAT) { aacPcmFile = AudioUtil.checkAndFillPcm(aacPcmFile, aacDurationMs, videoDurationMs); } //混合并调整音量 adjustedPcm = new File(cacheDir, "adjusted_" + System.currentTimeMillis() + ".pcm"); AudioUtil.mixPcm(videoPcmFile.getAbsolutePath(), aacPcmFile.getAbsolutePath(), adjustedPcm.getAbsolutePath() , videoVolume, aacVolume); wavFile = new File(context.getCacheDir(), adjustedPcm.getName() + ".wav"); long s4 = System.currentTimeMillis(); int channelConfig = AudioFormat.CHANNEL_IN_MONO; if (channelCount == 2) { channelConfig = AudioFormat.CHANNEL_IN_STEREO; } //淡入淡出 if (fadeInSec != 0 || fadeOutSec != 0) { AudioFadeUtil.audioFade(adjustedPcm.getAbsolutePath(), sampleRate, channelCount, fadeInSec, fadeOutSec); } //PCM转WAV new PcmToWavUtil(sampleRate, channelConfig, channelCount, AudioFormat.ENCODING_PCM_16BIT).pcmToWav(adjustedPcm.getAbsolutePath(), wavFile.getAbsolutePath()); long s5 = System.currentTimeMillis(); CL.et("hwLog", String.format("decode:%dms,resample:%dms,mix:%dms,fade:%dms", s2 - s1, s3 - s2, s4 - s3, s5 - s4)); MediaFormat oriAudioFormat = oriExtrator.getTrackFormat(oriAudioIndex); audioBitrate = getAudioBitrate(oriAudioFormat); oriAudioFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC); AudioUtil.checkCsd(oriAudioFormat, MediaCodecInfo.CodecProfileLevel.AACObjectLC, sampleRate, channelCount ); muxerAudioIndex = mediaMuxer.addTrack(oriAudioFormat); } else { AudioUtil.decodeToPCM(audioInput, aacPcmFile.getAbsolutePath(), 0, aacDurationMs > videoDurationMs ? videoDurationMs * 1000 : aacDurationMs * 1000); MediaFormat audioTrackFormat = audioExtractor.getTrackFormat(aacAudioIndex); audioBitrate = getAudioBitrate(audioTrackFormat); channelCount = audioTrackFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) ? audioTrackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) : 1; sampleRate = audioTrackFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) ? audioTrackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) : 44100; int channelConfig = AudioFormat.CHANNEL_IN_MONO; if (channelCount == 2) { channelConfig = AudioFormat.CHANNEL_IN_STEREO; } AudioUtil.checkCsd(audioTrackFormat, MediaCodecInfo.CodecProfileLevel.AACObjectLC, sampleRate, channelCount); audioTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC); muxerAudioIndex = mediaMuxer.addTrack(audioTrackFormat); sampleRate = audioTrackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); channelCount = audioTrackFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) ? audioTrackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) : 1; if (channelCount > 2) { File tempFile = new File(aacPcmFile + ".channel"); AudioUtil.stereoToMonoSimple(aacPcmFile.getAbsolutePath(), tempFile.getAbsolutePath(), channelCount); channelCount = 1; aacPcmFile.delete(); aacPcmFile = tempFile; } if (aacVolume != 50) { adjustedPcm = new File(cacheDir, "adjusted_" + System.currentTimeMillis() + ".pcm"); AudioUtil.adjustPcmVolume(aacPcmFile.getAbsolutePath(), adjustedPcm.getAbsolutePath(), aacVolume); } else { adjustedPcm = aacPcmFile; } channelConfig = AudioFormat.CHANNEL_IN_MONO; if (channelCount == 2) { channelConfig = AudioFormat.CHANNEL_IN_STEREO; } wavFile = new File(context.getCacheDir(), adjustedPcm.getName() + ".wav"); //淡入淡出 if (fadeInSec != 0 || fadeOutSec != 0) { AudioFadeUtil.audioFade(adjustedPcm.getAbsolutePath(), sampleRate, channelCount, fadeInSec, fadeOutSec); } //PCM转WAV new PcmToWavUtil(sampleRate, channelConfig, channelCount, AudioFormat.ENCODING_PCM_16BIT).pcmToWav(adjustedPcm.getAbsolutePath(), wavFile.getAbsolutePath()); } //重新写入音频 mediaMuxer.start(); MediaExtractor pcmExtrator = new MediaExtractor(); pcmExtrator.setDataSource(wavFile.getAbsolutePath()); int audioTrack = VideoUtil.selectTrack(pcmExtrator, true); pcmExtrator.selectTrack(audioTrack); MediaFormat pcmTrackFormat = pcmExtrator.getTrackFormat(audioTrack); int maxBufferSize = AudioUtil.getAudioMaxBufferSize(pcmTrackFormat); ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);//参数对应-> mime type、采样率、声道数 encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, audioBitrate);//比特率 encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxBufferSize); MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); encoder.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); boolean encodeInputDone = false; boolean encodeDone = false; long lastAudioFrameTimeUs = -1; final int AAC_FRAME_TIME_US = 1024 * 1000 * 1000 / sampleRate; boolean detectTimeError = false; try { while (!encodeDone) { int inputBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_US); if (!encodeInputDone && inputBufferIndex >= 0) { long sampleTime = pcmExtrator.getSampleTime(); if (sampleTime = 0) { oriExtrator.unselectTrack(oriAudioIndex); } oriExtrator.selectTrack(oriVideoIndex); oriExtrator.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); maxBufferSize = oriVideoFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE); int frameRate = oriVideoFormat.containsKey(MediaFormat.KEY_FRAME_RATE) ? oriVideoFormat.getInteger(MediaFormat.KEY_FRAME_RATE) : (int) Math.ceil(VideoUtil.getAveFrameRate(videoInput)); buffer = ByteBuffer.allocateDirect(maxBufferSize); final int VIDEO_FRAME_TIME_US = (int) (1000 * 1000f / frameRate); long lastVideoFrameTimeUs = -1; detectTimeError = false; while (true) { long sampleTimeUs = oriExtrator.getSampleTime(); if (sampleTimeUs == -1) { break; } if (sampleTimeUs endTimeUs) { break; } info.presentationTimeUs = sampleTimeUs - startTimeUs; info.flags = oriExtrator.getSampleFlags(); info.size = oriExtrator.readSampleData(buffer, 0); if (info.size
还没有评论,来说两句吧...