blob: b7a9b413b4adb0aa0bac84caa8c2693bf11a699c [file] [log] [blame]
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
#include <memory>
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/rtc_base/helpers.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/thread.h"
#include "webrtc/rtc_base/timeutils.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
namespace {
static const int kHeaderSize = 32;
static const int kFrameHeaderSize = 12;
static uint8_t dummy_payload[4] = {0, 1, 2, 3};
} // namespace
class IvfFileWriterTest : public ::testing::Test {
protected:
void SetUp() override {
file_name_ =
webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
}
void TearDown() override { rtc::RemoveFile(file_name_); }
bool WriteDummyTestFrames(VideoCodecType codec_type,
int width,
int height,
int num_frames,
bool use_capture_tims_ms) {
EncodedImage frame;
frame._buffer = dummy_payload;
frame._encodedWidth = width;
frame._encodedHeight = height;
for (int i = 1; i <= num_frames; ++i) {
frame._length = i % sizeof(dummy_payload);
if (use_capture_tims_ms) {
frame.capture_time_ms_ = i;
} else {
frame._timeStamp = i;
}
if (!file_writer_->WriteFrame(frame, codec_type))
return false;
}
return true;
}
void VerifyIvfHeader(rtc::File* file,
const uint8_t fourcc[4],
int width,
int height,
uint32_t num_frames,
bool use_capture_tims_ms) {
ASSERT_TRUE(file->IsOpen());
uint8_t data[kHeaderSize];
ASSERT_EQ(static_cast<size_t>(kHeaderSize), file->Read(data, kHeaderSize));
uint8_t dkif[4] = {'D', 'K', 'I', 'F'};
EXPECT_EQ(0, memcmp(dkif, data, 4));
EXPECT_EQ(0u, ByteReader<uint16_t>::ReadLittleEndian(&data[4]));
EXPECT_EQ(32u, ByteReader<uint16_t>::ReadLittleEndian(&data[6]));
EXPECT_EQ(0, memcmp(fourcc, &data[8], 4));
EXPECT_EQ(width, ByteReader<uint16_t>::ReadLittleEndian(&data[12]));
EXPECT_EQ(height, ByteReader<uint16_t>::ReadLittleEndian(&data[14]));
EXPECT_EQ(use_capture_tims_ms ? 1000u : 90000u,
ByteReader<uint32_t>::ReadLittleEndian(&data[16]));
EXPECT_EQ(1u, ByteReader<uint32_t>::ReadLittleEndian(&data[20]));
EXPECT_EQ(num_frames, ByteReader<uint32_t>::ReadLittleEndian(&data[24]));
EXPECT_EQ(0u, ByteReader<uint32_t>::ReadLittleEndian(&data[28]));
}
void VerifyDummyTestFrames(rtc::File* file, uint32_t num_frames) {
const int kMaxFrameSize = 4;
for (uint32_t i = 1; i <= num_frames; ++i) {
uint8_t frame_header[kFrameHeaderSize];
ASSERT_EQ(static_cast<unsigned int>(kFrameHeaderSize),
file->Read(frame_header, kFrameHeaderSize));
uint32_t frame_length =
ByteReader<uint32_t>::ReadLittleEndian(&frame_header[0]);
EXPECT_EQ(i % 4, frame_length);
uint64_t timestamp =
ByteReader<uint64_t>::ReadLittleEndian(&frame_header[4]);
EXPECT_EQ(i, timestamp);
uint8_t data[kMaxFrameSize] = {};
ASSERT_EQ(frame_length,
static_cast<uint32_t>(file->Read(data, frame_length)));
EXPECT_EQ(0, memcmp(data, dummy_payload, frame_length));
}
}
void RunBasicFileStructureTest(VideoCodecType codec_type,
const uint8_t fourcc[4],
bool use_capture_tims_ms) {
file_writer_ = IvfFileWriter::Wrap(rtc::File::Open(file_name_), 0);
ASSERT_TRUE(file_writer_.get());
const int kWidth = 320;
const int kHeight = 240;
const int kNumFrames = 257;
ASSERT_TRUE(WriteDummyTestFrames(codec_type, kWidth, kHeight, kNumFrames,
use_capture_tims_ms));
EXPECT_TRUE(file_writer_->Close());
rtc::File out_file = rtc::File::Open(file_name_);
VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFrames,
use_capture_tims_ms);
VerifyDummyTestFrames(&out_file, kNumFrames);
out_file.Close();
}
std::string file_name_;
std::unique_ptr<IvfFileWriter> file_writer_;
};
TEST_F(IvfFileWriterTest, WritesBasicVP8FileNtpTimestamp) {
const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
RunBasicFileStructureTest(kVideoCodecVP8, fourcc, false);
}
TEST_F(IvfFileWriterTest, WritesBasicVP8FileMsTimestamp) {
const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
RunBasicFileStructureTest(kVideoCodecVP8, fourcc, true);
}
TEST_F(IvfFileWriterTest, WritesBasicVP9FileNtpTimestamp) {
const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
RunBasicFileStructureTest(kVideoCodecVP9, fourcc, false);
}
TEST_F(IvfFileWriterTest, WritesBasicVP9FileMsTimestamp) {
const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
RunBasicFileStructureTest(kVideoCodecVP9, fourcc, true);
}
TEST_F(IvfFileWriterTest, WritesBasicH264FileNtpTimestamp) {
const uint8_t fourcc[4] = {'H', '2', '6', '4'};
RunBasicFileStructureTest(kVideoCodecH264, fourcc, false);
}
TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) {
const uint8_t fourcc[4] = {'H', '2', '6', '4'};
RunBasicFileStructureTest(kVideoCodecH264, fourcc, true);
}
TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) {
const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
const int kWidth = 320;
const int kHeight = 240;
const int kNumFramesToWrite = 2;
const int kNumFramesToFit = 1;
file_writer_ = IvfFileWriter::Wrap(
rtc::File::Open(file_name_),
kHeaderSize +
kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
ASSERT_TRUE(file_writer_.get());
ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
kNumFramesToWrite, true));
ASSERT_FALSE(file_writer_->Close());
rtc::File out_file = rtc::File::Open(file_name_);
VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true);
VerifyDummyTestFrames(&out_file, kNumFramesToFit);
out_file.Close();
}
} // namespace webrtc