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;}
}
源码地址https://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 之后,GTID 的相关功能的注意事项。 作者:秦福朗,爱可生 DBA 团是队成员,负责项目日常问题处理及公司平台问题排查。热爱互联网,会摄影、懂厨艺,不会厨艺的 DBA 不是好司机,…...

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

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

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

Spring(18) @Order注解介绍、使用、底层原理
目录 一、简介二、List 注入使用示例2.1 测试接口类2.2 测试接口实现类12.3 测试接口实现类22.4 启动类(测试)2.5 测试结果场景一:场景二: 三、CommandLineRunner 使用示例3.1 接口实现类13.2 接口实现类23.3 测试结果场景一&…...
目标检测YOLO实战应用案例100讲-基于改进YOLOv6的轧钢表面细小缺陷检测
目录 前言 存在的问题 轧钢缺陷图像特征分析 2.1单一类型缺陷 2.2面状缺陷...

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

智能物联网解决方案:蓝牙IOT主控模块打造高效监测和超低功耗
物联网蓝牙模块,无论单模,还是双模,或者双模音频的选择,如下文说描述: 蓝牙芯片模块市场的百花齐放,也带来的工程师在选型时碰到很大的困难,但是无论是做半成品,还是做成品…...

vue 拿到数据后,没有重新渲染视图,nuxt.js拿到数据后,没有重新渲染视图,强制更新视图
以下为Vue2的解决方案 一、 Vue.set() 问:什么情况下使用? 答:如果你向响应式数据添加新的“属性”,理论上,一般情况下是没问题的,但是,如果你的级别比较深,又…...

Docker基础操作命令演示
Docker中的常见命令,可以参考官方文档:https://docs.docker.com/engine/reference/commandline/cli/ 1、常见命令介绍 其中,比较常见的命令有: 命令说明文档地址docker pull拉取镜像docker pulldocker push推送镜像到DockerReg…...
XTU-OJ 1175-Change
题目描述 一个班有N个学生,每个学生有第一学期成绩Xi,第二学期成绩Yi,请问成绩上升,持平,下降的人数。 输入 每个样例的第一行是整数N(0≤N≤50),如果N0,表示输入结束,这个样例不需要处理。 第二行是N个整数…...

Python环境安装
环境安装 Windows安装Linux安装 Windows安装 下载最新版Python https://www.python.org/downloads 打开安装包 选择安装路径,安装 安装 验证安装是否成功,命令行输入python Linux安装 安装依赖环境 yum install wget zlib-devel bzip2-devel op…...
苏轼在密州的四首千古名作
苏轼,一个从诗歌王国掉落人间的落魄贵族,整个政治生涯几乎都以流浪为主,在古诗词世界或许只有李白与之最是相似,不过李白的流浪属于荡歌山水、云游四方,而苏轼的流浪则带有被动的成分:一纸贬黜公文就是苏轼…...
[计算机提升] 域及域用户(组)
1.3 域及域用户(组) 1.3.1 域的概念 在Windows操作系统中,域(Domain)是指一个或多个计算机或资源的集合,这些计算机或资源受到单个安全数据库(域控制器)的管理和控制。域可以包含多个计算机、用户、组和其…...

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

MPP产品介绍-定位-应用场景-技术特点
产品定位 FusionInsight LibrA是企业级的大规模并行处理关系型数据库。FusionInsight LibrA采用MPP(Massive Parallel Processing)架构,支持行存储与列存储,提供PB(Petabyte,2的50次方字节)级别数据量的处理能力。 FusionInsight LibrA在核…...

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

Archive Team: The Twitter Stream Grab
该集合不再更新,应被视为静态数据集。 从一般 Twitter 流中抓取的 JSON 的简单集合,用于研究、历史、测试和记忆的目的。这是“Spritzer”版本,最轻、最浅的 Twitter 抓取。不幸的是,我们目前无法访问流的洒水器或花园软管版本。 …...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...