前言
WebRTC 中每个音视频处理环节都对应着很多的类和结构体,它们中的很多都与视频 YUV 数据打交道,当分析到某个位置时,很希望知道这里的音视频数据是否正常,最好的方式就在这里把音视频数据保存下来,然后使用相应的播放器或者分析工具进行分析验证。本文以视频为例介绍两个常见的类保存 YUV 数据的方法。
接下来,针对 webrtc::VideoFrame 和 webrtc::I420Buffer 两种类型介绍如何保存其中 YUV 数据的方法。
webrtc::VideoFrame 类型是 WebRTC 中常见的数据类型,保存 webrtc::VideoFrame 类型 YUV 数据的代码实例如下:
rtc::scoped_refptr<webrtc::VideoFrameBuffer> vfb = frame.video_frame_buffer();
static FILE *fp = fopen(file, "wb+");
if (fp != NULL) {
fwrite(vfb.get()->GetI420()->DataY(), 1, frame.height() * frame.width(), fp);
fwrite(vfb.get()->GetI420()->DataU(), 1, frame.height() * frame.width() / 4, fp);
fwrite(vfb.get()->GetI420()->DataV(), 1, frame.height() * frame.width() / 4, fp);
fflush(fp);
}
webrtc::I420Buffer 类型是 WebRTC 中常见的数据类型,保存 webrtc::I420Buffer 类型 YUV 数据的代码实例如下:
i420_buffer_ = webrtc::I420Buffer::Create(frame_width, frame_height);
libyuv::ConvertToI420(output_data, 0, i420_buffer_->MutableDataY(),
i420_buffer_->StrideY(), i420_buffer_->MutableDataU(),
i420_buffer_->StrideU(), i420_buffer_->MutableDataV(),
i420_buffer_->StrideV(), 0, 0, frame_width,
frame_height, frame_width, frame_height,
libyuv::kRotate0, libyuv::FOURCC_ARGB);
std::string fileName = "G:\\record_" + std::to_string(frame_width) + "x" + std::to_string(frame_height) + ".yuv";
static FILE *fp = fopen(fileName.c_str(), "wb+");
if (fp != NULL) {
fwrite(i420_buffer_->MutableDataY(), 1, frame_width * frame_height, fp);
fwrite(i420_buffer_->MutableDataU(), 1, frame_width * frame_height / 4, fp);
fwrite(i420_buffer_->MutableDataV(), 1, frame_width * frame_height / 4, fp);
fflush(fp);
}
注意:尽量用“wb+”打开文件,不要用“ab+”模式,由于后者不清空原有文件,很容易误导我们以为代码没有生效,因为 yuv 文件播放到最后才是最新写入的内容。
当然这也要看具体需求,接下来简单对比一下“ab+”和“wb+”的异同:
a. 以二进制形式打开文件。
b. 打开文件进行“读写”操作,即既可读取,又可写入。
c. 若欲操作的文件不存在,则新建文件。
d. 成功打开文件时,文件指针位于文件开头。
e. 打开文件后,会清空文件内原有的内容。
f. 无论是读取内容还是写入内容,都可在文件中任意位置进行,且进行写入操作时,会覆盖原有位置的内容。
a. 以二进制形式打开文件。
b. 打开文件进行“读写”操作,即既可读取,又可写入。
c. 若欲操作的文件不存在,则新建文件。
d. 成功打开文件时,文件指针位于文件结尾。
e. 打开文件后,不会清空文件内原有内容。
f. 读取内容时,可以在任意位置进行,但写入内容时,只会追加在文件尾部。