How to get a .m4a audio file from a .opus audio file programmatically

2.3k views Asked by At

I am developing an application that manages Whatsapp voice notes. Some time ago WhatsApp started to save voice notes using the opus file format, instead of aac like previously.

My application gets a URI (via implicit intent) that points to an opus voice note stored in the WhatsApp folder.

Since opus audio files are impossible to play on many devices and in many apps (Viber for example), I would like to create in a separate file a m4a format version of the opus audio file that I get from WhatsApp.

What I tried to do is this: I used ContentResolver and openInputStream() to get an InputStream on the content represented by the Uri I get via implicit intent. Then I made a FileOutputStream on a file in my application folder. And I used a path that finishes with ".m4a" instead of ".opus".

Like this:

try {
                InputStream inputStream = getContentResolver().openInputStream(uriFromExternalIntent);
                String OUTPUT_DIRECTORY = Environment.getExternalStorageDirectory() + "/Audios/recordings/";


                boolean exists = (new File(OUTPUT_DIRECTORY)).exists();
                if (!exists) {
                    new File(OUTPUT_DIRECTORY).mkdirs();
                }

                Calendar c = Calendar.getInstance();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
                String strDate = sdf.format(c.getTime());
                //Setting the output file complete final path
                String OUTPUT_FILE = OUTPUT_DIRECTORY + strDate + ".m4a";
                File outputFile = new File(OUTPUT_FILE);
                OutputStream out = null;
                try {
                    out = new FileOutputStream(outputFile);
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (out != null) {
                            out.close();
                        }
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

By saving the file in a path that ends by ".m4a" on API 24 devices I am able to play the file inside my app and on apps like WhatsApp, Viber or Telegram. But on API 23 the same code gives errors on every app apart from WhatsApp.

To recap, I am looking for a way to programmatically get an .m4a audio file from a URI that points to a .opus audio file.

There are some apps that are able to do this without showing any kind of conversion, so I guess there a trick to do it easily without converting. Any advice?

1

There are 1 answers

5
Dave On

Copying the file with a different extension is definitely not the way to go.

You have a couple of options depending on how robust you want your app to be and how much work you are willing to put in.

The cheap and easy route is to find a free online file conversion tool that you can try to take advantage of in your app. I won't bother to link any particular service because I don't recommend this approach. Your app would be at the mercy of the service you chose for its stability. It's very likely doable, but it is also very ugly.

The more sound approach is to actually convert the file in your application. The easiest way to do this is to find a library that is capable of converting between the target and source formats. I don't know of any Android/Java audio conversion libraries, but I do know of a decent C library called SoX that has several Java wrappers available (e.g., here and here). The first of those links (the Guardian Project) is made for Android and includes both SoX and FFMpeg. It isn't particularly lightweight. The second doesn't seem to be for Android, so you may need to deal with finding an appropriate binary or building SoX yourself (hint: binaries at the Guardian Project are in "res/raw"). I have looked at SoX to confirm that it supports the Opus format. As a side note, FFMpeg supports Opus, as well. Once you get SoX or FFMpeg and a Java wrapper hooked up appropriately in your project, you should be able to use those to convert files. Depending on your use case, you should pay attention to the licenses of the libraries you choose to employ.

The hard way to convert the file is to write your own codec. You could potentially do this in Android Java using the MediaCodec class, but the Android MediaExtractor doesn't understand Opus (as far as I know) so that would not be a straightforward endeavor. I doubt you want to go down this road.