- 博客(46)
- 收藏
- 关注
原创 一种嵌入式项目的参数保存方案
从下表可知该参数的数据类型为uint32_t,参数数据长度为4字节,相较于参数区文件的偏移量为0,参数校验方式为自定义校验,不需要使用校验数据,校验函数为Version_CustomCheck_Fun;拥有校验机制,提供了范围校验/相等校验的接口,同时预留了自定义校验的接口;参数类型包含常见的数据类型,同时预留了自定义数据类型的接口;参数区拥有版本号概念,同时预留了参数区版本号变更的操作接口;表示该参数相对于整个参数区的偏移量,以字节为单位;枚举表示该参数的校验方式,比如范围校验为。则是具体的校验方法;
2022-12-24 22:34:05
787
原创 音视频录制器—打包数据流
av_rescale_q_rnd:通过指定方式,使用两个时间基,调整一个64位整数;avformat_alloc_output_context2:申请一个用于输出格式的AVFormatContext。avformat_write_header:申请数据流的私有数据,并将数据流header写入到输出文件。根据帧持续时间和帧数计算出当前数据流时间,再根据输入数据流时间基,换算成输入数据流中的时间戳。...
2022-08-14 23:55:11
271
原创 音视频录制器—捕获并保存摄像头和麦克风数据
av_write_frame:将数据包写入输出媒体文件,与av_interleaved_write_frame类似,区别在于前者只能用于单一数据流,后者可以用于单一或多个数据流。avcodec_encode_video2:对视频帧进行编码,从AVFrame中获取输入的原始视频数据,并将下一个输出数据包写入到AVPacket中。channels和channel_layout,输入数据的通道数和通道布局,如双通道,AV_CH_LAYOUT_STEREO布局;:指向字典结构指针的指针。......
2022-08-14 23:50:01
255
原创 音视频播放器—变速播放
因为视频帧持续时间的获取是根据基于pts的音视频时钟差和初始持续时间来获取的,所以当音频和视频同时变速时,不需要更改音频帧和视频帧的pts数据,即可维持两者之间的差值在同一基准。由于时间关系,此处使用了一个简化版本的变速播放方案,即视频变速采用改变每帧持续时间的方法,音频变速采用丢帧和填充帧的方法;改变视频播放速率的方法为直接改变帧率即改变每帧的帧持续时间,加速时缩短帧持续时间,反之减小帧持续时间。但是这种方法会导致声音在变速的同时变调。丢帧和填充帧,即加速播放时丢帧处理,反之在原始帧之间填充空白帧。...
2022-07-26 01:28:43
640
原创 音视频播放器—快进快退及逐帧播放
选择跳转后选取的数据流,如果为-1,则选择默认流,并且时间戳会自动从AV_TIME_BASE单位转换为特定于流的time_base;以AVStream.time_base为单位的时间戳,如果未指定流,则以AV_TIME_BASE为单位。当发生文件跳转时,原本demux时存取在队列中的数据包,以及解码后的数据包都不能使用,因此要进行清空队列操作。然后加上当前的跳转变化量,作为跳转到文件中的位置。多媒体文件的句柄,即打开文件时所使用的AVFormatContext结构体;...
2022-07-21 00:08:43
907
原创 音视频播放器—音视频同步
以视频时钟举例,当解码了一帧视频帧后,即以转换了时间基的pts作为当前视频时钟的基准,同时保存当前的外部时钟时间。当获取视频时钟时,获取的不是视频帧pts,而是以pts为基准,加上更新视频时钟基准到现在过去的时间,作为视频时钟。中提到了PID控制器的方法,这里也试着使用这种方法,实际效果上能将音视频时钟差收敛至视频帧一帧持续时长内(测试视频的最高帧率为60fps)。,简单来说就是根据音频时钟和视频时钟的差值去动态校准视频帧的持续时长(若视频时钟落后太多则进行丢帧处理),以达到音视频同步的效果。...
2022-07-20 23:44:55
248
原创 视频播放器—外部时钟源同步
获取视频时钟,首先要获取视频帧的显示时间戳pts;在AVFrame结构体中,有一成员变量为best_effort_timestamp,该变量是 单位为数据流时间基的帧时间戳,官方解释如下:获取到该时间戳后,再将其单位由数据流时间基转换为正常时间;这里的时间基是使用AVRational结构提描述的,即分子—分母式的描述方式;这里使用 av_q2d() 函数对其进行转换,将AVRational描述的时间基转换为正常时间基。具体流程如下:获取外部时钟外部时钟主要依赖于 av_gettime() 函数,
2022-07-10 16:01:17
375
原创 视频播放器—纹理-渲染-窗口
SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成;其封装了复杂的视音频底层交互工作,简化了视音频处理的难度;多用于开发游戏、模拟器、媒体播放器等多媒体应用领域;OpenGL(Open Graphics Library - 开放图形库)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API),这个接口由近350个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。SDL_CreateWindow:在特定位置创建一个指定大小
2022-07-10 15:58:00
188
原创 视频播放器—解码操作-图像后处理
avcodec_decode_video2:将大小为 avpkt->size 的视频帧从 avpkt->data 解码为图片。某些解码器可能支持单个 AVPacket包含多个帧,但解码器将只解码第一帧。AVCodecContext:描述该音频解码器上下文的结构体AVFrame:用于存储解码的数据int * got_frame_ptr:如果当前无法解码帧,则返回0;否则返回非0AVPacket:待解码的数据包返回值:负数表示出现错误,否则为解码时消耗AVPacket的字节数sws_getContext:
2022-07-10 15:56:46
964
原创 音频播放器—显示封面
AVStream结构体中的disposition参数,但该参数为AV_DISPOSITION_ATTACHED_PIC标志位时,表明存在封面图片。ffmpeg中对disposition参数的部分解释:ffmpeg中对AV_DISPOSITION_ATTACHED_PIC标志位解释如下因此当我们通过video stream的disposition参数判断是否有封面图片,然后从AVStream.attached_pic中去获取该封面图片。在创建纹理时需要指定输入数据的像素格式,同时该像素格式应为渲染器所支持。当
2022-07-10 15:53:56
648
原创 基于FFmpeg音视频项目的系列笔记
文章目录相关项目音频播放器视频播放器音视频播放器打包视频h264和音频aac为ts文件音视频录制器PCM音频数据封装为WAV文件FFmpeg笔记ffplay源码之serial变量ffplay源码之音视频同步分析基于FFmpeg音视频项目的基本知识点记录相关结构体AVPacket,AVFrame,AVStreamAVCodecContext,AVCodecAVFormatContext,AVIOContext音频播放器项目记录初始化打开音频设备(扬声器)保存未解码数据以及队列操作解码和重采样显示封面的图片视频
2022-04-22 02:34:22
2320
原创 Ubuntu 16.04 FFmpeg开发环境搭建
------------------------------------全系列文章目录------------------------------------以下为本人在搭建ffmpeg开发环境时遇到的问题和解决方法下载源码:https://launchpad.net/ubuntu/+source/ffmpeg/7:4.4.1-3ubuntu5选择某个版本,比如:ffmpeg_4.4.1.orig.tar.xz编译FFmpeg:https://www.cnblogs.com/deng-
2022-04-22 02:25:00
313
原创 音频播放器—解码和重采样
解码操作函数avcodec_decode_audio4,用于将AVPacket.size大小的AVPacket.data解码至AVFrame中,注意AVPacke可能包含多帧数据,但解码时一次只返回一帧数据。int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, const AVPacket *avpkt);AVCodecCo
2022-04-22 01:55:08
535
原创 音频播放器—读取未解码数据以及队列操作
读取MP3文件中的未解码数据包,保存为AAC文件av_read_frame(AVFormatContext *s, AVPacket *pkt) :此函数返回文件中存储的内容,它会将文件中存储的内容拆分为帧,并为每个调用返回一个帧。av_packet_unref(AVPacket *pkt):此函数用于擦拭数据包, 即取消引用数据包引用的缓冲区并将剩余的数据包字段重置为其默认值。url_feof(AVIOContext *s):当文件结束时返回非零,否则返回零。代码如下所示:/*
2022-04-22 01:52:56
2380
原创 音频播放器—打开音频设备(扬声器)
本文主要记录音频播放器中打开音频设备(扬声器)的操作音频文件主要有以下几个参数声道数:声道(Sound Channel) 是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,所以声道数也就是声音录制时的音源数量或回放时相应的扬声器数量。声道布局:根据声道数,摆放录制设备或扬声器设备。采样数:一帧音频帧的样本数量。采样率:每秒音频的样本数量。采样位数/采样格式:每个采样样本的量化位数。SDL_AudioSpec结构体同样道理,要想音频设备驱动程序能正确使用音频设备播放一
2022-04-22 01:49:47
793
原创 音视频播放器—初始化操作
本文主要记录音视频播放器的一些初始化操作av_register_all()该函数调用avcodec_register_all()注册了和编解码器有关的组件,如硬件加速器、解码器、编码器、Parser和Bitstream Filter;同时注册了复用器、解复用器和协议处理器。注册函数都大同小异,都是设定一个全局静态的链表头,将注册的组件以链表的形式保存起来,同时进行一些初始化操作。如编解码器的注册函数如下,可知其最终是调用了avcodec_register这个函数。#define REG
2022-04-22 01:43:17
2011
原创 AVFormatContext,AVIOContext结构体
URLContext用于描述URLProtocol的上下文typedef struct URLContext { const AVClass *av_class; /**< information for av_log(). Set by url_open(). */ const struct URLProtocol *prot; void *priv_data; char *filename; /**< specified
2022-04-22 01:32:53
139
原创 AVCodecContext,AVCodec结构体
AVCodec该结构体用于描述编解码器信息/* !!! 略有删减 */typedef struct AVCodec { const char *name; //编解码器名称,编码器之间和解码器之间是唯一的,但是编码器和解码器可共享同一个名字 const char *long_name; //编解码器的描述性名称,更易于阅读 enum AVMediaType type; //指明编解码器类型,视频、音频或字幕 enum AVCodecID id; //编解码器
2022-04-22 01:30:03
1321
原创 AVPacket,AVFrame,AVStream结构体
AVPacket用于描述压缩编码数据,其可作为输入传递给解码器,或者作为输出传递给编码器。对于视频,它通常应该包含一个压缩帧。对于音频,它可能包含多个压缩帧。使用 av_packet_alloc() 分配,使用 av_packet_free() 释放。/* !!!略有删减 */typedef struct AVPacket { AVBufferRef *buf; int64_t pts; //显示时间戳 int64_t dts; //解码时间戳 uint
2022-04-22 01:25:24
478
原创 PCM音频数据封装为WAV文件
PCM(Pulse Code Modulation,脉冲编码调制),PCM音频数据是指经过采样、量化、编码转换成的未压缩数字音频数据。WAV最常见的声音文件格式之一,是微软公司专门为Windows开发的一种标准数字音频文件,该文件能记录各种单声道或立体声的声音信息,并能保证声音不失真。WAV格式组成RIFF(Resource Interchange File Format,资源交换档案标准),是一种把资料储存在被标记的区块(tagged chunks)中的档案格式(meta-forma..
2022-03-19 15:36:37
3427
原创 指针和数组小结
一维数组的空间排布如下:arrarr[0]arr[1]arr[2]arr[3]…二维数组的空间排布如下:arrarr[0]arr[1]arr[2]arr[0] [0]arr[0] [1]arr[0] [2]arr[1] [0]arr[1] [1]arr[1] [2]arr[2] [0]arr[2] [1]arr[2] [2]指针的移动:指针每次移动的尺度为其所指向数据类型的大..
2022-03-13 00:08:14
158
原创 c/c++变量在内存的位置
c/c++变量在内存的位置C/C++编译的程序占用的内存栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等堆区:一般由程序员管理,自主分配和释放静态区(全局区):只有当程序都结束了,才会被释放常量区:存放不能更改的变量代码区:存放代码指令代码举例/* 全局变量global_int_var,在静态区(全局区) */int global_int_var;/* 全局指针变量global_str_p,在静态区(全局区);字符常量“string”在常量区 *
2022-03-04 01:42:33
911
原创 常见排序算法的简单总结
本文对几种常见的排序算法做一个简单总结基本概念排序稳定性:若排序的序列中,有多个相同的值,且假设A在B前面;若排序后可能使得B在A的前面,那么则称这个排序是不稳定的。大O法则:用于分析算法的复杂度,有以下约定:用常数1取代算法复杂度表达式的加法常数;算法复杂度表达式中,只保留最高阶;算法复杂度表达式最高阶项的相乘系数修改为1。冒泡排序流程:外循环控制排序轮数,排序的轮数为序列长度n。内循环从序列第一/最后序列项开始,与前一个序列项比较;若小于/大于前一序列项,则交换位置;第
2022-02-23 21:17:59
206
原创 ffplay源码之音视频同步分析
本文是根据ffplay源码-https://ffmpeg.org/download.html,分析其音视频同步的方式,不当之处恳请批评指正。视频显示的操作在主线程的refresh_loop_wait_event函数中,该函数及相关注释如下static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) { double remaining_time = 0.0; /*从设备收集所有待处理的输入信息并将其放入事件
2022-02-07 15:23:31
1459
原创 ffplay源码之serial变量
本文是根据ffplay源码-https://ffmpeg.org/download.html,分析其中的serial变量,不当之处恳请批评指正。serial翻译为连续的,在ffplay中是用于判断播放是否连续的标志,serial变量存在于自定义的多个结构体中/*存储未解码数据包*/typedef struct MyAVPacketList { AVPacket *pkt; int serial;} MyAVPacketList;/*存储未解码数据包的队列*/typedef
2022-02-07 15:05:23
1380
原创 Linux电源管理 — RPM
一. 简介RPM全称Runtime Power Management,即运行时电源管理。主要是用于设备运行时的电源管理,由kernel统一管理,当设备处于空闲状态时,挂起设备;而当设备需要被使用时,使设备恢复进入正常工作状态。二. 伪代码示例/*Test 驱动模块结构体*/struct TestDev_t{ dev_t dev_num; //设备号 struct cdev TestCdev; //使用Linux内核下cdev结构体,描述一个字符设备 struct class
2021-11-27 21:52:25
1304
原创 CRC校验(个人小结)
前言之前曾经在通信原理课程学习过、也在项目中使用过CRC校验。但是仍然有许多困惑的地方,在网上找的资料也是零零散散,于是自己根据课程学习、项目实践以及网上的资料,做一个总结。不当之处请在评论区指出。推荐一个CRC在线校验工具。一. CRC校验CRC校验数字通信系统中的一种检错方法,主要利用除法和余数的原理来进行错误检测,并且实现简单,生成的检错码占用空间小。因为CRC校验码是通过整组信息序列和校验序列得出的,即用待发送的信息序列多项式除以生成多项式,将最后的余数作为CRC校验码,所以检
2021-11-14 17:52:44
5569
2
原创 Linux字符设备驱动结构
一、内核模块字符设备驱动属于内核模块,而内核模块主要由模块加载函数、模块卸载函数和模块许可证声明组成。模块加载/卸载函数:当通过insmod/rmmod命令加载/卸载模块时,模块的加载/卸载函数会自动被内核执行。static int __init xxx_init(void);static int __exit xxx_exit(void); module_init(xxx_init); //指定模块加载函数xxx_initmodule_exit(xxx_exit);
2021-11-13 17:39:45
819
原创 Linux阻塞和非阻塞IO
一. 阻塞和非阻塞简介IO指的是输入/输出的意思,即应用程序对驱动程序的输入/输出操作若应用程序不能获取到设备资源阻塞式IO会将应用程序对应的线程挂起,直到获取到设备资源非阻塞式IO则不会挂起对应线程,而是一直轮询等待,直到获取到设备资源或直接放弃阻塞IO示意图非阻塞IO示意图应用程序阻塞式和非阻塞式访问驱动程序/* 对于驱动设备,默认是以阻塞的方式打开的 */fd = open("/dev/xxx_dev", O_RDWR);/* 若想以非阻塞式方式打开,需
2021-05-22 17:19:59
322
原创 Linux中断简记——IMX6ULL示例
一. IMX6ULL裸机中断配置好中断相关外设及初始化GIC中断控制器。在IRQ异常服务函数中,使用GIC控制器、cp15协处理器,获取当前发生的中断相关信息。跳转到中断处理函数中,执行对应的中断服务函数。返回至IRQ异常服务函数,退出至程序被打断处继续执行。 二. Linux中断——Linux内核提供了完善的中断框架配置好设备节点的中断信息,Linux内核会根据这些信息初始化相关中断初始化相关设备,以及加载设备、创建设备节点文件从设备树中获取中断号,初始化上半部中断服务函
2021-04-24 15:15:51
569
原创 Linux并发和竞争简记
一. 并发与竞争Linux系统并发(访问同一个共享资源)产生的原因多线程并发访问抢占式并发访问,调度程序可以在任意时刻抢占正在运行的线程中断服务程序的并发访问SMP(多核)核间并发访问Linux并发和竞争的几种解决方法:原子操作,自旋锁,信号量,互斥体二. 原子操作:不能再进一步分割的操作,一般用于变量或位操作Linux内核使用atomic_t 的结构体来完成整型数据的原子操作,该结构体定义在源码目录include/linux/types.h typedef st
2021-04-14 11:47:18
93
原创 Linux字符设备驱动—GPIO驱动
一. pinctrl和gpio子系统要想驱动一个设备,就必须先初始化设备,然后再进行相关操作对于Linux下的GPIO字符设备驱动,可以使用pinctrl子系统进行初始化初始化完成后,对GPIO的操作又可以使用GPIO子系统留出的API函数二. Linux设备树简记设备树是描述板级设备信息的文件要想驱动某个设备,就必须在设备树中添加设备节点的描述信息三. GPIO字符设备驱动框架当设备已经添加进设备树节点,就可以根据设备的具体情况以及驱动框架编写驱动了以上是我在学习过程中的总结
2021-04-09 10:04:57
328
原创 pinctrl和gpio子系统
一. 裸机驱动与Linux驱动裸机下GPIO驱动的编写,是用户通过直接操作寄存器来实现的;Linux内核提供了pinctrl和gpio子系统,用于GPIO驱动的开发。Linux驱动框架讲究驱动的分离和分层,pinctrl和gpio子系统就是该思想下的产物,驱动分离与分层其实就是按照面向对象编程的设计思想而设计的设备驱动框架。大多数SOC的pin都支持复用,如果采用用户直接操作寄存器来配置pin,繁琐且易出现问题(如pin复用),pinctrl子系统就是为了解决这些问题的;我们只需在设备树上
2021-04-09 10:03:56
191
原创 Linux设备树简记
一. 设备树是一种描述硬件设备的方法,描述设备数的文件叫DTS(Device Tree Source),DTS采用树形的结构描述板级设备在设备树出现之前,板级硬件设备的相关信息都被编译进Linux内核中,导致内核代码臃肿且难以维护,所以引入了设备树,将内核与硬件设备代码解耦。.dtsi文件(板级公共文件,使用时类似头文件)描述的是如SOC级信息:SOC有几个CPU、主频是多少、各个外设的控制信息等;.dts文件描述的是板级信息:IIC设备、SPI设备等;一个完整设备树文件是由1个dts+n个d
2021-04-09 10:03:22
184
原创 GPIO字符设备驱动框架
一.字符设备和驱动字符设备是指使用字节流进行操作的设备。驱动是位于内核空间的,所以用户空间(应用程序)要想操作某个设备时,则需要通过系统调用(如c库函数)的方法,实现对驱动的调用,从而实现操作设备。Linux驱动有两种运行方式直接编译进Linux内核,随操作系统启动而自动运行将驱动编译成模块,动态地加载、卸载驱动模块二. 当动态地加载卸载驱动模块时,需要向Linux内核注册驱动模块加载卸载的操作函数module_init(xxx_init); //注册驱动模块加载函
2021-04-09 10:02:18
309
原创 浅谈卡尔曼滤波器
一. 状态观测器当只知道原系统的输入矩阵U | k和输出矩阵Y | k,但是无法得知某些状态X | k时,可以采用状态观测器的方法。状态观测器即是将原系统模拟成一个观测器系统,观测器的输入矩阵为原系统的 U | k。若观测器完全复现了原系统,即观测器输出输出矩阵Ŷ | k将与原系统相等,这时即可认为原系统待观测状态矩阵X | k与观测器状态矩阵X^ | k相等;然而事实上观测器总是不能完全拟合原系统,因此引入了负反馈调节。数学分析X|k = A * X|k-1 + B * U|k + W|k
2021-03-10 16:55:40
5721
原创 浅谈设计模式之状态机模式
一. 为什么要引入状态机模式?系统状态的变化引起实际行为的改变,因此我们需要在系统的主任务中判断他们处于哪一个状态,使用if-else if-else或者switch-case分支结构可以很好的解决。但是如果在某个每个状态在不同场景都有多个分支、并且需要频繁切换状态且状态繁多的系统中,如果仍然使用if-else if-else或者switch-case分支结构,会降低代码的可阅读性以及可维护性。引入状态机模式,即是把所有的状态分隔为一层,将每个状态下遇到实际场景的行为分隔为一层,并将所有状态列出到一个
2021-03-09 12:24:51
5349
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人