File: desktop_capturer_android.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (142 lines) | stat: -rw-r--r-- 6,552 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURER_ANDROID_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURER_ANDROID_H_

#include <jni.h>

#include <optional>

#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"

namespace content {

// `DesktopCapturer` implementation for Android. The lifetime model is somewhat
// complex because there are a few things involved:
// - An instance of `DesktopCapturerAndroid` which creates a Java side object,
//    `ScreenCapture`.
// - `ScreenCapture` Java object, which manages interaction with the OS.
// -  A background thread created by `ScreenCapture` which calls back into
//     `DesktopCapturerAndroid` with the actual buffers from the OS.
//
// This is additionally complicated by the following factors:
// - Screen capture may be stopped from either the C++ side or the Java side.
// - Buffers must be freed on the Java side, but must be consumed on the desktop
//    capturer thread.
//
// We make the following observations:
// - We don't want to try to send frames (i.e. call any methods of `Callback`)
//    while `DesktopCapturerAndroid` is being destructed, since that happens
//    during destruction of the owning objects too.
// - C++ side JNI methods must have a valid `this` pointer at least some of the
//    time for them to know anything about what's safe to do, so necessarily we
//    may sometimes have to block in the destructor. This class is marked final
//    just in case to avoid future usages that access derived members that are
//    already destructed in this case. Blocking is accomplished using locking on
//    the Java side to wait for methods calling C++ side JNI methods.
// - In-flight but not yet executed tasks (e.g. processing a frame that came
//    from the background thread) need to be cancellable in some way to avoid
//    calling `Callback` methods during destruction.
// - Since destruction waits on C++ side JNI methods to complete from the
//    desktop capturer thread, C++ side JNI methods must not wait on progress on
//    the desktop capturer thread during destruction (i.e. while waiting on C++
//    side JNI methods) or there will be deadlocks.
//
// To handle these, we adopt the following rules:
// - C++ side JNI methods must not directly touch member variables that are
//   modified on the desktop capturer thread, since they are called from the
//   background thread.
// - C++ side JNI methods must not block on the desktop capturer thread, or
//   there could be a deadlock with destruction.
// - Java side code calling C++ side JNI methods must participate in locking to
//   prevent destruction of `DesktopCapturerAndroid` (to ensure C++ side JNI
//   methods have a valid `this` pointer).
class DesktopCapturerAndroid final : public webrtc::DesktopCapturer {
 public:
  DesktopCapturerAndroid(const webrtc::DesktopCaptureOptions& options);
  DesktopCapturerAndroid(const DesktopCapturerAndroid&) = delete;
  DesktopCapturerAndroid& operator=(const DesktopCapturerAndroid&) = delete;
  ~DesktopCapturerAndroid() override;

  // DesktopCapturer:
  void Start(Callback* callback) override;
  void SetSharedMemoryFactory(std::unique_ptr<webrtc::SharedMemoryFactory>
                                  shared_memory_factory) override;
  void CaptureFrame() override;
  bool SelectSource(SourceId id) override;

  // JNI - these methods are called from Java and may be invoked on a different
  // thread. They should not perform any work on member variables that are
  // accessed from the main thread, and should instead post a task to the main
  // thread to do so.
  void OnRgbaFrameAvailable(JNIEnv* env,
                            const base::android::JavaRef<jobject>& release_cb,
                            jlong timestamp_ns,
                            const base::android::JavaRef<jobject>& buf,
                            jint unchecked_pixel_stride,
                            jint unchecked_row_stride,
                            jint unchecked_crop_left,
                            jint unchecked_crop_top,
                            jint unchecked_crop_right,
                            jint unchecked_crop_bottom);

  void OnStop(JNIEnv* env);

 private:
  // `PlaneInfo` stores all info needed to process buffers received from
  // Android.
  struct PlaneInfo {
    PlaneInfo();
    ~PlaneInfo();
    PlaneInfo(PlaneInfo&& other);
    PlaneInfo& operator=(PlaneInfo&& other);

    PlaneInfo(const PlaneInfo&) = delete;
    PlaneInfo& operator=(const PlaneInfo&) = delete;

    // Java callback to run when this plane's buffer is no longer in use.
    base::android::ScopedJavaGlobalRef<jobject> release_cb;
    // Timestamp of the frame in nanoseconds.
    int64_t timestamp_ns;
    // Java ByteBuffer containing the plane data.
    base::android::ScopedJavaGlobalRef<jobject> buf;
    // The number of bytes between the start of adjacent pixels in a row.
    base::CheckedNumeric<uint32_t> pixel_stride;
    // The number of bytes between the start of adjacent rows of pixels.
    base::CheckedNumeric<uint32_t> row_stride;
    // The x-coordinate of the top-left corner of the crop rectangle.
    base::CheckedNumeric<uint32_t> crop_left;
    // The y-coordinate of the top-left corner of the crop rectangle.
    base::CheckedNumeric<uint32_t> crop_top;
    // The x-coordinate of the bottom-right corner of the crop rectangle.
    base::CheckedNumeric<uint32_t> crop_right;
    // The y-coordinate of the bottom-right corner of the crop rectangle.
    base::CheckedNumeric<uint32_t> crop_bottom;
  };

  void Shutdown();

  void ProcessRgbaFrame(int64_t timestamp_ns, PlaneInfo plane);

  raw_ptr<Callback> callback_ = nullptr;
  base::android::ScopedJavaGlobalRef<jobject> screen_capture_;

  std::unique_ptr<webrtc::DesktopFrame> next_frame_;
  int64_t last_frame_time_ns_ = 0;
  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  bool finishing_ = false;

  base::WeakPtrFactory<DesktopCapturerAndroid> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURER_ANDROID_H_