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.
Related
I'm developing phonegap app and right now I'm trying to write a plugin to convert .amr files to .mp3 files. I'm using JAVE to do this conversion and while it's working on desktop it fails on android with this exception:
java.io.IOException: Error running exec().
Command: [/data/data/<my_package>/cache/jave-1/ffmpeg, -i, /sdcard/<my_filename>.amr, -vn, -acodec, libmp3lame, -f, mp3, -y, /sdcard/<my_filename>.mp3]
Working Directory: null Enviroment: null
I'm trying to do conversion like this:
private void convert(String input_file, CallbackContext callbackContext){
File input = new File(input_file);
File output = new File(input_file.replace(".amr", ".mp3"));
Encoder encoder = new Encoder();
EncodingAttributes encodingAttributes = new EncodingAttributes();
AudioAttributes audioAttributes = new AudioAttributes();
audioAttributes.setCodec("libmp3lame");
encodingAttributes.setAudioAttributes(audioAttributes);
encodingAttributes.setFormat("mp3");
try{
encoder.encode(input, output, encodingAttributes);
input.delete();
callbackContext.success("finished");
}
catch(Exception e){
callbackContext.error(e.getMessage());
}
}
I found this answer and I understand why error happens but the answer doesn't provide any solution. Is there a way to get this working in Phonegap project? Do I need to package ffmpeg library together with plugin and copy it to correct folder when app invokes the plugin?
/data/data/<my_package>/cache/jave-1
Stucked with same problem. For now i found that there is problem with file ffmpeg permissions. JAVE trying to set them, but for some reason it not working.
So i set this permission by myself like this:
Process process = null;
DataOutputStream dataOutputStream = null;
try {
process = Runtime.getRuntime().exec("su");
dataOutputStream = new DataOutputStream(process.getOutputStream());
dataOutputStream.writeBytes("chmod 0755 __AppCachePath__/jave-1/ffmpeg\n");
dataOutputStream.writeBytes("exit\n");
dataOutputStream.flush();
process.waitFor();
} catch (Exception e) {
} finally {
try {
if (dataOutputStream != null) {
dataOutputStream.close();
}
process.destroy();
} catch (Exception e) {
}
}
After this File permission problem goes away.
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.
I'm using usb-serial-for-android library and I am getting some strange results. After 2.5 hours of continuous communicating with the serial port and reading and writing successfully, I get this exception:
exception in UsbManager.openDevice
android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.hardware.usb.IUsbManager$Stub$Proxy.openDevice(IUsbManager.java:339)
at android.hardware.usb.UsbManager.openDevice(UsbManager.java:255)
at com.hoho.android.usbserial.driver.UsbSerialProber$1.probe(UsbSerialProber.java:63)
at com.hoho.android.usbserial.driver.UsbSerialProber.probeSingleDevice(UsbSerialProber.java:174)
But when i force close my app and then restart it, everything is fine and my app can communicate with the port again.
It maybe useful to mention that before the exception , I get this exception:
java.io.FileNotFoundException: /sdcard/log.txt: open failed: EMFILE (Too many open files)
at libcore.io.IoBridge.open(IoBridge.java:406)
at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
at java.io.FileWriter.<init>(FileWriter.java:58)
at org.example.myapp.util.L.log(L.java:32)
I use class L for logging purposes:
public class L {
public synchronized void log(String message){
File logFile = new File("sdcard/log.txt");
if (!logFile.exists())
{
try
{
logFile.createNewFile();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try
{
//BufferedWriter for performance, true to set append to file flag
BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
buf.append(new Date(DateProvider.getInstance().getCurrentDateAsMillisecs()).toString()+": "+message);
buf.newLine();
buf.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
It seems that some kind of buffer or cache is getting filled and prevents communicating with the device.
How can I get rid of the exception?
Update:
Writing to log file never fails even though I'm getting the exception. It only affects communicating with usb device.
You are opening too many files.
Problems I have found:
- You never close logFile
- You open the file in the log function, a static File would be better and could be initialized in a log_init(); function.
Or, make logFile an object variable and initialize it in the constructor.
I think solving these will solve your problem.
Can you not use getFilesDir() to pass the directory path while creating file
public class L {
public static File logFile = new File("sdcard/log.txt");
public synchronized void log(String message){
....
Everytime you call new File you request a file resource from the system. As you keep doing it the system will complain that you opened too many files. To avoid this, request the file only once.
UPDATE There are a number of other posts asking how to get a Screenshot in android but none seemed to have a full answer of how to do so. Originally I posted this as a question due to a particular issue I was running into while attempting to open a stream to the Frame Buffer. Now I've swapped over to dumping the Frame Buffer to a file so I've updated my post to show how I got there. For reference (and acknowledgement), I found the command to send the FrameBuffer to a file from this post (unfortunately he didn't provide how he got to that point). I'm just missing how to turn the raw data I pulled from the Frame Buffer into an actual image file.
My intention was to take a full dump of the actual screen on an Android Device. The only way I could find to do so without using the adb bridge was to directly access the Frame Buffer of the system. Obviously this approach will require root privileges on the device and for the app running it! Fortunately for my purposes I have control over how the Device is set up and having the device rooted with root privileges provided to my application is feasible. My testing is currently being done on an old Droid running 2.2.3.
I found my first hints of how to approach it from https://stackoverflow.com/a/6970338/1446554. After a bit more research I found another article that describes how to properly run shell commands as root. They were using it to execute a reboot, I use it to send the current frame buffer to an actual file. My current testing has only gotten as far as doing this via ADB and in a basic Activity (each being provided root). I will be doing further testing from a Service running in the background, updates to come! Here is my entire test activity that can export the current screen to a file:
public class ScreenshotterActivity extends Activity {
public static final String TAG = "ScreenShotter";
private Button _SSButton;
private PullScreenAsyncTask _Puller;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_SSButton = (Button)findViewById(R.id.main_screenshotButton);
_SSButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (_Puller != null)
return;
//TODO: Verify that external storage is available! Could always use internal instead...
_Puller = new PullScreenAsyncTask();
_Puller.execute((Void[])null);
}
});
}
private void runSuShellCommand(String cmd) {
Runtime runtime = Runtime.getRuntime();
Process proc = null;
OutputStreamWriter osw = null;
StringBuilder sbstdOut = new StringBuilder();
StringBuilder sbstdErr = new StringBuilder();
try { // Run Script
proc = runtime.exec("su");
osw = new OutputStreamWriter(proc.getOutputStream());
osw.write(cmd);
osw.flush();
osw.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (osw != null) {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
if (proc != null)
proc.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
sbstdOut.append(readBufferedReader(new InputStreamReader(proc.getInputStream())));
sbstdErr.append(readBufferedReader(new InputStreamReader(proc.getErrorStream())));
}
private String readBufferedReader(InputStreamReader input) {
BufferedReader reader = new BufferedReader(input);
StringBuilder found = new StringBuilder();
String currLine = null;
String sep = System.getProperty("line.separator");
try {
// Read it all in, line by line.
while ((currLine = reader.readLine()) != null) {
found.append(currLine);
found.append(sep);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots");
if (ssDir.exists() == false) {
Log.i(TAG, "Screenshot directory doesn't already exist, creating...");
if (ssDir.mkdirs() == false) {
//TODO: We're kinda screwed... what can be done?
Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!");
return null;
}
}
File ss = new File(ssDir, "ss.raw");
if (ss.exists() == true) {
ss.delete();
Log.i(TAG, "Deleted old Screenshot file.");
}
String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath();
runSuShellCommand(cmd);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
_Puller = null;
}
}
}
This also requires adding the android.permission.WRITE_EXTERNAL_STORAGE permission to the Manifest. As suggested in this post. Otherwise it runs, doesn't complain, doesn't create the directories nor the file.
Originally I couldn't get usable data from the Frame Buffer due to not understanding how to properly run shell commands. Now that I've swapped to using the streams for executing commands I can use '>' to send the Frame Buffer's current data to an actual file...
Programmatically you can run "adb shell /system/bin/screencap -p /sdcard/img.png" as below :
Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
An easy solution for ICS devices is to use the following from the command line
adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png
This'll save the screenshot.png file in the current directory.
Tested on a Samsung Galaxy SII running 4.0.3.
That would be different for different phones. It depends on the underlying graphics format of your device. You can poll what the graphics format is using system calls. If you are only going to run this on devices that you know the graphics format of you can write a converter that turns it into a known format.
You can have a look at the following project: http://code.google.com/p/android-fb2png/
If you look at the source code for fb2png.c you can see that they poll FBIOGET_VSCREENINFO which contains info about how the device stores the screen image in memory. Once you know that, you should be able to convert it into a format you can use.
I hope this helps.
I am trying to execute shell command through my code for adding entry in Iptables. The following is my piece of code
private void createShellCommand(String uidValue_param, String interface_param) {
// TODO Auto-generated method stub
StringBuilder cmdScript=new StringBuilder();
script.append("iptables -A OUTPUT " + interface_param + "-m owner --uid-owner " + uidValue_param + "-j REJECT");
writeIptables(cmdScript);
}
private void writeIptables(StringBuilder scriptfile) {
// TODO Auto-generated method stub
String cmd=scriptfile.toString();
if(RootTools.isRootAvailable())
{
Process exec;
try {
exec = Runtime.getRuntime().exec(new String[]{"su","-c"});
final OutputStreamWriter out = new OutputStreamWriter(exec.getOutputStream());
out.write(cmd);
// Terminating the "su" session
out.write("exit\n");
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("IPtables updation failed", "Iptable write failed"+e);
}
}
else
{
Log.e("Root Access denied", "Access Denied");
}
}
Here there are two methods i.e, createshellCommand() for building the shell command and writeIptables() for updating the iptables. But whenever I run the program logcat is displaying the following warning
"W 19913 su request rejected (0->0 /system/bin/sh)"
But I can manually add the entry through command prompt and its adding to the iptables but by using the above code its not updating. My phone is rooted and I am using android 2.3.4 with linux kernel 2.6.29. And I am using external library "Roottools" for checking the root access.
Please help me to rectify the error.
This Works:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
execute_reboot();
}
void execute_reboot()
{
Process reboot;
try {
reboot=Runtime.getRuntime().exec("su");
DataOutputStream os =
new DataOutputStream(reboot.getOutputStream());
os.writeBytes("reboot\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
reboot.waitFor();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This Code Works fine. There are couple of small mistakes in your program. Try the one i pasted. Its working charm. All the best. I kept it as simple as possible so that it is easy to understand. You can still use arrays and other stuff to fancy your coding.
And yaa the same one also works for chmod command where you need to pass more than one argument.
For this Just replace
"os.writeBytes("reboot\n");"
with
"chmod 777 /dev/video0\n"(or any other system file).
Thanks. LEt me know if there is something tat i can do.
public static void rebootNow() {
Log.d(TAG, "Rebooting");
try {
#SuppressWarnings("unused")
Process proc = Runtime.getRuntime().exec(
new String[] { "su", "-c", "reboot" });
} catch (Exception ex) {
Log.d(TAG, "Rebooting failed (Terminal Error)");
}
}
This one is a little more compact
You can add "proc.waitFor();" after the Process proc... line to get rid of the unused warning, but rebooting your device takes a few seconds time and if you want to disable some features during the few seconds in your UI-thread, I think its better to not wait for the process to end.
Trying using the iptables command (with sudo and without), rather than just clobbering the iptables config file.