I am trying to merge two videos in Android using FFMPEG and I have been following the Android War Zone blog which gives great ideas and simple methods to integrate FFMPEG in our project. However, I am facing issues in merging two videos.
Command :
vk.run(new String[]{
"ffmpeg",
"-f",
"concat",
"-i",
list,
"-s",
"hd720",
"-c",
"copy",
"-b",
br_from_db + "k",
path + "/" + "merged_video_3.mp4"
}, work_path, getActivity());
And the "list" in the above command is the one where I am facing a issue.It throws me the following error when I use the following method :
Code :
private String generateList(String[] inputs) {
File list;
Writer writer = null;
try {
list = File.createTempFile("ffmpeg-list", ".txt");
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(list)));
for (String input : inputs) {
writer.write("file '" + input + "'\n");
Log.d(TAG, "Writing to list file: file '" + input + "'");
}
} catch (IOException e) {
e.printStackTrace();
return "/";
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
Log.d(TAG, "Wrote list file to " + list.getAbsolutePath());
return list.getAbsolutePath();
}
Error :
12-16 19:49:57.416 5437-5437/? E/ffmpeg4android﹕ Command validation failed.
12-16 19:49:57.416 5437-5437/? E/ffmpeg4android﹕ Check if input file exists: /data/data/com.family45.golive.family45v1/cache/ffmpeg-list-1803386407.txt/storage/emulated/0/DCIM/Camera/dec24.mp4 /storage/emulated/0/DCIM/Camera/vid2.mp4
12-16 19:49:57.416 5437-5437/? W/System.err﹕ com.netcompss.ffmpeg4android.CommandValidationException
12-16 19:49:57.416 5437-5437/? W/System.err﹕ at com.netcompss.loader.LoadJNI.run(LoadJNI.java:34)
12-16 19:49:57.416 5437-5437/? W/System.err﹕ at com.netcompss.loader.LoadJNI.run(LoadJNI.java:49)
I obtained the command from this stack question. Its accepted but I am facing the above issue. I am very sure that the videos are present in their respective locations and all the paths are right but I cant seem to make it work.
Any insights on this is highly appreciated. Thanks in advance.
Update :
Call to generateList:
ArrayList<String> paths_to_merge = new ArrayList<String>();
paths_to_merge.add(path + "/" + "dec24.mp4");
paths_to_merge.add(path + "/" + "vid2.mp4");
LoadJNI vk = new LoadJNI();
String[] v12 = new String[paths_to_merge.size()];
v12 = paths_to_merge.toArray(v12);
String list = generateList(v12);
I am not sure what went wrong in my code, I am still not able to come with the right list. However, I found another command which seems to be working good.
Command :
vk.run(new String[]{"ffmpeg","-y","-i",path + "/" + "num1.mp4","-i",path + "/" + "num2.mp4","-i",path + "/" + "num3.mp4","-i",path + "/" + "num4.mp4",
"-i",created_folder + "/" + "created_video2.mp4","-strict","experimental",
"-filter_complex",
"[0:v]scale=640x480,setsar=1:1[v0];[1:v]scale=640x480,setsar=1:1[v1];[2:v]scale=640x480,setsar=1:1[v2];[3:v]scale=640x480,setsar=1:1[v3];" +
"[4:v]scale=640x480,setsar=1:1[v4];[v0][0:a][v1][1:a][v2][2:a][v3][3:a][v4][4:a] concat=n=5:v=1:a=1",
"-ab","48000","-ac","2","-ar","22050","-s","640x480","-r","30","-vcodec","mpeg4","-b","2097k",path + "/" + "numbers_video_m.mp4"},path,getActivity());
As you can see in the command, I have appended 5 videos for the purpose of testing but I believe that we can add more videos dynamically and this works without any issues for me.
Things to be noted :
"-i",path + "/" + "num1.mp4"
represent the input and you can append as many as you want.
[0:v]scale=640x480,setsar=1:1[v0];
and add this according to the number of inputs accordingly as [0:v]...[1:v].. and so on.
[v0][0:a]
and also this parameter to be added according the number of inputs.
concat=n=5:v=1:a=1
Give the value of n according to the number of videos.
So those are the main things that needs to be taken care of.
Related
I'm writing a project in Xamarin Forms, and today we try our app on Android device. And our request to googleAPI stop working. Here's code :
HttpWebRequest webRequest = WebRequest.Create(#"https://maps.googleapis.com/maps/api/place/search/json?location=" + position.Latitude + "," + position.Longitude + "&radius=1000&types=bar&sensor=false&key=APIKEY") as HttpWebRequest;
webRequest.Method = "GET";
webRequest.BeginGetResponse(new AsyncCallback(RequestCompleted), webRequest);
and then :
var request = (HttpWebRequest)result.AsyncState;
var response = (HttpWebResponse)request.EndGetResponse(result);
using (var stream = response.GetResponseStream())
{
var r = new StreamReader(stream);
var resp = r.ReadToEnd();
}
We got response and change it to json. On computer it works OK, but on Android it is saying INVALID REQUEST. Someone worked with Xamarin and know how to solve it?
If I would have to guess, I'd guess, that the locale/ language on your phone is different to that one on your pc.
HttpWebRequest webRequest = WebRequest.Create(#"https://maps.googleapis.com/maps/api/place/search/json?location=" + position.Latitude + "," + position.Longitude + "&radius=1000&types=bar&sensor=false&key=APIKEY") as HttpWebRequest;
There are implicit ToString() at ...n=" + position.Latitude + "," + position.Longitude + "&ra... that produce a localized output. But the google API needs the values with a . as decimal separator.
Append .ToString("G", CultureInfo.InvariantCulture)
HttpWebRequest webRequest = WebRequest.Create(#"https://maps.googleapis.com/maps/api/place/search/json?location=" +
position.Latitude.ToString("G", CultureInfo.InvariantCulture) +
"," + position.Longitude.ToString("G", CultureInfo.InvariantCulture) +
"&radius=1000&types=bar&sensor=false&key=APIKEY") as HttpWebRequest;
Neither on Android nor on iOS the sound file referenced by a LocalNotification instance is played (check the sample code below). On Android the system's standard sound is played. Anyone knows what I am doing wrong here?
#Override
protected void postMain(Form f) {
Container contentPane = f.getContentPane();
contentPane.setLayout(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE));
contentPane.setUIID("ToastBar");
Button btn = new Button("create Notification");
btn.addActionListener((e) -> {
scheduleNotification(Display.getInstance(), 0, "Title", "Body", "notification_sound_file.mp3", System.currentTimeMillis() + 1000);
});
contentPane.add(BorderLayout.CENTER, btn);
}
private void scheduleNotification(Display dsp, int nId, String nTitle, String nBody, String soundFileName, long scheduleTime) {
LocalNotification ntf = new LocalNotification();
ntf.setId(nId + " " + scheduleTime);
ntf.setAlertTitle(nTitle);
ntf.setAlertBody(nBody + " " + scheduleTime);
ntf.setAlertSound("/" + soundFileName);
Log.p("scheduling ntf ("+nTitle+","+ ntf.getAlertBody() +","+ ntf.getAlertSound());
dsp.scheduleLocalNotification(ntf, scheduleTime, LocalNotification.REPEAT_NONE);
}
Note: notification_sound_file.mp3 does exist in my default package!
If you want to get the sound file from device storage, then it can't detect directly
Instead of using this
ntf.setAlertSound("/" + soundFileName);
Try this
ntf.setAlertSound(Environment.getExternalStorageDirectory().getAbsolutePath() + "Folder_Name/" + soundFileName);
This will locate your file and your specific sound will play. One more thing add "android.permission.READ_EXTERNAL_STORAGE" permission also in manifest.
Best of luck.
The sound file needs to be a path to a file in FileSystemStorage which must always be fully qualified. You need to copy the file into the filesystem ideally under the app directory see https://www.codenameone.com/how-do-i-use-storage-file-system-sql.html
Is there any possibility to upload a file on mobile browser (Safari on iOS, Chrome on Android)? Conventional methods doesn't seem to work. Maybe there are similar options to BrowserStack where file upload can actually work?
BrowserStack uses Appium to drive your Selenium tests on Android and iOS.
As given here, since Appium currently does not support uploads, BrowserStack too wouldn't be able to support File Upload on mobile devices.
This is a hacky file upload solution using base64 and JS, but it works, so hey, I hope you find this useful:
public CorePage UploadHack(string fileInputId, string contentType, string fileContent, string fileName, string angularScopeVar)
{
UploadFile(_filePath);
var uploadHack =
"(function(){" +
"function convert(base64){" +
"var raw = atob(base64);" +
"var arr = new Uint8Array(new ArrayBuffer(raw.length));" +
"for (var i = 0; i < raw.length; i++){" +
"arr[i] = raw.charCodeAt(i);" +
"}" +
"return arr; " +
"}" +
$"var file = new Blob([convert('{fileContent}')], {{'type':'{contentType}'}}); " +
$"file.name = '{fileName}'; " +
$"angular.element('#{fileInputId}').scope().{angularScopeVar} = file;" +
"})()";
Driver.ExecuteJavaScript(uploadHack);
return this;
}
I am using Microsoft Sync Framework to sync the details from the Datadictionary on android device with SQL Server. Initially get success to sync all data from sql server. But after adding some data and when clicking on the Sync button getting the following error. Can you please tell me is anybody came across this?
[Sync Error]:Error occurs during sync. Please check logs below.
[Upload Change Response Error]: 500 Response: <ServiceError xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Synchronization.Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorDescription>System.InvalidOperationException
serverBlob is empty
at Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.ApplyChanges(Byte[] serverBlob, List`1 entities)
at Microsoft.Synchronization.Services.UploadChangesRequestProcessor.ProcessRequest(Request incomingRequest)
at Microsoft.Synchronization.Services.SyncService`1.ProcessRequestForMessage(Stream messageBody)
</ErrorDescription></ServiceError>
In the below code, i'm getting xmlHttp.status=500 when clicking on the Sync button
this.sendRequest = function (serviceUri, successCallback, errorCallback, dir) {
TraceObj("[" + dir + " Request]:", serviceUri, this.dataObject());
// Construct HTTP POST request
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", serviceUri);
xmlHttp.setRequestHeader("Accept", "application/json");
xmlHttp.setRequestHeader("Content-Type", "application/json");
// Handle success & error response from server and then callback
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
if (xmlHttp.status == 200) {
var res = new SyncFormatter();
if (res.parse(xmlHttp.responseText)) {
TraceObj("[" + dir + " Response]:", serviceUri, res.dataObject());
alert("[" + dir + " Response]:", serviceUri, res.dataObject());
successCallback(res);
return;
}
}
TraceMsg("[" + dir + " Response Error]: ", xmlHttp.status + " Response: " + xmlHttp.responseText);
errorCallback(xmlHttp.responseText);
}
};
xmlHttp.send(this.toString());
};
}
I have found the root cause of the problem. That is when i get the values from datadictionary storage, value has the single quotes in the values so not able to load the values in webview. Now i have replaced single quotes by \'. Now working fine.
Is there a way to redirect the logcat's output towards an InputStream or something like that? I'm thinking of something in the lines of how you can redirect the stderr in C.
I want to do it so its redirected when my app starts and everything that happends get dumped to a file.
If you can connect the device for debugging, you can this from the command line
$ adb logcat > textfile.txt
The easiest way I found is to use System.setErr. I allows you to easily redirect the error output to a file.
Example:
System.setErr(new PrintStream(new File("<file_path>"))
The only redirecting possibilites of LogCat output are documentend here and here.
It is not possible to reuse the LogCat output in your app itself. You can however, export it to a file like you ask.
to filter logcat just from your app try this:
int pid = android.os.Process.myPid();
File outputFile = new File(Environment.getExternalStorageDirectory() + "/logcat.txt");
Log.d("zzz","outputFile: " + outputFile);
try {
String command = "logcat | grep " + pid + " > " + outputFile.getAbsolutePath();
Log.d("zzz","command: " + command);
Process p = Runtime.getRuntime().exec("su");
OutputStream os = p.getOutputStream();
os.write((command + "\n").getBytes("ASCII"));
} catch (IOException e) {
}
In Kotlin, this is what I used and it seems to create and log to the logcat.txt file (located in the app directory) whether the app is being debugged or run directly from the tablet. (Sidenote: For some reason the directory doesn't refresh or show up in file explorer until I stop the app and reconnect the device.) (thanks for the help from the previous contributors)
located in utilities...
val publicDirectory = globalContext.getExternalFilesDir(null)
publicDirectoryName = publicDirectory?.toString() + "/"
fun redirectLogcatToFile() {
try {
val filename =
File(Utilities.publicDirectoryName + "logcat.txt")
filename.createNewFile()
val cmd = "logcat -v time -f " + filename.getAbsolutePath() +" -s " + logcatTag
Runtime.getRuntime().exec(cmd)
} catch (ex: Exception ) {
Utilities.toastOnAnyThread("Utilities.redirectLogcatToFile: " + ex.message)
}
}
fun Log( message : String ){
Log.i(logcatTag, message )
}