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

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

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

相关文章

C进阶 自定义类型

目录 前言 一 结构体 二 结构体的存储 三 位段 四 枚举 五 联合体 总结 前言 我们之前学习的int char double ......都是内置类型&#xff0c;但是我们今天所学习的是自定义类型&#xff0c;比如联合体&#xff0c;结构体&#xff0c;枚举 一 结构体 结构体是一…

STM32的HAL库开发---ADC采集内部温度传感器

一、STM32内部温度传感器简介 二、温度计算方法 F1系列&#xff1a; 从数据手册中可以找到V25和Avg_Slope F4、F7、H7系列只是标准值不同&#xff0c;自行查阅手册 三、实验简要 1、功能描述 通过ADC1通道16采集芯片内部温度传感器的电压&#xff0c;将电压值换算成温度后&…

【PyQt5】python可视化开发:PyQt5介绍,开发环境搭建快速入门

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Android 串口通信

引言 在iot项目中&#xff0c;Android 端总会有和硬件通信。 通信这里&#xff1a;串口通信&#xff0c;蓝牙通信或者局域网通信。 这里讲一下串口通信。 什么是串口&#xff1f; “串口”&#xff08;Serial Port&#xff09;通常是指一种用于与外部设备进行串行通信的接口。…

跟据spring boot版本,查看对应的tomcat,并查看可支持的tomcat的版本范围

一 查看springboot自带的tomcat版本&#xff1a; 可直接在项目中找到Maven Dependencies中找到tomcat版本 二、查看SpringBoot内置tomcat版本的支持范围 我这边是跟据maven仓库查看的 首先跟据链接打开maven仓库&#xff1a;https://mvnrepository.com/ 然后搜索&#xff1a…

磐维数据库双中心容灾流复制集群搭建

1. 架构 磐维数据库PanWeiDB V2.0.0基于gs_sdr工具&#xff0c;在不借助额外存储介质的情况下实现跨Region的异地容灾。提供流式容灾搭建&#xff0c;容灾升主&#xff0c;计划内主备切换&#xff0c;容灾解除、容灾状态监控等功能。 2. 部署双中心磐维集群 2.1. 主集群 角色…

Spring事务原理 二

在上一篇博文《Spring事务原理 一》中&#xff0c;我们熟悉了Spring声明式事务的AOP原理&#xff0c;以及事务执行的大体流程。 本文中&#xff0c;介绍了Spring事务的核心组件、传播行为的源码实现。下一篇中&#xff0c;我们将结合案例&#xff0c;来讲解实战中有关事务的易…

【机器学习】【KMeans聚类分析实战】用户分群聚类详解——SSE、CH 指数、SC全解析,实战电信客户分群案例

1.引言 在实际数据分析中&#xff0c;聚类算法常用于客户分群、图像分割等场景。如何确定聚类数 k 是聚类分析中的关键问题之一。本文将以“用户分群”为例&#xff0c;展示如何通过 KMeans 聚类&#xff0c;利用 SSE&#xff08;误差平方和&#xff0c;也称 Inertia&#xff…

20-R 绘图 - 饼图

R 绘图 - 饼图 R 语言提供来大量的库来实现绘图功能。 饼图&#xff0c;或称饼状图&#xff0c;是一个划分为几个扇形的圆形统计图表&#xff0c;用于描述量、频率或百分比之间的相对关系。 R 语言使用 pie() 函数来实现饼图&#xff0c;语法格式如下&#xff1a; pie(x, l…

搭建 Hadoop 3.3.6 伪分布式

搭建 Hadoop 3.3.6 伪分布式 IP 192.168.157.132 初始化操作 更改yum源 # 1_1.安装Wget yum install wget# 1_2.备份CentOS-Base.repo文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo_bak# 2.下载阿里yum源配置 wget -O /etc/yum.repos.d/Cen…

python电影数据分析及可视化系统建设

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

手机壁纸设计中,金属质感字体可以为壁纸增添独特的视觉效果和高端感

在手机壁纸设计中&#xff0c;金属质感字体可以为壁纸增添独特的视觉效果和高端感。以下是一些关于金属质感字体在手机壁纸设计中的应用建议和案例分析&#xff1a; 1. 金属质感字体的特点 视觉冲击力强&#xff1a;金属质感字体具有独特的光泽和质感&#xff0c;能够在视觉上…

使用ezuikit-js封装一个对接摄像头的组件

ezuikit-js 是一个基于 JavaScript 的视频播放库&#xff0c;主要用于在网页中嵌入实时视频流播放功能。它通常用于与支持 RTSP、RTMP、HLS 等协议的摄像头或视频流服务器进行交互&#xff0c;提供流畅的视频播放体验。 主要功能 多协议支持&#xff1a;支持 RTSP、RTMP、HLS …

PHP post 数据丢失问题

max_input_vars是PHP配置选项之一&#xff0c;用于设置一个请求中允许的最大输入变量数。它指定了在处理POST请求或者通过URL传递的参数时&#xff0c;PHP脚本能够接收和处理的最大变量数量。 max_input_vars的默认值是1000&#xff0c;意味着一个请求中最多可以包含1000个输入…

jenkins docker容器中安装python3.9环境

在运维过程中&#xff0c;不过避免的需要使用到python&#xff0c;在jenkins 的docker容器中&#xff0c;是没有python环境的&#xff0c;需要我们自己手动安装一下。 查看是否有工具apt-get 直接输入apt-get 然后回车&#xff0c;出现以下内容&#xff0c;表示支持apt-get命令…

《Spring实战》(第6版) 保护Spring

第1部分 Spring基础 第4章 使用非关系型数据 关系型数据库一直是首选&#xff0c;近年来"NoSQL"数据库提供了数据存储的不同概念和结构。 SpringData为很多NoSQL数据库提供了支持&#xff0c;包括MongoDB、Cassandra、Couchbase、Neo4j、Redis等&#xff0c;无论选…

SQLMesh 系列教程7- 详解 seed 模型

SQLMesh 是一个强大的数据建模和管道管理工具&#xff0c;允许用户通过 SQL 语句定义数据模型并进行版本控制。Seed 模型是 SQLMesh 中的一种特殊模型&#xff0c;主要用于初始化和填充基础数据集。它通常包含静态数据&#xff0c;如参考数据和配置数据&#xff0c;旨在为后续的…

【JavaEE】-- 多线程(初阶)2

文章目录 3.线程的状态3.1观察线程的所有状态3.2线程状态和状态转移的意义 4.多线程带来的的风险-线程安全 (重点)4.1观察线程不安全4.2 线程不安全的原因4.2.1 线程调度是随机的4.2.2 修改共享数据4.2.3 原子性4.2.4 内存可见性4.2.5 指令重排序 4.3解决之前的线程不安全问题 …

安卓系统远程控制电脑方法,手机远控教程,ToDesk工具

不知道大家有没有觉得手机、平板虽然很好用&#xff0c;却也仍存在有很多替代不了电脑的地方。就比如说撰写文档、做数据报表啥的就不如PC端操作般方便&#xff0c;就跟别说PS修图、AE视频剪辑等需高性能设备来带动才易用的了。 好在也是有对策可解决&#xff0c;装个ToDesk远程…

机器学习(李宏毅)——RL(强化学习)

一、前言 本文章作为学习2023年《李宏毅机器学习课程》的笔记&#xff0c;感谢台湾大学李宏毅教授的课程&#xff0c;respect&#xff01;&#xff01;&#xff01; 二、大纲 What is RL&#xff1f;Three steps in MLPolicy GradientActor-CriticReward Shaping 三、What …