windows下使用FFmpeg开源库进行视频编解码完整步聚

最终解码效果:

1.UI设计

 2.在控件属性窗口中输入默认值

3.复制已编译FFmpeg库到工程同级目录下

 4.在工程引用FFmpeg库及头文件

 

5.链接指定FFmpeg库

 

6.使用FFmpeg库

引用头文件 

extern "C"
{
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/bsf.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libavutil/imgutils.h"
#include "libavutil/time.h"
#include <libswresample/swresample.h>

}

创建视频编解码管理类 

实现视频编解码管理类

#include "ffmpegmananger.h"
#include <QThread>
ffmpegMananger::ffmpegMananger(QObject *parent ):
    QObject(parent)
{
    m_pInFmtCtx = nullptr;
    m_pTsFmtCtx  = nullptr;
    m_qstrRtspURL = "";
    m_qstrOutPutFile = "";
}
ffmpegMananger::~ffmpegMananger()
{
    avformat_free_context(m_pInFmtCtx);
    avformat_free_context(m_pTsFmtCtx);
}

void ffmpegMananger::getRtspURL(QString strRtspURL)
{
    this->m_qstrRtspURL = strRtspURL;
}
void ffmpegMananger::getOutURL(QString strRute)
{
    this->m_qstrOutPutFile = strRute;
    printf("===========%s\n",m_qstrOutPutFile.toStdString().c_str());
}
void ffmpegMananger::setOutputCtx(AVCodecContext *encCtx, AVFormatContext **pTsFmtCtx,int &nVideoIdx_out)
{
    avformat_alloc_output_context2(pTsFmtCtx , nullptr, nullptr, m_qstrOutPutFile.toStdString().c_str());
    if (!pTsFmtCtx ) {
        printf("Could not create output context\n");
        return;
    }
    if (avio_open(&((*pTsFmtCtx)->pb), m_qstrOutPutFile.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0)
    {
       avformat_free_context(*pTsFmtCtx);
       printf("avio_open fail.");
       return;
    }
    AVStream *out_stream = avformat_new_stream(*pTsFmtCtx, encCtx->codec);
    nVideoIdx_out = out_stream->index;
    //nVideoIdx_out = out_stream->index;
    avcodec_parameters_from_context(out_stream->codecpar, encCtx);
    printf("==========Output Information==========\n");
    av_dump_format(*pTsFmtCtx, 0, m_qstrOutPutFile.toStdString().c_str(), 1);
    printf("======================================\n");
}
int ffmpegMananger::ffmepgInput()
{
    int nRet = 0;
    AVCodecContext *encCtx = nullptr;//编码器
    //const char *pUrl = "D:/videos/264.dat";
    std::string temp = m_qstrRtspURL.toStdString();
    const char *pUrl = temp.c_str();
    printf("===========%s\n",pUrl);
    AVDictionary *options = nullptr;
    av_dict_set(&options,"rtsp_transport", "tcp", 0);
    av_dict_set(&options,"stimeout","10000000",0);
    // 设置“buffer_size”缓存容量
    av_dict_set(&options, "buffer_size", "1024000", 0);
    nRet = avformat_open_input(&m_pInFmtCtx,pUrl,nullptr,&options);
    if( nRet < 0)
    {
        printf("Could not open input file,===========keep trying \n");
        return nRet;
    }
    avformat_find_stream_info(m_pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");
    av_dump_format(m_pInFmtCtx, 0, pUrl, 0);
    printf("======================================\n");
    //1.获取视频流编号
    int nVideo_indx = av_find_best_stream(m_pInFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
    if(nVideo_indx < 0)
    {
        avformat_free_context(m_pInFmtCtx);
        printf("查找解码器失败\n");
        return -1;
    }
    //2.查找解码器
    AVCodec *pInCodec = avcodec_find_decoder(m_pInFmtCtx->streams[nVideo_indx]->codecpar->codec_id);
    if(nullptr == pInCodec)
    {
        printf("avcodec_find_decoder fail.");
        return -1;
    }
    //获取解码器上下文
    AVCodecContext* pInCodecCtx = avcodec_alloc_context3(pInCodec);
    //复制解码器参数
    nRet = avcodec_parameters_to_context(pInCodecCtx, m_pInFmtCtx->streams[nVideo_indx]->codecpar);
    if(nRet < 0)
    {

        avcodec_free_context(&pInCodecCtx);
        printf("avcodec_parameters_to_context fail.");
        return -1;
    }
    //打开解码器
    if(avcodec_open2(pInCodecCtx, pInCodec, nullptr) < 0)
    {
        avcodec_free_context(&pInCodecCtx);
        printf("Error: Can't open codec!\n");
        return -1;
    }
    printf("width = %d\n", pInCodecCtx->width);
    printf("height = %d\n", pInCodecCtx->height);
    int frame_index = 0;
    int got_picture = 0;
    AVStream *in_stream =nullptr;
    AVStream *out_stream =nullptr;
    AVFrame *pFrame= av_frame_alloc();
    AVPacket *newpkt = av_packet_alloc();
    AVPacket *packet = av_packet_alloc();
    av_init_packet(newpkt);
    av_init_packet(packet);
    // alloc AVFrame
    AVFrame*pFrameRGB = av_frame_alloc();
    // 图像色彩空间转换、分辨率缩放、前后图像滤波处理
    SwsContext *m_SwsContext = sws_getContext(pInCodecCtx->width, pInCodecCtx->height,
            pInCodecCtx->pix_fmt, pInCodecCtx->width, pInCodecCtx->height,
            AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);

    int bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pInCodecCtx->width, pInCodecCtx->height,4);
    uint8_t *m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));

    // 将分配的内存空间给pFrameRGB使用
    avpicture_fill((AVPicture *)pFrameRGB, m_OutBuffer, AV_PIX_FMT_RGB32, pInCodecCtx->width, pInCodecCtx->height);
    if(encCtx == nullptr)
    {
        //打开编码器
        openEncoder(pInCodecCtx->width, pInCodecCtx->height,&encCtx);
    }
    int videoindex_out = 0;
    //设置输出文件上下文
    setOutputCtx(encCtx,&m_pTsFmtCtx,videoindex_out);
    //Write file header
    if (avformat_write_header(m_pTsFmtCtx, nullptr) < 0)
    {
        avformat_free_context(m_pTsFmtCtx);
        printf("Error occurred when opening output file\n");
        return -1;
    }
    printf("==============writer trail===================.\n");
    int count = 0;
    nRet = 0;
    while(av_read_frame(m_pInFmtCtx, packet) >= 0)//从pInFmtCtx读H264数据到packet;
    {
        if(packet->stream_index != nVideo_indx)//仅保留图像
        {
            continue;
        }
        if(avcodec_send_packet(pInCodecCtx, packet)<0)//送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
        {
           break;
        }
        av_packet_unref(packet);
        got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);//把解码好的YUV数据放到pFrame中
        if(0 == got_picture)//解码好一帧数据
        {
            //发送显示图像的信号
            // 对解码视频帧进行缩放、格式转换等操作
            sws_scale(m_SwsContext, (uint8_t const * const *)pFrame->data,
                     pFrame->linesize, 0, pInCodecCtx->height,
                     pFrameRGB->data, pFrameRGB->linesize);

            // 转换到QImage
            QImage tmmImage((uchar *)m_OutBuffer, pInCodecCtx->width, pInCodecCtx->height, QImage::Format_RGB32);
            QImage image = tmmImage.copy();

            // 发送QImage
            emit Sig_GetOneFrame(image);

            setDecoderPts(newpkt->stream_index,count, pFrame);
            count++;
            //送原始数据给编码器进行编码
            nRet = avcodec_send_frame(encCtx,pFrame);
            if(nRet < 0)
            {
                continue;
            }
            //从编码器获取编号的数据
            while(nRet >= 0)
            {
                nRet = avcodec_receive_packet(encCtx,newpkt);
                if(nRet < 0)
                {
                    break;
                }
                setEncoderPts(nVideo_indx,frame_index,videoindex_out,newpkt);
                int _count = 1;
                printf("Write %d Packet. size:%5d\tpts:%lld\n", _count,newpkt->size, newpkt->pts);

                if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
                {
                    printf("Error muxing packet\n");
                    goto end;
                }
                _count++;
                av_packet_unref(newpkt);
            }
        }
    }
    while(1)//从pInFmtCtx读H264数据到packet;
    {
        if(packet->stream_index != nVideo_indx)//仅保留图像
        {
            continue;
        }
        if(avcodec_send_packet(pInCodecCtx, packet)<0)//送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
        {
            continue;
        }
        av_packet_unref(packet);
        got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);//把解码好的YUV数据放到pFrame中
        if(!got_picture)//解码好一帧数据
        {
            AVRational in_time_base1 = in_stream->time_base;
            in_stream = m_pInFmtCtx->streams[newpkt->stream_index];

            //Duration between 2 frames (us)
            int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
            count++;
            //送原始数据给编码器进行编码
            nRet = avcodec_send_frame(encCtx,pFrame);
            if(nRet < 0)
            {
                break;
            }
            //从编码器获取编号的数据
            while(nRet >= 0)
            {
                nRet = avcodec_receive_packet(encCtx,newpkt);
                if(nRet < 0)
                {
                    continue;
                }
                in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
                out_stream = m_pTsFmtCtx->streams[videoindex_out];
                if (newpkt->stream_index == nVideo_indx)
                {
                    //FIX:No PTS (Example: Raw H.264)
                    //Simple Write PTS
                    if (newpkt->pts == AV_NOPTS_VALUE)
                    {
                        //Write PTS
                        AVRational time_base1 = in_stream->time_base;
                        //Duration between 2 frames (us)
                        int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
                        //Parameters
                        newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                        newpkt->dts = newpkt->pts;
                        newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                        frame_index++;
                    }
                 }
                //Convert PTS/DTS
                newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
                newpkt->pos = -1;
                newpkt->stream_index = videoindex_out;
                int count = 1;
                printf("Write %d Packet. size:%5d\tpts:%lld\n", count,newpkt->size, newpkt->pts);

                if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
                {
                    printf("Error muxing packet\n");
                    goto end;
                }
                count++;
                av_packet_unref(newpkt);
            }
        }
    }
    //Write file trailer
    av_write_trailer(m_pTsFmtCtx);
end:
    av_frame_free(&pFrame);
    av_frame_free(&pFrameRGB);
    av_packet_unref(newpkt);
    av_packet_unref(packet);
    std::cout<<"rtsp's h264 to ts end";  
    return  0;
}
void ffmpegMananger::setDecoderPts(int idx,int count,AVFrame *pFrame)
{
    AVStream* in_stream = m_pInFmtCtx->streams[idx];
    AVRational in_time_base1 = in_stream->time_base;
    //Duration between 2 frames (us)
    int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
    pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
}
void ffmpegMananger::setEncoderPts(int nVideo_indx,int frame_index,int videoindex_out,AVPacket *newpkt)
{
    AVStream*in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
    AVStream*out_stream = m_pTsFmtCtx->streams[videoindex_out];
    if (newpkt->stream_index == nVideo_indx)
    {
        //FIX:No PTS (Example: Raw H.264)
        //Simple Write PTS
        if (newpkt->pts == AV_NOPTS_VALUE)
        {
            //Write PTS
            AVRational time_base1 = in_stream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            //Parameters
            newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            newpkt->dts = newpkt->pts;
            newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            frame_index++;
        }
     }
    //Convert PTS/DTS
    newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
    newpkt->pos = -1;
    newpkt->stream_index = videoindex_out;
}
void ffmpegMananger::writeTail()
{
    //Write file trailer
    av_write_trailer(m_pTsFmtCtx);
}
void ffmpegMananger::openEncoder(int width, int height, AVCodecContext** enc_ctx)
{
    //使用libx264编码器
    AVCodec * pCodec = avcodec_find_encoder_by_name("libx264");
    if(nullptr == pCodec)
    {
        printf("avcodec_find_encoder_by_name fail.\n");
        return;
    }
    //获取编码器上下文
    *enc_ctx = avcodec_alloc_context3(pCodec);
    if(nullptr == enc_ctx)
    {
        printf("avcodec_alloc_context3(pCodec) fail.\n");
        return;
    }
    //sps/pps
    (*enc_ctx)->profile = FF_PROFILE_H264_MAIN;
    (*enc_ctx)->level = 30;//表示level是5.0
    //分辨率
    (*enc_ctx)->width = width;
    (*enc_ctx)->height = height;
    //gop
    (*enc_ctx)->gop_size = 25;//i帧间隔
    (*enc_ctx)->keyint_min = 20;//设置最小自动插入i帧的间隔.OPTION
    //B帧
    (*enc_ctx)->max_b_frames = 0;//不要B帧
    (*enc_ctx)->has_b_frames = 0;//
    //参考帧
    (*enc_ctx)->refs = 3;//OPTION
    //设置输入的yuv格式
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    //设置码率
    (*enc_ctx)->bit_rate = 3000000;
    //设置帧率
    //(*enc_ctx)->time_base = (AVRational){1,25};//帧与帧之间的间隔
    (*enc_ctx)->time_base.num = 1;
    (*enc_ctx)->time_base.den = 25;
    //(*enc_ctx)->framerate = (AVRational){25,1};//帧率 25帧每秒
    (*enc_ctx)->framerate.num = 25;
    (*enc_ctx)->framerate.den = 1;
    if(avcodec_open2((*enc_ctx),pCodec,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    return;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文章分类管理接口

目录 前言 新建表 获取文章分类列表接口 初始化路由模块 将路由对象导出并使用 初始化路由对象处理函数 修改路由代码 导入数据库 定义sql语句 调用db.query() 完整的获取文章分类列表处理函数 新增文章分类接口 定义路由和处理函数 验证表单数据 查询分类名称与…

CSS基础入门04

目录 1.内边距 1.1基础写法 1.2复合写法 2.外边距 2.1基础写法 2.2复合写法 2.3块级元素水平居中 3.去除浏览器默认样式 4.弹性布局 4.1初体验 5.flex 布局基本概念 6.常用属性 6.1justify-content 6.2align-items 1.内边距 padding 设置内容和边框之间的距离. …

3D RPG Course | Core 学习日记一:初识URP

前言 最近开始学习Unity中文课堂M_Studio&#xff08;麦大&#xff09;的3D RPG Course&#xff0c;学习一下3D RPG游戏核心功能的实现&#xff0c;第一课我们学习到的是地图场景的编辑&#xff0c;其中涉及到了URP渲染。 我们首先进入Unity资源商店把地图素材和人物素材导入好…

前端将图片储存table表格中,页面回显

<el-table :data"tableData" v-loading"loading" style"width: 100%" height"calc(100vh - 270px)" :size"tableSize"row-dblclick"enterClick"><el-table-column prop"name" label"文档…

个人服务器怎么搭建?个人服务器搭建方法

​  个人服务器是指一台由个人拥有和管理的服务器&#xff0c;用于存储和提供个人网站、应用程序或其他在线服务。搭建个人服务器可以让我们更好地掌控自己的数据和网络资源。下面介绍一种常见的个人服务器搭建方法。 第一步&#xff1a;选择合适的硬件 我们需要选择一台适合…

Java毕业设计 SpringBoot 新能源充电桩管理系统

Java毕业设计 SpringBoot 新能源充电桩管理系统 SpringBoot 新能源充电桩管理系统 功能介绍 管理员 登录 验证码 注册 系统用户管理 普通用户管理 通知公告管理 留言管理 充电站管理 充电桩管理 充电桩预约 充电管理 订单管理 修改密码 普通用户 登录 修改个人资料 通知公告…

Flink Hive Catalog操作案例

在此对Flink读写Hive表操作进行逐步记录&#xff0c;需要指出的是&#xff0c;其中操作Hive分区表和非分区表的DDL有所不同&#xff0c;以下分别记录。 基础环境 Hive-3.1.3 Flink-1.17.1 基本操作与准备 1、上传依赖jar包到flink/lib目录下 cp flink-sql-connector-hive-…

Java工具库——commons-lang3的50个常用方法

未来的你&#xff0c;我亲爱的女孩&#xff0c;愿此刻无忧无虑&#xff0c;开心&#xff0c;快乐… 工具库介绍 Apache Commons Lang 3&#xff08;通常简称为Commons Lang 3&#xff09;是Apache Commons项目中的一个Java工具库&#xff0c;它提供了一系列实用的工具类和方法…

Linux|安装Nomachine

参考&#xff1a;2022 Nomachine 最简安装与使用指南&#xff08;https://blog.csdn.net/qq_51116518/article/details/127450253&#xff09; 解压 先将目录调整到压缩包所在目录&#xff0c;输入sudo tar zxvf nomachine_7.6.2_3_aarch64.tar.gz 添加权限 sudo chmod -R…

GZ035 5G组网与运维赛题第6套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项&#xff08;高职组&#xff09; 赛题第6套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通&#xff08;35分&#xff09; 子任务1&#xff1a;5G公共网络部署与调试&#xff08;15分&#xff09; …

RK3568-clock

pll锁相环 总线 gating rk3568.dtsi pmucru: clock-controller@fdd00000 {compatible = "rockchip,rk3568-pmucru";reg = <0x0 0xfdd00000 0x0 0x1000>;rockchip,grf = <&grf>;rockchip,pmugrf = <&pmugrf>;#clock-cells = <1>;#re…

SQL BETWEEN运算符

SQL BETWEEN 运算符 BETWEEN运算符用于选取介于两个值之间的数据范围内的值。 BETWEEN运算符选择给定范围内的值。值可以是数字&#xff0c;文本或日期。 BETWEEN运算符是包含性的&#xff1a;包括开始和结束值&#xff0c;且开始值需小于结束值。 SQL BETWEEN 语法 SELECT …

使用 SQL 的方式查询消息队列数据以及踩坑指南

背景 为了让业务团队可以更好的跟踪自己消息的生产和消费状态&#xff0c;需要一个类似于表格视图的消息列表&#xff0c;用户可以直观的看到发送的消息&#xff1b;同时点击详情后也能查到消息的整个轨迹。 消息列表 点击详情后查看轨迹 原理介绍 由于 Pulsar 并没有关系型数…

Angular-03:组件模板

各种学习后的知识点整理归纳&#xff0c;非原创&#xff01; 组件模板 ① 数据绑定② 属性绑定③ 类名绑定④ 样式绑定⑤ 事件绑定⑥ 获取原生DOM对象6.1 在组件模板中获取6.2 在组件类中获取 ⑦ 双向数据绑定⑧ 内容投影8.1 select选择器8.2 单槽投影8.3 多槽投影 ⑨ 安全操作…

Vue图片路径问题(动态引入)

vue项目中我们经常会遇到动态路径的图片无法显示的问题&#xff0c;以下是静态路径和动态路径的常见使用方法。 1.静态路径 在日常的开发中&#xff0c;图片的静态路径通过相对路径和绝对路径的方式引入。 相对路径&#xff1a;以.开头的&#xff0c;例如./、../之类的。就是…

LangChain+LLM实战---Langchain-Chatchat概述

LangChain介绍 LangChain是个开源的框架&#xff0c;它可以让AI开发人员把像GPT-4这样的大型语言模型(LLM)和外部数据结合起来。可以简单认为LangChain是LLM领域的Spring&#xff0c;以及开源版的ChatGPT插件系统。 LangChain的强大之处不仅能通过API调用语言模型&#xff0c;…

Panda3d 介绍

Panda3d 介绍 文章目录 Panda3d 介绍Panda3d 的安装Panda3d 的坐标系统介绍Panda3d 的运行Panda3d 加载一个熊猫父节点和子节点之间的关系 验证Panda3d 的坐标系统X 轴的平移Y 轴的平移Z 轴的平移X 轴的旋转Y 轴的旋转Z 轴的旋转 Panda3D是一个3D引擎:一个用于3D渲染和游戏开发…

为什么看了那么多测试技术帖,感觉自己还是菜?

作为测试新手&#xff0c;最爱莫过于看各大牛发的技术贴&#xff0c;这篇很牛叉&#xff0c;那篇也很有道理&#xff0c;似乎自己看着看着也会成为高手。然而几年后&#xff0c;发现自己对专业知识的理解乱的很&#xff0c;里面更有很多自相矛盾的地方&#xff0c;这到底是哪里…

iTransformer: INVERTED TRANSFORMERS ARE EFFECTIVE FOR TIME SERIES FORECASTING

#论文题目&#xff1a;ITRANSFORMER: INVERTED TRANSFORMERS ARE EFFECTIVE FOR TIME SERIES FORECASTING #论文地址&#xff1a;https://arxiv.org/abs/2310.06625 #论文源码开源地址&#xff1a;https://github.com/thuml/Time-Series-Library #论文所属会议&#xff1a;Mach…

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧&#xff0c;非常容易上手&#xff1a; # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK&#xff1a;欢迎访问主页&#xff01;"if __name__ "__main__"…