当前位置: 首页 > news >正文

SDL2绘制ffmpeg解析的mp4文件

文章目录

  • 1.FFMPEG利用命令行将mp4转yuv420
  • 2.ffmpeg将mp4解析为yuv数据
    • 2.1 核心api:
  • 3.SDL2进行yuv绘制到屏幕
    • 3.1 核心api
  • 4.完整代码
  • 5.效果展示
  • 6.SDL2事件响应补充
  • 6.1 处理方式-01
  • 6.2 处理方式-02


本项目采用生产者消费者模型,生产者线程:使用ffmpeg将mp4格式数据解析为yuv的帧,消费者线程:利用sdl2将解析的yuv的帧进行消费,绘制到屏幕上。未完成的部分:1.解析音频数据,并与视频数据同步。2.增加界面:暂停,播放按钮,支持视频前进和后退。
学习音视频的参考资料与项目:
playdemo_github
雷神的csdn博客


1.FFMPEG利用命令行将mp4转yuv420

ffmpeg -i input.mp4 -c:v rawvideo -pix_fmt yuv420p output.yuv

在这里插入图片描述

2.ffmpeg将mp4解析为yuv数据

2.1 核心api:

  • av_read_frame:读取一帧数据
  • avcodec_send_packet:将数据包发送给解码器
  • avcodec_receive_frame:将数据包从解码器中取
  • sws_scale:格式转换,将解码后的帧数据转为yuv数据,存储在data[0],data[1],data[2]中
    在这里插入图片描述
void readFrame()
{AVPacket* avPacket = av_packet_alloc();AVFrame* frame = av_frame_alloc();FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data","wb+") ;while (av_read_frame(formatContext, avPacket) >= 0 && fp){if (avPacket->stream_index == videoStreamIndex){if (avcodec_send_packet(codecContext, avPacket) < 0) {std::cerr << "发送数据包到解码器失败" << std::endl;break;}/*解码*/int ret = avcodec_receive_frame(codecContext, frame);printf("ret:%d\n", ret);if (ret >= 0){ret = sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, yuvFrame->data, yuvFrame->linesize);printf("sws_scale ret=%d\n", ret);std::lock_guard<std::mutex>lck(mtx);isFinished = false;memcpy(yuvBuf, yuvFrame->data[0], yuvFrame->width * yuvFrame->height);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height, yuvFrame->data[1], yuvFrame->width * yuvFrame->height / 4);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height*5/4, yuvFrame->data[2], yuvFrame->width * yuvFrame->height / 4);isFinished = true;condvar.notify_one();//保存y分量//fwrite(yuvFrame->data[0], 1, yuvFrame->width * yuvFrame->height, fp);//保存uv分量//fwrite(yuvFrame->data[1], 1, yuvFrame->width * yuvFrame->height/4, fp);//fwrite(yuvFrame->data[2], 1, yuvFrame->width * yuvFrame->height / 4, fp);}}}fclose(fp);av_frame_unref(yuvFrame);av_packet_free(&avPacket);av_frame_unref(frame);
}

3.SDL2进行yuv绘制到屏幕

3.1 核心api

  • SDL_Init
  • SDL_CreateWindow
  • SDL_CreateRenderer
  • SDL_CreateTexture
  • SDL_UpdateTexture
  • SDL_RenderCopy
  • SDL_RenderPresent
  • SDL_Delay:控制帧率
    在这里插入图片描述
int sdl_display()
{if (SDL_Init(SDL_INIT_VIDEO)) {printf("sdl init failed\n");return -1;}SDL_Window* window = SDL_CreateWindow("sdl_demo", 200, 200, codecContext->width, codecContext->height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {SDL_Quit();return -1;}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);if (!renderer){SDL_DestroyWindow(window);SDL_Quit();return -1;}SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);SDL_RenderClear(renderer);Uint32 pixformat = SDL_PIXELFORMAT_IYUV;SDL_Texture* sdlTexture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height);//FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data", "rb+");while (1) {//int ret = fread(yuvBuf, 1, yuvlen, fp);//if (ret <= 0) {//	break;//}std::unique_lock<std::mutex>lck(mtx);if (condvar.wait_for(lck, std::chrono::seconds(1), [] {return isFinished;})){isFinished = false;SDL_UpdateTexture(sdlTexture, NULL, yuvBuf, codecContext->width);SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);SDL_RenderPresent(renderer);//控制帧率25fpsSDL_Delay(40);}else {printf("sdl thread exit!\n");break;}}SDL_Quit();return 0;
}

4.完整代码

-使用两个线程,生产者消费者模型

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdexcept>
#include <iostream>
#include <string>
#include <thread>
#include <fstream>#include <mutex>
#include <condition_variable>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>#include <SDL.h>
}
#undef main
#pragma warning(disable:4996)
AVFormatContext* formatContext = nullptr;
AVCodecContext* codecContext = nullptr;
SwsContext* swsContext = nullptr;
int videoStreamIndex = -1;
AVFrame* yuvFrame;
unsigned char* yuvBuf;
bool isReady = false;
bool isFinished = false;std::mutex mtx;
std::condition_variable condvar;
void readFrame()
{AVPacket* avPacket = av_packet_alloc();AVFrame* frame = av_frame_alloc();FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data","wb+") ;while (av_read_frame(formatContext, avPacket) >= 0 && fp){if (avPacket->stream_index == videoStreamIndex){if (avcodec_send_packet(codecContext, avPacket) < 0) {std::cerr << "发送数据包到解码器失败" << std::endl;break;}/*解码*/int ret = avcodec_receive_frame(codecContext, frame);printf("ret:%d\n", ret);if (ret >= 0){ret = sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, yuvFrame->data, yuvFrame->linesize);printf("sws_scale ret=%d\n", ret);std::lock_guard<std::mutex>lck(mtx);isFinished = false;memcpy(yuvBuf, yuvFrame->data[0], yuvFrame->width * yuvFrame->height);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height, yuvFrame->data[1], yuvFrame->width * yuvFrame->height / 4);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height*5/4, yuvFrame->data[2], yuvFrame->width * yuvFrame->height / 4);isFinished = true;condvar.notify_one();//保存y分量//fwrite(yuvFrame->data[0], 1, yuvFrame->width * yuvFrame->height, fp);//保存uv分量//fwrite(yuvFrame->data[1], 1, yuvFrame->width * yuvFrame->height/4, fp);//fwrite(yuvFrame->data[2], 1, yuvFrame->width * yuvFrame->height / 4, fp);}}}fclose(fp);av_frame_unref(yuvFrame);av_packet_free(&avPacket);av_frame_unref(frame);
}int sdl_display()
{if (SDL_Init(SDL_INIT_VIDEO)) {printf("sdl init failed\n");return -1;}SDL_Window* window = SDL_CreateWindow("sdl_demo", 200, 200, codecContext->width, codecContext->height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {SDL_Quit();return -1;}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);if (!renderer){SDL_DestroyWindow(window);SDL_Quit();return -1;}SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);SDL_RenderClear(renderer);Uint32 pixformat = SDL_PIXELFORMAT_IYUV;SDL_Texture* sdlTexture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height);//FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data", "rb+");while (1) {//int ret = fread(yuvBuf, 1, yuvlen, fp);//if (ret <= 0) {//	break;//}std::unique_lock<std::mutex>lck(mtx);if (condvar.wait_for(lck, std::chrono::seconds(1), [] {return isFinished;})){isFinished = false;SDL_UpdateTexture(sdlTexture, NULL, yuvBuf, codecContext->width);SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);SDL_RenderPresent(renderer);//控制帧率25fpsSDL_Delay(40);}else {printf("sdl thread exit!\n");break;}}SDL_Quit();return 0;
}/*ffmpeg -i input.mp4 -c:v rawvideo -pix_fmt yuv420p output.yuv*/
int main(int argc, char* argv[])
{/*if (argc != 2) {std::cerr << "文件名未指定" << std::endl;return -1;}*/std::string filename = "F:/VS_Project/ffmpeg_demo/1.mkv";if (avformat_open_input(&formatContext, filename.c_str(), nullptr, nullptr) != 0){std::cerr << "无法打开文件" << std::endl;return -1;}if (avformat_find_stream_info(formatContext, nullptr) < 0) {std::cerr << "无法找到视频流" << std::endl;return -1;}for (int i = 0; i < formatContext->nb_streams; i++){enum AVMediaType type = AVMEDIA_TYPE_VIDEO;AVStream* st = formatContext->streams[i];AVCodecParameters* codecpar = st->codecpar;if (codecpar->codec_type == type){videoStreamIndex = i;const AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);codecContext = avcodec_alloc_context3(codec);avcodec_parameters_to_context(codecContext, codecpar);avcodec_open2(codecContext, codec, nullptr);swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,codecContext->width, codecContext->height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, nullptr, nullptr, nullptr);std::cout << "w:" << codecpar->width << std::endl;std::cout << "h:" << codecpar->height << std::endl;}}yuvFrame = av_frame_alloc();yuvFrame->width = codecContext->width;yuvFrame->height = codecContext->height;yuvFrame->format = AV_PIX_FMT_YUV420P;int yuvlen = codecContext->width * codecContext->height * 3 / 2;yuvBuf = new unsigned char[yuvlen];int ret = av_frame_get_buffer(yuvFrame, 0);if (ret < 0) {printf("分配缓冲区失败\n");return -1;}//sdl_init();std::thread th1(readFrame);std::thread th2(sdl_display);th1.join();th2.join();delete[]yuvBuf;return 0;
}

5.效果展示

在这里插入图片描述

6.SDL2事件响应补充

6.1 处理方式-01

起一个refresh_video线程,用于产生一个自定义更新video的事件:REFRESH_EVENT,并且每40ms更新一次。while循环中持续等待事件的到来:SDL_WaitEvent,当收到事件后更新一帧画面。同时当监测到窗口改变的事件,SDL_GetWindowSize对窗口进行调整。

int refresh_video(void *opaque){thread_exit=0;while (thread_exit==0) {SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);SDL_Delay(40);}thread_exit=0;//BreakSDL_Event event;event.type = BREAK_EVENT;SDL_PushEvent(&event);return 0;
}
SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);SDL_Event event;while(1){//WaitSDL_WaitEvent(&event);if(event.type==REFRESH_EVENT){if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){// Loopfseek(fp, 0, SEEK_SET);fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);}SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);  //FIX: If window is resizesdlRect.x = 0;  sdlRect.y = 0;  sdlRect.w = screen_w;  sdlRect.h = screen_h;  SDL_RenderClear( sdlRenderer );   SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  SDL_RenderPresent( sdlRenderer );  }else if(event.type==SDL_WINDOWEVENT){//If ResizeSDL_GetWindowSize(screen,&screen_w,&screen_h);}else if(event.type==SDL_QUIT){thread_exit=1;}else if(event.type==BREAK_EVENT){break;}}SDL_Quit();

6.2 处理方式-02

起一个事件循环线程,SDL_PeepEvents 从事件队列中取出一个事件,然后更新事件队列,并进行绘制操作。

在这里插入图片描述在这里插入图片描述

//播放控制循环
void VideoCtl::LoopThread(VideoState *cur_stream)
{SDL_Event event;double incr, pos, frac;m_bPlayLoop = true;while (m_bPlayLoop){double x;refresh_loop_wait_event(cur_stream, &event);switch (event.type) {case SDL_KEYDOWN:switch (event.key.keysym.sym) {case SDLK_s: // S: Step to next framestep_to_next_frame(cur_stream);break;case SDLK_a:stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);break;case SDLK_v:stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);break;case SDLK_c:stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);break;case SDLK_t:stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);break;default:break;}break;case SDL_WINDOWEVENT://窗口大小改变事件qDebug()<<"SDL_WINDOWEVENT  "<<endl;switch (event.window.event) {case SDL_WINDOWEVENT_RESIZED:screen_width = cur_stream->width = event.window.data1;screen_height = cur_stream->height = event.window.data2;case SDL_WINDOWEVENT_EXPOSED:cur_stream->force_refresh = 1;}break;case SDL_QUIT:case FF_QUIT_EVENT:do_exit(cur_stream);break;default:break;}}do_exit(m_CurStream);//m_CurStream = nullptr;}

相关文章:

SDL2绘制ffmpeg解析的mp4文件

文章目录 1.FFMPEG利用命令行将mp4转yuv4202.ffmpeg将mp4解析为yuv数据2.1 核心api: 3.SDL2进行yuv绘制到屏幕3.1 核心api 4.完整代码5.效果展示6.SDL2事件响应补充6.1 处理方式-016.2 处理方式-02 本项目采用生产者消费者模型&#xff0c;生产者线程&#xff1a;使用ffmpeg将m…...

决策树C4.5算法的技术深度剖析、实战解读

目录 一、简介决策树&#xff08;Decision Tree&#xff09;例子&#xff1a; 信息熵&#xff08;Information Entropy&#xff09;与信息增益&#xff08;Information Gain&#xff09;例子&#xff1a; 信息增益比&#xff08;Gain Ratio&#xff09;例子&#xff1a; 二、算…...

LLMs Python解释器程序辅助语言模型(PAL)Program-aided language models (PAL)

正如您在本课程早期看到的&#xff0c;LLM执行算术和其他数学运算的能力是有限的。虽然您可以尝试使用链式思维提示来克服这一问题&#xff0c;但它只能帮助您走得更远。即使模型正确地通过了问题的推理&#xff0c;对于较大的数字或复杂的运算&#xff0c;它仍可能在个别数学操…...

【12】c++设计模式——>单例模式练习(任务队列)

属性&#xff1a; &#xff08;1&#xff09;存储任务的容器&#xff0c;这个容器可以选择使用STL中的队列&#xff08;queue) &#xff08;2&#xff09;互斥锁&#xff0c;多线程访问的时候用于保护任务队列中的数据 方法&#xff1a;主要是对任务队列中的任务进行操作 &…...

Python之函数、模块、包库

函数、模块、包库基础概念和作用 A、函数 减少代码重复 将复杂问题代码分解成简单模块 提高代码可读性 复用老代码 """ 函数 """# 定义一个函数 def my_fuvtion():# 函数执行部分print(这是一个函数)# 定义带有参数的函数 def say_hello(n…...

SQL创建与删除索引

索引创建、删除与使用&#xff1a; 1.1 create方式创建索引&#xff1a;CREATE [UNIQUE – 唯一索引 | FULLTEXT – 全文索引 ] INDEX index_name ON table_name – 不指定唯一或全文时默认普通索引 (column1[(length) [DESC|ASC]] [,column2,…]) – 可以对多列建立组合索引 …...

网络协议--链路层

2.1 引言 从图1-4中可以看出&#xff0c;在TCP/IP协议族中&#xff0c;链路层主要有三个目的&#xff1a; &#xff08;1&#xff09;为IP模块发送和接收IP数据报&#xff1b; &#xff08;2&#xff09;为ARP模块发送ARP请求和接收ARP应答&#xff1b; &#xff08;3&#xf…...

HDLbits: Count clock

目前写过最长的verilog代码&#xff0c;用了将近三个小时&#xff0c;编写12h显示的时钟&#xff0c;改来改去&#xff0c;估计只有我自己看得懂&#xff08;吐血&#xff09; module top_module(input clk,input reset,input ena,output pm,output [7:0] hh,output [7:0] mm,…...

【1day】用友移动管理系统任意文件上传漏洞学习

注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现...

【c++】向webrtc学习容器操作

std::map的key为std::pair 时的查找 std::map<RemoteAndLocalNetworkId, size_t> in_flight_bytes_RTC_GUARDED_BY(&lock_);private:using RemoteAndLocalNetworkId = std::pair<uint16_t, uint16_t...

SpringBoot+Vue3外卖项目构思

SpringBoot的学习&#xff1a; SpringBoot的学习_明里灰的博客-CSDN博客 实现功能 前台 用户注册&#xff0c;邮箱登录&#xff0c;地址管理&#xff0c;历史订单&#xff0c;菜品规格&#xff0c;购物车&#xff0c;下单&#xff0c;菜品浏览&#xff0c;评价&#xff0c;…...

【AI视野·今日NLP 自然语言处理论文速览 第四十七期】Wed, 4 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 4 Oct 2023 Totally 73 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Contrastive Post-training Large Language Models on Data Curriculum Authors Canwen Xu, Corby Rosset, Luc…...

c++的lambda表达式

文章目录 1 lambda表达式2 捕捉列表 vs 参数列表3 lambda表达式的传递3.1 函数作为形参3.2 场景1&#xff1a;条件表达式3.3 场景2&#xff1a;线程的运行表达式 1 lambda表达式 lambda表达式可以理解为匿名函数&#xff0c;也就是没有名字的函数&#xff0c;既然是函数&#…...

电梯安全监测丨S271W无线水浸传感器用于电梯机房/电梯基坑水浸监测

城市化进程中&#xff0c;电梯与我们的生活息息相关。高层住宅、医院、商场、学校、车站等各种商业体建筑、公共建筑中电梯为我们生活工作提供了诸多便利。 保障电梯系统的安全至关重要&#xff01;特别是电梯机房和电梯基坑可通过智能化改造提高其安全性和稳定性。例如在暴风…...

Java异常:基本概念、分类和处理

Java异常&#xff1a;基本概念、分类和处理 在Java编程中&#xff0c;异常处理是一个非常重要的部分。了解如何识别、处理和避免异常对于编写健壮、可维护的代码至关重要。本文将介绍Java异常的基本概念、分类和处理方法&#xff0c;并通过简单的代码示例进行说明。 一、什么…...

小谈设计模式(19)—备忘录模式

小谈设计模式&#xff08;19&#xff09;—备忘录模式 专栏介绍专栏地址专栏介绍 备忘录模式主要角色发起人&#xff08;Originator&#xff09;备忘录&#xff08;Memento&#xff09;管理者&#xff08;Caretaker&#xff09; 应用场景结构实现步骤Java程序实现首先&#xff…...

《数据库系统概论》王珊版课后习题

第一章 绪论 1.数据、数据库、数据库管理系统、数据库系统的概念 &#xff08;1&#xff09;数据&#xff08;Data&#xff09;&#xff1a;数据是数据库中存储的基本对象&#xff0c;是描述事物的符号记录。数据有多种表现形式&#xff0c;它们都可以经过数字化后存入计算机…...

MariaDB 修改用户远程登录

今天修改MariaDB数据库用户的Host时出现错误&#xff1a; ERROR 1356 (HY000): View ‘mysql.user’ references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them 我的步骤如下&#xff1a; 1.登陆 2.use mysql; 3.执行…...

Elasticsearch使用mapping映射定义以及基本的数据类型

1、说明 Elasticsearch的映射相当于数据库的数据字典&#xff0c;它定义了每个字段的名称和能够保存的数据类型&#xff0c;并且内置了20多种字段类型用于支持多种多样的结构化数据&#xff0c;这里仅介绍几种常用的字段类型&#xff0c;如需要了解全部的类型&#xff0c;请参…...

【unity】制作一个角色的初始状态(左右跳二段跳)【2D横板动作游戏】

前言 hi~ 大家好&#xff01;欢迎大家来到我的全新unity学习记录系列。现在我想在2d横板游戏中&#xff0c;实现一个角色的初始状态-闲置状态、移动状态、空中状态。并且是利用状态机进行实现的。 本系列是跟着视频教程走的&#xff0c;所写也是作者个人的学习记录笔记。如有错…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解

一、前言 在HarmonyOS 5的应用开发模型中&#xff0c;featureAbility是旧版FA模型&#xff08;Feature Ability&#xff09;的用法&#xff0c;Stage模型已采用全新的应用架构&#xff0c;推荐使用组件化的上下文获取方式&#xff0c;而非依赖featureAbility。 FA大概是API7之…...

Linux基础开发工具——vim工具

文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...

2025-05-08-deepseek本地化部署

title: 2025-05-08-deepseek 本地化部署 tags: 深度学习 程序开发 2025-05-08-deepseek 本地化部署 参考博客 本地部署 DeepSeek&#xff1a;小白也能轻松搞定&#xff01; 如何给本地部署的 DeepSeek 投喂数据&#xff0c;让他更懂你 [实验目的]&#xff1a;理解系统架构与原…...

python打卡第47天

昨天代码中注意力热图的部分顺移至今天 知识点回顾&#xff1a; 热力图 作业&#xff1a;对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图&#xff0c;展示模…...