| OLD | NEW | 
|    1 // Copyright 2015 The Chromium Authors. All rights reserved. |    1 // Copyright 2015 The Chromium Authors. All rights reserved. | 
|    2 // Use of this source code is governed by a BSD-style license that can be |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "content/renderer/media/video_track_recorder.h" |    5 #include "content/renderer/media/video_track_recorder.h" | 
|    6  |    6  | 
|    7 #include "base/bind.h" |    7 #include "base/bind.h" | 
|    8 #include "base/logging.h" |    8 #include "base/logging.h" | 
|    9 #include "base/sys_info.h" |    9 #include "base/sys_info.h" | 
|   10 #include "base/threading/thread.h" |   10 #include "base/threading/thread.h" | 
|   11 #include "base/time/time.h" |   11 #include "base/time/time.h" | 
|   12 #include "base/trace_event/trace_event.h" |   12 #include "base/trace_event/trace_event.h" | 
|   13 #include "content/child/child_process.h" |  | 
|   14 #include "media/base/bind_to_current_loop.h" |  | 
|   15 #include "media/base/video_frame.h" |   13 #include "media/base/video_frame.h" | 
|   16  |   14  | 
|   17 extern "C" { |   15 extern "C" { | 
|   18 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |   16 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 
|   19 // backwards compatibility for legacy applications using the library. |   17 // backwards compatibility for legacy applications using the library. | 
|   20 #define VPX_CODEC_DISABLE_COMPAT 1 |   18 #define VPX_CODEC_DISABLE_COMPAT 1 | 
|   21 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |   19 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 
|   22 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |   20 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" | 
|   23 } |   21 } | 
|   24  |   22  | 
|   25 using media::VideoFrame; |   23 using media::VideoFrame; | 
|   26 using media::VideoFrameMetadata; |   24 using media::VideoFrameMetadata; | 
|   27  |   25  | 
|   28 namespace content { |   26 namespace content { | 
|   29  |   27  | 
|   30 namespace { |   28 namespace { | 
 |   29  | 
|   31 const vpx_codec_flags_t kNoFlags = 0; |   30 const vpx_codec_flags_t kNoFlags = 0; | 
|   32  |   31  | 
 |   32 // Originally from remoting/codec/scoped_vpx_codec.h. | 
 |   33 // TODO(mcasas): Refactor into a common location. | 
 |   34 struct VpxCodecDeleter { | 
 |   35   void operator()(vpx_codec_ctx_t* codec) { | 
 |   36     if (!codec) | 
 |   37       return; | 
 |   38     vpx_codec_err_t ret = vpx_codec_destroy(codec); | 
 |   39     CHECK_EQ(ret, VPX_CODEC_OK); | 
 |   40     delete codec; | 
 |   41   } | 
 |   42 }; | 
 |   43  | 
 |   44 typedef scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> ScopedVpxCodecCtxPtr; | 
 |   45  | 
|   33 void OnFrameEncodeCompleted( |   46 void OnFrameEncodeCompleted( | 
|   34     const content::VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb, |   47     const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb, | 
|   35     const scoped_refptr<VideoFrame>& frame, |   48     const scoped_refptr<VideoFrame>& frame, | 
|   36     scoped_ptr<std::string> data, |   49     scoped_ptr<std::string> data, | 
|   37     base::TimeTicks capture_timestamp, |   50     base::TimeTicks capture_timestamp, | 
|   38     bool keyframe) { |   51     bool keyframe) { | 
|   39   DVLOG(1) << (keyframe ? "" : "non ") << "keyframe " |   52   DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "<< data->length() << "B, " | 
|   40            << capture_timestamp << " ms - " << data->length() << "B "; |   53            << capture_timestamp << " ms"; | 
|   41   on_encoded_video_cb.Run(frame, base::StringPiece(*data), capture_timestamp, |   54   on_encoded_video_cb.Run(frame, base::StringPiece(*data), capture_timestamp, | 
|   42                           keyframe); |   55                           keyframe); | 
|   43 } |   56 } | 
|   44  |   57  | 
|   45 }  // anonymous namespace |   58 }  // anonymous namespace | 
|   46  |   59  | 
|   47 // Inner class encapsulating all libvpx interactions and the encoding+delivery |   60 // Inner class encapsulating all libvpx interactions and the encoding+delivery | 
|   48 // of received frames. This class is: |   61 // of received frames. Limitation: Only VP8 is supported for the time being. | 
|   49 // - created and destroyed on its parent's thread (usually the main render |   62 // This class must be ref-counted because the MediaStreamVideoTrack will hold a | 
|   50 // thread), |   63 // reference to it, via the callback MediaStreamVideoSink passes along, and it's | 
|   51 // - receives VideoFrames and Run()s the callbacks on another thread (supposedly |   64 // unknown when exactly it will release that reference. This class: | 
|   52 // the render IO thread), which is cached on first frame arrival, |   65 // - is created and destroyed on its parent's thread (usually the main Render | 
 |   66 // thread); | 
 |   67 // - receives VideoFrames and Run()s the callbacks on |origin_task_runner_|, | 
 |   68 // which is cached on first frame arrival, and is supposed to be the render IO | 
 |   69 // thread, but this is not enforced; | 
|   53 // - uses an internal |encoding_thread_| for libvpx interactions, notably for |   70 // - uses an internal |encoding_thread_| for libvpx interactions, notably for | 
|   54 // encoding (which might take some time). |   71 // encoding (which might take some time). | 
|   55 // Only VP8 is supported for the time being. |   72 class VideoTrackRecorder::VpxEncoder final | 
|   56 class VideoTrackRecorder::VpxEncoder final { |   73     : public base::RefCountedThreadSafe<VpxEncoder> { | 
|   57  public: |   74  public: | 
 |   75   static void ShutdownEncoder(scoped_ptr<base::Thread> encoding_thread, | 
 |   76                               ScopedVpxCodecCtxPtr encoder); | 
 |   77  | 
|   58   explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback); |   78   explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback); | 
|   59   ~VpxEncoder(); |  | 
|   60  |   79  | 
|   61   void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |   80   void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, | 
|   62                         base::TimeTicks capture_timestamp); |   81                         base::TimeTicks capture_timestamp); | 
|   63  |   82  | 
|   64  private: |   83  private: | 
 |   84   friend class base::RefCountedThreadSafe<VpxEncoder>; | 
 |   85   ~VpxEncoder(); | 
 |   86  | 
|   65   void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, |   87   void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, | 
|   66                               base::TimeTicks capture_timestamp); |   88                               base::TimeTicks capture_timestamp); | 
|   67  |   89  | 
|   68   void ConfigureVp8Encoding(const gfx::Size& size); |   90   void ConfigureVp8Encoding(const gfx::Size& size); | 
|   69  |   91  | 
|   70   // Returns true if |codec_config_| has been filled in at least once. |   92   // Returns true if |codec_config_| has been filled in at least once. | 
|   71   bool IsInitialized() const; |   93   bool IsInitialized() const; | 
|   72  |   94  | 
|   73   // Estimate the frame duration from |frame| and |last_frame_timestamp_|. |   95   // Estimate the frame duration from |frame| and |last_frame_timestamp_|. | 
|   74   base::TimeDelta CalculateFrameDuration( |   96   base::TimeDelta CalculateFrameDuration( | 
|   75       const scoped_refptr<VideoFrame>& frame); |   97       const scoped_refptr<VideoFrame>& frame); | 
|   76  |   98  | 
|   77   // Used to check that we are destroyed on the same thread we were created. |   99   // Used to shutdown properly on the same thread we were created. | 
|   78   base::ThreadChecker main_render_thread_checker_; |  100   const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 
|   79  |  101  | 
|   80   // Task runner where frames to encode and reply callbacks must happen. |  102   // Task runner where frames to encode and reply callbacks must happen. | 
|   81   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; |  103   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; | 
|   82  |  104  | 
|   83   // This callback should be exercised on IO thread. |  105   // This callback should be exercised on IO thread. | 
|   84   const OnEncodedVideoCB on_encoded_video_callback_; |  106   const OnEncodedVideoCB on_encoded_video_callback_; | 
|   85  |  107  | 
|   86   // Thread for encoding. Active as long as VpxEncoder exists. All variables |  108   // Thread for encoding. Active for the lifetime of VpxEncoder. All variables | 
|   87   // below this are used in this thread. |  109   // below this are used in this thread. | 
|   88   base::Thread encoding_thread_; |  110   scoped_ptr<base::Thread> encoding_thread_; | 
|   89   // VP8 internal objects: configuration, encoder and Vpx Image wrapper. |  111   // VP8 internal objects: configuration and encoder. | 
|   90   vpx_codec_enc_cfg_t codec_config_; |  112   vpx_codec_enc_cfg_t codec_config_; | 
|   91   vpx_codec_ctx_t encoder_; |  113   // |encoder_| is a special scoped pointer to guarantee proper destruction. | 
 |  114   // Again, it should only be accessed on |encoding_thread_|. | 
 |  115   ScopedVpxCodecCtxPtr encoder_; | 
|   92  |  116  | 
|   93   // The |VideoFrame::timestamp()| of the last encoded frame.  This is used to |  117   // The |VideoFrame::timestamp()| of the last encoded frame.  This is used to | 
|   94   // predict the duration of the next frame. |  118   // predict the duration of the next frame. | 
|   95   base::TimeDelta last_frame_timestamp_; |  119   base::TimeDelta last_frame_timestamp_; | 
|   96  |  120  | 
|   97   DISALLOW_COPY_AND_ASSIGN(VpxEncoder); |  121   DISALLOW_COPY_AND_ASSIGN(VpxEncoder); | 
|   98 }; |  122 }; | 
|   99  |  123  | 
 |  124 // static | 
 |  125 void VideoTrackRecorder::VpxEncoder::ShutdownEncoder( | 
 |  126     scoped_ptr<base::Thread> encoding_thread, | 
 |  127     ScopedVpxCodecCtxPtr encoder) { | 
 |  128   DCHECK(encoding_thread->IsRunning()); | 
 |  129   encoding_thread->Stop(); | 
 |  130   // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. | 
 |  131 } | 
 |  132  | 
|  100 VideoTrackRecorder::VpxEncoder::VpxEncoder( |  133 VideoTrackRecorder::VpxEncoder::VpxEncoder( | 
|  101     const OnEncodedVideoCB& on_encoded_video_callback) |  134     const OnEncodedVideoCB& on_encoded_video_callback) | 
|  102     : on_encoded_video_callback_(on_encoded_video_callback), |  135     : main_task_runner_(base::MessageLoop::current()->task_runner()), | 
|  103       encoding_thread_("EncodingThread") { |  136       on_encoded_video_callback_(on_encoded_video_callback), | 
 |  137       encoding_thread_(new base::Thread("EncodingThread")) { | 
|  104   DCHECK(!on_encoded_video_callback_.is_null()); |  138   DCHECK(!on_encoded_video_callback_.is_null()); | 
|  105  |  139  | 
|  106   codec_config_.g_timebase.den = 0;  // Not initialized. |  140   codec_config_.g_timebase.den = 0;  // Not initialized. | 
|  107  |  141  | 
|  108   DCHECK(!encoding_thread_.IsRunning()); |  142   DCHECK(!encoding_thread_->IsRunning()); | 
|  109   encoding_thread_.Start(); |  143   encoding_thread_->Start(); | 
|  110 } |  144 } | 
|  111  |  145  | 
|  112 VideoTrackRecorder::VpxEncoder::~VpxEncoder() { |  146 VideoTrackRecorder::VpxEncoder::~VpxEncoder() { | 
|  113   DCHECK(main_render_thread_checker_.CalledOnValidThread()); |  147   main_task_runner_->PostTask(FROM_HERE, | 
|  114   DCHECK(encoding_thread_.IsRunning()); |  148                               base::Bind(&VpxEncoder::ShutdownEncoder, | 
|  115   encoding_thread_.Stop(); |  149                                          base::Passed(&encoding_thread_), | 
|  116   vpx_codec_destroy(&encoder_); |  150                                          base::Passed(&encoder_))); | 
|  117 } |  151 } | 
|  118  |  152  | 
|  119 void VideoTrackRecorder::VpxEncoder::StartFrameEncode( |  153 void VideoTrackRecorder::VpxEncoder::StartFrameEncode( | 
|  120     const scoped_refptr<VideoFrame>& frame, |  154     const scoped_refptr<VideoFrame>& frame, | 
|  121     base::TimeTicks capture_timestamp) { |  155     base::TimeTicks capture_timestamp) { | 
|  122   // Cache the thread sending frames on first frame arrival. |  156   // Cache the thread sending frames on first frame arrival. | 
|  123   if (!origin_task_runner_.get()) |  157   if (!origin_task_runner_.get()) | 
|  124     origin_task_runner_ = base::MessageLoop::current()->task_runner(); |  158     origin_task_runner_ = base::MessageLoop::current()->task_runner(); | 
|  125   DCHECK(origin_task_runner_->BelongsToCurrentThread()); |  159   DCHECK(origin_task_runner_->BelongsToCurrentThread()); | 
|  126  |  160  | 
|  127   encoding_thread_.task_runner()->PostTask( |  161   encoding_thread_->task_runner()->PostTask( | 
|  128       FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, |  162       FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, | 
|  129                             base::Unretained(this), frame, capture_timestamp)); |  163                             this, frame, capture_timestamp)); | 
|  130 } |  164 } | 
|  131  |  165  | 
|  132 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( |  166 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( | 
|  133     const scoped_refptr<VideoFrame>& frame, |  167     const scoped_refptr<VideoFrame>& frame, | 
|  134     base::TimeTicks capture_timestamp) { |  168     base::TimeTicks capture_timestamp) { | 
|  135   TRACE_EVENT0("video", |  169   TRACE_EVENT0("video", | 
|  136                "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); |  170                "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); | 
|  137   DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); |  171   DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 
|  138  |  172  | 
|  139   const gfx::Size frame_size = frame->visible_rect().size(); |  173   const gfx::Size frame_size = frame->visible_rect().size(); | 
|  140   if (!IsInitialized() || |  174   if (!IsInitialized() || | 
|  141       gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) { |  175       gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) { | 
|  142     ConfigureVp8Encoding(frame_size); |  176     ConfigureVp8Encoding(frame_size); | 
|  143   } |  177   } | 
|  144  |  178  | 
|  145   vpx_image_t vpx_image; |  179   vpx_image_t vpx_image; | 
|  146   vpx_image_t* const result = vpx_img_wrap(&vpx_image, |  180   vpx_image_t* const result = vpx_img_wrap(&vpx_image, | 
|  147                                            VPX_IMG_FMT_I420, |  181                                            VPX_IMG_FMT_I420, | 
|  148                                            frame_size.width(), |  182                                            frame_size.width(), | 
|  149                                            frame_size.height(), |  183                                            frame_size.height(), | 
|  150                                            1  /* align */, |  184                                            1  /* align */, | 
|  151                                            frame->data(VideoFrame::kYPlane)); |  185                                            frame->data(VideoFrame::kYPlane)); | 
|  152   DCHECK_EQ(result, &vpx_image); |  186   DCHECK_EQ(result, &vpx_image); | 
|  153   vpx_image.planes[VPX_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane); |  187   vpx_image.planes[VPX_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane); | 
|  154   vpx_image.planes[VPX_PLANE_U] = frame->visible_data(VideoFrame::kUPlane); |  188   vpx_image.planes[VPX_PLANE_U] = frame->visible_data(VideoFrame::kUPlane); | 
|  155   vpx_image.planes[VPX_PLANE_V] = frame->visible_data(VideoFrame::kVPlane); |  189   vpx_image.planes[VPX_PLANE_V] = frame->visible_data(VideoFrame::kVPlane); | 
|  156   vpx_image.stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane); |  190   vpx_image.stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane); | 
|  157   vpx_image.stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane); |  191   vpx_image.stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane); | 
|  158   vpx_image.stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane); |  192   vpx_image.stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane); | 
|  159  |  193  | 
|  160   const base::TimeDelta duration = CalculateFrameDuration(frame); |  194   const base::TimeDelta duration = CalculateFrameDuration(frame); | 
|  161   // Encode the frame.  The presentation time stamp argument here is fixed to |  195   // Encode the frame.  The presentation time stamp argument here is fixed to | 
|  162   // zero to force the encoder to base its single-frame bandwidth calculations |  196   // zero to force the encoder to base its single-frame bandwidth calculations | 
|  163   // entirely on |predicted_frame_duration|. |  197   // entirely on |predicted_frame_duration|. | 
|  164   const vpx_codec_err_t ret = vpx_codec_encode(&encoder_, |  198   const vpx_codec_err_t ret = vpx_codec_encode(encoder_.get(), | 
|  165                                                &vpx_image, |  199                                                &vpx_image, | 
|  166                                                0  /* pts */, |  200                                                0  /* pts */, | 
|  167                                                duration.InMicroseconds(), |  201                                                duration.InMicroseconds(), | 
|  168                                                kNoFlags, |  202                                                kNoFlags, | 
|  169                                                VPX_DL_REALTIME); |  203                                                VPX_DL_REALTIME); | 
|  170   DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #" |  204   DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #" | 
|  171                                << vpx_codec_error(&encoder_) << " -" |  205                                << vpx_codec_error(encoder_.get()) << " -" | 
|  172                                << vpx_codec_error_detail(&encoder_); |  206                                << vpx_codec_error_detail(encoder_.get()); | 
|  173  |  207  | 
|  174   scoped_ptr<std::string> data(new std::string); |  208   scoped_ptr<std::string> data(new std::string); | 
|  175   bool keyframe = false; |  209   bool keyframe = false; | 
|  176   vpx_codec_iter_t iter = NULL; |  210   vpx_codec_iter_t iter = NULL; | 
|  177   const vpx_codec_cx_pkt_t* pkt = NULL; |  211   const vpx_codec_cx_pkt_t* pkt = NULL; | 
|  178   while ((pkt = vpx_codec_get_cx_data(&encoder_, &iter)) != NULL) { |  212   while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) { | 
|  179     if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) |  213     if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) | 
|  180       continue; |  214       continue; | 
|  181     data->assign(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz); |  215     data->assign(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz); | 
|  182     keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; |  216     keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; | 
|  183     break; |  217     break; | 
|  184   } |  218   } | 
|  185   origin_task_runner_->PostTask(FROM_HERE, |  219   origin_task_runner_->PostTask(FROM_HERE, | 
|  186       base::Bind(&OnFrameEncodeCompleted, |  220       base::Bind(OnFrameEncodeCompleted, | 
|  187                  on_encoded_video_callback_, |  221                  on_encoded_video_callback_, | 
|  188                  frame, |  222                  frame, | 
|  189                  base::Passed(&data), |  223                  base::Passed(&data), | 
|  190                  capture_timestamp, |  224                  capture_timestamp, | 
|  191                  keyframe)); |  225                  keyframe)); | 
|  192 } |  226 } | 
|  193  |  227  | 
|  194 void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding( |  228 void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding( | 
|  195     const gfx::Size& size) { |  229     const gfx::Size& size) { | 
|  196   if (IsInitialized()) { |  230   if (IsInitialized()) { | 
|  197     // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- |  231     // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- | 
|  198     // than-or-equal than the old size, in terms of area, the existing encoder |  232     // than-or-equal than the old size, in terms of area, the existing encoder | 
|  199     // instance could be reused after changing |codec_config_.{g_w,g_h}|. |  233     // instance could be reused after changing |codec_config_.{g_w,g_h}|. | 
|  200     DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " |  234     DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " | 
|  201              << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() |  235              << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() | 
|  202              << " --> " << size.ToString(); |  236              << " --> " << size.ToString(); | 
|  203     vpx_codec_destroy(&encoder_); |  237     encoder_.reset(); | 
|  204   } |  238   } | 
|  205  |  239  | 
|  206   const vpx_codec_iface_t* interface = vpx_codec_vp8_cx(); |  240   const vpx_codec_iface_t* interface = vpx_codec_vp8_cx(); | 
|  207   const vpx_codec_err_t result = vpx_codec_enc_config_default(interface, |  241   const vpx_codec_err_t result = vpx_codec_enc_config_default(interface, | 
|  208                                                               &codec_config_, |  242                                                               &codec_config_, | 
|  209                                                               0 /* reserved */); |  243                                                               0 /* reserved */); | 
|  210   DCHECK_EQ(VPX_CODEC_OK, result); |  244   DCHECK_EQ(VPX_CODEC_OK, result); | 
|  211  |  245  | 
|  212   // Adjust default bit rate to account for the actual size. |  246   // Adjust default bit rate to account for the actual size. | 
|  213   DCHECK_EQ(320u, codec_config_.g_w); |  247   DCHECK_EQ(320u, codec_config_.g_w); | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
|  239  |  273  | 
|  240   // Do not saturate CPU utilization just for encoding. On a lower-end system |  274   // Do not saturate CPU utilization just for encoding. On a lower-end system | 
|  241   // with only 1 or 2 cores, use only one thread for encoding. On systems with |  275   // with only 1 or 2 cores, use only one thread for encoding. On systems with | 
|  242   // more cores, allow half of the cores to be used for encoding. |  276   // more cores, allow half of the cores to be used for encoding. | 
|  243   codec_config_.g_threads = |  277   codec_config_.g_threads = | 
|  244       std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); |  278       std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); | 
|  245  |  279  | 
|  246   // Number of frames to consume before producing output. |  280   // Number of frames to consume before producing output. | 
|  247   codec_config_.g_lag_in_frames = 0; |  281   codec_config_.g_lag_in_frames = 0; | 
|  248  |  282  | 
|  249   const vpx_codec_err_t ret = vpx_codec_enc_init(&encoder_, interface, |  283   DCHECK(!encoder_); | 
 |  284   encoder_.reset(new vpx_codec_ctx_t); | 
 |  285   const vpx_codec_err_t ret = vpx_codec_enc_init(encoder_.get(), interface, | 
|  250                                                  &codec_config_, kNoFlags); |  286                                                  &codec_config_, kNoFlags); | 
|  251   DCHECK_EQ(VPX_CODEC_OK, ret); |  287   DCHECK_EQ(VPX_CODEC_OK, ret); | 
|  252 } |  288 } | 
|  253  |  289  | 
|  254 bool VideoTrackRecorder::VpxEncoder::IsInitialized() const { |  290 bool VideoTrackRecorder::VpxEncoder::IsInitialized() const { | 
|  255   DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); |  291   DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 
|  256   return codec_config_.g_timebase.den != 0; |  292   return codec_config_.g_timebase.den != 0; | 
|  257 } |  293 } | 
|  258  |  294  | 
|  259 base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration( |  295 base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration( | 
|  260     const scoped_refptr<VideoFrame>& frame) { |  296     const scoped_refptr<VideoFrame>& frame) { | 
|  261   DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); |  297   DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 
|  262  |  298  | 
|  263   using base::TimeDelta; |  299   using base::TimeDelta; | 
|  264   TimeDelta predicted_frame_duration; |  300   TimeDelta predicted_frame_duration; | 
|  265   if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, |  301   if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, | 
|  266                                        &predicted_frame_duration) || |  302                                        &predicted_frame_duration) || | 
|  267       predicted_frame_duration <= TimeDelta()) { |  303       predicted_frame_duration <= TimeDelta()) { | 
|  268     // The source of the video frame did not provide the frame duration.  Use |  304     // The source of the video frame did not provide the frame duration.  Use | 
|  269     // the actual amount of time between the current and previous frame as a |  305     // the actual amount of time between the current and previous frame as a | 
|  270     // prediction for the next frame's duration. |  306     // prediction for the next frame's duration. | 
|  271     // TODO(mcasas): This duration estimation could lead to artifacts if the |  307     // TODO(mcasas): This duration estimation could lead to artifacts if the | 
|  272     // cadence of the received stream is compromised (e.g. camera freeze, pause, |  308     // cadence of the received stream is compromised (e.g. camera freeze, pause, | 
|  273     // remote packet loss).  Investigate using GetFrameRate() in this case. |  309     // remote packet loss).  Investigate using GetFrameRate() in this case. | 
|  274     predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; |  310     predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; | 
|  275   } |  311   } | 
|  276   last_frame_timestamp_ = frame->timestamp(); |  312   last_frame_timestamp_ = frame->timestamp(); | 
|  277   // Make sure |predicted_frame_duration| is in a safe range of values. |  313   // Make sure |predicted_frame_duration| is in a safe range of values. | 
|  278   const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); |  314   const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); | 
|  279   const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); |  315   const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); | 
|  280   return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, |  316   return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, | 
|  281                                               kMinFrameDuration)); |  317                                               kMinFrameDuration)); | 
|  282 } |  318 } | 
|  283  |  319  | 
|  284 VideoTrackRecorder::VideoTrackRecorder( |  320 VideoTrackRecorder::VideoTrackRecorder( | 
|  285     const blink::WebMediaStreamTrack& track, |  321     const blink::WebMediaStreamTrack& track, | 
|  286     const OnEncodedVideoCB& on_encoded_video_callback) |  322     const OnEncodedVideoCB& on_encoded_video_callback) | 
|  287     : track_(track), |  323     : track_(track), | 
|  288       encoder_(new VpxEncoder(on_encoded_video_callback)), |  324       encoder_(new VpxEncoder(on_encoded_video_callback)) { | 
|  289       weak_factory_(this) { |  | 
|  290   DCHECK(main_render_thread_checker_.CalledOnValidThread()); |  325   DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 
|  291   DCHECK(!track_.isNull()); |  326   DCHECK(!track_.isNull()); | 
|  292   DCHECK(track.extraData()); |  327   DCHECK(track.extraData()); | 
 |  328  | 
 |  329   // StartFrameEncode() will be called on Render IO thread. | 
|  293   AddToVideoTrack(this, |  330   AddToVideoTrack(this, | 
|  294                   media::BindToCurrentLoop( |  331                   base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode, | 
|  295                       base::Bind(&VideoTrackRecorder::OnVideoFrame, |  332                              encoder_), | 
|  296                                  weak_factory_.GetWeakPtr())), |  | 
|  297                   track_); |  333                   track_); | 
|  298 } |  334 } | 
|  299  |  335  | 
|  300 VideoTrackRecorder::~VideoTrackRecorder() { |  336 VideoTrackRecorder::~VideoTrackRecorder() { | 
|  301   DCHECK(main_render_thread_checker_.CalledOnValidThread()); |  337   DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 
|  302   RemoveFromVideoTrack(this, track_); |  338   RemoveFromVideoTrack(this, track_); | 
|  303   weak_factory_.InvalidateWeakPtrs(); |  | 
|  304   track_.reset(); |  339   track_.reset(); | 
|  305 } |  340 } | 
|  306  |  341  | 
|  307 void VideoTrackRecorder::OnVideoFrame(const scoped_refptr<VideoFrame>& frame, |  342 void VideoTrackRecorder::OnVideoFrameForTesting( | 
|  308                                       base::TimeTicks timestamp) { |  343     const scoped_refptr<media::VideoFrame>& frame, | 
|  309   DCHECK(main_render_thread_checker_.CalledOnValidThread()); |  344     base::TimeTicks timestamp) { | 
|  310   encoder_->StartFrameEncode(frame, timestamp); |  345   encoder_->StartFrameEncode(frame, timestamp); | 
|  311 } |  346 } | 
|  312  |  347  | 
|  313 }  // namespace content |  348 }  // namespace content | 
| OLD | NEW |