CarPlayPacketDumper
是一个用于记录和存储 CarPlay 数据包(特别是 H.264 视频帧)的工具类。它将接收到的数据帧按顺序写入文件,并提供开始、停止和写入操作。
public class CarPlayPacketDumper {
private static final String TAG = "CarPlayPacketDumper";
private final String filePath;
private FileOutputStream fos; // 文件输出流
private FileChannel channel; // 文件通道,提供更高效的文件操作
public boolean isRecording = false; // 记录状态标志
private final AtomicLong writtenBytes = new AtomicLong(0); // 线程安全的计数器
public CarPlayPacketDumper(String filePath) {
this.filePath = filePath;
}
public synchronized void start() throws IOException {
Log.d(TAG, "启动H.264顺序录制...");
stop(); // 确保先关闭现有资源
// 追加模式(false表示覆盖原有文件)
fos = new FileOutputStream(filePath, false);
channel = fos.getChannel(); // 获取文件通道
isRecording = true;
writtenBytes.set(0);
Log.d(TAG, "已清空并准备写入: " + filePath);
}
public synchronized void writeFrame(ByteBuffer frame) throws IOException {
if (!isRecording) {
Log.e(TAG, "当前未在录制状态!");
throw new IllegalStateException("Not recording");
}
final int frameSize = frame.remaining(); // 获取剩余字节数
// 调试信息:采样前16字节
byte[] sample = new byte[Math.min(16, frameSize)];
frame.get(sample); // 读取数据到字节数组
Log.d(TAG, String.format(
"写入帧 [大小:%,d字节] [头16字节:%s]",
frameSize,
bytesToHex(sample)
));
frame.rewind(); // 重置position
// 关键点:直接顺序写入
channel.write(frame);
writtenBytes.addAndGet(frameSize);
}
public synchronized void stop() {
if (isRecording) {
isRecording = false;
Log.d(TAG, "停止录制,总写入大小: " + formatFileSize(writtenBytes.get()));
try {
if (channel != null) {
channel.force(true); // 强制刷盘
channel.close();
}
if (fos != null) fos.close();
} catch (IOException e) {
Log.e(TAG, "关闭资源失败", e);
} finally {
channel = null;
fos = null;
}
}
}
// 辅助方法:字节转十六进制
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
// 辅助方法:格式化文件大小
private static String formatFileSize(long bytes) {
if (bytes < 1024) return bytes + "B";
return String.format("%.2fMB", bytes / (1024.0 * 1024.0));
}
}
//使用示例
//1.初始化
private final CarPlayPacketDumper dumper = new CarPlayPacketDumper("/storage/emulated/0/Movies/video.h264");
//2.开始录制
dumper.start();
//3.写入帧数据
// 在CarPlay视频流处理中的典型用法
public void onVideoFrameReceived(ByteBuffer frame) {
if (dumper != null && dumper.isRecording) {
try {
dumper.writeFrame(frame);
} catch (IOException e) {
Log.e(TAG, "写入帧失败", e);
dumper.stop();
}
}
}
//4.停止录制
dumper.stop();
//5.异常处理:必须处理IOException,调用stop()确保资源释放