Problem with extracting audio from a video file (android) - c#

I need to convert an mp4 (or any other video format) to mp3 or wav file. I am using C# Xamarin.Forms. Any library I used either doesn't work for me, or isn't compatible with android. I have tried using Xamarin.Android.FFMpeg and Xobe.FFMpeg.
I repost this because it was marked as duplicate and I clearly stated that that solution isn't working for me. This is the post that was flagged as duplicate: How can I extract the audio out of a video file? (android) . Please, I tried it but it just gives me the "Downloading Video Converter" dialog with a progress bar when I try to run the code in the solution, Also, it doesn't even extract the audio so after the user waits such a long time, it turned out as a waste of time.
Thanks in advance! :)
EDIT 1:
This is the code I am using:
private async Task<string> ExtractAudioAsync(string path, Action<string> logger = null,
Action<int, int> onProgress = null)
{
List<string> cmd = new List<string>();
cmd.Add("-i");
cmd.Add(path + ".mp4");
cmd.Add("-vn");
cmd.Add("-acodec");
cmd.Add("copy");
cmd.Add(path + ".mp3");
string cmdParams = string.Join(' ', cmd);
await FFMpeg.Xamarin.FFMpegLibrary.Run(Context, cmdParams);
return path + ".mp3";
}
There are no exceptions thrown. When the Run method is called it just shows on the application the following dialog:
After that, it just closes the dialog and goes to the return line without even extracting the audio out of the video. Even if I run this code again it does the same thing, downloading it again.
EDIT 2:
I tried adding the -report option but it just did the same thing and did not save any log file. Am I doing something wrong?

Related

Playing audio in Xamarin UWP crashes

I am building a Xamarin app for UWP (Target Version 10.0.19041.0) and I am trying to play audio through my speakers based on a certain condition. It utilizes the Xam.Plugin.SimpleAudioPlayer (version 1.6.0) to do this (I have not been able to load/use the built-in MediaPlayer).
Based on the other StackOverflow questions (here and here) similar to this one, I made a function that should allow me to load a .mp3 or .wav as a stream and then use the Xam.Plugin.SimpleAudioPlayer to play it through my speakers.
Here is the definition of my function that attempts to load audio files as a stream. I have two files in the Assets folder of my UWP app, beep.wav and beep.mp3, both with Build Action set to Content.
Stream GetStreamFromFile(string filename) {
var assembly = typeof(Application).GetType().Assembly;
var stream = assembly.GetManifestResourceStream(
"Assets/" + filename // have also tried "Assets." since that was in a stackoverflow response
);
return stream;
}
Here is the function that calls the GetStreamFromFile function. I included all the nested conditionals/switches/etc. just in case that could be a possible reason for the issue. If I comment out the audio code lines, my code runs without error and with the expected behavior.
private void AddOrUpdateData() {
InvokeOnMainThread(() => {
...
switch (SensorType) {
case 0:
...
case 1:
...
default:
try {
...
}
finally {
if (my_value < 20) {
var stream = GetStreamFromFile("beep-09.mp3");
// stream.Seek(0L, SeekOrigin.Begin); // Have tried with and without this, not really sure what Seek does
var audio = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
audio.Load(stream);
audio.Play();
}
...
}
}
...
}
}
When this code runs, I get the null reference exception:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
It occurs whenever 'stream' is called; if stream.Seek is uncommented, it crashes there, otherwise it crashes when audio.Load(stream) occurs since stream appears to be None.
If I try to do it without a stream, and just loading the file from the AudioPlayer, I get this error:
System.Runtime.InteropServices.COMException: 'Error HRESULT E_FAIL has been returned from a call to a COM component.'
The code for that looks like this:
var audio = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
// Have tried both the mp3 and wav, with/without the Assets directory, all get the same error
audio.Load("Assets/beep.mp3");
audio.Play();
Are there any suggestions on how to fix this? I would try to use MediaPlayer but I cannot seem to import it correctly in Xamarin Forms since I am coding within a generic C# file that compiles to all of my platforms. I am also not sure if it has something to do with the nested locks, switches, and conditionals that maybe mess up how the rest of the code runs (I am not an expert at Xamarin so there are likely processes going on above my head).
If you put files(e.g. beep.mp3) in the Assets folder of your UWP app,you can try the following code:
var player = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
player.Load("beep.mp3");
player.Play();
Note: Here you don't need add Assets as the prefix.And make sure set the build action to Content.
audio.Load("Assets/beep.mp3");
There is samples here SimpleAudioPlayer, you can refer to it's code.
When you put your files in the folder of xamarin forms, we can use following code to get the stream:
Stream GetStreamFromFile(string filename)
{
var assembly = typeof(App).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream("SAPlayerSample." + filename);
return stream;
}
And use following code to load the mp3:
ISimpleAudioPlayer player;
var stream = GetStreamFromFile("Diminished.mp3");
player = CrossSimpleAudioPlayer.CreateSimpleAudioPlayer();
player.Load(stream);
And use function Seek to seek to the special postion.
player.Seek(sliderPosition.Value);
For more information, you can check sample: LibraryAudioPage.xaml.cs .

How to know file download progress status in server-side Blazor?

Currently, I download byte arrays as files using JsInterop.
Here is my JS file:
function downloadFile(fileName, base64String) {
const url = "data:application/octet-stream;base64," + base64String;
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
And here is a method in my razor component:
async Task DownloadFile(byte[] file)
{
ShowMessage("Start");
await JSRuntime.InvokeVoidAsync("downloadFile", "FileName", Convert.ToBase64String(file));
ShowMessage("End");
}
This code works, and I am able to download files. My issue is that I cannot implement the progress bar, or even show the loading spinner because await JSRuntime has no idea about an actual file download size and its progress. JSRuntime only launches the process of downloading and immediately continues to the next line of code.
In the code above ShowMessage("Start") and ShowMessage("End") are both shown one after another as soon as I click the download button, but the file in the browser downloads much later (depending on the file size).
How may I await the download process and execute relevant code only when the file has been downloaded? And it would be even better if I could read downloaded bytes to show a progress bar with percentages.
Update: for test purposes, I upload the file from the browser and store it in a byte[] variable. Then I download the same file from the variable using JS. Even though I store the file in the memory, it still takes time to download the file. I suppose that when I store a file in memory, it is already on my PC (client), and should download immediately. But instead, my window gets frozen for the duration of downloading the file. Tested on 6 - 11- 20 MB files. The bigger file, the more I have to wait for it to download.
I suggest you should be show message ShowMessage("Start") and ShowMessage("End"); in function downloadFile at JS

Windows 10 UWP App XElement.Save() "Access is denied" exception

I'm writing a Windows 10 UWP App, and I'm hitting a snag. I have a Settings.xml in the root of the app folder that I will use to store the DB connection information, as well as a few ancillary settings. I can load the XML fine, and my function to edit works (I can extract the XML through debug and see the changes). My problem comes when trying to write the edited file. I understand that I don't have direct access to the file system with UWP, however I've tried several different methods I've found online to work within my constraints and still can't find one that works. I always come back with an "Access is denied" error in some form or another. Here is a snippet of what my Save function looks like right now. Any help would be greatly appreciated.
try
{
XElement xmlSettings = XElement.Load(uri: "Settings.xml");
XElement xmlNode;
//Do Stuff (clipped for brevity).
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync(desiredName: "Settings.xml", options: CreationCollisionOption.ReplaceExisting);
Stream stream = await file.OpenStreamForWriteAsync();
xmlSettings.Save(stream);
Error = "";
}
catch (Exception e)
{
Error = "SaveSettings";
}
I added an xml file to my solution (in the root) and copy pasted your code.
It runs the first time, but gets the exception the second time. The reason is that your stream is not closed. You should use it with using:
using (Stream stream = await file.OpenStreamForWriteAsync())
{
xmlSettings.Save(stream);
}
With this change the code worked even the second time.

Can't understand C# windows phone 8.1 (SL) file storage

I'm writing a simple quizz game for WP8.1, at some point I decided to add a highscore capability.
I took an option which I know is not the best one but that should work anyhow : include a highscore.txt file in my application with a default value 0 and placed this file in the "Resources" folder. When the actual score is higher than the highscore I want to overwrite.
Here is how I have proceeded :
First: a method to grab the highscore and store it in a variable (This first method works pretty fine, it reads the file and gets the highscore)
public async void getHighScore(){
Uri highScoreFileLoc = new Uri("ms-appx:///Resources/Highscore.txt");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(highScoreFileLoc);
string highScoreSTR = await FileIO.ReadTextAsync(file);
}
A method to write the highscore into the file. My thought is that if I use the same exact file path and name, I should overwrite the file I have just opened.
private async void ecritHighScore(int highScore)
{
byte[] line = System.Text.Encoding.UTF8.GetBytes(scoreJoueur.ToString());
Uri HighScoreFileLoc = new Uri("ms-appx:///Resources/Highscore.txt");
StorageFile file2 = await StorageFile.GetFileFromApplicationUriAsync(HighScoreFileLoc);
string highScoreSTR = highScore.ToString();
await FileIO.WriteBytesAsync(file2, line);
}
When I run the app, the highscore is written and read correctly the second time.
But if I quit the emulator and run it again, the highscore is back to the default value: the original Highscore.txt file was not overwritten...
To sum up the situation :
I open a file and read it correctly but when I write into the same exact file (Uri is exactly the same), it is another file that is written into.
How is this possible ? It seems to me that I am missing something somewhere, but I could not find out...
Any help would be much appreciated.
You file uri points to the install directory which is read-only. (And your first write will probably fail on a real device.
Use an ms-appdata:// instead, which is the apps local directory.

C# SharpZipLib extract .TGZ NotSupportedException

I'm new to programming so please be patient.
I am currently developing a small Program in Visual C# 2010 Express, .NET Framework 4.0, which starts a Script on a Linux Machine (what creates the File /tmp/logs.tgz), downloads the File and then I want to extract it. Running the Script and downloading the File via Renci.SshNet works flawlessly.
But when I want to extract it, it gives me an Error "NotSupportedException" my Filepath Format is incorrect (which is not the case, I think?).
I copy and pasted the Code directly from here (Simple full extract from a TGZ (.tar.gz)) and edited it for my Needs:
using System.IO;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
//it executed the Script and created the file on the Linux Machine /tmp/logs.tgz
//now I want to download it
string myTime = DateTime.Now.ToString("yyyyMMdd");
var pathWithEnv = (#"%USERPROFILE%\Desktop\logs" + myTime + ".tgz");
var filePath = Environment.ExpandEnvironmentVariables(pathWithEnv);
string localFile = filePath;
//then downloads /tmp/logs.tgz to ..\Desktop\logs+ myTime +.tgz
//everything great until now. here I want to extract .TGZ:
var pathWithEnv2 = (#"%USERPROFILE%\Desktop\logs" + myTime);
var fileDir = Environment.ExpandEnvironmentVariables(pathWithEnv2);
string localDir = fileDir;
Stream inStream = File.OpenRead(localFile);
Stream gzipStream = new GZipInputStream(inStream);
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream);
//ERROR OCCURS HERE:
tarArchive.ExtractContents(localDir);
tarArchive.Close();
gzipStream.Close();
inStream.Close();
I even tried to set the localFile and localDir string without the EnviromentVariable, but that didnt help. I tried:
- download and extract it directly on C:\ (or on a mapped Network Drive U:) to prevent too long filenames (which should not be the case as it should never get longer than 86 characters).
- string = #"C:..\logs", string = "C:\..\logs", string = #"C:..\logs\", etc.
- tried it without myTime
- using ICSharpCode.SharpZipLib.Core;
I did a MessageBox.Show(localDir); before the tarArchive.ExtractContents(localDir); and it showed "C:\Users\Baumann\Desktop\logs20140530" which is correct, thats the Directory I want to extract it to. Even creating the Directory before executing it doesn't help.
I also created a new Project with just one button which should start the Extraction and the same error occurs.
I tried, doing it separately, first extract the GZip and then the .tar, but it also gives me the same Error when extracting the GZip (using ICSharpCode.SharpZipLib.Core; of course).
What drives me even more crazy about it, is, that it starts to extract it, but not everything, before it fails. And always the same Files, whats not clear for me why these and why not the others.
I'm on Windows 8.1, using SharpZipLib 0.86.0.518, downloaded directly from the Website.
Thanks in advance.
well, I finally fixed the Problem. The Linux machine is creating a file which includes the MAC-Adress and since Windows can't handle ":" in a Filename, it crashes.
I am now extracting file by file and checking each file for ":" and replacing it with "_", works flawlessly.

Categories