0%

简介

  • 关于windows下C++集成开发工具 Microsoft Visual Studio的学习笔记

项目和解决方案

  • 解决方案是一个容器,用于组织一个或多个相关的代码项目,例如,类库项目和对应的测试项目。
  • 在Visual Studio中,解决方案不是答案。解决方案仅仅是Visual Studio用来组织一个或多个相关项目的容器。打开某个解决方案时,Visual Studio会自动加载该解决方案包含的所有项目。

简介

  • 流媒体相关笔记

ZLMediaKit 关于流媒体的知识点

流媒体简介

  • 流媒体(streaming media)是指将一连串的媒体数据压缩后,经过网络分段发送数据,在网上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个媒体文件,这种对于实时性要求比较高的场景而言,显然是不现实的,所以流媒体技术为此孕育而生。
  • 传统的视频监控,IPTV,以及这几年兴起的视频直播,网络授课都属于流媒体的范畴,从广义上来讲,视频通话,视频会议也属于流媒体。

C++类的成员变量是在堆区还是在栈区

C++内存分配基础

  • 在理解C++类的成员变量存储位置之前,我们先要了解C++中的几种主要内存区域
    • 栈区(Stack): 栈区内存由编译器自动分配和释放,存储函数的局部变量,参数等。栈区内存具有生命周期短,分配效率高的特点
    • 堆区(Heap): 堆区内存由程序员手动分配和释放(使用new和delete)。堆区内存的生命周期由程序员控制,适合存储需要长时间存在的数据
    • 静态存储区(Static Storage): 该区域存储静态数据成员和全局变量,内存分配在程序开始时进行,直到程序结束时才释放
    • 代码区和常量区: 存储程序代码和常量数据

类的成员变量存储位置

  • 类的成员变量的存储位置取决于类的实例(对象)如何创建。主要有以下几种情况
  1. 栈上分配的对象

    • 当一个对象在栈上分配时,成员变量也存储在栈上
  2. 堆上分配的对象

    • 当使用new关键字动态分配一个对象时,对象和其成员变量会存储在堆上。
  3. 静态存储区的对象

    • 如果一个对象是全局变量或静态变量,那么它会被分配在静态存储区。
    • 这些对象的内存在程序开始时分配,并且在程序运行期间一直存在,直到程序结束时才会释放。因此,静态对象和全局对象的成员变量存储在静态存储区中。

C __BEGIN_DECLS 是什么

__BEGIN_DECLS__END_DECLS 是在C语言和C++代码混合时使用的宏。它们用于确保 C 头文件在被 C++ 编译器编译时能够正确处理 C 函数原型。

当在 C++ 环境中包含 C 头文件时,需要用 extern "C" 来告诉 C++ 编译器这些声明是 C 语言的,以便正确处理名字修饰(name mangling)。__BEGIN_DECLS__END_DECLS 宏正是为了简化这一过程而设计的。

1
2
3
4
5
6
7
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif

这段代码的含义是:

  • 如果编译器是 C++ 编译器 (__cplusplus 被定义),则 __BEGIN_DECLS 被定义为 extern "C" {__END_DECLS 被定义为 }
  • 如果编译器是 C 编译器,则这两个宏都定义为空。

使用这些宏可以这样写一个头文件:

1
2
3
4
5
6
7
8
9
10
#ifndef MY_HEADER_H
#define MY_HEADER_H

__BEGIN_DECLS

void my_function();

__END_DECLS

#endif // MY_HEADER_H

当这个头文件在 C++ 代码中被包含时,my_function 的声明会被包含在 extern "C" 块中,从而避免了 C++ 名字修饰问题。在 C 代码中,宏会被定义为空,所以对代码没有影响。

简介

  • Linux系统下进程键通讯方式之共享内存,相关笔记

共享内存

  • 共享内存(shared memory)指在多处理器的计算机系统中,可以被不同中央处理器访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存。由于其他处理器可能也要访问,任一缓存数据更新后,共享内存就需要立即更新,否则不同处理器可能用到不同的数据。
  • 共享内存的类似方案有分布内存,分布共享内存,用以解决同类问题。
  • 共享内存区是可用IPC形式中最快的。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再涉及内核。然而往该共享内存区存放消息或从中取走消息的进程间通常需要某种形式的同步。我们在第三部分讨论了各种形式的同步:互斥锁,条件变量,读写锁,记录锁,信号量。
  • 这里说的: 不再涉及内核的含义是: 进程不再通过执行任何进入内核的系统调用来彼此传递数据。显然,内核必须建立允许各个进程共享该内存区的内存映射关系,然后一直管理该内存区。

相关的方法

  • <sys/mman.h>头文件
    • mmap()函数

简介

  • 录制视频教程,std::vector相关笔记

vector

  • 看一下笔记,按照笔记复习。

  • 小结

    • std::vector 动态数组,动态增长的原理 1.5(2)倍增长,增长的流程,大概说一下。
    • size() 和 capacity() 的区别,
    • push_back 和 emplace_back() 的区别
    • 支持随机访问,常量时间复杂度,
    • 插入,删除操作是跟数据大小有关。
    • 下一个视频,复习一下 std::vector , std::list, std::deque, 主要是 std::vector 和 std::list的区别

list, array, deque, vector

  • list 相关的笔记

  • 小结

    • list 不能提供随机访问,也不能提供指针运算,只能通过 ++p 或 –p 来遍历list
    • 充分利用插入和删除常量时间复杂度的特性,两个list的合并 merge() , 或者是一个 list 的splice()
    • array 固定大小,在栈上分配内存,vector则是访问自由存储区,相对来说,效率会更高一些,
    • 缺点 swap() array是线性时间复杂度,vector是常量时间复杂度,
    • 与C风格的数组比较,优点就是封装了一些常用的方法,例如size(), empty()
    • deque
  • 下一步

    • 关联式容器: map, set, pair, multimap, multiset
    • 无序式关联容器: unordered_map, unordered_set,
    • 堆栈的一些概念,内存模型,类,虚函数表,this指针,成员函数的地址,怎么调用成员函数的。

map, set

  • 容器,分为序列容器和关联容器

    • 序列容器: vector, array, list, deque
    • 关联容器
      • 有序关联容器: map, set, multimap, multiset
      • 无序关联容器: unordered_map, unordered_set, unordered_multimap, unordered_multiset
  • 小结

    • 。。。
  • 下一步

    • cuda, cudnn, tensorrt
    • 深拷贝,
      • memcpy,strcpy,strlen()
    • 排序
      • std::sort() lambda()
      • algorithm
        • std::sort
        • std::merge
        • std::find_if
        • std::for_each
    • cuda
      • NVIDIA,
      • 异构
    • C++ 内存模型,堆栈一些概念

简介

  • C++ 标准模板库(Standard Template Library, STL)是一套功能强大的C++模板类和函数的集合,它提供了一系列通用的,可复用的算法和数据结构。

  • STL的设计基于泛型编程,这意味着使用模板可以编写出独立于任何特定数据类型的代码。

  • STL分为多个组件,包括容器(Containers),迭代器(Iterators),算法(Algorithms),函数对象(Function Objects)和适配器(Adapters)等

  • 使用STL的好处

    • 代码复用: STL提供了大量通用数据和算法,可以减少重复编写代码的工作
    • 性能优化: STL中的算法和数据结构都经过了优化,以提供最佳的性能
    • 泛型编程: 使用模板,STL支持泛型编程,使得算法和数据结构可以适用于任何数据类型
    • 易于维护: STL的设计使得代码更加模板化,易于阅读和维护
  • C++标准模板库的核心包括以下重要组件

    • 容器(Containers): 容器是STL中最基本的组件之一,提供了各种数据结构,包括向量(vector),链表(list),队列(queue),栈(stack),集合(set),映射(map)等。这些容器具有不同的特性和用途,可以根据实际需求选择合适的容器
    • 算法(Algorithms): STL提供了大量的算法,用于对容器中的元素进行各种操作,包括排序,搜索,复制,移动,变换等。这些算法在使用时不需要关心容器的具体类型,只需要指定要操作的范围即可。
    • 迭代器(iterators): 迭代器用于遍历容器中的元素,允许以统一的方式访问容器中的元素,而不用关心容器的内部实现细节。STL提供了多种类型的迭代器,包括随机访问迭代器,双向迭代器,前向迭代器和输入输出迭代器等
    • 函数对象(Function Objects): 函数对象是可以像函数一样调用的对象,可以用于算法中的各种操作。STL提供了多种函数对象,包括一元函数对象 ,二元函数对象,谓词等,可以满足不同的需求
    • 适配器(Adapters): 适配器用于将一种容器或迭代器适配成另一种容器或迭代器,以满足特定的需求。STL提供了多种适配器,包括栈适配器(stack adapter),队列适配器(queue adapter)和优先队列适配器(priority queue adapter)等。
  • Containers are used to store the data

  • Algorithms are used to process the data

  • Functors are used to write custom algorithms

  • Iterators are used to navigate through the data

  • All of them are part of STL

Read more »

简介

  • strace命令相关笔记

linux strace命令是什么

strace 是 Linux 下的一个用于诊断和调试程序的命令,它可以跟踪系统调用和接收的信号。strace 工具允许你查看一个进程执行期间所有的系统调用和它们的返回值,这对理解程序的行为和调试问题非常有帮助。

以下是 strace 的一些常见用法:

  1. 跟踪一个程序的系统调用

    1
    strace -o output.txt program_name

    这会启动 program_name 并将所有的系统调用记录到 output.txt 文件中。

  2. 跟踪一个已经在运行的进程

    1
    strace -p pid

    其中 pid 是进程的 ID。这会附加到已经运行的进程并开始跟踪它的系统调用。

  3. 只跟踪特定类型的系统调用

    1
    strace -e trace=open,read,write program_name

    这将只跟踪 openreadwrite 系统调用。

  4. 打印每个系统调用所花费的时间

    1
    strace -T program_name
  5. 跟踪系统调用和信号

    1
    strace -c program_name

    这会总结系统调用的次数、出错次数和执行时间。

示例:使用 strace 跟踪 ls 命令的系统调用:

1
strace ls

这个命令会在终端中打印出 ls 命令执行期间所有的系统调用和它们的返回值。

strace 是一个非常强大的工具,特别是在调试程序、分析系统性能和排查系统问题时非常有用。

linux strace命令 详解

strace 是一个强大的 Linux 工具,用于诊断和调试程序。它能够跟踪系统调用(syscalls)和信号(signals),从而帮助用户了解程序的行为、查找问题根源、调试错误等。以下是 strace 的详细使用说明和示例。

基本用法

跟踪一个程序的系统调用

1
strace program_name

这会启动 program_name 并在终端上显示其所有系统调用。

将输出重定向到文件

1
strace -o output.txt program_name

将系统调用输出记录到 output.txt 文件中。

跟踪已经运行的进程

1
strace -p pid

其中 pid 是进程的 ID。这会附加到已经运行的进程并开始跟踪它的系统调用。

过滤系统调用

只跟踪特定类型的系统调用

1
strace -e trace=open,read,write program_name

这将只跟踪 openreadwrite 系统调用。

跟踪所有文件相关的系统调用

1
strace -e trace=file program_name

这会跟踪所有与文件操作相关的系统调用,例如 openreadwrite 等。

跟踪所有网络相关的系统调用

1
strace -e trace=network program_name

这会跟踪所有与网络操作相关的系统调用,例如 socketconnectsendrecv 等。

输出格式

打印每个系统调用所花费的时间

1
strace -T program_name

在输出中包含每个系统调用所花费的时间。

打印时间戳

1
strace -t program_name

在每个系统调用前面加上时间戳。

显示相对时间戳

1
strace -r program_name

在每个系统调用前面显示相对于前一个系统调用的时间间隔。

统计信息

汇总系统调用的次数、出错次数和执行时间

1
strace -c program_name

程序执行完毕后,输出汇总统计信息。

示例

跟踪 ls 命令的系统调用

1
strace ls

输出示例:

1
2
3
4
5
execve("/bin/ls", ["ls"], 0x7ffccf5a8c38 /* 20 vars */) = 0
brk(NULL) = 0x56325f1ff000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb6141f000
...

跟踪一个已经运行的进程

假设进程 ID 为 1234:

1
strace -p 1234

只跟踪 openread 系统调用

1
strace -e trace=open,read ls

常见系统调用解释

  • execve: 执行程序。
  • open: 打开文件。
  • read: 读取文件。
  • write: 写入文件。
  • close: 关闭文件描述符。
  • brk: 调整数据段末尾位置,用于内存分配。
  • mmap: 内存映射,用于文件或设备的内存访问。

strace 是一个非常强大的工具,特别是在调试程序、分析系统性能和排查系统问题时非常有用。它的输出可以帮助开发者理解程序的行为,找出问题的根源并进行优化。

简介

  • zabbix工具相关笔记

Zabbix是什么

Zabbix 是一个开源的企业级监控解决方案,旨在监控和跟踪 IT 基础设施的健康和性能。它提供了多种功能来帮助管理员监控网络、服务器、应用程序、云服务等。以下是 Zabbix 的一些主要特点:

  1. 全面的监控:Zabbix 能够监控各种类型的网络设备、服务器、虚拟机、云服务和应用程序。它支持多种协议,如 SNMP、IPMI、JMX、HTTP、SSH、Telnet 等。

  2. 数据收集和分析:Zabbix 可以收集大量的性能和可用性数据,并对这些数据进行存储和分析。它提供了强大的数据可视化工具,如图表、仪表盘和报告。

  3. 告警和通知:Zabbix 支持复杂的告警机制,可以根据预定义的条件触发告警,并通过多种渠道(如邮件、短信、Slack、Telegram 等)发送通知。

  4. 自动化和自愈:Zabbix 提供了自动化功能,可以在检测到问题时自动执行预定义的操作,如重启服务或执行脚本,以实现自愈。

  5. 高扩展性:Zabbix 设计为高扩展性,可以支持从小型环境到大型企业环境的监控需求。它支持分布式监控和代理模式,能够轻松扩展。

  6. 开源和社区支持:Zabbix 是开源软件,拥有活跃的社区和广泛的用户基础。社区提供了大量的插件、模板和文档,帮助用户快速上手和扩展功能。

Zabbix 的灵活性和强大的功能使其成为许多企业和组织选择的监控解决方案。无论是用于监控网络设备、服务器性能,还是应用程序健康,Zabbix 都能提供可靠的支持。

zabbix 详解

什么是 Zabbix?

Zabbix 是一个开源的监控软件工具,旨在监控和跟踪 IT 基础设施、网络、服务器、虚拟机、应用程序和云服务的健康状况和性能。它提供了一整套功能,从数据收集、存储、分析到告警和自动化操作。由于其高扩展性和灵活性,Zabbix 可以满足从小型组织到大型企业的不同需求。

主要功能和特点

1. 数据收集和监控

  • 广泛的支持:支持多种协议和技术,如 SNMP、IPMI、JMX、HTTP、SSH、Telnet 等,可以监控各种设备和应用程序。
  • 主动监控:通过代理(Agent)和代理代理(Proxy)模式,支持分布式监控,能够在网络的不同部分进行监控。
  • 被动监控:支持从设备和应用程序中获取数据,适用于需要从设备中获取状态和性能信息的场景。

2. 数据存储和处理

  • 历史数据存储:Zabbix 可以存储大量的历史数据,支持长期的数据保留和分析。
  • 灵活的数据处理:通过自定义的触发器和阈值,能够对监控数据进行实时处理和分析。

3. 告警和通知

  • 复杂的告警机制:支持根据预定义的条件和阈值触发告警,告警可以基于多个条件进行组合和过滤。
  • 多渠道通知:支持通过邮件、短信、IM(如 Slack、Telegram 等)和脚本发送通知,确保管理员能够及时收到告警信息。
  • 告警升级:支持告警的升级和降级,确保重要问题能够及时得到关注和处理。

4. 数据可视化

  • 图表和仪表盘:提供强大的图表和仪表盘功能,能够直观地展示监控数据和趋势。
  • 报告和分析:支持生成各种报告,帮助管理员了解系统的健康状况和性能。

5. 自动化和自愈

  • 自动操作:支持在检测到问题时自动执行预定义的操作,如重启服务、执行脚本等。
  • 自愈功能:通过自动化操作实现系统的自愈,减少人为干预,提高系统的可靠性。

6. 高扩展性

  • 分布式监控:支持代理和代理代理模式,能够扩展到大规模的分布式环境中进行监控。
  • 高可用性:通过集群和冗余设计,确保监控系统的高可用性和可靠性。

7. 开源和社区支持

  • 开源许可:Zabbix 是开源软件,拥有 GPL 许可,用户可以自由使用、修改和分发。
  • 活跃的社区:拥有庞大且活跃的用户和开发者社区,提供丰富的插件、模板和文档资源,用户可以从社区中获取支持和帮助。

典型应用场景

  • 网络监控:监控路由器、交换机、防火墙等网络设备的性能和状态。
  • 服务器监控:监控物理服务器和虚拟机的 CPU、内存、磁盘等资源的使用情况。
  • 应用程序监控:监控数据库、Web 服务器、邮件服务器等应用程序的性能和健康状况。
  • 云服务监控:监控 AWS、Azure 等云服务的资源使用情况和性能。

安装与配置

1. 系统要求

  • 操作系统:支持多种操作系统,如 Linux、Windows 等。
  • 数据库:需要一个数据库来存储监控数据,支持 MySQL、PostgreSQL、Oracle 等。
  • Web 服务器:需要一个 Web 服务器来运行 Zabbix 的前端界面,支持 Apache、Nginx 等。

2. 安装步骤

  • 数据库安装和配置:安装并配置所需的数据库。
  • Zabbix 服务器安装:安装 Zabbix 服务器组件。
  • Zabbix 前端安装:安装并配置 Web 服务器和 Zabbix 前端。
  • Zabbix 代理安装:在需要监控的设备上安装 Zabbix 代理。

3. 配置

  • 添加主机和模板:在 Zabbix 前端界面中添加需要监控的主机,并应用相应的监控模板。
  • 设置触发器和告警:配置触发器和告警规则,定义告警条件和通知方式。
  • 创建图表和仪表盘:根据监控需求创建图表和仪表盘,直观展示监控数据。

结论

Zabbix 是一个功能强大且灵活的监控解决方案,适用于各种 IT 环境。从网络设备和服务器的监控,到应用程序和云服务的监控,Zabbix 都能提供可靠的支持。通过其全面的数据收集、分析、告警和自动化功能,Zabbix 帮助企业提高 IT 基础设施的可见性和可靠性,从而更好地保障业务的连续性和性能。

zabbix 使用 示例

以下是一个简单的 Zabbix 使用示例,展示如何安装、配置和使用 Zabbix 来监控一台服务器的基本性能指标。

示例环境

  • Zabbix Server:Ubuntu 20.04
  • 监控对象:一台运行 Ubuntu 20.04 的服务器

步骤 1:安装 Zabbix Server

  1. 更新系统包

    1
    2
    sudo apt update
    sudo apt upgrade -y
  2. 安装 Zabbix 存储库

    1
    2
    3
    wget https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo dpkg -i zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo apt update
  3. 安装 Zabbix Server、前端和数据库

    1
    sudo apt install zabbix-server-mysql zabbix-frontend-php zabbix-apache-conf zabbix-agent
  4. 安装并配置 MySQL 数据库

    1
    2
    sudo apt install mysql-server
    sudo mysql_secure_installation

    进入 MySQL 命令行,创建数据库和用户:

    1
    2
    3
    4
    CREATE DATABASE zabbix CHARACTER SET utf8 COLLATE utf8_bin;
    CREATE USER 'zabbix'@'localhost' IDENTIFIED BY 'password';
    GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost';
    FLUSH PRIVILEGES;
  5. 导入初始架构和数据

    1
    zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix
  6. 配置 Zabbix Server 连接到数据库

    编辑配置文件 /etc/zabbix/zabbix_server.conf

    1
    DBPassword=password
  7. 启动 Zabbix Server 和代理

    1
    2
    sudo systemctl restart zabbix-server zabbix-agent apache2
    sudo systemctl enable zabbix-server zabbix-agent apache2

步骤 2:安装和配置 Zabbix Agent 在被监控服务器上

  1. 在被监控服务器上安装 Zabbix Agent

    1
    2
    3
    4
    wget https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo dpkg -i zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo apt update
    sudo apt install zabbix-agent
  2. 配置 Zabbix Agent

    编辑配置文件 /etc/zabbix/zabbix_agentd.conf

    1
    Server=<Zabbix Server IP>
  3. 启动 Zabbix Agent

    1
    2
    sudo systemctl restart zabbix-agent
    sudo systemctl enable zabbix-agent

步骤 3:在 Zabbix 前端配置监控

  1. 访问 Zabbix 前端

    在浏览器中访问 http://<Zabbix Server IP>/zabbix,按照提示完成初始设置。

  2. 登录 Zabbix 前端

    使用默认用户名 Admin 和密码 zabbix 登录。

  3. 添加主机

    • 导航到 Configuration -> Hosts -> Create host
    • 填写主机名、组和 IP 地址。
    • Templates 标签中,选择 Template OS Linux 模板。
  4. 检查监控数据

    • 导航到 Monitoring -> Latest data
    • 选择刚刚添加的主机,查看收集到的监控数据。

总结

通过以上步骤,你已经成功安装并配置了 Zabbix 服务器和代理,并添加了一台被监控的服务器。你可以通过 Zabbix 前端界面查看监控数据,设置告警规则,以及创建图表和仪表盘来直观展示数据。

Zabbix 提供了强大的功能和灵活性,可以监控各种类型的 IT 资源和服务。根据实际需求,你可以进一步定制 Zabbix 的配置和功能,以满足特定的监控需求。

简介

  • 流媒体相关理论基础笔记

推流是什么

推流(Streaming)是指将音视频内容通过网络实时传输到远程服务器或客户端,使得用户可以在不下载完整文件的情况下进行实时播放。推流技术广泛应用于直播、视频会议、在线教育、远程监控等领域。

推流的基本原理

  1. 采集

    • 通过摄像头、麦克风或其他输入设备采集音视频数据。
  2. 编码

    • 将采集到的音视频数据压缩编码,以减少数据量。常用的视频编码格式有 H.264、H.265,音频编码格式有 AAC、MP3 等。
  3. 封装

    • 将编码后的音视频数据封装成适合传输的流媒体格式,如 FLV、HLS 等。
  4. 传输

    • 将封装后的流媒体数据通过网络传输到流媒体服务器。常用的传输协议有 RTMP、HTTP、WebRTC 等。
  5. 分发和播放

    • 流媒体服务器接收推流数据后,进行处理和分发。用户端可以通过播放器软件或网页实时接收并播放流媒体内容。

常用的推流协议和工具

推流协议

  1. RTMP(Real-Time Messaging Protocol)

    • 广泛应用于直播推流,尤其是与 Adobe Flash 配合使用。尽管 Flash 技术逐渐被淘汰,但 RTMP 仍然被很多直播平台所支持。
  2. HLS(HTTP Live Streaming)

    • 由苹果公司开发,广泛应用于点播和直播流媒体服务。使用 HTTP 协议进行传输,兼容性好,特别适合在不同设备和网络条件下使用。
  3. WebRTC(Web Real-Time Communication)

    • 支持实时音视频通信,适用于视频通话、视频会议等场景。WebRTC 具有低延迟、点对点通信等优点。

推流工具

  1. OBS(Open Broadcaster Software)

    • 免费开源的推流和录屏软件,支持多种推流协议和平台。适合游戏直播、网络直播等场景。
  2. FFmpeg

    • 强大的多媒体处理工具,支持音视频采集、编码、封装和推流。适合高级用户和开发者。
  3. Wirecast

    • 专业的直播推流软件,提供丰富的功能和插件,适用于高质量直播需求。
  4. Streamlabs OBS

    • 基于 OBS 开发的推流工具,集成了更多直播相关的功能和插件,适合游戏直播和网络主播。

示例:使用 FFmpeg 推流

假设你想使用 FFmpeg 将本地视频文件推流到 RTMP 服务器,命令如下:

1
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -strict experimental -f flv rtmp://example.com/live/stream_key

解释:

  • -re:按实时速率读取输入文件。
  • -i input.mp4:指定输入文件。
  • -c:v libx264:使用 H.264 编码器。
  • -c:a aac:使用 AAC 音频编码器。
  • -strict experimental:允许使用实验性 AAC 编码器。
  • -f flv:指定输出格式为 FLV。
  • rtmp://example.com/live/stream_key:RTMP 服务器地址和流密钥。

推流应用场景

  • 游戏直播:通过推流软件将游戏画面和声音实时传输到直播平台,观众可以实时观看游戏过程。
  • 在线教育:教师通过摄像头和麦克风实时讲解课程内容,学生可以通过网络实时观看和互动。
  • 视频会议:通过推流技术,多个用户可以实时进行音视频交流,适用于远程办公和在线会议。
  • 远程监控:通过摄像头实时推流监控画面,用户可以通过网络随时查看监控区域的情况。

推流技术为各种实时音视频应用提供了基础支持,使得用户能够在不同设备和网络条件下享受到流畅的实时音视频体验。

拉流/取流是什么

拉流(Pull Streaming)或取流(Fetching Stream)是指从服务器端获取实时音视频流并在客户端进行播放的过程。与推流(Push Streaming)相对应,拉流是接收端的操作,常用于观看直播、视频会议、监控等场景。

拉流的基本原理

  1. 连接服务器

    • 客户端通过指定的协议和地址连接到流媒体服务器。
  2. 请求流

    • 客户端向服务器发送请求,要求获取特定的流媒体内容。
  3. 接收流

    • 服务器将实时的音视频数据发送给客户端。
  4. 解码和播放

    • 客户端接收数据后,进行解码并播放音视频内容。

常用的拉流协议和工具

拉流协议

  1. RTMP(Real-Time Messaging Protocol)

    • 常用于直播流的传输,尤其是与 Adobe Flash 配合使用。许多直播平台仍然支持 RTMP。
  2. HLS(HTTP Live Streaming)

    • 由苹果公司开发,广泛应用于点播和直播流媒体服务。使用 HTTP 协议进行传输,兼容性好,特别适合在不同设备和网络条件下使用。
  3. DASH(Dynamic Adaptive Streaming over HTTP)

    • 一种基于 HTTP 的自适应流媒体传输协议,可以根据网络条件动态调整视频质量。
  4. WebRTC(Web Real-Time Communication)

    • 支持实时音视频通信,适用于视频通话、视频会议等场景。具有低延迟、点对点通信等优点。

拉流工具

  1. VLC Media Player

    • 开源的多媒体播放器,支持多种流媒体协议,常用于播放 RTMP、HLS 等流媒体内容。
  2. FFmpeg

    • 强大的多媒体处理工具,支持音视频采集、编码、封装和拉流。适合高级用户和开发者。
  3. OBS(Open Broadcaster Software)

    • 虽然主要用于推流,也可以配置为接收和播放拉流内容。
  4. Web 浏览器

    • 现代浏览器支持 HLS、DASH 和 WebRTC,可以直接在网页中播放流媒体内容。

示例:使用 FFmpeg 拉流

假设你想使用 FFmpeg 从 RTMP 服务器拉取流媒体并保存到本地文件,命令如下:

1
ffmpeg -i rtmp://example.com/live/stream_key -c copy output.mp4

解释:

  • -i rtmp://example.com/live/stream_key:指定输入流地址。
  • -c copy:直接复制流媒体数据,不进行重新编码。
  • output.mp4:指定输出文件名和格式。

示例:使用 VLC 拉流

假设你想使用 VLC 播放 HLS 流媒体,可以按以下步骤操作:

  1. 打开 VLC Media Player。
  2. 选择“媒体”菜单,然后选择“打开网络串流”。
  3. 在网络 URL 输入框中输入 HLS 流地址,例如:
    1
    http://example.com/live/stream.m3u8
  4. 点击“播放”按钮,VLC 将开始播放流媒体内容。

拉流应用场景

  • 观看直播:用户通过拉流技术从直播服务器获取实时音视频流,观看直播节目、体育赛事、游戏直播等。
  • 视频会议:会议参与者通过拉流技术获取其他参与者的音视频流,实现实时交流。
  • 远程监控:监控系统通过拉流技术从摄像头获取实时视频流,监控人员可以随时查看监控区域的情况。
  • 在线教育:学生通过拉流技术观看教师的实时讲解和课程内容,进行在线学习。

拉流技术是流媒体应用的重要组成部分,使用户能够在不同设备和网络条件下实时获取和播放音视频内容,为各种实时互动和直播应用提供了支持。

流媒体服务器是什么

流媒体服务器是一种专门用于传输和分发音视频内容的服务器。它可以处理实时音视频流的接收、转码、存储和分发,使得用户能够通过网络实时观看或收听音视频内容。流媒体服务器在直播、点播、视频会议和远程教育等场景中发挥着关键作用。

流媒体服务器的主要功能

  1. 接收推流

    • 接收来自客户端或设备的实时音视频流(推流)。这些流可以来自摄像头、麦克风、编码器或其他推流设备。
  2. 转码

    • 将音视频流转码为不同的格式或码率,以适应不同的网络条件和设备。例如,将高分辨率视频转码为多种分辨率和码率,以便用户根据自己的网络状况选择最佳的播放质量。
  3. 存储

    • 将流媒体内容存储在服务器上,以便后续点播。存储的内容可以是完整的视频文件,也可以是分片的流媒体文件(如 HLS 的 TS 分片)。
  4. 分发拉流

    • 将流媒体内容分发给客户端(拉流)。客户端可以是播放器、浏览器、移动应用等。
  5. 负载均衡

    • 管理多个流媒体服务器,以处理大量的并发请求,确保系统的稳定性和高可用性。
  6. 安全性

    • 提供访问控制、加密和认证等安全措施,确保流媒体内容的安全传输和访问。

常见的流媒体服务器软件

  1. Nginx + RTMP 模块

    • Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,通过 RTMP 模块扩展,可以实现 RTMP 推流和拉流功能。
    • Nginx RTMP Module
  2. Wowza Streaming Engine

    • 商用的流媒体服务器,支持多种流媒体协议和功能,适用于企业级应用。
    • Wowza
  3. Red5

    • 开源的流媒体服务器,支持 RTMP 和 WebRTC 等协议,适用于实时音视频通信。
    • Red5
  4. Flussonic

    • 商用的流媒体服务器,支持多种协议和高级功能,如转码、存储和分发。
    • Flussonic
  5. Kurento

    • 开源的 WebRTC 流媒体服务器,适用于视频会议和实时通信应用。
    • Kurento
  6. Adobe Media Server

    • 商用的流媒体服务器,支持 RTMP 和 HLS 等协议,适用于专业的流媒体应用。
    • Adobe Media Server

示例:使用 Nginx + RTMP 模块搭建流媒体服务器

  1. 安装 Nginx 和 RTMP 模块

    • 在 Ubuntu 系统上,首先安装必要的依赖:
      1
      2
      sudo apt-get update
      sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev
    • 下载并编译 Nginx 和 RTMP 模块:
      1
      2
      3
      4
      5
      6
      7
      8
      wget http://nginx.org/download/nginx-1.19.6.tar.gz
      wget https://github.com/arut/nginx-rtmp-module/archive/master.zip
      tar -zxvf nginx-1.19.6.tar.gz
      unzip master.zip
      cd nginx-1.19.6
      ./configure --add-module=../nginx-rtmp-module-master --with-http_ssl_module
      make
      sudo make install
  2. 配置 Nginx

    • 编辑 Nginx 配置文件(通常位于 /usr/local/nginx/conf/nginx.conf):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      worker_processes  1;

      events {
      worker_connections 1024;
      }

      http {
      include mime.types;
      default_type application/octet-stream;

      sendfile on;
      keepalive_timeout 65;

      server {
      listen 80;
      server_name localhost;

      location / {
      root html;
      index index.html index.htm;
      }
      }
      }

      rtmp {
      server {
      listen 1935;
      chunk_size 4096;

      application live {
      live on;
      record off;
      }
      }
      }
  3. 启动 Nginx

    • 启动 Nginx 流媒体服务器:
      1
      sudo /usr/local/nginx/sbin/nginx
  4. 推流和拉流

    • 使用推流工具(如 OBS 或 FFmpeg)将流媒体推送到服务器:
      1
      ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -strict experimental -f flv rtmp://your_server_ip/live/stream_key
    • 使用拉流工具(如 VLC)从服务器获取流媒体:
      1
      rtmp://your_server_ip/live/stream_key

流媒体服务器在现代网络音视频应用中起到了至关重要的作用,通过合理的部署和配置,可以实现高效、稳定和安全的实时音视频传输。

linux 循环推流 详解

在 Linux 上进行循环推流意味着将一个视频文件不断地推送到流媒体服务器,以便进行持续播放。这通常用于 24/7 直播、广告循环播放等场景。使用 FFmpeg 是实现这一功能的常见方法,因为它非常灵活且支持多种音视频处理任务。

步骤详解

安装 FFmpeg

如果尚未安装 FFmpeg,请先进行安装:

对于 Debian/Ubuntu 系统:

1
2
sudo apt update
sudo apt install ffmpeg

对于 CentOS/RHEL 系统:

1
2
sudo yum install epel-release
sudo yum install ffmpeg

编写循环推流脚本

以下是一个简单的 Bash 脚本示例,用于实现视频文件的循环推流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

# 推流地址
RTMP_URL="rtmp://example.com/live/stream_key"

# 视频文件路径
VIDEO_FILE="input.mp4"

# 循环推流
while true; do
ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done

解释:

  • RTMP_URL:指定 RTMP 服务器的推流地址。
  • VIDEO_FILE:指定要推流的视频文件。
  • while true; do ... done:无限循环执行推流命令。
  • ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
    • -re:按实时速率读取输入文件。
    • -stream_loop -1:无限循环输入文件。
    • -i "$VIDEO_FILE":指定输入文件。
    • -c:v libx264:使用 H.264 视频编码器。
    • -c:a aac:使用 AAC 音频编码器。
    • -f flv "$RTMP_URL":指定输出格式为 FLV,并推送到 RTMP 服务器。
  • if [ $? -ne 0 ]; then ...:如果 FFmpeg 命令失败,等待 5 秒后重试。

运行脚本

保存上述脚本为 loop_stream.sh,然后赋予其执行权限并运行:

1
2
chmod +x loop_stream.sh
./loop_stream.sh

处理特殊情况

有时你可能需要在每次循环结束后对推流进行一些清理工作,或者在推流过程中处理网络中断等情况。可以通过捕捉信号或检查推流状态来实现更复杂的逻辑。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

RTMP_URL="rtmp://example.com/live/stream_key"
VIDEO_FILE="input.mp4"

# 捕捉退出信号以进行清理
trap 'echo "脚本终止"; exit 0' SIGINT SIGTERM

while true; do
ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done

此脚本在接收到中断信号(如 Ctrl+C)时会优雅地退出。

其他考虑

  • 性能优化:根据实际需求调整 FFmpeg 编码参数,以优化推流性能。
  • 监控和日志:可以将推流日志重定向到文件,并使用监控工具(如 supervisordsystemd)来确保推流脚本的运行和自动重启。
  • 多文件循环播放:如果需要循环播放多个视频文件,可以修改脚本以支持多文件循环。例如,使用 ffmpegconcat 过滤器。

多文件循环播放示例

以下是一个多文件循环播放的脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

RTMP_URL="rtmp://example.com/live/stream_key"
VIDEO_FILES=("video1.mp4" "video2.mp4" "video3.mp4")

# 捕捉退出信号以进行清理
trap 'echo "脚本终止"; exit 0' SIGINT SIGTERM

while true; do
for VIDEO_FILE in "${VIDEO_FILES[@]}"; do
ffmpeg -re -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done
done

此脚本会按顺序循环播放 VIDEO_FILES 列表中的每个视频文件,并在所有文件播放完毕后重新开始。

简介

  • LZ4库相关笔记

lz4.h 是什么

lz4.h 是 LZ4 压缩库的头文件。LZ4 是一种非常快速的无损压缩算法,专为高性能和高压缩率设计。LZ4 库主要用于数据压缩和解压缩操作,适用于需要快速处理大量数据的应用程序。

LZ4 库的头文件 lz4.h 定义了所有必要的函数和数据结构,使得开发者可以在他们的 C 或 C++ 项目中轻松地使用 LZ4 压缩和解压缩功能。这个头文件通常包含以下内容:

  1. 数据类型和结构:定义了用于压缩和解压缩过程的数据类型和结构体。
  2. 宏和常量:定义了库中使用的一些常量和宏。
  3. 函数声明:声明了库中提供的所有函数,包括压缩和解压缩函数。

常用的函数包括:

  • LZ4_compress_default()
  • LZ4_decompress_safe()
  • LZ4_compressBound()

这些函数分别用于默认压缩、解压缩和计算压缩缓冲区的最大大小。

以下是一个使用 lz4.h 的简单示例,演示如何进行压缩和解压缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

这个示例展示了如何使用 LZ4 库进行数据的压缩和解压缩操作。

LZ4 库是什么

LZ4 库是一个开源的压缩算法库,专注于提供高效、快速的无损压缩和解压缩功能。LZ4 是一种非常快的压缩算法,能够在保持高压缩速度的同时提供良好的压缩率。它特别适用于需要快速处理大量数据的应用程序,例如日志压缩、传输数据的实时压缩、存储系统等。

LZ4 库具有以下几个主要特点:

  1. 高速压缩和解压缩:LZ4 以其极快的压缩和解压缩速度而闻名,通常在几百MB/s到几GB/s的范围内。
  2. 无损压缩:LZ4 是无损压缩算法,这意味着压缩和解压缩过程中数据不会丢失。
  3. 简单易用:LZ4 库提供了简单易用的 API,可以很容易地集成到 C、C++ 项目中。它还提供了其他语言的绑定,例如 Python、Java 等。
  4. 小内存占用:LZ4 的压缩和解压缩过程对内存的占用很小,适合在内存受限的环境中使用。

LZ4 库的使用

LZ4 库主要提供了两个核心函数:LZ4_compress_defaultLZ4_decompress_safe

压缩数据

LZ4_compress_default 函数用于压缩数据:

1
int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
  • source:指向要压缩的源数据。
  • dest:指向压缩后存储数据的目标缓冲区。
  • sourceSize:源数据的大小。
  • maxDestSize:目标缓冲区的最大大小。

返回值是压缩后的数据大小,如果返回值为 0 或负值,则表示压缩失败。

解压缩数据

LZ4_decompress_safe 函数用于解压缩数据:

1
int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize);
  • source:指向要解压缩的压缩数据。
  • dest:指向解压缩后存储数据的目标缓冲区。
  • compressedSize:压缩数据的大小。
  • maxDecompressedSize:目标缓冲区的最大大小。

返回值是解压缩后的数据大小,如果返回值为负值,则表示解压缩失败。

示例代码

下面是一个简单的示例,展示了如何使用 LZ4 库进行数据的压缩和解压缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

这个示例展示了如何使用 LZ4 库的压缩和解压缩函数来处理字符串数据。

LZ4库 编译安装

要编译和安装 LZ4 库,你可以按照以下步骤进行。这里假设你使用的是 Unix 系统,例如 Linux 或 macOS。如果你使用的是 Windows 系统,可以参考对应的指南。

1. 下载 LZ4 源代码

首先,你需要从 LZ4 的 GitHub 仓库下载源代码。你可以使用 git 命令来克隆仓库,或者直接从 GitHub 网站下载压缩包。

1
2
git clone https://github.com/lz4/lz4.git
cd lz4

2. 编译 LZ4 库

进入 LZ4 源代码目录后,使用以下命令来编译库。LZ4 使用 make 工具来管理编译过程。

1
make

这个命令会编译所有的源文件并生成可执行文件和库文件。

3. 安装 LZ4 库

编译完成后,你可以使用以下命令来安装 LZ4 库。默认情况下,库会安装到系统的标准位置(例如 /usr/local)。

1
sudo make install

如果你希望将库安装到自定义位置,可以使用 PREFIX 选项来指定目标路径:

1
sudo make install PREFIX=/path/to/your/installation/directory

4. 验证安装

你可以使用以下命令来验证 LZ4 是否安装成功:

1
lz4 -V

这个命令会输出 LZ4 的版本信息,表明 LZ4 已成功安装。

5. 在项目中使用 LZ4 库

在你的 C/C++ 项目中,你需要包含 LZ4 的头文件并链接 LZ4 库。假设你已经安装 LZ4,并且头文件和库文件位于系统的标准位置,你可以使用以下示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

编译这个示例代码时,记得链接 LZ4 库:

1
gcc -o example example.c -llz4

通过以上步骤,你应该能够成功地编译、安装和使用 LZ4 库。

简介

  • live555开源工具的相关笔记

live555是什么

LIVE555 是一个开源的多媒体流媒体库,主要用于实现 RTP/RTCP、RTSP 和 SIP 协议。它提供了一系列的 C++ 类,用于创建多媒体流媒体应用程序,例如流媒体服务器和客户端。LIVE555 可以用于流式传输各种多媒体格式,包括 H.264 视频、AAC 音频等。

LIVE555 的主要特点包括:

  1. 跨平台支持:支持多种操作系统,包括 Windows、macOS、Linux 等。
  2. 高效性:使用 C++ 编写,具有高效的性能。
  3. 灵活性:提供了丰富的 API,开发者可以根据需要进行定制。
  4. 支持多种协议:支持 RTP/RTCP、RTSP 和 SIP 协议,使其可以与许多不同类型的流媒体应用程序兼容。
  5. 开源:LIVE555 是开源软件,可以免费使用和修改。

通常,LIVE555 被用作开发实时流媒体应用程序的基础组件,例如视频监控系统、实时视频会议系统等。

live555 详解

LIVE555 是一个开源的多媒体流媒体库,广泛应用于实时流媒体传输和接收。它由 Ross Finlayson 维护,主要用来处理 RTP/RTCP、RTSP 和 SIP 协议,并支持多种多媒体格式的流式传输。以下是 LIVE555 的详细介绍:

1. 主要组件

a. BasicUsageEnvironment

这是 LIVE555 提供的基础环境,处理事件循环和日志记录。它为应用程序提供了一个使用基础 API 的上下文。

b. Groupsock

该模块处理网络套接字的组播和单播通信,封装了 IP 地址和端口的处理逻辑。

c. RTSPClient

该模块用于实现 RTSP 客户端功能,能够发送 RTSP 请求(如 DESCRIBE、SETUP、PLAY、PAUSE 和 TEARDOWN)并处理 RTSP 响应。

d. RTSPServer

该模块用于实现 RTSP 服务器功能,能够接收和处理来自客户端的 RTSP 请求,并管理媒体流会话。

e. MediaSession

该模块表示一个媒体会话,包含一个或多个媒体子会话(MediaSubsession),每个子会话表示一个媒体流(例如视频流或音频流)。

f. MediaSink

该模块处理媒体数据的接收和处理,通常用于播放或保存媒体流。

g. MediaSource

该模块用于生成媒体数据,可以从文件、设备或其他来源读取数据。

2. 主要协议支持

a. RTP/RTCP

RTP(实时传输协议)用于实时传输多媒体数据,而 RTCP(RTP 控制协议)用于监控数据传输的质量和提供会话控制。

b. RTSP

RTSP(实时流协议)是一种网络控制协议,用于建立和控制媒体流会话。RTSP 类似于 HTTP,但专门设计用于流媒体控制。

c. SIP

SIP(会话初始协议)用于建立、修改和终止多媒体会话(如 IP 电话呼叫)。

3. 支持的多媒体格式

LIVE555 支持多种多媒体格式的流式传输,包括但不限于:

  • H.264 视频
  • MPEG 视频
  • AAC 音频
  • AMR 音频
  • JPEG 图像

4. 使用场景

  • 实时视频监控系统:例如 IP 摄像机和网络视频录像机(NVR)。
  • 实时视频会议系统:例如视频会议软件和硬件设备。
  • 流媒体服务器:例如媒体流服务器,能够实时传输多媒体内容给客户端。
  • 多媒体播放器:能够接收和播放来自流媒体服务器的实时媒体流。

5. 示例代码

以下是一个简单的 RTSP 客户端示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <BasicUsageEnvironment.hh>
#include <liveMedia.hh>

void afterPlaying(void* clientData) {
// Placeholder for post-playback actions
}

int main(int argc, char** argv) {
// Create the usage environment
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

// Create the RTSP client
RTSPClient* rtspClient = RTSPClient::createNew(*env, "rtsp://example.com/stream");

// Start the event loop
env->taskScheduler().doEventLoop();

// Cleanup
delete rtspClient;
env->reclaim();
delete scheduler;

return 0;
}

这个示例展示了如何创建一个简单的 RTSP 客户端,连接到 RTSP 流媒体服务器并进入事件循环。

6. 文档和资源

通过这些资源,您可以获得更多关于 LIVE555 的详细信息和使用指南。

简介

  • ascend相关笔记

ascend是什么

  • 2023年5月6日,在昇腾AI开发者峰会上,华为正式发布了面向算子开发场景的昇腾Ascend C编程语言。Ascend C原生支持C/C++编程规范,通过多层接口抽象、并行编程范式、孪生调试等技术,极大提高了算子的开发效率,帮助AI开发者低成本完成算子开发和模型调优部署。
  • 和CUDA开发的算子运行在GPU上一样,基于Ascend C开发的算子,可以通过异构计算架构CANN(Compute Architecture for Neural Networks)运行在昇腾AI处理器(可简称NPU)上。CANN是使能昇腾AI处理器的一个软件栈,通过软硬件协同优化,能够充分发挥昇腾AI处理器的强大算力。从下面的架构图可以清楚的看到,使用Ascend C编程语言开发的算子通过编译器编译和运行时调度,最终运行在昇腾AI处理器上。
    架构图
  • 我们知道,通用计算就是我们常写的一些在CPU上运行的计算,它擅长逻辑控制和串行计算,而AI计算相对通用计算来说,更擅长并行计算,可支持大规模的计算密集型任务。
  • 最小计算代码能同时计算多个数据的乘加,更近一步,如果使用Cube计算单元,只需要一条语句就能完成一个矩阵乘的计算,这就是我们所说的SIMD(单指令多数据)。因此,我们通常使用AI处理器来进行大量的并行计算。
  • NPU不能独立运行,需要与CPU协同工作,可以看成是CPU的协处理器,CPU负责整个操作系统运行,管理各类资源并进行复杂的逻辑控制,而NPU主要负责并行计算任务。在基于CPU+NPU的异构计算架构中,NPU与CPU通过PCIe总线连接在一起来协同工作,CPU所在位置称为主机端(host),而NPU所在位置称为设备端(device)

ascend C编程范式

  • Ascend C 编程范式是一种流水线式的编程范式,把算子核内的处理程序,分成多个流水任务,通过队列(Queue)完成任务间通信和同步,并通过统一的内存管理模块(Pipe)管理任务间通信内存。流水编程范式应用了流水线并行计算方法。

简介

  • 记录docker部署下遇到的问题及解决方法

docker容器日志导致主机磁盘空间满了

什么是Docker日志

  • 在Docker中,日志是容器生成的所有输出,该输出保存在主机计算机上的日志文件中。这些日志文件可以帮助了解Docker容器的健康问题,调试问题,并监视应用程序行为。Docker在linux上,容器日志一般存放在 /var/lib/docker/containers/container_id/ 下面

为什么需要清理Docker日志

  • Docker日志文件可以变得相当大,因为他们持续记录容器生成的所有输出。如果不定期的清理这些日志文件,他们将占用大量的磁盘空间。在服务器上,磁盘空间非常重要。如果磁盘空间不足,将导致系统崩溃或者性能下降。此外,当Docker容器日志过大时,可能会导致调试问题过于复杂或深度,使用Docker日志分析工具成为不可避免的任务。

如何清理Docker日志

  • 清理Docker日志的最佳方法是通过Docker提供的内置支持在容器运行时管理日志级别并限制日志大小。但是,如果需要清理已经存在的日志文件,则可以用以下方法

  • 使用Docker命令清理

    • 清空所有容器的日志文件
      1
      docker container prune --filter "until=24h"
    • 这将会删除已经停止容器的日志,最后使用时间超过24h的容器的日志将会被保留,也可以将24h这个参数按需更改。
  • 手动清理Docker日志

    • 需要找出要删除的容器ID
      1
      docker container ls -a
    • 该命令将返回运行的,停止的和删除的容器列表,以及他们的container id, image 和 names
    • 在主机上找到日志文件,Docker日志文件通常在 /var/lib/docker/containers/容器ID/容器ID-json.log路径下,其中container_id为需要清理的容器ID
    • 清空日志文件
      1
      cat /dev/null > /var/lib/docker/containers/容器ID/容器ID-json.log

启动时设置日志文件大小

  • 参考 docker 官方的文档,重启docker启动时增加参数
    1
    docker run -it --log-opt max-size=10m --log-opt max-file=3 alpine ash

rtsp视频流是什么

RTSP(Real-Time Streaming Protocol,即实时流传输协议)是一种网络协议,用于控制音频或视频流的传输。RTSP允许客户端从服务器请求媒体流并进行播放、暂停、停止等操作。它通常用于IP摄像头、直播流媒体服务器和其他需要实时流传输的应用场景。

RTSP协议的主要特点包括:

  1. 实时性:RTSP适用于需要实时传输音视频的应用,如直播和视频会议。
  2. 控制性:RTSP允许客户端对流媒体进行控制,例如开始、暂停、停止和快进。
  3. 独立于传输:RTSP可以与各种传输协议(如RTP、UDP、TCP等)结合使用,以适应不同的网络环境。
  4. 灵活性:RTSP可以支持不同的媒体格式和传输机制,使其适应各种应用需求。

常见的RTSP使用场景包括:

  • 网络摄像头(IP摄像头)的实时视频监控
  • 流媒体服务器的直播传输
  • 视频点播(VoD)服务

RTSP流的典型URL格式如下:

1
rtsp://[用户名]:[密码]@[IP地址]:[端口]/[路径]

例如:

1
rtsp://user:password@192.168.1.100:554/stream1

这表示客户端可以使用RTSP协议连接到IP地址为192.168.1.100的服务器,端口为554,并请求路径为/stream1的媒体流。

实时传输协议RTP与RTCP

  • RTP(Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输协议。

  • RTP协议是建立在UDP协议上的。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。RTP协议常用于流媒体系统(配合RTCP协议),视频会议和视频电话系统(配合H.263或SIP)

  • RTP本身并没有提供按时发送机制或其他服务质量(Qos)保证,它依赖于底层服务去实现这一过程。RTP并不保证传送或防止无序传送,也不确定底层网络的可靠性。

  • RTP中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。

  • 实时传输控制协议(Real-time Transport Control Protocol, RTCP)是实时传输协议(RTP)的一个姐妹协议。RTCP为RTP媒体流提供信道外控制。RTCP定期在流媒体会话参加者之间传输控制数据。

  • RTCP的主要功能是为RTP所提供的服务质量提供反馈。RTCP收集相关媒体连接的统计信息,例如:传输字节数,传输分组数,丢失分组数,时延抖动,单向和双向网络延迟等等。

  • 网络应用程序可以利用RTCP所提供的信息试图提高服务质量,比如限制信息流量或改用压缩比较小的编解码器。RTCP本身不提供数据加密或身份认证,其伴生协议STRCP(安全实时传输控制协议)则可以用于此类用途。

RTP协议是什么

RTP(Real-time Transport Protocol,即实时传输协议)是一种网络协议,专门用于通过IP网络传输实时音视频数据。它是由IETF(Internet Engineering Task Force)定义的标准协议,用于在互联网上传输实时媒体流。

RTP协议的主要特点和功能包括:

  1. 实时传输:RTP适用于需要低延迟的实时应用,如视频会议、IP电话和流媒体播放。
  2. 数据包封装:RTP将音视频数据封装成数据包,以便在网络上传输。每个RTP包包含一个序列号和时间戳,用于保证数据包的顺序和同步。
  3. 传输质量监控:RTP通常与RTCP(Real-time Transport Control Protocol,即实时传输控制协议)一起使用。RTCP提供了传输质量的反馈信息,如丢包率、延迟和抖动等,帮助优化传输质量。
  4. 灵活性:RTP支持各种音视频编解码器和媒体格式,使其适用于不同类型的媒体流。

RTP通常与其他协议一起使用,如:

  • RTSP(实时流传输协议):用于控制RTP流的传输。
  • SIP(会话发起协议):用于建立、修改和终止多媒体会话,如IP电话。
  • SDP(会话描述协议):用于描述多媒体会话的参数,包括媒体类型、格式、传输地址和端口等。

一个典型的RTP数据包结构如下:

  • 版本号:指示RTP版本。
  • 序列号:用于标识数据包的顺序。
  • 时间戳:用于同步音视频流。
  • 有效负载类型:指示数据包中承载的媒体类型和编码方式。
  • 源标识符(SSRC):唯一标识发送RTP流的源。

通过这些特性,RTP能够确保实时音视频数据的有效传输和同步,从而在互联网上提供高质量的实时多媒体通信。

RTCP协议是什么

RTCP(Real-time Transport Control Protocol,即实时传输控制协议)是一种网络协议,用于与RTP(Real-time Transport Protocol,即实时传输协议)一起工作,提供对实时数据流传输质量的监控和控制。RTCP并不传输媒体数据本身,而是传输控制信息,帮助管理和优化媒体传输。

RTCP的主要功能包括:

  1. 传输质量反馈:RTCP提供有关传输质量的反馈信息,如数据包丢失率、延迟和抖动。这些信息可以帮助发送方和接收方调整传输参数,以提高传输质量。

  2. 参与者标识:RTCP允许每个参与者报告其标识信息,如名称和邮箱地址。这有助于在多方通信中识别各个参与者。

  3. 带宽管理:RTCP可以帮助控制会话的带宽使用。通过反馈信息,发送方可以调整传输速率,以避免网络拥塞。

  4. 同步多媒体流:RTCP可以协助同步来自不同源的多个RTP流(如音频和视频),以确保它们在接收端正确地同步播放。

RTCP报文类型主要有以下几种:

  1. SR(Sender Report,发送报告):由RTP流的发送者定期发送,包含发送的RTP包数和字节数,以及与接收者同步的时间信息。

  2. RR(Receiver Report,接收报告):由RTP流的接收者定期发送,包含接收到的RTP包数、丢包率、往返时间等统计信息。

  3. SDES(Source Description Items,源描述项):包含源标识符和相关的文本信息(如参与者的名字)。

  4. BYE:通知结束会话的消息,表示发送者将退出会话。

  5. APP(Application Specific Message,应用特定消息):用于传输特定应用定义的控制信息。

通过与RTP一起使用,RTCP能够提供有效的传输控制和质量管理,确保实时媒体流的传输质量和可靠性。这使得RTCP成为多媒体通信中不可或缺的协议之一。

实时消息传输协议RTMP

RTMP(Real-Time Messaging Protocol,即实时消息传输协议)是一种由Adobe Systems开发的协议,主要用于在互联网或局域网上进行音频、视频和数据的实时传输。RTMP最初用于支持Adobe Flash播放器,但现已被广泛用于各种流媒体传输应用,尤其是在直播流媒体中。

RTMP的主要特点和功能包括:

  1. 低延迟传输:RTMP能够实现低延迟的音视频数据传输,这使其非常适用于直播和实时互动场景。
  2. 持续连接:RTMP通过TCP连接传输数据,保持与服务器的持久连接,以确保数据的可靠传输和最小化延迟。
  3. 多路复用:RTMP支持将音频、视频和控制信息多路复用在一个TCP连接上,从而简化了传输过程。
  4. 灵活性:RTMP支持多种音频和视频编解码器,包括H.264和AAC,这使其能够适应不同的媒体格式和质量要求。
  5. 控制消息:RTMP支持多种控制消息,如暂停、播放、停止等,以便对流媒体进行实时控制。

RTMP的传输过程通常分为三个阶段:

  1. 握手阶段:客户端和服务器通过交换握手消息建立连接,并协商通信参数。
  2. 连接阶段:客户端向服务器发送连接请求,并建立会话。
  3. 数据传输阶段:客户端和服务器之间开始传输音视频数据和控制消息。

RTMP有几个变种,包括:

  • RTMPT(RTMP Tunneled):通过HTTP进行封装,以绕过防火墙。
  • RTMPS(RTMP Secure):使用SSL/TLS加密的RTMP,提供安全的数据传输。
  • RTMPE(RTMP Encrypted):使用Adobe的加密机制进行加密,提供安全的数据传输。

一个典型的RTMP URL格式如下:

1
rtmp://[服务器IP或域名]/[应用名称]/[流名称]

例如:

1
rtmp://live.example.com/app/stream

这表示客户端可以使用RTMP协议连接到live.example.com服务器,请求app应用中的stream流。

尽管RTMP已经不再是主流,但它仍然在许多直播平台和视频传输系统中使用。现代替代方案如HLS(HTTP Live Streaming)和DASH(Dynamic Adaptive Streaming over HTTP)在许多新应用中更为常见。

  • RTMP是Adobe Systems公司为Flash播放器和服务器之间音频,视频和数据传输开发的开放协议。它有三种变种

    • 工作在TCP之上的明文协议,使用端口1935
    • RTMPT封装在HTTP请求之中,可穿越防火墙
    • RTMPS类似RTMPT,但使用的是HTTPS连接
  • RTMP视频播放的特点是

    • RTMP协议是采用实时的流式传输,所以不会缓存文件到客户端,这种特性说明用户想下载RTMP协议下的视频是比较难的。
    • 视频流可以随意拖动,既可以从任意时间点向服务器发送请求进行播放,并不需要视频有关键帧。相比而言,HTTP协议下视频需要有关键帧才可以随意拖动
    • RTMP协议支持点播/回放(通俗点讲,就是支持把flv, f4v, mp4文件放在RTMP服务器,客户端可以直接播放),直播(边录制视频边播放)

HLS

HLS(HTTP Live Streaming,即HTTP实时流传输协议)是一种基于HTTP的流媒体传输协议,由苹果公司开发。HLS最早在2009年与iOS 3.0一起发布,现已成为一种广泛使用的流媒体传输标准,适用于直播和点播视频。

HLS的主要特点和功能包括:

  1. 基于HTTP:HLS使用标准的HTTP协议传输数据,因此能够通过现有的HTTP基础设施(如CDN和HTTP缓存服务器)进行流媒体分发。
  2. 分片传输:HLS将媒体内容分割成小的时间片段(通常是几秒钟的长度),每个片段以单独的HTTP请求进行传输。这种分片传输方式可以更好地适应网络波动,提供更流畅的观看体验。
  3. 自适应比特率:HLS支持自适应比特率传输,客户端可以根据当前的网络状况动态选择不同质量的媒体片段,从而在网络带宽变化时提供最佳的观看体验。
  4. 跨平台支持:HLS不仅支持苹果设备(如iPhone、iPad和Apple TV),还可以在其他操作系统和设备上使用,包括Android设备、智能电视和Web浏览器。
  5. 简单实现:由于HLS基于HTTP协议,实现相对简单,并且容易与现有的Web技术集成。

HLS工作流程如下:

  1. 媒体切片:服务器将媒体内容分割成一系列小的、连续的TS(MPEG-2 Transport Stream)文件,这些文件通常是几秒钟的长度。
  2. 索引文件(M3U8):服务器生成一个索引文件(通常是M3U8格式),列出所有媒体片段的URL。这个文件称为播放列表,包含了媒体片段的顺序和位置。
  3. 客户端请求:客户端请求播放列表(M3U8文件),并根据播放列表中的URL逐个请求媒体片段。
  4. 播放和缓存:客户端下载并播放媒体片段,同时可以缓存一定数量的片段以应对网络波动。

一个典型的HLS播放列表文件(M3U8)的示例如下:

1
2
3
4
5
6
7
8
9
10
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://example.com/segment0.ts
#EXTINF:10.0,
http://example.com/segment1.ts
#EXTINF:10.0,
http://example.com/segment2.ts

这个播放列表文件指示客户端依次请求和播放segment0.tssegment1.tssegment2.ts,每个片段长度为10秒。

HLS在流媒体行业中得到广泛应用,特别是在直播和视频点播服务中,成为了许多视频传输解决方案的基础。

  • HTTP Live Streaming(HLS)是苹果公司实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播,主要应用于IOS系统。HLS点播是分段HTTP点播,不同在于它的分段非常小。要实现HLS点播,重点在于对媒体文件分段,目前有不少开源工具可以使用。
  • 相对于常见的流媒体直播协议,HLS直播最大的不同在于,直播客户端获得的并不是一个完整的数据流,HLS协议再服务器端将直播数据流存储为连续的,很短时长的媒体文件(MPEG-TS格式)。而客户端则不断的下载并播放这些小文件,因为服务器总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可见,基本上可以认为,HLS是以点播的技术方式实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过HLS的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。

概述

  • 程序开发过程要求我们做到两点:
    • 高效的数据描述
    • 步骤合理,可用程序实现的算法设计
  • 要做到第一点,必须具备数据结构领域的专门知识
  • 要做到第二点,必须具备算法设计领域的专门知识

第一张 C++回顾

第二章 程序性能分析

概述

  • 程序最重要的属性是正确性。一个程序,如果不能正确的实现算法,他就没有什么用处。
  • 我们用程序性能(program performance)来指一个程序对内存和时间的需求。要对数据结构和算法设计方法给予应用的评价,就必须能够计算程序性能。
  • 我们用操作数和执行步数来估计程序的运行时间。用符号法来分别描述程序在最好,最坏和平均情况下的运行时间。
  • 本章开发了很多应用程序,这些程序在以后的章节中是很有用处的,他们是
    • 数组元素的查找
    • 数组元素的排序:排列排序,选择排序,冒泡排序和插入排序
  • 基于霍纳法则的多项式计算
  • 矩阵加法,转置和乘法

什么是程序性能

  • 所谓程序性能(performance of a program)是指运行这个程序所需要的内存和时间的多少。我们用两种方法来确定一个程序的性能,一个是分析方法,另一个是实验方法。在性能分析(performance analysis)时,采用分析方法,而在性能测量(performance measurement)时,使用实验方法。

  • 所谓一个程序的空间复杂度(space complexity)是指该程序的运行所需内存的大小。我们对程序的空间复杂度感兴趣的主要原因如下

    • 如果一个程序要运行在一个多用户计算机系统中,那么我们需要指明该程序所需要内存的大小
    • 在任何一个计算机系统上运行程序,都需要知道是否有足够的内存可以用来运行该程序
    • 一个问题可能有若干个解决方案,他们对内存的需求各不相同
    • 利用空间复杂度,我们可以估算一个程序所能解决的问题最大可以是什么规模。
  • 所谓程序的时间复杂度(time complexity)是指运行程序所需要的时间。我们对程序的时间复杂度感兴趣的主要原因如下

    • 有些计算机需要用户提供程序运行时间的上限,一旦达到了这个上限,程序将被强制结束。
    • 正在开发的程序可能需要一个令人满意的实时响应。
    • 如果一个问题有多种解决方案,那么具体采用哪一种方案,主要根据这些方案的性能差异。对于各种方案的时间和空间性能,我们将采用加权测量方式进行评价。

空间复杂度

  • 程序所需要的空间主要由以下部分构成

    • 指令空间(instruction space), 是指编译之后的程序所需要的存储空间
    • 数据空间(data space), 是指所有常量和变量所需要的存储空间。它有两个部分构成
      • 常量和简单变量所需要的存储空间
      • 动态数组和动态类实例等动态对象所需要的空间
    • 环境栈空间(environment stack space),是用来保存暂停的函数和方法在恢复运行时所需要的信息。
  • 指令空间的数量取决于如下因素

    • 把程序转换成机器代码的编译器
    • 在编译时的编译器选项
    • 目标计算机
  • 在决定最终代码需要多少空间的时候,编译器是一个最重要的因素。

  • 即使采用相同的编译器,编译后的程序代码也可能不同。因为一个编译器可能具备优化选项。

  • 编译器的覆盖选项也可以显著的减少程序空间。在覆盖模式下,空间仅分配给当前正在执行的程序模块。调用一个新模块需要从磁盘或者其他设备中读取,新模块代码将覆盖原模块的代码。因此,程序所需要的空间便是最大模块所需要的空间,而不是所有模块所需要的空间之和。

  • 对各种数据类型,C++语言并没有指定他们的空间大小,只是大多数C++编译器有相应的空间分配。

  • 一个结构变量的空间大小是每个结构成员所需要的空间大小之和。类似的,一个数组的空间大小是数组的长度乘以一个数组元素的空间大小。

  • 当计算分配给一个数组的空间时,我们只关心分配给数组元素的空间。数组a的空间是100个double类型元素所占用的空间。若每个元素空间是8字节,则数组a的空间是800字节。数组maze有rows*cols个int类型的元素,占用的空间是4 * rows * cols字节。

  • 在开始性能分析时,人们通常会忽略环境栈所需要的空间,因为他们不理解函数(特别是递归函数)是如何被调用的以及在函数调用结束时会发生什么。每当一个函数被调用时,下面的数据将被保存在环境栈中

    • 返回地址
    • 正在调用的函数的所有局部变量的值以及形式参数的值(仅对递归函数而言)
  • 值得注意的是,有些编译器,不论对递归函数还是非递归函数,在函数调用时,都会保留局部变量和形参的值,而有些编译器仅对递归函数才会如此。因此实际使用的编译器将影响环境栈所需空间的大小。

  • 程序要处理的问题实例都有一些特征,这些特征都包含着可以决定程序空间大小的因素(例如,输入和输出的数量或相关数的大小)。例如,对n个元素排序的程序,它所需要的空间大小是n的函数,n为其实例特征;将两个n x n矩阵累加的程序,n为其实例特征;把两个m x n矩阵相加的程序,m和n为其实例特征。

  • 相对来说,指令空间的大小受实例特征的影响不大。

  • 可以把一个程序所需要的空间分为两部分

    • 固定部分。它独立于实例特征。这一部分通常包括指令空间(即代码空间),简单变量空间和常量空间等
    • 可变部分。它由动态分配空间构成和递归栈空间构成。前者在某种程度上依赖实例特征,而后者主要依赖实例特征。
  • 任意程序P所需要的空间可以表示为: c + Sp(实例特征)

    • 其中c是一个常量,表示空间需要的固定部分,Sp表示空间需求的可变部分。
  • 在分析一个程序的空间复杂度时,我们将集中计算Sp对任意给定的问题,我们首先要确定哪些实例特征可以用来估算空间需求。选择实例特征是一个很具体的问题,我们需要求助于实际的例子来说明各种可能的情况。一般来说,我们的选择仅限于程序输入和输出的规模。有时我们也会对数据项之间的关系进行复杂的估算。

时间复杂度

  • 用分析方法确定一个程序的运行时间是复杂的,因此只能估算运行时间。而且有两个比较容易控制的方法

    • 找出一个或多个关键操作,确定他们的执行时间
    • 确定程序总的步数
  • 估算一个程序或函数的时间复杂度,一种方法是选择一种或者多种关键操作,例如加,乘,比较等,然后 确定每一种操作的执行次数。使用这种方法成功与否取决于是否能够找到耗时最大的操作。

第三章 渐近记法

第四章 性能测量

引言

  • 性能测量(performance measurement),关注的是一个程序实际需要的空间和时间。
  • C++函数 clock() 来测量时间,它用 滴答 数来计时。在头文件 time.h 中定义了常数 CLOCK_PER_SEC,它记录每秒流逝的 滴答 数,并转换成秒。CLOCK_PER_SEC=1000,滴答一次等于一毫秒。

第五章 线性表-数组描述

  • 从本章开始研究数据结构,一直到第16章为止。第5章和第6章集中研究线性表,但主要是介绍数据的描述方法,即数据在计算机内存和磁盘上的存储方式。在随后的章节中,我们研究其他常用的数据结构描述方法。这些数据结构有矩阵,栈,队列,字典,优先级队列,竞赛树,搜索树和图。

  • C++程序常用的数据描述方法是数组描述和链式描述。线性表可以用来说明这两种方法。

  • STL容器(vector和list)大致相当于线性表描述方法和链式描述方法。STL的类还有很多其他的方法。在建立线性表的数组描述和链式描述中,我们使用的函数名和签名与STL代码所使用的相同。

  • 数组描述方法将元素存储在一个数组中,用一个数学公式来确定每个元素存储的位置,即在数组中的索引。这是最简单的一种存储方式,所有元素依次存储在一片连续的存储空间中,这就是通常所说的顺序表。

  • 本章引用的数据结构的概念如下

    • 抽象数据类型和相应的C++抽象类
    • 线性表
    • 变长数组和数组容量倍增
    • 数组描述
    • 数据结构迭代器
  • 本章新增的C++概念

    • 抽象类
    • 迭代器

数据对象和数据结构

  • 数据对象(data object)是一组实例或者值,例如

    1
    2
    boolean = {false, true};
    digit = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  • boolean, digit都是数据对象,true和false是boolean的实例,而0,1—9是digit的实例。

  • 数据对象的一个实例,要么是一个不可再分的原子,要么是由另一个数据对象的实例作为成员复合而成。对后一种情形,用元素(element)来表示这些成员。

  • 数据对象的实例以及构成实例的元素通常都有某种相关性。例如,自然数0是最小的自然数,1是仅比0大的自然数。在串good中,g是第一个字母,o是第二个和第三个字母,而d是最后一个字母。

  • 数据结构(data structure)是一个数据对象,同时这个对象的实例以及构成实例的元素都存在着联系,而且这些联系由相关的函数来规定。

  • 研究数据结构,关心的是数据对象(实际上是实例)的描述以及相关函数的具体实现。数据对象描述的好,函数的实现就会高效。

线性表数据结构

  • 线性表(linear list)也称有序表(ordered list),它的每一个实例都是元素的一个有序集合。

  • 一个线性表可以用一个抽象数据类型(abstract data type, ADT)来说明,既说明它的实例,也说明对他的操作。抽象数据类型的说明独立于任何程序语言的描述。所有对抽象数据类型的语言描述必须满足抽象数据类型的说明,抽象数据类型的说明保证了程序语言描述的有效性。另外,所有满足抽象数据类型说明的语言描述,都可以在应用中替换使用。

数组描述

  • 在数组描述(array representation)中,用数组来存储线性表的元素。虽然可以用一个数组存储若干个线性表的实例,但是用不同数组存储每个实例更加容易一些。我们可以用一个数学公式来确定一个线性表的元素在数组中的位置。

  • 假定使用一个一维数组element来存储线性表的元素。数组element的位置有 element[0] — element[arrayLength - 1],其中arrayLength是数组长度或者容量。数组的每一个位置都可以存储线性表的一个元素。我们需要一个映射,使线性表的一个元素对应数组的一个位置。线性表的第0个元素在数组的什么位置?线性表的最后一个元素在数组的什么位置?这种映射可以用下面的一个公式来表示

    • location(i) = i
  • 一维数组a,线性表元素存储在a[0:n-1]中,要增加或者减少这个数组的长度,首先要建立一个具有新长度的数组,然后把数组a的元素复制到这个新数组,最后改变数组a的值,使它能够引用到新数组。

  • 当数组满而需要加大数组长度时,数组长度常常是要加倍的。这个过程称为数组倍增(array doubling)。数组倍增的时间,从渐近意义上考量,不会大于元素插入的总时间。

  • 为什么数组长度不是增加1或2,而是要加倍呢?数组长度每次增加1或2,虽然不影响插入操作的最坏时间复杂度,但是影响连续插入时的渐近时间复杂度。

  • 定理:如果我们总是按一个乘法因子来增加数组长度,那么实施一系列线性表的操作所需要的时间与不用改变数组长度时相比,至多增加一个常数因子。

C++ 迭代器

  • 一个迭代器(iterator)是一个指针,指向对象的一个元素(例如,一个指向数组元素的指针)。顾名思义,一个迭代器可以用来逐个访问对象的所有元素。

  • 迭代器是编写C++通用算法的基础概念。STL的copy函数便是用来复制任何具有迭代器的对象的元素。

  • 为了简化迭代器的开发和基于迭代器的通用算法的分类,C++的STL定义了5中迭代器:

    • 输入,输出,向前,双向和随机访问
  • 所有迭代器都具备操作符 == , != 和解引用操作符 *

  • 另外,输入迭代器还提供了对其指向元素的只读操作以及前++和后++操作符。输出迭代器提供了对其指向元素的写操作和++操作符。向前迭代器具有++操作符,而双向迭代器既具有++操作符也具有–操作符。随机访问迭代器是最一般的迭代器,它既可以随意的实现跳跃移动,也可以通过指针算术运算来实现跳跃移动。

vector的描述

  • STL提供了一个基于数组的类 vector。

第六章 链式描述

  • 用数组描述线性表是很自然的,因此你可能以为没有其他的描述方法了。
  • 在链式描述中,线性表的元素在内存中的存储位置是随即的。每个元素都有一个明确的指针或链(指针和链是一个意思)指向线性表的下一个元素的位置(即地址)
  • 在基于数组的描述中,元素的地质是由数学公式决定的。而在链式描述中,元素的地址是随即分布的。
  • STL的容器类list使用带有头节点的双向循环链表来描述实例。它的方法与vector的方法具有相同的签名和操作。因此,他的erase和insert的签名和抽象类型linearList的要求不同,然而和vector一样,它可以用来设计从抽象类linearList的方法具有相同的签名和操作。

单向链表

  • 描述

    • 在链式描述中,数据对象实例的每一个元素都用一个单元或节点来描述。节点不必是数组成员,因此不是用公式来确定元素的位置。取而代之的是,每一个节点都明确包含另一个相关节点的位置信息,这个信息称为链(link)或指针(pointer)
    • 在对这个线性表的一个可能的链式描述中,每个元素都在一个单独的节点中描述,每一个节点都有一个链域,它的值是线性表的下一个元素的位置,即地址。
    • 一般来说,为了找到索引为theIndex的元素,需要从firstNode开始,跟踪theIndex个指针才能找到。
  • 每一个节点只有一个链,这种结构称为单向链表(singly linked list)。链表从左到右,每一个节点(最后一个节点除外)都链接着下一个节点,最后一个节点的链阈值为NULL。这样的结构也称为链条(chain)

  • 结构chainNode

    • 为了用链表描述线性表,我们要定义一个结构chainNode和一个类chain。
  • 类chain

    • 类chain用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向链接的一组节点实现线性表。

循环链表和头节点

  • 两条措施,可以使链表的应用代码简洁和高效
    • 把线性表描述成一个单向循环链表(singly linked circular list)(简称循环链表),而不是单向链表
    • 在链表的前面增加一个节点,称为头节点(header node)。只要将单向链表的尾节点与头节点链接起来,单向链表就成为循环链表。

双向链表

  • 对于线性表的大多数应用来说,采用链表和/或循环链表已经足够了。然而,对于有些应用,如果每个元素节点既有一个指向后继的指针,又有一个指向前驱的指针,就会更加方便。
  • 双向链表(doubly linked list)便是这样一个有序的节点序列,其中每个节点都有两个指针nest和previous。
    • next指针指向右边节点(如果存在)
    • previous指针指向左边节点(如果存在)

第七章 数组和矩阵

  • 在实际应用中,数据通常以表的形式出现。尽管用数组来描述表是最自然的方式,但为了减少程序所需的时间和空间,经常采用自定义的描述方式。
  • 本章首先检查了多维数组的行主描述方式和列主描述方式。这些描述方式把多维数组映射成一维数组。
  • 矩阵经常用二维数组来表示。然后,矩阵的索引通过从1开始,而C++的二维数组是从0开始。矩阵的操作有加法,减法和转置,但是C++的二维数组不支持这些操作。因此我们开发了类matrix,它与矩阵的关系密切
  • 我们还要考察具有特殊结构的矩阵,例如对角矩阵,三对角矩阵,三角矩阵和对陈矩阵。关于这些矩阵的描述方法,自定义数组与二维数组相比,不仅大大减少了存储空间安,也减少了大多数矩阵操作的运行时间。

数组

  • 抽象数据类型
  • 一个数组的每一个实例都是形如(索引,值)的数对集合,其中任意两个数对的索引(index)都不相同。有关数组的操作如下
    • 取值: 对一个给定的索引,取对应数对中的值
    • 存值: 把一个新数对加到数对集合中。如果已存在一个索引相同的数对,就用新数对覆盖
  • 这两个操作定义了抽象数据类型array

行主映射和列主映射

  • 数组的应用需要我们把数组元素序列化,即按一维顺序排列。例如,数组元素只能一次输出或输入一个。因此,我们必须确定一个输入或输出的顺序。
  • 从第一行开始,依次对每一行的索引从左至右连续编号。它把二维数组的索引映射为 [0, n-1]中的数,这种映射方式称为行主映射(row major mapping)。索引对应的数称为行主次序
  • 另一种映射方式,称为列主映射(column major mapping)。在列主映射中,对索引的编号从最左列开始,依次对每一列的索引从上到下连续编号。

矩阵

  • 一个m * n的矩阵(matrix)是一个m行,n列的表,m和n是矩阵的维数(dimension)

  • 矩阵通常用来组织数据。

  • 矩阵最常见的操作是矩阵转置,矩阵相加,矩阵相乘。

  • 两个矩阵仅当维数相同时(即它们的行数和列数都分别相等)才可以相加。两个m * n的矩阵A和B相加之后是一个m * n的矩阵C

  • 一个m * n的矩阵A和一个q * p的矩阵B,只有当A的列数等于B的行数(即n=q)时,才可以相乘AB。AB的结果是一个m * p的矩阵C

  • 特殊矩阵

    • 方阵(square matrix)是行数和列数相同的矩阵。一些常用的特殊方阵如下
    • 对角矩阵(diagonal)
    • 三对角矩阵(tridiagonal)
    • 下三角矩阵(lower triangular)
    • 上三角矩阵(upper triangular)
    • 对陈矩阵(symmetric)
  • 一个m * n的矩阵,如果大多数元素都是0,则称为稀疏矩阵(spare matrix)。一个矩阵如果不是稀疏的,就成为稠密矩阵(dense matrix)

第八章 栈

  • 栈和队列很可能是应用频率最高的数据结构。在第五章和第六章曾经广泛研究了线性表和有序表数据结构,而栈和队列是它们的限制版。

  • 栈和队列的应用广泛,以至于C++的标准类模板库STL都提供了用数组实现的栈和队列。

  • 把线性表的插入和删除操作限制在同一端进行,就得到栈数据结构。因此,栈是一个后进先出(last-in-first-out, FIFO)的数据结构。因为栈是一种特殊的线性表,所以从相应的线性表类派生出栈类是很自然的事情。

定义和应用

  • 栈(stack)是一种特殊的线性表,其插入(也称入栈或压栈)和删除(也称出栈或弹栈)操作都在表的同一端进行。这一端称为栈顶(top),另一端称为栈底(bottom)

  • 计算机是如何执行递归函数?

    • 使用递归工作栈(recursion stack)。当一个函数被调用时,一个返回地址(即被调函数一旦执行完,接下去要执行的程序指令的地址)和被调函数的局部变量和形参的值都要存储在递归工作栈中。当执行一次返回时,被调函数的局部变量和形参的值被恢复为调用之前的值(这些值存储在递归工作栈的顶部),而且程序从返回地址处继续执行,这个返回地址也存储在递归工作栈的顶部。
  • 抽象数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<class T>
    class stack
    {
    public:
    virtual ~stack();
    virtual bool empty() const = 0; // 返回true,当且仅当栈为空
    virtual int size() const = 0; // 返回栈中元素个数
    virtual T& top() = 0; // 返回栈顶元素的引用
    virtual void pop() = 0; // 删除栈顶元素
    virtual void push(const T& theElement) = 0; // 将元素theElement压入栈顶
    };

SD、HD、FHD、UHD、FUHD

  • SD(Standard Definition),标清

    • 480p
    • 576p
  • HD(High Definition),高清

    • 720p
  • FHD(Full High Definition),全高清

    • 1080p
  • UHD(Ultra High Definition),超高清,4k UHD

    • 4k
  • FUHD(Full Ultra High Definition),8k超高清,8k UHD

    • 8k

return

  • return,它的主要任务是在函数结束时将控制权交还给调用他的代码。

  • return不仅可以返回基本数据类型,还可以返回复杂的数据结构,例如对象和数组。

  • 合理使用return,可以使代码更加简洁明了。在进行条件判断时,我们可以在条件满足时立即返回,避免不必要的嵌套。

exit

  • exit,它不仅仅是结束当前的函数,而是终止整个程序的执行,立即返回给操作系统。

  • 应急处理和日志记录。在一些关键任务中,遇到不可恢复的错误时,我们可以使用exit来终止程序,并在退出前记录错误日志,以便后续分析。

  • 注册清理函数。使用atexit函数,我们可以在程序终止时执行一些清理工作,例如释放资源或者保存程序状态

return 与 exit的对比和选择

  • 作用范围

    • return 仅终止当前函数,返回到调用者
    • exit 终止整个程序的执行
  • 清理操作

    • return 执行当前函数的清理工作
    • exit 执行全局和静态对象的析沟函数,并调用通过atexit注册的函数
  • 使用场景

    • return 用于函数结束后返回结果
    • exit 用于遇到不可恢复错误或者需要立即终止程序的情况
  • 综合考虑

    • 在实际开发中,我们应该优先使用return进行正常的函数返回,以保持代码的可读性和可维护性。而exit应仅在特殊情况下使用,确保在调用前做好必要的清理工作和日志记录。