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

FFmpeg 打包mediacodec 编码帧 MPEGTS

在Android平台上合成视频一般使用MediaCodec进行硬编码,使用MediaMuxer进行封装,但是因为MediaMuxer支持格式有限,一般会采用ffmpeg封装,比如监控一般使用mpeg2ts格式而非MP4,这是因为两者对帧时pts等信息封装差异导致应用场景不同。

数据流转

相机帧数据输出到编码器mediacodec经过h264编码输出压缩数据 MediaCodec.BufferInfo

经由Mpeg2TsUtils传递给ffmpeg ,ffmpeg初始化AVFormatContext 输出上下文添加视频流

将编码器输出帧重新封装AVPacket 写入输出流。

1.1 编码器输出对接

定义 IFrameMuxer 实现编码输出流封装 ,并定义Mpeg2TsMuxer 实现mpeg2ts格式封装

1  IFrameMuxer

public abstract class IFrameMuxer {protected String filePath;protected int format;protected int orientationHint;public IFrameMuxer(String filePath, int format, int orientationHint) {this.orientationHint = orientationHint;this.filePath = filePath;this.format = format;}.....public abstract void start();public abstract void stop();public abstract void writeSampleData(boolean videoTrack, ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo);public abstract int addTrack(MediaFormat mediaFormatChanged, boolean video);public abstract void config(ByteBuffer configBuffer);
}

2  Mpeg2TsMuxer

package com.tyearlin.camera2;import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;import java.nio.ByteBuffer;import nl.bravobit.mpeg2ts.Mpeg2TsUtils;public class Mpeg2TsMuxer extends IFrameMuxer {private static final String TAG = "Mpeg2TsMuxer";public volatile boolean running = false;public Mpeg2TsMuxer(String filePath, int format, int orientationHint) {super(filePath, format, orientationHint);Log.i(TAG, "Mpeg2TsMuxer:");Mpeg2TsUtils.init(filePath, 1280, 1024);}@Overridepublic int addTrack(MediaFormat outputFormat, boolean isVideo) {return 0;}@Overridepublic void config(ByteBuffer buffer) {int size = buffer.capacity();Mpeg2TsUtils.config(buffer,size);}@Overridepublic void start() {if (running) {Log.e(TAG, "has on running ");throw new IllegalStateException("Muxer is running should stop first ");}Mpeg2TsUtils.start();running = true;}@Overridepublic void stop() {running = false;Mpeg2TsUtils.stop();}@Overridepublic void writeSampleData(boolean videoTrack, ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo) {if(!running) {Log.e(TAG,"not running");return;}long size = bufferInfo.size;long pts = bufferInfo.presentationTimeUs;boolean keyFrame = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0;Mpeg2TsUtils.write_encoded_frame(outputBuffer, size, pts, keyFrame);}
}

3 对接mediacodec 输出

编码器部分通过注册监听回调 调用Mpeg2TsMuxer 对应方法。

1.2  JNI 方法实现

jni承接上下文

1.2.1初始化 ffmpeg相关上下文

extern "C"
JNIEXPORT jlong JNICALL
Java_nl_bravobit_mpeg2ts_Mpeg2TsUtils_init(JNIEnv *env, jclass clazz, jstring path, jint width,jint height) {av_log_set_callback(av_log_android_print_callback);
//    av_log_test();//创建输出路径const char *file_path = env->GetStringUTFChars(path, JNI_FALSE);H2642Ts *h2642Ts = H2642Ts::instance();h2642Ts->av_init(file_path, width, height);env->ReleaseStringUTFChars(path, file_path);return reinterpret_cast<long>(h2642Ts);}

1.2.2  写入每一帧h264编码数据

mpeg2ts还支持其它格式 android自带 MediaMuxer不支持mpeg2ts ,虽然framwork层已有mpeg2ts write实现但未对sdk开放接口,也可以修改固件支持

extern "C"
JNIEXPORT jint JNICALL
Java_nl_bravobit_mpeg2ts_Mpeg2TsUtils_write_1encoded_1frame(JNIEnv *env, jclass clazz,jobject buffer, jlong size, jlong pts,jboolean is_key_frame) {//封装 h264 流H2642Ts *h2642Ts = H2642Ts::instance();uint8_t *data = static_cast<uint8_t *>(env->GetDirectBufferAddress(buffer));int ret = h2642Ts->write_packet(data, size, pts, is_key_frame);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "write_packet failed ret=  %d", ret);}return ret;
}

1.3 mpeg2ts封装实现

定义H2642Ts.cpp实现整个封装功能 直接通过调用 ffmpeg 实现

//
// Created by Q 2023/7/28.
//#ifndef CAMERA2STREAMGET_H2642TS_H
#define CAMERA2STREAMGET_H2642TS_Hextern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/timestamp.h>
}#include <thread>using namespace std;class H2642Ts {public:void init();static H2642Ts *instance();void av_init(const char *path, int width, int height);/*** 写入编码数据帧*/int write_packet(uint8_t *data, long size, long pts, bool isKeyFrame);int write_strem_params(uint8_t *data, int size);void printf_packet(AVFormatContext *fmt_ctx, AVPacket *pkt) ;int stop();int start();void release();private:AVFormatContext *ofmt_ctx;AVStream *out_stream;static H2642Ts *m_pInstance;static std::mutex m_Mutex   ;AVFormatContext *createAvFormatOutContext(const char *path);AVStream *createAvStream(int width, int height);H2642Ts();~H2642Ts();};#endif //CAMERA2STREAMGET_H2642TS_H

1.3.1 创建  createAvFormatOutContext

指定输出文件格式及路径 ffmpeg会根据avformat_alloc_output_context2  传入的format name “mpegts”通过av_guess_format方法遍历

muxer_list 匹配对应的 AVOutputFormat ,


AVFormatContext *H2642Ts::createAvFormatOutContext(const char *path) {AVFormatContext *ofmt_ctx;av_log(nullptr, AV_LOG_INFO,"createAvFormatOutContext: path = %s ",path);int ret = avformat_alloc_output_context2(&ofmt_ctx, nullptr, "mpegts", path);av_log(ofmt_ctx, AV_LOG_INFO,"createAvFormatOutContext: ret = %d ",ret);if (ret < 0) {av_log(nullptr, AV_LOG_ERROR,"avformat_alloc_output_context2: Something went really wrong .\n");return nullptr;}int result = avio_open(&ofmt_ctx->pb, path, AVIO_FLAG_READ_WRITE);if(result < 0) {av_log(ofmt_ctx,AV_LOG_INFO,"SingleAudioRecorder::StartRecord avio_open ret=%d", result);avformat_free_context(ofmt_ctx);return nullptr ;}return ofmt_ctx;
}

1.3.2 添加输出流 

这里 avformat_new_stream创建 并自动添加到AVFormatContext-> streams列表内

AVStream *H2642Ts::createAvStream(int width, int height) {av_log(ofmt_ctx, AV_LOG_INFO,"createAvStream");AVStream *stream = avformat_new_stream(ofmt_ctx, nullptr);if (!stream) {av_log(ofmt_ctx, AV_LOG_ERROR,"Failed allocating output stream .\n");return nullptr;}av_log(ofmt_ctx, AV_LOG_INFO, " stream index = %d .\n", stream->index);AVCodecParameters *codecpar = stream->codecpar;codecpar->codec_type = AVMEDIA_TYPE_VIDEO;codecpar->codec_id = AV_CODEC_ID_H264;codecpar->width = width;codecpar->height = height;return stream;
}

1.3.3 写入头部信息

int H2642Ts::start() {if (!ofmt_ctx) {av_log(nullptr, AV_LOG_ERROR,"ofmt_ctx is null: Something went really wrong .\n");return -1;}av_log(ofmt_ctx, AV_LOG_INFO,"start");
//    AVDictionary *dict;
//    int ret = av_dict_set(&dict, "tsflags", "faststart", 0);
//    if (ret < 0) {
//        av_log(ofmt_ctx, AV_LOG_ERROR,
//               "av_dict_set ret = %d : Something went really wrong .\n", ret);
//        return ret;
//    }if (!ofmt_ctx->streams || !out_stream) {av_log(ofmt_ctx, AV_LOG_ERROR,"ofmt_ctx->streams not ready : Something went really wrong .\n");return -1;}return avformat_write_header(ofmt_ctx, nullptr);
}

1.3.4 配置输出流参数(主要sps/pps)

int H2642Ts::write_strem_params(uint8_t *data, int size) {if (!out_stream) {av_log(ofmt_ctx, AV_LOG_ERROR,"out_stream is null");return -1;}AVCodecParameters *codecpar = out_stream->codecpar;if (codecpar == nullptr) {av_log(ofmt_ctx, AV_LOG_INFO, "codecpar is null");return -1;}codecpar->extradata = (uint8_t *) av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
//    codecpar->extradata = (uint8_t *) av_mallocz(size );memcpy(codecpar->extradata, data, size);codecpar->extradata_size = size;return 0;
}

注意 padding_size

/*** Extra binary data needed for initializing the decoder, codec-dependent.** Must be allocated with av_malloc() and will be freed by* avcodec_parameters_free(). The allocated size of extradata must be at* least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding* bytes zeroed.*/
uint8_t *extradata;

1.3.5 写入编码帧

注意时间基转换  

int H2642Ts::write_packet(uint8_t *data, long size, long pts, bool isKeyFrame) {// 获取视频编码数据地址av_log(ofmt_ctx, AV_LOG_INFO,"call write_packet:size:%d", size);AVPacket *packet = av_packet_alloc();av_init_packet(packet);packet->stream_index = out_stream->index;packet->data = data;packet->size = size;packet->pts = av_rescale_q(pts, {1, 1000000}, out_stream->time_base);packet->dts = packet->pts;
//    packet->duration =packet->flags |= isKeyFrame ? AV_PKT_FLAG_KEY : 0;av_log(ofmt_ctx, AV_LOG_INFO,"write_packet: start");int ret = av_interleaved_write_frame(ofmt_ctx, packet);printf_packet(ofmt_ctx,packet);av_packet_unref(packet);av_packet_free(&packet);av_log(ofmt_ctx, AV_LOG_INFO,"write_packet: end");return ret;}

 对于频繁处理的对象可以创建全局变量动态更换属性 而不是每次都创建 

1.3.6 结束写入尾部信息

int H2642Ts::stop() {if (!ofmt_ctx) {av_log(nullptr, AV_LOG_ERROR,"ofmt_ctx is null: Something went really wrong .\n");return -1;}av_log(ofmt_ctx, AV_LOG_INFO,"stop");av_write_trailer(ofmt_ctx);avio_closep(&ofmt_ctx->pb);avformat_free_context(ofmt_ctx);return 0;
}

相关文章:

FFmpeg 打包mediacodec 编码帧 MPEGTS

在Android平台上合成视频一般使用MediaCodec进行硬编码&#xff0c;使用MediaMuxer进行封装&#xff0c;但是因为MediaMuxer支持格式有限&#xff0c;一般会采用ffmpeg封装&#xff0c;比如监控一般使用mpeg2ts格式而非MP4,这是因为两者对帧时pts等信息封装差异导致应用场景不同…...

软件测试如何推进项目进度?

在软件研发中&#xff0c;有一种思想叫TDD&#xff0c;即测试驱动开发&#xff0c;TDD是敏捷方法中的一项核心实践&#xff0c;其原理是在开发功能代码之前&#xff0c;先编写单元测试用例代码&#xff0c;对要编写的函数或类明确测试方法后&#xff0c;再进行设计与编码。 本…...

首次尝试鸿蒙开发!

今天是我第一次尝试鸿蒙开发&#xff0c;是因为身边的学长有搞这个的&#xff0c;而我也觉得我也该拓宽一下技术栈&#xff01; 首先配置环境&#xff0c;唉~真的是非常心累&#xff0c;下载一个DevEco Studio 3.0.0.993&#xff0c;然后配置环境变量这些操作不用多说&#xff…...

前端面试题-react

1 React 中 keys 的作⽤是什么&#xff1f; Keys 是 React ⽤于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识在开发过程中&#xff0c;我们需要保证某个元素的 key 在其同级元素中具有唯⼀性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建…...

EIP-2535 Diamond standard 实用工具分享

前段时间工作对接到了这标准的协议&#xff0c;于是简单介绍下这个标准分享下方便前端er使用的调用工具 一、标准的诞生 在写复杂逻辑的solidity智能合约时&#xff0c;经常会碰到两个问题&#xff0c;升级和合约大小限制。 升级目前有几种proxy模式&#xff0c;通过delegateca…...

【LangChain】向量存储(Vector stores)

LangChain学习文档 【LangChain】向量存储(Vector stores)【LangChain】向量存储之FAISS 概要 存储和搜索非结构化数据的最常见方法之一是嵌入它并存储生成的嵌入向量&#xff0c;然后在查询时嵌入非结构化查询并检索与嵌入查询“最相似”的嵌入向量。向量存储负责存储嵌入数…...

Debian/Ubuntu 安装 Chrome 和 Chrome Driver 并使用 selenium 自动化测试

截至目前&#xff0c;Chrome 仍是最好用的浏览器&#xff0c;没有之一。Chrome 不仅是日常使用的利器&#xff0c;通过 Chrome Driver 驱动和 selenium 等工具包&#xff0c;在执行自动任务中也是一绝。相信大家对 selenium 在 Windows 的配置使用已经有所了解了&#xff0c;下…...

[SQL挖掘机] - 窗口函数 - 合计: with rollup

介绍: 在sql中&#xff0c;with rollup 是一种用于在查询结果中生成小计和总计的选项。它可以与 group by 子句一起使用&#xff0c;用于在分组查询的结果中添加附加行。 with rollup 的作用是为每个指定的分组列生成小计&#xff0c;并在最后添加一行总计。这样&#xff0c;…...

远程控制平台一之推拉流的实现

确定框架 在选用推拉流框架的时候,有了解过nginx+rtmp/rtsp,Janus,以及其他开源的推拉流框架,要么是延迟严重(延迟一分多钟),要么配置复杂,而且这些框架对于只是转发远程画面这个简单需求来说,过于庞大了。机缘巧合之下,我了解到了一个简单易用的框架,就是ZeroMQ的…...

RTT(RT-Thread)线程管理(1.2W字详细讲解)

目录 RTT线程管理 线程管理特点 线程工作机制 线程控制块 线程属性 线程状态之间切换 线程相关操作 创建和删除线程 创建线程 删除线程 动态创建线程实例 启动线程 初始化和脱离线程 初始化线程 脱离线程 静态创建线程实例 线程辅助函数 获得当前线程 让出处…...

你真的会自动化吗?Web自动化测试-PO模式实战,一文通透...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 PO模式 Page Obj…...

C# 使用堆栈实现队列

232 使用堆栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;、、、&#xff09;&#xff1a;pushpoppeekempty 实现 类&#xff1a;MyQueue void push(int x)将元素 x 推到队列的末尾 int pop()从队列的开头移除并返回元素 in…...

git操作:修改本地的地址

Windows下git如何修改本地默认下载仓库地址 - 简书 (jianshu.com) 详细解释&#xff1a; 打开终端拉取git时&#xff0c;会默认在git安装的地方&#xff0c;也就是终端前面的地址。 需要将代码 拉取到D盘的话&#xff0c;现在D盘创建好需要安放代码的文件夹&#xff0c;然后…...

【以图搜图】Python实现根据图片批量匹配(查找)相似图片

目的&#xff1a;可以解决在本地实现根据图片查找相似图片的功能 背景&#xff1a;由于需要查找别人代码保存的图像的命名&#xff0c;但由于数据集是cifa10图像又小又多&#xff0c;所以直接找很费眼睛&#xff0c;所以实现用该代码根据图像查找图像&#xff0c;从而得到保存…...

【无标题】JSP--Java的服务器页面

jsp是什么&#xff1f; jsp的全称是Java server pages,翻译过来就是java的服务器页面。 jsp有什么作用&#xff1f; jsp的主要作用是代替Servlet程序回传html页面的数据&#xff0c;因为Servlet程序回传html页面数据是一件非常繁琐的事情&#xff0c;开发成本和维护成本都非常高…...

【Linux】进程间通信——system V共享内存 | 消息队列 | 信号量

文章目录 一、system V共享内存1. 共享内存的原理2. 共享内存相关函数3. 共享内存实现通信4. 共享内存的特点 二、system V消息队列&#xff08;了解&#xff09;三、system V信号量&#xff08;信号量&#xff09; 一、system V共享内存 1. 共享内存的原理 共享内存是一种在…...

CentOS实现html转pdf

CentOS使用实现html转PDF&#xff0c;需安装以下软件&#xff1a; yum install wkhtmltopdf # 转换工具&#xff0c;将HTML文件或网页转换为PDFyum install xorg-x11-server-Xvfb # 虚拟的X服务器&#xff0c;在无图形界面环境下运行图形应用程yum install wqy-zenhei-fonts #…...

【C++】基于多设计模式下的同步异步日志系统

✍作者&#xff1a;阿润021 &#x1f4d6;专栏&#xff1a;C 文章目录 一、项目介绍二、项目实现准备工作1.日志系统技术实现策略2.相关技术知识补充2.1 不定参函数设计2.2 设计模式 三、日志项目框架设计1.模块划分2.各模块关系图 四、详细代码实现1.实用工具类设计2.日志等级…...

防火墙监控工具

防火墙监控是跟踪在高效防火墙性能中起着关键作用的重要防火墙指标&#xff0c;防火墙监控通常应包括&#xff1a; 防火墙日志监控防火墙规则监控防火墙配置监控防火墙警报监控 防火墙监控服务的一个重要方面是它应该是主动的。主动识别内部和外部安全威胁有助于在早期阶段识…...

组合模式——树形结构的处理

1、简介 1.1、概述 树形结构在软件中随处可见&#xff0c;例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等。如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题。组合模式通过一种巧妙的设计方案使得用户可以一致性地处理整个树形…...

Suno API:生成 AI 音乐的完整指南

简介 Suno API 是 Ace Data Cloud 提供的一项强大服务&#xff0c;旨在将 AI 音乐生成能力集成到您的应用程序中。借助这一稳定且全面的 RESTful API&#xff0c;您可以创建自定义歌曲、纯音乐、混音、翻唱等。本文将详细介绍如何使用 Suno API&#xff0c;并提供快速上手的指…...

基于Redis的4种延时队列实现方式及实战

什么是延时队列? 延时队列顾名思义,是指元素进入队列后,可以延时一定时间再被消费者取出执行。这与普通队列的区别在于,普通队列中的元素一旦入队就可以被立即消费,而延时队列中的元素需要等到指定时间后才能被消费。 为什么要使用Redis实现延时队列? 使用Redis实现延…...

Matlab源代码教程:枝晶生长模拟中的溶质与液相分数分析

枝晶生长模拟&#xff0c;溶质、液相分数&#xff0c;matlab源代码 教程相场法模拟枝晶生长这事挺有意思的——想象金属熔液凝固时&#xff0c;那些像雪花般绽放的晶体结构&#xff0c;背后其实是溶质扩散和相变的战场。今儿咱们用MATLAB整活&#xff0c;搞个能看见晶体长毛刺的…...

【数据结构】树的定义、核心术语与关键性质全解析

在数据结构的世界里&#xff0c;树&#xff08;Tree&#xff09; 是一种极其重要的非线性结构&#xff0c;它完美模拟了自然界中树的层次关系&#xff0c;从文件系统、组织结构&#xff0c;到算法中的二叉搜索树、堆&#xff0c;再到 AI 中的决策树&#xff0c;树的身影无处不在…...

避坑指南:微信小程序递归组件的3个常见错误(以tree组件为例)

微信小程序递归组件开发避坑指南&#xff1a;以Tree组件为例 递归组件是前端开发中处理嵌套数据结构的利器&#xff0c;但在微信小程序中实现时&#xff0c;不少开发者容易陷入一些典型陷阱。我曾在一个电商后台管理系统项目中&#xff0c;因为递归组件的状态更新问题导致整个商…...

南北阁模型新玩法:一键部署极简WebUI,体验手机短信般AI对话

南北阁模型新玩法&#xff1a;一键部署极简WebUI&#xff0c;体验手机短信般AI对话 还在用那些界面老旧、反应迟钝的AI对话工具吗&#xff1f;每次发送问题后&#xff0c;只能盯着屏幕上的加载图标干等&#xff0c;几秒甚至十几秒后才能看到一大段文字“啪”地一下弹出来&…...

计算机毕业设计:Python汽车销售数据爬虫可视化分析平台 Flask框架 requests爬虫 可视化 数据分析 大数据 机器学习 大模型(建议收藏)✅

博主介绍&#xff1a;✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战8年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ > &#x1f345;想要获取完整文章或者源码&#xff0c;或者代做&#xff0c;拉到文章底部即可与…...

如何自学使用关键字排名软件_关键字排名软件与SEO有什么关系

如何自学使用关键字排名软件_关键字排名软件与SEO有什么关系 在当今数字化时代&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;已成为每一个网站运营者必不可少的技能。其中&#xff0c;关键字排名软件扮演了极其重要的角色。如何自学使用关键字排名软件呢&#xff1f;关…...

万象视界灵坛惊艳效果展示:同一张宠物图在‘金毛犬’‘幼犬’‘户外玩耍’‘毛发蓬松’多维排序

万象视界灵坛惊艳效果展示&#xff1a;同一张宠物图在"金毛犬""幼犬""户外玩耍""毛发蓬松"多维排序 1. 效果展示开场 今天我要向大家展示万象视界灵坛这个神奇工具的实际效果。它就像一个视觉魔法师&#xff0c;能够深入理解图片中的…...

深度解析:相机、LiDAR与IMU紧耦合SLAM技术的最新进展与挑战

1. 为什么需要相机、LiDAR与IMU紧耦合&#xff1f; 想象一下你第一次玩VR游戏时的场景&#xff1a;头显里的画面随着你转头而实时变化&#xff0c;但稍有延迟就会让人头晕目眩。这正是SLAM技术要解决的核心问题——在未知环境中实时确定自身位置并构建地图。而单一传感器就像只…...