在处理音频流时,有时需要在流的开头添加音频头信息。对于 MP3 音频文件,ID3v2 标签通常用于存储音频头信息。以下示例展示了如何在后端处理流并在其中拼接 ID3v2 音频头信息。
步骤
- 添加依赖:
- 确保你的项目中包含处理 MP3 的依赖,比如使用 Apache Commons IO 和 Jaudiotagger 库。
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.jaudiotagger</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies>
- 后端代码:
- 使用 Jaudiotagger 库读取 MP3 文件的 ID3v2 标签,并将其与音频数据流拼接。
import org.apache.commons.io.IOUtils;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.id3.ID3v24Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@RestController
public class AudioStreamController {
private static final String AUDIO_FILE_PATH = "/path/to/your/audio/file.mp3";
@GetMapping("/audio-stream")
public void streamAudio(HttpServletResponse response) throws IOException {
File audioFile = new File(AUDIO_FILE_PATH);
byte[] id3v2TagData = getID3v2TagData(audioFile);
response.setContentType("audio/mpeg");
response.setHeader("Connection", "keep-alive");
try (InputStream audioInputStream = audioFile.toURI().toURL().openStream();
OutputStream outputStream = response.getOutputStream()) {
// Write ID3v2 tag data to the response output stream
outputStream.write(id3v2TagData);
outputStream.flush();
// Write the rest of the audio data to the response output stream
IOUtils.copy(audioInputStream, outputStream);
}
}
private byte[] getID3v2TagData(File audioFile) {
try {
AudioFile f = AudioFileIO.read(audioFile);
Tag tag = f.getTag();
if (tag instanceof ID3v24Tag) {
ID3v24Tag id3v2Tag = (ID3v24Tag) tag;
return id3v2Tag.toBytes();
}
} catch (Exception e) {
e.printStackTrace();
}
return new byte[0];
}
}
关键点
-
Jaudiotagger 库:
- 使用
AudioFileIO.read
方法读取 MP3 文件。 - 使用
ID3v24Tag.toBytes()
方法获取 ID3v2 标签的字节数据。
- 使用
-
流处理:
- 将 ID3v2 标签数据写入响应输出流。
- 将音频文件的其余部分通过流写入响应输出流。
测试
启动 Spring Boot 应用程序并访问 /audio-stream
端点,确保浏览器或音频播放器能够播放音频流,并且流的开头包含正确的 ID3v2 标签信息。
注意事项
- 确保 MP3 文件包含 ID3v2 标签。
- 根据需要调整 ID3 标签读取和写入逻辑,以适应不同的音频文件和标签版本。
通过以上步骤,你可以在后端流中拼接 ID3v2 音频头信息,从而使前端或客户端在接收音频流时能够识别并播放带有标签信息的音频文件。
后端 流里面 拼接 V3 音频的头信息,同时我还不想引入其他jar,有办法来实现么;
可以在不引入额外库的情况下实现这一功能。我们可以手动构建 ID3v2.3 标签并将其与音频流拼接。虽然这需要一些底层的字节操作,但可以实现你的要求。
手动构建 ID3v2.3 标签
ID3v2.3 标签有一个固定的结构:
-
ID3v2.3 标签头:
- 标签头固定为10个字节。
- 前3个字节为标识符 "ID3"。
- 接下来的2个字节为版本号和子版本号(分别为 3 和 0)。
- 第6个字节为标志(一般为0)。
- 最后4个字节为标签的大小。
-
标签大小:
- 标签大小为32位无符号整数,用同步安全编码表示。
- 同步安全编码将每个字节的最高位设为0,实际大小要按字节对齐。
-
标签帧:
- 每个标签帧包含一个标识符(4个字节)、大小(4个字节)和标志(2个字节)。
以下是一个示例代码,展示如何手动创建 ID3v2.3 标签并将其与音频流拼接:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
@RestController
public class AudioStreamController {
private static final String AUDIO_FILE_PATH = "/path/to/your/audio/file.mp3";
@GetMapping("/audio-stream")
public void streamAudio(HttpServletResponse response) throws IOException {
File audioFile = new File(AUDIO_FILE_PATH);
byte[] id3v2TagData = createID3v2Tag("Title", "Artist", "Album");
response.setContentType("audio/mpeg");
response.setHeader("Connection", "keep-alive");
try (FileInputStream audioInputStream = new FileInputStream(audioFile);
OutputStream outputStream = response.getOutputStream()) {
// Write ID3v2 tag data to the response output stream
outputStream.write(id3v2TagData);
outputStream.flush();
// Write the rest of the audio data to the response output stream
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = audioInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
outputStream.flush();
}
}
}
private byte[] createID3v2Tag(String title, String artist, String album) throws IOException {
// Create frames
byte[] titleFrame = createFrame("TIT2", title);
byte[] artistFrame = createFrame("TPE1", artist);
byte[] albumFrame = createFrame("TALB", album);
// Calculate total size
int totalSize = titleFrame.length + artistFrame.length + albumFrame.length;
byte[] sizeBytes = intToSynchsafeBytes(totalSize);
// Create header
byte[] header = new byte[10];
header[0] = 'I';
header[1] = 'D';
header[2] = '3';
header[3] = 3; // Version
header[4] = 0; // Subversion
header[5] = 0; // Flags
System.arraycopy(sizeBytes, 0, header, 6, 4);
// Combine header and frames
byte[] id3Tag = new byte[header.length + totalSize];
System.arraycopy(header, 0, id3Tag, 0, header.length);
System.arraycopy(titleFrame, 0, id3Tag, header.length, titleFrame.length);
System.arraycopy(artistFrame, 0, id3Tag, header.length + titleFrame.length, artistFrame.length);
System.arraycopy(albumFrame, 0, id3Tag, header.length + titleFrame.length + artistFrame.length, albumFrame.length);
return id3Tag;
}
private byte[] createFrame(String frameID, String content) throws IOException {
byte[] contentBytes = content.getBytes("UTF-8");
byte[] sizeBytes = intToBytes(contentBytes.length);
byte[] frame = new byte[10 + contentBytes.length];
System.arraycopy(frameID.getBytes("UTF-8"), 0, frame, 0, 4);
System.arraycopy(sizeBytes, 0, frame, 4, 4);
frame[8] = 0; // Frame flags (two bytes, set to 0)
frame[9] = 0;
System.arraycopy(contentBytes, 0, frame, 10, contentBytes.length);
return frame;
}
private byte[] intToBytes(int value) {
return new byte[]{
(byte) ((value >> 24) & 0xFF),
(byte) ((value >> 16) & 0xFF),
(byte) ((value >> 8) & 0xFF),
(byte) (value & 0xFF)
};
}
private byte[] intToSynchsafeBytes(int value) {
return new byte[]{
(byte) ((value >> 21) & 0x7F),
(byte) ((value >> 14) & 0x7F),
(byte) ((value >> 7) & 0x7F),
(byte) (value & 0x7F)
};
}
}
关键点
-
创建 ID3v2 标签:
- 构建 ID3v2.3 标签头信息,并手动创建每个帧。
- 使用 UTF-8 编码存储帧内容。
-
计算标签大小:
- 使用同步安全编码表示标签的大小。
-
流处理:
- 将 ID3v2 标签数据写入响应输出流。
- 将音频文件的其余部分通过流写入响应输出流。
通过这种方式,你可以手动创建并拼接 ID3v2.3 标签头信息到音频流中,而无需引入额外的库。