首页
📁归档
⏳时光机
📫留言
🚩友链
💰资助名单
推荐
🎧音乐
🏜️ 壁纸
❤ 捐助
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
19,480 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,313 阅读
3
【高数】形心计算公式讲解大全
8,837 阅读
4
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,565 阅读
5
Vivado-FPGA Verilog烧写固化教程
7,088 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
21(共147篇)
找到
147
篇与
21
相关的结果
- 第 6 页
【美文】平常烟火,珍惜一段春色
习惯了,一个人早晨的时候去呼吸新鲜的空气,公园散步,与早起的人们一起,沿着一湾清水,走在幽静的小路上,鸟鸣啾啾,春意缓归,也是惬意。 散步回来,一杯牛奶,几片面包,也就打发了自己。 隔着窗,萧瑟凋零的草木重新焕发生机,醒来时,已是春光乍泄。 光阴似箭,走过曾经的时光,拥抱春天,当最后一片雪花飘落,风霜雨雪便是人生的足迹,一步一步走向美好。 春已至,站在春天里,遥看春色渐暖,浅淡颜色,渐渐有了模样,物候秩序,水墨丹青细细描摹。 枝头的蓓蕾藏,期待着花期,用自己的方式,各自准备着给春天的厚礼。 图片 苏醒的枝蔓,缓缓伸展着腰肢,春意盎然,只是时间问题,那些醉人的风景和迷人的姿态,慢慢的,都会一一到达。 时光不老,却是又重返青春,跟着流云,掠过一帧一帧的花影,在疏影横斜的背景里,日子可以走的亦是轻松,沿着季节的方向,明媚着未来的路途。 凝眸春风过处,款款春意盎然恣意,似淡墨染成的画卷。许多时候,一点灵犀,便是曼妙,在蕾枝之间彼此守护。 就如某种默契,无需言说即可心领神会。就如岁月的枝头,只要春风一来,无论静默多久,都会开出鲜艳的花瓣,释放出一分欢喜和几分明媚。 那画卷里,风景旖旎,千丝万缕细枝,沿途摇暖了一季风景,眸中清寒,也是时光里的潋滟,春来时,轻轻翻开一个个冬天埋下的伏笔,瞬间花苞炸裂,芳菲开遍。 图片 平淡的事情,用心感悟才有了意味深长,万千风景,用心了才是清欢有味。 在最美的时光里,安静知足,对任何喜欢的人或事都学会有所期待,耐心等待,无需刻意,只是顺其自然,只怕不经意间就失去平淡的光泽。 在春天的希望里,看似水流年,看绿肥红瘦,淡然之间,让生命有了柔和的温度。 春在路上,花在枝上,所有的美好都在心上,努力过好自己的日子,冷暖之间,各自安好。 心怀美好,用一份付出与努力,善待自己,接纳生活,摈弃杂念,别让鸡零狗碎的事情,耗尽你对美好生活的向往。 守住生命与自然的本色,自然而然,以淡泊宁静的心态去面对现实,无需仰视枝头的花朵,生活还需简单明了,只是珍爱,与这尘世和睦相处,再多一份心头的默契,如此,甚好。 图片 不念过往,不惧将来,我们无须铭记或者忘记,每一段岁月都是值得拥有的,你曾经拥有过的,都是最好的时光。 活在当下,你看,春风春雨春烟,让山河有了温柔的气息,一花一草一木都是春天的美丽。 平淡日子,让自己安静下来,用最美心情接受春天得到来,缘之所寄,是淡淡时光;一往而深,深情之处,平淡生活也是春天。
励志美文
刘航宇
4年前
1
895
5
SIM900A发送与接受短信
目录 一.SMS简介 二.短信的控制模式与编码1.Text Mode 2.PDU Mode 3.GSM编码 4.UCS2编码 三.收发英文短信1.AT+CPMS查询短信数量 2.AT+CNMI设置新消息提示类型 3.AT+CMGF选择短信模式 4.AT+CSCS设置编码 5.AT+CSMP设置短信文本模式 6.AT+CMGS发送消息到指定手机号 四.收发中文短信 一.SMS简介 SMS(Short Messaging Service)中文名称短信服务,短信是当下每一部手机上必备的功能之一,顾名思义,它是在手机之间发送文字信息或从个人计算机或手持设备向手机发送信息的一种方式,其文本信息的最大发送量为160个字符(字母、数字或者拉丁字母中的字符),对于中文一般最大发送量为70个字符。 这里我想用SIM900A模块实现短信的收发。 图片 二.短信的控制模式与编码 先说一下:我采用的是Text Mode下使用GSM编码收发英文短信、使用UCS2编码收发中文短信 对于短信的控制一共有三种模式:Block Mode、基于AT指令的Text Mode、基于AT指令的PDU Mode 。目前手机中默认使用PDU Mode,通过PDU编码的短信可以是文字、声音或者图像。Text Mode只能用于发送ANSI范围的字符,发送方式比较简单。 SIM900A模块只提供Text Mode 和PDU Mode。 中文短信中,所有汉字和字符都是采用UNICODE编码。 1.Text Mode Text Mode下,发送及接收到的数据均以ASCII码的显示来表示,可以发送指令"AT+CMGF=1",将GSM短信发送方式更改为文本模式。文本模式下接收的数据会自动解码,比如你收到一条短信息,GSM会返回:+CMGR: “REC UNREAD”,"+8613806XXXXXX",“11/10/21,13:22:13+32” hello (短信内容会自动换行) 不需要自己解码,便可得到短信的发送者、发送时间和短信内容,比较容易操作。 理论上Text Mode下,是只能够收发英文短信,但是SIM900A模块可以在Text Mode下使用UCS2编码,从而可以发送接收中文短信。 2.PDU Mode PDU相当于一个数据包,它由SMS的信息组成,作为一种数据单元,必须包含源地址、目的地址、有效时间、数据格式、协议类型、正文、正文长度(可达140字节),这些信息都以十六进制表示。 PUD Mode被所有手机支持,可以使用任何字符集,其编码方式分为:7bit、8bit、UCS2。 7-bit编码用于发送普通的ASCII字符,它将一串7-bit的字符(最高位为0)编码成8-bit的数据,每8个字符可“压缩”成7个;8-bit编码通常用于发送数据消息,比如图片和铃声等;而UCS2编码用于发送Unicode字符。在这三种编码方式下,PDU串的用户信息(TP-UD)段最大容量(可以发送的短消息的最大字符数)分别是160、140和70。这里,将一个英文字母、一个汉字和一个数据字节都视为一个字符。 理论上发送中文短信需要使用PDU Mode的UCS2编码,上面也说了,SIM900A模块可以使用Text Mode发送UCS2编码,而且PDU Mode比Text Mode更加复杂,所以我采用了Text Mode收发中英文短信。 PDU Mode的详细讲解可以借鉴此博客:点击链接跳转 3.GSM编码 在GSM编码模式下,收发消息的内容和电话号码,都是以ASCII字符的形式显示的,发送英文短信时使用十分方便。 所以我使用GSM编码来收发英文短信。 4.UCS2编码 谈到UCS2编码就不得不说UNICODE,UNICODE又叫统一码、万国码,是计算机科学领域里的一项行业标准,包括字符集、编码方案等。UNICODE 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。简单来说,UNICODE编码可以表示世界上任意一种语言(有点夸张),自然也可以表示中文。 而UCS2编码是UNICODE的一种,UCS2中每个字符都占俩个字节。 使用UCS2编码后,发送短信的手机号码、短信内容都要经过UCS2编码转换才可以,但UCS2编码发送的消息在手机上可以直接显示为中文。 所以,需要UCS2与中文的转换软件,如下,可以实现中文和UCS2的双向转换 图片 三.收发英文短信 1.AT+CPMS查询短信数量 图片 2.AT+CNMI设置新消息提示类型 图片 3.AT+CMGF选择短信模式 图片 4.AT+CSCS设置编码 图片 图片 5.AT+CSMP设置短信文本模式 使用Text Mode的GSM编码时,短信文本模式设置为: AT+CSMP=17,167,0,240(消息显示在终端) AT+CSMP=17,167,0,241(消息存储在SIM卡中) 图片 6.AT+CMGS发送消息到指定手机号 使用Text Mode的GSM编码时,直接AT+CMGS="手机号"即可,然后,在>后输入要发送的内容(不要勾选发送新行),最后发送HEX(十六进制)的:1A 即可。 如图,我使用本机向本机发送了一条短信,而且,本机收到短信后在终端中显示出来了。 图片 这是SIM公司给出的例子: 图片 CTRL-Z 代表十六进制:1A 使用SIM900A向本机发送一条英文短信的完整步骤如下: 图片 四.收发中文短信 收发中文短信利用的是Text Mode下的UCS2编码。 相比于收发英文短信,收发中文短信需要修改的地方有: 修改编码:AT+CSCS=“UCS2” 修改短信文本模式:AT+CSMP=17,167,0,24(短消息显示在终端,25:存储在SIM中) 手机号码要用UNICODE码 发送的消息要用UNICODE码(有专门的转换软件) 这是SIM公司给出的例子: 图片 我要发送信息:可爱的小白鼠,女朋友收到后回复消息,用软件将UNICODE码转换为中文,串口操作界面如示: 图片 发消息如示: 图片 收消息如示: 图片 这是我的SIM900A 图片
嵌入式&系统
刘航宇
4年前
0
1,179
2
2022-01-29
【9】基于STM32CubeMX-STM32OLED开发
目录 必备文件 关于OLED的概述 OLED开发相关资源下载 基于STM32CubeMX的OLED底层驱动函数移植 OLED驱动库中常用的函数 实训案例:STM32控制OLED显示 必备文件 OLED文件-SPI版 下载地址:https://wwu.lanzoub.com/iCqK302ybokh 提取码: OLED I2C版4脚 下载地址:https://wwi.lanzouw.com/ilMr9zjrhsb 提取码: 关于OLED的概述 OLED:Organic Light-Emitting Display,有机发光显示。 OLED具备自发光、厚度薄、视角广、功耗低、对比度高、响应速度快、可用于挠曲性面板、使用温度范围广、构造及其制作过程较简单等优异特性,并认为是一种比液晶显示更为先进的新一代平板显示技术。以目前的技术,OLED的尺寸还难以大型化,但是分辨率却可以做得很高。 基于STM32的OLED应用,要做那些事情: 【1】移植OLED的底层驱动函数库。 【2】准备需要的中文字符和图片等数据。 【3】调用OLED驱动库中的底层函数进行应用开发。 图片 OLED开发相关资源下载 基于STM32CubeMX的OLED屏驱动程序库(内含4个文件) 【1】XMF_OLED_STM32Cube.c:驱动程序的源文件。 【2】XMF_OLED_STM32Cube.h:驱动程序的头文件。 【3】XMF_OLED_Font.h:字库数据文件。 【4】XMF_OLED_BMP.h:图片数据文件。 基于STM32CubeMX的OLED底层驱动函数移植 【1】将4个驱动文件拷贝到工程文件中,和main.c放在同一目录,并将XMF_OLED_STM32Cube.c添加到工程代码文件中,并在main.c中引入头文件XMF_OLED_STM32Cube.h。 【2】根据所选用的芯片型号,修改XMF_OLED_STM32Cube.h头文件中所以用的芯片头文件。 【3】根据硬件电路原理图中,修改XMF_OLED_STM32Cube.h中OLED的引脚定义。 【4】查看OLED_Init(void)初始化函数的源码,根据电路接口和应用需要进行修改。 图片 OLED驱动库中常用的函数 void OLED_Init(void); //OLED初始化函数 void OLED_Clear(void); //OLED清屏函数//显示英文字符串函数 void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *p); 参数1:x,起点列坐标,0~127 参数2:y,起点行坐标,0~7 参数3:*p,字符串指针 返回值:void,无。//显示中文字符函数 void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no); 参数1:x,起点列坐标,0~127 参数2:y,起点行坐标,0~7 参数3:no,待显示中文字符在数组Hzk[][32]中的位置。 返回值:void,无。//显示图片函数 void OLED_DrawBMP( unsigned char x0, unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]); 参数1:x0,起点列坐标,0~127 参数2:y0,起点行坐标,0~7 参数3:x1,图片的列范围,1~128 参数4:y1,图片的行范围,1~8 参数5:BMP[],待显示图片数据的数组。 返回值:void,无。实训案例:STM32控制OLED显示 要点 图片 字库软件中行前后缀不需要括号 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】用取字模软件生产一张图片数据,作为开机界面在OLED上显示。 【2】0.5秒后进入信息界面, 第1行显示网址“sciarm.com”, 第2行显示中文“小蜜蜂笔记”,第3行显示日期“2022-02-18”。 图片 //用取字模软件生成开机LOGO图片数据,并拷贝到XMF_OLED_BMP.h的数组中。 const unsigned char BMP1[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //此处省略64×62个字节元素 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, };//用取字模软件生成所需中文字符数据,保持到XMF_OLED_Font.h中的Hzk[]数组。 const unsigned char Hzk[][32]={ {0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00}, {0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00},/*"小",0*/ /* (16 X 16 , 宋体 )*/ {0x10,0x4C,0x24,0x04,0xF4,0x84,0x8D,0x56,0x44,0x24,0x14,0xC4,0x24,0x54,0x0C,0x00}, {0x00,0x41,0x5D,0x55,0x54,0x55,0x55,0x7F,0x55,0x55,0x55,0x55,0x7C,0xC0,0x00,0x00},/*"蜜",1*/ /* (16 X 16 , 宋体 )*/ {0x00,0xF8,0x08,0xFF,0x08,0xF8,0x80,0x90,0x4C,0x57,0xA4,0x54,0x4C,0x84,0x80,0x00}, {0x20,0x63,0x21,0x1F,0x11,0x39,0x10,0x10,0x15,0x15,0xFF,0x15,0x15,0x10,0x10,0x00},/*"蜂",2*/ /* (16 X 16 , 宋体 )*/ {0x10,0x08,0x44,0x47,0x4C,0x54,0x44,0xD4,0x28,0x27,0x24,0x2C,0x34,0x04,0x04,0x00}, {0x00,0x10,0x12,0x12,0x12,0x12,0x12,0x7F,0x89,0x89,0x89,0x89,0x89,0x88,0xF0,0x00},/*"笔",3*/ /* (16 X 16 , 宋体 )*/ {0x40,0x40,0x42,0xCC,0x00,0x00,0x00,0x84,0x84,0x84,0x84,0x84,0xFC,0x00,0x00,0x00}, {0x00,0x00,0x00,0x7F,0x20,0x10,0x00,0x3F,0x40,0x40,0x40,0x40,0x41,0x40,0x70,0x00},/*"记",4*/ /* (16 X 16 , 宋体 )*/ };extern unsigned char BMP1[]; void OLED_display_pic() { OLED_Clear(); OLED_DrawBMP(0,0,128,8,BMP1); }void OLED_display_info() { OLED_Clear(); OLED_ShowString(6,0,(uint8_t *)"sciarm.com"); OLED_ShowCHinese(10,3,0); //小 OLED_ShowCHinese(28,3,1); //蜜 OLED_ShowCHinese(46,3,2); //蜂 OLED_ShowCHinese(64,3,3); //笔 OLED_ShowCHinese(82,3,4); //记 OLED_ShowString(24,6,(uint8_t *)"2022-02-18"); }//在mian()函数中添加下面的代码: /* USER CODE BEGIN 2 */ OLED_Init(); //OLED初始化 OLED_display_pic(); //显示图片 HAL_Delay(500); //延时0.5秒 OLED_display_info(); //显示信息 /* USER CODE END 2 */
嵌入式&系统
# 嵌入式
刘航宇
4年前
17
2,052
14
2022-01-16
【1】基于STM32CubeMX-STM32GPIO端口开发
目录 STM32CubeMX的声明 protuesSTM32仿真开发板下载 STM32的GPIO端口知识要点 GPIO电平输出HAL库函数 GPIO电平翻转HAL库函数 GPIO初始化函数源码剖析 实训案例:基于STM32CubeMX的跑马灯 仿真开发实例 STM32CubeMX的声明 【1】STM32CubeMX 是一个代码辅助生成工具,在生成工程代码的同时,根据你的功能选择和配置帮你做了必要的初始化,这也就意味着你不再需要对底层的特殊功能寄存器进行初 始化配置,但是不代表你就可以不去学习 STM32 的基本知识和各个外设的工作原理与参数 特性等。 【2】虽然不再要求你去了解 STM32 底层寄存器的定义,但却要求你要了解由 STM32CubeMX生产的代码内在的逻辑联系以及 HAL 库中的常用函数原型与使用。 【3】STM32CubeMX 的最大好处就是,使开发流程、文件结构、库函数等标准化 protuesSTM32仿真开发板下载 protues STM32C8开发板 下载地址:https://wwu.lanzoub.com/ixmmx04hidud 提取码: STM32的GPIO端口知识要点 GPIO:General Purpose Input & Output STM32芯片最拥有GPIOA、GPIOB…GPIOG等7组端口,每组端口最多拥有Pin0、Pin1…Pin15共16个引脚。 STM32的每个I/O端口都可以自由编程,但I/O端口寄存器必须按32位字被访问。STM32的每个I/O端口都由7个寄存器来控制。 STM32的GPIO端口可以由软件配置成8种模式: 推挽输出、开漏输出、推挽式复用功能、开漏式复用功能; 模拟输入、浮空输入、下拉输入、上拉输入。 GPIO电平输出HAL库函数 void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 参数3:PinState,引脚输出状态。高电平----GPIO_PIN_SET;低电平----GPIO_PIN_RESET。 返回值:void,空。 应用举例:向PB8引脚输出高电平。 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);GPIO电平翻转HAL库函数 void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 返回值:void,空。 应用举例:将PA3引脚输出电平翻转。 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);GPIO初始化函数源码剖析 void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO端口时钟使能 */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*配置GPIO端口引脚的初始化输出电平 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET); /*配置GPIO端口输入引脚 : PC13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; //GPIO端口的引脚号是:13 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //GPIO的模式是:输入 GPIO_InitStruct.Pull = GPIO_NOPULL; //没有上拉 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); //将参数结构设置到GPIOC端口 /*配置GPIO端口输出引脚 : PB8 PB9 */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; //GPIO端口的引脚号是:8和9 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //GPIO的模式是:输出 GPIO_InitStruct.Pull = GPIO_NOPULL; //没有上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //GPIO的输出速度是:非常低速 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //将参数结构设置到GPIOB端口 }实训案例:基于STM32CubeMX的跑马灯 在XMF07A或XMF07C开发板上,利用STM32CubeMX对STM32芯片的LED控制引脚进行配置并快速生产项目,在Keil5中进行代码编写,实现跑马灯功能,即:LED1灯亮,过一会,LED2灯亮,过一会,LED1灯熄灭,过一会,LED2灯熄灭….如此循环。 XMF07C开发板:STM32L151C8T6 (电路功能兼容物联网国赛设备) XMF07A开发板:STM32F103C8T6 图片 /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET); //向PB9输出高电平,点亮LED1灯 HAL_Delay(500); //延时500ms HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET); //向PB9输出低电平,熄灭LED1灯 HAL_Delay(500); //延时500ms HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9); //用翻转电平的方式,实现LED1灯的点亮与熄灭 HAL_Delay(500); //延时500ms HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); //用翻转电平的方式,实现LED2灯的点亮与熄灭 HAL_Delay(500); //延时500ms /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */仿真开发实例 原理图: 图片 1.新建CubeMX工程 选择对应的MCU,在proteus8中支持STM32F103的几款muc,这里我们选择STM32R6 图片 设置下载方式 图片 配置LED灯的GPIO l1p0om1l.png图片 配置时钟树 l1p0ownv.png图片 选择keil5工程(或者其他工程) l1p0pb0l.png图片 勾选此处可以使得每个初始化的外设有独立的.c和.h l1p0ptxo.png图片 生成MDK工程 l1p0ro0x.png图片 添加LED闪烁的代码,并编译生成.hex文件 HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5); HAL_Delay(0XFF)l1p0sq2c.png图片 将hex文件下载到proteus8中去 l1p0t26z.png图片 此时仿真会报错,原因是电源为接入,只需要将电源接入即可 l1p0tjr9.png图片 此处VDDA,VSSA分别加入到电网VCC与GND中,详细可以百度 l1p0tpli.png图片 最后,就可以看到有一个LED灯反复交替闪烁! l1p0u3mg.png图片
嵌入式&系统
# 嵌入式
刘航宇
4年前
8
6,925
118
嵌入式视频流知识点及代码解析-精简版
目录 背景和意义 框架 代码及相关知识点 一、知识点篇 二、问答篇 三、代码篇RTSP程序要不要等待播放器器程序请求? 请你找出上述代码所在位置 live555(了解) SDL 背景和意义 (1)视频的带宽很大,存储,传输不便,故要压缩、解压 、播放。 (2)应用领域很广 ,交通,在线教育,播放器,自动驾驶。 框架 图片 下面这个图及其重要以及3个ip关系 abcde代表先后实现顺序注意观看!!! TO67M8.png图片 代码及相关知识点 一、知识点篇 Live 555: 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了标准流媒体传输,对标准流媒体传输协议如RTP/RTCP、RTSP、SIP等的支持。Live555实现了对多种音视频编码格式的音视频数据的流化、接收和处理等支持,包括MPEG、H.263+、DV、JPEG视频和多种音频编码。同时由于良好的设计,Live555非常容易扩展对其他格式的支持。Live555已经被用于多款播放器的流媒体播放功能的实现,如VLC(VideoLan)、MPlayer。 在本次开发实践中主要用于接收海康威视摄像头的RTP数据包 并通过UDP网络进行转发给PC机。 FFmpeg: Fmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。 FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。 在本次开发实践中主要用于H264数据解码。 SDL: SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。在本次开发实践中主要用于YUV数据的显示。 二、问答篇 1.什么是YUV?与RGB有什么不同? YUV 是一种颜色编码方法,FFmpeg 解码后的数据格式。Y 表示明亮度 U 表示色 度 V 表示浓度。因为通过研究发现,人类对于图像的感知中对明亮最侧重,色彩和 浓度就相对不那么重要,所以在保存图片时,让明亮度占较多的比重,有效的在不影 响观看的情况下节约了空间。视频播放器解码出来的格式为 YUV420P,其中明亮度 占整个数据的 2/3,色度和浓度占 1/3。 2.RTSP在什么层?答:应用层 3.你关于IP问题你用到了那些命令?答:ipconfig,ifconfig,ping等 4.本次课程设计你用到了那些去年学的知识?答:网络通信,文件开关与读写,第一章shell操作命令 下面部分自行百度: 5.TCP与UDP特点(TCP的可靠,UDP 的不可靠,UDP快) 6.简述TCP与UDP 7.了解HTTP/https 8.三次握手和四次挥手过程 三、代码篇 RTSP程序要不要等待播放器器程序请求? 答:要 请你找出上述代码所在位置 答:如图所示 图片 live555(了解) BasicTaskScheduler 的父类是BasicTaskScheduler0 BasicTaskScheduler0是一个用作传递的类,它继承自TaskScheduler,又派生出BasicTaskScheduler。其定义在live555sourcecontrol\UsageEnvironment\include\BasicUsageEnvironment0.hh文件中。 BasicTaskScheduler0中有 BasicTaskScheduler 这个类主要实现事件的处理 BasicUsageEnvironment 涉及调试语句,输出语句 ourRTSPClient 主要是涉及的数据的发送相关的功能函数,主要的功能继承于父类RTSPClient RequestRecord 创建一个请求记录对象,并将回调函数与之关联 sendRequest 第一次进入调用:openConnection解析URL,并调用setupStreamSocket和connectToServer,然后 envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION, (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this); 若不是第一次调用,则打包 RTSP 包协议,并调用 Send函数发送到服务器。 重点 RequestRecord入队列 等候读取数据。 setupStreamSocket调用createSocket ,createSocket调用: sock = socket(AF_INET, type, 0); connectToServer调用 connect(socketNum, (struct sockaddr*) &remoteName, sizeof remoteName) setBackgroundHandling 主要初始化select 函数的 文件描述符集合 incomingDataHandler 中包含函数 readSocket 调用 int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0, (struct sockaddr*)&fromAddress, &addressSize);到此发送描述命令结束 ,等待响应服务器发送过来的数据。 incomingDataHandler1读取服务器的数据 incomingDataHandler1先调用readSocket调用,然后调用 handleResponseBytes 解析RTSP服务器数据 调用(*foundRequest->handler())(this, resultCode, resultString); 这个函数就是continueAfterDESCRIBE continueAfterDESCRIBE 调用下一步 setup 功能 SDL 抓1,2,3,4....等每段的关键句,可能考流程 rb为只读,对于不需要进行更新的文件,可以防止用户的错误的写回操作,防止损毁原有数据。具有较高的安全性。 rb+为更新二进制文件,可以读取,同时也可以写入,需要用到fseek之类的函数进行配合,以免出错,对于需要不时更新的文件,比如信息管理系统中的数据,可以这样打开。 初始化SDL 使用SDL_Init()初始化SDL。该函数可以确定希望激活的子系统。 int SDLCALL SDL_Init(Uint32 flags) SDL_INIT_VIDEO:视频 创建窗口(Window) 使用SDL_CreateWindow()创建一个用于视频播放的窗口。 SDL_Window SDLCALL SDL_CreateWindow(const char title,int x, int y, int w,int h, Uint32 flags); SDL_CreatWindow:第一个参数是窗口名字,第二三是窗口的坐标(SDL_winpos_undefined 为采用系统默认) title :窗口标题 x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。 y :窗口位置y坐标。同上。 w :窗口的宽 h :窗口的高 flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。 flags:SDL_WINDOW_RESIZABLE 自动调整窗口 基于窗口创建渲染器(Render) 使用SDL_CreateRenderer()基于窗口创建渲染器 SDL_Renderer SDLCALL SDL_CreateRenderer(SDL_Window window,int index, Uint32 flags); window: 渲染的目标窗口。 index:打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。 SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步 创建纹理(Texture) 使用SDL_CreateTexture()基于渲染器创建一个纹理 SDL_Texture SDLCALL SDL_CreateTexture(SDL_Renderer renderer,Uint32 format,int access, int w,int h); renderer:目标渲染器。 format:纹理的格式。 access:定义位于SDL_TextureAccess中 access:SDL_TEXTUREACCESS_STREAMING :变化频繁 w:纹理的宽 h:纹理的高 SDL_Thread *refresh_thread = SDL_CreateThread(RefreshVideo,NULL,NULL); //创建线程 SDL_Event event; //设置触发事件 6.在SDL中,当事件等待函数监听到事件后,判断事件类型,如果event.type == SDL_KEYDOWN,表明用户按下键盘,保存在event.key.keysym.sym是相应的键值。而根据键值,调用函数SDL_GetKeyName(event.key.keysym.sym)),即可得到按下的按键键名。 SDLK_RETURN == 13 回车! 7.读文件fread size_t fread(void buffer,size_t size,size_t count,FILE stream) buffer 是读取的数据存放的内存的指针(可以是数组,也可以是新开辟的空间,buffer就是一个索引) size 是每次读取的字节数 count 是读取次数 stream 是要读取的文件的指针 8.循环显示画面 (1)设置纹理的数据 使用SDL_UpdateTexture()设置纹理的像素数据 int SDLCALL SDL_UpdateTexture(SDL_Texture texture,const SDL_Rect rect,const void *pixels, int pitch); texture:目标纹理。 rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。 pixels:像素数据。 pitch:一行像素数据的字节数。 (2)纹理复制给渲染目标 使用SDL_RenderCopy()将纹理数据复制给渲染目标。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目标。实际上视频播放的时候不使用SDL_RenderClear()也是可以的,因为视频的后一帧会完全覆盖前一帧 int SDLCALL SDL_RenderCopy(SDL_Renderer renderer,SDL_Texture texture,const SDL_Rect srcrect,const SDL_Rect dstrect); renderer:渲染目标。 texture:输入纹理。 srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。 dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。 (3) 显示 使用SDL_RenderPresent()显示画面 void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer); 参数renderer用于指定渲染目标 9.退出 event.type==SDL_QUIT fclose(fp); //关闭文件! 防止遗留无用进程 SDL_DestroyTexture(texture); //关闭纹理 SDL_DestroyRenderer(renderer); //关闭渲染 SDL_DestroyWindow(window); //关闭窗口 SDL_Quit(); //退出线程 FFMPEG解码deco fun_deco_display.c 从void * deco_thread开始 play_ubuntu内 功能函数于主函数同时编译 多个c编译! gcc -o demo main.c fun_deco_display.c fun_others.c fun_recv_control.c -I /monchickey/ffmpeg/include -L /monchickey/ffmpeg/lib -lavformat -lavcodec -lavutil -lSDL2 -lpthread AVCodec codec; / 解码CODEC*/ AVCodecContext *cctx; AVFrame frame; / 解码后的图像*/ int byte_buffer_size; //解码器码流长度 uint8_t *byte_buffer = NULL; //h.264码流 AVPacket *pkt; //保存媒体流信息 AVPacket主要保存一些媒体流的基本信息,例如PTS、DTS时间。最重要的当然就是媒体数据的buffer地址了。 比较重要的有: pts:控制显示的pts时间 dts:控制解码的dts时间 *data:媒体数据buffer的指针 duration:AVStream-> time_base单位中此数据包的持续时间,如果未知则为0。 在演示顺序中等于next_pts - this_pts。 AVFormatContext主要存储视音频封装格式中包含的信息 解码前数据:AVPacket 解码后数据:AVFrame 1.AVFormatContext avformat_alloc_context(void)函数用来申请AVFormatContext类型变量并初始化默认参数。申请的空间通过void avformat_free_context(AVFormatContext s)函数释放。 2.avformat_find_stream_info()主要用于给每个媒体流(音频/视频)的AVStream结构体赋值,已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作 3.av_find_best_stream()函数就是要获取音视频对应的stream_index 获取流的索引 4.解码模块第一步:获取解码器 avcodec_find_decoder()FFmpeg的解码器编码器都存在avcodec的结构体中 5.avcodec_alloc_context3,avcodec_parameters_to_context,解码器初始化 6.avcodec_open2打开解码器 7.av_frame_alloc()首先调用av_mallocz()为AVFrame结构体分配内存 8.int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);函数的作用是通过指定像素格式、图像宽、图像高来计算所需的内存大小,av_malloc 按需分配空间 9.av_packet_alloc实际是分配AVPacket以后,调用av_init_packet对AVPacket的成员变量进行初始化赋值 10.av_read_frame()的作用是读取码流中的音频若干帧或者视频一帧 11.avcodec_send_packet()以在AVPacket中给出解码器原始的压缩数据 avcodec_receive_frame()。 成功后,它将返回一个包含未压缩音频或视频数据的 AVFrame 12. void av_image_copy_uc_from ( uint8_t * dst_data[4], const ptrdiff_t dst_linesizes[4], const uint8_t * src_data[4], const ptrdiff_t src_linesizes[4], enum AVPixelFormat pix_fmt, int width, int height ) 数据拷贝 13.av_packet_free(&pkt); //释放数据 关闭进程 av_frame_free(&frame); avformat_close_input(&fctx); avcodec_free_context(&cctx); avformat_free_context(fctx); Client && Server(了解) //Client_Upd 1.int socket(int domain, int type, int protocol); 函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族 SOCK_DGRAM udp连接 socket()函数的原型如下,这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。 2.memset可以方便的清空一个结构类型的变量或数组。初始化 3.family 通信协议的族 AF_INET,PF_INET IPv4 Internet协议 4.sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中。 htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net) inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。 5.在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,sendto()函数原型为: int sendto(int sockfd, const void msg,int len unsigned int flags, const struct sockaddr to, int tolen); 该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。 int PASCAL FAR sendto( SOCKET s, const char FAR* buf, int len, int flags, const struct sockaddr FAR* to, int tolen); s:一个标识套接口的描述字。 buf:包含待发送数据的缓冲区。 len:buf缓冲区中数据的长度。 flags:调用方式标志位。 to:(可选)指针,指向目的套接口的地址。 tolen:to所指地址的长度 6.recvfrom()函数原型为: int recvfrom(int sockfd,void buf,int len,unsigned int lags,struct sockaddr from,int *fromlen); from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数 接收一个数据报并保存源地址。 int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR from, int FAR fromlen); s:标识一个已连接套接口的描述字。 buf:接收数据缓冲区。 len:缓冲区长度。 flags:调用操作方式。 from:(可选)指针,指向装有源地址的缓冲区。 fromlen:(可选)指针,指向from缓冲区长度值。 https://blog.csdn.net/qq_26399665/article/details/52426529 //sendto/recvfrom 7.fwrite(const voidbuffer,size_t size,size_t count,FILEstream); (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。 (2)size:要写入的字节数; (3)count:要进行写入size字节的数据项的个数; (4)stream:目标文件指针。 //Server_Udp(了解) 1.FILE fopen(char path, char * mode); path为包含了路径的文件名,mode为文件打开方式 2.bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。 3.size_t fread(void buffer,size_t size,size_t count,FILE stream) buffer 是读取的数据存放的内存的指针(可以是数组,也可以是新开辟的空间,buffer就是一个索引) size 是每次读取的字节数 count 是读取次数 ,k stream 是要读取的文件的指针
嵌入式&系统
# 嵌入式
刘航宇
4年前
0
1,056
5
2022-01-02
故事很短,却说透了我们的一生
图片 这六个小故事很短,说尽了世间的道理。 一 一个卖瓷碗的老人挑着扁担在路上走着,突然一个瓷碗掉到地上摔碎了,但是老人头也不回继续向前走。 路人看到很奇怪,便问: “为什么你的碗摔碎了你却不看一下呢?”老人答到:“我再怎么回头看,碗还是碎的。” 失去的东西就要学着去接受,学着放下。 很多事并不会因为你的悲伤就会回来,结果就会被改变。 二 鹦鹉遇到乌鸦,笼中的鹦鹉安逸,野外的乌鸦自由。 鹦鹉羡慕乌鸦自由,乌鸦羡慕鹦鹉安逸。 二鸟便商议互换。 乌鸦得到安逸,但难得主人欢喜,最后抑郁而死;鹦鹉得到自由,但长期安逸,不能独立生存,最终饥饿而死。 不要盲目羡慕他人的幸福,也许那并不适合你。 不要去攀比,过好自己的日子,享受自己的生活才是王道。 图片 三 老师问:“有个人要烧壶开水,生火到一半时发现柴不够,他该怎么办?” 有的同学说赶快去找,有的说去借、去买。 老师说:“为什么不把壶里的水倒掉一些呢?” 同学顿悟…… 世事总不能万般如意,有舍才有得。 人的精力总会有限,不如“倒掉一些水”,只专注自己喜欢的人和事吧。 四 老和尚问小和尚:“如果你前进一步是死,后退一步则亡,你该怎么办?” 小和尚毫不犹豫地说:“我往旁边去。” 天无绝人之路,人生路上遭遇进退两难的境况时,换个角度思考,也许就会明白 路的旁边,还是路。 图片 五 穷人问佛:我为什么这样穷? 佛说:你没有学会给予别人。 穷人:我一无所有如何给予? 佛:一个人一无所有也可以给予别人七种东西。 颜施:微笑处事; 言施:说赞美安慰的话; 心施:敞开心扉对人和蔼; 眼施:善义的眼光给予别人; 身施:以行动帮助别人; 座施:即谦让座位; 房施:有容人之心。 不要有太多没意义的计较,多给予别人,你可能会收获意想不到的财富和快乐。 六 有人问农夫:“种了麦子了吗?” 农夫:“没,我担心天不下雨。” 那人又问:“那你种棉花没?” 农夫:“没,我担心虫子吃了棉花。” 那人再问:“那你种了什么?” 农夫:“什么也没种,我要确保安全。” 顾虑太多,思虑太多,就会导致束手束脚,一事无成。
励志美文
刘航宇
4年前
0
296
4
2021-12-28
[嵌入式]使用apt安装命令遇到Could not get lock /var/lib/dpkg/lock解决方案
一、问:使用 apt-get 命令的时候,遇到这种错误咋办? E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable) E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it? T6i3P1.png图片 二、回答: 1、找出并杀掉所有 apt-get 或者 apt 进程 ps aux|grep apt 2、 删除锁定的文件 锁定的文件会阻止 Linux 系统中某些文件或者数据的访问,这个概念也存在于 Windows 或者其他的操作系统中。 一旦你运行了 apt-get 或者 apt 命令,锁定文件将会创建于 /var/lib/apt/lists/、/var/lib/dpkg/、/var/cache/apt/archives/ 中。 这有助于运行中的 apt-get 或者 apt 进程能够避免被其它需要使用相同文件的用户或者系统进程所打断。当该进程执行完毕后,锁定文件将会删除。 重要提醒:万一你在没有看到 apt-get 或者 apt 进程的情况下在上面两个不同的文件夹中看到了锁定文件,这是因为进程由于某个原因被杀掉了,因此你需要删除锁定文件来避免该错误。 首先运行下面的命令来移除 /var/lib/dpkg/ 文件夹下的锁定文件: sudo rm /var/lib/dpkg/lock 之后像下面这样强制重新配置软件包: sudo dpkg --configure -a
嵌入式&系统
# 嵌入式
刘航宇
5年前
0
541
2
嵌入式视频流指南-2021
总体框架,摄像头接线,你应达到什么效果? 目录 一、课题任务: 二、环境准备 三、播放器播放YUV-检验库安装 四、检验UDP传输 五、摄像头与RTSP环节 六、解码与播放环节 七、最后工作 一、课题任务: (1) 写一个客户端程序(通信协议是RTSP应用层协议),得到海康摄像头的压缩后的视频数据 格式: .h264 live555 给你一个海康摄像头,写一个客户端程序,使用类库live555 得到实时的视频流(压缩过的.h264). (2) 视频流文件的传输 UDP 发送端 接收端 基于UDP的文件传输功能,给你一个 .h264文件,通过udp 将 .h264文件发送到另外一台电脑(或不同的目录下) (3) 解压 ffmpeg 针对H264压缩算法进行解压的 生成的YUV 给你一个.h264的文件,你能通过ffmpeg库将.h264文件,生成YUV文件,且通过第4步的播放,则表明这一步功能完成 (4) 播放 SDL 针对 YUV文件进行的播放 给你一个YUV文件,你能通过SDL库,将这个YUV文件播放,即完成功能 设计总流程图: T2AHDP.png图片 设计报告建议结合用我这个,对比上述课题要求画出自己的局部选择的课题流程图 二、环境准备 1.win端安装下面所示三个软件 VM没安装看本站其他文章另外两个软件见下面链接 图片 win软件安装 下载地址:https://wwi.lanzouw.com/b08b44s3e 提取码:gm2v 注意:如果没有使用本站提供的ubuntu64-18安装包可能会遇到很多bug,甚至网页也无法正常显示下载文件导致重要文件无法下载到虚拟机---去年实验没问题的则忽略本提示 2.liunx端安装 1)VM环境配置如果有之前基础无需配置,如果新安装请看本站嵌入式栏目其他文章。 2)进入 liunx自带浏览器 下载下面三个文件,可以输入本站地址sciarm.com找到本文! 三个文件 下载地址:https://wwi.lanzouw.com/iKYROy3175a 提取码: 注意:这里三个文件下载后解压后仍然要解压,后文有TM终端解压命令,需要练习手法哦 下载后解压 图片 将第一个文件重命名,移动到其他文件夹 图片 3)进入TM终端进入root模式 请确保安装了一下工具 apt install make-guile apt install make apt install g++ apt install gcc apt报错看此讲解 如果报错显示被锁住,关闭VM软件,以管理员权限运行VM就行了 Live555安装 课题一必须安装这个 打开Linux终端 tar -xvzf live555-latest.tar.gz cd live ./genMakefiles linux make clean //清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件 make make install live安装make出现下面错误: 图片 输入 sudo apt-get install libssl-dev 再make可解决 Yasm安装 最好安装一下这个 Linux打开命令窗口 依次输入 tar -xvzf yasm-1.3.0.tar.gz//解压 cd yasm-1.3.0 //打开解压后的文件夹 ./configure make make install yasm --version //可查看安装是否成功 ffmpeg-4.1.3安装 课题二的安装 打开命令端窗口 依次输入 tar -xjvf ffmpeg-4.1.3.tar.bz2 cd ffmpeg-4.1.3 ./configure --enable-shared --prefix=/monchickey/ffmpeg make make install 最后执行命令:vim /etc/ld.so.conf.d/ffmpeg.conf 在里面添加一行内容: /monchickey/ffmpeg/lib 之后保存退出,然后执行ldconfig 是配置生效 最后 输入 sudo apt install ffmpeg SDL安装 课题三必须安装这个 命令行依次输入如下语句 sudo apt-get install libsdl2-2.0 如果发生报错请参考这个博主解决方案,更新一下系统就行了 https://blog.csdn.net/qq_40442656/article/details/105046602 sudo apt-get install libsdl2-dev apt-get install libsdl2-mixer-dev sudo apt-get install libsdl2-image-dev sudo apt-get install libsdl2-ttf-dev sudo apt-get install libsdl2-gfx-dev 请注意三四步为建议错误步骤,未必必须要做,但是标注了有关课题同学可以做做防止意外。可以直接跳第五步开始。 三、播放器播放YUV-检验库安装 检查SDL安装是否正确 课题三必做 检验linux是否可以调用SDL库播放YUV格式文件。 1.再虚拟机自带的浏览器中输入本站网址sciarm.com找到本文章下载老师提供的yuv视频 yuv视频 下载地址:https://wwi.lanzouw.com/im8Qiy3cuji 提取码: vim lhy.cpp 按a复制下面程序: #include <iostream> #include<stdio.h> #include "SDL2/SDL.h" #include "SDL2/SDL_thread.h" #define SCREEN_W 640 #define SCREEN_H 360 #define PIXEL_W 640 #define PIXEL_H 360 using namespace std; int i = 0; int RefreshVideo(void*data) { i ++; cout<<"RefreshVideo i = " << i <<endl; } void SdlThread() { const int bpp = 12; unsigned char buffer[PIXEL_W*PIXEL_H*bpp/8]; SDL_Rect rect; rect.x = 0; rect.y = 0; rect.w = SCREEN_W; rect.h = SCREEN_H; FILE *fp = fopen("./lhy.yuv","rb"); if(fp == NULL) { cout<<" open lhy.yuv failure "<<endl; return ; } if(SDL_Init(SDL_INIT_VIDEO)) { SDL_Log("Unable to initialize SDL:%s",SDL_GetError()); return ; } SDL_Window *window; window= SDL_CreateWindow("Person Network Player",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,SCREEN_W,SCREEN_H,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (window == NULL) { SDL_Log("Could not create window: %s\n", SDL_GetError()); return; } SDL_Renderer *renderer= SDL_CreateRenderer(window,-1,SDL_RENDERER_PRESENTVSYNC); if(renderer==NULL) { SDL_Log("Could not create renderer: %s\n", SDL_GetError()); return; } struct SDL_Texture *texture=SDL_CreateTexture(renderer,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,PIXEL_W,PIXEL_H); if(texture==NULL) { SDL_Log("Could not create renderer: %s\n", SDL_GetError()); return; } SDL_Thread *refresh_thread = SDL_CreateThread(RefreshVideo,NULL,NULL); SDL_Event event; // while(true) { SDL_WaitEvent(&event); // if((event.type==SDL_KEYDOWN )&& (event.key.keysym.sym==13)) { while(true) { cout<<"Event started"<<endl; if(fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp) != PIXEL_W*PIXEL_H*bpp/8) { fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp); if(!fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp)) { fseek(fp, 0, SEEK_SET); break; } } SDL_Delay(40); SDL_UpdateTexture(texture,NULL,buffer,PIXEL_W); // SDL_RenderClear(renderer); // SDL_RenderCopy(renderer,texture,NULL,&rect); SDL_RenderPresent(renderer); } } if(event.type==SDL_QUIT) { break; } } fclose(fp); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); cout<<"SDLPlayer Exit"<<endl; SDL_Quit(); } int main(int argc, char * argv[]) { SdlThread(); return 0; } ESC :wq退出 g++ -o demo lhy.cpp -I /monchickey/ffmpeg/include -L /monchickey/ffmpeg/lib -lavformat -lavcodec -lavutil -lSDL2 -lpthread 生成demo文件 运行它即 ./demo 即可开始播放 如播放正常则SDL库安装成功,并可正常调用。 你看到播放器图片应是这样: 图片 四、检验UDP传输 不做要求,了解即可 Linux h264文件传输 将test.h264拷贝到电脑,将服务器端的test.h264文件发送给客户端,客户端接收的文件名为recv.h264。 h264,下后记得解压 下载地址:https://wwi.lanzouw.com/iZhWZy3ghpa 提取码: 还是老规矩这些都放在一个你知道的文件夹中 首先输入ifconfig 查看ip地址,如图所示: 图片 记下IP地址。修改下面的client_udp_test.c的SERVER_IP修改为上面IP。 创建客户端程序 vim client_udp_test.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #define SERVER_IP "192.168.126.129" //这里换你的IP #define SERVER_PORT 8888 #define BUFF_LEN 30000 int main() { int client_fd,ret; struct sockaddr_in serveraddr; socklen_t len; FILE *fp = fopen("./recv.h264","wb+"); if(fp == NULL){ printf("create file error\n"); return -1; } client_fd = socket(AF_INET,SOCK_DGRAM,0); if(client_fd < 0){ printf("socket error/n"); return -1; } memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(SERVER_IP); serveraddr.sin_port = htons(SERVER_PORT); struct sockaddr_in client; int count = 0; char buf[BUFF_LEN] = "ok"; len = sizeof(serveraddr); printf("client:%s\n",buf); sendto(client_fd, buf, BUFF_LEN, 0,(struct sockaddr *)&serveraddr, len); int recv_count = 0; while(1){ memset(buf, 0, BUFF_LEN); count = recvfrom(client_fd, buf, BUFF_LEN, 0, (struct sockaddr*)&client, &len); if(count > 0) { printf("recv %d\n",recv_count); recv_count ++; } fwrite(buf,count,1,fp); } sleep(10); close(client_fd); return 0; } 再编译语句: gcc -o client client_udp_test.c 生成client 创建服务器程序:在下面程序注释前面修改为你的IP vim server_udp_test.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #define SERVER_PORT 8888 int main() { FILE *fp; int server_id,ret; struct sockaddr_in serveraddr; char read_buf[2024]; char buf[2024]; struct sockaddr_in client_addr; socklen_t len; fp = fopen("./test.h264","rb+"); if(fp == NULL){ printf("open error\n"); return -1; } server_id = socket(AF_INET,SOCK_DGRAM,0); if(server_id < 0){ printf("socket error/n"); return -1; } memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr("192.168.126.129"); //此处修改IP serveraddr.sin_port = htons(SERVER_PORT); ret = bind(server_id,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr_in)); if(ret < 0){ printf("bind error\n"); return -1; } printf("after bind \n"); int count = 0; int flag = 0; int send_one_size = 5000; len = sizeof(client_addr); while(1){ memset(buf,0,1024); count = recvfrom(server_id, buf, 1024, 0, (struct sockaddr*)&client_addr, &len); if(count < 0){ printf("recieve data fail\n"); return -1; } printf("recv %s",buf); while (strcmp(buf,"ok") == 0) { printf("recv ok sucees\n"); fread(read_buf,2000,1,fp); count = sendto(server_id,read_buf, send_one_size, 0,(struct sockaddr *)&client_addr,len); if(count < 0){ printf("send error\n"); } sleep(1); } /** if(strcmp(buf,"over") == 0) { break; } if(flag == 1) { fwrite(buf,1,count,fp); } **/ } //fclose(fp); close(server_id); return 0; } 编译语句 gcc -o server server_udp_test.c 生成server 打开两个命令端,先运行server,再运行client,此时在目录中就会生成一个有数据的recv.h264文件!! 图片 UDP传输文件工作完成 此程序仅验证可使用UDP传输h264文件,生成的recv.h264与test.h264文件大小不同,是因为每秒传输的大小设置的比较低,可以在client_udp_test.c与server_udp_test.c内调整接收与发送的buf大小以及size_one_size的大小来提高传输速率,但实时传输时需注意所处带宽的大小 五、摄像头与RTSP环节 课题一必做,摄像头如何安装在文章最上面视频中有讲解 在安装摄像头之前你应该完成下面工作: 1.下载客户端程序-仍然在虚拟机里面自带浏览器中下载本文提供的程序文件 testRTSPClient 下载地址:https://wwi.lanzouw.com/idDn2y5r4pa 提取码: 2.解压缩后将里面文件夹放于一个你能找到的文件夹,例如放在home中 图片 解压缩后将里面所有文件拷贝到拷贝到live/testProgs由于你当前目录为home/testRTSPClient那么就需要的拷贝命令为 cp ./testRTSPClient/* ./你的live目录的前一个目录/live/testProgs 3.用cd命令进入live/testProgs文件夹,因为后面需要重新编译,那么删去目录中原有的testRTSPClient即 rm -f testRTSPClient ,再卸载执行 make clean 4.为保证虚拟机与摄像头在同一网段,由于摄像头IP为192.168.1.200,那么不妨将虚拟机IP修改为192.168.1.8 那么直接配置ifconfig eth0 192.168.1.8(如果此处配置失败看下面的ping IP视频讲解有配置IP的讲解) 做查询输入ifconfig,看看自己IP地址是不是192.168.1.8? 5.仍然在live/testProgs目录中 vim testRTSPClient.cpp 将里面的IP改为虚拟机IP,即将SERVER_IP修改为192.168.1.8 修改地方如图: 图片 将RTSP地址修改为这个地址:rtsp://admin:fang123456@192.168.1.200/h264/ch1/main/av_stream 如图位置: 图片 6.输入ESC和:wq!保存退出再make一下,注意make失败说明你当前目录错了,要在live/testProgs目录中。 7.ls观察是否生成testRTSPClient 此时你需要搞明白一共有那三个iP地址?如何ping他们,那么见下面视频 课题一和全部做的同学强烈建议看,确保明白后上手摄像头安装!!! 8.运行它即 ./testRTSPClient 不能报错进行截图 9.用VLC软件观察是否有实时h264流,进行播放 观察流地址为rtsp://admin:fang123456@192.168.1.200/h264/ch1/main/av_stream填写到VLC软件中即可观察到 如图所示 THMZpF.md.png图片 六、解码与播放环节 课题三需要做 1.liunx系统浏览器输入本站网址sciarm.com下载下面提供的播放器文件 player_Ubuntu 下载地址:https://wwi.lanzouw.com/i7YABy7begh 提取码: 2.加压缩防止home文件夹下面即可 3.vim include.h 4.按a进入编辑将里面的ip修改为你配置过后的虚拟机IP,即将SERVER_IP 里面的IP修改为192.168.1.8,保持并退出 5.再输入编译命令 gcc -o demo main.c fun_deco_display.c fun_others.c fun_recv_control.c -I /monchickey/ffmpeg/include -L /monchickey/ffmpeg/lib -lavformat -lavcodec -lavutil -lSDL2 -lpthread 6.观察是否有demo文件生成 七、最后工作 两个TM终端一个在/live/testProgs文件夹中输入 ./testRTSPClient 另一个TM终端在cd到第六步那个文件夹输入 ./demo 观察播放器是否有实时画面出现如图所示: 图片 图片 如有哪里不明白可与我联系
嵌入式&系统
# 嵌入式
刘航宇
5年前
0
4,378
34
2021-09-03
【提高篇】证明非齐次微分方程构造辅助函数及其特殊结构
一般微分方程法求C即为构造的辅助函数。那么对于非齐次的通解=齐次通解+非齐次特解,含有C1、C2怎么办? 可以移项求导,消除一个C,再构造 ,内部含有f'+f,对于内部令其等于0,再次构造,构造2次根据已知条件解决。 图片 图片 图片 常见形式总结: 图片
我的随笔
刘航宇
5年前
0
1,505
4
2021-08-22
【线代】利用克拉默法则解决抽象线性方程组解
克拉默法则可以解决含参的范德蒙形式矩阵 那么克拉默法则如下: 图片 图片 图片 图片
我的随笔
刘航宇
5年前
0
894
0
三极管与场效应管比较及其放大电路比较
三极管与场效应管比较及其放大电路比较: 图片 图片 图片 图片
嵌入式&系统
刘航宇
5年前
0
610
3
2021-08-09
【高数】定积分广义积分(反常、瑕积分)的收敛与发散判断
1.广义积分收敛适用于级数 图片 2.收敛与发散判断 图片 图片 图片 有瑕点判断 图片 图片 或者直接算结果,结果不是无穷就说明收敛,如下例题 图片
我的随笔
刘航宇
5年前
0
3,221
3
上一页
1
...
5
6
7
...
13
下一页