某些Android设备中的音频编码问题,用于在线流式播放

时间:2015-04-18 03:46:47

标签: javascript android html5-audio audio-streaming android-mediarecorder

背景:

我正在开发一个Android移动应用程序,用户在应用程序中录制音频,然后将其上传到服务器,然后将其传输回社区。

到目前为止,我们已经使用.WAV格式,它可以输出相当大的文件大小,以便通过移动网络上传和流式传输。我正在努力转换为.AAC格式,但我已经了解了不同制造商发生的一些不同问题。

的问题:

  • 到目前为止测试的所有Android手机(API-15 +)都可以在AAC录制音频。
  • 但是,Samsung设备不会使用MediaPlayer直接播放AAC音频文件。相反,它们需要在AAC中编码,但需要在OutputFormat .MP4
  • 中编码
  • .MP4容器中的AAC音频文件记录完美,并​​在所有测试设备上播放。
  • 文件可以很好地上传到服务器。

然后这是主要问题。将文件流式传输到音频播放器时,声音播放器无法解码仅在三星设备上录制的音频。摩托罗拉和Nexus手机上录制的AAC文件可与之前的WAV文件完美互换。我可以直接在桌面上播放声音文件,如果我直接在网络浏览器中打开文件,它们都会播放。

当我使用AudioContext对音频进行流式处理和解码时会发生此问题。我认为这个问题与三星音频编解码器缺少标题数据有关,但我已经在这个问题上投入了大量时间,并且还没有找到可行的解决方案。这个问题似乎很简单,但我需要做很多工作才能确切地找出问题所在。任何帮助深表感谢。

我看到的两种方法是:  1.修复Android应用程序中源代码的编码问题  2.纠正浏览器中的错误。我看过有关使用函数尝试重新同步流的帖子,但我不完全了解如何实现这一点。

我认为最好的解决方案是#1。

我在浏览器中下载了两个示例文件,并查看了原始数据以进行比较。 (原始数据的快照http://i.imgur.com/tF6uHoO.png //我没有足够的声誉嵌入图片,但稍后会修改。)

在Android中录制代码 RecordActiviy.java

// FILE NAME 
mFileName = "file.mp4";

// INITIATE MEDIA RECORDER
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioSamplingRate(48000);
mRecorder.setAudioEncodingBitRate(64000);

try {
    mRecorder.prepare();
    mRecorder.start();

    } catch (IOException e) {
        Log.e("AUDIO RECORDING", "prepare() failed");
        e.printStackTrace();
    }
}
...

getSound.php

...    
$File = $path . $filename;

switch($filetype){
case 'wav':
    //FILE TYPE WAV
    $mime_type = "audio/vnd.wave";
    header("Content-type: ".$mime_type);
    $test = readfile($file);
    break;

case 'mp4': 
    //FILE TYPE MP4
    //$mime_type = "audio/mp4";
    $mime_type = "audio/aac";
    header("Content-type: ".$mime_type);
    $test = readfile($file);
    break;
default:
    break;
}

soundPlayer.js

...
var player;

function loadPlayer() {
    player = new SoundPlayer();
    ..UI and other stuff..
}

// Build Sound Player Object
function SoundPlayer() {

this.arrayBuffer = null; //array buffer of audio data

this.context = new AudioContext(); //browser sound context

this.duration = null;   //Length of audio clip
this.source = null;   //Converted audio source
this.trueEnd = true;   //Check to see if sound ended due to completion
this.isPlaying = false;   //Check to see if sound is currently playing
this.t = 0;   //Update variable for time counter
this.startTime = null;   //Start time of audio is logged when playAudio()
this.byteData = null;

var sp = this; // sp = soundPlayer (Reference to SoundPlayer obj)

this.playAudio = function() {

    this.source = null;

    /* ArrayBuffer must be decoded each time play is called
     * due to the nature of a decoded AudioBuffer only 
     * being able to be played once */

    sp.context.decodeAudioData(sp.arrayBuffer, decoded); 
    sp.t = setInterval(updateProgress, 10);
};

this.loadSound = function(pid) {

    $.ajax({
        async: true,
        url: "getSound.php", 
        data: { soundID: sID },
        beforeSend: function(xhr) {

            //Allow for binary data
            xhr.overrideMimeType("text/plain; charset=x-user-defined");
        },
        success: function(dataAsString) {

            dataAsString = dataAsString.trim();

            if( dataAsString.localeCompare("") == 0 ){
                alert("Sound File Not Found");
                return;
            }

            // convert string to byte array 
            var bytes = [];
            var i, l = dataAsString.length;

            for (i = 0; i < l; ++i) {
                bytes.push(dataAsString.charCodeAt(i) & 0xFF);
            }

            sp.byteData = bytes;

            //Convert byte array into audio buffer
            var arrayBuffer = new ArrayBuffer(bytes.length);
            var bufferView = new Uint8Array(arrayBuffer);

            for (var i = 0; i < bytes.length; i++) {
                bufferView[i] = bytes[i];
            }

            sp.arrayBuffer = arrayBuffer;

            //get sound duration info
            sp.context.decodeAudioData(arrayBuffer, function(buffer) {

                sp.duration = buffer.duration;
                notify_UI_load_complete();

            }, function(error){
****            // WE GET TO THIS BLOCK IN THE BROWSER
****            // RETURNS UNDEFINED
                console.log("error decoding "+ arrayBuffer.length);
                console.log(arrayBuffer);
                console.log("error type "+ error);
            });

        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log("load error");
            console.log(textStatus, errorThrown);
            alert("Could not load specific sound file");
        }
    });
}
...

TL; DR Web Audio AudioContext中的Android Samsung AAC音频流错误。网络音频很难。我们是否可以获得通用标准。

1 个答案:

答案 0 :(得分:-1)

音频文件具有元数据信息。在某些文件中,它出现在文件末尾。在这种情况下,这些文件只能在完全下载时播放,即无法播放。

要使这些文件可流式化,您需要将该元数据信息移动到音频文件的开头。你可以使用MP4Box:

MP4Box -v -inter 300 old.mp4 -out new.mp4

其中300是以毫秒为单位的交错。