linux学习:多媒体开发库SDL+视频、音频、事件子系统+处理yuv视频源

本文主要是介绍linux学习:多媒体开发库SDL+视频、音频、事件子系统+处理yuv视频源,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

编译和移植

视频子系统

视频子系统产生图像的步骤

api

初始化 SDL 的相关子系统

使用指定的宽、高和色深来创建一个视窗 surface

使用 fmt 指定的格式创建一个像素点​编辑

将 dst 上的矩形 dstrect 填充为单色 color​编辑

将 src 快速叠加到 dst 上​编辑

更新 screen 上的图像元素​编辑

api例子

音频子系统

SDL 中默认支持的对 wav 格式 的音频文件的 API

存放音频数据的具体信息

加载 wav 格式的音频文件​编辑

启动音频设备​编辑

暂停或者继续​编辑

api例子

事件子系统

联合体

使用鼠标的实例

处理YUV视频源


sdl是一个跨平台的底层开发库,提供操作诸如音频、 键盘、鼠标、游戏杆以及显卡等硬件的方法,被很多多媒体播放器、模拟器和流行游戏所使 用,SDL 支持 Windows、MacOS、Linux、iOS 以及 Android,也就是说你目所能及的 几乎所有平台它都能运行,并且 SDL 是开源的,完全由 C 语言编写,

编译和移植

  1. 下载:http://www.libsdl.org/release/SDL-1.2.15.tar.gz
  2. 解压进入根目录
  3. ./configure --host=arm-none-Linux-gnueabi --prefix=/usr/local/sdl
    1. 注意,--host 是指定交叉编译器的前缀,--prefix 是指定 SDL 的安装目录,两者都要根 据你的具体情况来写,不必照抄
  4. make
  5. make install
  6. 将编译后的目录/usr/local/sdl 全部拷贝到开发板中,设置好库目录的环境变量
    1. 将此目录拷贝到开发板的/usr/local/sdl 中就设置export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/sdl/lib

视频子系统

在屏幕的显示能力,当我们需要显示图片、文字的时候,那就必须使用视频子系统

支持设置视频模式,即创建视频窗口,也支持直接的图像帧缓冲、 支持 Alpha 像素混合、支持窗口管理和图形渲染等

视频子系统产生图像的步骤

  • 初始化 SDL 视频子系统
  • 设置视频模式(包括宽高、色深等),并创建得到视窗 surface
  • 加载一张图像,获得该图像的 surface
  • 将图像 surface“放到”视窗 surface 上,同时可以设置你要放置的位置
  • 更新视窗 surface,使得图像可

api

初始化 SDL 的相关子系统

使用指定的宽、高和色深来创建一个视窗 surface

使用 fmt 指定的格式创建一个像素点

将 dst 上的矩形 dstrect 填充为单色 color

将 src 快速叠加到 dst 上

更新 screen 上的图像元素

api例子

使用以上 API,结合触摸屏运行库 tslib 来实现移动图片的效果

// 初始化 SDL 视频子系统,并设置视窗 surface 的参数(与 LCD 一致)
SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(LCD_WIDTH, LCD_HEIGHT, 0, SDL_ANYFORMAT | SDL_SWSURFACE);// 装载 BMP 图片文件 SDL 表示没压力
image = SDL_LoadBMP(argv[1]);// 1, image_offset 规定了图片要显示的矩形部分
// 2, background_offset 规定了图像要显示在视窗的那个位置,其中:
SDL_Rect image_offset;
SDL_Rect backgroud_offset;
bzero(&image_offset, sizeof(image_offset));
bzero(&backgroud_offset, sizeof(backgroud_offset));
printf("press Ctrl+c to quit.\n");
sem_init(&s, 0, 0);pthread_t tid;
pthread_create(&tid, NULL, read_moving, NULL);// 1, x 和 y 规定了图像要显示的矩形的左上角坐标
// 2, w 和 h 规定了以(x,y)为左上角的矩形的宽和高
image_offset.x = 0;
image_offset.y = 0;
image_offset.w = 400;
image_offset.h = 240;while (1) {// 产生一个 RGB 值为 000(黑色)的像素uint32_t black_pixel = SDL_MapRGB(screen->format, 0, 0, 0);// 将屏幕刷成黑色SDL_FillRect(screen, &screen->clip_rect, black_pixel);// 将图像(image)blit 到屏幕上(screen)long tmp1 = backgroud_offset.x;long tmp2 = backgroud_offset.y;SDL_BlitSurface(image, &image_offset, screen, &backgr_offset);// 显示 screen 上的元素SDL_Flip(screen);// 1,x 和 y 规定了图像 surface 放在视窗的左上角坐标// 2,w 和 h 都是作废的。backgroud_offset.x = tmp1 + xoffset;backgroud_offset.y = tmp2 + yoffset;printf("backgroud_offset.x: %d\n", backgroud_offset.x);printf("backgroud_offset.y: %d\n", backgroud_offset.y);sem_wait(&s);
}
  • 要渲染视频流需要结合 FFmpeg 来做

  • 图片不是 bmp 格式的,比如 jpeg、png、tiff 等,需要使用第三方扩展库 SDL_image

音频子系统

SDL 中默认支持的对 wav 格式 的音频文件的 API

存放音频数据的具体信息

加载 wav 格式的音频文件

启动音频设备

暂停或者继续

api例子

#include <stdio.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_audio.h"
#include "SDL_config.h"struct wave
{SDL_AudioSpec spec;Uint8 *sound; /* 音频数据缓冲区指针 */Uint32 soundlen; /* 音频数据尺寸 */int soundpos; /* 已处理数据大小 */
} wave;// 画进度条
void draw_progress_bar(int left, int len)
{int i;for (i = 0; i < 20; i++)printf("\b");printf("[");int n = ((1 - (float)left / len) * 100) / 10;for (i = 0; i <= n; i++)printf("-");printf(">");for (i = 0; i < 9 - n; i++)printf(" ");printf("] %.1f%%", (1 - (float)left / len) * 100);fflush(stdout);
}// 音频解码回调函数
void deal_audio(void *unused, Uint8 *stream, int len)
{Uint8 *waveptr;int waveleft;waveptr = wave.sound + wave.soundpos;waveleft = wave.soundlen - wave.soundpos;while (waveleft <= len){memcpy(stream, waveptr, waveleft);stream += waveleft;len -= waveleft;waveptr = wave.sound;waveleft = wave.soundlen;wave.soundpos = 0;printf("\n");SDL_CloseAudio();SDL_FreeWAV(wave.sound);SDL_Quit();exit(0);}memcpy(stream, waveptr, len);wave.soundpos += len;draw_progress_bar(waveleft, wave.soundlen);
}int main(int argc, char *argv[])
{// 初始化音频子系统SDL_Init(SDL_INIT_AUDIO);// 加载 WAV 文件SDL_LoadWAV(argv[1], &wave.spec, &wave.sound, &wave.soundlen);// 指定音频数据处理回调函数wave.spec.callback = deal_audio;// 启动音频设备SDL_OpenAudio(&wave.spec, NULL);SDL_PauseAudio(0);printf("\npress Enter to pause and unpause.\n");static int pause_on = 1;while (1){// 按下回车键,暂停或播放getchar();SDL_PauseAudio(pause_on++);if (!(pause_on %= 2))printf("stopped.\n");}return 0;
}

SDL 利用函数 SDL_LoadWAV()将音频数据加载到一个缓冲区中,然后 通过结构体 SDL_AudioSpec 中的 callback 指定回调函数 deal_audio(),该函数会在音 频设备准备好要读取数据的时候被自动调用。 然后,调用 SDL_OpenAudio()启动音频设备,并且调用 SDL_PauseAudio(0)来使 得启动整个流程,此时只要音频设备准备好了,需要数据的时候,就会自动调用 deal_audio 这个函数。回调函数 deal_audio()就像一个搬运工,一旦音频设备准备好可以读取数据了, 他就将音频数据源源不断地搬到音频设备上去播放

音频文件不是 wav 格式的,比如 MP3,MIDI,OGG,MOD 这些,就需要用到 SDL 的第三方扩展库 SDL_Mixer。

事件子系统

  • SDL的事件允许程序接收从用户输入的信息,当调用SDL_Init(SDL_INIT_VIDEO)初始化视频子系统时,事件子系统将被连带自动初始化。
  • 本质上所有的事件都将被SDL置入一个所谓的“等待队列”中,我们可以使用诸如SDL_PollEvent()或者 SDL_WaitEvent()或者 SDL_PeepEvent()来处理或者检查当前正在等待的事件。
  • SDL中处理事件的关键核心,是一个叫SDL_Event的联合体,事实上“等待队列”中储存的就是这些联合体,SDL_PollEvent()或者SDL_WaitEvent()讲这些联合体从队列中读出,然后根据其中的信息作出相应的处理

联合体

囊括了SDL-1.2版本所支持的所有事件

  • type事件的类型
  • active事件触发
  • key键盘
  • motion鼠标移动
  • button鼠标按键
  • jaxis游戏杆摇杆
  • jball游戏杆轨迹球
  • jhatJoystick游戏杆帽
  • jbutton游戏杆按键
  • resize窗口大小变更
  • expose窗口焦点变更
  • quit退出
  • user用户自定义事件
  • syswm未定义窗口管理事件

使用鼠标的实例

功能

  • 使用鼠标左键点击向左小箭头,显示上一张图片
  • 使用鼠标左键点击向右小箭头,显示下一张图片
  • 使用鼠标右键退出程序

代码

#include <SDL.h>
#include <stdio.h>
#include <stdbool.h>#define WIDTH 800
#define HEIGHT 480
#define BPP 32SDL_Surface* screen;
SDL_Surface* image;
SDL_Surface* left, *right;SDL_Surface* load_image(const char* filename) {return SDL_DisplayFormat(SDL_LoadBMP(filename));
}void show_bmp(const char* filename) {// Fill the screen with black coloruint32_t black_pixel = SDL_MapRGB(screen->format, 0, 0, 0);SDL_FillRect(screen, &screen->clip_rect, black_pixel);// Load the imageimage = load_image(filename);// Set the position of the imagestatic SDL_Rect rect = {0, 0};SDL_BlitSurface(image, NULL, screen, &rect);// Set the positions of the left and right arrowsstatic SDL_Rect left_pos = {100, 200};static SDL_Rect right_pos = {700, 200};SDL_BlitSurface(left, NULL, screen, &left_pos);SDL_BlitSurface(right, NULL, screen, &right_pos);// Refresh the screen to display the imageSDL_Flip(screen);
}int main(int argc, char const* argv[]) {if (argc != 2) {printf("Usage: %s <bmp_directories>\n", argv[0]);exit(0);}SDL_Init(SDL_INIT_EVERYTHING);screen = SDL_SetVideoMode(WIDTH, HEIGHT, BPP, SDL_SWSURFACE);const char* bmp_files[] = {"1.bmp", "2.bmp", "3.bmp", "4.bmp"};chdir(argv[1]);left = load_image("left.bmp");right = load_image("right.bmp");// Blit the image onto the screenSDL_Rect rect = {0, 0};SDL_BlitSurface(image, NULL, screen, &rect);SDL_Rect left_pos = {100, 200};SDL_Rect right_pos = {700, 200};// Set white color as transparentint32_t key = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff);SDL_SetColorKey(left, SDL_SRCCOLORKEY, key);SDL_SetColorKey(right, SDL_SRCCOLORKEY, key);// Display the first imageshow_bmp(bmp_files[0]);// Block and wait for mouse clickint i = 0;SDL_Event event;while (1) {SDL_WaitEvent(&event);// Switch to the previous image when the left arrow is clickedif (event.button.type == SDL_MOUSEBUTTONUP &&event.button.button == SDL_BUTTON_LEFT &&event.button.y >= 200 &&event.button.y <= 287) {if (event.button.x >= 100 && event.button.x <= 160) {i = (i == 0) ? 3 : (i - 1);}if (event.button.x >= 700 && event.button.x <= 760) {i = (i + 1) % 4;}// Release the current image resources before displaying another imageSDL_FreeSurface(image);show_bmp(bmp_files[i]);}// Quit the program when the right mouse button is clickedif (event.button.type == SDL_MOUSEBUTTONUP &&event.button.button == SDL_BUTTON_RIGHT) {break;}}return 0;
}

处理YUV视频源

使用V4L2接口获取摄像头数据(YUV格式),然后使用SDL将视频数据显示到LCD显示器上

步骤

  • 1.准备好LCD,设置好相应的参数,备用
  • 2.准备好摄像头,设置好采集格式等参数,备用
  • 3.初始化SDL,并创建YUV层,备用
  • 4.启动摄像头,开始捕获YUV数据,并将数据丢给SDL的YUV处理层显示

具体代码

  • 1
    • 打开LCD设备
    • 获取LCD显示器的设备参数
    • 申请一块适当跟LCD尺寸一样大小的显存
  • 2.
    • 打开摄像头设备文件
    • 配置摄像头的采集格式
    • 设置即将要申请的摄像头缓存的参数
    • 使用该参数reqbuf来申请缓存
    • 根据刚刚设置的reqbuf.count的值,来定义相应数量的struct v4l2_buffer
  • 3.
    • 初始化带音视频和定时器子系统的SDL
    • 创建基本surface
    • 创建一个YUYV格式的surface
  • 4.
    • 启动摄像头
    • 准备好应用层缓冲区参数
    • 开始捕获采集数据
    • 将数据丢给SDL处理
#include <SDL.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <linux/videodev2.h>#define LCD_WIDTH 800
#define LCD_HEIGHT 480void 打开LCD并分配显存(unsigned int **fb_mem, struct fb_var_screeninfo *lcdinfo) {int lcd = open("/dev/fb0", O_RDWR);ioctl(lcd, FBIOGET_VSCREENINFO, lcdinfo);*fb_mem = mmap(NULL, lcdinfo->xres * lcdinfo->yres * lcdinfo->bits_per_pixel / 8,PROT_READ | PROT_WRITE, MAP_SHARED, lcd, 0);
}void 打开摄像头并配置(int *cam_fd, struct v4l2_format *fmt, int *nbuf, struct v4l2_requestbuffers *reqbuf,struct v4l2_buffer *buffer, unsigned char **start) {*cam_fd = open("/dev/video3", O_RDWR);bzero(fmt, sizeof(*fmt));fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt->fmt.pix.width = LCD_WIDTH;fmt->fmt.pix.height = LCD_HEIGHT;fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // YUYV 格式fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;ioctl(*cam_fd, VIDIOC_S_FMT, fmt);*nbuf = 3;bzero(reqbuf, sizeof(*reqbuf));reqbuf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;reqbuf->memory = V4L2_MEMORY_MMAP;reqbuf->count = *nbuf;ioctl(*cam_fd, VIDIOC_REQBUFS, reqbuf);for (int i = 0; i < *nbuf; i++) {bzero(&buffer[i], sizeof(buffer[i]));buffer[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buffer[i].memory = V4L2_MEMORY_MMAP;buffer[i].index = i;ioctl(*cam_fd, VIDIOC_QUERYBUF, &buffer[i]);start[i] = mmap(NULL, buffer[i].length, PROT_READ | PROT_WRITE,MAP_SHARED, *cam_fd, buffer[i].m.offset);ioctl(*cam_fd, VIDIOC_QBUF, &buffer[i]);}
}int main() {int lcd, cam_fd, nbuf;unsigned int *fb_mem;struct fb_var_screeninfo lcdinfo;struct v4l2_format fmt;struct v4l2_requestbuffers reqbuf;struct v4l2_buffer buffer[3];unsigned char *start[3];// 打开 LCD 并分配帧缓冲区打开LCD并分配显存(&fb_mem, &lcdinfo);// 打开摄像头并配置打开摄像头并配置(&cam_fd, &fmt, &nbuf, &reqbuf, buffer, start);// 初始化 SDL 包括视频、音频和定时器子系统SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);// 创建基本表面SDL_Surface *screen = NULL;screen = SDL_SetVideoMode(LCD_WIDTH, LCD_HEIGHT, 0, 0);// 创建 YUYV 格式的表面SDL_Overlay *bmp = SDL_CreateYUVOverlay(fmt.fmt.pix.width, fmt.fmt.pix.height,SDL_YUY2_OVERLAY, screen);// 启动摄像头enum v4l2_buf_type vtype = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(cam_fd, VIDIOC_STREAMON, &vtype);// 准备应用层缓冲区参数struct v4l2_buffer v4lbuf;bzero(&v4lbuf, sizeof(v4lbuf));v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;v4lbuf.memory = V4L2_MEMORY_MMAP;while (1) {// 捕获数据for (int i = 0; i < nbuf; i++) {v4lbuf.index = i;ioctl(cam_fd, VIDIOC_DQBUF, &v4lbuf);memcpy(bmp->pixels[0], start[i], buffer[i].length);bmp->pitches[0] = fmt.fmt.pix.width;ioctl(cam_fd, VIDIOC_QBUF, &v4lbuf);}// 锁定 YUV 重叠SDL_LockYUVOverlay(bmp);// 解锁 YUV 重叠SDL_UnlockYUVOverlay(bmp);// 显示 YUV 重叠SDL_DisplayYUVOverlay(bmp, NULL);}// 清理munmap(fb_mem, lcdinfo.xres * lcdinfo.yres * lcdinfo.bits_per_pixel / 8);close(lcd);close(cam_fd);return 0;
}

这篇关于linux学习:多媒体开发库SDL+视频、音频、事件子系统+处理yuv视频源的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/988053

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;