Play m4a audio file on HTML5 page

Time:2021-10-13

Business scenario:

The recording is recorded on the app side of the mobile phone, and then uploaded to the background server. The front end obtains the recording from the background server and plays it on the web page of the PC side.

Practical problems:

First of all, the app recording file defaults to m4a format. On the web H5 page on the PC side, the < audio > tag does not explicitly say that m4a format is supported. If the recording generated on the app side does not make relevant settings, but uses the default settings, it can not be played on H5.

In fact, at the beginning, I didn’t think too much, but also wanted to turn m4a files into MP3 for the front desk.

After checking on the Internet, many people say that they use jave-1.0.2.2.jar. However, in fact, this package is very old and can be transferred on windows, but CentOS 8 does not support m4a format transcoding, which has compatibility problems on the system.Trust me, don’t use it

Then I found this package in the code library. Here is a link:https://github.com/a-schild/jave2, this package is also based on ffmpeg and provides dependencies supporting win64, osx64 and linux64. It is suggested that when Maven is packaged, it refers to the dependencies of the corresponding environment according to different development or production environments.

Attached below is my m4a to MP3 java code:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com</groupId>
	<artifactId>test</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>pom</packaging>
 
    <properties>       
        <jave.version>2.7.1</jave.version>
    </properties>
 
	<dependencyManagement>
		<dependencies>           
            <!-- For recording conversion, Jave all DEPs includes the dependencies of all platforms. Because the packaging is too large, it is recommended to select the specified dependency -- > when packaging
            <!--<dependency>-->
                <!--<groupId>ws.schild</groupId>-->
                <!--<artifactId>jave-all-deps</artifactId>-->
                <!--<version>${jave.version}</version>-->
            <!--</dependency>-->
            <!-- For recording conversion, specify platform dependency. Jave core must specify -- >
            <dependency>
                <groupId>it.sauronsoftware</groupId>
                <artifactId>jave</artifactId>
                <groupId>ws.schild</groupId>
                <artifactId>jave-core</artifactId>
                <version>${jave.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
   
    <!-- Activate profile configuration to switch the configuration of different environments -- >
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <profiles.actives>dev</profiles.actives>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
                <activeByDefault>false</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-linux64</artifactId>
                    <version>${jave.version}</version>
                </dependency>
            </dependencies>
        </profile>
 
        <profile>
            <id>pro</id>
            <properties>
                <profiles.actives>pro</profiles.actives>
            </properties>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-linux64</artifactId>
                    <version>${jave.version}</version>
                </dependency>
            </dependencies>
        </profile>
 
        <profile>
            <id>test</id>
            <properties>
                <profiles.actives>test</profiles.actives>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-win64</artifactId>
                    <version>${jave.version}</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
 
</project>

Audio file conversion code:

package com.utils;
 
import com.alibaba.fastjson.JSON;
import com.qirui.framework.common.base.syslog.SysLog;
import com.qirui.framework.common.base.syslog.SysLogAnnotation;
import com.qirui.framework.common.base.syslog.SysLogPrint;
import com.qirui.framework.common.utils.RequestUtil;
import org.springframework.stereotype.Component;
import ws.schild.jave.*;
 
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
 
/**
 * @ClassName AudioTransUtil
 *@ description recording conversion
 * @Author admin
 * @Version 1.0.0
 **/
@Component
public class AudioTransUtil {
    static {
    //The project is the springboot jar package. The code in the jar package needs to deal with the reading path to read the files in the external folder,
    //Here, put the recording source files and conversion files in the same level folder of the springboot jar package
        String path = AudioTransUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
 
        if(path.contains("jar")){
            //file:/F:/ideaWorkspace/test/smp-admin/framework-client/target/framework-client-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/framework-service-0.0.1-SNAPSHOT.jar!/
            //Remove "file:"
            path = path.substring(path.indexOf("/"), path.length());
        }
        if(System.getProperty("os.name").contains("dows")) {
            path = path.substring(1, path.length());
            //Jar package for widonws
            if(path.contains("jar")){
                path = path.substring(0, path.indexOf(".jar"));
                rootPath = path.substring(0, path.lastIndexOf("/"));
            }else{
                rootPath =  path.replace("/target/classes/", "");
            }
        }else if(System.getProperty("os.name").contains("Mac")){
            rootPath = path.replace("/target/classes/", "");
        }
        else {
            path = path.substring(0, path.indexOf(".jar"));
            rootPath = path.substring(0, path.lastIndexOf("/"));
        }
    }
 
 
    protected static final String rootPath;
    /**
     *Directory path
     */
    private static final StringBuilder dirPathStr = new StringBuilder(rootPath).append("/temp/audio/");
    private static final String MP3 = "mp3";
 
    @Syslogannotation (describe = "recording conversion format")
    public String trans2Mp3(byte[] sourceAudioBytes, String sourceAudioName){
        //File path
        String soureAudioFilePathStr = new StringBuilder(dirPathStr).append(sourceAudioName).toString();
        String sourceAudioType = sourceAudioName.substring(sourceAudioName.indexOf(".")+1);
        String targetAudioFilePathStr = new StringBuilder(soureAudioFilePathStr).toString().replace(sourceAudioType, MP3);
 
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        try{
            File dir = new File(dirPathStr.toString());
            if(!dir.exists()){
                dir.mkdirs();
            }
 
            File sourceAudioFile = new File(soureAudioFilePathStr);
            fos = new FileOutputStream(sourceAudioFile);
            bos = new BufferedOutputStream(fos);
            bos.write(sourceAudioBytes);
 
            File targetAudioFile = new File(targetAudioFilePathStr);
 
            AudioAttributes audioAttributes = new AudioAttributes();
            audioAttributes.setCodec("libmp3lame");
            audioAttributes.setBitRate(new Integer(32000));
//            audioAttributes.setChannels(new Integer(2));
//            audioAttributes.setSamplingRate(new Integer(22050));
 
            EncodingAttributes attrs = new EncodingAttributes();
            attrs.setFormat("mp3");
            attrs.setAudioAttributes(audioAttributes);
 
            Encoder encoder = new Encoder();
 
            //Add when necessary. You can view the file formats that support processing according to different system environments
            System.out.println("encoder.getVideoDecoders():" + JSON.toJSON(encoder.getVideoDecoders()).toString());
            System.out.println("encoder.getSupportedDecodingFormats():" + JSON.toJSON(encoder.getSupportedDecodingFormats()).toString());
 
            MyJaveListener myJaveListener = new MyJaveListener();
 
            encoder.encode(new MultimediaObject(sourceAudioFile), targetAudioFile, attrs, myJaveListener);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(null != bos){
                    bos.close();
                }
                if(null != fos){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
        SysLog sysLog = new SysLog();
        sysLog.setLogId(RequestUtil.getAccessLogId());
        sysLog.setParams(targetAudioFilePathStr);
        Syslog.setdescribe ("recording conversion path");
        SysLogPrint.printSysLogBody(sysLog);
 
        return targetAudioFilePathStr;
    }
 
    //Delete local temporary recording
    public void deleteTempAudio(String fileName){
        //File path
        String fileNameTemp = fileName.substring(fileName.lastIndexOf("/")+1, fileName.length());
        String soureAudioFilePathStr = new StringBuilder(dirPathStr).append(fileNameTemp).toString();
        String sourceAudioType = fileName.substring(fileName.indexOf(".")+1);
        String targetAudioFilePathStr = new StringBuilder(soureAudioFilePathStr).toString().replace(sourceAudioType, MP3);
 
        File file = new File(soureAudioFilePathStr);
        file.delete();
        file = new File(targetAudioFilePathStr);
        file.delete();
    }
 
    /**
     *Recording transcoding processing listener, which can listen to file processing results, is very useful for error messages
     */
    private class MyJaveListener implements EncoderProgressListener {
        @Override
        public void sourceInfo(MultimediaInfo multimediaInfo) {
            System.out.println("MyListener.sourceInfo:" + JSON.toJSON(multimediaInfo).toString());
        }
 
        @Override
        public void progress(int i) {
            System.out.println("MyListener.progress:" + i);
        }
 
        @Override
        public void message(String s) {
            System.out.println("MyListener.message:" + s);
        }
    }
}

The above code can be converted normally in CentOS 8 environment. At the beginning, my production environment also used this code.

Later, I looked for the difference between m4a and MP3 and MP4, and found that MP4 is AAC coding encapsulated with MPEG-4, and the essence of m4a is the same as audio MP4. It is an extension used by apple to distinguish between pure audio MP4 files and MP4 files containing video.

So the question is, m4a and MP4 are essentially the same, and even browser H5 can play MP4. Why m4a can’t? In terms of audio coding, AAC coding is the key to solve the problem.

Several key screenshots of Android internal output recording code are attached below:

If it is not set by default, audioencoder is 0. 0 is not AAC coding. We need to set MPEG in the output format_ 4. Set the encoding format to AAC,

As shown in the third figure:


setOutPutFormat(MediaRecorder.OutputFormat.MPEG_4)

setAudioEncoder(MediaRecorder.AudioEncoder.AAC)

In this way, the generated m4a recording file can be played directly in the H5 page of the browser without background. In the whole program, not only the transcoding time of the code is reduced, but also the m4a file itself is very small.

This is the end of this article about playing m4a audio files on the HTML5 page. For more information about playing m4a on the HTML5 page, please search the previous articles of developeppaer or continue to browse the relevant articles below. I hope you will support developeppaer in the future!