最简单的基于 FFmpeg 的内存读写的例子:内存转码器

最简单的基于 FFmpeg 的内存读写的例子:内存转码器

  • 最简单的基于 FFmpeg 的内存读写的例子:内存转码器
    • 正文
    • 源程序
    • 结果
    • 工程文件下载
    • 参考链接

最简单的基于 FFmpeg 的内存读写的例子:内存转码器

参考雷霄骅博士的文章,链接:最简单的基于FFmpeg的内存读写的例子:内存播放器

正文

之前的所有有关 FFmpeg 的例子都是对文件进行操作的。实际上,并不是所有视频的编码,解码都是针对文件进行处理的。有的时候需要的解码的视频数据在一段内存中。例如,通过其他系统送来的视频数据。同样,有的时候编码后的视频数据也未必要保存成一个文件。例如,要求将编码后的视频数据送给其他的系统进行下一步的处理。以上两种情况就要求 FFmpeg 不仅仅是对文件进行“读,写”操作,而是要对内存进行“读,写”操作。因此打算记录的两个例子就是使用 FFmpeg 对内存进行读写的例子。

这篇文章记录一个基于 FFmpeg 的内存转码器。该转码器可以使用 FFmpeg 读取内存中的数据,转码为 H.264 之后再将数据输出到内存。

关于如何从内存中读取数据在这里不再详述,可以参考文章:《ffmpeg 从内存中读取数据(或将数据输出到内存)》。

FFmpeg 读写内存的关键点就两个:

第一,初始化自定义的 AVIOContext,指定自定义的回调函数。示例代码如下:

//AVIOContext中的缓存
unsigned char *aviobuffer=(unsigned char*)av_malloc(32768);
AVIOContext *avio=avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);
pFormatCtx->pb=avio;if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){printf("Couldn't open inputstream.(无法打开输入流)\n");return -1;
}

上述代码中,自定义了回调函数 read_buffer()。在使用 avformat_open_input() 打开媒体数据的时候,就可以不指定文件的 URL 了,即其第 2 个参数为 NULL(因为数据不是靠文件读取,而是由 read_buffer() 提供)。

第二,自己写回调函数。注意函数的参数和返回值(尤其是返回值)。示例代码如下:

//Callback
int read_buffer(void *opaque, uint8_t *buf, int buf_size){if(!feof(fp_open)){inttrue_size=fread(buf,1,buf_size,fp_open);return true_size;}else{return -1;}
}

当系统需要数据的时候,会自动调用该回调函数以获取数据。这个例子为了简单,直接使用 fread() 读取数据至内存。回调函数需要格外注意它的参数和返回值。

转码实际上就是解码和编码的结合。该方面的知识可以参考文章:

  1. 解码:100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
  2. 编码:最简单的基于FFMPEG的视频编码器(YUV编码为H.264)
  3. 转码: 最简单的基于FFMPEG的转码程序

程序的流程图如下图所示。从图中可以看出,首先分别初始化了输入和输出的 AVFormatContext。然后首先解码输入的 AVPacket,得到存储像素数据(YUV420P 格式)的 AVFrame,然后编码 AVFrame 为 H.264 的 AVPacket,最后将编码后的 AVPacket 输出。

在这里插入图片描述

源程序

/**
* 最简单的基于FFmpeg的内存读写例子(内存转码器)
* Simplest FFmpeg mem Transcoder
*
* 源程序:
* 雷霄骅,张晖
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序实现了任意格式视频数据(例如 MPEG2)转码为 H.264 码流数据。
* 本程序并不是对文件进行处理,而是对内存中的视频数据进行处理。
* 它从内存读取数据,并且将转码后的数据输出到内存中。
* 是最简单的使用 FFmpeg 读写内存的例子。
*
* This software convert video bitstream (Such as MPEG2) to H.264
* bitstream. It read video bitstream from memory (not from a file),
* convert it to H.264 bitstream, and finally output to another memory.
* It's the simplest example to use FFmpeg to read (or write) from
* memory.
*
*/#include "stdafx.h"#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
// Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#ifdef __cplusplus
};
#endif
#endifFILE *fp_open;
FILE *fp_write;// Read File
int read_buffer(void *opaque, uint8_t *buf, int buf_size)
{if (!feof(fp_open)){int true_size = fread(buf, 1, buf_size, fp_open);return true_size;}else{return -1;}
}// Write File
int write_buffer(void *opaque, uint8_t *buf, int buf_size)
{if (!feof(fp_write)){int true_size = fwrite(buf, 1, buf_size, fp_write);return true_size;}else{return -1;}
}int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY))return 0;while (1){av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index);//ret = encode_write_frame(NULL, stream_index, &got_frame);enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame);av_frame_free(NULL);if (ret < 0)break;if (!got_frame){ret = 0;break;}// prepare packet for muxingenc_pkt.stream_index = stream_index;enc_pkt.dts = av_rescale_q_rnd(enc_pkt.dts,fmt_ctx->streams[stream_index]->codec->time_base,fmt_ctx->streams[stream_index]->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.pts = av_rescale_q_rnd(enc_pkt.pts,fmt_ctx->streams[stream_index]->codec->time_base,fmt_ctx->streams[stream_index]->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.duration = av_rescale_q(enc_pkt.duration,fmt_ctx->streams[stream_index]->codec->time_base,fmt_ctx->streams[stream_index]->time_base);av_log(NULL, AV_LOG_DEBUG, "Muxing frame.\n");// mux encoded frameret = av_write_frame(fmt_ctx, &enc_pkt);if (ret < 0)break;}return ret;
}int main(int argc, char* argv[])
{int ret;AVFormatContext *ifmt_ctx = NULL;AVFormatContext *ofmt_ctx = NULL;AVPacket packet, enc_pkt;AVFrame *frame = NULL;enum AVMediaType type;unsigned int stream_index;unsigned int i = 0;int got_frame, enc_got_frame;AVStream *out_stream;AVStream *in_stream;AVCodecContext *dec_ctx, *enc_ctx;AVCodec *encoder;fp_open = fopen("cuc60anniversary_start.ts", "rb");	// 视频源文件 fp_write = fopen("cuc60anniversary_start.h264", "wb+"); // 输出文件av_register_all();ifmt_ctx = avformat_alloc_context();avformat_alloc_output_context2(&ofmt_ctx, NULL, "h264", NULL);unsigned char* inbuffer = NULL;unsigned char* outbuffer = NULL;inbuffer = (unsigned char*)av_malloc(32768);outbuffer = (unsigned char*)av_malloc(32768);AVIOContext *avio_in = NULL;AVIOContext *avio_out = NULL;// open input fileavio_in = avio_alloc_context(inbuffer, 32768, 0, NULL, read_buffer, NULL, NULL);if (avio_in == NULL)goto end;ifmt_ctx->pb = avio_in;ifmt_ctx->flags = AVFMT_FLAG_CUSTOM_IO;if ((ret = avformat_open_input(&ifmt_ctx, "whatever", NULL, NULL)) < 0){av_log(NULL, AV_LOG_ERROR, "Cannot open input file.\n");return ret;}if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0){av_log(NULL, AV_LOG_ERROR, "Cannot find stream information.\n");return ret;}for (i = 0; i < ifmt_ctx->nb_streams; i++){AVStream *stream;AVCodecContext *codec_ctx;stream = ifmt_ctx->streams[i];codec_ctx = stream->codec;// Reencode video & audio and remux subtitles etcif (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO){// Open decoderret = avcodec_open2(codec_ctx,avcodec_find_decoder(codec_ctx->codec_id), NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u.\n", i);return ret;}}}// Print some input information//av_dump_format(ifmt_ctx, 0, "whatever", 0);// open output fileavio_out = avio_alloc_context(outbuffer, 32768, 1, NULL, NULL, write_buffer, NULL);if (avio_out == NULL)goto end;//avio_out->write_packet=write_packet;ofmt_ctx->pb = avio_out;ofmt_ctx->flags = AVFMT_FLAG_CUSTOM_IO;for (i = 0; i < 1; i++){out_stream = avformat_new_stream(ofmt_ctx, NULL);if (!out_stream){av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream.\n");return AVERROR_UNKNOWN;}in_stream = ifmt_ctx->streams[i];dec_ctx = in_stream->codec;enc_ctx = out_stream->codec;if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO){encoder = avcodec_find_encoder(AV_CODEC_ID_H264);enc_ctx->height = dec_ctx->height;enc_ctx->width = dec_ctx->width;enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;enc_ctx->pix_fmt = encoder->pix_fmts[0];enc_ctx->time_base = dec_ctx->time_base;//enc_ctx->time_base.num = 1;//enc_ctx->time_base.den = 25;// H.264的必备选项,没有就会错enc_ctx->me_range = 16;enc_ctx->max_qdiff = 4;enc_ctx->qmin = 10;enc_ctx->qmax = 51;enc_ctx->qcompress = 0.6;enc_ctx->refs = 3;enc_ctx->bit_rate = 500000;ret = avcodec_open2(enc_ctx, encoder, NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u.\n", i);return ret;}}else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN){av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed.\n", i);return AVERROR_INVALIDDATA;}else{// if this stream must be remuxedret = avcodec_copy_context(ofmt_ctx->streams[i]->codec,ifmt_ctx->streams[i]->codec);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Copying stream context failed.\n");return ret;}}if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;}// Print some output information//av_dump_format(ofmt_ctx, 0, "whatever", 1);// init muxer, write output file headerret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file.\n");return ret;}i = 0;// read all packetswhile (1){i++;if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0)break;stream_index = packet.stream_index;if (stream_index != 0)continue;type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type;av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u.\n",stream_index);av_log(NULL, AV_LOG_DEBUG, "Going to reencode the frame.\n");frame = av_frame_alloc();if (!frame){ret = AVERROR(ENOMEM);break;}// 解压缩时间戳packet.dts = av_rescale_q_rnd(packet.dts,ifmt_ctx->streams[stream_index]->time_base,ifmt_ctx->streams[stream_index]->codec->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));// 显示时间戳packet.pts = av_rescale_q_rnd(packet.pts,ifmt_ctx->streams[stream_index]->time_base,ifmt_ctx->streams[stream_index]->codec->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));// 解码输入文件ret = avcodec_decode_video2(ifmt_ctx->streams[stream_index]->codec, frame,&got_frame, &packet);printf("Decode 1 Packet\tsize:%d\tpts:%lld.\n", packet.size, packet.pts);if (ret < 0){av_frame_free(&frame);av_log(NULL, AV_LOG_ERROR, "Decoding failed.\n");break;}if (got_frame){frame->pts = av_frame_get_best_effort_timestamp(frame);frame->pict_type = AV_PICTURE_TYPE_NONE;enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = avcodec_encode_video2(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,frame, &enc_got_frame);printf("Encode 1 Packet\tsize:%d\tpts:%lld.\n", enc_pkt.size, enc_pkt.pts);av_frame_free(&frame);if (ret < 0)goto end;if (!enc_got_frame)continue;// prepare packet for muxingenc_pkt.stream_index = stream_index;enc_pkt.dts = av_rescale_q_rnd(enc_pkt.dts,ofmt_ctx->streams[stream_index]->codec->time_base,ofmt_ctx->streams[stream_index]->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.pts = av_rescale_q_rnd(enc_pkt.pts,ofmt_ctx->streams[stream_index]->codec->time_base,ofmt_ctx->streams[stream_index]->time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.duration = av_rescale_q(enc_pkt.duration,ofmt_ctx->streams[stream_index]->codec->time_base,ofmt_ctx->streams[stream_index]->time_base);av_log(NULL, AV_LOG_INFO, "Muxing frame %d.\n", i);// mux encoded frameav_write_frame(ofmt_ctx, &enc_pkt);if (ret < 0)goto end;}else{av_frame_free(&frame);}av_free_packet(&packet);}// flush encodersfor (i = 0; i < 1; i++){// flush encoderret = flush_encoder(ofmt_ctx, i);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed.\n");goto end;}}// Write file trailerav_write_trailer(ofmt_ctx);end:// avio_in 释放之前需要先释放 avio_in->buffer, 否则会出现内存泄漏av_free(avio_in->buffer);av_freep(&avio_in);av_free(avio_out->buffer);av_freep(&avio_out);av_free(inbuffer);av_free(outbuffer);av_free_packet(&packet);av_frame_free(&frame);avformat_close_input(&ifmt_ctx);avformat_free_context(ofmt_ctx);//fcloseall();fclose(fp_open);fclose(fp_write);if (ret < 0)av_log(NULL, AV_LOG_ERROR, "Error occurred.\n");return (ret ? 1 : 0);
}

结果

一开始会报错:

在这里插入图片描述

属性页将 SDL 检查设置为否:

在这里插入图片描述

程序的运行结果如下:

在这里插入图片描述

输入文件:

在这里插入图片描述

输出文件:

在这里插入图片描述

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Memory-Transcoder

CSDN:Simplest FFmpeg Memory Transcoder.zip

参考链接

  1. ffmpeg 从内存中读取数据(或将数据输出到内存)
  2. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
  3. 最简单的基于FFMPEG的视频编码器(YUV编码为H.264)
  4. 最简单的基于FFMPEG的转码程序

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

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

相关文章

Chrome中如何导出和导入书签

导出书签 如下图所示&#xff1a; 右上角三点->书签和清单->书签管理器->右上角三点->导出书签 然后你选择保存地址即可。打开后如下&#xff1a; 导入书签 如下图所示&#xff1a; 右上角三点->书签和清单->导入书签和设置->选择以前导出的书签&…

【Node.js】-闲聊:前端框架发展史

前端框架的发展史是一个不断演进和创新的过程&#xff0c;旨在提高开发效率、优化用户体验&#xff0c;并推动前端技术的不断发展。以下是前端框架发展的主要阶段和关键里程碑&#xff1a; 早期阶段&#xff1a; 在这个阶段&#xff0c;前端主要由HTML、CSS和JavaScript等基础技…

ceph 换盘扩容

调整时间 基础设施调整操作&#xff1a;工作日0点之后操作&#xff0c;或者非工作日 基础设施包括网络、主机系统、存储 / 备份系统、安全系统、以及机房动力环境等 调整规范 变更管理实现所有基础设施和应用系统的变更&#xff0c;变更管理应记录并对所有要求的变更进行分…

LLM Drift(漂移), Prompt Drift Cascading(级联)

原文地址&#xff1a;LLM Drift, Prompt Drift & Cascading 提示链接可以手动或自动执行&#xff1b;手动需要通过 GUI 链构建工具手工制作链。自治代理在执行时利用可用的工具动态创建链。这两种方法都容易受到级联、LLM 和即时漂移的影响。 2024 年 2 月 23 日 在讨论大型…

什么是自动化测试?什么情况下使用?

什么是自动化测试? 自动化测试是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架&#xff0c;编写自动化测试脚本&#xff0c;来模拟手工测试过程。比如说&#xff0c;在项目迭代过程中&#xff0c;持续的回归测试是一项非常枯燥…

如何在Mapbox GL中处理大的GEOJSON文件

Mapbox GL可以将 GeoJSON 数据由客户端(Web 浏览器或移动设备)即时转换为 Mapbox 矢量切片进行显示和处理。本文的目的是教大家如何有效加载和渲染大型 GeoJSON 源,并优化渲染显示速度,增强用户体验,减少客户端卡顿问题。本文以Mapbox 为例,至于其它框架原理大致相同,可…

【HarmonyOS】ArkTS-枚举类型

枚举类型 枚举类型是一种特殊的数据类型&#xff0c;约定变量只能在一组数据范围内选择值 定义枚举类型 定义枚举类型&#xff08;常量列表&#xff09; enum 枚举名 { 常量1 值, 常量2 值,......}enum ThemeColor {Red #ff0f29,Orange #ff7100,Green #30b30e}使用枚举…

HTML世界之标签Ⅲ

一、dfn 标签 <dfn> 标签是一个短语标签&#xff0c;用来定义一个定义项目。 写法&#xff1a; <dfn></dfn> 二、dialog 标签 <dialog> 标签定义一个对话框、确认框或窗口。 属性 值 描述 open open 规定 dialog 元素是有效的&#xff0c;用户…

为什么接口测试工具不跨域

浏览器实施了同源策略&#xff0c;限制了在不同域之间的资源共享。这是出于安全考虑&#xff0c;以防止恶意网站获取用户的敏感信息。同源策略要求发送请求的源&#xff08;协议、域名和端口&#xff09;必须与接收响应的源相同。如果源不同&#xff0c;则浏览器会拒绝该请求&a…

报错Importing ArkTS files to JS and TS files is not allowed. <etsLint>

ts文件并不支持导入ets文件&#xff0c;为了方便开发应用卡片&#xff0c;entryformAbility创建的时候默认是ts文件&#xff0c;这里只需要把ts文件改成ets便可以轻松的导入所需要的ets即可 我创建了一个鸿蒙开发的交流群&#xff0c;喜欢的鸿蒙朋友可以扫码或者写群号&#xf…

微服务自动化管理初步认识与使用

目录 一、ETCD 1.1、ETCD简介 对于实施工程师&#xff1a; 1.2、特点 1.3. 使用场景 1.4、 关键字 1.5 工作原理 二、ETCD的安装 2.1、下载路径 2.2、介绍 2.3、具体操作 安装服务端 安装etcd客户端 测试 三、ETCD使用 3.1、前奏具体操作 3.2、 常用操作 一、ET…

【NERF】入门学习整理(一)

【NERF】入门学习整理 1. 【NERF】入门学习整理1.1 基础含义输入输出2.位置编码含义3.代码中实际网路结构4.Volume Render部分(64个采样点处理)5.Volume Render部分(64个采样点处理)【NERF】及其变种(二) 1. 【NERF】入门学习整理 1.1 基础含义输入输出 深度学习模型中…

ROS——Ubuntu环境搭建

Ubuntu安装 首先下载 Ubuntu 的镜像文件&#xff0c;链接如下:ubuntu-releases-20.04安装包下载_开源镜像站-阿里云ubuntu-releases-20.04安装包是阿里云官方提供的开源镜像免费下载服务&#xff0c;每天下载量过亿&#xff0c;阿里巴巴开源镜像站为包含ubuntu-releases-20.04…

【Android 内存优化】KOOM 快手开源框架线上内存监控方案-源码剖析

文章目录 前言OOMMonitorInitTask.INSTANCE.initOOMMonitor.INSTANCE.startLoopsuper.startLoopcall() LoopState.Terminate dumpAndAnalysisdumpstartAnalysisService回到startLoop方法总结 前言 这篇文章主要剖析KOOM的Java层源码设计逻辑。 使用篇请看上一篇: 【Android …

使用阿里云服务器搭建网站简单吗?超简单教程

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网aliyunfuwuqi.com以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流…

Pytorch学习 day08(最大池化层、非线性激活层、正则化层、循环层、Transformer层、线性层、Dropout层)

最大池化层 最大池化&#xff0c;也叫上采样&#xff0c;是池化核在输入图像上不断移动&#xff0c;并取对应区域中的最大值&#xff0c;目的是&#xff1a;在保留输入特征的同时&#xff0c;减小输入数据量&#xff0c;加快训练。参数设置如下&#xff1a; kernel_size&#…

Linux网络基础2之协议

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 1.协议 1.序列化与反序列换 2.协议定制 二…

JavaCV 进行视频操作

1、JavaCV实现将视频以帧方式抽取 ## JavaCV实现将视频以帧方式抽取java import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Java2DFrameConverter;import javax.imageio.ImageIO; import java.awt.image.Buffer…

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记10 - STM32的SDIO学习2

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记10 - STM32的SDIO学习2 一、问题回顾二、本次的任务三、 需要注意的问题3.1 Card Identification Mode时的时钟频率3.2 CMD0指令的疑似问题3.3 发送带参数的ACMD41时要注意时间时序和时效3.4 CPSM的指令发送问题3.5 调试过程中的SD卡的…

linuxOPS基础_linux系统注意事项

Linux严格区分大小写 Linux 和Windows不同&#xff0c;Linux严格区分大小写的&#xff0c;包括文件名和目录名、命令、命令选项、配置文件设置选项等。 例如&#xff0c;Win7 系统桌面上有文件夹叫做Test&#xff0c;当我们在桌面上再新建一个名为 test 的文件夹时&#xff0c…