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

Android MediaCodec将h264实时视频流数据解码为yuv,并转换yuv的颜色格式为nv21

初始化mediacodec

    private MediaCodec mediaCodec;private ByteBuffer[] inputBuffers;private void initMediaCodec(Surface surface) {try {Log.d(TAG, "onGetNetVideoData: ");//创建解码器 H264的Type为  AACmediaCodec = MediaCodec.createDecoderByType("video/avc");//创建配置MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", Width, Height);//设置解码预期的帧速率【以帧/秒为单位的视频格式的帧速率的键】mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);//
//            byte[] headerSps = {0, 0, 0, 1, 103, 66, 0, 41, -115, -115, 64, 80, 30, -48, 15, 8, -124, 83, -128};
//            byte[] headerPps = {0, 0, 0, 1, 104, -54, 67, -56};
//
//            mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(headerSps));
//            mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(headerPps));//配置绑定mediaFormat和surfacemediaCodec.configure(mediaFormat, null, null, 0);mediaCodec.start();} catch (IOException e) {e.printStackTrace();//创建解码失败Log.e(TAG, "创建解码失败");}inputBuffers = mediaCodec.getInputBuffers();}

处理数据,解码h264数据为yuv格式

这里传入的是h264格式的实时视频流数据。

    private void onFrame(byte[] buf, int offset, int length) {MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();//查询10000毫秒后,如果dSP芯片的buffer全部被占用,返回-1;存在则大于0int inIndex = mediaCodec.dequeueInputBuffer(10000);if (inIndex >= 0) {//根据返回的index拿到可以用的bufferByteBuffer byteBuffer = inputBuffers[inIndex];//清空缓存byteBuffer.clear();//开始为buffer填充数据byteBuffer.put(buf);//填充数据后通知mediacodec查询inIndex索引的这个buffer,mediaCodec.queueInputBuffer(inIndex, 0, length, mCount * 20, 0);mCount++;} else {Log.i(TAG, "inIndex < 0");//等待查询空的bufferreturn;}//mediaCodec 查询 "mediaCodec的输出方队列"得到索引int outIndex = mediaCodec.dequeueOutputBuffer(info, 10000);Log.e(TAG, "解码输出outIndex " + outIndex);if (outIndex >= 0) {//dsp的byteBuffer无法直接使用ByteBuffer byteBuffer = mediaCodec.getOutputBuffer(outIndex);//设置偏移量byteBuffer.position(info.offset);byteBuffer.limit(info.size + info.offset);byte[] ba = new byte[byteBuffer.remaining()];byteBuffer.get(ba);//需要预先分配与NV12相同大小的字节数组byte[] yuv = new byte[ba.length];//不确定是什么颜色格式,挨个试的//convertI420ToNV21(ba, yuv, Width, Height);//convertYV12toNV21(ba, yuv, Width, Height);convertNV12toNV21(ba, yuv, Width, Height);NV21Data(yuv);//检查所支持的颜色格式
//            MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType("video/avc");
//             for (int i = 0; i < capabilities.colorFormats.length; i++) {
//                int format = capabilities.colorFormats[i];
//
//                //华为平板:COLOR_FormatYUV420SemiPlanar、COLOR_FormatYUV420Planar
//                //魅族手机:COLOR_FormatYUV420SemiPlanar
//                //rk3588s: COLOR_FormatYUV420Planar、COLOR_FormatYUV420Flexible、COLOR_FormatYUV420PackedSemiPlanar、COLOR_FormatYUV420SemiPlanar
//                switch (format) {
//                    case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar://(对应 I420 or YV12)
//                        Log.i("COLOR_Format_TAG", "=========COLOR_FormatYUV420Planar");
//                        byte[] convertNv21YUV420Planar = new byte[ba.length];
//                        //不确定是什么颜色格式,挨个试的convertI420ToNV21(ba, convertNv21YUV420Planar, Width, Height);convertYV12toNV21(ba, convertNv21YUV420Planar, Width, Height);
//                        long l1 = System.currentTimeMillis();
//                        convertNV12toNV21(ba, convertNv21YUV420Planar, Width, Height);
//                        Log.i("耗时测试", "转为nv21的耗时: " + (System.currentTimeMillis() - l1));
//                        long l2 = System.currentTimeMillis();
//                        NV21Data(convertNv21YUV420Planar);
//                        Log.i("耗时测试", "识别耗时: " + (System.currentTimeMillis() - l2));
//                        continue;
//
//                    case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar://NV12
//                        Log.i("COLOR_Format_TAG", "=======COLOR_FormatYUV420SemiPlanar");
//                        byte[] nv21YUV420SemiPlanar = new byte[ba.length];
//                        convertNV12toNV21(ba, nv21YUV420SemiPlanar, Width, Height);
//                        NV21Data(nv21YUV420SemiPlanar);
//
//                        continue;
//                    case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
//                        Log.i("COLOR_Format_TAG", "=======COLOR_FormatYUV420PackedSemiPlanar");
//                        byte[] nv21YUV420PackedSemiPlanar = new byte[ba.length];
//                        convertNV12toNV21(ba, nv21YUV420PackedSemiPlanar, Width, Height);
//                        NV21Data(nv21YUV420PackedSemiPlanar);
//                        continue;
//                    case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible:
//                        byte[] nv21YUV420YUV420Flexible = new byte[ba.length];
//                        convertNV12toNV21(ba, nv21YUV420YUV420Flexible, Width, Height);
//                        NV21Data(nv21YUV420YUV420Flexible);
//                        Log.i("COLOR_Format_TAG", "=======COLOR_FormatYUV420Flexible");
//                        continue;
//                    default:
//                        continue;
//
//                }
//
//            }//如果surface绑定了,则直接输入到surface渲染并释放mediaCodec.releaseOutputBuffer(outIndex, false);} else {Log.e(TAG, "没有解码成功");}}

处理获取到的nv21颜色格式的yuv数据

    private int printImageStatus = 0;private void NV21Data(byte[] nv21) {//将nv21视频流数据传入YuvImage中,转换成bitmap之后,显示在imageview上、//或者保存为png图片到本地,如果不出现灰色、不出现蓝色图像和红色图像颜色颠倒,//图像显示正常,则说明是标准的nv21格式视频流数据YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, Width, Height, null);ByteArrayOutputStream baos = new ByteArrayOutputStream();yuvImage.compressToJpeg(new Rect(0, 0, Width, Height), 100, baos);byte[] data = baos.toByteArray();Log.i(TAG, "NV21Data-data: " + data.length);Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);if (bitmap != null) {runOnUiThread(new Runnable() {@Overridepublic void run() {mIvShowImage.setImageBitmap(bitmap);}});//保存bitmap为png图片if (printImageStatus == 0) {printImageStatus = 1;try {File myCaptureFile = new File(Environment.getExternalStorageDirectory(), "img.png");BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);bos.flush();bos.close();} catch (Exception e) {e.printStackTrace();}}}}

 yuv视频数据颜色格式转换

    public static void convertI420ToNV21(byte[] i420, byte[] nv21, int width, int height) {System.arraycopy(i420, 0, nv21, 0, width * height);int offset = width * height;for (int i = 0; i < width * height / 4; i++) {nv21[offset + 2 * i] = i420[offset + i + width * height / 4];nv21[offset + 2 * i + 1] = i420[offset + i];}}public static void convertYV12toNV21(byte[] yv12, byte[] nv21, int width, int height) {int size = width * height;int vOffset = size;int uOffset = size + (size / 4);// Copy Y channel as it isSystem.arraycopy(yv12, 0, nv21, 0, size);for (int i = 0; i < size / 4; i++) {nv21[vOffset + (i * 2)] = yv12[vOffset + i];      // Vnv21[vOffset + (i * 2) + 1] = yv12[uOffset + i];  // U}}public static void convertNV12toNV21(byte[] nv12, byte[] nv21, int width, int height) {int size = width * height;int offset = size;// copy Y channel as it isSystem.arraycopy(nv12, 0, nv21, 0, offset);for (int i = 0; i < size / 4; i++) {nv21[offset + (i * 2) + 1] = nv12[offset + (i * 2)];       // Unv21[offset + (i * 2)] = nv12[offset + (i * 2) + 1];       // V}}

h264实时视频流的数据来源

    @Overridepublic void onPacketEvent(byte[] data) {onFrame(data, 0, data.length);//写入h264视频流到sdcard中//wirte2file(data, data.length);}

写入h264视频流到sdcard中

private String dsetfilePath = Environment.getExternalStorageDirectory() + "/" + "test.h264";private void wirte2file(byte[] buf, int length) {if (isStart) {if (BufOs == null) {destfile = new File(dsetfilePath);try {destfs = new FileOutputStream(destfile);BufOs = new BufferedOutputStream(destfs);Log.d(TAG, "wirte2file-new ");} catch (FileNotFoundException e) {// TODO: handle exceptionLog.i("TRACK", "initerro" + e.getMessage());Log.d(TAG, "wirte2file-FileNotFoundException:" + e.getMessage());e.printStackTrace();}}try {BufOs.write(buf, 0, length);BufOs.flush();Log.d(TAG, "wirte2file-write");} catch (Exception e) {Log.d(TAG, "wirte2file-e: " + e.getMessage());// TODO: handle exception}}}private boolean isStart;public void onStop(View view) {isStart = false;Toast.makeText(this, "停止保存", Toast.LENGTH_SHORT).show();}public void onStart(View view) {isStart = true;Toast.makeText(this, "开始保存", Toast.LENGTH_SHORT).show();}

rtsp获取h264实时视频流数据

public class FFDemuxJava {static {System.loadLibrary("demux");}private long m_handle = 0;private EventCallback mEventCallback = null;public void init(String url) {m_handle = native_Init(url);}public void Start() {native_Start(m_handle);}public void stop() {native_Stop(m_handle);}public void unInit() {native_UnInit(m_handle);}public void addEventCallback(EventCallback callback) {mEventCallback = callback;}private void playerEventCallback(int msgType, float msgValue) {if(mEventCallback != null)mEventCallback.onMessageEvent(msgType, msgValue);}private void packetEventCallback(byte[]data) {if(mEventCallback != null)mEventCallback.onPacketEvent(data);}private native long native_Init(String url);private native void native_Start(long playerHandle);private native void native_Stop(long playerHandle);private native void native_UnInit(long playerHandle);public interface EventCallback {void onMessageEvent(int msgType, float msgValue);void onPacketEvent(byte []data);}}
 编写C代码加载ffmpeg库
#include <jni.h>
#include <string>#include "FFBridge.h"extern "C"
{
#include <libavutil/time.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/packet.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
};extern "C" JNIEXPORT jstring JNICALL
Java_com_qmcy_demux_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}extern "C" JNIEXPORT jstring JNICALL
Java_com_qmcy_demux_MainActivity_GetVersion(JNIEnv* env,jobject /* this */) {char strBuffer[1024 * 4] = {0};strcat(strBuffer, "libavcodec : ");strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));strcat(strBuffer, "\nlibavformat : ");strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));strcat(strBuffer, "\nlibavutil : ");strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));strcat(strBuffer, "\nlibavfilter : ");strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));strcat(strBuffer, "\nlibswresample : ");strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));strcat(strBuffer, "\nlibswscale : ");strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));strcat(strBuffer, "\navcodec_configure : \n");strcat(strBuffer, avcodec_configuration());strcat(strBuffer, "\navcodec_license : ");strcat(strBuffer, avcodec_license());//LOGCATE("GetFFmpegVersion\n%s", strBuffer);return env->NewStringUTF(strBuffer);
}extern "C" JNIEXPORT jlong JNICALL Java_com_qmcy_demux_FFDemuxJava_native_1Init(JNIEnv *env, jobject obj, jstring jurl)
{const char* url = env->GetStringUTFChars(jurl, nullptr);FFBridge *bridge = new FFBridge();bridge->Init(env, obj, const_cast<char *>(url));env->ReleaseStringUTFChars(jurl, url);return reinterpret_cast<jlong>(bridge);
}extern "C"
JNIEXPORT void JNICALL Java_com_qmcy_demux_FFDemuxJava_native_1Start(JNIEnv *env, jobject obj, jlong handle)
{if(handle != 0){FFBridge *bridge = reinterpret_cast<FFBridge *>(handle);bridge->Start();}}extern "C"
JNIEXPORT void JNICALL Java_com_qmcy_demux_FFDemuxJava_native_1Stop(JNIEnv *env, jobject obj, jlong handle)
{if(handle != 0){FFBridge *bridge = reinterpret_cast<FFBridge *>(handle);bridge->Stop();}
}extern "C"
JNIEXPORT void JNICALL Java_com_qmcy_demux_FFDemuxJava_native_1UnInit(JNIEnv *env, jobject obj, jlong handle)
{if(handle != 0){FFBridge *bridge = reinterpret_cast<FFBridge *>(handle);bridge->UnInit();delete bridge;}
}

源码地址icon-default.png?t=N7T8https://gitee.com/baipenggui/demux_demo.git

相关文章:

Android MediaCodec将h264实时视频流数据解码为yuv,并转换yuv的颜色格式为nv21

初始化mediacodec private MediaCodec mediaCodec;private ByteBuffer[] inputBuffers;private void initMediaCodec(Surface surface) {try {Log.d(TAG, "onGetNetVideoData: ");//创建解码器 H264的Type为 AACmediaCodec MediaCodec.createDecoderByType("v…...

Postgresql SQL 字段拼接

本文介绍Postgresql 数据库sql字段拼接的方法。 1.使用字符串连接函数 select pkey || - || vname as "项目-版本" from test_jira_project_verison; 2.使用字符串连接操作符 select CONCAT(pkey, -, vname) as "项目-版本" from test_jira_project_ve…...

MySQL 迁移完不能快速导数据了?

关于 5.6 升级到 5.7 之后&#xff0c;GTID 的相关功能的注意事项。 作者&#xff1a;秦福朗&#xff0c;爱可生 DBA 团是队成员&#xff0c;负责项目日常问题处理及公司平台问题排查。热爱互联网&#xff0c;会摄影、懂厨艺&#xff0c;不会厨艺的 DBA 不是好司机&#xff0c;…...

Lazysysadmin靶机

信息收集 主机发现 nmap -sn 192.168.88.0/24 //-sn&#xff1a;制作主机发现&#xff0c;不做端口扫描&#xff1b;扫描结果包含本机IP 端口扫描 nmap --min-rate 10000 -p- 192.168.88.136 扫描端口详细信息 端口扫描发现&#xff0c;该主机的22、80、139、445、3306、…...

LeetCode09——回文数

LeetCode09 自己写的解,转化为字符串再反转&#xff0c;比较笨。 import java.util.Scanner; public class Result01 {public static void main(String[] args) {System.out.println("请输入整数&#xff0c;我来帮您判断是否是回文数。");Scanner scanner new Sc…...

云安全—分布式基础

0x00 前言 云必然是依赖于分布式技术来进行实现的&#xff0c;所以有必要学习和来了解分布式相关的内容 0x01 分布式计算 1.基本概述 分布式计算的定义&#xff1a;通过网络互联的计算机都具有一定的计算能力&#xff0c;他们之间互相传递数据&#xff0c;实现信息共享&…...

Spring(18) @Order注解介绍、使用、底层原理

目录 一、简介二、List 注入使用示例2.1 测试接口类2.2 测试接口实现类12.3 测试接口实现类22.4 启动类&#xff08;测试&#xff09;2.5 测试结果场景一&#xff1a;场景二&#xff1a; 三、CommandLineRunner 使用示例3.1 接口实现类13.2 接口实现类23.3 测试结果场景一&…...

目标检测YOLO实战应用案例100讲-基于改进YOLOv6的轧钢表面细小缺陷检测

目录 前言 存在的问题 轧钢缺陷图像特征分析 2.1单一类型缺陷 2.2面状缺陷...

leetcode:507. 完美数(python3解法)

难度&#xff1a;简单 对于一个 正整数&#xff0c;如果它和除了它自身以外的所有 正因子 之和相等&#xff0c;我们称它为 「完美数」。 给定一个 整数 n&#xff0c; 如果是完美数&#xff0c;返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;num…...

智能物联网解决方案:蓝牙IOT主控模块打造高效监测和超低功耗

物联网蓝牙模块&#xff0c;无论单模&#xff0c;还是双模&#xff0c;或者双模音频的选择&#xff0c;如下文说描述&#xff1a; 蓝牙芯片模块市场的百花齐放&#xff0c;也带来的工程师在选型时碰到很大的困难&#xff0c;但是无论是做半成品&#xff0c;还是做成品&#xf…...

vue 拿到数据后,没有重新渲染视图,nuxt.js拿到数据后,没有重新渲染视图,强制更新视图

以下为Vue2的解决方案 一、 Vue.set&#xff08;&#xff09; 问&#xff1a;什么情况下使用&#xff1f; 答&#xff1a;如果你向响应式数据添加新的“属性”&#xff0c;理论上&#xff0c;一般情况下是没问题的&#xff0c;但是&#xff0c;如果你的级别比较深&#xff0c;又…...

Docker基础操作命令演示

Docker中的常见命令&#xff0c;可以参考官方文档&#xff1a;https://docs.docker.com/engine/reference/commandline/cli/ 1、常见命令介绍 其中&#xff0c;比较常见的命令有&#xff1a; 命令说明文档地址docker pull拉取镜像docker pulldocker push推送镜像到DockerReg…...

XTU-OJ 1175-Change

题目描述 一个班有N个学生&#xff0c;每个学生有第一学期成绩Xi,第二学期成绩Yi&#xff0c;请问成绩上升&#xff0c;持平&#xff0c;下降的人数。 输入 每个样例的第一行是整数N(0≤N≤50),如果N0&#xff0c;表示输入结束&#xff0c;这个样例不需要处理。 第二行是N个整数…...

Python环境安装

环境安装 Windows安装Linux安装 Windows安装 下载最新版Python https://www.python.org/downloads 打开安装包 选择安装路径&#xff0c;安装 安装 验证安装是否成功&#xff0c;命令行输入python Linux安装 安装依赖环境 yum install wget zlib-devel bzip2-devel op…...

苏轼在密州的四首千古名作

苏轼&#xff0c;一个从诗歌王国掉落人间的落魄贵族&#xff0c;整个政治生涯几乎都以流浪为主&#xff0c;在古诗词世界或许只有李白与之最是相似&#xff0c;不过李白的流浪属于荡歌山水、云游四方&#xff0c;而苏轼的流浪则带有被动的成分&#xff1a;一纸贬黜公文就是苏轼…...

[计算机提升] 域及域用户(组)

1.3 域及域用户(组) 1.3.1 域的概念 在Windows操作系统中&#xff0c;域&#xff08;Domain&#xff09;是指一个或多个计算机或资源的集合&#xff0c;这些计算机或资源受到单个安全数据库&#xff08;域控制器&#xff09;的管理和控制。域可以包含多个计算机、用户、组和其…...

命令行配置文件

在说具体的配置方式之前&#xff0c;我们需要首先梳理清除几个概念。这有助于我们明白自己在做什么&#xff0c;以及如何把经验平移到其他方面。 和命令行相关的有几个概念&#xff1a;terminal&#xff08;终端&#xff09;、shell&#xff08;解释器&#xff09;&#xff1b…...

MPP产品介绍-定位-应用场景-技术特点

产品定位 FusionInsight LibrA是企业级的大规模并行处理关系型数据库。FusionInsight LibrA采用MPP(Massive Parallel Processing)架构&#xff0c;支持行存储与列存储&#xff0c;提供PB(Petabyte&#xff0c;2的50次方字节)级别数据量的处理能力。 FusionInsight LibrA在核…...

Linux性能优化--性能工具:磁盘I/O

6.0 概述 本章介绍的性能工具能帮助你评估磁盘I/O子系统的使用情况。这些工具可以展示哪些磁盘或分区已被使用&#xff0c;每个磁盘处理了多少I/O,发给这些磁盘的I/O请求要等多久才被处理。 阅读本章后&#xff0c;你将能够&#xff1a; 确定系统内磁盘I/O的总量和类型(读/写…...

Archive Team: The Twitter Stream Grab

该集合不再更新&#xff0c;应被视为静态数据集。 从一般 Twitter 流中抓取的 JSON 的简单集合&#xff0c;用于研究、历史、测试和记忆的目的。这是“Spritzer”版本&#xff0c;最轻、最浅的 Twitter 抓取。不幸的是&#xff0c;我们目前无法访问流的洒水器或花园软管版本。 …...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...