簡體   English   中英

某些Android設備中用於在線流播放的音頻編碼問題

[英]Audio encoding issues in certain Android devices for online streaming playback

背景:

我正在開發一個Android移動應用程序,用戶在該應用程序中記錄音頻,然后將其上傳到服務器,然后可以在其中流回社區。

到目前為止,我們一直使用.WAV格式,該格式輸出很大的文件大小,以便通過移動網絡進行上傳和流式傳輸。 我正在轉換為.AAC格式,但是我了解到不同制造商會發生一些不同的問題。

問題:

  • 到目前為止,所有經過測試的Android手機(API-15 +)都能以AAC錄制音頻。
  • 但是,三星設備將不會使用MediaPlayer直接播放AAC音頻文件。 相反,它們需要在AAC中進行編碼,但必須使用OutputFormat .MP4進行編碼。
  • .MP4容器中的AAC音頻文件可以完美記錄並在所有測試的設備上播放。
  • 文件可以上傳到服務器。

然后這是主要問題。 將文件流傳輸到音頻播放器時,聲音播放器無法解碼僅在Samsung設備上記錄的音頻。 摩托羅拉和Nexus手機上記錄的AAC文件可與以前的WAV文件完美互換。 我可以直接在桌面上播放聲音文件,如果直接在Web瀏覽器中打開聲音文件,它們都可以播放。

當我使用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 Android音頻WebContext中的Android Samsung AAC音頻流錯誤。 網絡音頻很難。 我們可以得到一個通用標准了嗎?

音頻文件具有元數據信息。 在某些文件中,它位於文件末尾。 在這種情況下,這些文件僅在完全下載后才能播放,即無法流式傳輸。

為了使這些文件可流式傳輸,您需要將元數據信息移動到音頻文件的開頭。 您可以使用MP4Box:

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

其中300是以毫秒為單位的交織。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM