blob: 7ec6db88585456f59f78452542c0a73e3aecc8bb [file] [log] [blame]
/*
* Copyright (c) 2012 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/video_engine/test/libvietest/include/vie_to_file_renderer.h"
#include <assert.h>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
namespace test {
struct Frame {
public:
Frame() : buffer(nullptr), buffer_size(0), timestamp(0), render_time(0) {}
webrtc::scoped_ptr<unsigned char[]> buffer;
size_t buffer_size;
uint32_t timestamp;
int64_t render_time;
private:
DISALLOW_COPY_AND_ASSIGN(Frame);
};
}; // namespace test
ViEToFileRenderer::ViEToFileRenderer()
: output_file_(NULL),
output_path_(),
output_filename_(),
thread_(webrtc::ThreadWrapper::CreateThread(
ViEToFileRenderer::RunRenderThread,
this, webrtc::kNormalPriority, "ViEToFileRendererThread")),
frame_queue_cs_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
frame_render_event_(webrtc::EventWrapper::Create()),
render_queue_(),
free_frame_queue_() {
}
ViEToFileRenderer::~ViEToFileRenderer() {
while (!free_frame_queue_.empty()) {
delete free_frame_queue_.front();
free_frame_queue_.pop_front();
}
}
bool ViEToFileRenderer::PrepareForRendering(
const std::string& output_path,
const std::string& output_filename) {
assert(output_file_ == NULL);
output_file_ = fopen((output_path + output_filename).c_str(), "wb");
if (output_file_ == NULL) {
return false;
}
output_filename_ = output_filename;
output_path_ = output_path;
unsigned int tid;
return thread_->Start(tid);
}
void ViEToFileRenderer::StopRendering() {
assert(output_file_ != NULL);
if (thread_.get() != NULL) {
// Signal that a frame is ready to be written to file.
frame_render_event_->Set();
// Call Stop() repeatedly, waiting for ProcessRenderQueue() to finish.
while (!thread_->Stop()) continue;
}
fclose(output_file_);
output_file_ = NULL;
}
bool ViEToFileRenderer::SaveOutputFile(const std::string& prefix) {
assert(output_file_ == NULL && output_filename_ != "");
if (rename((output_path_ + output_filename_).c_str(),
(output_path_ + prefix + output_filename_).c_str()) != 0) {
perror("Failed to rename output file");
return false;
}
ForgetOutputFile();
return true;
}
bool ViEToFileRenderer::DeleteOutputFile() {
assert(output_file_ == NULL && output_filename_ != "");
if (remove((output_path_ + output_filename_).c_str()) != 0) {
perror("Failed to delete output file");
return false;
}
ForgetOutputFile();
return true;
}
const std::string ViEToFileRenderer::GetFullOutputPath() const {
return output_path_ + output_filename_;
}
void ViEToFileRenderer::ForgetOutputFile() {
output_filename_ = "";
output_path_ = "";
}
test::Frame* ViEToFileRenderer::NewFrame(size_t buffer_size) {
test::Frame* frame;
if (free_frame_queue_.empty()) {
frame = new test::Frame();
} else {
// Reuse an already allocated frame.
frame = free_frame_queue_.front();
free_frame_queue_.pop_front();
}
if (frame->buffer_size < buffer_size) {
frame->buffer.reset(new unsigned char[buffer_size]);
frame->buffer_size = buffer_size;
}
return frame;
}
int ViEToFileRenderer::DeliverFrame(unsigned char *buffer,
size_t buffer_size,
uint32_t time_stamp,
int64_t ntp_time_ms,
int64_t render_time,
void* /*handle*/) {
webrtc::CriticalSectionScoped lock(frame_queue_cs_.get());
test::Frame* frame = NewFrame(buffer_size);
memcpy(frame->buffer.get(), buffer, buffer_size);
frame->timestamp = time_stamp;
frame->render_time = render_time;
render_queue_.push_back(frame);
// Signal that a frame is ready to be written to file.
frame_render_event_->Set();
return 0;
}
int ViEToFileRenderer::DeliverI420Frame(
const webrtc::I420VideoFrame* input_frame) {
assert(input_frame);
const size_t buffer_size = CalcBufferSize(webrtc::kI420, input_frame->width(),
input_frame->height());
webrtc::CriticalSectionScoped lock(frame_queue_cs_.get());
test::Frame* frame = NewFrame(buffer_size);
const int length =
ExtractBuffer(*input_frame, frame->buffer_size, frame->buffer.get());
assert(static_cast<size_t>(length) == buffer_size);
if (length < 0)
return -1;
frame->timestamp = input_frame->timestamp();
frame->render_time = input_frame->render_time_ms();
render_queue_.push_back(frame);
// Signal that a frame is ready to be written to file.
frame_render_event_->Set();
return 0;
}
bool ViEToFileRenderer::IsTextureSupported() { return false; }
int ViEToFileRenderer::FrameSizeChange(unsigned int width,
unsigned int height,
unsigned int number_of_streams) {
return 0;
}
bool ViEToFileRenderer::RunRenderThread(void* obj) {
assert(obj);
ViEToFileRenderer* renderer = static_cast<ViEToFileRenderer*>(obj);
return renderer->ProcessRenderQueue();
}
bool ViEToFileRenderer::ProcessRenderQueue() {
// Wait for a frame to be rendered.
frame_render_event_->Wait(WEBRTC_EVENT_INFINITE);
frame_queue_cs_->Enter();
// Render all frames in the queue.
while (!render_queue_.empty()) {
test::Frame* frame = render_queue_.front();
render_queue_.pop_front();
// Leave the critical section before writing to file to not block calls to
// the renderer.
frame_queue_cs_->Leave();
assert(output_file_);
size_t written = fwrite(frame->buffer.get(), sizeof(unsigned char),
frame->buffer_size, output_file_);
frame_queue_cs_->Enter();
// Return the frame.
free_frame_queue_.push_front(frame);
if (written != frame->buffer_size) {
frame_queue_cs_->Leave();
return false;
}
}
frame_queue_cs_->Leave();
return true;
}