From 5e535bc2eb4e57a42c3da57b90b465644f292d16 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 24 Aug 2019 23:25:06 -0400 Subject: [PATCH] Added support for grcd. --- nx/include/switch/services/grc.h | 34 ++++++++++ nx/source/services/grc.c | 108 +++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/nx/include/switch/services/grc.h b/nx/include/switch/services/grc.h index 91deb3d3..e09c2a3d 100644 --- a/nx/include/switch/services/grc.h +++ b/nx/include/switch/services/grc.h @@ -23,6 +23,14 @@ typedef struct { u8 reserved[0x28]; ///< Unused, always zero. } GrcGameMovieId; +/// Stream type values for \ref grcdRead. +typedef enum { + GrcStream_Video = 0, ///< Video stream with H.264 NAL units. Official sw uses buffer size 0x32000. + GrcStream_Audio = 1, ///< Audio stream with PcmFormat_Int16, 2 channels, and samplerate = 48000Hz. Official sw uses buffer size 0x1000. +} GrcStream; + +// Trimming + /** * @brief Creates a \ref GrcGameMovieTrimmer using \ref appletCreateGameMovieTrimmer, uses the cmds from it to trim the specified video, then closes it. * @note See \ref appletCreateGameMovieTrimmer for the requirements for using this. @@ -36,3 +44,29 @@ typedef struct { */ Result grcTrimGameMovie(GrcGameMovieId *dst_movieid, const GrcGameMovieId *src_movieid, size_t tmem_size, const void* thumbnail, s32 start, s32 end); +// grc:d + +/// Initialize grc:d. +Result grcdInitialize(void); + +/// Exit grc:d. +void grcdExit(void); + +/// Gets the Service for grc:d. +Service* grcdGetServiceSession(void); + +/// Begins streaming. This must not be called more than once, even from a different service session: otherwise the sysmodule will assert. +Result grcdBegin(void); + +/** + * @brief Reads a stream, from the video recording being done of the currently running game title. + * @note This will block until data is available. This will hang if there is no game title running which has video capture enabled. + * @param[in] stream \ref GrcStream + * @param[out] buffer Output buffer. + * @param[in] size Max size of the output buffer. + * @param[out] unk Unknown. + * @param[out] data_size Actual output data size. + * @param[out] timestamp Timestamp? + */ +Result grcdRead(GrcStream stream, void* buffer, size_t size, u32 *unk, u32 *data_size, u64 *timestamp); + diff --git a/nx/source/services/grc.c b/nx/source/services/grc.c index 3c0d094a..d788e225 100644 --- a/nx/source/services/grc.c +++ b/nx/source/services/grc.c @@ -12,6 +12,38 @@ static void _grcGameMovieTrimmerClose(GrcGameMovieTrimmer *t); +static Result _grcCmdNoIO(Service* srv, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + static Result _grcGetEvent(Service* srv, Event* out_event, u64 cmd_id, bool autoclear) { IpcCommand c; ipcInitialize(&c); @@ -228,3 +260,79 @@ Result grcTrimGameMovie(GrcGameMovieId *dst_movieid, const GrcGameMovieId *src_m return rc; } +// grc:d + +static Service g_grcdSrv; +static u64 g_grcdRefCnt; + +Result grcdInitialize(void) { + atomicIncrement64(&g_grcdRefCnt); + + if (serviceIsActive(&g_grcdSrv)) + return 0; + + Result rc = smGetService(&g_grcdSrv, "grc:d"); + + if (R_FAILED(rc)) grcdExit(); + + return rc; +} + +void grcdExit(void) { + if (atomicDecrement64(&g_grcdRefCnt) == 0) + serviceClose(&g_grcdSrv); +} + +Service* grcdGetServiceSession(void) { + return &g_grcdSrv; +} + +Result grcdBegin(void) { + return _grcCmdNoIO(&g_grcdSrv, 1); +} + +Result grcdRead(GrcStream stream, void* buffer, size_t size, u32 *unk, u32 *data_size, u64 *timestamp) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddRecvBuffer(&c, buffer, size, BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u32 stream; + } *raw; + + raw = serviceIpcPrepareHeader(&g_grcdSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + raw->stream = stream; + + Result rc = serviceIpcDispatch(&g_grcdSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 unk; + u32 data_size; + u64 timestamp; + } *resp; + + serviceIpcParse(&g_grcdSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + if (unk) *unk = resp->unk; + if (data_size) *data_size = resp->data_size; + if (timestamp) *timestamp = resp->timestamp; + } + } + + return rc; +} +