| Index: media/gpu/d3d11_h264_accelerator.cc
|
| diff --git a/media/gpu/d3d11_h264_accelerator.cc b/media/gpu/d3d11_h264_accelerator.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..49aaab34bd14785e1958384918cffb1bb8ed7288
|
| --- /dev/null
|
| +++ b/media/gpu/d3d11_h264_accelerator.cc
|
| @@ -0,0 +1,455 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "media/gpu/d3d11_h264_accelerator.h"
|
| +
|
| +#include <d3d11.h>
|
| +#include <dxva.h>
|
| +#include <windows.h>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "base/win/scoped_comptr.h"
|
| +#include "media/gpu/h264_decoder.h"
|
| +#include "media/gpu/h264_dpb.h"
|
| +#include "third_party/angle/include/EGL/egl.h"
|
| +#include "third_party/angle/include/EGL/eglext.h"
|
| +#include "ui/gfx/color_space.h"
|
| +#include "ui/gl/gl_bindings.h"
|
| +#include "ui/gl/gl_context.h"
|
| +#include "ui/gl/gl_surface_egl.h"
|
| +#include "ui/gl/scoped_binders.h"
|
| +
|
| +namespace media {
|
| +
|
| +#define RETURN_ON_FAILURE(result, log, ret) \
|
| + do { \
|
| + if (!(result)) { \
|
| + DLOG(ERROR) << log; \
|
| + return ret; \
|
| + } \
|
| + } while (0)
|
| +
|
| +D3D11PictureBuffer::D3D11PictureBuffer(PictureBuffer picture_buffer,
|
| + size_t level)
|
| + : picture_buffer_(picture_buffer), level_(level) {}
|
| +
|
| +D3D11PictureBuffer::~D3D11PictureBuffer() {}
|
| +
|
| +bool D3D11PictureBuffer::Init(
|
| + base::win::ScopedComPtr<ID3D11VideoDevice> video_device,
|
| + base::win::ScopedComPtr<ID3D11Texture2D> texture,
|
| + const GUID& decoder_guid) {
|
| + texture_ = texture;
|
| + D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {};
|
| + view_desc.DecodeProfile = decoder_guid;
|
| + view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
|
| + view_desc.Texture2D.ArraySlice = (UINT)level_;
|
| +
|
| + HRESULT hr = video_device->CreateVideoDecoderOutputView(
|
| + texture.get(), &view_desc, output_view_.Receive());
|
| +
|
| + CHECK(SUCCEEDED(hr));
|
| + EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
|
| + const EGLint stream_attributes[] = {
|
| + EGL_CONSUMER_LATENCY_USEC_KHR,
|
| + 0,
|
| + EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR,
|
| + 0,
|
| + EGL_NONE,
|
| + };
|
| + stream_ = eglCreateStreamKHR(egl_display, stream_attributes);
|
| + RETURN_ON_FAILURE(!!stream_, "Could not create stream", false);
|
| + gl::ScopedActiveTexture texture0(GL_TEXTURE0);
|
| + gl::ScopedTextureBinder texture0_binder(
|
| + GL_TEXTURE_EXTERNAL_OES, picture_buffer_.service_texture_ids()[0]);
|
| + gl::ScopedActiveTexture texture1(GL_TEXTURE1);
|
| + gl::ScopedTextureBinder texture1_binder(
|
| + GL_TEXTURE_EXTERNAL_OES, picture_buffer_.service_texture_ids()[1]);
|
| +
|
| + EGLAttrib consumer_attributes[] = {
|
| + EGL_COLOR_BUFFER_TYPE,
|
| + EGL_YUV_BUFFER_EXT,
|
| + EGL_YUV_NUMBER_OF_PLANES_EXT,
|
| + 2,
|
| + EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
|
| + 0,
|
| + EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
|
| + 1,
|
| + EGL_NONE,
|
| + };
|
| + EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
|
| + egl_display, stream_, consumer_attributes);
|
| + RETURN_ON_FAILURE(result, "Could not set stream consumer", false);
|
| +
|
| + EGLAttrib producer_attributes[] = {
|
| + EGL_NONE,
|
| + };
|
| +
|
| + result = eglCreateStreamProducerD3DTextureNV12ANGLE(egl_display, stream_,
|
| + producer_attributes);
|
| +
|
| + EGLAttrib frame_attributes[] = {
|
| + EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, level_, EGL_NONE,
|
| + };
|
| +
|
| + result = eglStreamPostD3DTextureNV12ANGLE(egl_display, stream_,
|
| + static_cast<void*>(texture.get()),
|
| + frame_attributes);
|
| + RETURN_ON_FAILURE(result, "Could not post texture", false);
|
| + result = eglStreamConsumerAcquireKHR(egl_display, stream_);
|
| + RETURN_ON_FAILURE(result, "Could not post acquire stream", false);
|
| + return true;
|
| +}
|
| +
|
| +class D3D11H264Picture : public H264Picture {
|
| + public:
|
| + D3D11H264Picture(D3D11PictureBuffer* picture, size_t input_buffer_id)
|
| + : picture(picture),
|
| + level_(picture->level()),
|
| + input_buffer_id_(input_buffer_id) {}
|
| +
|
| + D3D11PictureBuffer* picture;
|
| + size_t level_;
|
| + size_t input_buffer_id_;
|
| +
|
| + protected:
|
| + ~D3D11H264Picture() override;
|
| +};
|
| +
|
| +D3D11H264Accelerator::D3D11H264Accelerator(
|
| + D3D11VideoDecoderClient* client,
|
| + base::win::ScopedComPtr<ID3D11VideoDecoder> video_decoder,
|
| + base::win::ScopedComPtr<ID3D11VideoDevice> video_device,
|
| + base::win::ScopedComPtr<ID3D11VideoContext> video_context)
|
| + : client_(client),
|
| + video_decoder_(video_decoder),
|
| + video_device_(video_device),
|
| + video_context_(video_context) {}
|
| +
|
| +D3D11H264Accelerator::~D3D11H264Accelerator() {}
|
| +
|
| +scoped_refptr<H264Picture> D3D11H264Accelerator::CreateH264Picture() {
|
| + D3D11PictureBuffer* picture = client_->GetPicture();
|
| + if (!picture) {
|
| + return nullptr;
|
| + }
|
| + picture->set_in_picture_use(true);
|
| + return make_scoped_refptr(
|
| + new D3D11H264Picture(picture, client_->input_buffer_id()));
|
| +}
|
| +
|
| +bool D3D11H264Accelerator::SubmitFrameMetadata(
|
| + const H264SPS* sps,
|
| + const H264PPS* pps,
|
| + const H264DPB& dpb,
|
| + const H264Picture::Vector& ref_pic_listp0,
|
| + const H264Picture::Vector& ref_pic_listb0,
|
| + const H264Picture::Vector& ref_pic_listb1,
|
| + const scoped_refptr<H264Picture>& pic) {
|
| + scoped_refptr<D3D11H264Picture> our_pic(
|
| + static_cast<D3D11H264Picture*>(pic.get()));
|
| +
|
| + HRESULT hr;
|
| + hr = video_context_->DecoderBeginFrame(
|
| + video_decoder_.get(), our_pic->picture->output_view_.get(), 0, nullptr);
|
| + CHECK(SUCCEEDED(hr));
|
| +
|
| + sps_ = *sps;
|
| + for (size_t i = 0; i < 16; i++) {
|
| + ref_frame_list_[i].bPicEntry = 0xFF;
|
| + field_order_cnt_list_[i][0] = 0;
|
| + field_order_cnt_list_[i][1] = 0;
|
| + frame_num_list_[i] = 0;
|
| + }
|
| + used_for_reference_flags_ = 0;
|
| + non_existing_frame_flags_ = 0;
|
| +
|
| + int i = 0;
|
| +
|
| + for (auto it = dpb.begin(); it != dpb.end(); it++) {
|
| + scoped_refptr<D3D11H264Picture> our_ref_pic(
|
| + static_cast<D3D11H264Picture*>(it->get()));
|
| + if (!our_ref_pic->ref) {
|
| + i++;
|
| + continue;
|
| + }
|
| + ref_frame_list_[i].Index7Bits = our_ref_pic->level_;
|
| + ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term;
|
| + field_order_cnt_list_[i][0] = our_ref_pic->top_field_order_cnt;
|
| + field_order_cnt_list_[i][1] = our_ref_pic->bottom_field_order_cnt;
|
| + frame_num_list_[i] = ref_frame_list_[i].AssociatedFlag
|
| + ? our_ref_pic->long_term_pic_num
|
| + : our_ref_pic->pic_num;
|
| + int ref = 3;
|
| + used_for_reference_flags_ |= ref << (2 * i);
|
| + non_existing_frame_flags_ |= (our_ref_pic->nonexisting) << i;
|
| + i++;
|
| + }
|
| + slice_info_.clear();
|
| + RetrieveBitstreamBuffer();
|
| + return true;
|
| +}
|
| +
|
| +void D3D11H264Accelerator::RetrieveBitstreamBuffer() {
|
| + current_offset_ = 0;
|
| + void* buffer;
|
| + UINT buffer_size;
|
| + HRESULT hr = video_context_->GetDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_BITSTREAM, &buffer_size,
|
| + &buffer);
|
| + bitstream_buffer_bytes_ = (uint8_t*)buffer;
|
| + bitstream_buffer_size_ = buffer_size;
|
| + CHECK(SUCCEEDED(hr));
|
| +}
|
| +
|
| +bool D3D11H264Accelerator::SubmitSlice(const H264PPS* pps,
|
| + const H264SliceHeader* slice_hdr,
|
| + const H264Picture::Vector& ref_pic_list0,
|
| + const H264Picture::Vector& ref_pic_list1,
|
| + const scoped_refptr<H264Picture>& pic,
|
| + const uint8_t* data,
|
| + size_t size) {
|
| + scoped_refptr<D3D11H264Picture> our_pic(
|
| + static_cast<D3D11H264Picture*>(pic.get()));
|
| +
|
| + DXVA_PicParams_H264 pic_param = {};
|
| +
|
| +#define FROM_SPS_TO_PP(a) pic_param.a = sps_.a
|
| +#define FROM_SPS_TO_PP2(a, b) pic_param.a = sps_.b
|
| +#define FROM_PPS_TO_PP(a) pic_param.a = pps->a
|
| +#define FROM_PPS_TO_PP2(a, b) pic_param.a = pps->b
|
| +#define FROM_SLICE_TO_PP(a) pic_param.a = slice_hdr->a
|
| +#define FROM_SLICE_TO_PP2(a, b) pic_param.a = slice_hdr->b
|
| + FROM_SPS_TO_PP2(wFrameWidthInMbsMinus1, pic_width_in_mbs_minus1);
|
| + FROM_SPS_TO_PP2(wFrameHeightInMbsMinus1, pic_height_in_map_units_minus1);
|
| + pic_param.CurrPic.Index7Bits = our_pic->level_;
|
| + // UNUSED: pic_param.CurrPic.AssociatedFlag = slide_hdr->field_pic_flag
|
| + FROM_SPS_TO_PP2(num_ref_frames, max_num_ref_frames);
|
| +
|
| + FROM_SLICE_TO_PP(field_pic_flag);
|
| + pic_param.MbaffFrameFlag =
|
| + sps_.mb_adaptive_frame_field_flag && pic_param.field_pic_flag;
|
| + FROM_SPS_TO_PP2(residual_colour_transform_flag, separate_colour_plane_flag);
|
| + FROM_SLICE_TO_PP(sp_for_switch_flag);
|
| + FROM_SPS_TO_PP(chroma_format_idc);
|
| + pic_param.RefPicFlag = pic->ref;
|
| + FROM_PPS_TO_PP(constrained_intra_pred_flag);
|
| + FROM_PPS_TO_PP(weighted_pred_flag);
|
| + FROM_PPS_TO_PP(weighted_bipred_idc);
|
| + pic_param.MbsConsecutiveFlag = 1;
|
| + FROM_SPS_TO_PP(frame_mbs_only_flag);
|
| + FROM_PPS_TO_PP(transform_8x8_mode_flag);
|
| + // UNUSED: Minlumabipredsize
|
| + // UNUSED: pic_param.IntraPicFlag = slice_hdr->IsISlice();
|
| + FROM_SPS_TO_PP(bit_depth_luma_minus8);
|
| + FROM_SPS_TO_PP(bit_depth_chroma_minus8);
|
| + memcpy(pic_param.RefFrameList, ref_frame_list_,
|
| + sizeof pic_param.RefFrameList);
|
| + if (pic_param.field_pic_flag && pic_param.CurrPic.AssociatedFlag) {
|
| + pic_param.CurrFieldOrderCnt[1] = pic->bottom_field_order_cnt;
|
| + pic_param.CurrFieldOrderCnt[0] = 0;
|
| + } else if (pic_param.field_pic_flag && !pic_param.CurrPic.AssociatedFlag) {
|
| + pic_param.CurrFieldOrderCnt[0] = pic->top_field_order_cnt;
|
| + pic_param.CurrFieldOrderCnt[1] = 0;
|
| + } else {
|
| + pic_param.CurrFieldOrderCnt[0] = pic->top_field_order_cnt;
|
| + pic_param.CurrFieldOrderCnt[1] = pic->bottom_field_order_cnt;
|
| + }
|
| + memcpy(pic_param.FieldOrderCntList, field_order_cnt_list_,
|
| + sizeof pic_param.FieldOrderCntList);
|
| + FROM_PPS_TO_PP(pic_init_qs_minus26);
|
| + FROM_PPS_TO_PP(chroma_qp_index_offset);
|
| + FROM_PPS_TO_PP(second_chroma_qp_index_offset);
|
| + pic_param.ContinuationFlag = 1;
|
| + FROM_PPS_TO_PP(pic_init_qp_minus26);
|
| + FROM_PPS_TO_PP2(num_ref_idx_l0_active_minus1,
|
| + num_ref_idx_l0_default_active_minus1);
|
| + FROM_PPS_TO_PP2(num_ref_idx_l1_active_minus1,
|
| + num_ref_idx_l1_default_active_minus1);
|
| + // UNUSED: Reserved8BitsA
|
| + memcpy(pic_param.FrameNumList, frame_num_list_,
|
| + sizeof pic_param.FrameNumList);
|
| + pic_param.UsedForReferenceFlags = used_for_reference_flags_;
|
| + pic_param.NonExistingFrameFlags = non_existing_frame_flags_;
|
| + pic_param.frame_num = pic->frame_num;
|
| + FROM_SPS_TO_PP(log2_max_frame_num_minus4);
|
| + FROM_SPS_TO_PP(pic_order_cnt_type);
|
| + FROM_SPS_TO_PP(log2_max_pic_order_cnt_lsb_minus4);
|
| + FROM_SPS_TO_PP(delta_pic_order_always_zero_flag);
|
| + FROM_SPS_TO_PP(direct_8x8_inference_flag);
|
| + FROM_PPS_TO_PP(entropy_coding_mode_flag);
|
| + FROM_PPS_TO_PP2(pic_order_present_flag,
|
| + bottom_field_pic_order_in_frame_present_flag);
|
| + FROM_PPS_TO_PP(num_slice_groups_minus1);
|
| + CHECK_EQ(0u, pic_param.num_slice_groups_minus1);
|
| + // UNUSED: slice_group_map_type
|
| + FROM_PPS_TO_PP(deblocking_filter_control_present_flag);
|
| + FROM_PPS_TO_PP(redundant_pic_cnt_present_flag);
|
| + // UNUSED: Reserved8BitsB
|
| + // UNUSED: slice_group_change_rate
|
| + //
|
| + //
|
| + //
|
| +
|
| + pic_param.StatusReportFeedbackNumber = 1;
|
| +
|
| + UINT buffer_size;
|
| + void* buffer;
|
| + HRESULT hr = video_context_->GetDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS,
|
| + &buffer_size, &buffer);
|
| + CHECK(SUCCEEDED(hr));
|
| +
|
| + memcpy(buffer, &pic_param, sizeof(pic_param));
|
| + hr = video_context_->ReleaseDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS);
|
| + CHECK(SUCCEEDED(hr));
|
| +
|
| + DXVA_Qmatrix_H264 iq_matrix_buf = {};
|
| +
|
| + if (pps->pic_scaling_matrix_present_flag) {
|
| + for (int i = 0; i < 6; ++i) {
|
| + for (int j = 0; j < 16; ++j)
|
| + iq_matrix_buf.bScalingLists4x4[i][j] = pps->scaling_list4x4[i][j];
|
| + }
|
| +
|
| + for (int i = 0; i < 2; ++i) {
|
| + for (int j = 0; j < 64; ++j)
|
| + iq_matrix_buf.bScalingLists8x8[i][j] = pps->scaling_list8x8[i][j];
|
| + }
|
| + } else {
|
| + for (int i = 0; i < 6; ++i) {
|
| + for (int j = 0; j < 16; ++j)
|
| + iq_matrix_buf.bScalingLists4x4[i][j] = sps_.scaling_list4x4[i][j];
|
| + }
|
| +
|
| + for (int i = 0; i < 2; ++i) {
|
| + for (int j = 0; j < 64; ++j)
|
| + iq_matrix_buf.bScalingLists8x8[i][j] = sps_.scaling_list8x8[i][j];
|
| + }
|
| + }
|
| + hr = video_context_->GetDecoderBuffer(
|
| + video_decoder_.get(),
|
| + D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX, &buffer_size,
|
| + &buffer);
|
| + CHECK(SUCCEEDED(hr));
|
| + memcpy(buffer, &iq_matrix_buf, sizeof(iq_matrix_buf));
|
| + hr = video_context_->ReleaseDecoderBuffer(
|
| + video_decoder_.get(),
|
| + D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX);
|
| +
|
| + // Ideally all slices in a frame are put in the same bitstream buffer.
|
| + // However the bitstream buffer may not fit all the data, so split on the
|
| + // necessary boundaries.
|
| +
|
| + size_t out_bitstream_size = size + 3;
|
| +
|
| + size_t remaining_bitstream = out_bitstream_size;
|
| + size_t start_location = 0;
|
| +
|
| + while (remaining_bitstream > 0) {
|
| + if (bitstream_buffer_size_ < remaining_bitstream &&
|
| + slice_info_.size() > 0) {
|
| + SubmitSliceData();
|
| + RetrieveBitstreamBuffer();
|
| + }
|
| +
|
| + size_t bytes_to_copy = remaining_bitstream;
|
| + bool contains_end = true;
|
| + if (bytes_to_copy > bitstream_buffer_size_) {
|
| + bytes_to_copy = bitstream_buffer_size_;
|
| + contains_end = false;
|
| + }
|
| + size_t real_bytes_to_copy = bytes_to_copy;
|
| + // TODO(jbauman): fix hack
|
| + uint8_t* out_start = bitstream_buffer_bytes_;
|
| + if (bytes_to_copy >= 3 && start_location == 0) {
|
| + *(out_start++) = 0;
|
| + *(out_start++) = 0;
|
| + *(out_start++) = 1;
|
| + real_bytes_to_copy -= 3;
|
| + }
|
| + memcpy(out_start, data + start_location, real_bytes_to_copy);
|
| +
|
| + DXVA_Slice_H264_Short slice_info = {};
|
| + slice_info.BSNALunitDataLocation = (UINT)current_offset_;
|
| + slice_info.SliceBytesInBuffer = (UINT)bytes_to_copy;
|
| + if (contains_end && start_location == 0)
|
| + slice_info.wBadSliceChopping = 0;
|
| + else if (!contains_end && start_location == 0)
|
| + slice_info.wBadSliceChopping = 1;
|
| + else if (contains_end && start_location != 0)
|
| + slice_info.wBadSliceChopping = 2;
|
| + else
|
| + slice_info.wBadSliceChopping = 3;
|
| +
|
| + slice_info_.push_back(slice_info);
|
| + bitstream_buffer_size_ -= bytes_to_copy;
|
| + current_offset_ += bytes_to_copy;
|
| + start_location += bytes_to_copy;
|
| + remaining_bitstream -= bytes_to_copy;
|
| + bitstream_buffer_bytes_ += bytes_to_copy;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void D3D11H264Accelerator::SubmitSliceData() {
|
| + CHECK(slice_info_.size() > 0);
|
| + UINT buffer_size;
|
| + void* buffer;
|
| + HRESULT hr = video_context_->GetDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL,
|
| + &buffer_size, &buffer);
|
| + CHECK(SUCCEEDED(hr));
|
| + CHECK_LE(sizeof(slice_info_[0]) * slice_info_.size(), buffer_size);
|
| + memcpy(buffer, &slice_info_[0], sizeof(slice_info_[0]) * slice_info_.size());
|
| + hr = video_context_->ReleaseDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL);
|
| +
|
| + hr = video_context_->ReleaseDecoderBuffer(
|
| + video_decoder_.get(), D3D11_VIDEO_DECODER_BUFFER_BITSTREAM);
|
| + D3D11_VIDEO_DECODER_BUFFER_DESC buffers[4] = {};
|
| + buffers[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
|
| + buffers[0].DataOffset = 0;
|
| + buffers[0].DataSize = sizeof(DXVA_PicParams_H264);
|
| + buffers[1].BufferType =
|
| + D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX;
|
| + buffers[1].DataOffset = 0;
|
| + buffers[1].DataSize = sizeof(DXVA_Qmatrix_H264);
|
| + buffers[2].BufferType = D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL;
|
| + buffers[2].DataOffset = 0;
|
| + buffers[2].DataSize = (UINT)(sizeof(slice_info_[0]) * slice_info_.size());
|
| + buffers[3].BufferType = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM;
|
| + buffers[3].DataOffset = 0;
|
| + buffers[3].DataSize = (UINT)current_offset_;
|
| +
|
| + hr = video_context_->SubmitDecoderBuffers(video_decoder_.get(), 4, buffers);
|
| + current_offset_ = 0;
|
| + slice_info_.clear();
|
| +}
|
| +
|
| +bool D3D11H264Accelerator::SubmitDecode(const scoped_refptr<H264Picture>& pic) {
|
| + SubmitSliceData();
|
| +
|
| + HRESULT hr = video_context_->DecoderEndFrame(video_decoder_.get());
|
| + CHECK(SUCCEEDED(hr));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool D3D11H264Accelerator::OutputPicture(
|
| + const scoped_refptr<H264Picture>& pic) {
|
| + scoped_refptr<D3D11H264Picture> our_pic(
|
| + static_cast<D3D11H264Picture*>(pic.get()));
|
| + client_->OutputResult(our_pic->picture, our_pic->input_buffer_id_);
|
| + return true;
|
| +}
|
| +
|
| +D3D11H264Picture::~D3D11H264Picture() {
|
| + picture->set_in_picture_use(false);
|
| +}
|
| +
|
| +} // namespace media
|
|
|