1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h"
12
13 #include <assert.h>
14
15 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
16 #include "webrtc/system_wrappers/interface/event_wrapper.h"
17 #include "webrtc/system_wrappers/interface/thread_wrapper.h"
18
19 namespace test {
20 struct Frame {
21 public:
Frametest::Frame22 Frame(unsigned char* buffer,
23 int buffer_size,
24 uint32_t timestamp,
25 int64_t render_time)
26 : buffer(new unsigned char[buffer_size]),
27 buffer_size(buffer_size),
28 timestamp(timestamp),
29 render_time(render_time) {
30 memcpy(this->buffer.get(), buffer, buffer_size);
31 }
32
33 webrtc::scoped_ptr<unsigned char[]> buffer;
34 int buffer_size;
35 uint32_t timestamp;
36 int64_t render_time;
37
38 private:
39 DISALLOW_COPY_AND_ASSIGN(Frame);
40 };
41 }; // namespace test
42
ViEToFileRenderer()43 ViEToFileRenderer::ViEToFileRenderer()
44 : output_file_(NULL),
45 output_path_(),
46 output_filename_(),
47 thread_(webrtc::ThreadWrapper::CreateThread(
48 ViEToFileRenderer::RunRenderThread,
49 this, webrtc::kNormalPriority, "ViEToFileRendererThread")),
50 frame_queue_cs_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
51 frame_render_event_(webrtc::EventWrapper::Create()),
52 render_queue_(),
53 free_frame_queue_() {
54 }
55
~ViEToFileRenderer()56 ViEToFileRenderer::~ViEToFileRenderer() {
57 while (!free_frame_queue_.empty()) {
58 delete free_frame_queue_.front();
59 free_frame_queue_.pop_front();
60 }
61 }
62
PrepareForRendering(const std::string & output_path,const std::string & output_filename)63 bool ViEToFileRenderer::PrepareForRendering(
64 const std::string& output_path,
65 const std::string& output_filename) {
66
67 assert(output_file_ == NULL);
68
69 output_file_ = fopen((output_path + output_filename).c_str(), "wb");
70 if (output_file_ == NULL) {
71 return false;
72 }
73
74 output_filename_ = output_filename;
75 output_path_ = output_path;
76 unsigned int tid;
77 return thread_->Start(tid);
78 }
79
StopRendering()80 void ViEToFileRenderer::StopRendering() {
81 assert(output_file_ != NULL);
82 if (thread_.get() != NULL) {
83 thread_->SetNotAlive();
84 // Signal that a frame is ready to be written to file.
85 frame_render_event_->Set();
86 // Call Stop() repeatedly, waiting for ProcessRenderQueue() to finish.
87 while (!thread_->Stop()) continue;
88 }
89 fclose(output_file_);
90 output_file_ = NULL;
91 }
92
SaveOutputFile(const std::string & prefix)93 bool ViEToFileRenderer::SaveOutputFile(const std::string& prefix) {
94 assert(output_file_ == NULL && output_filename_ != "");
95 if (rename((output_path_ + output_filename_).c_str(),
96 (output_path_ + prefix + output_filename_).c_str()) != 0) {
97 perror("Failed to rename output file");
98 return false;
99 }
100 ForgetOutputFile();
101 return true;
102 }
103
DeleteOutputFile()104 bool ViEToFileRenderer::DeleteOutputFile() {
105 assert(output_file_ == NULL && output_filename_ != "");
106 if (remove((output_path_ + output_filename_).c_str()) != 0) {
107 perror("Failed to delete output file");
108 return false;
109 }
110 ForgetOutputFile();
111 return true;
112 }
113
GetFullOutputPath() const114 const std::string ViEToFileRenderer::GetFullOutputPath() const {
115 return output_path_ + output_filename_;
116 }
117
ForgetOutputFile()118 void ViEToFileRenderer::ForgetOutputFile() {
119 output_filename_ = "";
120 output_path_ = "";
121 }
122
DeliverFrame(unsigned char * buffer,int buffer_size,uint32_t time_stamp,int64_t ntp_time_ms,int64_t render_time,void *)123 int ViEToFileRenderer::DeliverFrame(unsigned char *buffer,
124 int buffer_size,
125 uint32_t time_stamp,
126 int64_t ntp_time_ms,
127 int64_t render_time,
128 void* /*handle*/) {
129 webrtc::CriticalSectionScoped lock(frame_queue_cs_.get());
130 test::Frame* frame;
131 if (free_frame_queue_.empty()) {
132 frame = new test::Frame(buffer, buffer_size, time_stamp, render_time);
133 } else {
134 // Reuse an already allocated frame.
135 frame = free_frame_queue_.front();
136 free_frame_queue_.pop_front();
137 if (frame->buffer_size < buffer_size) {
138 frame->buffer.reset(new unsigned char[buffer_size]);
139 }
140 memcpy(frame->buffer.get(), buffer, buffer_size);
141 frame->buffer_size = buffer_size;
142 frame->timestamp = time_stamp;
143 frame->render_time = render_time;
144 }
145 render_queue_.push_back(frame);
146 // Signal that a frame is ready to be written to file.
147 frame_render_event_->Set();
148 return 0;
149 }
150
IsTextureSupported()151 bool ViEToFileRenderer::IsTextureSupported() { return false; }
152
FrameSizeChange(unsigned int width,unsigned int height,unsigned int number_of_streams)153 int ViEToFileRenderer::FrameSizeChange(unsigned int width,
154 unsigned int height,
155 unsigned int number_of_streams) {
156 return 0;
157 }
158
RunRenderThread(void * obj)159 bool ViEToFileRenderer::RunRenderThread(void* obj) {
160 assert(obj);
161 ViEToFileRenderer* renderer = static_cast<ViEToFileRenderer*>(obj);
162 return renderer->ProcessRenderQueue();
163 }
164
ProcessRenderQueue()165 bool ViEToFileRenderer::ProcessRenderQueue() {
166 // Wait for a frame to be rendered.
167 frame_render_event_->Wait(WEBRTC_EVENT_INFINITE);
168 frame_queue_cs_->Enter();
169 // Render all frames in the queue.
170 while (!render_queue_.empty()) {
171 test::Frame* frame = render_queue_.front();
172 render_queue_.pop_front();
173 // Leave the critical section before writing to file to not block calls to
174 // the renderer.
175 frame_queue_cs_->Leave();
176 assert(output_file_);
177 int written = fwrite(frame->buffer.get(), sizeof(unsigned char),
178 frame->buffer_size, output_file_);
179 frame_queue_cs_->Enter();
180 // Return the frame.
181 free_frame_queue_.push_front(frame);
182 if (written != frame->buffer_size) {
183 frame_queue_cs_->Leave();
184 return false;
185 }
186 }
187 frame_queue_cs_->Leave();
188 return true;
189 }
190