Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(809)

Unified Diff: content/common/gpu/media/vt_video_encode_accelerator_mac.cc

Issue 1801343002: Revert of H264 HW encode using VideoToolbox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/common/gpu/media/vt_video_encode_accelerator_mac.h ('k') | content/content_common.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/common/gpu/media/vt_video_encode_accelerator_mac.cc
diff --git a/content/common/gpu/media/vt_video_encode_accelerator_mac.cc b/content/common/gpu/media/vt_video_encode_accelerator_mac.cc
deleted file mode 100644
index 71c80ef3a9f1bfb58b4dc8b5f97769409af42fcb..0000000000000000000000000000000000000000
--- a/content/common/gpu/media/vt_video_encode_accelerator_mac.cc
+++ /dev/null
@@ -1,552 +0,0 @@
-// 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 "content/common/gpu/media/vt_video_encode_accelerator_mac.h"
-
-#include "base/thread_task_runner_handle.h"
-#include "media/base/mac/coremedia_glue.h"
-#include "media/base/mac/corevideo_glue.h"
-#include "media/base/mac/video_frame_mac.h"
-
-namespace content {
-
-namespace {
-
-// TODO(emircan): Check if we can find the actual system capabilities via
-// creating VTCompressionSessions with varying requirements.
-// See crbug.com/584784.
-const size_t kBitsPerByte = 8;
-const size_t kDefaultResolutionWidth = 640;
-const size_t kDefaultResolutionHeight = 480;
-const size_t kMaxFrameRateNumerator = 30;
-const size_t kMaxFrameRateDenominator = 1;
-const size_t kMaxResolutionWidth = 4096;
-const size_t kMaxResolutionHeight = 2160;
-const size_t kNumInputBuffers = 3;
-
-} // namespace
-
-struct VTVideoEncodeAccelerator::InProgressFrameEncode {
- InProgressFrameEncode(base::TimeDelta rtp_timestamp,
- base::TimeTicks ref_time)
- : timestamp(rtp_timestamp), reference_time(ref_time) {}
- const base::TimeDelta timestamp;
- const base::TimeTicks reference_time;
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(InProgressFrameEncode);
-};
-
-struct VTVideoEncodeAccelerator::EncodeOutput {
- EncodeOutput(VTEncodeInfoFlags info_flags, CMSampleBufferRef sbuf)
- : info(info_flags), sample_buffer(sbuf, base::scoped_policy::RETAIN) {}
- const VTEncodeInfoFlags info;
- const base::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer;
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(EncodeOutput);
-};
-
-struct VTVideoEncodeAccelerator::BitstreamBufferRef {
- BitstreamBufferRef(int32_t id,
- scoped_ptr<base::SharedMemory> shm,
- size_t size)
- : id(id), shm(std::move(shm)), size(size) {}
- const int32_t id;
- const scoped_ptr<base::SharedMemory> shm;
- const size_t size;
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(BitstreamBufferRef);
-};
-
-VTVideoEncodeAccelerator::VTVideoEncodeAccelerator()
- : client_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- encoder_thread_("VTEncoderThread"),
- encoder_task_weak_factory_(this) {
- encoder_weak_ptr_ = encoder_task_weak_factory_.GetWeakPtr();
-}
-
-VTVideoEncodeAccelerator::~VTVideoEncodeAccelerator() {
- DVLOG(3) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
-
- Destroy();
- DCHECK(!encoder_thread_.IsRunning());
- DCHECK(!encoder_task_weak_factory_.HasWeakPtrs());
-}
-
-media::VideoEncodeAccelerator::SupportedProfiles
-VTVideoEncodeAccelerator::GetSupportedProfiles() {
- DVLOG(3) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
-
- SupportedProfiles profiles;
- // Check if HW encoder is supported initially.
- videotoolbox_glue_ = VideoToolboxGlue::Get();
- if (!videotoolbox_glue_) {
- DLOG(ERROR) << "Failed creating VideoToolbox glue.";
- return profiles;
- }
- const bool rv = CreateCompressionSession(
- media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0),
- gfx::Size(kDefaultResolutionWidth, kDefaultResolutionHeight), true);
- DestroyCompressionSession();
- if (!rv) {
- VLOG(1)
- << "Hardware encode acceleration is not available on this platform.";
- return profiles;
- }
-
- SupportedProfile profile;
- profile.profile = media::H264PROFILE_BASELINE;
- profile.max_framerate_numerator = kMaxFrameRateNumerator;
- profile.max_framerate_denominator = kMaxFrameRateDenominator;
- profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
- profiles.push_back(profile);
- return profiles;
-}
-
-bool VTVideoEncodeAccelerator::Initialize(
- media::VideoPixelFormat format,
- const gfx::Size& input_visible_size,
- media::VideoCodecProfile output_profile,
- uint32_t initial_bitrate,
- Client* client) {
- DVLOG(3) << __FUNCTION__
- << ": input_format=" << media::VideoPixelFormatToString(format)
- << ", input_visible_size=" << input_visible_size.ToString()
- << ", output_profile=" << output_profile
- << ", initial_bitrate=" << initial_bitrate;
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(client);
-
- if (media::PIXEL_FORMAT_I420 != format) {
- DLOG(ERROR) << "Input format not supported= "
- << media::VideoPixelFormatToString(format);
- return false;
- }
- if (media::H264PROFILE_BASELINE != output_profile) {
- DLOG(ERROR) << "Output profile not supported= "
- << output_profile;
- return false;
- }
-
- videotoolbox_glue_ = VideoToolboxGlue::Get();
- if (!videotoolbox_glue_) {
- DLOG(ERROR) << "Failed creating VideoToolbox glue.";
- return false;
- }
-
- client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
- client_ = client_ptr_factory_->GetWeakPtr();
- input_visible_size_ = input_visible_size;
- frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
- target_bitrate_ = initial_bitrate;
- bitstream_buffer_size_ = input_visible_size.GetArea();
-
- if (!encoder_thread_.Start()) {
- DLOG(ERROR) << "Failed spawning encoder thread.";
- return false;
- }
- encoder_thread_task_runner_ = encoder_thread_.task_runner();
-
- if (!ResetCompressionSession()) {
- DLOG(ERROR) << "Failed creating compression session.";
- return false;
- }
-
- client_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers,
- input_visible_size_, bitstream_buffer_size_));
- return true;
-}
-
-void VTVideoEncodeAccelerator::Encode(
- const scoped_refptr<media::VideoFrame>& frame,
- bool force_keyframe) {
- DVLOG(3) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
-
- encoder_thread_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VTVideoEncodeAccelerator::EncodeTask,
- base::Unretained(this), frame, force_keyframe));
-}
-
-void VTVideoEncodeAccelerator::UseOutputBitstreamBuffer(
- const media::BitstreamBuffer& buffer) {
- DVLOG(3) << __FUNCTION__ << ": buffer size=" << buffer.size();
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (buffer.size() < bitstream_buffer_size_) {
- DLOG(ERROR) << "Output BitstreamBuffer isn't big enough: " << buffer.size()
- << " vs. " << bitstream_buffer_size_;
- client_->NotifyError(kInvalidArgumentError);
- return;
- }
-
- scoped_ptr<base::SharedMemory> shm(
- new base::SharedMemory(buffer.handle(), false));
- if (!shm->Map(buffer.size())) {
- DLOG(ERROR) << "Failed mapping shared memory.";
- client_->NotifyError(kPlatformFailureError);
- return;
- }
-
- scoped_ptr<BitstreamBufferRef> buffer_ref(
- new BitstreamBufferRef(buffer.id(), std::move(shm), buffer.size()));
-
- encoder_thread_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VTVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
- base::Unretained(this), base::Passed(&buffer_ref)));
-}
-
-void VTVideoEncodeAccelerator::RequestEncodingParametersChange(
- uint32_t bitrate,
- uint32_t framerate) {
- DVLOG(3) << __FUNCTION__ << ": bitrate=" << bitrate
- << ": framerate=" << framerate;
- DCHECK(thread_checker_.CalledOnValidThread());
-
- encoder_thread_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VTVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
- base::Unretained(this), bitrate, framerate));
-}
-
-void VTVideoEncodeAccelerator::Destroy() {
- DVLOG(3) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // Cancel all callbacks.
- client_ptr_factory_.reset();
-
- if (encoder_thread_.IsRunning()) {
- encoder_thread_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VTVideoEncodeAccelerator::DestroyTask,
- base::Unretained(this)));
- encoder_thread_.Stop();
- } else {
- DestroyTask();
- }
-}
-
-void VTVideoEncodeAccelerator::EncodeTask(
- const scoped_refptr<media::VideoFrame>& frame,
- bool force_keyframe) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- DCHECK(compression_session_);
- DCHECK(frame);
-
- // TODO(emircan): See if we can eliminate a copy here by using
- // CVPixelBufferPool for the allocation of incoming VideoFrames.
- base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer =
- media::WrapVideoFrameInCVPixelBuffer(*frame);
- base::ScopedCFTypeRef<CFDictionaryRef> frame_props =
- media::video_toolbox::DictionaryWithKeyValue(
- videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(),
- force_keyframe ? kCFBooleanTrue : kCFBooleanFalse);
-
- base::TimeTicks ref_time;
- if (!frame->metadata()->GetTimeTicks(
- media::VideoFrameMetadata::REFERENCE_TIME, &ref_time)) {
- ref_time = base::TimeTicks::Now();
- }
- auto timestamp_cm = CoreMediaGlue::CMTimeMake(
- frame->timestamp().InMicroseconds(), USEC_PER_SEC);
- // Wrap information we'll need after the frame is encoded in a heap object.
- // We'll get the pointer back from the VideoToolbox completion callback.
- scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode(
- frame->timestamp(), ref_time));
-
- // We can pass the ownership of |request| to the encode callback if
- // successful. Otherwise let it fall out of scope.
- OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame(
- compression_session_, pixel_buffer, timestamp_cm,
- CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props,
- reinterpret_cast<void*>(request.get()), nullptr);
- if (status != noErr) {
- DLOG(ERROR) << " VTCompressionSessionEncodeFrame failed: " << status;
- NotifyError(kPlatformFailureError);
- } else {
- CHECK(request.release());
- }
-}
-
-void VTVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
- scoped_ptr<BitstreamBufferRef> buffer_ref) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
-
- // If there is already EncodeOutput waiting, copy its output first.
- if (!encoder_output_queue_.empty()) {
- scoped_ptr<VTVideoEncodeAccelerator::EncodeOutput> encode_output =
- std::move(encoder_output_queue_.front());
- encoder_output_queue_.pop_front();
- ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref));
- return;
- }
-
- bitstream_buffer_queue_.push_back(std::move(buffer_ref));
-}
-
-void VTVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
- uint32_t bitrate,
- uint32_t framerate) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
-
- frame_rate_ = framerate > 1 ? framerate : 1;
- target_bitrate_ = bitrate > 1 ? bitrate : 1;
-
- if (!compression_session_) {
- NotifyError(kPlatformFailureError);
- return;
- }
-
- media::video_toolbox::SessionPropertySetter session_property_setter(
- compression_session_, videotoolbox_glue_);
- // TODO(emircan): See crbug.com/425352.
- bool rv = session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(),
- target_bitrate_);
- rv &= session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(),
- frame_rate_);
- rv &= session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_DataRateLimits(),
- media::video_toolbox::ArrayWithIntegerAndFloat(
- target_bitrate_ / kBitsPerByte, 1.0f));
- DLOG_IF(ERROR, !rv) << "Couldn't change session encoding parameters.";
-}
-
-void VTVideoEncodeAccelerator::DestroyTask() {
- DCHECK(thread_checker_.CalledOnValidThread() ||
- (encoder_thread_.IsRunning() &&
- encoder_thread_task_runner_->BelongsToCurrentThread()));
-
- // Cancel all encoder thread callbacks.
- encoder_task_weak_factory_.InvalidateWeakPtrs();
-
- // This call blocks until all pending frames are flushed out.
- DestroyCompressionSession();
-}
-
-void VTVideoEncodeAccelerator::NotifyError(
- media::VideoEncodeAccelerator::Error error) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- client_task_runner_->PostTask(
- FROM_HERE, base::Bind(&Client::NotifyError, client_, error));
-}
-
-// static
-void VTVideoEncodeAccelerator::CompressionCallback(void* encoder_opaque,
- void* request_opaque,
- OSStatus status,
- VTEncodeInfoFlags info,
- CMSampleBufferRef sbuf) {
- // This function may be called asynchronously, on a different thread from the
- // one that calls VTCompressionSessionEncodeFrame.
- DVLOG(3) << __FUNCTION__;
-
- auto encoder = reinterpret_cast<VTVideoEncodeAccelerator*>(encoder_opaque);
- DCHECK(encoder);
-
- // Release InProgressFrameEncode, since we don't have support to return
- // timestamps at this point.
- scoped_ptr<InProgressFrameEncode> request(
- reinterpret_cast<InProgressFrameEncode*>(request_opaque));
- request.reset();
-
- // EncodeOutput holds onto CMSampleBufferRef when posting task between
- // threads.
- scoped_ptr<EncodeOutput> encode_output(new EncodeOutput(info, sbuf));
-
- // This method is NOT called on |encoder_thread_|, so we still need to
- // post a task back to it to do work.
- encoder->encoder_thread_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VTVideoEncodeAccelerator::CompressionCallbackTask,
- encoder->encoder_weak_ptr_, status,
- base::Passed(&encode_output)));
-}
-
-void VTVideoEncodeAccelerator::CompressionCallbackTask(
- OSStatus status,
- scoped_ptr<EncodeOutput> encode_output) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
-
- if (status != noErr) {
- DLOG(ERROR) << " encode failed: " << status;
- NotifyError(kPlatformFailureError);
- return;
- }
-
- // If there isn't any BitstreamBuffer to copy into, add it to a queue for
- // later use.
- if (bitstream_buffer_queue_.empty()) {
- encoder_output_queue_.push_back(std::move(encode_output));
- return;
- }
-
- scoped_ptr<VTVideoEncodeAccelerator::BitstreamBufferRef> buffer_ref =
- std::move(bitstream_buffer_queue_.front());
- bitstream_buffer_queue_.pop_front();
- ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref));
-}
-
-void VTVideoEncodeAccelerator::ReturnBitstreamBuffer(
- scoped_ptr<EncodeOutput> encode_output,
- scoped_ptr<VTVideoEncodeAccelerator::BitstreamBufferRef> buffer_ref) {
- DVLOG(3) << __FUNCTION__;
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
-
- if (encode_output->info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped) {
- DVLOG(2) << " frame dropped";
- client_task_runner_->PostTask(
- FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_,
- buffer_ref->id, 0, false));
- return;
- }
-
- auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(
- CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(
- encode_output->sample_buffer.get(), true),
- 0));
- const bool keyframe =
- !CFDictionaryContainsKey(sample_attachments,
- CoreMediaGlue::kCMSampleAttachmentKey_NotSync());
-
- size_t used_buffer_size = 0;
- const bool copy_rv = media::video_toolbox::CopySampleBufferToAnnexBBuffer(
- encode_output->sample_buffer.get(), keyframe, buffer_ref->size,
- reinterpret_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size);
- if (!copy_rv) {
- DLOG(ERROR) << "Cannot copy output from SampleBuffer to AnnexBBuffer.";
- used_buffer_size = 0;
- }
-
- client_task_runner_->PostTask(
- FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_,
- buffer_ref->id, used_buffer_size, keyframe));
-}
-
-bool VTVideoEncodeAccelerator::ResetCompressionSession() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- DestroyCompressionSession();
-
- CFTypeRef attributes_keys[] = {
- kCVPixelBufferOpenGLCompatibilityKey,
- kCVPixelBufferIOSurfacePropertiesKey,
- kCVPixelBufferPixelFormatTypeKey
- };
- const int format[] = {
- CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange};
- CFTypeRef attributes_values[] = {
- kCFBooleanTrue,
- media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0)
- .release(),
- media::video_toolbox::ArrayWithIntegers(format, arraysize(format))
- .release()};
- const base::ScopedCFTypeRef<CFDictionaryRef> attributes =
- media::video_toolbox::DictionaryWithKeysAndValues(
- attributes_keys, attributes_values, arraysize(attributes_keys));
- for (auto& v : attributes_values)
- CFRelease(v);
-
- bool session_rv =
- CreateCompressionSession(attributes, input_visible_size_, false);
- if (!session_rv) {
- DestroyCompressionSession();
- return false;
- }
-
- const bool configure_rv = ConfigureCompressionSession();
- if (configure_rv)
- RequestEncodingParametersChange(target_bitrate_, frame_rate_);
- return configure_rv;
-}
-
-bool VTVideoEncodeAccelerator::CreateCompressionSession(
- base::ScopedCFTypeRef<CFDictionaryRef> attributes,
- const gfx::Size& input_size,
- bool require_hw_encoding) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- std::vector<CFTypeRef> encoder_keys;
- std::vector<CFTypeRef> encoder_values;
- if (require_hw_encoding) {
- encoder_keys.push_back(videotoolbox_glue_
- ->kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder());
- encoder_values.push_back(kCFBooleanTrue);
- } else {
- encoder_keys.push_back(videotoolbox_glue_
- ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder());
- encoder_values.push_back(kCFBooleanTrue);
- }
- base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec =
- media::video_toolbox::DictionaryWithKeysAndValues(
- encoder_keys.data(), encoder_values.data(), encoder_keys.size());
-
- // Create the compression session.
- // Note that the encoder object is given to the compression session as the
- // callback context using a raw pointer. The C API does not allow us to use a
- // smart pointer, nor is this encoder ref counted. However, this is still
- // safe, because we 1) we own the compression session and 2) we tear it down
- // safely. When destructing the encoder, the compression session is flushed
- // and invalidated. Internally, VideoToolbox will join all of its threads
- // before returning to the client. Therefore, when control returns to us, we
- // are guaranteed that the output callback will not execute again.
- OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate(
- kCFAllocatorDefault,
- input_size.width(),
- input_size.height(),
- CoreMediaGlue::kCMVideoCodecType_H264,
- encoder_spec,
- attributes,
- nullptr /* compressedDataAllocator */,
- &VTVideoEncodeAccelerator::CompressionCallback,
- reinterpret_cast<void*>(this),
- compression_session_.InitializeInto());
- if (status != noErr) {
- DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status;
- return false;
- }
- DVLOG(3) << " VTCompressionSession created with HW encode: "
- << require_hw_encoding << ", input size=" << input_size.ToString();
- return true;
-}
-
-bool VTVideoEncodeAccelerator::ConfigureCompressionSession() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(compression_session_);
-
- media::video_toolbox::SessionPropertySetter session_property_setter(
- compression_session_, videotoolbox_glue_);
- bool rv = true;
- rv &= session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(),
- videotoolbox_glue_->kVTProfileLevel_H264_Baseline_AutoLevel());
- rv &= session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), true);
- rv &= session_property_setter.Set(
- videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(),
- false);
- DLOG_IF(ERROR, !rv) << " Setting session property failed.";
- return rv;
-}
-
-void VTVideoEncodeAccelerator::DestroyCompressionSession() {
- DCHECK(thread_checker_.CalledOnValidThread() ||
- (encoder_thread_.IsRunning() &&
- encoder_thread_task_runner_->BelongsToCurrentThread()));
-
- if (compression_session_) {
- videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_);
- compression_session_.reset();
- }
-}
-
-} // namespace content
« no previous file with comments | « content/common/gpu/media/vt_video_encode_accelerator_mac.h ('k') | content/content_common.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698