Expose Ivf logging through the native API
BUG=webrtc:6300 Review-Url: https://codereview.webrtc.org/2303273002 Cr-Commit-Position: refs/heads/master@{#14419}
This commit is contained in:
parent
242d8bdddd
commit
e75f204b06
@ -15,6 +15,7 @@
|
||||
|
||||
#include "webrtc/api/call/audio_sink.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/platform_file.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/media/base/rtputils.h"
|
||||
|
||||
@ -182,6 +183,13 @@ webrtc::VideoSendStream::Stats FakeVideoSendStream::GetStats() {
|
||||
return stats_;
|
||||
}
|
||||
|
||||
void FakeVideoSendStream::EnableEncodedFrameRecording(
|
||||
const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) {
|
||||
for (rtc::PlatformFile file : files)
|
||||
rtc::ClosePlatformFile(file);
|
||||
}
|
||||
|
||||
void FakeVideoSendStream::ReconfigureVideoEncoder(
|
||||
webrtc::VideoEncoderConfig config) {
|
||||
if (config.encoder_specific_settings != NULL) {
|
||||
@ -258,6 +266,11 @@ void FakeVideoReceiveStream::SetStats(
|
||||
stats_ = stats;
|
||||
}
|
||||
|
||||
void FakeVideoReceiveStream::EnableEncodedFrameRecording(rtc::PlatformFile file,
|
||||
size_t byte_limit) {
|
||||
rtc::ClosePlatformFile(file);
|
||||
}
|
||||
|
||||
FakeCall::FakeCall(const webrtc::Call::Config& config)
|
||||
: config_(config),
|
||||
audio_network_state_(webrtc::kNetworkUp),
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#define WEBRTC_MEDIA_ENGINE_FAKEWEBRTCCALL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/api/call/audio_receive_stream.h"
|
||||
@ -123,6 +124,9 @@ class FakeVideoSendStream final
|
||||
return num_encoder_reconfigurations_;
|
||||
}
|
||||
|
||||
void EnableEncodedFrameRecording(const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) override;
|
||||
|
||||
private:
|
||||
// rtc::VideoSinkInterface<VideoFrame> implementation.
|
||||
void OnFrame(const webrtc::VideoFrame& frame) override;
|
||||
@ -162,6 +166,9 @@ class FakeVideoReceiveStream final : public webrtc::VideoReceiveStream {
|
||||
|
||||
void SetStats(const webrtc::VideoReceiveStream::Stats& stats);
|
||||
|
||||
void EnableEncodedFrameRecording(rtc::PlatformFile file,
|
||||
size_t byte_limit) override;
|
||||
|
||||
private:
|
||||
// webrtc::VideoReceiveStream implementation.
|
||||
void Start() override;
|
||||
@ -257,4 +264,4 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver {
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
#endif // TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_
|
||||
#endif // WEBRTC_MEDIA_ENGINE_FAKEWEBRTCCALL_H_
|
||||
|
||||
@ -10,48 +10,47 @@
|
||||
|
||||
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
||||
|
||||
// TODO(palmkvist): make logging more informative in the absence of a file name
|
||||
// (or get one)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
IvfFileWriter::IvfFileWriter(const std::string& file_name,
|
||||
std::unique_ptr<FileWrapper> file,
|
||||
VideoCodecType codec_type)
|
||||
: codec_type_(codec_type),
|
||||
const size_t kIvfHeaderSize = 32;
|
||||
|
||||
IvfFileWriter::IvfFileWriter(rtc::File file, size_t byte_limit)
|
||||
: codec_type_(kVideoCodecUnknown),
|
||||
bytes_written_(0),
|
||||
byte_limit_(byte_limit),
|
||||
num_frames_(0),
|
||||
width_(0),
|
||||
height_(0),
|
||||
last_timestamp_(-1),
|
||||
using_capture_timestamps_(false),
|
||||
file_name_(file_name),
|
||||
file_(std::move(file)) {}
|
||||
file_(std::move(file)) {
|
||||
RTC_DCHECK(byte_limit == 0 || kIvfHeaderSize <= byte_limit)
|
||||
<< "The byte_limit is too low, not even the header will fit.";
|
||||
}
|
||||
|
||||
IvfFileWriter::~IvfFileWriter() {
|
||||
Close();
|
||||
}
|
||||
|
||||
const size_t kIvfHeaderSize = 32;
|
||||
|
||||
std::unique_ptr<IvfFileWriter> IvfFileWriter::Open(const std::string& file_name,
|
||||
VideoCodecType codec_type) {
|
||||
std::unique_ptr<IvfFileWriter> file_writer;
|
||||
std::unique_ptr<FileWrapper> file(FileWrapper::Create());
|
||||
if (!file->OpenFile(file_name.c_str(), false))
|
||||
return file_writer;
|
||||
|
||||
file_writer.reset(new IvfFileWriter(
|
||||
file_name, std::unique_ptr<FileWrapper>(std::move(file)), codec_type));
|
||||
if (!file_writer->WriteHeader())
|
||||
file_writer.reset();
|
||||
|
||||
return file_writer;
|
||||
std::unique_ptr<IvfFileWriter> IvfFileWriter::Wrap(rtc::File file,
|
||||
size_t byte_limit) {
|
||||
return std::unique_ptr<IvfFileWriter>(
|
||||
new IvfFileWriter(std::move(file), byte_limit));
|
||||
}
|
||||
|
||||
bool IvfFileWriter::WriteHeader() {
|
||||
if (file_->Rewind() != 0) {
|
||||
LOG(LS_WARNING) << "Unable to rewind output file " << file_name_;
|
||||
if (!file_.Seek(0)) {
|
||||
LOG(LS_WARNING) << "Unable to rewind ivf output file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -98,21 +97,28 @@ bool IvfFileWriter::WriteHeader() {
|
||||
static_cast<uint32_t>(num_frames_));
|
||||
ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0); // Reserved.
|
||||
|
||||
if (!file_->Write(ivf_header, kIvfHeaderSize)) {
|
||||
LOG(LS_ERROR) << "Unable to write IVF header for file " << file_name_;
|
||||
if (file_.Write(ivf_header, kIvfHeaderSize) < kIvfHeaderSize) {
|
||||
LOG(LS_ERROR) << "Unable to write IVF header for ivf output file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes_written_ < kIvfHeaderSize) {
|
||||
bytes_written_ = kIvfHeaderSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image) {
|
||||
bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image,
|
||||
VideoCodecType codec_type) {
|
||||
width_ = encoded_image._encodedWidth;
|
||||
height_ = encoded_image._encodedHeight;
|
||||
RTC_CHECK_GT(width_, 0);
|
||||
RTC_CHECK_GT(height_, 0);
|
||||
using_capture_timestamps_ = encoded_image._timeStamp == 0;
|
||||
|
||||
codec_type_ = codec_type;
|
||||
|
||||
if (!WriteHeader())
|
||||
return false;
|
||||
|
||||
@ -130,20 +136,22 @@ bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image) {
|
||||
default:
|
||||
codec_name = "Unknown";
|
||||
}
|
||||
LOG(LS_WARNING) << "Created IVF file " << file_name_
|
||||
<< " for codec data of type " << codec_name
|
||||
LOG(LS_WARNING) << "Created IVF file for codec data of type " << codec_name
|
||||
<< " at resolution " << width_ << " x " << height_
|
||||
<< ", using " << (using_capture_timestamps_ ? "1" : "90")
|
||||
<< "kHz clock resolution.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image) {
|
||||
RTC_DCHECK(file_->is_open());
|
||||
|
||||
if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image))
|
||||
bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image,
|
||||
VideoCodecType codec_type) {
|
||||
if (!file_.IsOpen())
|
||||
return false;
|
||||
|
||||
if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image, codec_type))
|
||||
return false;
|
||||
RTC_DCHECK_EQ(codec_type_, codec_type);
|
||||
|
||||
if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) &&
|
||||
(encoded_image._encodedHeight != height_ ||
|
||||
encoded_image._encodedWidth != width_)) {
|
||||
@ -163,35 +171,41 @@ bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image) {
|
||||
last_timestamp_ = timestamp;
|
||||
|
||||
const size_t kFrameHeaderSize = 12;
|
||||
if (byte_limit_ != 0 &&
|
||||
bytes_written_ + kFrameHeaderSize + encoded_image._length > byte_limit_) {
|
||||
LOG(LS_WARNING) << "Closing IVF file due to reaching size limit: "
|
||||
<< byte_limit_ << " bytes.";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
uint8_t frame_header[kFrameHeaderSize] = {};
|
||||
ByteWriter<uint32_t>::WriteLittleEndian(
|
||||
&frame_header[0], static_cast<uint32_t>(encoded_image._length));
|
||||
ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp);
|
||||
if (!file_->Write(frame_header, kFrameHeaderSize) ||
|
||||
!file_->Write(encoded_image._buffer, encoded_image._length)) {
|
||||
LOG(LS_ERROR) << "Unable to write frame to file " << file_name_;
|
||||
if (file_.Write(frame_header, kFrameHeaderSize) < kFrameHeaderSize ||
|
||||
file_.Write(encoded_image._buffer, encoded_image._length) <
|
||||
encoded_image._length) {
|
||||
LOG(LS_ERROR) << "Unable to write frame to file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_written_ += kFrameHeaderSize + encoded_image._length;
|
||||
|
||||
++num_frames_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IvfFileWriter::Close() {
|
||||
if (!file_->is_open())
|
||||
if (!file_.IsOpen())
|
||||
return false;
|
||||
|
||||
if (num_frames_ == 0) {
|
||||
// No frame written to file, close and remove it entirely if possible.
|
||||
file_->CloseFile();
|
||||
if (remove(file_name_.c_str()) != 0)
|
||||
LOG(LS_WARNING) << "Failed to remove empty IVF file " << file_name_;
|
||||
|
||||
file_.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = WriteHeader();
|
||||
file_->CloseFile();
|
||||
file_.Close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -15,38 +15,42 @@
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/file.h"
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/modules/include/module_common_types.h"
|
||||
#include "webrtc/video_frame.h"
|
||||
#include "webrtc/system_wrappers/include/file_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class IvfFileWriter {
|
||||
public:
|
||||
// Takes ownership of the file, which will be closed either through
|
||||
// Close or ~IvfFileWriter. If writing a frame would take the file above the
|
||||
// |byte_limit| the file will be closed, the write (and all future writes)
|
||||
// will fail. A |byte_limit| of 0 is equivalent to no limit.
|
||||
static std::unique_ptr<IvfFileWriter> Wrap(rtc::File file, size_t byte_limit);
|
||||
~IvfFileWriter();
|
||||
|
||||
static std::unique_ptr<IvfFileWriter> Open(const std::string& file_name,
|
||||
VideoCodecType codec_type);
|
||||
bool WriteFrame(const EncodedImage& encoded_image);
|
||||
bool WriteFrame(const EncodedImage& encoded_image, VideoCodecType codec_type);
|
||||
bool Close();
|
||||
|
||||
private:
|
||||
IvfFileWriter(const std::string& path_name,
|
||||
std::unique_ptr<FileWrapper> file,
|
||||
VideoCodecType codec_type);
|
||||
bool WriteHeader();
|
||||
bool InitFromFirstFrame(const EncodedImage& encoded_image);
|
||||
explicit IvfFileWriter(rtc::File file, size_t byte_limit);
|
||||
|
||||
const VideoCodecType codec_type_;
|
||||
bool WriteHeader();
|
||||
bool InitFromFirstFrame(const EncodedImage& encoded_image,
|
||||
VideoCodecType codec_type);
|
||||
|
||||
VideoCodecType codec_type_;
|
||||
size_t bytes_written_;
|
||||
size_t byte_limit_;
|
||||
size_t num_frames_;
|
||||
uint16_t width_;
|
||||
uint16_t height_;
|
||||
int64_t last_timestamp_;
|
||||
bool using_capture_timestamps_;
|
||||
rtc::TimestampWrapAroundHandler wrap_handler_;
|
||||
const std::string file_name_;
|
||||
std::unique_ptr<FileWrapper> file_;
|
||||
rtc::File file_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(IvfFileWriter);
|
||||
};
|
||||
|
||||
@ -26,23 +26,18 @@ namespace {
|
||||
static const int kHeaderSize = 32;
|
||||
static const int kFrameHeaderSize = 12;
|
||||
static uint8_t dummy_payload[4] = {0, 1, 2, 3};
|
||||
static const int kMaxFileRetries = 5;
|
||||
} // namespace
|
||||
|
||||
class IvfFileWriterTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const uint64_t start_id = rtc::CreateRandomId64();
|
||||
uint64_t id = start_id;
|
||||
do {
|
||||
std::ostringstream oss;
|
||||
oss << test::OutputPath() << "ivf_test_file_" << id++ << ".ivf";
|
||||
file_name_ = oss.str();
|
||||
} while ((id - start_id) < 100u && FileExists(false));
|
||||
ASSERT_LT(id - start_id, 100u);
|
||||
file_name_ =
|
||||
webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
|
||||
}
|
||||
void TearDown() override { rtc::RemoveFile(file_name_); }
|
||||
|
||||
bool WriteDummyTestFrames(int width,
|
||||
bool WriteDummyTestFrames(VideoCodecType codec_type,
|
||||
int width,
|
||||
int height,
|
||||
int num_frames,
|
||||
bool use_capture_tims_ms) {
|
||||
@ -57,21 +52,21 @@ class IvfFileWriterTest : public ::testing::Test {
|
||||
} else {
|
||||
frame._timeStamp = i;
|
||||
}
|
||||
if (!file_writer_->WriteFrame(frame))
|
||||
if (!file_writer_->WriteFrame(frame, codec_type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerifyIvfHeader(FileWrapper* file,
|
||||
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->is_open());
|
||||
ASSERT_TRUE(file->IsOpen());
|
||||
uint8_t data[kHeaderSize];
|
||||
ASSERT_EQ(kHeaderSize, file->Read(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));
|
||||
@ -87,11 +82,12 @@ class IvfFileWriterTest : public ::testing::Test {
|
||||
EXPECT_EQ(0u, ByteReader<uint32_t>::ReadLittleEndian(&data[28]));
|
||||
}
|
||||
|
||||
void VerifyDummyTestFrames(FileWrapper* file, uint32_t num_frames) {
|
||||
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(kFrameHeaderSize, file->Read(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);
|
||||
@ -109,67 +105,27 @@ class IvfFileWriterTest : public ::testing::Test {
|
||||
void RunBasicFileStructureTest(VideoCodecType codec_type,
|
||||
const uint8_t fourcc[4],
|
||||
bool use_capture_tims_ms) {
|
||||
file_writer_ = IvfFileWriter::Open(file_name_, codec_type);
|
||||
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;
|
||||
EXPECT_TRUE(
|
||||
WriteDummyTestFrames(kWidth, kHeight, kNumFrames, use_capture_tims_ms));
|
||||
ASSERT_TRUE(WriteDummyTestFrames(codec_type, kWidth, kHeight, kNumFrames,
|
||||
use_capture_tims_ms));
|
||||
EXPECT_TRUE(file_writer_->Close());
|
||||
|
||||
std::unique_ptr<FileWrapper> out_file(FileWrapper::Create());
|
||||
ASSERT_TRUE(out_file->OpenFile(file_name_.c_str(), true));
|
||||
VerifyIvfHeader(out_file.get(), fourcc, kWidth, kHeight, kNumFrames,
|
||||
rtc::File out_file = rtc::File::Open(file_name_);
|
||||
VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFrames,
|
||||
use_capture_tims_ms);
|
||||
VerifyDummyTestFrames(out_file.get(), kNumFrames);
|
||||
VerifyDummyTestFrames(&out_file, kNumFrames);
|
||||
|
||||
out_file->CloseFile();
|
||||
|
||||
bool file_removed = false;
|
||||
for (int i = 0; i < kMaxFileRetries; ++i) {
|
||||
file_removed = remove(file_name_.c_str()) == 0;
|
||||
if (file_removed)
|
||||
break;
|
||||
|
||||
// Couldn't remove file for some reason, wait a sec and try again.
|
||||
rtc::Thread::SleepMs(1000);
|
||||
}
|
||||
EXPECT_TRUE(file_removed);
|
||||
}
|
||||
|
||||
// Check whether file exists or not, and if it does not meet expectation,
|
||||
// wait a bit and check again, up to kMaxFileRetries times. This is an ugly
|
||||
// hack to avoid flakiness on certain operating systems where antivirus
|
||||
// software may unexpectedly lock files and keep them from disappearing or
|
||||
// being reused.
|
||||
bool FileExists(bool expected) {
|
||||
bool file_exists = expected;
|
||||
std::unique_ptr<FileWrapper> file_wrapper;
|
||||
int iterations = 0;
|
||||
do {
|
||||
if (file_wrapper.get() != nullptr)
|
||||
rtc::Thread::SleepMs(1000);
|
||||
file_wrapper.reset(FileWrapper::Create());
|
||||
file_exists = file_wrapper->OpenFile(file_name_.c_str(), true);
|
||||
file_wrapper->CloseFile();
|
||||
} while (file_exists != expected && ++iterations < kMaxFileRetries);
|
||||
return file_exists;
|
||||
out_file.Close();
|
||||
}
|
||||
|
||||
std::string file_name_;
|
||||
std::unique_ptr<IvfFileWriter> file_writer_;
|
||||
};
|
||||
|
||||
TEST_F(IvfFileWriterTest, RemovesUnusedFile) {
|
||||
file_writer_ = IvfFileWriter::Open(file_name_, kVideoCodecVP8);
|
||||
ASSERT_TRUE(file_writer_.get() != nullptr);
|
||||
EXPECT_TRUE(FileExists(true));
|
||||
EXPECT_TRUE(file_writer_->Close());
|
||||
EXPECT_FALSE(FileExists(false));
|
||||
EXPECT_FALSE(file_writer_->Close()); // Can't close twice.
|
||||
}
|
||||
|
||||
TEST_F(IvfFileWriterTest, WritesBasicVP8FileNtpTimestamp) {
|
||||
const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
|
||||
RunBasicFileStructureTest(kVideoCodecVP8, fourcc, false);
|
||||
@ -200,4 +156,28 @@ TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) {
|
||||
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
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#include "webrtc/test/fake_encoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
@ -193,7 +195,10 @@ EncodedImageCallback::Result FakeH264Encoder::OnEncodedImage(
|
||||
++fragment_counter;
|
||||
}
|
||||
}
|
||||
return callback_->OnEncodedImage(encoded_image, NULL, &fragmentation);
|
||||
CodecSpecificInfo specifics;
|
||||
memset(&specifics, 0, sizeof(specifics));
|
||||
specifics.codecType = kVideoCodecH264;
|
||||
return callback_->OnEncodedImage(encoded_image, &specifics, &fragmentation);
|
||||
}
|
||||
|
||||
DelayedEncoder::DelayedEncoder(Clock* clock, int delay_ms)
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/event.h"
|
||||
#include "webrtc/base/file.h"
|
||||
#include "webrtc/base/optional.h"
|
||||
#include "webrtc/base/rate_limiter.h"
|
||||
#include "webrtc/call.h"
|
||||
@ -3750,4 +3751,105 @@ TEST_F(EndToEndTest, TransportSeqNumOnAudioAndVideo) {
|
||||
|
||||
RunBaseTest(&test);
|
||||
}
|
||||
|
||||
class EndToEndLogTest : public EndToEndTest {
|
||||
void SetUp() { paths_.clear(); }
|
||||
void TearDown() {
|
||||
for (const auto& path : paths_) {
|
||||
rtc::RemoveFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int AddFile() {
|
||||
paths_.push_back(test::TempFilename(test::OutputPath(), "test_file"));
|
||||
return static_cast<int>(paths_.size()) - 1;
|
||||
}
|
||||
|
||||
rtc::PlatformFile OpenFile(int idx) {
|
||||
return rtc::OpenPlatformFile(paths_[idx]);
|
||||
}
|
||||
|
||||
void LogSend(bool open) {
|
||||
if (open) {
|
||||
video_send_stream_->EnableEncodedFrameRecording(
|
||||
std::vector<rtc::PlatformFile>(1, OpenFile(AddFile())), 0);
|
||||
} else {
|
||||
video_send_stream_->DisableEncodedFrameRecording();
|
||||
}
|
||||
}
|
||||
void LogReceive(bool open) {
|
||||
if (open) {
|
||||
video_receive_streams_[0]->EnableEncodedFrameRecording(
|
||||
OpenFile(AddFile()), 0);
|
||||
} else {
|
||||
video_receive_streams_[0]->DisableEncodedFrameRecording();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> paths_;
|
||||
};
|
||||
|
||||
TEST_F(EndToEndLogTest, LogsEncodedFramesWhenRequested) {
|
||||
static const int kNumFramesToRecord = 10;
|
||||
class LogEncodingObserver : public test::EndToEndTest,
|
||||
public EncodedFrameObserver {
|
||||
public:
|
||||
explicit LogEncodingObserver(EndToEndLogTest* fixture)
|
||||
: EndToEndTest(kDefaultTimeoutMs),
|
||||
fixture_(fixture),
|
||||
recorded_frames_(0) {}
|
||||
|
||||
void PerformTest() override {
|
||||
fixture_->LogSend(true);
|
||||
fixture_->LogReceive(true);
|
||||
ASSERT_TRUE(Wait()) << "Timed out while waiting for frame logging.";
|
||||
}
|
||||
|
||||
void ModifyVideoConfigs(
|
||||
VideoSendStream::Config* send_config,
|
||||
std::vector<VideoReceiveStream::Config>* receive_configs,
|
||||
VideoEncoderConfig* encoder_config) override {
|
||||
encoder_.reset(VideoEncoder::Create(VideoEncoder::kVp8));
|
||||
decoder_.reset(VP8Decoder::Create());
|
||||
|
||||
send_config->post_encode_callback = this;
|
||||
send_config->encoder_settings.payload_name = "VP8";
|
||||
send_config->encoder_settings.encoder = encoder_.get();
|
||||
|
||||
(*receive_configs)[0].decoders.resize(1);
|
||||
(*receive_configs)[0].decoders[0].payload_type =
|
||||
send_config->encoder_settings.payload_type;
|
||||
(*receive_configs)[0].decoders[0].payload_name =
|
||||
send_config->encoder_settings.payload_name;
|
||||
(*receive_configs)[0].decoders[0].decoder = decoder_.get();
|
||||
}
|
||||
|
||||
void EncodedFrameCallback(const EncodedFrame& encoded_frame) override {
|
||||
rtc::CritScope lock(&crit_);
|
||||
if (recorded_frames_++ > kNumFramesToRecord) {
|
||||
fixture_->LogSend(false);
|
||||
fixture_->LogReceive(false);
|
||||
rtc::File send_file(fixture_->OpenFile(0));
|
||||
rtc::File receive_file(fixture_->OpenFile(1));
|
||||
uint8_t out[100];
|
||||
// If logging has worked correctly neither file should be empty, i.e.
|
||||
// we should be able to read something from them.
|
||||
EXPECT_LT(0u, send_file.Read(out, 100));
|
||||
EXPECT_LT(0u, receive_file.Read(out, 100));
|
||||
observation_complete_.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EndToEndLogTest* const fixture_;
|
||||
std::unique_ptr<VideoEncoder> encoder_;
|
||||
std::unique_ptr<VideoDecoder> decoder_;
|
||||
rtc::CriticalSection crit_;
|
||||
int recorded_frames_ GUARDED_BY(crit_);
|
||||
} test(this);
|
||||
|
||||
RunBaseTest(&test);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -172,6 +172,14 @@ std::string SL1() {
|
||||
return static_cast<std::string>(FLAGS_sl1);
|
||||
}
|
||||
|
||||
DEFINE_string(encoded_frame_path,
|
||||
"",
|
||||
"The base path for encoded frame logs. Created files will have "
|
||||
"the form <encoded_frame_path>.<n>.(recv|send.<m>).ivf");
|
||||
std::string EncodedFramePath() {
|
||||
return static_cast<std::string>(FLAGS_encoded_frame_path);
|
||||
}
|
||||
|
||||
DEFINE_bool(logs, false, "print logs to stderr");
|
||||
|
||||
DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");
|
||||
@ -224,12 +232,21 @@ void Loopback() {
|
||||
call_bitrate_config.max_bitrate_bps = flags::MaxBitrateKbps() * 1000;
|
||||
|
||||
VideoQualityTest::Params params;
|
||||
params.common = {flags::Width(), flags::Height(), flags::Fps(),
|
||||
flags::MinBitrateKbps() * 1000, flags::TargetBitrateKbps() * 1000,
|
||||
flags::MaxBitrateKbps() * 1000, false, flags::Codec(),
|
||||
flags::NumTemporalLayers(), flags::SelectedTL(),
|
||||
flags::MinTransmitBitrateKbps() * 1000, flags::FLAGS_send_side_bwe,
|
||||
false, call_bitrate_config};
|
||||
params.common = {flags::Width(),
|
||||
flags::Height(),
|
||||
flags::Fps(),
|
||||
flags::MinBitrateKbps() * 1000,
|
||||
flags::TargetBitrateKbps() * 1000,
|
||||
flags::MaxBitrateKbps() * 1000,
|
||||
false,
|
||||
flags::Codec(),
|
||||
flags::NumTemporalLayers(),
|
||||
flags::SelectedTL(),
|
||||
flags::MinTransmitBitrateKbps() * 1000,
|
||||
flags::FLAGS_send_side_bwe,
|
||||
false,
|
||||
flags::EncodedFramePath(),
|
||||
call_bitrate_config};
|
||||
params.screenshare = {true, flags::SlideChangeInterval(),
|
||||
flags::ScrollDuration()};
|
||||
params.analyzer = {"screenshare", 0.0, 0.0, flags::DurationSecs(),
|
||||
|
||||
@ -183,6 +183,14 @@ std::string SL1() {
|
||||
return static_cast<std::string>(FLAGS_sl1);
|
||||
}
|
||||
|
||||
DEFINE_string(encoded_frame_path,
|
||||
"",
|
||||
"The base path for encoded frame logs. Created files will have "
|
||||
"the form <encoded_frame_path>.<n>.(recv|send.<m>).ivf");
|
||||
std::string EncodedFramePath() {
|
||||
return static_cast<std::string>(FLAGS_encoded_frame_path);
|
||||
}
|
||||
|
||||
DEFINE_bool(logs, false, "print logs to stderr");
|
||||
|
||||
DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");
|
||||
@ -230,12 +238,21 @@ void Loopback() {
|
||||
call_bitrate_config.max_bitrate_bps = flags::MaxBitrateKbps() * 1000;
|
||||
|
||||
VideoQualityTest::Params params;
|
||||
params.common = {flags::Width(), flags::Height(), flags::Fps(),
|
||||
flags::MinBitrateKbps() * 1000, flags::TargetBitrateKbps() * 1000,
|
||||
flags::MaxBitrateKbps() * 1000, flags::FLAGS_suspend_below_min_bitrate,
|
||||
flags::Codec(), flags::NumTemporalLayers(), flags::SelectedTL(),
|
||||
0, // No min transmit bitrate.
|
||||
flags::FLAGS_send_side_bwe, flags::FLAGS_use_fec, call_bitrate_config};
|
||||
params.common = {flags::Width(),
|
||||
flags::Height(),
|
||||
flags::Fps(),
|
||||
flags::MinBitrateKbps() * 1000,
|
||||
flags::TargetBitrateKbps() * 1000,
|
||||
flags::MaxBitrateKbps() * 1000,
|
||||
flags::FLAGS_suspend_below_min_bitrate,
|
||||
flags::Codec(),
|
||||
flags::NumTemporalLayers(),
|
||||
flags::SelectedTL(),
|
||||
0, // No min transmit bitrate.
|
||||
flags::FLAGS_send_side_bwe,
|
||||
flags::FLAGS_use_fec,
|
||||
flags::EncodedFramePath(),
|
||||
call_bitrate_config};
|
||||
params.video = {flags::Clip()};
|
||||
params.analyzer = {"video", 0.0, 0.0, flags::DurationSecs(),
|
||||
flags::OutputFilename(), flags::GraphTitle()};
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "webrtc/base/event.h"
|
||||
#include "webrtc/base/format_macros.h"
|
||||
#include "webrtc/base/optional.h"
|
||||
#include "webrtc/base/platform_file.h"
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/call.h"
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
@ -798,7 +799,8 @@ class VideoAnalyzer : public PacketReceiver,
|
||||
rtc::Event done_;
|
||||
};
|
||||
|
||||
VideoQualityTest::VideoQualityTest() : clock_(Clock::GetRealTimeClock()) {}
|
||||
VideoQualityTest::VideoQualityTest()
|
||||
: clock_(Clock::GetRealTimeClock()), receive_logs_(0), send_logs_(0) {}
|
||||
|
||||
void VideoQualityTest::TestBody() {}
|
||||
|
||||
@ -1188,6 +1190,8 @@ void VideoQualityTest::RunWithAnalyzer(const Params& params) {
|
||||
rtc::VideoSinkWants wants;
|
||||
capturer_->AddOrUpdateSink(analyzer.InputInterface(), wants);
|
||||
|
||||
StartEncodedFrameLogs(video_send_stream_);
|
||||
StartEncodedFrameLogs(video_receive_streams_[0]);
|
||||
video_send_stream_->Start();
|
||||
for (VideoReceiveStream* receive_stream : video_receive_streams_)
|
||||
receive_stream->Start();
|
||||
@ -1311,12 +1315,15 @@ void VideoQualityTest::RunWithRenderers(const Params& params) {
|
||||
if (params_.audio_video_sync)
|
||||
audio_config.sync_group = kSyncGroup;
|
||||
|
||||
audio_receive_stream =call->CreateAudioReceiveStream(audio_config);
|
||||
audio_receive_stream = call->CreateAudioReceiveStream(audio_config);
|
||||
|
||||
const CodecInst kOpusInst = {120, "OPUS", 48000, 960, 2, 64000};
|
||||
EXPECT_EQ(0, voe.codec->SetSendCodec(voe.send_channel_id, kOpusInst));
|
||||
}
|
||||
|
||||
StartEncodedFrameLogs(video_receive_stream);
|
||||
StartEncodedFrameLogs(video_send_stream_);
|
||||
|
||||
// Start sending and receiving video.
|
||||
video_receive_stream->Start();
|
||||
video_send_stream_->Start();
|
||||
@ -1364,4 +1371,29 @@ void VideoQualityTest::RunWithRenderers(const Params& params) {
|
||||
DestroyVoiceEngine(&voe);
|
||||
}
|
||||
|
||||
void VideoQualityTest::StartEncodedFrameLogs(VideoSendStream* stream) {
|
||||
if (!params_.common.encoded_frame_base_path.empty()) {
|
||||
std::ostringstream str;
|
||||
str << send_logs_++;
|
||||
std::string prefix =
|
||||
params_.common.encoded_frame_base_path + "." + str.str() + ".send.";
|
||||
stream->EnableEncodedFrameRecording(
|
||||
std::vector<rtc::PlatformFile>(
|
||||
{rtc::CreatePlatformFile(prefix + "1.ivf"),
|
||||
rtc::CreatePlatformFile(prefix + "2.ivf"),
|
||||
rtc::CreatePlatformFile(prefix + "3.ivf")}),
|
||||
10000000);
|
||||
}
|
||||
}
|
||||
void VideoQualityTest::StartEncodedFrameLogs(VideoReceiveStream* stream) {
|
||||
if (!params_.common.encoded_frame_base_path.empty()) {
|
||||
std::ostringstream str;
|
||||
str << receive_logs_++;
|
||||
std::string path =
|
||||
params_.common.encoded_frame_base_path + "." + str.str() + ".recv.ivf";
|
||||
stream->EnableEncodedFrameRecording(rtc::CreatePlatformFile(path),
|
||||
10000000);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -41,6 +41,7 @@ class VideoQualityTest : public test::CallTest {
|
||||
int min_transmit_bps;
|
||||
bool send_side_bwe;
|
||||
bool fec;
|
||||
std::string encoded_frame_base_path;
|
||||
|
||||
Call::Config::BitrateConfig call_bitrate_config;
|
||||
} common;
|
||||
@ -107,6 +108,9 @@ class VideoQualityTest : public test::CallTest {
|
||||
void SetupCommon(Transport* send_transport, Transport* recv_transport);
|
||||
void SetupScreenshare();
|
||||
|
||||
void StartEncodedFrameLogs(VideoSendStream* stream);
|
||||
void StartEncodedFrameLogs(VideoReceiveStream* stream);
|
||||
|
||||
// We need a more general capturer than the FrameGeneratorCapturer.
|
||||
std::unique_ptr<test::VideoCapturer> capturer_;
|
||||
std::unique_ptr<test::TraceToStderr> trace_to_stderr_;
|
||||
@ -114,6 +118,9 @@ class VideoQualityTest : public test::CallTest {
|
||||
std::unique_ptr<VideoEncoder> encoder_;
|
||||
Clock* const clock_;
|
||||
|
||||
int receive_logs_;
|
||||
int send_logs_;
|
||||
|
||||
Params params_;
|
||||
};
|
||||
|
||||
|
||||
@ -31,8 +31,6 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const bool kEnableFrameRecording = false;
|
||||
|
||||
static bool UseSendSideBwe(const VideoReceiveStream::Config& config) {
|
||||
if (!config.rtp.transport_cc)
|
||||
return false;
|
||||
@ -365,16 +363,12 @@ EncodedImageCallback::Result VideoReceiveStream::OnEncodedImage(
|
||||
EncodedFrame(encoded_image._buffer, encoded_image._length,
|
||||
encoded_image._frameType));
|
||||
}
|
||||
if (kEnableFrameRecording) {
|
||||
if (!ivf_writer_.get()) {
|
||||
RTC_DCHECK(codec_specific_info);
|
||||
std::ostringstream oss;
|
||||
oss << "receive_bitstream_ssrc_" << config_.rtp.remote_ssrc << ".ivf";
|
||||
ivf_writer_ =
|
||||
IvfFileWriter::Open(oss.str(), codec_specific_info->codecType);
|
||||
}
|
||||
{
|
||||
rtc::CritScope lock(&ivf_writer_lock_);
|
||||
if (ivf_writer_.get()) {
|
||||
bool ok = ivf_writer_->WriteFrame(encoded_image);
|
||||
RTC_DCHECK(codec_specific_info);
|
||||
bool ok = ivf_writer_->WriteFrame(encoded_image,
|
||||
codec_specific_info->codecType);
|
||||
RTC_DCHECK(ok);
|
||||
}
|
||||
}
|
||||
@ -397,6 +391,24 @@ void VideoReceiveStream::SendNack(
|
||||
rtp_stream_receiver_.RequestPacketRetransmit(sequence_numbers);
|
||||
}
|
||||
|
||||
void VideoReceiveStream::EnableEncodedFrameRecording(rtc::PlatformFile file,
|
||||
size_t byte_limit) {
|
||||
{
|
||||
rtc::CritScope lock(&ivf_writer_lock_);
|
||||
if (file == rtc::kInvalidPlatformFileValue) {
|
||||
ivf_writer_.reset();
|
||||
} else {
|
||||
ivf_writer_ = IvfFileWriter::Wrap(rtc::File(file), byte_limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (file != rtc::kInvalidPlatformFileValue) {
|
||||
// Make a keyframe appear as early as possible in the logs, to give actually
|
||||
// decodable output.
|
||||
RequestKeyFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoReceiveStream::RequestKeyFrame() {
|
||||
rtp_stream_receiver_.RequestKeyFrame();
|
||||
}
|
||||
|
||||
@ -84,6 +84,14 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
// Implements KeyFrameRequestSender.
|
||||
void RequestKeyFrame() override;
|
||||
|
||||
// Takes ownership of the file, is responsible for closing it later.
|
||||
// Calling this method will close and finalize any current log.
|
||||
// Giving rtc::kInvalidPlatformFileValue disables logging.
|
||||
// If a frame to be written would make the log too large the write fails and
|
||||
// the log is closed and finalized. A |byte_limit| of 0 means no limit.
|
||||
void EnableEncodedFrameRecording(rtc::PlatformFile file,
|
||||
size_t byte_limit) override;
|
||||
|
||||
private:
|
||||
static bool DecodeThreadFunction(void* ptr);
|
||||
void Decode();
|
||||
@ -105,7 +113,8 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
std::unique_ptr<VideoStreamDecoder> video_stream_decoder_;
|
||||
RtpStreamsSynchronizer rtp_stream_sync_;
|
||||
|
||||
std::unique_ptr<IvfFileWriter> ivf_writer_;
|
||||
rtc::CriticalSection ivf_writer_lock_;
|
||||
std::unique_ptr<IvfFileWriter> ivf_writer_ GUARDED_BY(ivf_writer_lock_);
|
||||
};
|
||||
} // namespace internal
|
||||
} // namespace webrtc
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/file.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/trace_event.h"
|
||||
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
|
||||
@ -267,6 +269,9 @@ class VideoSendStreamImpl : public webrtc::BitrateAllocatorObserver,
|
||||
|
||||
VideoSendStream::RtpStateMap GetRtpStates() const;
|
||||
|
||||
void EnableEncodedFrameRecording(const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit);
|
||||
|
||||
private:
|
||||
class CheckEncoderActivityTask;
|
||||
class EncoderReconfiguredTask;
|
||||
@ -316,9 +321,9 @@ class VideoSendStreamImpl : public webrtc::BitrateAllocatorObserver,
|
||||
BitrateAllocator* const bitrate_allocator_;
|
||||
VieRemb* const remb_;
|
||||
|
||||
static const bool kEnableFrameRecording = false;
|
||||
static const int kMaxLayers = 3;
|
||||
std::unique_ptr<IvfFileWriter> file_writers_[kMaxLayers];
|
||||
rtc::CriticalSection ivf_writers_crit_;
|
||||
std::unique_ptr<IvfFileWriter> file_writers_[kMaxSimulcastStreams] GUARDED_BY(
|
||||
ivf_writers_crit_);
|
||||
|
||||
int max_padding_bitrate_;
|
||||
int encoder_min_bitrate_bps_;
|
||||
@ -615,6 +620,12 @@ bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
|
||||
return send_stream_->DeliverRtcp(packet, length);
|
||||
}
|
||||
|
||||
void VideoSendStream::EnableEncodedFrameRecording(
|
||||
const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) {
|
||||
send_stream_->EnableEncodedFrameRecording(files, byte_limit);
|
||||
}
|
||||
|
||||
VideoSendStreamImpl::VideoSendStreamImpl(
|
||||
SendStatisticsProxy* stats_proxy,
|
||||
rtc::TaskQueue* worker_queue,
|
||||
@ -891,25 +902,16 @@ EncodedImageCallback::Result VideoSendStreamImpl::OnEncodedImage(
|
||||
EncodedImageCallback::Result result = payload_router_.OnEncodedImage(
|
||||
encoded_image, codec_specific_info, fragmentation);
|
||||
|
||||
if (kEnableFrameRecording) {
|
||||
int layer = codec_specific_info->codecType == kVideoCodecVP8
|
||||
? codec_specific_info->codecSpecific.VP8.simulcastIdx
|
||||
: 0;
|
||||
IvfFileWriter* file_writer;
|
||||
{
|
||||
if (file_writers_[layer] == nullptr) {
|
||||
std::ostringstream oss;
|
||||
oss << "send_bitstream_ssrc";
|
||||
for (uint32_t ssrc : config_->rtp.ssrcs)
|
||||
oss << "_" << ssrc;
|
||||
oss << "_layer" << layer << ".ivf";
|
||||
file_writers_[layer] =
|
||||
IvfFileWriter::Open(oss.str(), codec_specific_info->codecType);
|
||||
}
|
||||
file_writer = file_writers_[layer].get();
|
||||
}
|
||||
if (file_writer) {
|
||||
bool ok = file_writer->WriteFrame(encoded_image);
|
||||
RTC_DCHECK(codec_specific_info);
|
||||
|
||||
int layer = codec_specific_info->codecType == kVideoCodecVP8
|
||||
? codec_specific_info->codecSpecific.VP8.simulcastIdx
|
||||
: 0;
|
||||
{
|
||||
rtc::CritScope lock(&ivf_writers_crit_);
|
||||
if (file_writers_[layer].get()) {
|
||||
bool ok = file_writers_[layer]->WriteFrame(
|
||||
encoded_image, codec_specific_info->codecType);
|
||||
RTC_DCHECK(ok);
|
||||
}
|
||||
}
|
||||
@ -1064,6 +1066,27 @@ uint32_t VideoSendStreamImpl::OnBitrateUpdated(uint32_t bitrate_bps,
|
||||
return protection_bitrate;
|
||||
}
|
||||
|
||||
void VideoSendStreamImpl::EnableEncodedFrameRecording(
|
||||
const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) {
|
||||
{
|
||||
rtc::CritScope lock(&ivf_writers_crit_);
|
||||
for (unsigned int i = 0; i < kMaxSimulcastStreams; ++i) {
|
||||
if (i < files.size()) {
|
||||
file_writers_[i] = IvfFileWriter::Wrap(rtc::File(files[i]), byte_limit);
|
||||
} else {
|
||||
file_writers_[i].reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.empty()) {
|
||||
// Make a keyframe appear as early as possible in the logs, to give actually
|
||||
// decodable output.
|
||||
vie_encoder_->SendKeyFrame();
|
||||
}
|
||||
}
|
||||
|
||||
int VideoSendStreamImpl::ProtectionRequest(
|
||||
const FecProtectionParams* delta_params,
|
||||
const FecProtectionParams* key_params,
|
||||
|
||||
@ -79,6 +79,16 @@ class VideoSendStream : public webrtc::VideoSendStream {
|
||||
Stats GetStats() override;
|
||||
|
||||
typedef std::map<uint32_t, RtpState> RtpStateMap;
|
||||
|
||||
// Takes ownership of each file, is responsible for closing them later.
|
||||
// Calling this method will close and finalize any current logs.
|
||||
// Giving rtc::kInvalidPlatformFileValue in any position disables logging
|
||||
// for the corresponding stream.
|
||||
// If a frame to be written would make the log too large the write fails and
|
||||
// the log is closed and finalized. A |byte_limit| of 0 means no limit.
|
||||
void EnableEncodedFrameRecording(const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) override;
|
||||
|
||||
RtpStateMap StopPermanentlyAndGetRtpStates();
|
||||
|
||||
private:
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/platform_file.h"
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/common_video/include/frame_callback.h"
|
||||
#include "webrtc/config.h"
|
||||
@ -208,6 +209,17 @@ class VideoReceiveStream {
|
||||
// TODO(pbos): Add info on currently-received codec to Stats.
|
||||
virtual Stats GetStats() const = 0;
|
||||
|
||||
// Takes ownership of the file, is responsible for closing it later.
|
||||
// Calling this method will close and finalize any current log.
|
||||
// Giving rtc::kInvalidPlatformFileValue disables logging.
|
||||
// If a frame to be written would make the log too large the write fails and
|
||||
// the log is closed and finalized. A |byte_limit| of 0 means no limit.
|
||||
virtual void EnableEncodedFrameRecording(rtc::PlatformFile file,
|
||||
size_t byte_limit) = 0;
|
||||
inline void DisableEncodedFrameRecording() {
|
||||
EnableEncodedFrameRecording(rtc::kInvalidPlatformFileValue, 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~VideoReceiveStream() {}
|
||||
};
|
||||
|
||||
@ -13,8 +13,10 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/platform_file.h"
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/common_video/include/frame_callback.h"
|
||||
#include "webrtc/config.h"
|
||||
@ -192,6 +194,22 @@ class VideoSendStream {
|
||||
|
||||
virtual Stats GetStats() = 0;
|
||||
|
||||
// Takes ownership of each file, is responsible for closing them later.
|
||||
// Calling this method will close and finalize any current logs.
|
||||
// Some codecs produce multiple streams (VP8 only at present), each of these
|
||||
// streams will log to a separate file. kMaxSimulcastStreams in common_types.h
|
||||
// gives the max number of such streams. If there is no file for a stream, or
|
||||
// the file is rtc::kInvalidPlatformFileValue, frames from that stream will
|
||||
// not be logged.
|
||||
// If a frame to be written would make the log too large the write fails and
|
||||
// the log is closed and finalized. A |byte_limit| of 0 means no limit.
|
||||
virtual void EnableEncodedFrameRecording(
|
||||
const std::vector<rtc::PlatformFile>& files,
|
||||
size_t byte_limit) = 0;
|
||||
inline void DisableEncodedFrameRecording() {
|
||||
EnableEncodedFrameRecording(std::vector<rtc::PlatformFile>(), 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~VideoSendStream() {}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user