I'm facing a problem with media muxer. Why does my app crash? and what is the solution?
java.lang.IllegalStateException: Failed to stop the muxer
at android.media.MediaMuxer.nativeStop(Native Method)
at android.media.MediaMuxer.stop(MediaMuxer.java:245)
at com.app.filter.helper.config.VideoInfoUtils.combineVideo(VideoInfoUtils.java:159)
at com.app.filter.helper.filter.video.VideoFilterView.handleSaveCompleted(VideoFilterView.java:74)
at com.app.filter.helper.filter.video.VideoFilterView$ViewHandler.handleMessage(VideoFilterView.java:167)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
and my java code is
public class VideoInfoUtils {
public static MediaFormat getVideoInfo(String path) {
MediaExtractor audioExtractor = new MediaExtractor();
try {
audioExtractor.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
int selectTrack = selectTrack(audioExtractor);
if (selectTrack < 0) {
throw new RuntimeException("No video track found in " + path);
}
MediaFormat format = audioExtractor.getTrackFormat(selectTrack);
return format;
}
/**
* Selects the video track, if any.
*
* #return the track index, or -1 if no video track is found.
*/
private static int selectTrack(MediaExtractor mMediaExtractor) {
// Select the first video track we find, ignore the rest.
int numTracks = mMediaExtractor.getTrackCount();
for (int i = 0; i < numTracks; i++) {
MediaFormat format = mMediaExtractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
return i;
}
}
return -1;
}
#SuppressLint("NewApi")
public static void combineVideo(String videoPath, String newVideo, String output) throws IllegalArgumentException {
try {
MediaExtractor videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(newVideo);
MediaFormat videoFormat = null;
int videoTrackIndex = -1;
int videoTrackCount = videoExtractor.getTrackCount();
for (int i = 0; i < videoTrackCount; i++) {
videoFormat = videoExtractor.getTrackFormat(i);
String mimeType = videoFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
videoTrackIndex = i;
break;
}
}
MediaExtractor audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(videoPath);
MediaFormat audioFormat = null;
int audioTrackIndex = -1;
int audioTrackCount = audioExtractor.getTrackCount();
for (int i = 0; i < audioTrackCount; i++) {
audioFormat = audioExtractor.getTrackFormat(i);
String mimeType = audioFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("audio/")) {
audioTrackIndex = i;
break;
}
}
videoExtractor.selectTrack(videoTrackIndex);
audioExtractor.selectTrack(audioTrackIndex);
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
MediaMuxer mediaMuxer;
int writeVideoTrackIndex;
int writeAudioTrackIndex;
try {
mediaMuxer = new MediaMuxer(output, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
writeVideoTrackIndex = mediaMuxer.addTrack(videoFormat);
writeAudioTrackIndex = mediaMuxer.addTrack(audioFormat);
mediaMuxer.start();
} catch (IOException ioe) {
throw new RuntimeException("MediaMuxer creation failed", ioe);
}
ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
long videoStampTime = 0;
{
videoExtractor.readSampleData(byteBuffer, 0);
if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
videoExtractor.advance();
}
videoExtractor.readSampleData(byteBuffer, 0);
long secondTime = videoExtractor.getSampleTime();
videoExtractor.advance();
long thirdTime = videoExtractor.getSampleTime();
videoStampTime = Math.abs(thirdTime - secondTime);
}
videoExtractor.unselectTrack(videoTrackIndex);
videoExtractor.selectTrack(videoTrackIndex);
long audioStampTime = 0;
//获取帧之间的间隔时间
{
audioExtractor.readSampleData(byteBuffer, 0);
if (audioExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
audioExtractor.advance();
}
audioExtractor.readSampleData(byteBuffer, 0);
long secondTime = audioExtractor.getSampleTime();
audioExtractor.advance();
audioExtractor.readSampleData(byteBuffer, 0);
long thirdTime = audioExtractor.getSampleTime();
audioStampTime = Math.abs(thirdTime - secondTime);
Log.e("time", audioStampTime + "");
}
audioExtractor.unselectTrack(audioTrackIndex);
audioExtractor.selectTrack(audioTrackIndex);
while (true) {
int readVideoSampleSize = videoExtractor.readSampleData(byteBuffer, 0);
if (readVideoSampleSize < 0) {
break;
}
videoBufferInfo.size = readVideoSampleSize;
videoBufferInfo.presentationTimeUs += videoStampTime;
videoBufferInfo.offset = 0;
videoBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo);
videoExtractor.advance();
}
while (true) {
int readAudioSampleSize = audioExtractor.readSampleData(byteBuffer, 0);
if (readAudioSampleSize < 0) {
break;
}
audioBufferInfo.size = readAudioSampleSize;
audioBufferInfo.presentationTimeUs += audioStampTime;
audioBufferInfo.offset = 0;
audioBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audioBufferInfo);
audioExtractor.advance();
}
if (mediaMuxer != null) {
mediaMuxer.stop();
mediaMuxer.release();
}
videoExtractor.release();
audioExtractor.release();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In my application i'm trying to add filters on video.
But sometimes my app crashes and sometimes it works fine.
The error is "Failed to stop the muxer"
I got below code from https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
// Throws exception b/c start() is not called.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
muxer.addTrack(MediaFormat.createVideoFormat("video/avc", 480, 320));
try {
muxer.stop();
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
according to this, you need to make sure you stated mutex before you stop it. You can keep variable like isMutextStarted like to store state. But it is just hack.
I recommand you to initalize it only muxer going to start next. Otherwise it is keep as null then your mediaMuxer.stop() and release parts are not executed.
I try to concat two mp4 video by mp4parser lib. But I'm getting error. Here is my code
public void appendTwoVideo(String mp4_1,String mp4_2) throws IOException {
Movie[] inMovies = new Movie[]{MovieCreator.build(mp4_1),
MovieCreator.build(mp4_2)};
List<Track> videoTracks = new LinkedList<Track>();
List<Track> audioTracks = new LinkedList<Track>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
IsoFile out = new DefaultMp4Builder().build(result);
FileChannel fc = new RandomAccessFile(String.format("output.mp4"), "rw").getChannel();
fc.position(0);
out.getBox(fc);
fc.close();
}
In line
IsoFile out = new DefaultMp4Builder().build(result);
I'm getting error
Error:(296, 53) error: incompatible types: Container cannot be converted to IsoFile.
Can anyone help me with my error or how can concat two videos any other way?
I find the solution
Just don’t use IsoFile. Use Container as type, write the container to disk with
WritableByteChannel wbc = new FileOutputStream(
new File(outputDirectory, filename)).getChannel();
try {
container.writeContainer(wbc);
} finally {
wbc.close();
}
https://groups.google.com/forum/#!msg/mp4parser-discussion/t3jRVeRsGFs/RTT3bdDN-qQJ
I have a problem related to the merging of two videos. When I'm trying to merge two videos from different landscape modes (left/right) one of the videos is displaying upside down. I'm using MP4Parser as a library to manage the trimming and merging
protected void combineClips() throws IOException {
MediaMetadataRetriever m = new MediaMetadataRetriever();
for (int i = 0; i < paths.size(); i++) {
m.setDataSource(paths.get(i));
if (Build.VERSION.SDK_INT >= 17) {
String s = m.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if (Integer.valueOf(s) == 0) {
isLeftLandscape = true;
}
if (Integer.valueOf(s) == 180) {
isRightLandscape = true;
}
}
}
for (int i = 0; i < paths.size(); i++) {
Movie tm = MovieCreator.build(paths.get(i));
Log.d(TAG, files.size() + "files");
files.add(tm);
}
if (isLeftLandscape && isRightLandscape) {
for (int i = 0; i < paths.size(); i++) {
m.setDataSource(paths.get(i));
if (Build.VERSION.SDK_INT >= 17) {
String s = m.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if (Integer.valueOf(s) == 0)
{
Log.d(TAG, "the rotation is " + s);
rotate(paths.get(i), i);
}}
}
}
List<Track> videoTracks = new ArrayList<Track>();
List<Track> audioTracks = new ArrayList<Track>();
for (Movie movie : files) {
for (Track t : movie.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
Log.d(TAG, String.valueOf(audioTracks.size()) + "audi");
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
Log.d(TAG, String.valueOf(audioTracks.size()) + "vide");
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container out = new DefaultMp4Builder().build(result);
String folder_main = "VidApp";
File f = new File(Environment.getExternalStorageDirectory(), folder_main);
if (!f.exists()) {
f.mkdirs();
}
FileChannel fc = new RandomAccessFile(String.format(System.getenv("EXTERNAL_STORAGE") + "/VidApp" + "/output.mp4"), "rw").getChannel();
out.writeContainer(fc);
fc.close();
while (!audioTracks.isEmpty()) {
audioTracks.remove(0);
}
while (!videoTracks.isEmpty()) {
videoTracks.remove(0);
}
while (!files.isEmpty()) {
files.remove(0);
}
}
public void rotate(String path, int position) throws IOException {
IsoFile isoFile = new IsoFile(path);
Movie m = new Movie();
List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(
TrackBox.class);
for (TrackBox trackBox : trackBoxes) {
trackBox.getTrackHeaderBox().setMatrix(Matrix.ROTATE_180);
m.addTrack(new Mp4TrackImpl(trackBox));
}
Log.d(TAG, position + " of " + files.size());
files.set(position, m);
}
I am using this code. I need to merge two videos. It saved all videos in temp folder but not in merged condition. Append and DoAppend are my functions which I want for merging the videos.
public String append(ArrayList<String> trimVideos) {
for (int i = 0; i < trimVideos.size() - 1; i++) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
if (i == 0) {
String OutPutFileName = Constants.STORAGE_VIDEO_TEMP_PATH +
File.separator + "APPEND" + "_" + timeStamp + ".mp4";
doAppend(trimVideos.get(0), trimVideos.get(i + 1),OutPutFileName);
Log.e(Constants.TAG, "In First: " + i + " " + OutPutFileName);
} else {
String OutPutFileName = Constants.STORAGE_VIDEO_TEMP_PATH
+ File.separator + "APPEND" + i + "_" + timeStamp + ".mp4";
doAppend(lastAppendOut, trimVideos.get(i + 1), OutPutFileName);
Log.e(Constants.TAG, "In Second: " + i + " " + OutPutFileName);
}
}
Log.e(Constants.TAG, "In End: " + " " + lastAppendOut);
return lastAppendOut;
}
This Method Crashed my application on add track.
private String doAppend(String _firstVideo, String _secondVideo,String _newName) {
try {
Log.e("test", "Stage1");
FileInputStream fis1 = new FileInputStream(_firstVideo);
FileInputStream fis2 = new FileInputStream(_secondVideo);
Movie[] inMovies = new Movie[] {
MovieCreator.build(fis1.getChannel()),MovieCreator.build(fis2.getChannel()) };
List<Track> videoTracks = new LinkedList<Track>();
List<Track> audioTracks = new LinkedList<Track>();
//It returns one item of video and 2 item of video.
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Log.e("test", "Stage2");
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
IsoFile out = new DefaultMp4Builder().build(result);
Log.e("test", "Stage3");
String filename = _newName;
lastAppendOut = filename;
Log.e(Constants.TAG, "In Append: " + " " + lastAppendOut);
FileOutputStream fos = new FileOutputStream(filename);
FileChannel fco = fos.getChannel();
fco.position(0);
out.getBox(fco);
fco.close();
fos.close();
fis1.close();
fis2.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
Log.e("check", e.getMessage());
}
return _newName;
}
Code For Merging Multiple Video
Gradle Dependency
implementation 'com.googlecode.mp4parser:isoparser:1.1.9'
Code
private String appendTwoVideos(String firstVideoPath, String secondVideoPath)
{
try {
Movie[] inMovies = new Movie[2];
inMovies[0] = MovieCreator.build(firstVideoPath);
inMovies[1] = MovieCreator.build(secondVideoPath);
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks
.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks
.toArray(new Track[videoTracks.size()])));
}
BasicContainer out = (BasicContainer) new DefaultMp4Builder().build(result);
#SuppressWarnings("resource")
FileChannel fc = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/wishbyvideo.mp4", "rw").getChannel();
out.writeContainer(fc);
fc.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/wishbyvideo.mp4";
return mFileName;
}
You might wanna call this function from a background thread.
The above answer is perfectly right but It will only work if your media encoder is H264.....
mediaRecorder.setVideoEncoder(VideoEncoder.H264);
happy Coding :) :D
The above answer will only work when your codec, framerate, and bitrate are the same.
Gradle Dependency(This library will work in nearly every case)
implementation 'com.github.yangjie10930:EpMedia:v0.9.5'
CODE
private void mergeVideos() {
ArrayList<EpVideo> epVideos = new ArrayList<>();
epVideos.add(new EpVideo (file2)); // Video 1
epVideos.add(new EpVideo (file1)); // Video 2
EpEditor. OutputOption outputOption =new EpEditor.OutputOption(fileOutput);
outputOption.setWidth(720);
outputOption.setHeight(1280);
outputOption.frameRate = 25 ;
outputOption.bitRate = 10 ; //Default
EpEditor.merge(epVideos, outputOption, new OnEditorListener() {
#Override
public void onSuccess () {
Log.d("Status","Success");
}
#Override
public void onFailure () {
}
#Override
public void onProgress ( float progress ) {
// Get processing progress here
Log.d("Progress",""+progress);
}
});
}
Gradle Dependency
implementation "com.writingminds:FFmpegAndroid:0.3.2"
Code
Command to concate two videos side by side into one
val cmd : arrayOf("-y", "-i", videoFile!!.path, "-i", videoFileTwo!!.path, "-filter_complex", "hstack", outputFile.path)
Command to append two videos (one after another) into one
val cmd : arrayOf("-y", "-i", videoFile!!.path, "-i", videoFileTwo!!.path, "-strict", "experimental", "-filter_complex",
"[0:v]scale=iw*min(1920/iw\\,1080/ih):ih*min(1920/iw\\,1080/ih), pad=1920:1080:(1920-iw*min(1920/iw\\,1080/ih))/2:(1080-ih*min(1920/iw\\,1080/ih))/2,setsar=1:1[v0];[1:v] scale=iw*min(1920/iw\\,1080/ih):ih*min(1920/iw\\,1080/ih), pad=1920:1080:(1920-iw*min(1920/iw\\,1080/ih))/2:(1080-ih*min(1920/iw\\,1080/ih))/2,setsar=1:1[v1];[v0][0:a][v1][1:a] concat=n=2:v=1:a=1",
"-ab", "48000", "-ac", "2", "-ar", "22050", "-s", "1920x1080", "-vcodec", "libx264", "-crf", "27",
"-q", "4", "-preset", "ultrafast", outputFile.path)
Note :
"videoFile" is your first video path.
"videoFileTwo" is your second video path.
"outputFile" is your combined video path which is our final output path
To create output path of video
fun createVideoPath(context: Context): File {
val timeStamp: String = SimpleDateFormat(Constant.DATE_FORMAT, Locale.getDefault()).format(Date())
val imageFileName: String = "APP_NAME_"+ timeStamp + "_"
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
if (storageDir != null) {
if (!storageDir.exists()) storageDir.mkdirs()
}
return File.createTempFile(imageFileName, Constant.VIDEO_FORMAT, storageDir)
}
Code to execute command
try {
FFmpeg.getInstance(context).execute(cmd, object : ExecuteBinaryResponseHandler() {
override fun onStart() {
}
override fun onProgress(message: String?) {
callback!!.onProgress(message!!)
}
override fun onSuccess(message: String?) {
callback!!.onSuccess(outputFile)
}
override fun onFailure(message: String?) {
if (outputFile.exists()) {
outputFile.delete()
}
callback!!.onFailure(IOException(message))
}
override fun onFinish() {
callback!!.onFinish()
}
})
} catch (e: Exception) {
} catch (e2: FFmpegCommandAlreadyRunningException) {
}
I am using Sebastian Annies example for the mp4parser where I append 3 videos. The result should be one video that plays all the three videos simultaneously. However, I get one video that plays the last video three times. Here is my code...
// int i = number of videos....
try {
String[] f = new String[i];
for (int count = 0; count < i; count++) {
f[count] = "/sdcard/vid" + i + ".mp4";
}
Movie[] inMovies = new Movie[i];
for (int count = 0; count < i; count++) {
inMovies[count] = MovieCreator.build(f[count]);
}
List<Track> videoTracks = new LinkedList<Track>();
List<Track> audioTracks = new LinkedList<Track>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container out = new DefaultMp4Builder().build(result);
FileChannel fc = new RandomAccessFile(String.format
("/sdcard/output.mp4"),
"rw").getChannel();
out.writeContainer(fc);
fc.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
VideoView v = (VideoView) findViewById(R.id.videoView1);
// v.setVideoPath("/sdcard/aaa" + i + ".mp4");
v.setVideoPath("/sdcard/output.mp4");
v.setMediaController(new MediaController(this));
v.start();
I dont know why it isn't doing what it's supposed to do. Please help me. Thanks
Your problem is that you're filling the input file-names with the same file:
for (int count = 0; count < i; count++) {
f[count] = "/sdcard/vid" + i + ".mp4";
}
Should be
for (int count = 0; count < i; count++) {
f[count] = "/sdcard/vid" + count + ".mp4";
}
You might be better off, for readability, to make i the loop variable, and have count be the number of files.