摄像头子系统(CategoryCamera)
SDL 库提供的视频采集功能,专门用于读取摄像头(如网络摄像头)等视频源的输入数据。
通过这组 API,应用可实现以下功能:
- 枚举、查询系统中的摄像头设备;
- 打开摄像头并获取逐帧视频数据(以
SDL_Surface形式返回); - 将视频帧上传至
SDL_Texture渲染,或直接处理内存中的像素数据。
权限说明
多数平台会对摄像头访问做权限管控:
- 应用首次访问摄像头时,系统会弹出 UI 向用户请求授权;
- 授权结果可通过两种方式获取:
- 主动查询:调用
SDL_GetCameraPermissionState(); - 事件监听:等待
SDL_EVENT_CAMERA_DEVICE_APPROVED(授权通过)或SDL_EVENT_CAMERA_DEVICE_DENIED(授权拒绝)事件;
- 主动查询:调用
- 无权限管控的平台会立即返回「授权通过」状态,摄像头打开后即可获取帧数据。
功能限制
- SDL 摄像头仅提供原始视频帧,不输出编码后的视频文件(应用可自行将帧编码为任意格式);
- 该 API 不提供摄像头硬件的音频数据:
- 多数摄像头无麦克风;
- 即使有,直播/视频通话等场景通常也需独立麦克风;
- 音频采集需通过 SDL 音频 API 实现,与摄像头硬件无关。
摄像头使用注意事项
消费级摄像头设备打开后需要「预热时间」:
- 刚打开的摄像头可能先输出几帧全黑画面,或曝光不足的图像;
- 若需自动抓拍单帧、或无预览直接录制视频,建议丢弃前几帧(甚至前几秒)的数据,再使用有效帧。
函数
- SDL_AcquireCameraFrame:获取摄像头的最新视频帧(返回 SDL_Surface 指针,用完需调用 Release 释放)
- SDL_CloseCamera:关闭已打开的摄像头设备(释放摄像头资源)
- SDL_GetCameraDriver:获取指定索引的摄像头驱动名称(如 "uvc" 表示 USB 视频类驱动)
- SDL_GetCameraFormat:获取摄像头当前使用的视频格式(分辨率、像素格式、帧率等)
- SDL_GetCameraID:获取摄像头设备的唯一标识 ID(SDL_CameraID 类型)
- SDL_GetCameraName:获取摄像头设备的友好名称(如 "USB Camera (046d:0825)")
- SDL_GetCameraPermissionState:查询摄像头的权限状态(授权中/已授权/已拒绝,参考 SDL_CameraPermissionState)
- SDL_GetCameraPosition:获取摄像头的物理位置(前置/后置/未指定,参考 SDL_CameraPosition)
- SDL_GetCameraProperties:获取摄像头的详细属性(如支持的最大分辨率、是否自动对焦等)
- SDL_GetCameras:枚举系统中的所有摄像头设备(返回设备列表及数量)
- SDL_GetCameraSupportedFormats:获取摄像头支持的所有视频格式(分辨率、帧率、像素格式组合)
- SDL_GetCurrentCameraDriver:获取当前正在使用的摄像头驱动名称
- SDL_GetNumCameraDrivers:获取系统中可用的摄像头驱动数量
- SDL_OpenCamera:打开指定的摄像头设备(传入设备 ID 和期望的视频格式,返回 SDL_Camera 句柄)
- SDL_ReleaseCameraFrame:释放已获取的摄像头视频帧(与 Acquire 成对使用,避免内存泄漏)
数据类型
- SDL_Camera:摄像头设备句柄类型(标识已打开的摄像头)
- SDL_CameraID:摄像头设备唯一标识类型(用于区分不同摄像头)
结构体
- SDL_CameraSpec:摄像头规格结构体(指定期望的分辨率、帧率、像素格式等参数)
枚举
- SDL_CameraPermissionState:摄像头权限状态枚举(SDL_CAMERA_PERMISSION_UNKNOWN/GRANTED/DENIED/PENDING)
- SDL_CameraPosition:摄像头物理位置枚举(SDL_CAMERA_POSITION_UNKNOWN/FRONT/BACK)
宏
- (无)
FreeBASIC 示例代码
' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"
' 错误处理辅助函数
Sub CheckError(ByVal msg As String)
Dim As ZString Ptr err = SDL_GetError()
If (*err <> 0) Then
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "%s:%s", msg, err)
SDL_ClearError()
End 1
End If
End Sub
' 主程序
Dim As SDL_CameraID Ptr cameraIDs = NULL
Dim As Integer cameraCount = 0
Dim As SDL_Camera Ptr camera = NULL
Dim As SDL_CameraSpec cameraSpec
Dim As SDL_Surface Ptr frame = NULL
Dim As SDL_Window Ptr window = NULL
Dim As SDL_Renderer Ptr renderer = NULL
Dim As SDL_Texture Ptr texture = NULL
Dim As SDL_Rect dstRect
Dim As SDL_CameraPermissionState permState
Dim As Integer quit = 0, frameCount = 0
Dim As SDL_Event evt
' 1. 初始化 SDL(需启用视频子系统)
If (SDL_Init(SDL_INIT_VIDEO) < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "SDL 初始化失败:%s", SDL_GetError())
End 1
End If
' 2. 枚举系统中的摄像头设备
cameraCount = SDL_GetCameras(0, NULL)
If (cameraCount <= 0) Then
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "未检测到摄像头设备")
SDL_Quit()
End 1
End If
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "检测到 %d 个摄像头设备", cameraCount)
' 分配内存并获取摄像头 ID 列表
cameraIDs = Callocate(cameraCount * SizeOf(SDL_CameraID))
cameraCount = SDL_GetCameras(cameraCount, cameraIDs)
For i As Integer = 0 To cameraCount - 1
Dim As ZString Ptr camName = SDL_GetCameraName(cameraIDs[i])
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "摄像头 %d:%s", i, camName)
SDL_free(camName) ' 释放名称字符串
Next
' 3. 配置摄像头规格(期望 640x480,30fps,RGBA8888 格式)
cameraSpec.w = 640
cameraSpec.h = 480
cameraSpec.fps = 30
cameraSpec.format = SDL_PIXELFORMAT_RGBA8888
' 4. 打开第一个摄像头
camera = SDL_OpenCamera(cameraIDs[0], @cameraSpec)
If (camera = NULL) Then
CheckError("打开摄像头失败")
GoTo cleanup
End If
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "摄像头已打开,等待权限授权...")
' 5. 等待摄像头权限授权(循环查询直到授权完成)
Do
permState = SDL_GetCameraPermissionState(camera)
Select Case permState
Case SDL_CAMERA_PERMISSION_GRANTED
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "摄像头权限已授权")
Exit Do
Case SDL_CAMERA_PERMISSION_DENIED
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "摄像头权限被拒绝")
GoTo cleanup
Case SDL_CAMERA_PERMISSION_PENDING
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "权限请求中...")
SDL_Delay(500)
Case Else
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "权限状态未知,默认授权")
Exit Do
End Select
Loop
' 6. 创建窗口和渲染器(用于显示摄像头画面)
window = SDL_CreateWindow("SDL 摄像头示例", _
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _
640, 480, SDL_WINDOW_SHOWN)
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED)
If (window = NULL Or renderer = NULL) Then
CheckError("创建窗口/渲染器失败")
GoTo cleanup
End If
dstRect = Type<SDL_Rect>(0, 0, 640, 480)
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "=== 摄像头预览开始 ===")
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "按 ESC 退出 | 摄像头预热中,前几帧可能黑屏")
' 7. 主循环:获取并渲染摄像头帧
While (quit = 0)
' 处理事件
While (SDL_PollEvent(@evt))
Select Case evt.type
Case SDL_QUITEVENT
quit = 1
Case SDL_KEYDOWN
If (evt.key.keysym.sym = SDLK_ESCAPE) Then
quit = 1
End If
' 监听摄像头权限事件(可选)
Case SDL_EVENT_CAMERA_DEVICE_APPROVED
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "收到摄像头授权通过事件")
Case SDL_EVENT_CAMERA_DEVICE_DENIED
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "收到摄像头授权拒绝事件")
quit = 1
End Select
Wend
' 获取摄像头帧(非阻塞)
frame = SDL_AcquireCameraFrame(camera)
If (frame <> NULL) Then
frameCount += 1
' 跳过前 10 帧预热数据
If (frameCount > 10) Then
' 创建/更新纹理
If (texture = NULL) Then
texture = SDL_CreateTextureFromSurface(renderer, frame)
Else
SDL_UpdateTexture(texture, NULL, frame->pixels, frame->pitch)
End If
' 渲染纹理
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255)
SDL_RenderClear(renderer)
SDL_RenderTexture(renderer, texture, NULL, @dstRect)
SDL_RenderPresent(renderer)
End If
' 释放帧(必须调用,否则摄像头会阻塞)
SDL_ReleaseCameraFrame(camera, frame)
End If
SDL_Delay(10) ' 控制循环频率
Wend
' 8. 清理资源
cleanup:
' 释放纹理/渲染器/窗口
If (texture <> NULL) Then SDL_DestroyTexture(texture)
If (renderer <> NULL) Then SDL_DestroyRenderer(renderer)
If (window <> NULL) Then SDL_DestroyWindow(window)
' 关闭摄像头
If (camera <> NULL) Then
SDL_CloseCamera(camera)
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "摄像头已关闭")
End If
' 释放摄像头 ID 列表
If (cameraIDs <> NULL) Then
Deallocate(cameraIDs)
End If
' 退出 SDL
SDL_Quit()
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "程序正常退出,共渲染 %d 帧", frameCount - 10)
End 0
总结
- SDL 摄像头子系统核心流程:
SDL_GetCameras枚举设备 →SDL_OpenCamera打开设备 →SDL_AcquireCameraFrame获取帧 →SDL_ReleaseCameraFrame释放帧; - FreeBASIC 中使用时需注意:
- 必须处理权限状态(
SDL_GetCameraPermissionState或监听授权事件); - 摄像头帧需「获取-释放」成对使用,且建议跳过前几帧预热数据;
- 帧数据(SDL_Surface)可转换为 SDL_Texture 渲染,或直接处理像素;
- 必须处理权限状态(
- 关键限制:仅输出原始视频帧,无音频/编码功能,音频需通过 SDL 音频 API 单独采集。
评论一下?