这是我的日志:
System.err:java.lang.IllegalStateException: Failed to add the track to the muxer
W/System.err: at android.media.MediaMuxer.nativeAddTrack(Native Method)
W/System.err: at android.media.MediaMuxer.addTrack(MediaMuxer.java:626)
W/System.err: at com.marvhong.videoeffect.composer.MuxRender.onSetOutputFormat(MuxRender.java:64)
W/System.err: at com.marvhong.videoeffect.composer.VideoComposer.drainEncoder(VideoComposer.java:224)
W/System.err: at com.marvhong.videoeffect.composer.VideoComposer.stepPipeline(VideoComposer.java:113)
W/System.err: at com.marvhong.videoeffect.composer.Mp4ComposerEngine.runPipelines(Mp4ComposerEngine.java:181)
W/System.err: at com.marvhong.videoeffect.composer.Mp4ComposerEngine.compose(Mp4ComposerEngine.java:127)
W/System.err: at com.marvhong.videoeffect.composer.Mp4Composer$1.run(Mp4Composer.java:198)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:764)
E/TrimVideoActivity: filterVideo---onFailed()
在我的应用程序中,我正在尝试在视频上添加过滤器。但有时我的应用程序崩溃,有时它工作正常。错误是Failed to add the track to the muxer
我已经调试了代码,发现音频下的视频问题可用,然后应用过滤器并保存它,但保存过滤器视频不起作用。
MuxRender 类:
class MuxRender {
private static final String TAG = "MuxRender";
private static final int BUFFER_SIZE = 64 * 1024; // I have no idea whether this value is appropriate or not...
private final MediaMuxer muxer;
private MediaFormat videoFormat;
private MediaFormat audioFormat;
private int videoTrackIndex;
private int audioTrackIndex;
private ByteBuffer byteBuffer;
private final List<SampleInfo> sampleInfoList;
private boolean started;
MuxRender(MediaMuxer muxer) {
this.muxer = muxer;
sampleInfoList = new ArrayList<>();
}
void setOutputFormat(SampleType sampleType, MediaFormat format) {
switch (sampleType) {
case VIDEO:
videoFormat = format;
break;
case AUDIO:
ObLogger.i(TAG, "format > " + format);
audioFormat = format;
break;
default:
throw new AssertionError();
}
}
void onSetOutputFormat() {
if (videoFormat != null && audioFormat != null) {
videoTrackIndex = muxer.addTrack(videoFormat);
ObLogger.v(TAG, "Added track #" + videoTrackIndex + " with " + videoFormat.getString(
MediaFormat.KEY_MIME) + " to muxer");
ObLogger.i(TAG, "audioFormat > " + audioFormat);
audioTrackIndex = muxer.addTrack(audioFormat);
ObLogger.v(TAG, "Added track #" + audioTrackIndex + " with " + audioFormat.getString(
MediaFormat.KEY_MIME) + " to muxer");
} else if (videoFormat != null) {
videoTrackIndex = muxer.addTrack(videoFormat);
ObLogger.v(TAG, "Added track #" + videoTrackIndex + " with " + videoFormat.getString(
MediaFormat.KEY_MIME) + " to muxer");
}
muxer.start();
started = true;
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocate(0);
}
byteBuffer.flip();
ObLogger.v(TAG, "Output format determined, writing " + sampleInfoList.size() +
" samples / " + byteBuffer.limit() + " bytes to muxer.");
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int offset = 0;
for (SampleInfo sampleInfo : sampleInfoList) {
sampleInfo.writeToBufferInfo(bufferInfo, offset);
muxer.writeSampleData(getTrackIndexForSampleType(sampleInfo.sampleType), byteBuffer, bufferInfo);
offset += sampleInfo.size;
}
sampleInfoList.clear();
byteBuffer = null;
}
void writeSampleData(SampleType sampleType, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
if (started) {
muxer.writeSampleData(getTrackIndexForSampleType(sampleType), byteBuf, bufferInfo);
return;
}
byteBuf.limit(bufferInfo.offset + bufferInfo.size);
byteBuf.position(bufferInfo.offset);
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE).order(ByteOrder.nativeOrder());
}
byteBuffer.put(byteBuf);
sampleInfoList.add(new SampleInfo(sampleType, bufferInfo.size, bufferInfo));
}
private int getTrackIndexForSampleType(SampleType sampleType) {
switch (sampleType) {
case VIDEO:
return videoTrackIndex;
case AUDIO:
return audioTrackIndex;
default:
throw new AssertionError();
}
}
public enum SampleType {VIDEO, AUDIO}
private static class SampleInfo {
private final SampleType sampleType;
private final int size;
private final long presentationTimeUs;
private final int flags;
private SampleInfo(SampleType sampleType, int size, MediaCodec.BufferInfo bufferInfo) {
this.sampleType = sampleType;
this.size = size;
presentationTimeUs = bufferInfo.presentationTimeUs;
flags = bufferInfo.flags;
}
private void writeToBufferInfo(MediaCodec.BufferInfo bufferInfo, int offset) {
bufferInfo.set(offset, size, presentationTimeUs, flags);
}
}
}
Mp4ComposerEngine 类:
class Mp4ComposerEngine {
private static final String TAG = "Mp4ComposerEngine";
private static final double PROGRESS_UNKNOWN = -1.0;
private static final long SLEEP_TO_WAIT_TRACK_TRANSCODERS = 10;
private static final long PROGRESS_INTERVAL_STEPS = 10;
private FileDescriptor inputFileDescriptor;
private VideoComposer videoComposer;
private IAudioComposer audioComposer;
private MediaExtractor mediaExtractor;
private MediaMuxer mediaMuxer;
private ProgressCallback progressCallback;
private long durationUs;
void setDataSource(FileDescriptor fileDescriptor) {
inputFileDescriptor = fileDescriptor;
}
void setProgressCallback(ProgressCallback progressCallback) {
this.progressCallback = progressCallback;
}
void compose(
final String destPath,
final Resolution outputResolution,
final GlFilter filter,
final int bitrate,
final boolean mute,
final Rotation rotation,
final Resolution inputResolution,
final FillMode fillMode,
final FillModeCustomItem fillModeCustomItem,
final int timeScale,
final boolean flipVertical,
final boolean flipHorizontal
) throws IOException {
try {
mediaExtractor = new MediaExtractor();
mediaExtractor.setDataSource(inputFileDescriptor);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
mediaMuxer = new MediaMuxer(destPath, OutputFormat.MUXER_OUTPUT_MPEG_4);
}
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(inputFileDescriptor);
try {
durationUs = Long
.parseLong(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)) * 1000;
} catch (NumberFormatException e) {
durationUs = -1;
}
ObLogger.d(TAG, "Duration (us): " + durationUs);
MediaFormat videoOutputFormat = MediaFormat
.createVideoFormat("video/avc", outputResolution.width(), outputResolution.height());
videoOutputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
videoOutputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
videoOutputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
videoOutputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, CodecCapabilities.COLOR_FormatSurface);
MuxRender muxRender = new MuxRender(mediaMuxer);
// identify track indices
MediaFormat format = mediaExtractor.getTrackFormat(0);
String mime = format.getString(MediaFormat.KEY_MIME);
final int videoTrackIndex;
final int audioTrackIndex;
if (mime.startsWith("video/")) {
videoTrackIndex = 0;
audioTrackIndex = 1;
} else {
videoTrackIndex = 1;
audioTrackIndex = 0;
}
// setup video composer
videoComposer = new VideoComposer(mediaExtractor, videoTrackIndex, videoOutputFormat, muxRender, timeScale);
videoComposer.setUp(filter, rotation, outputResolution, inputResolution, fillMode, fillModeCustomItem, flipVertical, flipHorizontal);
mediaExtractor.selectTrack(videoTrackIndex);
// setup audio if present and not muted
if (mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO) != null && !mute) {
// has Audio video
if (timeScale < 2) {
audioComposer = new AudioComposer(mediaExtractor, audioTrackIndex, muxRender);
} else {
audioComposer = new RemixAudioComposer(mediaExtractor, audioTrackIndex, mediaExtractor.getTrackFormat(audioTrackIndex), muxRender, timeScale);
}
audioComposer.setup();
mediaExtractor.selectTrack(audioTrackIndex);
runPipelines();
} else {
// no audio video
runPipelinesNoAudio();
}
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
mediaMuxer.stop();
}
} finally {
try {
if (videoComposer != null) {
videoComposer.release();
videoComposer = null;
}
if (audioComposer != null) {
audioComposer.release();
audioComposer = null;
}
if (mediaExtractor != null) {
mediaExtractor.release();
mediaExtractor = null;
}
} catch (RuntimeException e) {
e.printStackTrace();
// Too fatal to make alive the app, because it may leak native resources.
// throw new Error("Could not shutdown mediaExtractor, codecs and mediaMuxer pipeline.", e);
}
try {
if (mediaMuxer != null) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
mediaMuxer.release();
}
mediaMuxer = null;
}
} catch (RuntimeException e) {
ObLogger.e(TAG, "Failed to release mediaMuxer.", e);
}
}
}
private void runPipelines() {
long loopCount = 0;
if (durationUs <= 0) {
if (progressCallback != null) {
progressCallback.onProgress(PROGRESS_UNKNOWN);
}// unknown
}
while (!(videoComposer.isFinished() && audioComposer.isFinished())) {
boolean stepped = videoComposer.stepPipeline()
|| audioComposer.stepPipeline();
loopCount++;
if (durationUs > 0 && loopCount % PROGRESS_INTERVAL_STEPS == 0) {
double videoProgress = videoComposer.isFinished() ? 1.0 : Math
.min(1.0, (double) videoComposer.getWrittenPresentationTimeUs() / durationUs);
double audioProgress = audioComposer.isFinished() ? 1.0 : Math
.min(1.0, (double) audioComposer.getWrittenPresentationTimeUs() / durationUs);
double progress = (videoProgress + audioProgress) / 2.0;
if (progressCallback != null) {
progressCallback.onProgress(progress);
}
}
if (!stepped) {
try {
Thread.sleep(SLEEP_TO_WAIT_TRACK_TRANSCODERS);
} catch (InterruptedException e) {
// nothing to do
}
}
}
}
private void runPipelinesNoAudio() {
long loopCount = 0;
if (durationUs <= 0) {
if (progressCallback != null) {
progressCallback.onProgress(PROGRESS_UNKNOWN);
} // unknown
}
while (!videoComposer.isFinished()) {
boolean stepped = videoComposer.stepPipeline();
loopCount++;
if (durationUs > 0 && loopCount % PROGRESS_INTERVAL_STEPS == 0) {
double videoProgress = videoComposer.isFinished() ? 1.0 : Math
.min(1.0, (double) videoComposer.getWrittenPresentationTimeUs() / durationUs);
if (progressCallback != null) {
progressCallback.onProgress(videoProgress);
}
}
if (!stepped) {
try {
Thread.sleep(SLEEP_TO_WAIT_TRACK_TRANSCODERS);
} catch (InterruptedException e) {
// nothing to do
}
}
}
}
interface ProgressCallback {
/**
* Called to notify progress. Same thread which initiated transcode is used.
*
* @param progress Progress in [0.0, 1.0] range, or negative value if progress is unknown.
*/
void onProgress(double progress);
}
}
Mp4Composer 类:
public class Mp4Composer {
private final static String TAG = Mp4Composer.class.getSimpleName();
private final String srcPath;
private final String destPath;
private GlFilter filter;
private Resolution outputResolution;
private int bitrate = -1;
private boolean mute = false;
private Rotation rotation = Rotation.NORMAL;
private Listener listener;
private FillMode fillMode = FillMode.PRESERVE_ASPECT_FIT;
private FillModeCustomItem fillModeCustomItem;
private int timeScale = 1;
private boolean flipVertical = false;
private boolean flipHorizontal = false;
private ExecutorService executorService;
public Mp4Composer(@NonNull final String srcPath, @NonNull final String destPath) {
this.srcPath = srcPath;
this.destPath = destPath;
}
public Mp4Composer filter(@NonNull GlFilter filter) {
this.filter = filter;
return this;
}
public Mp4Composer size(int width, int height) {
this.outputResolution = new Resolution(width, height);
return this;
}
public Mp4Composer videoBitrate(int bitrate) {
this.bitrate = bitrate;
return this;
}
public Mp4Composer mute(boolean mute) {
this.mute = mute;
return this;
}
public Mp4Composer flipVertical(boolean flipVertical) {
this.flipVertical = flipVertical;
return this;
}
public Mp4Composer flipHorizontal(boolean flipHorizontal) {
this.flipHorizontal = flipHorizontal;
return this;
}
public Mp4Composer rotation(@NonNull Rotation rotation) {
this.rotation = rotation;
return this;
}
public Mp4Composer fillMode(@NonNull FillMode fillMode) {
this.fillMode = fillMode;
return this;
}
public Mp4Composer customFillMode(@NonNull FillModeCustomItem fillModeCustomItem) {
this.fillModeCustomItem = fillModeCustomItem;
this.fillMode = FillMode.CUSTOM;
return this;
}
public Mp4Composer listener(@NonNull Listener listener) {
this.listener = listener;
return this;
}
public Mp4Composer timeScale(final int timeScale) {
this.timeScale = timeScale;
return this;
}
private ExecutorService getExecutorService() {
if (executorService == null) {
executorService = Executors.newSingleThreadExecutor();
}
return executorService;
}
public Mp4Composer start() {
getExecutorService().execute(new Runnable() {
@Override
public void run() {
Mp4ComposerEngine engine = new Mp4ComposerEngine();
engine.setProgressCallback(new Mp4ComposerEngine.ProgressCallback() {
@Override
public void onProgress(final double progress) {
if (listener != null) {
listener.onProgress(progress);
}
}
});
final File srcFile = new File(srcPath);
final FileInputStream fileInputStream;
try {
fileInputStream = new FileInputStream(srcFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
if (listener != null) {
listener.onFailed(e);
}
return;
}
try {
engine.setDataSource(fileInputStream.getFD());
} catch (IOException e) {
e.printStackTrace();
if (listener != null) {
listener.onFailed(e);
}
return;
}
final int videoRotate = getVideoRotation(srcPath);
final Resolution srcVideoResolution = getVideoResolution(srcPath, videoRotate);
if (filter == null) {
filter = new GlFilter();
}
if (fillMode == null) {
fillMode = FillMode.PRESERVE_ASPECT_FIT;
}
if (fillModeCustomItem != null) {
fillMode = FillMode.CUSTOM;
}
if (outputResolution == null) {
if (fillMode == FillMode.CUSTOM) {
outputResolution = srcVideoResolution;
} else {
Rotation rotate = Rotation.fromInt(rotation.getRotation() + videoRotate);
if (rotate == Rotation.ROTATION_90 || rotate == Rotation.ROTATION_270) {
outputResolution = new Resolution(srcVideoResolution.height(), srcVideoResolution.width());
} else {
outputResolution = srcVideoResolution;
}
}
}
if (filter instanceof IResolutionFilter) {
((IResolutionFilter) filter).setResolution(outputResolution);
}
if (timeScale < 2) {
timeScale = 1;
}
ObLogger.d(TAG, "rotation = " + (rotation.getRotation() + videoRotate));
ObLogger.d(TAG, "inputResolution width = " + srcVideoResolution.width() + " height = " + srcVideoResolution.height());
ObLogger.d(TAG, "outputResolution width = " + outputResolution.width() + " height = " + outputResolution.height());
ObLogger.d(TAG, "fillMode = " + fillMode);
try {
if (bitrate < 0) {
bitrate = calcBitRate(outputResolution.width(), outputResolution.height());
}
engine.compose(
destPath,
outputResolution,
filter,
bitrate,
mute,
Rotation.fromInt(rotation.getRotation() + videoRotate),
srcVideoResolution,
fillMode,
fillModeCustomItem,
timeScale,
flipVertical,
flipHorizontal
);
} catch (Exception e) {
e.printStackTrace();
if (listener != null) {
listener.onFailed(e);
}
executorService.shutdown();
return;
}
if (listener != null) {
listener.onCompleted();
}
executorService.shutdown();
}
});
return this;
}
public void cancel() {
getExecutorService().shutdownNow();
}
public interface Listener {
/**
* Called to notify progress.
*
* @param progress Progress in [0.0, 1.0] range, or negative value if progress is unknown.
*/
void onProgress(double progress);
/**
* Called when transcode completed.
*/
void onCompleted();
/**
* Called when transcode canceled.
*/
void onCanceled();
void onFailed(Exception exception);
}
private int getVideoRotation(String videoFilePath) {
try {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(videoFilePath);
ObLogger.e("MediaMetadataRetriever", "getVideoRotation error");
String orientation = mediaMetadataRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
return Integer.valueOf(orientation);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return 0;
}
private int calcBitRate(int width, int height) {
final int bitrate = (int) (0.25 * 30 * width * height);
ObLogger.i(TAG, "bitrate=" + bitrate);
return bitrate;
}
private Resolution getVideoResolution(final String path, final int rotation) {
int width = 0;
int height = 0;
if (path != null && !path.isEmpty()) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(path);
try {
String Strwidth = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
String Strheight = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
if (Strwidth != null && Strheight != null) {
width = Integer.valueOf(Strwidth);
height = Integer.valueOf(Strheight);
}
retriever.release();
} catch (NumberFormatException e) {
retriever.release();
e.printStackTrace();
} catch (IllegalArgumentException e) {
retriever.release();
e.printStackTrace();
}
}
return new Resolution(width, height);
}
}
主要活动调用 Mp4Composer:
private void startMediaCodec(String srcPath,String outputPath) {
mMp4Composer = new Mp4Composer(srcPath, outputPath)
// .rotation(Rotation.ROTATION_270)
//.size(720, 1280)
.fillMode(FillMode.PRESERVE_ASPECT_FIT)
.filter(MagicFilterFactory.getFilter())
.mute(false)
.flipHorizontal(false)
.flipVertical(false)
.listener(new Listener() {
@Override
public void onProgress(double progress) {
ObLogger.d(TAG, "filterVideo---onProgress: " + (int) (progress * 100));
runOnUiThread(new Runnable() {
@Override
public void run() {
//show progress
}
});
}
@Override
public void onCompleted() {
ObLogger.d(TAG, "filterVideo---onCompleted");
runOnUiThread(new Runnable() {
@Override
public void run() {
ObLogger.i(TAG, "run: Editor Screen is >>> ");
Intent intent = new Intent();
intent.putExtra(Extras.EXTRA_FILTER_SCREEN, outputPath);
setResult(RESULT_OK, intent);
finish();
}
});
}
@Override
public void onCanceled() {
ObLogger.e(TAG, "onCanceled");
NormalProgressDialog.stopLoading();
}
@Override
public void onFailed(Exception exception) {
ObLogger.e(TAG, "filterVideo---onFailed()");
NormalProgressDialog.stopLoading();
// Toast.makeText(TrimVideoActivity.this, "Video processing failed", Toast.LENGTH_SHORT).show();
}
})
.start();
}
使用了以下链接,但我的问题没有解决。