| Index: content/common/gpu/media/dxva_video_decode_accelerator_win.cc
 | 
| diff --git a/content/common/gpu/media/dxva_video_decode_accelerator_win.cc b/content/common/gpu/media/dxva_video_decode_accelerator_win.cc
 | 
| index f127bb1c4390b3ff62b313b81a2824096f1182be..59efd342b97fdcf74d51a3b69552f383dbc67c09 100644
 | 
| --- a/content/common/gpu/media/dxva_video_decode_accelerator_win.cc
 | 
| +++ b/content/common/gpu/media/dxva_video_decode_accelerator_win.cc
 | 
| @@ -34,6 +34,7 @@
 | 
|  #include "base/win/windows_version.h"
 | 
|  #include "build/build_config.h"
 | 
|  #include "media/base/win/mf_initializer.h"
 | 
| +#include "media/filters/h264_parser.h"
 | 
|  #include "media/video/video_decode_accelerator.h"
 | 
|  #include "third_party/angle/include/EGL/egl.h"
 | 
|  #include "third_party/angle/include/EGL/eglext.h"
 | 
| @@ -111,6 +112,41 @@ DEFINE_GUID(CLSID_VideoProcessorMFT,
 | 
|  DEFINE_GUID(MF_XVP_PLAYBACK_MODE, 0x3c5d293f, 0xad67, 0x4e29, 0xaf, 0x12,
 | 
|              0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9);
 | 
|  
 | 
| +// Provides scoped access to the underlying buffer in an IMFMediaBuffer
 | 
| +// instance.
 | 
| +class MediaBufferScopedPointer {
 | 
| + public:
 | 
| +  MediaBufferScopedPointer(IMFMediaBuffer* media_buffer)
 | 
| +      : media_buffer_(media_buffer),
 | 
| +        buffer_(nullptr),
 | 
| +        max_length_(0),
 | 
| +        current_length_(0) {
 | 
| +    HRESULT hr = media_buffer_->Lock(&buffer_, &max_length_, ¤t_length_);
 | 
| +    CHECK(SUCCEEDED(hr));
 | 
| +  }
 | 
| +
 | 
| +  ~MediaBufferScopedPointer() {
 | 
| +    HRESULT hr = media_buffer_->Unlock();
 | 
| +    CHECK(SUCCEEDED(hr));
 | 
| +  }
 | 
| +
 | 
| +  uint8_t* get() {
 | 
| +    return buffer_;
 | 
| +  }
 | 
| +
 | 
| +  DWORD current_length() const {
 | 
| +    return current_length_;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  base::win::ScopedComPtr<IMFMediaBuffer> media_buffer_;
 | 
| +  uint8_t* buffer_;
 | 
| +  DWORD max_length_;
 | 
| +  DWORD current_length_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(MediaBufferScopedPointer);
 | 
| +};
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  namespace content {
 | 
| @@ -331,6 +367,112 @@ base::win::ScopedComPtr<T> QueryDeviceObjectFromANGLE(int object_type) {
 | 
|    return device_object;
 | 
|  }
 | 
|  
 | 
| +H264ConfigChangeDetector::H264ConfigChangeDetector()
 | 
| +    : last_sps_id_(0),
 | 
| +      last_pps_id_(0),
 | 
| +      config_changed_(false),
 | 
| +      pending_config_changed_(false) {
 | 
| +}
 | 
| +
 | 
| +H264ConfigChangeDetector::~H264ConfigChangeDetector() {
 | 
| +}
 | 
| +
 | 
| +bool H264ConfigChangeDetector::DetectConfig(const uint8_t* stream,
 | 
| +                                            unsigned int size) {
 | 
| +  std::vector<uint8_t> sps;
 | 
| +  std::vector<uint8_t> pps;
 | 
| +  media::H264NALU nalu;
 | 
| +  bool idr_seen = false;
 | 
| +
 | 
| +  media::H264Parser parser;
 | 
| +  parser.SetStream(stream, size);
 | 
| +  config_changed_ = false;
 | 
| +
 | 
| +  while (true) {
 | 
| +    media::H264Parser::Result result = parser.AdvanceToNextNALU(&nalu);
 | 
| +
 | 
| +    if (result == media::H264Parser::kEOStream)
 | 
| +      break;
 | 
| +
 | 
| +    if (result == media::H264Parser::kUnsupportedStream) {
 | 
| +      DLOG(ERROR) << "Unsupported H.264 stream";
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    if (result != media::H264Parser::kOk) {
 | 
| +      DLOG(ERROR) << "Failed to parse H.264 stream";
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    switch (nalu.nal_unit_type) {
 | 
| +      case media::H264NALU::kSPS:
 | 
| +        result = parser.ParseSPS(&last_sps_id_);
 | 
| +        if (result == media::H264Parser::kUnsupportedStream) {
 | 
| +          DLOG(ERROR) << "Unsupported SPS";
 | 
| +          return false;
 | 
| +        }
 | 
| +
 | 
| +        if (result != media::H264Parser::kOk) {
 | 
| +          DLOG(ERROR) << "Could not parse SPS";
 | 
| +          return false;
 | 
| +        }
 | 
| +
 | 
| +        sps.assign(nalu.data, nalu.data + nalu.size);
 | 
| +        break;
 | 
| +
 | 
| +      case media::H264NALU::kPPS:
 | 
| +        result = parser.ParsePPS(&last_pps_id_);
 | 
| +        if (result == media::H264Parser::kUnsupportedStream) {
 | 
| +          DLOG(ERROR) << "Unsupported PPS";
 | 
| +          return false;
 | 
| +        }
 | 
| +        if (result != media::H264Parser::kOk) {
 | 
| +          DLOG(ERROR) << "Could not parse PPS";
 | 
| +          return false;
 | 
| +        }
 | 
| +        pps.assign(nalu.data, nalu.data + nalu.size);
 | 
| +        break;
 | 
| +
 | 
| +      case media::H264NALU::kIDRSlice:
 | 
| +        idr_seen = true;
 | 
| +        // If we previously detected a configuration change, and see an IDR
 | 
| +        // slice next time around, we need to flag a configuration change.
 | 
| +        if (pending_config_changed_) {
 | 
| +          config_changed_ = true;
 | 
| +          pending_config_changed_ = false;
 | 
| +        }
 | 
| +        break;
 | 
| +
 | 
| +      default:
 | 
| +        break;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  if (!sps.empty() && sps != last_sps_) {
 | 
| +    if (!last_sps_.empty()) {
 | 
| +      // Flag configuration changes after we see an IDR slice.
 | 
| +      if (idr_seen) {
 | 
| +        config_changed_ = true;
 | 
| +      } else {
 | 
| +        pending_config_changed_ = true;
 | 
| +      }
 | 
| +    }
 | 
| +    last_sps_.swap(sps);
 | 
| +  }
 | 
| +
 | 
| +  if (!pps.empty() && pps != last_pps_) {
 | 
| +    if (!last_pps_.empty()) {
 | 
| +      // Flag configuration changes after we see an IDR slice.
 | 
| +      if (idr_seen) {
 | 
| +        config_changed_ = true;
 | 
| +      } else {
 | 
| +        pending_config_changed_ = true;
 | 
| +      }
 | 
| +    }
 | 
| +    last_pps_.swap(pps);
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
|  
 | 
|  // Maintains information about a DXVA picture buffer, i.e. whether it is
 | 
|  // available for rendering, the texture information, etc.
 | 
| @@ -788,6 +930,8 @@ bool DXVAVideoDecodeAccelerator::Initialize(const Config& config,
 | 
|        "Send MFT_MESSAGE_NOTIFY_START_OF_STREAM notification failed",
 | 
|        PLATFORM_FAILURE, false);
 | 
|  
 | 
| +  config_ = config;
 | 
| +
 | 
|    SetState(kNormal);
 | 
|  
 | 
|    StartDecoderThread();
 | 
| @@ -1040,13 +1184,15 @@ void DXVAVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_buffer_id) {
 | 
|    // us that we can now recycle this picture buffer, so if we were waiting to
 | 
|    // dispose of it we now can.
 | 
|    if (it == output_picture_buffers_.end()) {
 | 
| -    it = stale_output_picture_buffers_.find(picture_buffer_id);
 | 
| -    RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(),
 | 
| -        "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,);
 | 
| -    main_thread_task_runner_->PostTask(
 | 
| -        FROM_HERE,
 | 
| -        base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer,
 | 
| -                   weak_this_factory_.GetWeakPtr(), picture_buffer_id));
 | 
| +    if (!stale_output_picture_buffers_.empty()) {
 | 
| +      it = stale_output_picture_buffers_.find(picture_buffer_id);
 | 
| +      RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(),
 | 
| +          "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,);
 | 
| +      main_thread_task_runner_->PostTask(
 | 
| +          FROM_HERE,
 | 
| +          base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer,
 | 
| +                     weak_this_factory_.GetWeakPtr(), picture_buffer_id));
 | 
| +    }
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| @@ -1679,6 +1825,7 @@ void DXVAVideoDecodeAccelerator::Invalidate() {
 | 
|    pending_output_samples_.clear();
 | 
|    pending_input_buffers_.clear();
 | 
|    decoder_.Release();
 | 
| +  pictures_requested_ = false;
 | 
|  
 | 
|    if (use_dx11_) {
 | 
|      if (video_format_converter_mft_.get()) {
 | 
| @@ -1691,6 +1838,7 @@ void DXVAVideoDecodeAccelerator::Invalidate() {
 | 
|      d3d11_device_manager_.Release();
 | 
|      d3d11_query_.Release();
 | 
|      dx11_video_format_converter_media_type_needs_init_ = true;
 | 
| +    multi_threaded_.Release();
 | 
|    } else {
 | 
|      d3d9_.Release();
 | 
|      d3d9_device_ex_.Release();
 | 
| @@ -1845,13 +1993,31 @@ void DXVAVideoDecodeAccelerator::DecodeInternal(
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| +  // Check if the resolution, bit rate, etc changed in the stream. If yes we
 | 
| +  // reinitialize the decoder to ensure that the stream decodes correctly.
 | 
| +  bool config_changed = false;
 | 
| +
 | 
| +  HRESULT hr = CheckConfigChanged(sample.get(), &config_changed);
 | 
| +  RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to check video stream config",
 | 
| +      PLATFORM_FAILURE,);
 | 
| +
 | 
| +  if (config_changed) {
 | 
| +    main_thread_task_runner_->PostTask(
 | 
| +        FROM_HERE,
 | 
| +        base::Bind(&DXVAVideoDecodeAccelerator::ConfigChanged,
 | 
| +                   weak_this_factory_.GetWeakPtr(),
 | 
| +                   config_,
 | 
| +                   sample));
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
|    if (!inputs_before_decode_) {
 | 
|      TRACE_EVENT_ASYNC_BEGIN0("gpu", "DXVAVideoDecodeAccelerator.Decoding",
 | 
|                               this);
 | 
|    }
 | 
|    inputs_before_decode_++;
 | 
|  
 | 
| -  HRESULT hr = decoder_->ProcessInput(0, sample.get(), 0);
 | 
| +  hr = decoder_->ProcessInput(0, sample.get(), 0);
 | 
|    // As per msdn if the decoder returns MF_E_NOTACCEPTING then it means that it
 | 
|    // has enough data to produce one or more output samples. In this case the
 | 
|    // recommended options are to
 | 
| @@ -1929,7 +2095,7 @@ void DXVAVideoDecodeAccelerator::HandleResolutionChanged(int width,
 | 
|    main_thread_task_runner_->PostTask(
 | 
|        FROM_HERE,
 | 
|        base::Bind(&DXVAVideoDecodeAccelerator::DismissStaleBuffers,
 | 
| -                 weak_this_factory_.GetWeakPtr()));
 | 
| +                 weak_this_factory_.GetWeakPtr(), false));
 | 
|  
 | 
|    main_thread_task_runner_->PostTask(
 | 
|        FROM_HERE,
 | 
| @@ -1939,13 +2105,13 @@ void DXVAVideoDecodeAccelerator::HandleResolutionChanged(int width,
 | 
|                   height));
 | 
|  }
 | 
|  
 | 
| -void DXVAVideoDecodeAccelerator::DismissStaleBuffers() {
 | 
| +void DXVAVideoDecodeAccelerator::DismissStaleBuffers(bool force) {
 | 
|    OutputBuffers::iterator index;
 | 
|  
 | 
|    for (index = output_picture_buffers_.begin();
 | 
|         index != output_picture_buffers_.end();
 | 
|         ++index) {
 | 
| -    if (index->second->available()) {
 | 
| +    if (force || index->second->available()) {
 | 
|        DVLOG(1) << "Dismissing picture id: " << index->second->id();
 | 
|        client_->DismissPictureBuffer(index->second->id());
 | 
|      } else {
 | 
| @@ -2454,4 +2620,38 @@ bool DXVAVideoDecodeAccelerator::SetTransformOutputType(
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
| +HRESULT DXVAVideoDecodeAccelerator::CheckConfigChanged(
 | 
| +    IMFSample* sample, bool* config_changed) {
 | 
| +  if (codec_ != media::kCodecH264)
 | 
| +    return S_FALSE;
 | 
| +
 | 
| +  base::win::ScopedComPtr<IMFMediaBuffer> buffer;
 | 
| +  HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive());
 | 
| +  RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from input sample", hr);
 | 
| +
 | 
| +  MediaBufferScopedPointer scoped_media_buffer(buffer.get());
 | 
| +
 | 
| +  if (!config_change_detector_.DetectConfig(
 | 
| +          scoped_media_buffer.get(),
 | 
| +          scoped_media_buffer.current_length())) {
 | 
| +    RETURN_ON_HR_FAILURE(E_FAIL, "Failed to detect H.264 stream config",
 | 
| +        E_FAIL);
 | 
| +  }
 | 
| +  *config_changed = config_change_detector_.config_changed();
 | 
| +  return S_OK;
 | 
| +}
 | 
| +
 | 
| +void DXVAVideoDecodeAccelerator::ConfigChanged(
 | 
| +    const Config& config,
 | 
| +    const base::win::ScopedComPtr<IMFSample>& input_sample) {
 | 
| +  DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
 | 
| +  DismissStaleBuffers(true);
 | 
| +  Invalidate();
 | 
| +  Initialize(config_, client_);
 | 
| +  decoder_thread_task_runner_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&DXVAVideoDecodeAccelerator::DecodeInternal,
 | 
| +                 base::Unretained(this), input_sample));
 | 
| +}
 | 
| +
 | 
|  }  // namespace content
 | 
| 
 |