#include "sunshine/platform/common.h" #include "sunshine/platform/macos/av_img_t.h" #include "sunshine/platform/macos/av_video.h" #include "sunshine/platform/macos/nv12_zero_device.h" #include "sunshine/config.h" #include "sunshine/main.h" namespace fs = std::filesystem; namespace platf { using namespace std::literals; av_img_t::~av_img_t() { if(pixel_buffer != NULL) { CVPixelBufferUnlockBaseAddress(pixel_buffer, 0); } if(sample_buffer != nullptr) { CFRelease(sample_buffer); } data = nullptr; } struct av_display_t : public display_t { AVVideo *av_capture; CGDirectDisplayID display_id; ~av_display_t() { [av_capture release]; } capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr img, bool *cursor) override { __block auto img_next = std::move(img); auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) { auto av_img_next = std::static_pointer_cast(img_next); CFRetain(sampleBuffer); CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); if(av_img_next->pixel_buffer != nullptr) CVPixelBufferUnlockBaseAddress(av_img_next->pixel_buffer, 0); if(av_img_next->sample_buffer != nullptr) CFRelease(av_img_next->sample_buffer); av_img_next->sample_buffer = sampleBuffer; av_img_next->pixel_buffer = pixelBuffer; img_next->data = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer); size_t extraPixels[4]; CVPixelBufferGetExtendedPixels(pixelBuffer, &extraPixels[0], &extraPixels[1], &extraPixels[2], &extraPixels[3]); img_next->width = CVPixelBufferGetWidth(pixelBuffer) + extraPixels[0] + extraPixels[1]; img_next->height = CVPixelBufferGetHeight(pixelBuffer) + extraPixels[2] + extraPixels[3]; img_next->row_pitch = CVPixelBufferGetBytesPerRow(pixelBuffer); img_next->pixel_pitch = img_next->row_pitch / img_next->width; img_next = snapshot_cb(img_next); return img_next != nullptr; }]; dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER); return capture_e::ok; } std::shared_ptr alloc_img() override { return std::make_shared(); } std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override { if(pix_fmt == pix_fmt_e::yuv420p) { av_capture.pixelFormat = kCVPixelFormatType_32BGRA; return std::make_shared(); } else if(pix_fmt == pix_fmt_e::nv12) { auto device = std::make_shared(); device->init(static_cast(av_capture), setResolution, setPixelFormat); return device; } else { BOOST_LOG(error) << "Unsupported Pixel Format."sv; return nullptr; } } int dummy_img(img_t *img) override { auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) { auto av_img = (av_img_t *)img; CFRetain(sampleBuffer); CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); // XXX: next_img->img should be moved to a smart pointer with // the CFRelease as custom deallocator if(av_img->pixel_buffer != nullptr) CVPixelBufferUnlockBaseAddress(((av_img_t *)img)->pixel_buffer, 0); if(av_img->sample_buffer != nullptr) CFRelease(av_img->sample_buffer); av_img->sample_buffer = sampleBuffer; av_img->pixel_buffer = pixelBuffer; img->data = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer); size_t extraPixels[4]; CVPixelBufferGetExtendedPixels(pixelBuffer, &extraPixels[0], &extraPixels[1], &extraPixels[2], &extraPixels[3]); img->width = CVPixelBufferGetWidth(pixelBuffer) + extraPixels[0] + extraPixels[1]; img->height = CVPixelBufferGetHeight(pixelBuffer) + extraPixels[2] + extraPixels[3]; img->row_pitch = CVPixelBufferGetBytesPerRow(pixelBuffer); img->pixel_pitch = img->row_pitch / img->width; return false; }]; dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER); return 0; } /** * A bridge from the pure C++ code of the hwdevice_t class to the pure Objective C code. * * display --> an opaque pointer to an object of this class * width --> the intended capture width * height --> the intended capture height */ static void setResolution(void *display, int width, int height) { [static_cast(display) setFrameWidth:width frameHeight:height]; } static void setPixelFormat(void *display, OSType pixelFormat) { static_cast(display).pixelFormat = pixelFormat; } }; std::shared_ptr display(platf::mem_type_e hwdevice_type, const std::string &display_name, int framerate) { if(hwdevice_type != platf::mem_type_e::system) { BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; return nullptr; } auto display = std::make_shared(); display->display_id = CGMainDisplayID(); if(!display_name.empty()) { auto display_array = [AVVideo displayNames]; for(NSDictionary *item in display_array) { NSString *name = item[@"name"]; if(name.UTF8String == display_name) { NSNumber *display_id = item[@"id"]; display->display_id = [display_id unsignedIntValue]; } } } display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:framerate]; if(!display->av_capture) { BOOST_LOG(error) << "Video setup failed."sv; return nullptr; } display->width = display->av_capture.frameWidth; display->height = display->av_capture.frameHeight; return display; } std::vector display_names(mem_type_e hwdevice_type) { __block std::vector display_names; auto display_array = [AVVideo displayNames]; display_names.reserve([display_array count]); [display_array enumerateObjectsUsingBlock:^(NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { NSString *name = obj[@"name"]; display_names.push_back(name.UTF8String); }]; return display_names; } }