obs-plugin-reviewing

📁 meriley/claude-code-skills 📅 5 days ago
1
总安装量
1
周安装量
#52008
全站排名
安装命令
npx skills add https://github.com/meriley/claude-code-skills --skill obs-plugin-reviewing

Agent 安装分布

amp 1
opencode 1
kimi-cli 1
codex 1
github-copilot 1
claude-code 1

Skill 文档

OBS Plugin Code Review

Purpose

Audit OBS Studio plugins for correctness, performance, memory safety, and thread safety. Covers module registration, callback implementations, resource management, and audio-specific patterns.

When NOT to Use

  • Writing new plugins → Use obs-audio-plugin-writing
  • Getting started → Use obs-plugin-developing
  • Need guidance on which skill → Use obs-plugin-expert agent

Quick Review Checklist

Run these checks in order. Stop at first P0 issue.

P0 – CRITICAL (Must Fix)

Check Pattern Command
Missing module macro No OBS_DECLARE_MODULE() grep -r "OBS_DECLARE_MODULE" src/
Module load returns false return false in load grep -A5 "obs_module_load" src/
Memory leak in destroy Missing bfree() Manual review of destroy callbacks
Blocking in audio callback Mutex/sleep in filter_audio grep -A20 "filter_audio" src/
NULL dereference No channel null check grep -B5 -A10 "audio->data\[" src/

P1 – HIGH (Should Fix)

| Check | Pattern | Command | | ———————- | —————————- | ——————————- | ————- | | Buffer overflow risk | Ignoring audio->frames | grep -A15 "filter_audio" src/ | | Global state | Static variables | grep "^static.*=" src/*.c | | Missing error handling | No NULL checks | Manual review | | Thread safety issues | Shared state without atomics | grep -E "(pthread | mutex)" src/ |

P2 – MEDIUM (Consider Fixing)

Check Pattern Command
Missing defaults No get_defaults callback grep "get_defaults" src/
Hard-coded strings No obs_module_text() grep -E "\"[A-Z][a-z]" src/*.c
Missing locale No en-US.ini ls data/locale/

P3 – LOW (Nice to Have)

| Check | Pattern | Command | | ————— | ———————– | ————— | ————— | | Missing logging | No blog/obs_log calls | grep -E "(blog | obs_log)" src/ | | Code style | Inconsistent formatting | Manual review | | Documentation | Missing comments | Manual review |


Module Registration Review

Required Elements

Every OBS plugin MUST have:

/* 1. Module declaration macro - REQUIRED */
OBS_DECLARE_MODULE()

/* 2. Optional but recommended: locale support */
OBS_MODULE_USE_DEFAULT_LOCALE("plugin-name", "en-US")

/* 3. Module load function - REQUIRED, must return true */
bool obs_module_load(void)
{
    /* Register all sources/outputs/encoders/services */
    obs_register_source(&my_source);
    return true;  /* MUST return true on success */
}

/* 4. Module unload - optional but recommended */
void obs_module_unload(void)
{
    /* Cleanup global resources */
}

Common Module Errors

Error Impact Solution
Missing OBS_DECLARE_MODULE() Plugin won’t load Add macro at file start
obs_module_load returns false Plugin fails silently Return true on success
No unload cleanup Resource leak Implement obs_module_unload
Wrong locale path Strings not translated Use data/locale/en-US.ini

Grep Commands for Module Review

# Check for module macro
grep -l "OBS_DECLARE_MODULE" src/*.c

# Check module_load return value
grep -A10 "obs_module_load" src/*.c | grep -E "(return|false|true)"

# Check for source registration
grep "obs_register_source" src/*.c

# Check for locale setup
grep "OBS_MODULE_USE_DEFAULT_LOCALE" src/*.c

Memory Safety Review

Allocation/Deallocation Pattern

OBS uses custom memory functions:

OBS Function Standard Equivalent Purpose
bzalloc(size) calloc(1, size) Zero-initialized allocation
bfree(ptr) free(ptr) Free memory
bmalloc(size) malloc(size) Raw allocation
brealloc(ptr, size) realloc(ptr, size) Resize allocation

Memory Safety Checklist

□ Every bzalloc/bmalloc has matching bfree in destroy callback
□ Context struct freed in destroy callback
□ No memory allocation in audio/video callbacks
□ Pre-allocated buffers for real-time processing
□ Reference counting handled correctly (obs_source_addref/release)

Common Memory Errors

/* BAD: Memory leak - no bfree in destroy */
static void *my_create(obs_data_t *settings, obs_source_t *source)
{
    struct my_data *ctx = bzalloc(sizeof(*ctx));
    ctx->buffer = bmalloc(4096);  /* Also needs freeing! */
    return ctx;
}

static void my_destroy(void *data)
{
    /* WRONG: Missing bfree calls! */
}

/* GOOD: Proper cleanup */
static void my_destroy(void *data)
{
    struct my_data *ctx = data;
    if (ctx) {
        bfree(ctx->buffer);  /* Free nested allocations first */
        bfree(ctx);          /* Free context last */
    }
}

Grep Commands for Memory Review

# Find all allocations
grep -n "bzalloc\|bmalloc\|brealloc" src/*.c

# Find all deallocations
grep -n "bfree" src/*.c

# Check destroy callbacks
grep -A20 "_destroy\|\.destroy" src/*.c

# Find potential leaks (alloc without corresponding free)
# Manual comparison of above results

Thread Safety Review

OBS Threading Model

Thread Callbacks Rules
Main Thread create, destroy, update, get_properties, get_defaults Safe to block, allocate memory
Audio Thread filter_audio NEVER block, no allocations
Video Thread video_render, video_tick Minimize blocking

Audio Thread Rules (CRITICAL)

The filter_audio callback runs on the audio thread:

/* FORBIDDEN in filter_audio: */
pthread_mutex_lock(&mutex);  /* NEVER: Can cause audio glitches */
sleep(1);                    /* NEVER: Blocks audio processing */
bmalloc(size);              /* NEVER: Allocation can block */
obs_data_get_*();           /* NEVER: Settings access can lock */
fopen();                    /* NEVER: I/O blocks */

/* ALLOWED in filter_audio: */
atomic_load(&value);         /* OK: Lock-free */
ctx->cached_value;           /* OK: Pre-computed in update() */
math operations;             /* OK: CPU-bound is fine */

Safe Configuration Updates

/* Pattern: Update on main thread, read atomically on audio thread */
#include <stdatomic.h>

struct filter_data {
    atomic_int gain_scaled;  /* Atomic for thread-safe reads */
};

/* Main thread: update callback */
static void filter_update(void *data, obs_data_t *settings)
{
    struct filter_data *ctx = data;
    double db = obs_data_get_double(settings, "gain_db");
    int scaled = (int)(db_to_mul((float)db) * 1000);
    atomic_store(&ctx->gain_scaled, scaled);
}

/* Audio thread: filter_audio callback */
static struct obs_audio_data *filter_audio(void *data,
                                           struct obs_audio_data *audio)
{
    struct filter_data *ctx = data;
    float gain = atomic_load(&ctx->gain_scaled) / 1000.0f;
    /* ... process with gain ... */
    return audio;
}

Grep Commands for Thread Safety

# Find mutex usage in audio callbacks
grep -B5 -A20 "filter_audio" src/*.c | grep -E "(mutex|lock|pthread)"

# Find memory allocation in audio callbacks
grep -B5 -A20 "filter_audio" src/*.c | grep -E "(malloc|alloc|bfree)"

# Find I/O in audio callbacks
grep -B5 -A20 "filter_audio" src/*.c | grep -E "(fopen|fread|fwrite)"

# Check for atomic usage
grep -n "atomic\|_Atomic" src/*.c

Callback Correctness Review

Required Callbacks

Callback Required? Purpose
.id YES Unique identifier string
.type YES Source type enum
.output_flags YES Capability flags
.get_name YES Display name function
.create YES Instance creation
.destroy YES Instance cleanup
.update Recommended Settings application
.get_defaults Recommended Default values
.get_properties Recommended UI generation
.filter_audio For audio filters Audio processing

Audio Filter Callback Review

/* Review this pattern in filter_audio: */
static struct obs_audio_data *filter_audio(void *data,
                                           struct obs_audio_data *audio)
{
    struct filter_data *ctx = data;
    float **adata = (float **)audio->data;

    /* CHECK 1: Are all channels processed? */
    for (size_t c = 0; c < ctx->channels; c++) {
        /* CHECK 2: Is NULL channel check present? */
        if (!audio->data[c])  /* REQUIRED: Some channels may be NULL */
            continue;

        /* CHECK 3: Is frames count used correctly? */
        for (size_t i = 0; i < audio->frames; i++) {
            adata[c][i] *= ctx->gain;
        }
    }

    /* CHECK 4: Is audio returned (not NULL)? */
    return audio;
}

Callback Review Checklist

□ filter_audio checks audio->data[c] for NULL before access
□ filter_audio uses audio->frames for loop bounds (not hardcoded)
□ filter_audio returns the audio pointer
□ update callback converts dB to linear (if applicable)
□ destroy callback frees all allocated memory
□ create callback initializes all context fields
□ get_properties creates all needed UI elements
□ get_defaults sets sensible default values

Settings Validation Review

Settings API Patterns

/* GOOD: Proper settings handling */
static void filter_update(void *data, obs_data_t *settings)
{
    struct filter_data *ctx = data;

    /* Get values with type checking */
    double gain_db = obs_data_get_double(settings, "gain_db");
    bool enabled = obs_data_get_bool(settings, "enabled");

    /* Validate ranges */
    if (gain_db < -60.0) gain_db = -60.0;
    if (gain_db > 30.0) gain_db = 30.0;

    /* Convert and store */
    ctx->gain = db_to_mul((float)gain_db);
    ctx->enabled = enabled;
}

/* GOOD: Proper defaults */
static void filter_defaults(obs_data_t *settings)
{
    obs_data_set_default_double(settings, "gain_db", 0.0);
    obs_data_set_default_bool(settings, "enabled", true);
}

Common Settings Errors

Error Problem Solution
No range validation Values out of bounds Check min/max in update
Wrong type function Data corruption Match obsdata_get* to data type
No defaults Unpredictable behavior Implement get_defaults
Hardcoded defaults Maintenance burden Use get_defaults callback

Build System Review

Required Build Files

project/
├── CMakeLists.txt      # Build configuration
├── buildspec.json      # Plugin metadata
├── src/
│   ├── plugin-main.c   # Module entry point
│   └── *.c             # Source files
└── data/
    └── locale/
        └── en-US.ini   # Localization strings

CMakeLists.txt Review

# Required: Minimum CMake version
cmake_minimum_required(VERSION 3.28...3.30)

# Required: Project name and version
project(my-plugin VERSION 1.0.0)

# Required: Create module library
add_library(${CMAKE_PROJECT_NAME} MODULE)

# Required: Link libobs
find_package(libobs REQUIRED)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::libobs)

# Required: Add source files
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    src/plugin-main.c
    src/my-filter.c
)

Build Review Checklist

□ CMakeLists.txt uses cmake_minimum_required(VERSION 3.28)
□ add_library uses MODULE keyword
□ find_package(libobs REQUIRED) is present
□ target_link_libraries links OBS::libobs
□ All source files are listed in target_sources
□ buildspec.json has valid name and version
□ data/locale/en-US.ini exists with all strings

Automated Audit Script

Run this script to check for common issues:

#!/bin/bash
# obs-plugin-audit.sh

echo "=== OBS Plugin Audit ==="
echo ""

# P0: Critical checks
echo "P0 - CRITICAL:"
echo "  Module macro:"
grep -l "OBS_DECLARE_MODULE" src/*.c || echo "    MISSING: OBS_DECLARE_MODULE not found!"

echo "  Module load:"
grep -A5 "obs_module_load" src/*.c | grep -q "return true" && echo "    OK" || echo "    WARNING: Check return value"

echo "  Destroy callbacks:"
grep -l "bfree" src/*.c || echo "    WARNING: No bfree calls found"

# P1: High priority
echo ""
echo "P1 - HIGH:"
echo "  Static variables:"
grep -c "^static.*=" src/*.c | while read line; do echo "    $line"; done

echo "  Audio thread safety:"
grep -A20 "filter_audio" src/*.c | grep -E "(mutex|lock|malloc)" && echo "    WARNING: Potential blocking in audio thread"

# P2: Medium priority
echo ""
echo "P2 - MEDIUM:"
echo "  Localization:"
ls data/locale/*.ini 2>/dev/null || echo "    WARNING: No locale files found"

echo "  Defaults callback:"
grep -l "get_defaults" src/*.c || echo "    WARNING: No get_defaults found"

echo ""
echo "=== Audit Complete ==="

Related Files

  • CHECKLIST.md – Quick reference review checklist

Related Skills

  • obs-plugin-developing – Entry point and overview
  • obs-audio-plugin-writing – Audio plugin development patterns

Related Agent

Use obs-plugin-expert agent for:

  • Coordinated development and review guidance
  • Choosing between skills based on task
  • Complex plugin development workflows