Android - combining two MP4 files - android

I'm writing an Android project where I'm recording several audio files. Therefore, I'm setting the following parameters. The recording works fine.
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
My problem is that each time I record, the output is written in a separate file. Now, I need to combine these files to one file. Does anyone have an idea how to combine several MPEG 4 files in an Android project?
Thanks for your help....

I would suggest using my mp4parser library then you don't have to deal with native libs. Have a look at the AppendExample. It does exactly what you want to do. Look out for the latest version.
See below for AppendExample to get an idea how it works.
Movie[] inMovies = new Movie[]{MovieCreator.build(Channels.newChannel(AppendExample.class.getResourceAsStream("/count-deutsch-audio.mp4"))),
MovieCreator.build(Channels.newChannel(AppendExample.class.getResourceAsStream("/count-english-audio.mp4")))};
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 android there is no any inbuilt functionality for combining two audio files, If you are done it through any file operation then this also down to work because its not working as the headers of audio files are different.
I recommended to use a external library FFMPEG for your android application.

Related

Creating movie from files by adding track using com.googlecode.mp4parser with with memory limitations?

I have a audio recording in multiple files. I am creating one continues audio file using com.googlecode.mp4parser:isoparser:1.0.2 library.
Below is my code :
String mediaKey = isAudio ? "soun" : "vide";
List<Movie> listMovies = new ArrayList<>();
for (String filename : sourceFiles) {
listMovies.add(MovieCreator.build(filename));
}
List<Track> listTracks = new LinkedList<>();
for (Movie movie : listMovies) {
for (Track track : movie.getTracks()) {
if (track.getHandler().equals(mediaKey)) {
listTracks.add(track);
}
}
}
Movie outputMovie = new Movie();
if (!listTracks.isEmpty()) {
outputMovie.addTrack(new AppendTrack(listTracks.toArray(new Track[listTracks.size()])));
}
Container container = new DefaultMp4Builder().build(outputMovie);
FileChannel fileChannel = new RandomAccessFile(String.format(targetFile), "rw").getChannel();
container.writeContainer(fileChannel);
fileChannel.close();
Above code runs on Android phone. As its mobile environment there are memory limitations per application.
Problem with above code is when I load Movie from file and create a track list for small files its working fine. But as file size grows the operation starts to become non-responsive. It takes lot of memory. How can I make it memory efficient. Is their any way of doing this operations in small streams as we do in case of file copy operations in Java ?
Update :
For recording audio in files, I am using android MediaRecorder for this operations with Output format as MPEG_4 and Audio encoder as AAC
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

LibVLC on android not update SurfaceTexture

We would like to integrate libvlc in our project, replacing Android MediaPlayer.
Project compile, libvlc is initialized withput error but nothing happens.
Instead of render video on a android widget as SurfaceView, GLSurfaceView or TextureView, we musto to render to a SurfaceTexture which GL texture creted in JNI part. We are using this texture to render a sky dome in our game.
Right now we are decoding video to texture with android MediaPlayer and run perfectly. So issues is related with libVLC integration. No trace logs are written so is quite difficult so find where the problem is.
To summary our code:
1 - Set up libvlc library
ArrayList<String> options = new ArrayList<String>();
options.add("-vvv"); // verbosity
libVLC = new LibVLC(options);
libVLC.setOnNativeCrashListener(this);
mediaPlayer = new MediaPlayer(libVLC);
mediaPlayer.setEventListener(this);
2 - Load movie source
if (mediaPath.startsWith("http")) {
media = new Media(libVLC, mediaUri);
this.isLoaded = (media.parse(Media.Parse.FetchNetwork) && getMediaInformation()) ||
(media.parse(Media.Parse.ParseNetwork) && getMediaInformation());
} else if (mediaPath.startsWith("android.resource://")) {
media = new Media(libVLC, mediaUri);
} else {
media = new Media(libVLC, mediaPath);
this.isLoaded = (media.parse(Media.Parse.FetchLocal) && getMediaInformation()) ||
(media.parse(Media.Parse.ParseLocal) && getMediaInformation());
}
3 - Update texture in render loop
synchronized(this) {
if(isFrameNew) {
if(surfaceTexture != null) surfaceTexture.updateTexImage();
isFrameNew = false;
isMoviedone = false;
return true;
}
return false;
}
But nothing. Texture is empty and seems that libvlc is not working internally.
Anyone has the same issue?
Thank in advanced

Merging single video track and multiple audio tracks on Android

The idea
I am creating a save to device feature for a movie editing application that merges one video track with one (or optionally two) audio tracks.
First, there are multiple video clips that I merge into one single video track using MP4Parser (link).
Then, there are multiple audio clips that I would like to merge into one single audio track. These clips should not be appended, but merged into a single audio track at specific times. E.g. we have two audio clips (A1, A2) and a 60 sec video track (V1). These audio clips can be overlapping, or having white noise inbetween them. The length of the whole audio track has to match the Video track, that can be up to 60 seconds. There can be up to 100 audio clips added to the audio track 1
V1 - 60.0 s
A1 - 0.3 s
A2 - 1.1 s
Last, there might be an optional second audio track that contains a soundtrack as well, fit to the V1 video track.
Summary
This is how it would look like:
Video track 1: [--------------------------------------------------------------------------------] 60 sec
Audio track 1: [-A1--A2--------------------------------------------------------------------] 60 sec
Audio track 2: [-------------------------------------------------------------------------------] 60 sec
The problem
I tried approaching the problem by appending x second of white noise (empty wav file) to the audio track to get a full length track as described above, but that obviously would not work if the sounds are overlapping. What other ways can I try to tackle this problem?
private static final String OUTPUT = "output.mp4";
private static final String STORED_LOCATION = "/storage/emulated/0/"
/**
* Merges two videos that are located in /storage/emulated/0/ and saves it to the same place with the given parameters. Uses the ffmpeg/javacv library. All this is done in an Async task, not blocking the UI thread but showing a progress bar and a toast at the end.
*
*/
private void mergeVideosAsync()
{
new AsyncTask<Void, Void, String>()
{
#Override
protected String doInBackground(Void... arg0)
{
try
{
List<Movie> movieList = new ArrayList<>();
for (int i = 0; i < mVideoPathList.size(); ++i)
{
movieList.add(MovieCreator.build(new File(mVideoPathList.get(i)).getAbsolutePath()));
}
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : movieList)
{
for (Track t : m.getTracks())
{
if (t.getHandler().equals("soun"))
{
//TODO: Add audio tracks here to the merging process
// 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);
mOutputPath = String.format(STORED_LOCATION + File.separator + OUTPUT_FILENAME);
WritableByteChannel fc = new RandomAccessFile(mOutputPath, "rw").getChannel();
out.writeContainer(fc);
fc.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return mOutputPath;
}
}.execute();
}
If your audio tracks are overlapping then you have a problem as you'll need reencode the audio.
If the audio tracks are non-overlapping then you might be able to use the SilenceTrackImpl:
Track nuAudio = new AppendTrack(
audioTrackA1, new SilenceTrackImpl(100),
audioTrackA2, new SilenceTrackImpl(500),
audioTrackA3, new SilenceTrackImpl(1000),
audioTrackA4, new SilenceTrackImpl(50),
)
and so on.

Rotate video with Mp4parser

I need to rotate a video to adjust some of my needs. I'll explain the details on the following list.
I'm creating a Vine like app. I have to record video segments and then merge all the parts into just one file. I'm doing this without issue on an Android app using mp4 parser library with last version 1.0-RC-26 using the example provided on their website: here
The append video example works fine if all the videos have the same orientation but I discovered some issues recording video from the front camera so the quick solution was to set the video orientation recording on 270. The bad part on this solution is that the segment with this orientation appear with the wrong orientation on the merged video.
My possible solution to this is to rotate the video to apply what I need in different situations but I'm not having a working example with my code. Searching the internet I found solutions like this one here. The problem with this code is that is not compatible with the last version (It gives an compilation error) . I tried too to understand the logic of the library but I'm not having results. For example I experimented using the setMatrix instruction on the Movie object but It simply don't work.
public static void mergeVideo(int SegmentNumber) throws Exception {
Log.d("PM", "Merge process started");
Movie[] inMovies = new Movie[SegmentNumber] ;
//long[] Matrix = new long[SegmentNumber];
for (int i = 1 ; i <= SegmentNumber; i++){
File file = new File(getCompleteFilePath(i));
if (file.exists()){
FileInputStream fis = new FileInputStream(getCompleteFilePath(i));
//Set rotation I tried to experiment with this instruction but is not working
inMovies [i-1].setMatrix(Matrix.ROTATE_90);
inMovies [i-1] = MovieCreator.build(fis.getChannel());
Log.d("PM", "Video " + i + " merged" );
}
//fis.close();
}
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);
//out.getMovieBox().getMovieHeaderBox().setMatrix(Matrix.ROTATE_180); //set orientation, default merged video have wrong orientation
// Create a media file name
//
String filename = getCompleteMergedVideoFilePath() ;
FileChannel fc = new RandomAccessFile(String.format(filename), "rw").getChannel();
out.writeContainer(fc);
fc.close();
//don't leave until the file is on his place
File file = new File (filename);
do {
if (! file.exists()){
Log.d("PM", "Result file not ready");
}
} while (! file.exists() );
//
Log.d("PM", "Merge process finished");
}
Have someone rotated video with the very last version of Mp4 parser? English is not my native language so I apologize any grammar error.
for (int i = 1; i <= SegmentNumber; i++) {
IsoFile isoFile = new IsoFile(getCompleteFilePath(i));
Movie m = new Movie();
List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(
TrackBox.class);
for (TrackBox trackBox : trackBoxes) {
trackBox.getTrackHeaderBox().setMatrix(Matrix.ROTATE_90);
m.addTrack(new Mp4TrackImpl(trackBox));
}
inMovies[i - 1] = m;
}
This is what I did to rotate a video.

Video processing on android, how to write MPEG4 data into a MP4 file?

How to write (wrap) MPEG4 data into a MP4 file in android?
I am doing some kind video processing on android platform, but I don't know how to write the processed data (encoded in some kind standard, like MPEG4) back into video file like mp4. I think it is best to use API to do this, but I can't find the needed API.
Is there anyone have any ideas?
mp4parser can work only with fully created frame streams, u cant write frame by frame with it. Correct me if im wrong
H264TrackImpl h264Track = new H264TrackImpl(new BufferedInputStream(some input stream here));
Movie m = new Movie();
IsoFile out = new DefaultMp4Builder().build(m);
File file = new File("/sdcard/encoded.mp4");
FileOutputStream fos = new FileOutputStream(file);
out.getBox(fos.getChannel());
fos.close();
Now we need to know how to write frame by frame there.
OpenCV might be a little too much for the job, but I can't think of anything easier. OpenCV is a computer vision library that offers an API for C, C++ and Python.
Since you are using Android, you'll have to download a Java wrapper for OpenCV named JavaCV, and it's a 3rd party API. I wrote a small post with instructions to install OpenCV/JavaCV on Windows and use it with Netbeans, but at the end you'll have to search for a tutorial that shows how to install OpenCV/JavaCV for the Android platform.
This is a C++ example that shows how to open an input video and copy the frames to an output file. But since you are using Android an example using JavaCV is better, so the following code copies frames from an input video and writes it to an output file named out.mp4:
package opencv_videowriter;
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
public class OpenCV_videowriter
{
public static void main(String[] args)
{
CvCapture capture = cvCreateFileCapture("cleanfish47.mp4");
if (capture == null)
{
System.out.println("!!! Failed cvCreateFileCapture");
return;
}
int fourcc_code = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FOURCC);
double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
int w = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
int h = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
CvVideoWriter writer = cvCreateVideoWriter("out.mp4", // filename
fourcc_code, // video codec
fps, // fps
cvSize(w, h), // video dimensions
1); // is colored
if (writer == null)
{
System.out.println("!!! Failed cvCreateVideoWriter");
return;
}
IplImage captured_frame = null;
while (true)
{
// Retrieve frame from the input file
captured_frame = cvQueryFrame(capture);
if (captured_frame == null)
{
System.out.println("!!! Failed cvQueryFrame");
break;
}
// TODO: write code to process the captured frame (if needed)
// Store frame in output file
if (cvWriteFrame(writer, captured_frame) == 0)
{
System.out.println("!!! Failed cvWriteFrame");
break;
}
}
cvReleaseCapture(capture);
cvReleaseVideoWriter(writer);
}
}
Note: frames in OpenCV store pixels in the BGR order.
Your question doesn't make 100% sense. MPEG-4 is a family of specification (all ISO/IEC 14496-*) and MP4 is a the file format that is specified in ISO/IEC 14496-14.
If you want to create an MP4 file from a raw AAC and/or H264 stream I would suggest using the mp4parser library. There is an example that shows how to mux AAC and H264 into an MP4 file.
// Full working solution:
// 1. Add to app/build.gradle -> implementation 'com.googlecode.mp4parser:isoparser:1.1.22'
// 2. Add to your code:
try {
File mpegFile = new File(); // ... your mpeg file ;
File mp4file = new File(); // ... you mp4 file;
DataSource channel = new FileDataSourceImpl(mpegFile);
IsoFile isoFile = new IsoFile(channel);
List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
Movie movie = new Movie();
for (TrackBox trackBox : trackBoxes) {
movie.addTrack(new Mp4TrackImpl(channel.toString()
+ "[" + trackBox.getTrackHeaderBox().getTrackId() + "]", trackBox));
}
movie.setMatrix(isoFile.getMovieBox().getMovieHeaderBox().getMatrix());
Container out = new DefaultMp4Builder().build(movie);
FileChannel fc = new FileOutputStream(mp4file).getChannel();
out.writeContainer(fc);
fc.close();
isoFile.close();
channel.close();
Log.d("TEST", "file mpeg " + mpegFile.getPath() + " was changed to " + mp4file.getPath());
// mpegFile.delete(); // if you wish!
} catch (Exception e) {
e.printStackTrace();
}
// It's all! Happy coding =)

Categories

Resources