FFmpeg流媒体处理的收流与推流

news2025/1/6 18:35:59

1. 简介
流媒体是使用了流式传输的多媒体应用技术。如下是维基百科关于流媒体概念的定义:

流媒体 (streaming media) 是指将一连串的媒体数据压缩后,经过网络分段发送数据,在网络上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个媒体文件。

1.1 FFmpeg 影音处理的层次
FFmpeg 中对影音数据的处理,可以划分为协议层、容器层、编码层与原始数据层四个层次:

协议层:提供网络协议收发功能,可以接收或推送含封装格式的媒体流。协议层由 libavformat 库及第三方库(如 librtmp)提供支持。

容器层:处理各种封装格式。容器层由 libavformat 库提供支持。

编码层:处理音视频编码及解码。编码层由各种丰富的编解码器(libavcodec 库及第三方编解码库(如 libx264))提供支持。

原始数据层:处理未编码的原始音视频帧。原始数据层由各种丰富的音视频滤镜(libavfilter 库)提供支持。

本文提及的收流与推流的功能,属于协议层的处理。

FFmpeg 中 libavformat 库提供了丰富的协议处理及封装格式处理功能,在打开输入/输出时,FFmpeg 会根据 输入 URL / 输出 URL 探测输入/输出格式,选择合适的协议和封装格式。例如,如果输出 URL 是 "rtmp://192.168.0.104/live",那么 FFmpeg 打开输出时,会确定使用 rtmp 协议,封装格式为 flv。

FFmpeg 中打开输入/输出的内部处理细节用户不必关注,因此本文流处理的例程和前面转封装的例程非常相似,不同之处主要在于输入/输出 URL 形式不同,若 URL 携带 "rtmp://"、"rpt://"、"udp://"等前缀,则表示涉及流处理;否则,处理的是本地文件。

1.2 流媒体系统中的角色
流媒体系统是一个比较复杂的系统,简单来说涉及三个角色:流媒体服务器、推流客户端和收流客户端。推流客户端是内容生产者,收流客户端是内容消费者。示意图如下:

1.3 收流与推流

如果输入是网络流,输出是本地文件,则实现的是收流功能,将网络流存储为本地文件,如下:

如果输入是本地文件,输出是网络流,则实现的是推流功能,将本地文件推送到网络,如下: 

如果输入是网络流,输出也是网络流,则实现的是转流功能,将一个流媒体服务器上的流推送到另一个流媒体服务器,如下: 

2. 源码

源码和转封装例程大部分相同,可以认为是转封装例程的增强版:

#include <stdbool.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
​
// ffmpeg -re -i tnhaoxc.flv -c copy -f flv rtmp://192.168.0.104/live
// ffmpeg -i rtmp://192.168.0.104/live -c copy tnlinyrx.flv
// ./streamer tnhaoxc.flv rtmp://192.168.0.104/live
// ./streamer rtmp://192.168.0.104/live tnhaoxc.flv
int main(int argc, char **argv)
{
    AVOutputFormat *ofmt = NULL;
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
    int ret, i;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;
​
    if (argc < 3) {
        printf("usage: %s input output\n"
               "API example program to remux a media file with libavformat and libavcodec.\n"
               "The output format is guessed according to the file extension.\n"
               "\n", argv[0]);
        return 1;
    }
​
    in_filename  = argv[1];
    out_filename = argv[2];
​
    // 1. 打开输入
    // 1.1 读取文件头,获取封装格式相关信息
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf("Could not open input file '%s'", in_filename);
        goto end;
    }
    
    // 1.2 解码一段数据,获取流相关信息
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        goto end;
    }
​
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
​
    // 2. 打开输出
    // 2.1 分配输出ctx
    bool push_stream = false;
    char *ofmt_name = NULL;
    if (strstr(out_filename, "rtmp://") != NULL) {
        push_stream = true;
        ofmt_name = "flv";
    }
    else if (strstr(out_filename, "udp://") != NULL) {
        push_stream = true;
        ofmt_name = "mpegts";
    }
    else {
        push_stream = false;
        ofmt_name = NULL;
    }
    avformat_alloc_output_context2(&ofmt_ctx, NULL, ofmt_name, out_filename);
    if (!ofmt_ctx) {
        printf("Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
​
    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    if (!stream_mapping) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
​
    ofmt = ofmt_ctx->oformat;
​
    AVRational frame_rate;
    double duration;
​
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *out_stream;
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;
​
        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }
​
        if (push_stream && (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
            frame_rate = av_guess_frame_rate(ifmt_ctx, in_stream, NULL);
            duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);
        }
​
        stream_mapping[i] = stream_index++;
​
        // 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
        out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream) {
            printf("Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
​
        // 2.3 将当前输入流中的参数拷贝到输出流中
        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0) {
            printf("Failed to copy codec parameters\n");
            goto end;
        }
        out_stream->codecpar->codec_tag = 0;
    }
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
​
    if (!(ofmt->flags & AVFMT_NOFILE)) {    // TODO: 研究AVFMT_NOFILE标志
        // 2.4 创建并初始化一个AVIOContext,用以访问URL(out_filename)指定的资源
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf("Could not open output file '%s'", out_filename);
            goto end;
        }
    }
​
    // 3. 数据处理
    // 3.1 写输出文件头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf("Error occurred when opening output file\n");
        goto end;
    }
​
    while (1) {
        AVStream *in_stream, *out_stream;
​
        // 3.2 从输出流读取一个packet
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0) {
            break;
        }
​
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        if (pkt.stream_index >= stream_mapping_size ||
            stream_mapping[pkt.stream_index] < 0) {
            av_packet_unref(&pkt);
            continue;
        }
​
        int codec_type = in_stream->codecpar->codec_type;
        if (push_stream && (codec_type == AVMEDIA_TYPE_VIDEO)) {
            av_usleep((int64_t)(duration*AV_TIME_BASE));
        }
​
        pkt.stream_index = stream_mapping[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
​
        /* copy packet */
        // 3.3 更新packet中的pts和dts
        // 关于AVStream.time_base(容器中的time_base)的说明:
        // 输入:输入流中含有time_base,在avformat_find_stream_info()中可取到每个流中的time_base
        // 输出:avformat_write_header()会根据输出的封装格式确定每个流的time_base并写入文件中
        // AVPacket.pts和AVPacket.dts的单位是AVStream.time_base,不同的封装格式AVStream.time_base不同
        // 所以输出文件中,每个packet需要根据输出封装格式重新计算pts和dts
        av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
​
        // 3.4 将packet写入输出
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            printf("Error muxing packet\n");
            break;
        }
        av_packet_unref(&pkt);
    }
​
    // 3.5 写输出文件尾
    av_write_trailer(ofmt_ctx);
​
end:
    avformat_close_input(&ifmt_ctx);
​
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) {
        avio_closep(&ofmt_ctx->pb);
    }
    avformat_free_context(ofmt_ctx);
​
    av_freep(&stream_mapping);
​
    if (ret < 0 && ret != AVERROR_EOF) {
        printf("Error occurred: %s\n", av_err2str(ret));
        return 1;
    }
​
    return 0;
}

 

2.1 收流
收流的代码与打开普通文件的代码没有区别,打开输入时,FFmpeg 能识别流协议及封装格式,根据相应的协议层代码来接收流,收到流数据去掉协议层后得到的数据和普通文件内容是一样的,后续的处理流程也就一样了。

2.2 推流
推流有两个需要注意的地方。

一是需要根据输出流协议显式指定输出 URL 的封装格式:
 

 bool push_stream = false;
    char *ofmt_name = NULL;
    if (strstr(out_filename, "rtmp://") != NULL) {
        push_stream = true;
        ofmt_name = "flv";
    }
    else if (strstr(out_filename, "udp://") != NULL) {
        push_stream = true;
        ofmt_name = "mpegts";
    }
    else {
        push_stream = false;
        ofmt_name = NULL;
    }
    avformat_alloc_output_context2(&ofmt_ctx, NULL, ofmt_name, out_filename);

 

这里只写了两种。rtmp 推流必须推送 flv 封装格式,udp 推流必须推送 mpegts 封装格式,其他情况就当作是输出普通文件。这里使用 push_stream 变量来标志是否使用推流功能,这个标志后面会用到。

二是要注意推流的速度,不能一股脑将收到的数据全推出去,这样流媒体服务器承受不住。可以按视频播放速度(帧率)来推流。因此每推送一个视频帧,要延时一个视频帧的时长。音频流的数据量很小,可以不必关心此问题。

在打开输入 URL 时,获取视频帧的持续时长:
 

 AVRational frame_rate;
    double duration;
    if (push_stream && (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
        frame_rate = av_guess_frame_rate(ifmt_ctx, in_stream, NULL);
        duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);
    }

在 av_read_frame() 之后,av_interleaved_write_frame() 之前增加延时,延时时长就是一个视频帧的持续时长:

int codec_type = in_stream->codecpar->codec_type;
    if (push_stream && (codec_type == AVMEDIA_TYPE_VIDEO)) {
        av_usleep((int64_t)(duration*AV_TIME_BASE));
    }

3. 验证
3.1 编译第三方库 librtmp
FFmpeg 默认并不支持 rtmp 协议。需要先编译安装第三方库 librtmp,然后开启 --enable-librtmp 选项重新编译安装 FFmpeg。

3.2 搭建流媒体服务器
测试收流与推流功能需要搭建流媒体服务器。我们选用 nginx-rtmp 作为流媒体服务器用于测试。nginx-rtmp 服务器运行于虚拟机上,推流客户端与收流客户端和 nginx-rtmp 服务器处于同一局域网即可。我的虚拟机是 OPENSUSE LEAP 42.3,IP 是 192.168.0.104(就是 nginx-rtmp 服务器的地址)。

为避免搭建服务器的繁琐过程,我们直接使用 docker 拉取一个 nginx-rtmp 镜像。步骤如下:

[1] 安装与配置docker服务

安装 docker:

sudo zypper install docker
将当前用户添加到 docker 组(若 docker 组不存在则先创建),从而可以免 sudo 使用 docker 命令:

sudo gpasswd -a ${USER} docker
[2] 配置镜像加速

docker 镜像源位于美国,摘取镜像非常缓慢。可配置国内镜像源,加快镜像拉取速度。

修改 /etc/docker/daemon.json 文件并添加上 registry-mirrors 键值:
 

{
    "registry-mirrors":
    [
        "https://registry.docker-cn.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://hub-mirror.c.163.com",
        "https://mirror.ccs.tencentyun.com"
    ]
}

上述配置文件添加了四个国内镜像源:docker 中国、清华、163 和腾讯。

修改配置文件后重启 docker 服务:

systemctl restart docker
[3] 拉取 nginx-rtmp 镜像

docker pull tiangolo/nginx-rtmp
[4] 打开容器

docker run -d -p 1935:1935 --name nginx-rtmp tiangolo/nginx-rtmp
[5] 防火墙添加例外端口

如果无法推流,应在防火墙中将 1935 端口添加例外

openSUSE 系统:修改 /etc/sysconfig/SuSEfirewall2 文件,在 FW_SERVICES_EXT_TCP 项中添加 1935 端口,如下:

FW_SERVICES_EXT_TCP="ssh 1935"
然后重启防火墙:

systemctl restart SuSEfirewall2
CentOS 8 系统:运行如下命令将 1935 端口添加到防火墙例外端口中:

firewall-cmd --permanent --zone=public --add-port=1935/tcp
[6] 验证服务器

测试文件下载(右键另存为):tnhaoxc.flv

ffmpeg 推流测试:

ffmpeg -re -i tnhaoxc.flv -c copy -f flv rtmp://192.168.0.104/live
"-re":按视频帧率的速度读取输入 "-c copy":输出流使用和输入流相同的编解码器 "-f flv":指定输出流封装格式为flv

ffplay 收流播放测试:

ffplay rtmp://192.168.0.104/live
ffplay 播放正常,说明 nginx-rtmp 流媒体服务器搭建成功,可正常使用。

3.3 编译
在 shell 中运行如下命令下载例程源码:

svn checkout https://github.com/leichn/exercises/trunk/source/ffmpeg/ffmpeg_stream
在源码目录执行 ./compile.sh 命令,生成 streamer 可执行文件。

3.4 验证
测试文件下载(右键另存为):shifu.mkv,将测试文件保存在和源码同一目录。

推流测试:

./streamer shifu.mkv rtmp://192.168.0.104/live
使用 vlc 播放器打开网络串流,输入流地址 "rtmp://192.168.0.104/live",播放正常。上述测试命令等价于:

ffmpeg -re -i shifu.mkv -c copy -f flv rtmp://192.168.0.104/live

收流测试:先按照上一步命令启动推流,然后运行如下命令收流

./streamer rtmp://192.168.0.104/live shifu.ts
以上测试命令等价于:

ffmpeg -i rtmp://192.168.0.104/live -c copy shifu.ts
接收结束后检查一下生成的本地文件 shifu.ts 能否正常播放。

4. 遗留问题
推流的问题:不管是用 ffmpeg 命令,还是用本测试程序,推流结束时会打印如下信息
 

[flv @ 0x22ab9c0] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
Larger timestamp than 24-bit: 0xffffffc2
[flv @ 0x22ab9c0] Failed to update header with correct duration.
[flv @ 0x22ab9c0] Failed to update header with correct filesize.

收流的问题:推流结束后,收流超时未收以数据,会打印如下信息后程序退出运行

RTMP_ReadPacket, failed to read RTMP packet header

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/686025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20亿元项目“落子”杭州,这家企业加速全栈智驾平台规模化交付

6月20日&#xff0c;在“幸会杭州”投资促进大会上&#xff0c;中国领先的智能驾驶解决方案服务商和产品供应商福瑞泰克与杭州市政府就在杭州市高新技术开发区&#xff08;滨江&#xff09;建设全球研发管理总部达成签约。福瑞泰克董事长兼首席执行官张林博士受邀出席大会&…

SSM摄影服务线上选购预约系统-计算机毕设 附源码83784

SSM摄影服务线上选购预约系统 摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用SSM技…

网络安全|渗透测试入门学习,从零基础入门到精通—动态分析技术OllyDbg案例详情

目录 1、准备工作 2、加载目标文件进行调试 3、单步跟踪 4、设置断点 5、调试分析 1、准备工作 分析一个Windows程序要比分析一个DOS程序容易得多因为在Windows中只要API函数被使用了&#xff0c;再想对要寻找蛛丝马迹的人隐藏一些信息就比较困难了。因此&#xff0c;在分析…

软考高级系统架构设计师(七) 需求工程相关

目录 需求工程概述 需求工程-需求获取 需求开发-需求分析 需求分析-OOA OOA-UML-41视图 需求定义 需求验证 需求跟踪 需求变更管理 软件系统建模 结构化设计 面向对象设计 软件界面设计 需求工程概述 需求工程-需求获取 需求获取方法&#xff1a; 需求开发-需求分…

在 BI 工具 Tableau 中使用 databend

Tableau是一款流行的数据可视化和业务智能工具。它提供了直观、交互式的方式来探索、分析和呈现数据&#xff0c;帮助用户更好地理解数据的意义和洞察。 本教程帮助用户在 Tableau 中连接 databend 进行 BI 数据分析。 Databend jdbc Tableau 支持所有实现了 JDBC 协议的数据…

如何在 Spring Boot 中使用 WebFlux

如何在 Spring Boot 中使用 WebFlux 随着互联网应用的复杂度不断增加&#xff0c;传统的请求响应模型已经无法满足日益增长的需求。传统的 Web 应用使用 Servlet 容器&#xff0c;采用同步阻塞的方式来处理请求&#xff0c;请求需要等待相应的处理逻辑完成后才能返回结果。这种…

【C/C++实战项目】万年历

目录 项目分析 项目效果 头文件及全局变量 获取天数 打印月份、年份日历 main函数 项目分析 实现查询某一个年份、月份&#xff0c;以日历的格式打印为了观赏性利用符号对打印的每一个日期进行分格特殊节日日期能够在日历中标注出来万年历的起始日期是公元1年&#xff0…

[Orin Nx] 如何跑满GPU和CPU,观察温度和散热性能?

1、环境说明 硬件: Nvidia Orin NX 16GB 软件&#xff1a;Jetson Linux R35.3.1 2、工具安装部署 GPU的压力测试主要使用工具&#xff1a; https://github.com/anseeto/jetson-gpu-burn CPU的压力测试主要使用工具 stress 注意安装 jetson-gpu-burn需要在 /etc/apt/sourc…

SpringBoot 客户信息管理系统-计算机毕设 附源码80944

SpringBoot客户信息管理系统 摘 要 随着计算机技术的发展&#xff0c;特别是计算机网络技术与数据库技术的发展&#xff0c;使用人们的生活与工作方式发生了很大的改观。本课题研究的客户信息管理系统&#xff0c;主要功能模块包括统计图&#xff0c;系统用户&#xff08;管理…

JS高级用法:像大神一样玩转JavaScript

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 众所周知&#xff0c;JavaScript是一种非常流行的编程语言&#xff0c;它已经成为了…

回收站如何添加到桌面?没有回收站的数据怎么恢复

最近收到很多这样的咨询&#xff1a; 咨询1&#xff1a;在网吧上网&#xff0c;不小心把照片删了&#xff0c;可恨的是这里没有回收站&#xff0c;请问怎么找回照片&#xff01;&#xff01;&#xff01;急&#xff0c;谢谢了。 咨询2&#xff1a;在网吧上网&#xff0c;一删除…

emmylua+Rider 断点调试

1、下载安装并激活好rider&#xff08;本次版本基于&#xff1a;JetBrains Rider 2023.1.3&#xff09; 2、从插件商店中安装emmylua 3、打开项目 在unity中设置好&#xff0c;使用rider打开项目即可 4、Unity中打开相应的场景 &#xff08;此场景基于Xlua官方Demo展示&…

初中生台灯如何选择?眼科专家推荐的学生台灯分享

保护视力一直是家长们经常提醒孩子们的一句话&#xff0c;但据统计青少年学生的近视率还是非常高&#xff0c;很多家长认为这是不良的用眼习惯导致的&#xff0c;却没想过可能是其他原因&#xff0c;因为孩子除了自身保持良好的用眼习惯外&#xff0c;在学习时用眼的光线也是十…

有哪些智能聊天ai机器人?这几种ai工具都能进行聊天

在当前ai技术日益成熟的时代&#xff0c;越来越多的ai聊天机器人开始走进我们的生活中&#xff0c;为我们带来更多乐趣和便利。这几款ai聊天机器人不仅可以陪伴我们度过孤独的夜晚&#xff0c;还可以为我们提供各种对话服务。它们能理解我们的话语&#xff0c;与我们进行自然对…

【Linux 系统编程】Linux背景介绍 及 基本指令详解(一)

文章目录 1. Linux背景1.1 发展史1.2 开源精神1.3 官网1.4 企业应用现状1.5 发行版本1.6 os概念&#xff0c;定位 2. Linux下基本指令2.1 几个常用基本命令演示2.2 某些概念的解释2.3 ls 指令2.4 pwd 指令2.5 cd 指令Linux的目录结构绝对路径与相对路径cd 常用快捷命令 2.6 tou…

【unity每日一记】transform类精华之珠

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

【Java】JVM学习(二)

JVM的内存区域 运行时数据区域 运行时数据区的定义&#xff1a; Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域 Java 引以为豪的就是它的自动内存管理机制。相比于 C的手动内存管理、复杂难以理解的指针等&#xff0c;Java 程序写起来就方…

前端Vue自定义精美tabs,可设置下划线图标 热门标题

前端Vue自定义精美tabs&#xff0c;可设置下划线图标 热门标题&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13186 效果图如下&#xff1a; # cc-beautyTabs #### 使用方法 使用方法 <!-- tabchange: tab选择事…

为什么Python成了开发AI的主流语言?Java系列、Ruby等编程语言不行吗?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言AI的崛起与编程语言…

NXP i.MX 6ULL工业开发板硬件说明书( ARM Cortex-A7,主频792MHz)

前 言 本文档主要介绍TLIMX6U-EVM评估板硬件接口资源以及设计注意事项等内容。 创龙科技TLIMX6U-EVM是一款基于NXP i.MX 6ULL的ARM Cortex-A7高性能低功耗处理器设计的评估板&#xff0c;由核心板和评估底板组成。核心板经过专业的PCB Layout和高低温测试验证&#xff0c;稳…