我有一个场景,我正在构建一个允许收听和存储.mp3播客文件的播客网络应用程序。
我正在尝试实现一个基本的Web界面,有人可以从客户端添加整个id3标记(该文件将存储在客户端本地:此客户端不像每个人客户端,但只是获得原始播客文件而没有任何id3标签的人最好)。然后他主持这一页本地添加正确的id3标签然后复制这些.mp3做一个WebDav文件夹。
我知道编辑需要在服务器上完成,但如果可以在浏览器本地完成所有操作,那将非常有用。
当然没有现成的库来编辑文件,所以我决定使用HTML5文件系统api,即将文件放入虚拟文件系统,编辑它然后将其复制回本地系统。 (对于复制,有一个现成的库FileSaver.js)。
我能够做到以下几点: 1)使用webkitGetAsEntry
将放置在放置区域的mp3文件关联到文件系统api2)将此文件复制到文件系统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)我们可以从fileWriter方法将整个id3标签添加到文件中吗? 2)如果是,这将是二进制编辑或如何?
任何帮助都会有用。尝试了以下,但我猜我错了。
var blob1 = new Blob(['ID3hTIT2ga'], {type: 'audio/mp3'});
fileWriter.write(blob1);
答案 0 :(得分: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位同步整数(section 6.2),共有28个有效位(最多256MB)。
ID3v2标签大小是扩展
的字节长度的总和 标头,填充和不同步后的帧。如果是 页脚存在这等于('总大小' - 20)字节,否则 ('总大小' - 10)字节。
如何构建缓冲区的示例。首先定义一个足以容纳所有数据的缓冲区以及DataView:
var id3buffer = new ArrayBuffer(1024), // 1kb "space"
view = new DataView(id3buffer);
DataView默认为big-endian,这是完美的,所以我们现在需要做的就是填写应该在哪里的数据。我们可以制作一些辅助方法来帮助我们在写作的同时移动位置。 DataView的位置是字节绑定的:
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);
如果一切顺利,你现在有一个带有ID3标签的MP3(记得要检查现有的ID3 - 你需要扫描到结束)。
现在,您可以将ArrayBuffer发送到服务器,或者转换为Blob用于IndexedDB,或者如果要提供下载链接(此处未显示,因为答案超出范围),可以将其转换为Object-URL。 / p>
这应该足以让你开始 - 如上所述,你需要研究规格。如果您不熟悉typed array,请查看这些内容。
另请参阅other resources(框架等)的网站。
“MP3”文件使用以11位开头的帧,全部设置为1.如果标题的大小字段恰好包含11位设置为1,则解码器可能会错误地将其解释为声音数据。为避免这种情况,使用同步安全整数的概念,确保每个字节的MSB(最重要的位,位7)始终设置为0.该位移到左侧,下一个字节移位一位,用于ID3标记4次(因此4x%01111111)。
以下是如何使用JavaScript(来自Wikipedia C/C++ source)对同步安全整数进行编码和解码:
// 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;
}
对于上面演示中使用的示例值,sync-safe值将显示如下内容:&b01111111011111110111111101111111
。