音视频开发19 FFmpeg 视频解码- 将 h264 转化成 yuv

2024-06-07 07:36

本文主要是介绍音视频开发19 FFmpeg 视频解码- 将 h264 转化成 yuv,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

视频解码过程

视频解码过程如下图所示:
⼀般解出来的是420p

FFmpeg流程

这里的流程是和音频的解码过程一样的,不同的只有在存储YUV数据的时候的形式

存储YUV 数据

如果知道YUV 数据的格式

前提:这里我们打开的h264文件,默认是YUV420P 格式的,

我们可以通过  AVFrame->frame 获得,获得的值如果是视频就 是 AVPixelFormat。

我们可以通过 AVPixelFormat ,知道该视频的编码是啥?

在正常情况下,我们需要判断AVPixelFormat是那种类型,当前代码中并没有判断是因为我们默认使用的YUV420P,那么怎么存储这个YUV420P呢?

首先我们这里要明白,一个AVFrame就是一张图片,假设AVframe 我们存储的是322 * 356 ,322并不是16的整倍数,322/16 = 20......2 也就是说一行会有2个字节的剩余
那么这个剩余的2个字节,怎么办呢?会多给14个字节和剩余的2个字节 结合起来。
因此如果我们用和音频类似的写法: fwrite(frame->data[0], 1, frame->width * frame->height,  outfile) 去写,就会有问题,因为要保证这里 width是16的整倍数
这时候就要用到 ffmpeg 的AVFrame给我们提供的 linesize[x]了,

核心代码

        // 一般H264默认为 AV_PIX_FMT_YUV420P, 具体怎么强制转为 AV_PIX_FMT_YUV420P 在音视频合成输出的时候讲解// frame->linesize[1]  因为有字节对齐的问题。// 这里先回顾一下 音频的处理方式,在交错模式的时候,使用的 声道数*每个声道有多少个音频样本 * 每个样本占用多少个字节,这是因为音频上 没有字节对齐的问题//字节对齐问题的根本是因为 ,对于一张 322 * 356 的图片来说 ,322并不是16的整倍数,322/16 = 20......2 也就是说一行会有2个字节的剩余//那么这个剩余的2个字节,怎么办呢?会多给14个字节和剩余的2个字节 结合起来。//因此如果我们用和音频类似的写法: fwrite(frame->data[0], 1, frame->width * frame->height,  outfile) 去写,就会有问题,因为要保证这里 width是16的整倍数//这时候就要用到 ffmpeg 的AVFrame给我们提供的 linesize[x]了,//        uint8_t *data[AV_NUM_DATA_POINTERS]:
//        指向实际的帧数据的指针数组。
//        对于视频帧,这通常是图像平面(如YUV中的Y、U、V平面)。
//        对于音频帧,这通常是音频通道的数据指针。//        int linesize[AV_NUM_DATA_POINTERS]:
//        每一行(视频)或每一个音频通道(音频)的大小。
//        对于视频,这通常是图像宽度的字节数。如果图像的宽度 除以 16 有余数,则这个值会凑成16的倍数。
//        对于音频,这通常是这个通道的字节数大小。 在交错模式下: 理论上等于   声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节
//                            但是,测试发现,在第一个AVFrame包和最后一个 AVframe的时候,linesize[0]的值 比 声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节 大于64.//了解了linesize[]的意义,对于一个avframe,就是包含了一帧,就是一张图片,//YUV420P的存储方式是这样的  YYYYYYYYUUVV
//        那么对于 一张 YUV420P (322 * 120)的图片来看,有多少个Y 呢?多少个U,多少个V呢?
//        Y的个数为:有 120行,一行一行的存储,每一行的实际大小为322, 但是存储322个Y后,就结束了吗?没有 ,因为有字节对齐问题,因此每次存储完322后,还要跳过14个字节,也就是实际大小为linesize[0],//我们先将Y全部存储完毕。//再存储U,U的个数是多少呢?这里要回头看一下YUV420P存储结构图,这里只是结论:宽高均是Y的一半,因此这里要注意存储U的写法//V的存储和U是一样的。// 正确写法  linesize[]代表每行的字节数量,所以每行的偏移是linesize[],但是真正存储的值 Y 是宽度,for(int j=0; j<frame->height; j++)fwrite(frame->data[0] + j * frame->linesize[0], 1, frame->width, outfile);for(int j=0; j<frame->height/2; j++)fwrite(frame->data[1] + j * frame->linesize[1], 1, frame->width/2, outfile);for(int j=0; j<frame->height/2; j++)fwrite(frame->data[2] + j * frame->linesize[2], 1, frame->width/2, outfile);// 错误写法 用source.200kbps.766x322_10s.h264测试时可以看出该种方法是错误的//  写入y分量
//        fwrite(frame->data[0], 1, frame->width * frame->height,  outfile);//Y
//        // 写入u分量
//        fwrite(frame->data[1], 1, (frame->width) *(frame->height)/4,outfile);//U:宽高均是Y的一半
//        //  写入v分量
//        fwrite(frame->data[2], 1, (frame->width) *(frame->height)/4,outfile);//V:宽高均是Y的一半

AVCodecParser说明

⽤于解析输⼊的数据流并把它分成⼀帧⼀帧的压缩编码数据。⽐较形象
的说法就是把⻓⻓的⼀段连续的数据“切割”成⼀段段的数据。

avcodec_send_packet

函数:int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
作⽤: ⽀持将裸流数据包送给解码器
警告:
输⼊的avpkt-data缓冲区必须⼤于AV_INPUT_PADDING_SIZE,因为优化的字节流读取
器必须⼀次读取32或者64⽐特的数据
不能跟之前的API(例如avcodec_decode_video2)混⽤,否则会返回不可预知的错误
备注:
在将包发送给解码器的时候,AVCodecContext必须已经通过avcodec_open2打开
参数:
avctx:解码上下⽂
avpkt:输⼊AVPakcet.通常情况下,输⼊数据是⼀个单⼀的视频帧或者⼏个完整的⾳频
帧。调⽤者保留包的原有属性,解码器不会修改包的内容。解码器可能创建对包的引⽤。
如果包没有引⽤计数将拷⻉⼀份。跟以往的API不⼀样,输⼊的包的数据将被完全地消耗,
如果包含有多个帧,要求多次调⽤avcodec_recvive_frame,直到
avcodec_recvive_frame返回 VERROR(EAGAIN)或AVERROR_EOF 。输⼊参数可以为
NULL,或者AVPacket的data域设置为NULL或者size域设置为0,表示将刷新所有的包,
意味着数据流已经结束了。第⼀次发送刷新会总会成功,第⼆次发送刷新包是没有必要
的,并且返回AVERROR_EOF,如果×××缓存了⼀些帧,返回⼀个刷新包,将会返回所有的
解码包
返回值:
0: 表示成功
AVERROR(EAGAIN):当前状态不接受输⼊,⽤户必须先使⽤ avcodec_receive_frame() 读
取数据帧;
AVERROR_EOF:解码器已刷新,不能再向其发送新包;
AVERROR(EINVAL):没有打开解码器,或者这是⼀个编码器,或者要求刷新;
AVERRO(ENOMEN):⽆法将数据包添加到内部队列。

avcodec_receive_frame

函数:int avcodec_receive_frame ( AVCodecContext * avctx, AVFrame * frame )
作⽤:从解码器返回已解码的输出数据。
参数:
avctx: 编解码器上下⽂
frame: 获取使⽤reference-counted机制的audio或者video帧(取决于解码器类型)。请
注意,在执⾏其他操作之前,函数内部将始终先调⽤av_frame_unref(frame)

所有的代码

/**
* @projectName   07-05-decode_audio
* @brief         解码音频,主要的测试格式aac和mp3
* @author        Liao Qingfu
* @date          2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavutil/frame.h>
#include <libavutil/mem.h>#include <libavcodec/avcodec.h>#define VIDEO_INBUF_SIZE 20480
#define VIDEO_REFILL_THRESH 4096static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{av_strerror(errnum, err_buf, 128);return err_buf;
}static void print_video_format(const AVFrame *frame)
{printf("width: %u\n", frame->width);printf("height: %u\n", frame->height);printf("format: %u\n", frame->format);// 格式需要注意
}static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,FILE *outfile)
{int ret;/* send the packet with the compressed data to the decoder */ret = avcodec_send_packet(dec_ctx, pkt);if(ret == AVERROR(EAGAIN)){fprintf(stderr, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");}else if (ret < 0){fprintf(stderr, "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",av_get_err(ret), pkt->size);return;}/* read all the output frames (infile general there may be any number of them */while (ret >= 0){// 对于frame, avcodec_receive_frame内部每次都先调用ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0){fprintf(stderr, "Error during decoding\n");exit(1);}static int s_print_format = 0;if(s_print_format == 0){s_print_format = 1;print_video_format(frame);}printf("video frame data = %f \n", (frame->width) * (frame->height) * 1.5);printf("frame->line[0] = %d \n",frame->linesize[0]);printf("frame->line[1] = %d \n",frame->linesize[1]);printf("frame->line[2] = %d \n",frame->linesize[2]);printf("frame->pkt_size = %d \n",frame->pkt_size);// 一般H264默认为 AV_PIX_FMT_YUV420P, 具体怎么强制转为 AV_PIX_FMT_YUV420P 在音视频合成输出的时候讲解// frame->linesize[1]  因为有字节对齐的问题。// 这里先回顾一下 音频的处理方式,在交错模式的时候,使用的 声道数*每个声道有多少个音频样本 * 每个样本占用多少个字节,这是因为音频上 没有字节对齐的问题//字节对齐问题的根本是因为 ,对于一张 322 * 356 的图片来说 ,322并不是16的整倍数,322/16 = 20......2 也就是说一行会有2个字节的剩余//那么这个剩余的2个字节,怎么办呢?会多给14个字节和剩余的2个字节 结合起来。//因此如果我们用和音频类似的写法: fwrite(frame->data[0], 1, frame->width * frame->height,  outfile) 去写,就会有问题,因为要保证这里 width是16的整倍数//这时候就要用到 ffmpeg 的AVFrame给我们提供的 linesize[x]了,//        uint8_t *data[AV_NUM_DATA_POINTERS]:
//        指向实际的帧数据的指针数组。
//        对于视频帧,这通常是图像平面(如YUV中的Y、U、V平面)。
//        对于音频帧,这通常是音频通道的数据指针。//        int linesize[AV_NUM_DATA_POINTERS]:
//        每一行(视频)或每一个音频通道(音频)的大小。
//        对于视频,这通常是图像宽度的字节数。如果图像的宽度 除以 16 有余数,则这个值会凑成16的倍数。
//        对于音频,这通常是这个通道的字节数大小。 在交错模式下: 理论上等于   声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节
//                            但是,测试发现,在第一个AVFrame包和最后一个 AVframe的时候,linesize[0]的值 比 声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节 大于64.//了解了linesize[]的意义,对于一个avframe,就是包含了一帧,就是一张图片,//YUV420P的存储方式是这样的  YYYYYYYYUUVV
//        那么对于 一张 YUV420P (322 * 120)的图片来看,有多少个Y 呢?多少个U,多少个V呢?
//        Y的个数为:有 120行,一行一行的存储,每一行的实际大小为322, 但是存储322个Y后,就结束了吗?没有 ,因为有字节对齐问题,因此每次存储完322后,还要跳过14个字节,也就是实际大小为linesize[0],//我们先将Y全部存储完毕。//再存储U,U的个数是多少呢?这里要回头看一下YUV420P存储结构图,这里只是结论:宽高均是Y的一半,因此这里要注意存储U的写法//V的存储和U是一样的。// 正确写法  linesize[]代表每行的字节数量,所以每行的偏移是linesize[],但是真正存储的值 Y 是宽度,for(int j=0; j<frame->height; j++)fwrite(frame->data[0] + j * frame->linesize[0], 1, frame->width, outfile);for(int j=0; j<frame->height/2; j++)fwrite(frame->data[1] + j * frame->linesize[1], 1, frame->width/2, outfile);for(int j=0; j<frame->height/2; j++)fwrite(frame->data[2] + j * frame->linesize[2], 1, frame->width/2, outfile);// 错误写法 用source.200kbps.766x322_10s.h264测试时可以看出该种方法是错误的//  写入y分量
//        fwrite(frame->data[0], 1, frame->width * frame->height,  outfile);//Y
//        // 写入u分量
//        fwrite(frame->data[1], 1, (frame->width) *(frame->height)/4,outfile);//U:宽高均是Y的一半
//        //  写入v分量
//        fwrite(frame->data[2], 1, (frame->width) *(frame->height)/4,outfile);//V:宽高均是Y的一半}
}
// 注册测试的时候不同分辨率的问题
// 提取H264: ffmpeg -i source.200kbps.768x320_10s.flv -vcodec libx264 -an -f h264 source.200kbps.768x320_10s.h264
// 提取MPEG2: ffmpeg -i source.200kbps.768x320_10s.flv -vcodec mpeg2video -an -f mpeg2video source.200kbps.768x320_10s.mpeg2
// 播放:ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25  source.200kbps.768x320_10s.yuv
int main(int argc, char **argv)
{const char *outfilename;const char *filename;const AVCodec *codec;AVCodecContext *codec_ctx= NULL;AVCodecParserContext *parser = NULL;int len = 0;int ret = 0;FILE *infile = NULL;FILE *outfile = NULL;// AV_INPUT_BUFFER_PADDING_SIZE 在输入比特流结尾的要求附加分配字节的数量上进行解码uint8_t inbuf[VIDEO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data = NULL;size_t   data_size = 0;AVPacket *pkt = NULL;AVFrame *decoded_frame = NULL;//    if (argc <= 2)
//    {
//        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
//        exit(0);
//    }
//    filename    = argv[1];
//    outfilename = argv[2];filename    = "D:/AllInformation/qtworkspacenew/07-06-decode_video/source.200kbps.768x320_10s.h264";outfilename = "D:/AllInformation/qtworkspacenew/07-06-decode_video/source.200kbps.768x320_10s.yuv";//我们这里 768x320_10s 是使用的 YUV420p格式,那么一张图片的大小应该为 768*320*1.5 = 368640 bit = 46080 bytes = 45 kb//我们这里计算这个,就是为了查看是否 avframe 的大小.log如下,因为768除以16是没有余数的,因此这里没有字节对齐问题,
//    video frame data = 368640.000000
//    frame->line[0] = 768
//    frame->line[1] = 384
//    frame->line[2] = 384//    filename    = "D:/AllInformation/qtworkspacenew/07-06-decode_video/source.200kbps.766x322_10s.h264";
//    outfilename = "D:/AllInformation/qtworkspacenew/07-06-decode_video/source.200kbps.766x322_10s.yuv";//我们这里 766x322_10s 是使用的 YUV420p格式,那么一张图片的大小应该为 766*322*1.5 = 369,978 bit = 46247.25 bytes 约等于 45.16 kb//我们这里计算这个,就是为了查看是否 avframe 的大小 。log如下,说明在766除以16有余数的case下,是有字节对齐的问题存在的,因此在存储这个文件的pcm时候,要注意使用到 linesize[x]//    video frame data = 369978.000000
//    frame->line[0] = 768
//    frame->line[1] = 384
//    frame->line[2] = 384printf("aaa\n");pkt = av_packet_alloc();enum AVCodecID video_codec_id = AV_CODEC_ID_H264;if(strstr(filename, "264") != NULL){video_codec_id = AV_CODEC_ID_H264;}else if(strstr(filename, "mpeg2") != NULL){video_codec_id = AV_CODEC_ID_MPEG2VIDEO;}else{printf("default codec id:%d\n", video_codec_id);}// 查找解码器codec = avcodec_find_decoder(video_codec_id);  // AV_CODEC_ID_H264if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}// 获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)parser = av_parser_init(codec->id);if (!parser) {fprintf(stderr, "Parser not found\n");exit(1);}// 分配codec上下文codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate audio codec context\n");exit(1);}// 将解码器和解码器上下文进行关联if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}// 打开输入文件infile = fopen(filename, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}// 打开输出文件outfile = fopen(outfilename, "wb");if (!outfile) {av_free(codec_ctx);exit(1);}// 读取文件进行解码data      = inbuf;data_size = fread(inbuf, 1, VIDEO_INBUF_SIZE, infile);while (data_size > 0){if (!decoded_frame){if (!(decoded_frame = av_frame_alloc())){fprintf(stderr, "Could not allocate audio frame\n");exit(1);}}ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0){fprintf(stderr, "Error while parsing\n");exit(1);}data      += ret;   // 跳过已经解析的数据data_size -= ret;   // 对应的缓存大小也做相应减小if (pkt->size)decode(codec_ctx, pkt, decoded_frame, outfile);if (data_size < VIDEO_REFILL_THRESH)    // 如果数据少了则再次读取{memmove(inbuf, data, data_size);    // 把之前剩的数据拷贝到buffer的起始位置data = inbuf;// 读取数据 长度: VIDEO_INBUF_SIZE - data_sizelen = fread(data + data_size, 1, VIDEO_INBUF_SIZE - data_size, infile);if (len > 0)data_size += len;}}/* 冲刷解码器 */pkt->data = NULL;   // 让其进入drain modepkt->size = 0;decode(codec_ctx, pkt, decoded_frame, outfile);fclose(outfile);fclose(infile);avcodec_free_context(&codec_ctx);av_parser_close(parser);av_frame_free(&decoded_frame);av_packet_free(&pkt);printf("main finish, please enter Enter and exit\n");return 0;
}

播放测试:

ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25 source.200kbps.768x320_10s.yuv

ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25
source.200kbps.768x320_10s.yuv

分离H264或mpeg2video视频格式数据

提取H264:

ffmpeg -i source.200kbps.768x320_10s.flv -vcodec libx264 -an -f h264
source.200kbps.768x320_10s.h264

提取MPEG2:

ffmpeg -i source.200kbps.768x320_10s.flv -vcodec mpeg2video -an -f mpeg2video
source.200kbps.768x320_10s.mpeg2v

FFmpeg命令查找-f 后面的格式

⽐如我们在-f fmt打算指定格式时,怎么知道什么样的格式才是适合的format?
可以通过 ffmpeg -formats | findstr xx的⽅式去查找。
对于findstr,/i是忽略⼤⼩写
⽐如:
查找Audio的裸流解复⽤器:ffmpeg -formats | findstr /i audio
查找Video的裸流解复⽤器:ffmpeg -formats | findstr /i video

这篇关于音视频开发19 FFmpeg 视频解码- 将 h264 转化成 yuv的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1038547

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧