android调用ffmpeg解析rtsp协议的视频流
文章目录
- 一、背景
- 二、解析rtsp数据
- 1、C层功能代码
- 2、jni层的定义
- 3、app层的调用
- 三、源码下载
一、背景
本demo主要介绍android调用ffmpeg中的接口解析rtsp协议的视频流(不解析音频),得到yuv数据,把yuv转bitmap在android设备上显示,涉及到打开视频、解封装、解码、回调yuv数据。学习记录帖,C语言小白,不足的地方请指正,多谢!
二、解析rtsp数据
1、C层功能代码
Decoder.h
#ifndef DECODERTSP_DECODER_H
#define DECODERTSP_DECODER_H
#include <thread>
#include "include/jniLog.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>
#include <functional>
};using namespace std;
// 定义一个回调函数类型(typedef 用于为已有的数据类型创建一个别名)
typedef std::function<void(uint8_t *buf, int size)> Callback;// 解析rtsp视频流
int ReadFrameAndDecoder(const char *url,Callback callback);
void DoStop();
#endif //DECODERTSP_DECODER_H
Decoder.cpp
#include "include/Decoder.h"
#include "include/jniLog.h"
bool isStop = false;
int ReadFrameAndDecoder(const char* m_Url,Callback callback){char url[100] = {0};strcpy(url,m_Url);AVFormatContext *pFormatCtx = avformat_alloc_context();AVDictionary *options = NULL;av_dict_set(&options, "buffer_size", "1024000", 0);// 设置缓冲区大小av_dict_set(&options, "stimeout", "20000000", 0);av_dict_set(&options, "max_delay", "30000000", 0);
// av_dict_set(&options, "rtsp_transport", "tcp", 0); //使用 TCP 传输LOGI("ReadFrameAndDecoder:url = %s",url);if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) // 打开视频文件return -1;if (avformat_find_stream_info(pFormatCtx, NULL) < 0) // 查找视频流return -2;//视频解码,需要找到视频对应的AVStream所在pFormatCtx->streams的索引位置int video_stream_idx = -1;for (int i = 0; i < pFormatCtx->nb_streams; i++) {//根据类型判断,是否是视频流if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_idx = i;break;}}//只有知道视频的编码方式,才能够根据编码方式去找到解码器//获取视频流中的编解码上下文AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL);;avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[video_stream_idx]->codecpar);// AVDISCARD_NONKEY; // 丢弃非关键帧(如B帧)// AVDISCARD_NONINTRA; // 丢弃所有非帧内编码的帧(这个参数不会灰屏)// AVDISCARD_NONREF // 丢弃所有非参考帧// AVDISCARD_BIDIR // 丢弃所有双向预测帧// AVDISCARD_DEFAULT
// pCodecCtx->skip_frame = AVDISCARD_NONKEY;//4.根据编解码上下文中的编码id查找对应的解码//AVCodec *pCodec = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx->codec_id));AVCodec *pCodec = const_cast<AVCodec *>(avcodec_find_decoder(AV_CODEC_ID_HEVC));if (pCodec == NULL)return -3;// 打开解码器if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)return -4;// 设置参数(不缓冲,低延时)
// av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);
// pCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;// 分配视频帧(装载解码后的数据)AVFrame *pFrame = av_frame_alloc();AVPacket packet; // 读取视频帧(解码前的数据包)while (av_read_frame(pFormatCtx, &packet) >= 0 && !isStop) {if (packet.stream_index == video_stream_idx) {int got_picture;// 向解码器输入数据包got_picture = avcodec_send_packet(pCodecCtx, &packet);if (got_picture < 0) {LOGI("got_picture = %d", got_picture);continue;}while (got_picture == 0) {// 从解码器获取帧 返回值 -11:数据包不足?got_picture = avcodec_receive_frame(pCodecCtx, pFrame);//此时,pFrame中的数据就是YUV数据if(got_picture == 0){// 计算 frame 的数据大小int data_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pFrame->width, pFrame->height, 1);if (data_size < 0) {LOGE("Could not calculate buffer size");return -5;}// 分配内存来存储 byte[]uint8_t *buffer = (uint8_t *)av_malloc(data_size * sizeof(uint8_t));if (!buffer) {LOGE("Could not allocate memory for buffer");return -6;}// 将 AVFrame 的数据复制到 buffer 中int ret = av_image_copy_to_buffer(buffer, data_size,(const uint8_t * const *)pFrame->data, pFrame->linesize,AV_PIX_FMT_YUV420P, pFrame->width, pFrame->height, 1);if (ret < 0) {LOGE("Could not copy image data to buffer");av_free(buffer);return -7;}if (callback){callback(buffer,data_size * sizeof(uint8_t));}// 释放 bufferav_free(buffer);}else{LOGI("avcodec_receive_frame # got_picture = %d", got_picture);}}}av_packet_unref(&packet);}// 释放资源av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;
}void DoStop(){isStop = true;
}
2、jni层的定义
native-lib.cpp
#include <jni.h>
#include <string>
#include <include/jniLog.h>
#include "include/Decoder.h"extern "C"
JNIEXPORT jint JNICALL
Java_com_hisign_decodertsp_DecodeLib_native_1readAndDecode(JNIEnv *env, jobject thiz,jstring rtsp_url) {// TODO: implement native_readAndDecode()const char *url = env->GetStringUTFChars(rtsp_url, nullptr);LOGI("url = %s", url);// java层的回调函数jmethodID mid = env->GetMethodID(env->GetObjectClass(thiz), "packetEventCallback", "([B)V");if(!mid){LOGI("StartRestAndDecodePackage, mid is null");}// 获取java层的回调函数Callback dataCallback = [&env, &mid, &thiz](uint8_t *buf, int size){// todo 通过jni调用java层返回数据if(mid != nullptr){jbyteArray array1 = env->NewByteArray(size);env->SetByteArrayRegion(array1,0,size,(jbyte*)buf);env->CallVoidMethod(thiz, mid, array1);env->DeleteLocalRef(array1);}};int ret = ReadFrameAndDecoder(url,dataCallback);LOGI("ReadFrameAndDecoder ret = %d",ret);env->ReleaseStringUTFChars(rtsp_url, url);return 0;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_hisign_decodertsp_DecodeLib_native_1Stop(JNIEnv *env, jobject thiz) {// TODO: implement native_Stop()DoStop();return 0;
}
3、app层的调用
public class DecodeLib {static {System.loadLibrary("decodertsp");}private EventCallback mEventCallback = null;// 开始解码public void start(String url) {native_readAndDecode(url);}public void stop() {native_Stop();}public void addEventCallback(EventCallback callback) {mEventCallback = callback;}// 被native层回调private void packetEventCallback(byte[] data) {if (mEventCallback != null)mEventCallback.onReceiveData(data);}// 测试读取视频帧并解码private native int native_readAndDecode(String rtsp_url);private native int native_Stop();/*** 返回yuv数据*/public interface EventCallback {void onReceiveData(byte[] yuv);}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {private final static String KEY_IP = "KEY_IP";private static int width = 1920;private static int height = 1080;private ActivityMainBinding binding;private DecodeLib decodeLib;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());decodeLib = new DecodeLib();String ip = SPUtils.getInstance().getString(KEY_IP);binding.etIp.setText(ip);binding.btnStart.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String ip = binding.etIp.getText().toString();if(TextUtils.isEmpty(ip)){Toast.makeText(MainActivity.this, "please input ip!", Toast.LENGTH_SHORT).show();return;}SPUtils.getInstance().put(KEY_IP,ip);String url = "rtsp://"+ip+":554/livestream/0";new Thread(){public void run(){startRtsp(url);}}.start();KeyboardUtils.hideSoftInput(MainActivity.this);}});binding.btnStop.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {decodeLib.stop();}});}private void startRtsp(String url){decodeLib.addEventCallback(new DecodeLib.EventCallback() {@Overridepublic void onReceiveData(byte[] yuv) {// todo 这里显示图片runOnUiThread(new Runnable() {@Overridepublic void run() {Bitmap bmp = YuvToBitmapConverter.i420ToBitmap(yuv, width, height);if(bmp != null){binding.img.setImageBitmap(bmp);}}});}});decodeLib.start(url);}
}
三、源码下载
https://download.csdn.net/download/dami_lixm/90408495?spm=1001.2014.3001.5503
相关文章:
android调用ffmpeg解析rtsp协议的视频流
文章目录 一、背景二、解析rtsp数据1、C层功能代码2、jni层的定义3、app层的调用 三、源码下载 一、背景 本demo主要介绍android调用ffmpeg中的接口解析rtsp协议的视频流(不解析音频),得到yuv数据,把yuv转bitmap在android设备上显…...
Android 之 AIDL for HAL
Android AIDL for HAL 的作用与实现 作用: Android AIDL for HAL(Android Interface Definition Language for Hardware Abstraction Layer)旨在统一 HAL 开发接口,替代 HIDL(Hardware Interface Definition Language…...
Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?
问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…...
TensorFlow v2.16 Overview
TensorFlow v2.16 Overview 一、模块 Modules二、类 Classes三、函数 Functions TensorFlow v2.16.1 Overview 一、模块 Modules 模块是TensorFlow中组织代码的一种方式,将相关的功能和类封装在一起,方便用户使用和管理。每个模块都提供了特定领域的公共…...
Navicat17详细安装教程(附最新版本安装包和补丁)2025最详细图文教程安装手册
目录 前言:为什么选择Navicat 17? 一、下载Navicat17安装包 二、安装Navicat 1.运行安装程序 2.启动安装 3.同意“协议” 4.设置安装位置 5.创建桌面图标 6.开始安装 7.安装完成 三、安装补丁 1.解押补丁包 2.在解压后的补丁包目录下找到“w…...
机器视觉检测中,2D面阵相机和线扫相机的区别
2D面阵相机和线扫相机是工业视觉系统中常用的两种相机类型,各有其特点和应用场景。 2D面阵相机 特点: 成像方式:通过二维传感器一次性捕捉整个场景的图像。 分辨率:分辨率由传感器的像素数决定,常见的有百万像素到几千…...
【接口封装】——13、登录窗口的标题栏内容设置
解释: 1、封装内容:图标、文本内容、宽度 2、ui.iconLabel:在UI文件中的自定义命名 3、引入头文件:#include<qpixmap.h> 函数定义: #pragma once#include <QWidget> #include "ui_TitleBar.h"cl…...
一文详解U盘启动Legacy/UEFI方式以及GPT/MBR关系
对于装系统的老手而说一直想研究一下装系统的原理,以及面对一些问题时的解决思路,故对以前的方法进行原理上的解释,主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导,我们可以看一下微pe制作的启动盘&#…...
CMake管理依赖实战:多仓库的无缝集成
随着软件复杂度的增加,单个项目可能需要依赖多个外部库或模块。这些依赖项可能是来自不同的代码仓库,如ATest和BTest。为了实现高效的依赖管理,CMake提供了多种方式来处理这种多仓库的情况。下面我们将详细介绍几种常见的方法,并通…...
LeetCode 热题 100_搜索二维矩阵(64_74_中等_C++)(二分查找)(暴力破解法;Z字形查找;一次二分查找)
LeetCode 热题 100_搜索二维矩阵(64_74) 题目描述:输入输出样例:题解:解题思路:思路一(暴力破解法):思路二(Z字形查找):思路三&#x…...
学习量化交易的环境安装记录
1、安装anaconda 因为使用python,需要安装anaconda,具体是下面的官方地址,根据自己需要下载相应的版本 https://www.anaconda.com/download 运行上面下载的文件,安装anaconda 可以根据自己需要安装到相应的盘上面 同时环境变量…...
MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 1
第01章_Linux下MySQL的安装与使用 首先在vmware中下载centos7,实际上8更好一点,不过centos已经是时代的眼泪了,我之前已经教过了,不过是忘了,所以重新说一遍,看文档即可 2.开机前修改mac地址 ࿰…...
基于AVue的二次封装:快速构建后台管理系统的CRUD方案
基于AVue的二次封装:快速构建后台管理系统的CRUD方案 在开发后台管理系统时,表格是常见的组件之一。然而,使用原生的Element Plus实现CRUD(增删改查)功能往往需要编写大量重复代码,过程繁琐。即使借助类似…...
第6章:基于LangChain如何开发Agents,附带客户支持智能体示例
本文主要介绍了 LangChain4j 中的 Agent(代理) 概念,以及如何使用 LangChain4j 构建代理系统,重点提供了一个客户支持系统的智能体样例 代理(Agents)| LangChain4j 注意: 请注意,“A…...
【分布式理论13】分布式存储:数据存储难题与解决之道
文章目录 一、数据存储面临的问题二、RAID磁盘阵列的解决方案1. RAID概述2. RAID使用的技术3. RAID的代表性等级 三、分布式存储的新思路1. 分布式存储背景与特点2. 分布式存储的组成要素 一、数据存储面临的问题 在单机系统时代,当数据量不断增加、硬盘空间不够时…...
传统的自动化行业的触摸屏和上位机,PLC是否会被取代?
传统的自动化行业的触摸屏和上位机是否会被取代? 在工业自动化领域,触摸屏和上位机长期扮演着核心角色,尤其在污水处理、化工生产等场景中,它们通过实时数据采集、逻辑控制、报警联动等功能,保障了生产设备的稳定运行…...
智能合约的部署
https://blog.csdn.net/qq_40261606/article/details/123249473 编译 点击图中的 “Compile 1_Storage.sol” 存和取一个数的合约,remix自带 pragma solidity >0.8.2 <0.9.0; /*** title Storage* dev Store & retrieve value in a variable* custom:d…...
word$deepseep
1、进入官网地址。 DeepSeek 2、进入DeepSeek的API文档 3、点击DeepSeek开放平台左侧的“API Keys”, 再点击“创建API Key” 4、在弹出的对话框中,输入自己的API Key名称,点击创建。 sk-0385cad5e19346a0a4ac8b7f0d7be428 5、打开Word文档。 6、Word找…...
Redis中集合(Set)常见命令详解
集合(Set)常见命令详解 集合(Set)在Redis中是一种无序且不可重复的数据结构,非常适合用于存储唯一元素的集合。以下是Redis集合操作的一些常用命令及其详细说明: 添加成员 sadd key member [member ...]…...
Perl 面向对象编程指南
Perl 面向对象编程指南 引言 Perl 是一种强大的编程语言,以其灵活性和强大的文本处理能力而闻名。随着软件工程的发展,面向对象编程(OOP)已经成为现代编程的主流。本文将深入探讨 Perl 的面向对象编程,包括其基本概念…...
用 WOW.js 和 animate.css 实现动画效果
用 wow.js 就可以实现动画效果,但由于里面的动画样式太少,一般还会引入 animated.css 第一步:下载 选择合适的包管理器下载对应的内容 pnpm i wow.js animated.css --save 第二步:引入 在main.js中加入: import …...
Mac系统下使用Docker快速部署MaxKB:打造本地知识库问答系统
随着大语言模型的广泛应用,知识库问答系统逐渐成为提升工作效率和个人学习的有力工具。MaxKB是一款基于LLM(Large Language Model)大语言模型的知识库问答系统,支持多模型对接、文档上传和自动爬取等功能。本文将详细介绍如何在Ma…...
PyTorch torch.logsumexp 详解:数学原理、应用场景与性能优化(中英双语)
PyTorch torch.logsumexp 详解:数学原理、应用场景与性能优化 在深度学习和概率模型中,我们经常需要计算数值稳定的对数概率操作,特别是在处理 softmax 归一化、对数似然计算、损失函数优化 等任务时,直接求和再取对数可能会导致…...
如何为自己的 PDF 文件添加密码?在线加密 PDF 文件其实更简单
随着信息泄露和数据安全问题的日益突出,保护敏感信息变得尤为重要。加密 PDF 文件是一种有效的手段,可以确保只有授权用户才能访问或修改文档内容。本文将详细介绍如何使用 CleverPDF 在线工具为你的 PDF 文件添加密码保护,确保其安全性。 为…...
华为昇腾910b服务器部署DeepSeek翻车现场
最近到祸一台HUAWEI Kunpeng 920 5250,先看看配置。之前是部署的讯飞大模型,发现资源利用率太低了。把5台减少到3台,就出了他 硬件配置信息 基本硬件信息 按照惯例先来看看配置。一共3块盘,500G的系统盘, 2块3T固态…...
hive—常用的函数整理
1、size(split(...))函数用于计算分割后字符串数组的长度 实例1):由客户编号列表计算客户编号个数 --数据准备 with tmp_test01 as ( select tag074445270 tag_id,202501busi_mon , 012399931003,012399931000 index_val union all select tag07444527…...
深入浅出机器学习:概念、算法与实践
目录 引言 机器学习的基本概念 什么是机器学习 机器学习的基本要素 机器学习的主要类型 监督学习(Supervised Learning) 无监督学习(Unsupervised Learning) 强化学习(Reinforcement Learning) 机器…...
Unity Mirror 多房间匹配
文章目录 一 、一些唠叨二 、案例位置三、多房间匹配代码解析四、关于MatchInterestManagement五、总结 一 、一些唠叨 最近使用Mirror开发了一款多人同时在线的肉鸽塔防游戏,其目的是巩固一下Mirror这个插件的熟练度,另一方面是想和身边的朋友一起玩一下自己开发的游戏. 但是…...
基于flask+vue框架的的医院预约挂号系统i1616(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
系统程序文件列表 项目功能:用户,医生,科室信息,就诊信息,医院概况,挂号信息,诊断信息,取消挂号 开题报告内容 基于FlaskVue框架的医院预约挂号系统开题报告 一、研究背景与意义 随着医疗技术的不断进步和人们健康意识的日益增强,医院就诊量逐年增加。传统的现场…...
Rust编程语言入门教程(五)猜数游戏:生成、比较神秘数字并进行多次猜测
Rust 系列 🎀Rust编程语言入门教程(一)安装Rust🚪 🎀Rust编程语言入门教程(二)hello_world🚪 🎀Rust编程语言入门教程(三) Hello Cargo…...
