recording_service.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/services/recording/recording_service.h"
  5. #include <cmath>
  6. #include <cstdint>
  7. #include <cstdlib>
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/services/recording/recording_encoder_muxer.h"
  10. #include "ash/services/recording/recording_service_constants.h"
  11. #include "ash/services/recording/video_capture_params.h"
  12. #include "base/bind.h"
  13. #include "base/callback_helpers.h"
  14. #include "base/check.h"
  15. #include "base/location.h"
  16. #include "base/task/task_traits.h"
  17. #include "base/task/thread_pool.h"
  18. #include "base/time/time.h"
  19. #include "media/audio/audio_device_description.h"
  20. #include "media/base/audio_codecs.h"
  21. #include "media/base/status.h"
  22. #include "media/base/video_frame.h"
  23. #include "media/base/video_util.h"
  24. #include "media/capture/mojom/video_capture_buffer.mojom.h"
  25. #include "media/capture/mojom/video_capture_types.mojom.h"
  26. #include "media/renderers/paint_canvas_video_renderer.h"
  27. #include "services/audio/public/cpp/device_factory.h"
  28. #include "third_party/abseil-cpp/absl/types/optional.h"
  29. #include "ui/gfx/image/image_skia_operations.h"
  30. namespace recording {
  31. namespace {
  32. // For a capture size of 320 by 240, we use a bitrate of 256 kbit/s. This value
  33. // is used as the minimum bitrate that we don't go below regardless of the video
  34. // size.
  35. constexpr uint32_t kMinBitrateInBitsPerSecond = 256 * 1000;
  36. // The size (in DIPs) within which we will try to fit a thumbnail image
  37. // extracted from the first valid video frame. The value was chosen to be
  38. // suitable with the image container in the notification UI.
  39. constexpr gfx::Size kThumbnailSize{328, 184};
  40. // Calculates the bitrate (in bits/seconds) used to initialize the video encoder
  41. // based on the given |capture_size|.
  42. uint32_t CalculateVpxEncoderBitrate(const gfx::Size& capture_size) {
  43. // We use the Kush Gauge formula which goes like this:
  44. // bitrate (bits/s) = width * height * frame rate * motion factor * 0.07.
  45. // Here we use a motion factor = 1, which works well for our use cases.
  46. // This formula gives a balance between the video quality and the file size so
  47. // it doesn't become too large.
  48. const uint32_t bitrate =
  49. std::ceil(capture_size.GetArea() * kMaxFrameRate * 0.07f);
  50. // Make sure to return a value that is divisible by 8 so that we work with
  51. // whole bytes.
  52. return std::max(kMinBitrateInBitsPerSecond, (bitrate & ~7));
  53. }
  54. // Given the desired |capture_size|, it creates and returns the options needed
  55. // to configure the video encoder.
  56. media::VideoEncoder::Options CreateVideoEncoderOptions(
  57. const gfx::Size& capture_size) {
  58. media::VideoEncoder::Options video_encoder_options;
  59. video_encoder_options.bitrate =
  60. media::Bitrate::ConstantBitrate(CalculateVpxEncoderBitrate(capture_size));
  61. video_encoder_options.framerate = kMaxFrameRate;
  62. video_encoder_options.frame_size = capture_size;
  63. // This value, expressed as a number of frames, forces the encoder to code
  64. // a keyframe if one has not been coded in the last keyframe_interval frames.
  65. video_encoder_options.keyframe_interval = 100;
  66. return video_encoder_options;
  67. }
  68. media::AudioParameters GetAudioParameters() {
  69. static_assert(kAudioSampleRate % 100 == 0,
  70. "Audio sample rate is not divisible by 100");
  71. return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
  72. media::CHANNEL_LAYOUT_STEREO, kAudioSampleRate,
  73. kAudioSampleRate / 100);
  74. }
  75. // Extracts a potentially scaled-down RGB image from the given video |frame|,
  76. // which is suitable to use as a thumbnail for the video.
  77. gfx::ImageSkia ExtractImageFromVideoFrame(const media::VideoFrame& frame) {
  78. const gfx::Size visible_size = frame.visible_rect().size();
  79. media::PaintCanvasVideoRenderer renderer;
  80. SkBitmap bitmap;
  81. bitmap.allocN32Pixels(visible_size.width(), visible_size.height());
  82. renderer.ConvertVideoFrameToRGBPixels(&frame, bitmap.getPixels(),
  83. bitmap.rowBytes());
  84. // Since this image will be used as a thumbnail, we can scale it down to save
  85. // on memory if needed. For example, if recording a FHD display, that will be
  86. // (for 12 bits/pixel):
  87. // 1920 * 1080 * 12 / 8, which is approx. = 3 MB, which is a lot to keep
  88. // around for a thumbnail.
  89. const gfx::ImageSkia thumbnail = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  90. if (visible_size.width() <= kThumbnailSize.width() &&
  91. visible_size.height() <= kThumbnailSize.height()) {
  92. return thumbnail;
  93. }
  94. const gfx::Size scaled_size =
  95. media::ScaleSizeToFitWithinTarget(visible_size, kThumbnailSize);
  96. return gfx::ImageSkiaOperations::CreateResizedImage(
  97. thumbnail, skia::ImageOperations::ResizeMethod::RESIZE_BETTER,
  98. scaled_size);
  99. }
  100. // Called when the channel to the client of the recording service gets
  101. // disconnected. At that point, there's nothing useful to do here, and instead
  102. // of wasting resources encoding/muxing remaining frames, and flushing the
  103. // buffers, we terminate the recording service process immediately.
  104. void TerminateServiceImmediately() {
  105. LOG(ERROR)
  106. << "The recording service client was disconnected. Exiting immediately.";
  107. std::exit(EXIT_FAILURE);
  108. }
  109. } // namespace
  110. RecordingService::RecordingService(
  111. mojo::PendingReceiver<mojom::RecordingService> receiver)
  112. : audio_parameters_(GetAudioParameters()),
  113. receiver_(this, std::move(receiver)),
  114. consumer_receiver_(this),
  115. main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
  116. encoding_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  117. // We use |USER_VISIBLE| here as opposed to |BEST_EFFORT| since the
  118. // latter is extremely low priority and may stall encoding for random
  119. // reasons.
  120. {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
  121. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
  122. }
  123. RecordingService::~RecordingService() {
  124. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  125. if (!current_video_capture_params_)
  126. return;
  127. // If the service gets destructed while recording in progress, the client must
  128. // be still connected (since otherwise the service process would have been
  129. // immediately terminated). We attempt to flush whatever we have right now
  130. // before exiting.
  131. DCHECK(client_remote_.is_bound());
  132. DCHECK(client_remote_.is_connected());
  133. StopRecording();
  134. video_capturer_remote_.reset();
  135. consumer_receiver_.reset();
  136. // Note that we can call FlushAndFinalize() on the |encoder_muxer_| even
  137. // though it will be done asynchronously on the |encoding_task_runner_| and by
  138. // then this |RecordingService| instance will have already been gone. This is
  139. // because the muxer writes directly to the file and does not rely on this
  140. // instance.
  141. encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::FlushAndFinalize)
  142. .WithArgs(base::DoNothing());
  143. SignalRecordingEndedToClient(mojom::RecordingStatus::kServiceClosing);
  144. }
  145. void RecordingService::RecordFullscreen(
  146. mojo::PendingRemote<mojom::RecordingServiceClient> client,
  147. mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
  148. mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory,
  149. mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate,
  150. const base::FilePath& webm_file_path,
  151. const viz::FrameSinkId& frame_sink_id,
  152. const gfx::Size& frame_sink_size_dip,
  153. float device_scale_factor) {
  154. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  155. StartNewRecording(
  156. std::move(client), std::move(video_capturer),
  157. std::move(audio_stream_factory), std::move(drive_fs_quota_delegate),
  158. webm_file_path,
  159. VideoCaptureParams::CreateForFullscreenCapture(
  160. frame_sink_id, frame_sink_size_dip, device_scale_factor));
  161. }
  162. void RecordingService::RecordWindow(
  163. mojo::PendingRemote<mojom::RecordingServiceClient> client,
  164. mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
  165. mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory,
  166. mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate,
  167. const base::FilePath& webm_file_path,
  168. const viz::FrameSinkId& frame_sink_id,
  169. const gfx::Size& frame_sink_size_dip,
  170. float device_scale_factor,
  171. const viz::SubtreeCaptureId& subtree_capture_id,
  172. const gfx::Size& window_size_dip) {
  173. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  174. StartNewRecording(std::move(client), std::move(video_capturer),
  175. std::move(audio_stream_factory),
  176. std::move(drive_fs_quota_delegate), webm_file_path,
  177. VideoCaptureParams::CreateForWindowCapture(
  178. frame_sink_id, subtree_capture_id, frame_sink_size_dip,
  179. device_scale_factor, window_size_dip));
  180. }
  181. void RecordingService::RecordRegion(
  182. mojo::PendingRemote<mojom::RecordingServiceClient> client,
  183. mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
  184. mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory,
  185. mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate,
  186. const base::FilePath& webm_file_path,
  187. const viz::FrameSinkId& frame_sink_id,
  188. const gfx::Size& frame_sink_size_dip,
  189. float device_scale_factor,
  190. const gfx::Rect& crop_region_dip) {
  191. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  192. StartNewRecording(std::move(client), std::move(video_capturer),
  193. std::move(audio_stream_factory),
  194. std::move(drive_fs_quota_delegate), webm_file_path,
  195. VideoCaptureParams::CreateForRegionCapture(
  196. frame_sink_id, frame_sink_size_dip, device_scale_factor,
  197. crop_region_dip));
  198. }
  199. void RecordingService::StopRecording() {
  200. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  201. video_capturer_remote_->Stop();
  202. if (audio_capturer_)
  203. audio_capturer_->Stop();
  204. audio_capturer_.reset();
  205. }
  206. void RecordingService::OnRecordedWindowChangingRoot(
  207. const viz::FrameSinkId& new_frame_sink_id,
  208. const gfx::Size& new_frame_sink_size_dip,
  209. float new_device_scale_factor) {
  210. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  211. if (!current_video_capture_params_) {
  212. // A recording might terminate before we signal the client with an
  213. // |OnRecordingEnded()| call.
  214. return;
  215. }
  216. // If there's a change in the pixel size of the recorded window as a result of
  217. // it moving to a different display, we must reconfigure the video encoder so
  218. // that output video has the correct dimensions.
  219. if (current_video_capture_params_->OnRecordedWindowChangingRoot(
  220. video_capturer_remote_, new_frame_sink_id, new_frame_sink_size_dip,
  221. new_device_scale_factor)) {
  222. ReconfigureVideoEncoder();
  223. }
  224. }
  225. void RecordingService::OnRecordedWindowSizeChanged(
  226. const gfx::Size& new_window_size_dip) {
  227. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  228. if (!current_video_capture_params_) {
  229. // A recording might terminate before we signal the client with an
  230. // |OnRecordingEnded()| call.
  231. return;
  232. }
  233. if (current_video_capture_params_->OnRecordedWindowSizeChanged(
  234. video_capturer_remote_, new_window_size_dip)) {
  235. ReconfigureVideoEncoder();
  236. }
  237. }
  238. void RecordingService::OnFrameSinkSizeChanged(
  239. const gfx::Size& new_frame_sink_size_dip,
  240. float new_device_scale_factor) {
  241. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  242. if (!current_video_capture_params_) {
  243. // A recording might terminate before we signal the client with an
  244. // |OnRecordingEnded()| call.
  245. return;
  246. }
  247. // A change in the pixel size of the frame sink may result in changing the
  248. // pixel size of the captured target (e.g. window or region). we must
  249. // reconfigure the video encoder so that output video has the correct
  250. // dimensions.
  251. if (current_video_capture_params_->OnFrameSinkSizeChanged(
  252. video_capturer_remote_, new_frame_sink_size_dip,
  253. new_device_scale_factor)) {
  254. ReconfigureVideoEncoder();
  255. }
  256. }
  257. void RecordingService::OnFrameCaptured(
  258. media::mojom::VideoBufferHandlePtr data,
  259. media::mojom::VideoFrameInfoPtr info,
  260. const gfx::Rect& content_rect,
  261. mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
  262. callbacks) {
  263. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  264. DCHECK(encoder_muxer_);
  265. CHECK(data->is_read_only_shmem_region());
  266. base::ReadOnlySharedMemoryRegion& shmem_region =
  267. data->get_read_only_shmem_region();
  268. // The |data| parameter is not nullable and mojo type mapping for
  269. // `base::ReadOnlySharedMemoryRegion` defines that nullable version of it is
  270. // the same type, with null check being equivalent to IsValid() check. Given
  271. // the above, we should never be able to receive a read only shmem region that
  272. // is not valid - mojo will enforce it for us.
  273. DCHECK(shmem_region.IsValid());
  274. // We ignore any subsequent frames after a failure.
  275. if (did_failure_occur_)
  276. return;
  277. base::ReadOnlySharedMemoryMapping mapping = shmem_region.Map();
  278. if (!mapping.IsValid()) {
  279. DLOG(ERROR) << "Mapping of video frame shared memory failed.";
  280. return;
  281. }
  282. if (mapping.size() <
  283. media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
  284. DLOG(ERROR) << "Shared memory size was less than expected.";
  285. return;
  286. }
  287. if (!info->color_space) {
  288. DLOG(ERROR) << "Missing mandatory color space info.";
  289. return;
  290. }
  291. DCHECK(current_video_capture_params_);
  292. const gfx::Rect& visible_rect =
  293. current_video_capture_params_->GetVideoFrameVisibleRect(
  294. info->visible_rect);
  295. scoped_refptr<media::VideoFrame> frame = media::VideoFrame::WrapExternalData(
  296. info->pixel_format, info->coded_size, visible_rect, visible_rect.size(),
  297. reinterpret_cast<uint8_t*>(const_cast<void*>(mapping.memory())),
  298. mapping.size(), info->timestamp);
  299. if (!frame) {
  300. DLOG(ERROR) << "Failed to create a VideoFrame.";
  301. return;
  302. }
  303. // Takes ownership of |mapping| and |callbacks| to keep them alive until
  304. // |frame| is released.
  305. frame->AddDestructionObserver(base::BindOnce(
  306. [](base::ReadOnlySharedMemoryMapping mapping,
  307. mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
  308. callbacks) {},
  309. std::move(mapping), std::move(callbacks)));
  310. frame->set_metadata(info->metadata);
  311. frame->set_color_space(info->color_space.value());
  312. if (video_thumbnail_.isNull())
  313. video_thumbnail_ = ExtractImageFromVideoFrame(*frame);
  314. if (on_video_frame_delivered_callback_for_testing_) {
  315. std::move(on_video_frame_delivered_callback_for_testing_)
  316. .Run(*frame, content_rect);
  317. }
  318. encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::EncodeVideo).WithArgs(frame);
  319. }
  320. void RecordingService::OnNewCropVersion(uint32_t crop_version) {}
  321. void RecordingService::OnFrameWithEmptyRegionCapture() {}
  322. void RecordingService::OnStopped() {
  323. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  324. // If a failure occurred, we don't wait till the capturer sends us this
  325. // signal. The recording had already been terminated by now.
  326. if (!did_failure_occur_)
  327. TerminateRecording(mojom::RecordingStatus::kSuccess);
  328. }
  329. void RecordingService::OnLog(const std::string& message) {
  330. DLOG(WARNING) << message;
  331. }
  332. void RecordingService::OnCaptureStarted() {}
  333. void RecordingService::Capture(const media::AudioBus* audio_source,
  334. base::TimeTicks audio_capture_time,
  335. double volume,
  336. bool key_pressed) {
  337. // This is called on a worker thread created by the |audio_capturer_| (See
  338. // |media::AudioDeviceThread|. The given |audio_source| wraps audio data in a
  339. // shared memory with the audio service. Calling |audio_capturer_->Stop()|
  340. // will destroy that thread and the shared memory mapping before we get a
  341. // chance to encode and flush the remaining frames (See
  342. // media::AudioInputDevice::Stop(), and
  343. // media::AudioInputDevice::AudioThreadCallback::Process() for details). It is
  344. // safer that we own our AudioBuses that are kept alive until encoded and
  345. // flushed.
  346. auto audio_data =
  347. media::AudioBus::Create(audio_source->channels(), audio_source->frames());
  348. audio_source->CopyTo(audio_data.get());
  349. main_task_runner_->PostTask(
  350. FROM_HERE, base::BindOnce(&RecordingService::OnAudioCaptured,
  351. weak_ptr_factory_.GetWeakPtr(),
  352. std::move(audio_data), audio_capture_time));
  353. }
  354. void RecordingService::OnCaptureError(
  355. media::AudioCapturerSource::ErrorCode code,
  356. const std::string& message) {
  357. LOG(ERROR) << "AudioCaptureError: code=" << static_cast<uint32_t>(code)
  358. << ", " << message;
  359. }
  360. void RecordingService::OnCaptureMuted(bool is_muted) {}
  361. void RecordingService::StartNewRecording(
  362. mojo::PendingRemote<mojom::RecordingServiceClient> client,
  363. mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
  364. mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory,
  365. mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate,
  366. const base::FilePath& webm_file_path,
  367. std::unique_ptr<VideoCaptureParams> capture_params) {
  368. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  369. if (current_video_capture_params_) {
  370. LOG(ERROR) << "Cannot start a new recording while another is in progress.";
  371. return;
  372. }
  373. client_remote_.reset();
  374. client_remote_.Bind(std::move(client));
  375. client_remote_.set_disconnect_handler(
  376. base::BindOnce(&TerminateServiceImmediately));
  377. current_video_capture_params_ = std::move(capture_params);
  378. const bool should_record_audio = audio_stream_factory.is_valid();
  379. encoder_muxer_ = RecordingEncoderMuxer::Create(
  380. encoding_task_runner_,
  381. CreateVideoEncoderOptions(current_video_capture_params_->GetVideoSize()),
  382. should_record_audio ? &audio_parameters_ : nullptr,
  383. std::move(drive_fs_quota_delegate), webm_file_path,
  384. BindOnceToMainThread(&RecordingService::OnEncodingFailure));
  385. ConnectAndStartVideoCapturer(std::move(video_capturer));
  386. if (!should_record_audio)
  387. return;
  388. audio_capturer_ = audio::CreateInputDevice(
  389. std::move(audio_stream_factory),
  390. std::string(media::AudioDeviceDescription::kDefaultDeviceId),
  391. audio::DeadStreamDetection::kEnabled);
  392. DCHECK(audio_capturer_);
  393. audio_capturer_->Initialize(audio_parameters_, this);
  394. audio_capturer_->Start();
  395. }
  396. void RecordingService::ReconfigureVideoEncoder() {
  397. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  398. DCHECK(current_video_capture_params_);
  399. ++number_of_video_encoder_reconfigures_;
  400. encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::InitializeVideoEncoder)
  401. .WithArgs(CreateVideoEncoderOptions(
  402. current_video_capture_params_->GetVideoSize()));
  403. }
  404. void RecordingService::TerminateRecording(mojom::RecordingStatus status) {
  405. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  406. DCHECK(encoder_muxer_);
  407. current_video_capture_params_.reset();
  408. video_capturer_remote_.reset();
  409. consumer_receiver_.reset();
  410. encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::FlushAndFinalize)
  411. .WithArgs(BindOnceToMainThread(&RecordingService::OnEncoderMuxerFlushed,
  412. status));
  413. }
  414. void RecordingService::ConnectAndStartVideoCapturer(
  415. mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer) {
  416. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  417. DCHECK(current_video_capture_params_);
  418. video_capturer_remote_.reset();
  419. video_capturer_remote_.Bind(std::move(video_capturer));
  420. // The GPU process could crash while recording is in progress, and the video
  421. // capturer will be disconnected. We need to handle this event gracefully.
  422. video_capturer_remote_.set_disconnect_handler(base::BindOnce(
  423. &RecordingService::OnVideoCapturerDisconnected, base::Unretained(this)));
  424. current_video_capture_params_->InitializeVideoCapturer(
  425. video_capturer_remote_);
  426. video_capturer_remote_->Start(consumer_receiver_.BindNewPipeAndPassRemote(),
  427. viz::mojom::BufferFormatPreference::kDefault);
  428. }
  429. void RecordingService::OnVideoCapturerDisconnected() {
  430. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  431. // On a crash in the GPU, the video capturer gets disconnected, so we can't
  432. // communicate with it any longer, but we can still communicate with the audio
  433. // capturer. We will stop the recording and flush whatever video chunks we
  434. // currently have.
  435. did_failure_occur_ = true;
  436. if (audio_capturer_)
  437. audio_capturer_->Stop();
  438. audio_capturer_.reset();
  439. TerminateRecording(mojom::RecordingStatus::kVizVideoCapturerDisconnected);
  440. }
  441. void RecordingService::OnAudioCaptured(
  442. std::unique_ptr<media::AudioBus> audio_bus,
  443. base::TimeTicks audio_capture_time) {
  444. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  445. DCHECK(encoder_muxer_);
  446. // We ignore any subsequent frames after a failure.
  447. if (did_failure_occur_)
  448. return;
  449. encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::EncodeAudio)
  450. .WithArgs(std::move(audio_bus), audio_capture_time);
  451. }
  452. void RecordingService::OnEncodingFailure(mojom::RecordingStatus status) {
  453. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  454. did_failure_occur_ = true;
  455. StopRecording();
  456. // We don't wait for the video capturer to send us the OnStopped() signal, we
  457. // terminate recording immediately. We still need to flush the encoders, and
  458. // muxer since they may contain valid frames from before the failure occurred,
  459. // that we can propagate to the client.
  460. TerminateRecording(status);
  461. }
  462. void RecordingService::OnEncoderMuxerFlushed(mojom::RecordingStatus status) {
  463. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  464. SignalRecordingEndedToClient(status);
  465. }
  466. void RecordingService::SignalRecordingEndedToClient(
  467. mojom::RecordingStatus status) {
  468. DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
  469. DCHECK(encoder_muxer_);
  470. encoder_muxer_.Reset();
  471. client_remote_->OnRecordingEnded(status, video_thumbnail_);
  472. }
  473. } // namespace recording