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 "talk/base/bytebuffer.h"
     31 #include "talk/base/criticalsection.h"
     32 #include "talk/base/logging.h"
     33 #include "talk/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 (talk_base::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     talk_base::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 (talk_base::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 (talk_base::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 talk_base::Thread, public talk_base::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     talk_base::CritScope cs(&crit_);
    127     finished_ = true;
    128   }
    129 
    130   // Override virtual method of parent MessageHandler. Context: Worker Thread.
    131   virtual void OnMessage(talk_base::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     talk_base::CritScope cs(&crit_);
    143     return finished_;
    144   }
    145 
    146  private:
    147   FileVideoCapturer* capturer_;
    148   mutable talk_base::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 (talk_base::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   SetId(device.id);
    213   SetSupportedFormats(supported);
    214   return true;
    215 }
    216 
    217 bool FileVideoCapturer::Init(const std::string& filename) {
    218   return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename));
    219 }
    220 
    221 CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) {
    222   if (IsRunning()) {
    223     LOG(LS_ERROR) << "The file video capturer is already running";
    224     return CS_FAILED;
    225   }
    226 
    227   if (talk_base::SS_CLOSED == video_file_.GetState()) {
    228     LOG(LS_ERROR) << "File not opened yet";
    229     return CS_NO_DEVICE;
    230   } else if (!video_file_.SetPosition(0)) {
    231     LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
    232     return CS_FAILED;
    233   }
    234 
    235   SetCaptureFormat(&capture_format);
    236   // Create a thread to read the file.
    237   file_read_thread_ = new FileReadThread(this);
    238   start_time_ns_ = kNumNanoSecsPerMilliSec *
    239       static_cast<int64>(talk_base::Time());
    240   bool ret = file_read_thread_->Start();
    241   if (ret) {
    242     LOG(LS_INFO) << "File video capturer '" << GetId() << "' started";
    243     return CS_RUNNING;
    244   } else {
    245     LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start";
    246     return CS_FAILED;
    247   }
    248 }
    249 
    250 bool FileVideoCapturer::IsRunning() {
    251   return file_read_thread_ && !file_read_thread_->Finished();
    252 }
    253 
    254 void FileVideoCapturer::Stop() {
    255   if (file_read_thread_) {
    256     file_read_thread_->Stop();
    257     file_read_thread_ = NULL;
    258     LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped";
    259   }
    260   SetCaptureFormat(NULL);
    261 }
    262 
    263 bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
    264   if (!fourccs) {
    265     return false;
    266   }
    267 
    268   fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
    269   return true;
    270 }
    271 
    272 talk_base::StreamResult FileVideoCapturer::ReadFrameHeader(
    273     CapturedFrame* frame) {
    274   // We first read kFrameHeaderSize bytes from the file stream to a memory
    275   // buffer, then construct a bytebuffer from the memory buffer, and finally
    276   // read the frame header from the bytebuffer.
    277   char header[CapturedFrame::kFrameHeaderSize];
    278   talk_base::StreamResult sr;
    279   size_t bytes_read;
    280   int error;
    281   sr = video_file_.Read(header,
    282                         CapturedFrame::kFrameHeaderSize,
    283                         &bytes_read,
    284                         &error);
    285   LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr
    286                   << ", bytes read = " << bytes_read << ", error = " << error;
    287   if (talk_base::SR_SUCCESS == sr) {
    288     if (CapturedFrame::kFrameHeaderSize != bytes_read) {
    289       return talk_base::SR_EOS;
    290     }
    291     talk_base::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize);
    292     buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->width));
    293     buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->height));
    294     buffer.ReadUInt32(&frame->fourcc);
    295     buffer.ReadUInt32(&frame->pixel_width);
    296     buffer.ReadUInt32(&frame->pixel_height);
    297     buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->elapsed_time));
    298     buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->time_stamp));
    299     buffer.ReadUInt32(&frame->data_size);
    300   }
    301 
    302   return sr;
    303 }
    304 
    305 // Executed in the context of FileReadThread.
    306 bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) {
    307   uint32 start_read_time_ms = talk_base::Time();
    308 
    309   // 1. Signal the previously read frame to downstream.
    310   if (!first_frame) {
    311     captured_frame_.time_stamp = kNumNanoSecsPerMilliSec *
    312         static_cast<int64>(start_read_time_ms);
    313     captured_frame_.elapsed_time = captured_frame_.time_stamp - start_time_ns_;
    314     SignalFrameCaptured(this, &captured_frame_);
    315   }
    316 
    317   // 2. Read the next frame.
    318   if (talk_base::SS_CLOSED == video_file_.GetState()) {
    319     LOG(LS_ERROR) << "File not opened yet";
    320     return false;
    321   }
    322   // 2.1 Read the frame header.
    323   talk_base::StreamResult result = ReadFrameHeader(&captured_frame_);
    324   if (talk_base::SR_EOS == result) {  // Loop back if repeat.
    325     if (repeat_ != talk_base::kForever) {
    326       if (repeat_ > 0) {
    327         --repeat_;
    328       } else {
    329         return false;
    330       }
    331     }
    332 
    333     if (video_file_.SetPosition(0)) {
    334       result = ReadFrameHeader(&captured_frame_);
    335     }
    336   }
    337   if (talk_base::SR_SUCCESS != result) {
    338     LOG(LS_ERROR) << "Failed to read the frame header";
    339     return false;
    340   }
    341   // 2.2 Reallocate memory for the frame data if necessary.
    342   if (frame_buffer_size_ < captured_frame_.data_size) {
    343     frame_buffer_size_ = captured_frame_.data_size;
    344     delete[] static_cast<char*>(captured_frame_.data);
    345     captured_frame_.data = new char[frame_buffer_size_];
    346   }
    347   // 2.3 Read the frame adata.
    348   if (talk_base::SR_SUCCESS != video_file_.Read(captured_frame_.data,
    349                                                 captured_frame_.data_size,
    350                                                 NULL, NULL)) {
    351     LOG(LS_ERROR) << "Failed to read frame data";
    352     return false;
    353   }
    354 
    355   // 3. Decide how long to wait for the next frame.
    356   *wait_time_ms = 0;
    357 
    358   // If the capture format's interval is not kMinimumInterval, we use it to
    359   // control the rate; otherwise, we use the timestamp in the file to control
    360   // the rate.
    361   if (!first_frame && !ignore_framerate_) {
    362     int64 interval_ns =
    363         GetCaptureFormat()->interval > VideoFormat::kMinimumInterval ?
    364         GetCaptureFormat()->interval :
    365         captured_frame_.time_stamp - last_frame_timestamp_ns_;
    366     int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec);
    367     interval_ms -= talk_base::Time() - start_read_time_ms;
    368     if (interval_ms > 0) {
    369       *wait_time_ms = interval_ms;
    370     }
    371   }
    372   // Keep the original timestamp read from the file.
    373   last_frame_timestamp_ns_ = captured_frame_.time_stamp;
    374   return true;
    375 }
    376 
    377 }  // namespace cricket
    378