#ifndef HYPERDRM_PLAYBACK_H #define HYPERDRM_PLAYBACK_H #include #include #include #include typedef struct { void *lib; void *(*parse)(const char *); int (*get)(const void *, const char *, void **); int (*type)(const void *); size_t (*length)(const void *); void *(*at)(const void *, size_t); const char *(*string)(const void *); int (*boolean)(const void *); int (*put)(void *); } PlaybackJson; static inline bool playback_json_init(PlaybackJson *j) { j->lib = dlopen("libjson-c.so.5", RTLD_NOW); if (!j->lib) return false; j->parse = dlsym(j->lib, "json_tokener_parse"); j->get = dlsym(j->lib, "json_object_object_get_ex"); j->type = dlsym(j->lib, "json_object_get_type"); j->length = dlsym(j->lib, "json_object_array_length"); j->at = dlsym(j->lib, "json_object_array_get_idx"); j->string = dlsym(j->lib, "json_object_get_string"); j->boolean = dlsym(j->lib, "json_object_get_boolean"); j->put = dlsym(j->lib, "json_object_put"); if (!j->parse || !j->get || !j->type || !j->length || !j->at || !j->string || !j->boolean || !j->put) { dlclose(j->lib); j->lib = NULL; return false; } return true; } static inline void *playback_field(PlaybackJson *j, void *o, const char *key) { void *v = NULL; return o && j->type(o) == 4 && j->get(o, key, &v) ? v : NULL; } static inline bool playback_true(PlaybackJson *j, void *o, const char *key) { void *v = playback_field(j, o, key); return v && j->type(v) == 1 && j->boolean(v); } static inline bool playback_is(PlaybackJson *j, void *o, const char *key, const char *value) { void *v = playback_field(j, o, key); return v && j->type(v) == 6 && strcmp(j->string(v), value) == 0; } typedef enum { PLAYBACK_UNKNOWN, PLAYBACK_CLEAR, PLAYBACK_PROTECTED } PlaybackProtection; /* G4 resource-state classification, not black-frame or app-name guessing. * Any active Secure Video Path wins. Only a foreground media decoder without * a secure resource is eligible; HDMI, idle and malformed replies are unknown. */ static inline PlaybackProtection playback_classify(PlaybackJson *j, const char *text) { void *root = j->parse(text); if (!root) return PLAYBACK_UNKNOWN; bool clear = false, invalid = j->type(root) != 5, secure = false; size_t n = invalid ? 0 : j->length(root); for (size_t i = 0; i < n; i++) { void *p = j->at(root, i); if (!p || j->type(p) != 4) { invalid = true; continue; } void *resources = playback_field(j, p, "resource"); if (!resources) resources = playback_field(j, p, "resources"); if (!resources || j->type(resources) != 5) { invalid = true; continue; } bool decoder = false; for (size_t k = 0; k < j->length(resources); k++) { void *resource = j->at(resources, k); void *name = playback_field(j, resource, "resource"); if (!name || j->type(name) != 6) { invalid = true; continue; } const char *s = j->string(name); if (strcmp(s, "SVP_CPB") == 0) secure = true; if (strcmp(s, "VDEC") == 0) decoder = true; } if (decoder && playback_true(j, p, "is_foreground") && playback_is(j, p, "type", "media")) clear = true; } j->put(root); return secure ? PLAYBACK_PROTECTED : clear && !invalid ? PLAYBACK_CLEAR : PLAYBACK_UNKNOWN; } #endif