Home | History | Annotate | Download | only in devices
      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 
     28 #include "talk/media/devices/filevideocapturer.h"
     29 
     30 #include "webrtc/base/bytebuffer.h"
     31 #include "webrtc/base/criticalsection.h"
     32 #include "webrtc/base/logging.h"
     33 #include "webrtc/base/thread.h"
     34 
     35 namespace cricket {
     36 
     37 /////////////////////////////////////////////////////////////////////
     38 // Implementation of class VideoRecorder
     39 /////////////////////////////////////////////////////////////////////
     40 bool VideoRecorder::Start(const std::string& filename, bool write_header) {
     41   Stop();
     42   write_header_ = write_header;
     43   int err;
     44   if (!video_file_.Open(filename, "wb", &err)) {
     45     LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err;
     46     return false;
     47   }
     48   return true;
     49 }
     50 
     51 void VideoRecorder::Stop() {
     52   video_file_.Close();
     53 }
     54 
     55 bool VideoRecorder::RecordFrame(const CapturedFrame& frame) {
     56   if (rtc::SS_CLOSED == video_file_.GetState()) {
     57     LOG(LS_ERROR) << "File not opened yet";
     58     return false;
     59   }
     60 
     61   uint32 size = 0;
     62   if (!frame.GetDataSize(&size)) {
     63     LOG(LS_ERROR) << "Unable to calculate the data size of the frame";
     64     return false;
     65   }
     66 
     67   if (write_header_) {
     68     // Convert the frame header to bytebuffer.
     69     rtc::ByteBuffer buffer;
     70     buffer.WriteUInt32(frame.width);
     71     buffer.WriteUInt32(frame.height);
     72     buffer.WriteUInt32(frame.fourcc);
     73     buffer.WriteUInt32(frame.pixel_width);
     74     buffer.WriteUInt32(frame.pixel_height);
     75     buffer.WriteUInt64(frame.elapsed_time);
     76     buffer.WriteUInt64(frame.time_stamp);
     77     buffer.WriteUInt32(size);
     78 
     79     // Write the bytebuffer to file.
     80     if (rtc::SR_SUCCESS != video_file_.Write(buffer.Data(),
     81                                                    buffer.Length(),
     82                                                    NULL,
     83                                                    NULL)) {
     84       LOG(LS_ERROR) << "Failed to write frame header";
     85       return false;
     86     }
     87   }
     88   // Write the frame data to file.
     89   if (rtc::SR_SUCCESS != video_file_.Write(frame.data,
     90                                                  size,
     91                                                  NULL,
     92                                                  NULL)) {
     93     LOG(LS_ERROR) << "Failed to write frame data";
     94     return false;
     95   }
     96 
     97   return true;
     98 }
     99 
    100 ///////////////////////////////////////////////////////////////////////
    101 // Definition of private class FileReadThread that periodically reads
    102 // frames from a file.
    103 ///////////////////////////////////////////////////////////////////////
    104 class FileVideoCapturer::FileReadThread
    105     : public rtc::Thread, public rtc::MessageHandler {
    106  public:
    107   explicit FileReadThread(FileVideoCapturer* capturer)
    108       : capturer_(capturer),
    109         finished_(false) {
    110   }
    111 
    112   virtual ~FileReadThread() {
    113     Stop();
    114   }
    115 
    116   // Override virtual method of parent Thread. Context: Worker Thread.
    117   virtual void Run() {
    118     // Read the first frame and start the message pump. The pump runs until
    119     // Stop() is called externally or Quit() is called by OnMessage().
    120     int waiting_time_ms = 0;
    121     if (capturer_ && capturer_->ReadFrame(true, &waiting_time_ms)) {
    122       PostDelayed(waiting_time_ms, this);
    123       Thread::Run();
    124     }
    125 
    126     rtc::CritScope cs(&crit_);
    127     finished_ = true;
    128   }
    129 
    130   // Override virtual method of parent MessageHandler. Context: Worker Thread.
    131   virtual void OnMessage(rtc::Message* /*pmsg*/) {
    132     int waiting_time_ms = 0;
    133     if (capturer_ && capturer_->ReadFrame(false, &waiting_time_ms)) {
    134       PostDelayed(waiting_time_ms, this);
    135     } else {
    136       Quit();
    137     }
    138   }
    139 
    140   // Check if Run() is finished.
    141   bool Finished() const {
    142     rtc::CritScope cs(&crit_);
    143     return finished_;
    144   }
    145 
    146  private:
    147   FileVideoCapturer* capturer_;
    148   mutable rtc::CriticalSection crit_;
    149   bool finished_;
    150 
    151   DISALLOW_COPY_AND_ASSIGN(FileReadThread);
    152 };
    153 
    154 /////////////////////////////////////////////////////////////////////
    155 // Implementation of class FileVideoCapturer
    156 /////////////////////////////////////////////////////////////////////
    157 static const int64 kNumNanoSecsPerMilliSec = 1000000;
    158 const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:";
    159 
    160 FileVideoCapturer::FileVideoCapturer()
    161     : frame_buffer_size_(0),
    162       file_read_thread_(NULL),
    163       repeat_(0),
    164       start_time_ns_(0),
    165       last_frame_timestamp_ns_(0),
    166       ignore_framerate_(false) {
    167 }
    168 
    169 FileVideoCapturer::~FileVideoCapturer() {
    170   Stop();
    171   delete[] static_cast<char*>(captured_frame_.data);
    172 }
    173 
    174 bool FileVideoCapturer::Init(const Device& device) {
    175   if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
    176     return false;
    177   }
    178   std::string filename(device.name);
    179   if (IsRunning()) {
    180     LOG(LS_ERROR) << "The file video capturer is already running";
    181     return false;
    182   }
    183   // Open the file.
    184   int err;
    185   if (!video_file_.Open(filename, "rb", &err)) {
    186     LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err;
    187     return false;
    188   }
    189   // Read the first frame's header to determine the supported format.
    190   CapturedFrame frame;
    191   if (rtc::SR_SUCCESS != ReadFrameHeader(&frame)) {
    192     LOG(LS_ERROR) << "Failed to read the first frame header";
    193     video_file_.Close();
    194     return false;
    195   }
    196   // Seek back to the start of the file.
    197   if (!video_file_.SetPosition(0)) {
    198     LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
    199     video_file_.Close();
    200     return false;
    201   }
    202 
    203   // Enumerate the supported formats. We have only one supported format. We set
    204   // the frame interval to kMinimumInterval here. In Start(), if the capture
    205   // format's interval is greater than kMinimumInterval, we use the interval;
    206   // otherwise, we use the timestamp in the file to control the interval.
    207   VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval,
    208                      frame.fourcc);
    209   std::vector<VideoFormat> supported;
    210   supported.push_back(format);
    211 
    212   // TODO(thorcarpenter): Report the actual file video format as the supported
    213   // format. Do not use kMinimumInterval as it conflicts with video adaptation.
    214   SetId(device.id);
    215   SetSupportedFormats(supported);
    216 
    217   // TODO(wuwang): Design an E2E integration test for video adaptation,
    218   // then remove the below call to disable the video adapter.
    219   set_enable_video_adapter(false);
    220   return true;
    221 }
    222 
    223 bool FileVideoCapturer::Init(const std::string& filename) {
    224   return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename));
    225 }
    226 
    227 CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) {
    228   if (IsRunning()) {
    229     LOG(LS_ERROR) << "The file video capturer is already running";
    230     return CS_FAILED;
    231   }
    232 
    233   if (rtc::SS_CLOSED == video_file_.GetState()) {
    234     LOG(LS_ERROR) << "File not opened yet";
    235     return CS_NO_DEVICE;
    236   } else if (!video_file_.SetPosition(0)) {
    237     LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
    238     return CS_FAILED;
    239   }
    240 
    241   SetCaptureFormat(&capture_format);
    242   // Create a thread to read the file.
    243   file_read_thread_ = new FileReadThread(this);
    244   start_time_ns_ = kNumNanoSecsPerMilliSec *
    245       static_cast<int64>(rtc::Time());
    246   bool ret = file_read_thread_->Start();
    247   if (ret) {
    248     LOG(LS_INFO) << "File video capturer '" << GetId() << "' started";
    249     return CS_RUNNING;
    250   } else {
    251     LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start";
    252     return CS_FAILED;
    253   }
    254 }
    255 
    256 bool FileVideoCapturer::IsRunning() {
    257   return file_read_thread_ && !file_read_thread_->Finished();
    258 }
    259 
    260 void FileVideoCapturer::Stop() {
    261   if (file_read_thread_) {
    262     file_read_thread_->Stop();
    263     file_read_thread_ = NULL;
    264     LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped";
    265   }
    266   SetCaptureFormat(NULL);
    267 }
    268 
    269 bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
    270   if (!fourccs) {
    271     return false;
    272   }
    273 
    274   fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
    275   return true;
    276 }
    277 
    278 rtc::StreamResult FileVideoCapturer::ReadFrameHeader(
    279     CapturedFrame* frame) {
    280   // We first read kFrameHeaderSize bytes from the file stream to a memory
    281   // buffer, then construct a bytebuffer from the memory buffer, and finally
    282   // read the frame header from the bytebuffer.
    283   char header[CapturedFrame::kFrameHeaderSize];
    284   rtc::StreamResult sr;
    285   size_t bytes_read;
    286   int error;
    287   sr = video_file_.Read(header,
    288                         CapturedFrame::kFrameHeaderSize,
    289                         &bytes_read,
    290                         &error);
    291   LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr
    292                   << ", bytes read = " << bytes_read << ", error = " << error;
    293   if (rtc::SR_SUCCESS == sr) {
    294     if (CapturedFrame::kFrameHeaderSize != bytes_read) {
    295       return rtc::SR_EOS;
    296     }
    297     rtc::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize);
    298     buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->width));
    299     buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->height));
    300     buffer.ReadUInt32(&frame->fourcc);
    301     buffer.ReadUInt32(&frame->pixel_width);
    302     buffer.ReadUInt32(&frame->pixel_height);
    303     buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->elapsed_time));
    304     buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->time_stamp));
    305     buffer.ReadUInt32(&frame->data_size);
    306   }
    307 
    308   return sr;
    309 }
    310 
    311 // Executed in the context of FileReadThread.
    312 bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) {
    313   uint32 start_read_time_ms = rtc::Time();
    314 
    315   // 1. Signal the previously read frame to downstream.
    316   if (!first_frame) {
    317     captured_frame_.time_stamp = kNumNanoSecsPerMilliSec *
    318         static_cast<int64>(start_read_time_ms);
    319     captured_frame_.elapsed_time = captured_frame_.time_stamp - start_time_ns_;
    320     SignalFrameCaptured(this, &captured_frame_);
    321   }
    322 
    323   // 2. Read the next frame.
    324   if (rtc::SS_CLOSED == video_file_.GetState()) {
    325     LOG(LS_ERROR) << "File not opened yet";
    326     return false;
    327   }
    328   // 2.1 Read the frame header.
    329   rtc::StreamResult result = ReadFrameHeader(&captured_frame_);
    330   if (rtc::SR_EOS == result) {  // Loop back if repeat.
    331     if (repeat_ != rtc::kForever) {
    332       if (repeat_ > 0) {
    333         --repeat_;
    334       } else {
    335         return false;
    336       }
    337     }
    338 
    339     if (video_file_.SetPosition(0)) {
    340       result = ReadFrameHeader(&captured_frame_);
    341     }
    342   }
    343   if (rtc::SR_SUCCESS != result) {
    344     LOG(LS_ERROR) << "Failed to read the frame header";
    345     return false;
    346   }
    347   // 2.2 Reallocate memory for the frame data if necessary.
    348   if (frame_buffer_size_ < captured_frame_.data_size) {
    349     frame_buffer_size_ = captured_frame_.data_size;
    350     delete[] static_cast<char*>(captured_frame_.data);
    351     captured_frame_.data = new char[frame_buffer_size_];
    352   }
    353   // 2.3 Read the frame adata.
    354   if (rtc::SR_SUCCESS != video_file_.Read(captured_frame_.data,
    355                                                 captured_frame_.data_size,
    356                                                 NULL, NULL)) {
    357     LOG(LS_ERROR) << "Failed to read frame data";
    358     return false;
    359   }
    360 
    361   // 3. Decide how long to wait for the next frame.
    362   *wait_time_ms = 0;
    363 
    364   // If the capture format's interval is not kMinimumInterval, we use it to
    365   // control the rate; otherwise, we use the timestamp in the file to control
    366   // the rate.
    367   if (!first_frame && !ignore_framerate_) {
    368     int64 interval_ns =
    369         GetCaptureFormat()->interval > VideoFormat::kMinimumInterval ?
    370         GetCaptureFormat()->interval :
    371         captured_frame_.time_stamp - last_frame_timestamp_ns_;
    372     int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec);
    373     interval_ms -= rtc::Time() - start_read_time_ms;
    374     if (interval_ms > 0) {
    375       *wait_time_ms = interval_ms;
    376     }
    377   }
    378   // Keep the original timestamp read from the file.
    379   last_frame_timestamp_ns_ = captured_frame_.time_stamp;
    380   return true;
    381 }
    382 
    383 }  // namespace cricket
    384