фон:
Создание скриншотов — функция, которая часто встречается в процессе разработки, но как эта функция реализована на нижнем уровне? Сегодня я поделюсь с вами принципом реализации скриншотов.
Сценарий создания снимка экрана:
Как правило, существует несколько способов сделать снимок экрана:
1. Клавиша уменьшения громкости + питание
2. Опустите строку состояния в systemui или выключите систему и выберите интерфейс
3. Режим командной строки оболочки adb Режим командной строки screencap
Основные из них перечислены выше. Хотя сценарии различаются, интерфейсы снимков экрана, которые они в конечном итоге вызывают, на самом деле одинаковы, и все они представляют собой методы снимков экрана, которые будут вызываться в Surfaceflinger. Ниже приводится анализ снимка экрана с использованием команды оболочки adb. строка, потому что это вызов интерфейса является самым простым.Этот метод анализа в основном использует метод анализа трассировки perfetto.
Исходный код, связанный с командой Screencap, и захват операции perfetto
Как получить перфетто:
1. Введите следующую команду, чтобы захватить перфетто
test@test:~$ aosp/external/perfetto/tools/record_android_trace -o $(date +%Y%m%d_%H%M%S)_trace_file.perfetto-trace -t 5s -b 32mb sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory gfx view wm am ss video camera hal res sync idle binder_driver binder_lock ss
2. Введите следующую команду в другом терминале, чтобы сделать снимок экрана:
скриншот -p /sdcard/1.png
Местоположение кода:
рамки/база/cmds/screencap/screencap.cpp
int main(int argc, char** argv)
{
std::optional<DisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
//省略部分
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
//最为核心的方法ScreenshotClient::captureDisplay
status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
}
ScreenCaptureResults captureResults = captureListener->waitForResults();
if (captureResults.result != NO_ERROR) {
close(fd);
return 1;
}
ui::Dataspace dataspace = captureResults.capturedDataspace;
sp<GraphicBuffer> buffer = captureResults.buffer;
result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
if (png) {
AndroidBitmapInfo info;
info.format = flinger2bitmapFormat(buffer->getPixelFormat());
info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
info.width = buffer->getWidth();
info.height = buffer->getHeight();
info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
int result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base,
ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, &fd,
[](void* fdPtr, const void* data, size_t size) -> bool {
int bytesWritten = write(*static_cast<int*>(fdPtr),
data, size);
return bytesWritten == size;
});
//省略部分
if (fn != NULL) {
notifyMediaScanner(fn);
}
}
//省略部分
return 0;
}
Фактически, основной код всего скринкапа состоит всего из одного предложения:
ScreenshotClient::captureDisplay.
Это интерфейс, связанный с вызовом скриншотов. Интерфейс реализован следующим образом:
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
const sp<IScreenCaptureListener>& captureListener) {
sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
binder::Status status = s->captureDisplay(captureArgs, captureListener);
return status.transactionError();
}
По сути, суть заключается в кросс-процессном вызове и, наконец, будет вызван метод captureDisplay у Surfaceflinger:
status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
ui::Size size;
ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
return NAME_NOT_FOUND;
}
displayWeak = display;
layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
dataspace =
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
false /* useIdentityTransform */,
false /* captureSecureLayers */);
});
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale,
captureListener);
return fenceStatus(future.get());
}
В сочетании с анализом следов
Конкретная система и совершенство вышеупомянутого процесса показаны следующим образом:
Здесь показано, что инициируется межпроцессный вызов, а целевым сегментом является процесс Surfaceflinger.Сводная
диаграмма трассировки выглядит следующим образом:
Surfaceflinger также имеет дополнительную клиентскую операцию обратного вызова:
1. Подготовьте буфер для рисования снимков экрана.
2. Доминируйте над Surfaceflinger и потребуйте предварительной обработки соответствующего слоя.
3. Используйте движок SkiaGl для рисования соответствующего слоя в новом буфере.
4. Уведомите клиента и перезвоните слушателю.
Чтобы получить более полезный видеоконтент с пошаговыми инструкциями по обучению, вы можете подписаться на публичную учетную запись или у владельца станции B (Qianlima Learning Framework)
https://blog.csdn.net/learnframework/article/details/132739059.