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

news2025/7/19 10:20:17

初始化mediacodec

    //宽高根据摄像头分辨率设置
    private int Width = 1280;
    private int Height = 720;
    private MediaCodec mediaCodec;
    private ByteBuffer[] inputBuffers;

    private void initMediaCodec(Surface surface) {

        try {
            Log.d(TAG, "onGetNetVideoData: ");
            //创建解码器 H264的Type为  AAC
            mediaCodec = 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和surface
            mediaCodec.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;存在则大于0
        int inIndex = mediaCodec.dequeueInputBuffer(10000);
        if (inIndex >= 0) {
            //根据返回的index拿到可以用的buffer
            ByteBuffer 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");
            //等待查询空的buffer
            return;
        }
        //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() {
                  @Override
                  public 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 is
        System.arraycopy(yv12, 0, nv21, 0, size);

        for (int i = 0; i < size / 4; i++) {
            nv21[vOffset + (i * 2)] = yv12[vOffset + i];      // V
            nv21[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 is
        System.arraycopy(nv12, 0, nv21, 0, offset);

        for (int i = 0; i < size / 4; i++) {
            nv21[offset + (i * 2) + 1] = nv12[offset + (i * 2)];       // U
            nv21[offset + (i * 2)] = nv12[offset + (i * 2) + 1];       // V
        }
    }

h264实时视频流的数据来源

    @Override
    public 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 exception
                    Log.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

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

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

相关文章

Bootstrap的进度条效果

在Bootstrap中&#xff0c;进度条一般由嵌套的两层结构标签构成&#xff0c;外层标签引入类progress&#xff0c;用来形成进度槽&#xff1b;内层标签引入类progress-bar&#xff0c;用来设计进度条。 目录 01-最基本的进度条效果02-为进度条添加文本03-设置进度条的高度04-设…

【c语言】编译链接--详解

文章目录 一.程序的翻译环境和运行环境二.翻译环境&#xff1a;预编译编译汇编链接&#xff08;一&#xff09;预编译&#xff08;二&#xff09;编译1&#xff09;词法分析2&#xff09;语法分析3&#xff09;语义分析 &#xff08;三&#xff09;汇编(四&#xff09;链接1.编…

FDTD Solutions笔记

FDTD Solutions笔记 目录使用流程实例 目录 使用流程 实例 材料条件 步骤 基底 2. 添加规则膜层 3. 添加仿真区 解释&#xff1a; 仿真区为&#xff08;0,0&#xff09;&#xff0c;x方向为0.4&#xff0c;y方向是1 解释&#xff1a; 一般先用低精度进行计算 解释&#xff1a…

【汇编语言特别篇】DOSBox及常用汇编工具的详细安装教程

文章目录 &#x1f4cb;前言一. ⛳️dosbox的介绍、下载和安装1.1 &#x1f514;dosbos简介1.2 &#x1f514;dosbox的下载1.2.1 &#x1f47b;方式一&#xff1a;官网下载(推荐)1.2.2 &#x1f47b;方式二&#xff1a;网盘安装包 1.3 &#x1f514;dosbox的安装1.4 &#x1f5…

Git GUI使用笔记

看这个视频 Git GUI基本使用_哔哩哔哩_bilibili 1 下载 Git-2.42.0.2-64Window64位安装包-最新版资源-CSDN文库 安装软件就一路next就可以 2 配置 空白处右键&#xff0c;选择Open Git Bash here &#xff0c;输入下面两行配置信息 git config --global user.name "Y…

OJ第四篇

文章目录 链表分割环形链表有效的括号 链表分割 链接: 链表分割 虽然这个题牛客网中只有C,但是无所谓&#xff0c;我们只要知道C是兼容C的就可以了 至于说这个题的思路&#xff0c;我们就弄两个链表&#xff0c;把小于x的结点放到一个链表中&#xff0c;剩下的放到另一个链表…

excel导出-将后端返回的文件流导出为excel

有的业务场景&#xff0c;需要前端自己将文本流导出为excel有的是后端返回的文本流&#xff0c;有的是调用上传组件后&#xff0c;前端组件生成的文本流&#xff0c;组件上传后点击上传的文件名&#xff0c;要求实现下载功能&#xff0c;这时的导出就需要前端自己处理了 直接上…

百度文心一言 4.0 :如何申请百度文心一言 4.0

本心、输入输出、结果 文章目录 百度文心一言 4.0 &#xff1a;如何申请百度文心一言 4.0前言如何申请千帆大模型试用百度文心一言 4.0 主要功能介绍配套发布的十余款AI原生应用插件、API 生态 百度世界大会回顾弘扬爱国精神 百度文心一言 4.0 &#xff1a;如何申请百度文心一言…

ubuntu20.04 nerf开山之作

源码 GitHub - yenchenlin/nerf-pytorch: A PyTorch implementation of NeRF (Neural Radiance Fields) that reproduces the results. 代码的相关解读 NeRF代码解读-相机参数与坐标系变换 - 知乎 原文题目&#xff1a;NeRF: Representing Scenes as Neural Radiance Field…

架构师选择题--软件架构设计

架构师选择题--软件架构设计 真题 真题 c 中间件分为5类&#xff1a; 交互是最基本的功能 b 公共服务&#xff1a;可复用的服务 b c 微服务去中心化 面向服务集中式 d 架构的组成&#xff1a; 构件 , 连接件 , 约束规则 做为部署单元拆分没有意义 a 接口是已经命名的一组操…

Xftp和Xshell的使用

目录 Xftp和Xshell的区别 Xftp安装教程、使用Xftp进行远程文件传输 一、Xftp安装教程 二、使用Xftp进行远程文件传输 三、连接至服务器 四、从 windows -> 服务器 传输文件 .Xshell安装教程、使用Xshell进行Linux远程登录 什么是xshell 一、远程登录的意义 二、…

微控制器中的晶振电路

文章目录 1234567 1 2 3 4 5 6 7

Redis命令

一、数据结构介绍。 redis是一个key-value的数据库&#xff0c;key一般是string&#xff0c;但是value有很多种类型。包括&#xff1a; 字符串&#xff08;String&#xff09;&#xff1a;字符串是 Redis 最基础的数据结构之一&#xff0c;可以存储字符串、整数或浮点数。 哈…

JDBC增删改查示例

数据库表 CREATE TABLE customers ( id int NOT NULL AUTO_INCREMENT, name varchar(15) DEFAULT NULL, email varchar(20) DEFAULT NULL, birth date DEFAULT NULL, photo mediumblob, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT39 DEFAULT CHARSETgb2312;…

Redis 面试必备 全知识点思维导图

脑图下载地址&#xff1a;https://mm.edrawsoft.cn/mobile-share/index.html?uuidcf5bf135744412-src&share_type1 事务 定义 事务是一个单独的隔离操作,事务中的所有操作都将序列化,有顺序的执行,事务执行的过程中不会被其他客服端发来的命令打断 作用 串联多个命令防…

【兔子王赠书第2期】《案例学Python(基础篇)》

文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展&#xff0c;Python将会得到越来越多开发者的喜爱和应用。身边有很多朋友都开始使用Python语言进行开发。正是因为Python是一门如此受欢迎的编程语…

下载Jakarta

百度找到Jakarta的官网 https://jakarta.ee/zh/ 打开后在右上角有这两个按钮 其中starter按钮是 提供helloworld的&#xff0c;也就是【初体验】&#xff0c;可以根据版本号&#xff0c;jdk版本定制hello world&#xff1b; 另一个Download是下载【兼容产品】&#xff0c;点进…

Python合并多个相交矩形框

Python合并多个相交矩形框 前言前提条件相关介绍实验环境Python合并多个相交矩形框代码实现 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入Python日常小操作专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页…

小程序设计基本微信小程序的旅游社系统

项目介绍 现今市面上有关于旅游信息管理的微信小程序还是比较少的&#xff0c;所以本课题想对如今这么多的旅游景区做一个收集和分类。这样可以给身边喜欢旅游的朋友更好地推荐分享适合去旅行的地方。 前端采用HTML架构&#xff0c;遵循HTMLss JavaScript的开发方式&#xff0…

自动化测试框架指南

目录 定义测试自动化 不同类型的框架 以工具为中心的框架 面向项目的框架 关键字驱动的框架 完美测试自动化框架的主要组件 测试库 单元测试 集成和端到端测试 行为驱动开发 测试数据管理 mock&#xff0c;Stubs和虚拟化 实施模式的通用机制 测试结果报告 CI平台…