PageRenderTime 26ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc

https://gitlab.com/f3822/qtwebengine-chromium
C++ | 377 lines | 270 code | 41 blank | 66 comment | 52 complexity | f618b9477f1441fc4589872f22eeedfa MD5 | raw file
  1. // libjingle
  2. // Copyright 2004 Google Inc.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are met:
  6. //
  7. // 1. Redistributions of source code must retain the above copyright notice,
  8. // this list of conditions and the following disclaimer.
  9. // 2. Redistributions in binary form must reproduce the above copyright notice,
  10. // this list of conditions and the following disclaimer in the documentation
  11. // and/or other materials provided with the distribution.
  12. // 3. The name of the author may not be used to endorse or promote products
  13. // derived from this software without specific prior written permission.
  14. //
  15. // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
  16. // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  17. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  18. // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  20. // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  21. // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  22. // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  23. // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  24. // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. //
  26. // Implementation of VideoRecorder and FileVideoCapturer.
  27. #include "talk/media/devices/filevideocapturer.h"
  28. #include "talk/base/bytebuffer.h"
  29. #include "talk/base/criticalsection.h"
  30. #include "talk/base/logging.h"
  31. #include "talk/base/thread.h"
  32. namespace cricket {
  33. /////////////////////////////////////////////////////////////////////
  34. // Implementation of class VideoRecorder
  35. /////////////////////////////////////////////////////////////////////
  36. bool VideoRecorder::Start(const std::string& filename, bool write_header) {
  37. Stop();
  38. write_header_ = write_header;
  39. int err;
  40. if (!video_file_.Open(filename, "wb", &err)) {
  41. LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err;
  42. return false;
  43. }
  44. return true;
  45. }
  46. void VideoRecorder::Stop() {
  47. video_file_.Close();
  48. }
  49. bool VideoRecorder::RecordFrame(const CapturedFrame& frame) {
  50. if (talk_base::SS_CLOSED == video_file_.GetState()) {
  51. LOG(LS_ERROR) << "File not opened yet";
  52. return false;
  53. }
  54. uint32 size = 0;
  55. if (!frame.GetDataSize(&size)) {
  56. LOG(LS_ERROR) << "Unable to calculate the data size of the frame";
  57. return false;
  58. }
  59. if (write_header_) {
  60. // Convert the frame header to bytebuffer.
  61. talk_base::ByteBuffer buffer;
  62. buffer.WriteUInt32(frame.width);
  63. buffer.WriteUInt32(frame.height);
  64. buffer.WriteUInt32(frame.fourcc);
  65. buffer.WriteUInt32(frame.pixel_width);
  66. buffer.WriteUInt32(frame.pixel_height);
  67. buffer.WriteUInt64(frame.elapsed_time);
  68. buffer.WriteUInt64(frame.time_stamp);
  69. buffer.WriteUInt32(size);
  70. // Write the bytebuffer to file.
  71. if (talk_base::SR_SUCCESS != video_file_.Write(buffer.Data(),
  72. buffer.Length(),
  73. NULL,
  74. NULL)) {
  75. LOG(LS_ERROR) << "Failed to write frame header";
  76. return false;
  77. }
  78. }
  79. // Write the frame data to file.
  80. if (talk_base::SR_SUCCESS != video_file_.Write(frame.data,
  81. size,
  82. NULL,
  83. NULL)) {
  84. LOG(LS_ERROR) << "Failed to write frame data";
  85. return false;
  86. }
  87. return true;
  88. }
  89. ///////////////////////////////////////////////////////////////////////
  90. // Definition of private class FileReadThread that periodically reads
  91. // frames from a file.
  92. ///////////////////////////////////////////////////////////////////////
  93. class FileVideoCapturer::FileReadThread
  94. : public talk_base::Thread, public talk_base::MessageHandler {
  95. public:
  96. explicit FileReadThread(FileVideoCapturer* capturer)
  97. : capturer_(capturer),
  98. finished_(false) {
  99. }
  100. virtual ~FileReadThread() {
  101. Stop();
  102. }
  103. // Override virtual method of parent Thread. Context: Worker Thread.
  104. virtual void Run() {
  105. // Read the first frame and start the message pump. The pump runs until
  106. // Stop() is called externally or Quit() is called by OnMessage().
  107. int waiting_time_ms = 0;
  108. if (capturer_ && capturer_->ReadFrame(true, &waiting_time_ms)) {
  109. PostDelayed(waiting_time_ms, this);
  110. Thread::Run();
  111. }
  112. talk_base::CritScope cs(&crit_);
  113. finished_ = true;
  114. }
  115. // Override virtual method of parent MessageHandler. Context: Worker Thread.
  116. virtual void OnMessage(talk_base::Message* /*pmsg*/) {
  117. int waiting_time_ms = 0;
  118. if (capturer_ && capturer_->ReadFrame(false, &waiting_time_ms)) {
  119. PostDelayed(waiting_time_ms, this);
  120. } else {
  121. Quit();
  122. }
  123. }
  124. // Check if Run() is finished.
  125. bool Finished() const {
  126. talk_base::CritScope cs(&crit_);
  127. return finished_;
  128. }
  129. private:
  130. FileVideoCapturer* capturer_;
  131. mutable talk_base::CriticalSection crit_;
  132. bool finished_;
  133. DISALLOW_COPY_AND_ASSIGN(FileReadThread);
  134. };
  135. /////////////////////////////////////////////////////////////////////
  136. // Implementation of class FileVideoCapturer
  137. /////////////////////////////////////////////////////////////////////
  138. static const int64 kNumNanoSecsPerMilliSec = 1000000;
  139. const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:";
  140. FileVideoCapturer::FileVideoCapturer()
  141. : frame_buffer_size_(0),
  142. file_read_thread_(NULL),
  143. repeat_(0),
  144. start_time_ns_(0),
  145. last_frame_timestamp_ns_(0),
  146. ignore_framerate_(false) {
  147. }
  148. FileVideoCapturer::~FileVideoCapturer() {
  149. Stop();
  150. delete[] static_cast<char*>(captured_frame_.data);
  151. }
  152. bool FileVideoCapturer::Init(const Device& device) {
  153. if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
  154. return false;
  155. }
  156. std::string filename(device.name);
  157. if (IsRunning()) {
  158. LOG(LS_ERROR) << "The file video capturer is already running";
  159. return false;
  160. }
  161. // Open the file.
  162. int err;
  163. if (!video_file_.Open(filename, "rb", &err)) {
  164. LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err;
  165. return false;
  166. }
  167. // Read the first frame's header to determine the supported format.
  168. CapturedFrame frame;
  169. if (talk_base::SR_SUCCESS != ReadFrameHeader(&frame)) {
  170. LOG(LS_ERROR) << "Failed to read the first frame header";
  171. video_file_.Close();
  172. return false;
  173. }
  174. // Seek back to the start of the file.
  175. if (!video_file_.SetPosition(0)) {
  176. LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
  177. video_file_.Close();
  178. return false;
  179. }
  180. // Enumerate the supported formats. We have only one supported format. We set
  181. // the frame interval to kMinimumInterval here. In Start(), if the capture
  182. // format's interval is greater than kMinimumInterval, we use the interval;
  183. // otherwise, we use the timestamp in the file to control the interval.
  184. VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval,
  185. frame.fourcc);
  186. std::vector<VideoFormat> supported;
  187. supported.push_back(format);
  188. SetId(device.id);
  189. SetSupportedFormats(supported);
  190. return true;
  191. }
  192. bool FileVideoCapturer::Init(const std::string& filename) {
  193. return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename));
  194. }
  195. CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) {
  196. if (IsRunning()) {
  197. LOG(LS_ERROR) << "The file video capturer is already running";
  198. return CS_FAILED;
  199. }
  200. if (talk_base::SS_CLOSED == video_file_.GetState()) {
  201. LOG(LS_ERROR) << "File not opened yet";
  202. return CS_NO_DEVICE;
  203. } else if (!video_file_.SetPosition(0)) {
  204. LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
  205. return CS_FAILED;
  206. }
  207. SetCaptureFormat(&capture_format);
  208. // Create a thread to read the file.
  209. file_read_thread_ = new FileReadThread(this);
  210. start_time_ns_ = kNumNanoSecsPerMilliSec *
  211. static_cast<int64>(talk_base::Time());
  212. bool ret = file_read_thread_->Start();
  213. if (ret) {
  214. LOG(LS_INFO) << "File video capturer '" << GetId() << "' started";
  215. return CS_RUNNING;
  216. } else {
  217. LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start";
  218. return CS_FAILED;
  219. }
  220. }
  221. bool FileVideoCapturer::IsRunning() {
  222. return file_read_thread_ && !file_read_thread_->Finished();
  223. }
  224. void FileVideoCapturer::Stop() {
  225. if (file_read_thread_) {
  226. file_read_thread_->Stop();
  227. file_read_thread_ = NULL;
  228. LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped";
  229. }
  230. SetCaptureFormat(NULL);
  231. }
  232. bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
  233. if (!fourccs) {
  234. return false;
  235. }
  236. fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
  237. return true;
  238. }
  239. talk_base::StreamResult FileVideoCapturer::ReadFrameHeader(
  240. CapturedFrame* frame) {
  241. // We first read kFrameHeaderSize bytes from the file stream to a memory
  242. // buffer, then construct a bytebuffer from the memory buffer, and finally
  243. // read the frame header from the bytebuffer.
  244. char header[CapturedFrame::kFrameHeaderSize];
  245. talk_base::StreamResult sr;
  246. size_t bytes_read;
  247. int error;
  248. sr = video_file_.Read(header,
  249. CapturedFrame::kFrameHeaderSize,
  250. &bytes_read,
  251. &error);
  252. LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr
  253. << ", bytes read = " << bytes_read << ", error = " << error;
  254. if (talk_base::SR_SUCCESS == sr) {
  255. if (CapturedFrame::kFrameHeaderSize != bytes_read) {
  256. return talk_base::SR_EOS;
  257. }
  258. talk_base::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize);
  259. buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->width));
  260. buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->height));
  261. buffer.ReadUInt32(&frame->fourcc);
  262. buffer.ReadUInt32(&frame->pixel_width);
  263. buffer.ReadUInt32(&frame->pixel_height);
  264. buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->elapsed_time));
  265. buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->time_stamp));
  266. buffer.ReadUInt32(&frame->data_size);
  267. }
  268. return sr;
  269. }
  270. // Executed in the context of FileReadThread.
  271. bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) {
  272. uint32 start_read_time_ms = talk_base::Time();
  273. // 1. Signal the previously read frame to downstream.
  274. if (!first_frame) {
  275. captured_frame_.time_stamp = kNumNanoSecsPerMilliSec *
  276. static_cast<int64>(start_read_time_ms);
  277. captured_frame_.elapsed_time = captured_frame_.time_stamp - start_time_ns_;
  278. SignalFrameCaptured(this, &captured_frame_);
  279. }
  280. // 2. Read the next frame.
  281. if (talk_base::SS_CLOSED == video_file_.GetState()) {
  282. LOG(LS_ERROR) << "File not opened yet";
  283. return false;
  284. }
  285. // 2.1 Read the frame header.
  286. talk_base::StreamResult result = ReadFrameHeader(&captured_frame_);
  287. if (talk_base::SR_EOS == result) { // Loop back if repeat.
  288. if (repeat_ != talk_base::kForever) {
  289. if (repeat_ > 0) {
  290. --repeat_;
  291. } else {
  292. return false;
  293. }
  294. }
  295. if (video_file_.SetPosition(0)) {
  296. result = ReadFrameHeader(&captured_frame_);
  297. }
  298. }
  299. if (talk_base::SR_SUCCESS != result) {
  300. LOG(LS_ERROR) << "Failed to read the frame header";
  301. return false;
  302. }
  303. // 2.2 Reallocate memory for the frame data if necessary.
  304. if (frame_buffer_size_ < captured_frame_.data_size) {
  305. frame_buffer_size_ = captured_frame_.data_size;
  306. delete[] static_cast<char*>(captured_frame_.data);
  307. captured_frame_.data = new char[frame_buffer_size_];
  308. }
  309. // 2.3 Read the frame adata.
  310. if (talk_base::SR_SUCCESS != video_file_.Read(captured_frame_.data,
  311. captured_frame_.data_size,
  312. NULL, NULL)) {
  313. LOG(LS_ERROR) << "Failed to read frame data";
  314. return false;
  315. }
  316. // 3. Decide how long to wait for the next frame.
  317. *wait_time_ms = 0;
  318. // If the capture format's interval is not kMinimumInterval, we use it to
  319. // control the rate; otherwise, we use the timestamp in the file to control
  320. // the rate.
  321. if (!first_frame && !ignore_framerate_) {
  322. int64 interval_ns =
  323. GetCaptureFormat()->interval > VideoFormat::kMinimumInterval ?
  324. GetCaptureFormat()->interval :
  325. captured_frame_.time_stamp - last_frame_timestamp_ns_;
  326. int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec);
  327. interval_ms -= talk_base::Time() - start_read_time_ms;
  328. if (interval_ms > 0) {
  329. *wait_time_ms = interval_ms;
  330. }
  331. }
  332. // Keep the original timestamp read from the file.
  333. last_frame_timestamp_ns_ = captured_frame_.time_stamp;
  334. return true;
  335. }
  336. } // namespace cricket