Android video encoding with fr and resolution manipulation - android

I want to be able to take a video recorded with an Android device and encode it to a new Resolution and Frame Rate using my app. The purpose is to upload a much smaller version of the original video (in size), since this will be videos 30 min long or more.
So far, I've read of people saying FFmpeg is they way to go. However, the documentation seems to be lacking.
I have also considered using http opencv http://opencv.org/platforms/android.html
Considering I need to manipulate the video resolution and frame rate, which tool do you think can do such things better? Are there any other technologies to consider?
An important question is, since this will be long videos, is it reasonable to do the encoding in an android device (Consider power resources, time, etc.)
Thanks in advance!

I decided to use ffmpeg to tackle this project. After much researching and trials, I was not able to build ffmpeg for library (using Ubuntu 14.04 LTS.)
However, I used this excellent library https://github.com/guardianproject/android-ffmpeg-java
I just created a project and added that library and it works like a charm. No need to build your own files or mess with the Android NDK. Of course you would still need to build the library yourself if you want to customize it. But it has everything I need.
Here is an example of how I used to lower a video resolution and change the frame rate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// input source
final Clip clip_in = new Clip("/storage/emulated/0/Developer/test.mp4");
Activity activity = (Activity) MainActivity.this;
File fileTmp = activity.getCacheDir();
File fileAppRoot = new File(activity.getApplicationInfo().dataDir);
final Clip clip_out = new Clip("/storage/emulated/0/Developer/result2.mp4");
//put flags in clip
clip_out.videoFps = "30";
clip_out.width = 480;
clip_out.height = 320;
clip_out.videoCodec = "libx264";
clip_out.audioCodec = "copy";
try {
FfmpegController fc = new FfmpegController(fileTmp, fileAppRoot);
fc.processVideo(clip_in, clip_out, false, new ShellUtils.ShellCallback() {
#Override
public void shellOut(String shellLine) {
System.out.println("MIX> " + shellLine);
}
#Override
public void processComplete(int exitValue) {
if (exitValue != 0) {
System.err.println("concat non-zero exit: " + exitValue);
Log.d("ffmpeg","Compilation error. FFmpeg failed");
Toast.makeText(MainActivity.this, "result: ffmpeg failed", Toast.LENGTH_LONG).show();
} else {
if(new File( "/storage/emulated/0/Developer/result2.mp4").exists()) {
Log.d("ffmpeg","Success file:"+ "/storage/emulated/0/Developer/result2.mp4");
}
}
}
});
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// automated try and catch
setContentView(R.layout.activity_main);
}
}
The function processVideo produces a command similar to ffmpeg -i input -s 480X320 -r 30 -vcodec libx264 -acodec copy output
This a very simple example, but it outputted the same kind of conversion done by ffmpeg desktop. This codes needs lots of work! I hope it helps anyone.

Related

how to create video from multiple sequence of images using FFMpeg?

Hello all i am new in android developing. I want to create video from sequence of images. And i already fetch images from the specific folder which is resides in external memory card in android devices but i do not know how to use FF MPEG library to convert images into a video file. i had much tried to find out solution but yet i could not get the solution.
Any help would be appreciated and Thanks in advance.
I implement below code but it does not working.
private void convertImg_to_vid() {
// TODO Auto-generated method stub
Process chperm;
try {
chperm=Runtime.getRuntime().exec("su");
DataOutputStream os =
new DataOutputStream(chperm.getOutputStream());
os.writeBytes("ffmpeg -f image2 -i img%d.jpg /tmp/a.mpg\n");
os.flush();
chperm.waitFor();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You can use -loop command like this ffmpeg -loop 1 -i img.png -c:v libx264 -t 30 -pix_fmt yuv420p out.mp4. This will loop single image as mentioned here.
https://trac.ffmpeg.org/wiki/Create%20a%20video%20slideshow%20from%20images
and the documentation http://ffmpeg.org/ffmpeg.html

Out of memory - Android

I'm using ffmpeg in my Android application and sometimes I'm getting out of memory error, I'm calling the ffmpeg inside a HandlerThread, is it ok to catch out of memory error and exit the thread while the main thread keeps on running?
I read a lot of this being not a good practice, the thing is that I really need that because I have to edit the DB when there is any kind of error
fc = new FfmpegController(context, fileTmp);
try {
fc.processVideo(clip_in, clip_out, false,
new ShellUtils.ShellCallback() {
#Override
public void shellOut(String shellLine) {
}
#Override
public void processComplete(int exitValue) {
//Update the DB
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
} catch (InterruptedException e) {
} catch (Exception e) {
}catch (OutOfMemoryError e) {
//update the DB
}
No something is going wrong if you are getting OutOfMemory errors. I would look into buffering your audio, as likely you are running the whole clip through ffmpeg at once, which is going to use up alot of memory.
Also, keep in mind that lots of us doing Audio in Android end up using the NDK primarily because of issues like you are experiencing. Audio has to be really high performance, and using the NDK allows you to write more low level memory efficient audio handling.
Android's AudioTrack has a write method that allows you to push an Audio buffer to it. A warning that this is not entry level and requires some knowledge of AudioBuffer's as well as requires you to read buffers in, send them to ffmpeg and then pass to AudioTrack. Not easy to do, and unfortunately more advanced audio on Android is not easy.

Android - ffmpeg best approach

I am trying to build ffmpeg for android. I want to achieve two things with it.
1. Rotate video
2. Join two or more videos.
There are two approaches for having ffmpeg in my application.
1. Having ffmpeg executable, copying it to /data/package/ and executing ffmpeg commands.
2. Build ffmpeg library .so file with ndk and write jni code etc.
Which approach is best according to my needs? And can I have some code snippets that follows those approaches?
You can achieve it by two ways, I would do it with the first one:
Place your ffmpeg file into you raw folder.
You need to use the ffmpeg executable file using commands, but you'll need to place the file into a file-system folder and change the permissions of the file, so use this code:
public static void installBinaryFromRaw(Context context, int resId, File file) {
final InputStream rawStream = context.getResources().openRawResource(resId);
final OutputStream binStream = getFileOutputStream(file);
if (rawStream != null && binStream != null) {
pipeStreams(rawStream, binStream);
try {
rawStream.close();
binStream.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close streams!", e);
}
doChmod(file, 777);
}
}
public static OutputStream getFileOutputStream(File file) {
try {
return new FileOutputStream(file);
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found attempting to stream file.", e);
}
return null;
}
public static void pipeStreams(InputStream is, OutputStream os) {
byte[] buffer = new byte[IO_BUFFER_SIZE];
int count;
try {
while ((count = is.read(buffer)) > 0) {
os.write(buffer, 0, count);
}
} catch (IOException e) {
Log.e(TAG, "Error writing stream.", e);
}
}
public static void doChmod(File file, int chmodValue) {
final StringBuilder sb = new StringBuilder();
sb.append("chmod");
sb.append(' ');
sb.append(chmodValue);
sb.append(' ');
sb.append(file.getAbsolutePath());
try {
Runtime.getRuntime().exec(sb.toString());
} catch (IOException e) {
Log.e(TAG, "Error performing chmod", e);
}
}
Call this method:
private void installFfmpeg() {
File ffmpegFile = new File(getCacheDir(), "ffmpeg");
String mFfmpegInstallPath = ffmpegFile.toString();
Log.d(TAG, "ffmpeg install path: " + mFfmpegInstallPath);
if (!ffmpegFile.exists()) {
try {
ffmpegFile.createNewFile();
} catch (IOException e) {
Log.e(TAG, "Failed to create new file!", e);
}
Utils.installBinaryFromRaw(this, R.raw.ffmpeg, ffmpegFile);
}else{
Log.d(TAG, "It was installed");
}
ffmpegFile.setExecutable(true);
}
Then, you will have your ffmpeg file ready to use by commands. (This way works for me but there are some people that says that it doesn't work, I don't know why, hope it isn't your case). Then, we use the ffmpeg with this code:
String command = "data/data/YOUR_PACKAGE/cache/ffmpeg" + THE_REST_OF_YOUR_COMMAND;
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
Log.d(TAG, "Process finished");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
As I said, you have to use the ffmpeg file by commands, so you should search on Internet and choose the command you want to use, then, add it into the command string. If the command fails, you won't be alerted by any log, so you should try your command with a terminal emulator and be sure that it works. If it doesn´t work, you won't see any result.
Hope it's useful!!
The advantage of library approach is that you have better control over the progress of your conversion, and can tune it in the middle. One the other hand, operating the executable is a bit easier. Finally, you can simply install the ffmpeg4android app and work with their API.

how to use guardianproject's android ffmpeg library?

First, this is my first time "playing" with ffmpeg, so please bear with me.
Generally, i dont understand ffmpeg even a little bit. So i did lot, lot of researches (and also trial & error) and i finally found this project and its library
So i was successfully created the ffmpeg and sox binary file, and i put it in the raw folder at the library project (from the link i shared).
Now, i want to use the library for my project, but i still cant do it. I tried to use some methods in the FfmpegController like combineAudioAndVideo and more but its not working (yet).
I dont post the error here since i still do my trial&errors (and the error change regularly) but im getting tired now.
EDIT
This is what i did :
private FfmpegController ffController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File file = new File(Uri.parse("android.resource://com.my.package/" + R.raw.test).getPath());
try {
ffController = new FfmpegController(this, file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MediaDesc desc = ffController.combineAudioAndVideo(R.raw.test, R.raw.musictest, "test.mp4", null);
}
The combineAudioAndVideo always error because wrong parameters. It needs MediaDesc but i dont know how to do it.
I will be very happy if you can share your working code if you have done the ffmpeg processing with this library.

Running ffmpeg commands from android ffmpeg syntax error in logcat

I have successfully compiled ffmpeg for android and have ported it.
I placed
libffmpeg.so in /system/lib directory
ffmpeg executable in /system/bin and /system/xbin directory (i was not sure where to place it). i directly copied ffmpeg executable from source directory (Not sure whether it's a correct way)
Now i am executing commands from android with following code !!
imports *
public class LatestActivity extends Activity {
private Process process;
String command,text;
static {
System.loadLibrary("ffmpeg");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_latest);
//Execute Command !!
try {
Execute();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void Execute() throws IOException, InterruptedException{
try {
File dir=new File("/system/bin");
String[] cmd= {"ffmpeg","-codecs"};
process=Runtime.getRuntime().exec(cmd,null,dir);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d("Process IOException starts:",e.getMessage());
e.printStackTrace();
Log.d("System Manual exit !!",e.getMessage());
System.exit(MODE_PRIVATE);
}
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()),16384);
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
// read the output from the command
Log.d("Application output: ","Output if any !");
while ((text = stdInput.readLine()) != null) {
Log.d("Output: ",text); //$NON-NLS-1$
}
text="";
// read any errors from the attempted command
Log.d("Application output: ","Errors if any !"); //$NON-NLS-1$
while ((text = stdError.readLine()) != null) {
Log.d("Error: ",text); //$NON-NLS-1$
}
stdInput.close();
stdError.close();
process.waitFor();
process.getOutputStream().close();
process.getInputStream().close();
process.getErrorStream().close();
destroyProcess(process);
//process.destroy();
}
private static void destroyProcess(Process process) {
try {
if (process != null) {
// use exitValue() to determine if process is still running.
process.exitValue();
}
} catch (IllegalThreadStateException e) {
// process is still running, kill it.
process.destroy();
}
}
}
And Here is the logcat output:
09-05 15:29:13.287: D/dalvikvm(2670): No JNI_OnLoad found in /system/lib/libffmpeg.so 0x44e7e910, skipping init
09-05 15:29:29.117: I/global(2670): Default buffer size used in BufferedReader constructor. It would be better to be explicit if an 8k-char buffer is required.
09-05 15:29:29.117: D/Application output:(2670): Output if any !
09-05 15:29:29.117: D/Application output:(2670): Errors if any !
09-05 15:29:29.127: D/Error:(2670): /system/bin/ffmpeg: 1: Syntax error: "(" unexpected
m neither getting any errors nor output of command. At the end it shows syntax error. I want to know what kind of syntax error it is. how to tackle it?
m i doing something wrong?
This Error occurs if the ffmpeg file does not compiled for your cpu architechture.
Your commands might be right but you need to find correct ffmpeg file.
FIXED
#Gaganpreet Singh
You are right after so much research on this, I have got to know that CPU Chip-set matters too, FFMPEG commands doesn't support INTEL ATOM processor.
Asus Memo Pad 7 using INTEL ATOM cpu chip-set and when trying running ffmpeg command on it, it crashes and throw error "SYNTAX ERROR"
My commands working perfectly on all the devices except the device using INTEL ATOM chipset.
Please review this and this link if it will be helpful for you.
If anyone finds a solution. Please share with us.
Finally Fixed this issue by creating ffmpeg lib for x64 & armv7 using NDK. And used this Library in my Andriod project. Now I have 2 lib and using this lib for different Android CPU ARCH.
Please check this link too. Very helpful.

Categories

Resources