2015-06-12 34 views
0

我有場景,在那裏我建立一個播客的web應用程序,允許聆聽和存儲.mp3播客文件。添加id3標籤html5文件系統api

我想實現一個基本的Web界面,有人可以從客戶端添加整個ID3標籤(該文件將在本地客戶端上存儲:這個客戶是不是像大家客戶端,但只是最好是沒有任何id3標籤的原始播客文件)。然後,他在本地託管這一頁,然後添加正確的id3標籤,然後複製這些.mp3做一個WebDav文件夾。

我明白,編輯需要在服務器上完成,但如果瀏覽器本地完成所有操作,這將非常有幫助。

當然,沒有現成的庫來編輯文件,所以我決定使用HTML5文件系統api,即將文件放入虛擬文件系統,然後編輯它,然後將其複製回本地系統。 (對於複製,有一個準備好的庫FileSaver.js)。

我已經能夠做到以下幾點: 1)的MP3文件關聯下降在拖放區使用webkitGetAsEntry

2到文件系統API),然後將此文件複製到文件系統的API。代碼

部分看起來像:

function onDrop(e) 
{ 
    e.preventDefault(); 
    e.stopPropagation(); 

    var items = e.dataTransfer.items; 
    var files = e.dataTransfer.files; 

    for (var i = 0, item; item = items[i]; ++i) 
    { 
     // Skip this one if we didn't get a file. 
     if (item.kind != 'file') { 
      continue; 
     } 

     var entry = item.webkitGetAsEntry(); 

     if (entry.isFile) 
     { 
      // Copy the dropped entry into local filesystem. 
      entry.copyTo(cwd, null, function(copiedEntry) { 
      //setLoadingTxt({txt: DONE_MSG}); 
      renderMp3Writer(entry); 

我的困惑是如何添加的整個 ID3標籤? 。我迷失在這一點上,因爲我不確定:

1)我們可以將整個id3標籤添加到fileWriter方法的文件中嗎? 2)如果是,這是一個二進制編輯或如何? 。

任何幫助將是有益的。嘗試下面,但我猜我是錯的。

var blob1 = new Blob(['ID3hTIT2ga'], {type: 'audio/mp3'}); 
fileWriter.write(blob1); 

回答

1

你需要建立一個ID3緩衝,然後創建大到足以容納兩個ID3和MP3文件的緩衝區,插入ID3和追加MP3數據。

爲此,您需要ID3 specification並使用帶有DataView的類型化數組來構建您的數組。

的ID3整體結構的定義如下(見上面的鏈接):

+-----------------------------+ 
|  Header (10 bytes)  | 
+-----------------------------+ 
|  Extended Header  | 
| (variable length, OPTIONAL) | 
+-----------------------------+ 
| Frames (variable length) | 
+-----------------------------+ 
|   Padding   | 
| (variable length, OPTIONAL) | 
+-----------------------------+ 
| Footer (10 bytes, OPTIONAL) | 
+-----------------------------+ 

此時緩衝區長度是未知的,所以你需要做的步驟。有幾種方法可以做到這一點,您可以爲每個字段構建小緩衝區段,然後將它們彙總到單個緩衝區中。或者你可以製作一個更大的緩衝區,你知道可以容納你想要包含的所有字段,並將字段的總和從該緩衝區複製到最後一個。

後者往往更簡單,因爲我們正在處理非常小的尺寸,這可能是最好的方法(考慮到第一種方法中的每個片段都有其開銷)。

所以你需要做的第一件事就是定義標題。頭文件是這樣定義的:

ID3v2/file identifier  "ID3" 
ID3v2 version    $04 00 
ID3v2 flags    %abcd0000 (note: bit-representation) 
ID3v2 size    4 * %0xxxxxxx (note: bit-representation/mask) 

ID3和版本是固定值(其他版本當然存在,但讓我們跟隨當前)。

通過將它們設置爲0,您可能會忽略大多數標誌(如果不是全部),但請檢查文檔以查找您的用例,例如,如果要使用擴展標頭。

大小被定義:

的ID3v2標籤尺寸被存儲爲一個32位的整數synchsafe(部分 6.2),總共28個有效位(代表至多256MB)。

ID3v2標籤大小是擴展頭的字節長度,填充和非同步之後的幀之和的總和。如果存在頁腳
,則這等於('總大小'〜20)字節,否則爲 ('總大小'〜10)字節。

舉例說明如何構建緩衝區。首先定義大到足以容納所有的數據,以及一個數據視圖緩衝:

var id3buffer = new ArrayBuffer(1024), // 1kb "space" 
    view = new DataView(id3buffer); 

的數據視圖默認爲大端這是完美的,所以我們現在需要做的是在數據來填充它應該。我們可以提供一些幫助方法來幫助我們在寫作的同時移動位置。對於數據視圖位置是字節綁定:

var pos = 0; // global start position 

function setU8(value) { 
    view.setUint8(pos++, value) 
} 

function setU16(value) { 
    view.setUint16(pos, value); 
    pos += 2; 
} 

function setU32(value) { 
    view.setUint32(pos, value); 
    pos += 4; 
} 

等可以讓助手來寫文本unicode字符串(見TextEncoder例如)等等。

要定義標題,我們可以寫入「魔術」字ID3。你可以轉換一個字符串,或者因爲它只有3個字節,所以直接寫就可以了。 ID3 = 0x494433十六進制這樣:

setU8(0x49);  // at pos 0 
setU8(0x44);  // at pos 1 
setU8(0x33);  // at pos 2 

由於我們所取得的包裝,我們並不需要擔心緩衝位。

然後在版本寫(根據規範v.2.4.0使用的0x0400沒有使用主要版本(2)):

setU16(0x0400); // default is big-endian so this works 

現在你可以用標誌和尺寸(見規格)繼續。

當ID3標頭填滿時pos現在將保存總長度。所以要對ID3標籤和MP3緩衝區的新緩衝區:

var mp3 = new ArrayBuffer(pos + mp3Buffer.byteLength), 
    view8 = new Uint8Array(mp3); 

的view8視圖將允許我們做一個簡單的複製到目的地:

// create a segment from the tag buffer that will fit target: 
var segment = new Uint8Array(view.buffer, 0, n); // replace n with actual length 
view8.set(segment, 0); 
view8.set(mp3buffer, pos); 

如果一切正常,你現在有一個MP3一個ID3標籤(記得檢查現有的ID3 - 你需要掃描到結束)。

現在,您可以將ArrayBuffer發送到服務器,或者如果您希望提供下載鏈接(此處沒有顯示答案正在變得超出範圍),則可以將其轉換爲用於IndexedDB的Blob或Object-URL。

這應該足以讓你開始 - 如上所述,你需要研究規格。如果您對typed array不熟悉,請檢查這些。

另請參閱網站other resources(框架等)。

同步安全值

「MP3」文件使用與11位開始幀,全部設置爲1,如果頭的大小字段會包含設置爲1 11位,解碼器可能錯誤地它作爲聲音數據。爲了避免這種情況,使用同步安全整數的概念,確保每個字節的MSB(最重要的位,第7位)始終設置爲0.該位移動到左側,下一個字節移動一位,對於ID3標記4次(因此是4x%01111111)。

下面是如何(從Wikipedia C/C++ source)編碼和解碼使用JavaScript同步安全整數:

// test values 
 
var value = 0xfffffff, 
 
    sync = intToSyncsafe(value); 
 
document.write("<pre>Original size: 0x" + value.toString(16) + "<br>"); 
 
document.write("Synch-safe : 0x" + sync.toString(16) + "<br>"); 
 
document.write("Decoded value: 0x" + syncsafeToInt(sync).toString(16) + "</pre>"); 
 

 

 
function intToSyncsafe(value) { 
 
    var out, mask = 0x7f; 
 
    while(mask^0x7fffffff) { 
 
     out = value & ~mask; 
 
     out <<= 1; 
 
     out |= value & mask; 
 
     mask = ((mask + 1) << 8) - 1; 
 
     value = out; 
 
    } 
 
    return out 
 
} 
 

 
function syncsafeToInt(value) { 
 
    var out = 0, mask = 0x7F000000; 
 
    while (mask) { 
 
     out >>= 1; 
 
     out |= value & mask; 
 
     mask >>= 8; 
 
    } 
 
    return out; 
 
}

同步安全值將顯示像的位:&b01111111011111110111111101111111爲例如在上面的演示中使用的值。

+0

只是對標題標籤的標籤大小有疑問。根據id3規範,它說'ID3v2標籤大小存儲爲32位同步安全整數( 6.2),總共有28位有效位(代表高達256MB)。 ID3v2標籤大小是擴展的 標頭的字節長度,填充和非同步之後的幀之和。如果存在 頁腳,則等於('總大小'〜20)字節,否則爲 ('總大小'〜10)字節。目前,我只對代碼中的標題進行編碼,因此可以將其設置爲默認值:4 *%0xxxxxxx –

+0

它是一個同步安全值(請參閱doc中的6.2)。它需要以特殊的方式進行編碼,以便第7位(MSB)始終爲0.第7位移動到左側,然後該位的第7位執行相同操作,總共4次。在答案中增加了解決方案。 (並且由於我們將一個位設置爲0,所以我們放鬆了4位,因此最大尺寸爲28位)。由於該領域非常重要,因此文件可能會更清晰。 – K3N

+0

只是觀察一個問題。 'view8 = new Uint8Array(mp3); '。當我直接檢查「視圖」的blob,即var id3buffer = new ArrayBuffer(1024); view = new DataView(id3buffer);'正確顯示的十六進制顯示正確:'文件名:YourFileName(13).mp3 mime類型:0000-0010:49 44 33 00-00 00 00 00-00 00 00 00- 00 00 00 00 ID3'。但是對於類型化的數組,它顯示爲全部0'var mp3 = new ArrayBuffer(pos); view8 = new Uint8Array(mp3); view8.set(view.buffer,0);',當我查看mp3 Arraybuffer的值時,它全部爲0。 '文件名:YourFileName(14).mp3 mime類型:0000-0003:00 00 00' –