3516DV300 推流

news2025/1/24 22:37:42

3516DV300 推流

基于ffmpeg将编码后的264文件(或者直接推流)推流出去,使用ffplay进行播放和验证。
ffmpeg版本:N-109124-g63db6a02a7
RELEASE:5.1.git

ffmpeg udp文件推流 命令行

这里用的是开发板编码出的码流,直接用vlc可以进行播放,但是用ffmpeg udp推流后就不能用vlc正常播放了,不知道啥原因。文件是海思例程保存的264文件。

  1. 推流
    地址和端口是远端的
ffmpeg -re -i stream_chn0.h264 -vcodec copy -f mpegts udp://192.168.3.13:1234
  1. 播放
    本地端口
ffplay -protocol_whitelist "file,udp,rtp" -i udp://192.168.3.13:1234 -fflags nobuffer

ffmpeg udp文件推流 代码

读取文件,并推流到远端端口。

int file_udp_main()
{
	AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVStream *in_stream, *out_stream;
	AVPacket pkt;
	int ret, i;
	int videoindex = -1;
	int frame_index = 0;
	int64_t start_time = 0;
    
	const char *in_filename = "stream_chn0.h264";//输入URL(Input file URL)
	const char *out_filename = "udp://192.168.3.13:1234";//输出 URL  udp	
 
 
	// 打开输入文件
	ifmt_ctx = NULL;
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0)
	{
		return ret;
	}

    ifmt_ctx->probesize = 1024*1024;
    ifmt_ctx->max_analyze_duration = 3000000; //最大分析3秒
    ifmt_ctx->flags |= AVFMT_FLAG_NOBUFFER; //不缓存, 减小直播延时
    
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)
    {
        printf("avformat_find_stream_info failed\n");
        goto end;
    }
 
    for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        if (ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
        {
            //HTTP—FLV只支持 AAC和MP3 音频格式
            if ((ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AAC
                && ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_MP3)
                || ifmt_ctx->streams[i]->codecpar->sample_rate == 0)
            {
                hflv_audio = false;
            }
            else
            {
                hflv_audio = true;
            }
        }
        else if (ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            //HTTP—FLV只支持 h264 视频格式
            if (ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_H264)
            {
                goto end;
            }
 
            videoindex = i;
        }
    }
 
    if (videoindex == -1)
    {
        goto end;
    }
 
	av_dump_format(ifmt_ctx, 0, in_filename, 0);
 

    avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);
    if (!ofmt_ctx)
    {
        printf( "Could not create output context\n");
        goto end;
    }
    printf("avformat_alloc_output_context2\n");
 
    //将输入流音视频编码信息复制到输出流中
    for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        if (!hflv_audio)
        {
            if(ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
            {
                continue;
            }
        }
 
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream)
        {
            goto end;
        }
        if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
        {
            goto end;
        }
 
        out_stream->codecpar->codec_tag = 0;
    }
    printf("copy codec context \n");
 
    av_dump_format(ofmt_ctx, 0, out_filename, 1);


     if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            printf( "Could not open output URL '%s'", out_filename);
            goto end;
        }
        printf("avio_open \n");
    }
 
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0)
    {
        printf( "Error occurred when opening output URL\n");
        goto end;
    }
 
    printf("start push stream \n");
 

    while (1)
    {
        //获取每一帧数据
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
        {
            break;
        }
 
        if (!hflv_audio && pkt.stream_index != videoindex)
        {
            av_packet_unref(&pkt);
            continue;
        }
 
        in_stream = ifmt_ctx->streams[pkt.stream_index];
 
        if (!hflv_audio && pkt.stream_index == videoindex)
        {
            out_stream = ofmt_ctx->streams[0];
            pkt.stream_index = 0;
        }
        else
        {
            out_stream = ofmt_ctx->streams[pkt.stream_index];
        }
 
        if (pkt.pts == AV_NOPTS_VALUE)
        {
            //Write PTS
            AVRational time_base1 = in_stream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration = (int64_t)((double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate));
            //Parameters
            pkt.pts = (int64_t)((double)(frame_index * calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE));
            pkt.dts = pkt.pts;
            pkt.duration = (int64_t)((double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE));
        }
 
        //指定时间戳
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,
                                   (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base,
                                   (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
 
        if (pkt.stream_index == videoindex)
        {            
            //printf("Send %8d video frames to output URL, [%d]\n",frame_index, pkt.flags);
            frame_index++;
        }
 
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
 
        if (ret < 0)
        {
            printf("Error muxing packet.error code %d\n", ret);
            break;
        }
 
        //释放 packet,否则会内存泄露
        av_packet_unref(&pkt);
        
        // 添加延时,不然很快就播放完了
        av_usleep(40000);
    }
 
    av_write_trailer(ofmt_ctx);
 
end:
 
    // 该函数会释放用户自定义的IO buffer
    // 上面不再释放,否则会corrupted double-linked list
    avformat_close_input(&ifmt_ctx);
    avformat_free_context(ifmt_ctx);
 
    if (ofmt_ctx && !(ofmt_ctx->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
 
    if (ofmt_ctx)
    {
        avformat_free_context(ofmt_ctx);
    }

 
	return 0;
}

效果还可以,代码里面要加一个延时,不然文件一下子就读完了。
在这里插入图片描述

ffmpeg udp内存推流 代码

采用回调函数的方式读取文件,然后进行推流。和文件推流类似,区别在于使用了回调函数进行数据的读取(ffmpeg会不断调用该函数进行数据的获取)。


#define READ_DATA_LEN (1920*1080)
#define true 1
#define false 0
int fd;
int hflv_audio = 0;


int read_init(char *filename)
{
    fd = open(filename,O_RDONLY);
    if(fd < 0)
    {
        perror("open file failed");
        return -1;
    }
    return 1;
}

int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
   return  read(fd,buf,buf_size);
}




int mem_udp_main()
{
    char *in_filename = "stream_chn0.h264";
    char *out_filename = "udp://192.168.3.13:1234";

    read_init(in_filename);
    int ret = 0;
    int videoindex = -1;
    AVPacket pkt;
    int frame_index = 0;
    AVStream *in_stream, *out_stream;
 
    AVFormatContext *ictx = NULL;
    const AVInputFormat* ifmt = NULL;
 
    AVFormatContext *octx = NULL;
    AVOutputFormat *ofmt = NULL;
    AVIOContext *avio = NULL;


    unsigned char * iobuffer = (unsigned char *)av_malloc(READ_DATA_LEN);
    avio = avio_alloc_context(iobuffer, READ_DATA_LEN, 0, NULL, read_packet, NULL, NULL);
    if (!avio)
    {
        printf( "avio_alloc_context for input failed\n");
        goto end;
    }
 
    //探测流封装格式
    ret = av_probe_input_buffer(avio, &ifmt, "", NULL, 0, 0);
    if (ret < 0)
    {
        printf("av_probe_input_buffer failed\n");
        goto end;
    }
    printf("av_probe_input_buffer format:%s[%s]\n",ifmt->name, ifmt->long_name);
 
    ictx = avformat_alloc_context();
    ictx->pb = avio;
    ictx->flags=AVFMT_FLAG_CUSTOM_IO;
 
    ret = avformat_open_input(&ictx, "", NULL, NULL);
    if (ret < 0)
    {
        printf("avformat_open_input failed\n");
        goto end;
    }
 
    //获取音频视频的信息
    ictx->probesize = 1024*1024;
    ictx->max_analyze_duration = 3000000; //最大分析3秒
    ictx->flags |= AVFMT_FLAG_NOBUFFER; //不缓存, 减小直播延时
 
    ret = avformat_find_stream_info(ictx, NULL);
    if (ret < 0)
    {
        printf("avformat_find_stream_info failed\n");
        goto end;
    }
 
    for(unsigned int i = 0; i < ictx->nb_streams; i++)
    {
        if (ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
        {
            //HTTP—FLV只支持 AAC和MP3 音频格式
            if ((ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AAC
                && ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_MP3)
                || ictx->streams[i]->codecpar->sample_rate == 0)
            {
                hflv_audio = false;
            }
            else
            {
                hflv_audio = true;
            }
        }
        else if (ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            // //HTTP—FLV只支持 h264 视频格式
            // if (ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_H264)
            // {
            //     goto end;
            // }
 
            videoindex = i;
        }
    }
 
    if (videoindex == -1)
    {
        goto end;
    }
 
    av_dump_format(ictx, 0, "", 0);
 
    avformat_alloc_output_context2(&octx, NULL, "mpegts", out_filename);
    if (!octx)
    {
        printf( "Could not create output context\n");
        goto end;
    }
    printf("avformat_alloc_output_context2\n");
 
    //将输入流音视频编码信息复制到输出流中
    for (unsigned int i = 0; i < ictx->nb_streams; i++)
    {
        if (!hflv_audio)
        {
            if(ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
            {
                continue;
            }
        }
 
        AVStream *in_stream = ictx->streams[i];
 
        AVStream *out_stream = avformat_new_stream(octx, NULL);
        if (!out_stream)
        {
            goto end;
        }
 
        if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
        {
            goto end;
        }
 
        out_stream->codecpar->codec_tag = 0;
    }
    printf("copy codec context \n");
 
    av_dump_format(octx, 0, out_filename, 1);
 
    //打开输出URL,准备推流
    if (!(octx->oformat->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&octx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            printf( "Could not open output URL '%s'", out_filename);
            goto end;
        }
        printf("avio_open \n");
    }
 
    ret = avformat_write_header(octx, NULL);
    if (ret < 0)
    {
        printf( "Error occurred when opening output URL\n");
        goto end;
    }
 
    printf("start push stream \n");
 
    while (1)
    {
        //获取每一帧数据
        ret = av_read_frame(ictx, &pkt);
        if (ret < 0)
        {
            break;
        }
 
        if (!hflv_audio && pkt.stream_index != videoindex)
        {
            av_packet_unref(&pkt);
            continue;
        }
 
        in_stream = ictx->streams[pkt.stream_index];
 
        if (!hflv_audio && pkt.stream_index == videoindex)
        {
            out_stream = octx->streams[0];
            pkt.stream_index = 0;
        }
        else
        {
            out_stream = octx->streams[pkt.stream_index];
        }
 
        if (pkt.pts == AV_NOPTS_VALUE)
        {
            //Write PTS
            AVRational time_base1 = in_stream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration = (int64_t)((double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate));
            //Parameters
            pkt.pts = (int64_t)((double)(frame_index * calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE));
            pkt.dts = pkt.pts;
            pkt.duration = (int64_t)((double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE));
        }
 
        //指定时间戳
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,
                                   (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base,
                                   (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
 
        if (pkt.stream_index == videoindex)
        {            
            //printf("Send %8d video frames to output URL, [%d]\n",frame_index, pkt.flags);
            frame_index++;
        }
 
        ret = av_interleaved_write_frame(octx, &pkt);
 
        if (ret < 0)
        {
            printf("Error muxing packet.error code %d\n", ret);
            break;
        }

        //释放 packet,否则会内存泄露
        av_packet_unref(&pkt);

        av_usleep(40000);
    }
 
    av_write_trailer(octx);
 
end:
 
    // 该函数会释放用户自定义的IO buffer
    // 上面不再释放,否则会corrupted double-linked list
    avformat_close_input(&ictx);
    avformat_free_context(ictx);
 
    if (octx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(octx->pb);
 
    if (octx)
    {
        avformat_free_context(octx);
    }

}

效果感觉和直接文件推流差不多:
在这里插入图片描述

开发板推流

这里的主要难点是编码数据的获取和ffmpeg库(需要交叉编译)的链接。

  1. 编码数据的获取
    例程里面已经有了数据的获取例程,把它保存到一个队列里面。
    使用有名管道。
  • 数据写入:
    根据文件名创建有名管道,将编码数据写入。
    在这里插入图片描述
    在这里插入图片描述

  • 数据读取
    直接读取文件即可。
    和上面的从内存中读取文件一模一样。
    在这里插入图片描述

  1. ffmpeg静态库的链接
    这一步要修改Makefile,仿照SDK中的例子进行修改:
    文件:
    在这里插入图片描述
    Makefile 修改:
    在这里插入图片描述

  2. 效果
    能推流出来,但是延时不行,而且越来越大(无线和有线都是这样)。
    在这里插入图片描述

TODO

  1. 优化推流延时,应该是推流帧率(25fps)小于实际帧率(30fps)(另外HDMI的延时不稳定在0ms到15ms之间变化,这个不知道为啥不稳定)
  2. 提升画质(提高码率)
  3. 经过摄像头的图像有变形

参考文章

  1. ffmpeg-pusher
  2. 使用FFmpeg命令行进行UDP、RTP推流(H264、TS),ffplay接收
  3. 基于FFmpeg的推流器(UDP推流)
  4. ffmpeg内存读取数据推流rtmp

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

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

相关文章

MIT 6.S081 Operating System Lecture5 (随意的笔记)

系列文章目录 文章目录系列文章目录TrapsA questionsyscallTraps 用户空间和内核空间的切换通常被称为 trap example: shwrite()ecall(); //write 通过 ecall() 指令执行系统调用之后跳转执行到 usertrap 如下图的执行过程。最终sys_write将要显示的数据输出到 console 上 …

【Java 设计模式】创建者模式 之原型模式

原型模式1 定义2 角色3 三好学生案例3.1 浅克隆实现3.1.1 浅克隆定义3.1.2 类图3.1.3 实现3.2 深克隆实现3.2.1 深克隆定义3.2.2 实现1 定义 将一个已经创建好的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2 角色 抽象原型类&#xff1…

Chrome的使用技巧

1. 请求重发 F12 -> Network -> Replay XHR 2. 修改请求参数后重发 F12 -> Network -> Copy -> Copy as fetch 然后在 Console 控制台 ctrl + v ,而后就可以对请求的参数进行编辑,回车就会重更新请求 再在 Network 就可以看到这个请求了 3. 复制对象 (…

echarts入门到实战

官网地址&#xff1a;Apache ECharts 前言 我们应该经常看到或听到”数据可视化“这个词&#xff0c;他其实就是将数据通过各种图表更加直观的展现变化趋势&#xff0c;对比&#xff0c;峰值等等。数据可视化也是未来的趋势。 作为前端程序员&#xff0c;数据可视化也是我们必…

多任务全景感知YOLOPv2:目标检测、freespace、车道线

今年年初出了一片《端到端的多任务感知网络HybridNet&#xff0c;性能优于YOLOP》&#xff0c;论文 HybridNets: End2End Perception Network&#xff0c;代码已开源&#xff0c;在目标检测、车道线、freespace的多任务感知任务上性能优于YOLOP&#xff0c;取得了新SOTA。视频效…

大厂Java面试必备面试题:基础语法-数据类型-编码-注释-运算符-关键字-流程控制语句

基础语法 数据类型 Java有哪些数据类型 定义&#xff1a;Java语言是强类型语言&#xff0c;对于每一种数据都定义了明确的具体的数据类 型&#xff0c;在内存中分配了不同 大小的内存空间。 分类&#xff1a; 基本数据类型 Java源代码---->编译器---->jvm可执行的Java字…

如果Controller里有私有的方法,能成功访问吗?

背景 写代码的时候&#xff0c;复制粘贴的时候&#xff0c;没注意到方法的属性&#xff0c;就导致了Controller里有了一个私有的方法&#xff0c;然后访问这个接口的时候就报了空指针异常&#xff0c;找了好久才找到是这个原因。 来看一个例子 Service public class MyServi…

Netty面试经典问题

目录 Netty是怎么实现高性能设计的&#xff1f; 简单介绍一下对于Netty的了解 Netty的高性能表现在哪些方面 介绍一下Java中的几种IO模型 一个通俗例子读懂BIO、NIO、AIO BIO与NIO的区别 Netty的线程模型 什么是零拷贝 Netty中的模块组件&#xff1a; Netty 中有哪种…

Linux rpm方式安装 MYSQL8.0

1.卸载原有的mysql 数据库 1&#xff09;查找安装的mysql软件包和依赖包&#xff1a; rpm -pa | grep mysql 显示结果&#xff1a; mysql80-community-release-el7-1.noarch mysql-community-server-8.0.11-1.el7.x86_64 mysql-community-common-8.0.11-1.el7.x86_64 mysql…

ReLU,Sigmoid,Tanh,softmax【基础知识总结】

一、ReLU&#xff08;Rectified Linear Activation Function&#xff09;1、优点2、缺点补充二、Sigmoid1、优点2、缺点三、Tanh四、Sigmoid 和 Tanh 激活函数的局限性五、softmax&#xff08;待补充&#xff09;激活函数的引入是为了增加神经网络模型的非线性&#xff0c;没有…

【机器学习入门项目10例】(九):聚类算法用于降维,KMeans的矢量量化应用(图片压缩)

🌠 『精品学习专栏导航帖』 🐳最适合入门的100个深度学习实战项目🐳🐙【PyTorch深度学习项目实战100例目录】项目详解 + 数据集 + 完整源码🐙🐶【机器学习入门项目10例目录】项目详解 + 数据集 + 完整源码🐶🦜【机器学习项目实战10例目录】项目详解 + 数据集 +

万字长文!对比分析了多款存储方案,KeeWiDB最终选择自己来

大数据时代&#xff0c;无人不知Google的“三驾马车”。“三驾马车”指的是Google发布的三篇论文&#xff0c;介绍了Google在大规模数据存储与计算方向的工程实践&#xff0c;奠定了业界大规模分布式存储系统的理论基础&#xff0c;如今市场上流行的几款国产数据库都有参考这三…

Fama-French三因子和五因子模型和Stata代码(内附原始数据)

一、Fama-French三因子模型数据和Stata代码&#xff08;2000-2020年&#xff09; 1、数据来源&#xff1a;原始数据在分享文件中 2、时间跨度&#xff1a;2000-2020年 3、区域范围&#xff1a;全国 5、原始数据&#xff1a; 4、指标说明&#xff1a; 部分指标如下&#xff…

Linux虚拟机的克隆

文章目录&#x1f68f; Linux虚拟机的克隆&#x1f680; 克隆虚拟机&#x1f6ac; 1、虚拟机在未开启的状态下&#x1f6ac; 2、选择创建完整克隆&#x1f6ac; 3、选择虚拟机的名称和位置&#x1f684; 修改 克隆虚拟机的设置&#x1f6ac; 1、mac地址&#x1f6ac; 2、主机名…

RocketMQ安装部署

RocketMQ的物理部署结构图如下&#xff1a; Producer和Consumer对应的是我们的应用程序&#xff0c;多个NameServer实例组成集群&#xff0c;但相互独立&#xff0c;没有信息交换&#xff0c;所以对于NameServer来说部署两个或两个以上即可保证高可用&#xff0c;对于Broker来…

AWS Skill Builder - 练习 ACF 认证的第一站

AWS Skill Builder - 练习 ACF 认证的第一站 AWS Skill Builder https://explore.skillbuilder.aws/learn 是 AWS 针对要想要自学 AWS 云计算技术所提供的网站&#xff0c;里面提了很多自学的课程&#xff0c;今天要展示的是在学习完 AWS Academy Cloud Foundations 课程后&am…

车牌识别停车场智能管理系统

摘 要 本论文主要论述了如何使用JSP技术开发一个车牌识别停车场智能管理系统 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述车牌识别停车场智能管理系统的…

【Paraview教程】第一章安装与基础介绍

1 Paraview介绍 1.1基本介绍 ParaView是一个开源的&#xff0c;跨平台的数据处理和可视化程序。ParaView用户可以迅速的建立起可视化环境利用定量或者是定性的手段去分析数据。利用它的批量处理能力可以在三维空间内在工具栏和展示界面中进行交互操作&#xff0c;从而实现“数…

PDF中的某个图或表想几乎无损的插入ppt或者word里的方法

要使用ps打开pdf并另存为tiff或者&#xff0c;其他方法存储的tiff可能不如这种方法高清 0. 参考方法网址&#xff1a;PS导出符合投稿规范的图片 1. pdf可能很多页&#xff0c;一页内有很多图像文字&#xff0c;要先使用福昕阅读器(破解版本的)裁剪到想保留tiff的那张图或那个表…

四、nginx反向代理

一、反向代理 解释&#xff1a;nginx反向代理如正向代理原理类似&#xff0c;只是实现了不同的功能。客户端将请求发给服务端&#xff08;代理服务器&#xff09;后&#xff0c;服务端&#xff08;代理服务器&#xff09;并没有自己处理请求&#xff0c;而是交给被代理服务器&…