android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器

ffmpeg综合应用示例(三)——安卓手机摄像头编码 本文的示例将实现:读取安卓手机摄像头数据并使用H.264编码格式实时编码保存为flv文件。示例包含了 1、编译适用于安卓平台的ffmpeg库 2、在java中通过JNI使用ffmpeg 3、读取安卓摄像头数据并在后台线程中使用ffmpeg进行编码的基本流程 具有较强的综合性。 编译适用于安卓平台的ffmpeg库 平时我们编译ffmpeg类库都是在x86平台下,而安卓手机属于arm平台,... 阅读详情

音视频实践学习

概述

直播中推流和拉流是最核心的两个功能,之前的一篇博客记录过关于推流本地的mp4文件到推流服务器,而本节内容主要记录从相机采集数据到编码H264格式,最后通过RTMP推流出去的一个过程,为了从简单入手,本节内容暂不涉及音频的编码操作。

环境配置

操作系统:ubuntu 16.05
ndk版本:android-ndk-r16b
ffmpeg版本:ffmpeg-3.3.8

流程分析

开始实践

android相机采集的数据默认是NV21格式的,并且前置摄像头都是需要旋转270度。StartPublish主要完成一些初始化工作,并且标记transform=true,此时相机的数据才会交由EncodeBuffer来处理,完成编码H264数据并推送出去。

void H264Publisher::StartPublish() {

    //1.注册所有组件
    av_register_all();
    //2.初始化网络
    avformat_network_init();

    //3.初始化输出码流的AVFormatContext
    avformat_alloc_output_context2(&out_fmt, NULL, "flv", outputPath);
    //4.查找H.264编码器
    pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!pCodec) {
        LOGE("avcodec not found!");
        return;
    }
    //5.分配编码器并设置参数
    pCodecCtx = avcodec_alloc_context3(pCodec);
    //编码器的ID,这里是H264编码器
    pCodecCtx->codec_id = pCodec->id;
    //编码器编码的数据类型
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    //像素的格式
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    //前置摄像头需要旋转270度,宽和高要互换
    pCodecCtx->width = height;
    pCodecCtx->height = width;
    pCodecCtx->framerate = (AVRational) {fps, 1};
    pCodecCtx->time_base = (AVRational) {1, fps};
    pCodecCtx->gop_size = 50;
    pCodecCtx->max_b_frames = 0;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 50;
    pCodecCtx->bit_rate = 100 * 1024 * 8;
    pCodecCtx->level = 41;
    pCodecCtx->refs = 1;
    pCodecCtx->qcompress = 0.6;

    if (out_fmt->oformat->flags & AVFMT_GLOBALHEADER) {
        pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }

    //H.264
    AVDictionary *opts = NULL;
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
        av_dict_set(&opts, "preset", "superfast", 0);
        av_dict_set(&opts, "tune", "zerolatency", 0);
    }
    //6.打开编码器
    int result = avcodec_open2(pCodecCtx, pCodec, &opts);
    if (result < 0) {
        LOGE("open encoder failed %d", result);
        return;
    }

    //7.创建一个输出流
    pStream = avformat_new_stream(out_fmt, pCodec);
    if (!pStream) {
        LOGE("Failed allocating output outputPath");
        return;
    }
    pStream->time_base.num = 1;
    pStream->time_base.den = fps;
    pStream->codecpar->codec_tag = 0;
    if (avcodec_parameters_from_context(pStream->codecpar, pCodecCtx) < 0) {
        LOGE("Failed av codec parameters_from_context");
        return;
    }

    //8.打开网络输出流
    if (avio_open(&out_fmt->pb, outputPath, AVIO_FLAG_READ_WRITE) < 0) {
        LOGE("Failed to open output file!\n");
        return;
    }
    //9.写文件头部
    result = avformat_write_header(out_fmt, NULL);
    if (result < 0) {
        LOGE("Error occurred when opening output URL %d", result);
        return;
    }

    //初始化帧
    pFrame = av_frame_alloc();
    pFrame->width = pCodecCtx->width;
    pFrame->height = pCodecCtx->height;
    pFrame->format = pCodecCtx->pix_fmt;
    int bufferSize = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width,
                                              pCodecCtx->height, 1);
    pFrameBuffer = (uint8_t *) av_malloc(bufferSize);
    av_image_fill_arrays(pFrame->data, pFrame->linesize, pFrameBuffer, pCodecCtx->pix_fmt,
                         pCodecCtx->width, pCodecCtx->height, 1);

    //创建已编码帧
    av_new_packet(&avPacket, bufferSize * 3);

    //标记正在转换
    this->transform = true;
}

接着就是核心的编码函数:

int H264Publisher::EncodeFrame(AVCodecContext *pCodecCtx, AVFrame *pFrame, AVPacket *avPacket) {
    int ret = avcodec_send_frame(pCodecCtx, pFrame);
    if (ret < 0) {
        //failed to send frame for encoding
        return -1;
    }
    while (!ret) {
        ret = avcodec_receive_packet(pCodecCtx, avPacket);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return 0;
        } else if (ret < 0) {
            //error during encoding
            return -1;
        }

        //AVFrame PTS
        pFrame->pts = index;

        //编码数据
        avPacket->stream_index = pStream->index;
        AVRational time_base = out_fmt->streams[0]->time_base;//{ 1, 1000 };
        avPacket->pts = index * (pStream->time_base.den) / ((pStream->time_base.num) * fps);
        avPacket->dts = avPacket->pts;
        avPacket->duration = (pStream->time_base.den) / ((pStream->time_base.num) * fps);
        LOGI("Send frame index:%d,pts:%lld,dts:%lld,duration:%lld,time_base:%d,%d",
             index,
             (long long) avPacket->pts,
             (long long) avPacket->dts,
             (long long) avPacket->duration,
             time_base.num, time_base.den);
        avPacket->pos = -1;
        //写出数据
        ret = av_interleaved_write_frame(out_fmt, avPacket);
        if (ret != 0) {
            LOGE("av_interleaved_write_frame failed");
        }
        av_packet_unref(avPacket);

        index++;
    }
    return 0;
}

void H264Publisher::EncodeBuffer(unsigned char *nv21Buffer) {

    uint8_t *i420_y = pFrameBuffer;
    uint8_t *i420_u = pFrameBuffer + width * height;
    uint8_t *i420_v = pFrameBuffer + width * height * 5 / 4;

    //NV21转I420
    libyuv::ConvertToI420(nv21Buffer, width * height, i420_y, height, i420_u, height / 2, i420_v,
                          height / 2, 0, 0, width, height, width, height, libyuv::kRotate270,
                          libyuv::FOURCC_NV21);

    pFrame->data[0] = i420_y;
    pFrame->data[1] = i420_u;
    pFrame->data[2] = i420_v;

    //编码H.264
    EncodeFrame(pCodecCtx, pFrame, &avPacket);
}

我们结束推流的时候,将transform标记为false状态,然后处理完最后一帧数据,最后关闭相关的句柄,回收资源。

void H264Publisher::StopPublish() {
    //标记转换结束
    this->transform = false;

    int result = EncodeFrame(pCodecCtx, NULL, &avPacket);
    if (result >= 0) {
        //封装文件尾
        av_write_trailer(out_fmt);
        //释放内存
        if (pCodecCtx != NULL) {
            avcodec_close(pCodecCtx);
            avcodec_free_context(&pCodecCtx);
            pCodecCtx = NULL;
        }
        if (pFrame != NULL) {
            av_free(pFrame);
            pFrame = NULL;
        }
        if (pFrameBuffer != NULL) {
            av_free(pFrameBuffer);
            pFrameBuffer = NULL;
        }
        if (out_fmt != NULL) {
            avio_close(out_fmt->pb);
            avformat_free_context(out_fmt);
            out_fmt = NULL;
        }
    }
}

注意:之前也说过了,android相机默认采集的数据是NV21格式的,我们是无法直接处理的,需要先将NV21格式转换成Y420P格式的数据,并且旋转270度(后置相机旋转90度),得到最终的数据,送入编码器中进行编码成H264格式的数据,最后才通过av_interleaved_write_frame推流到服务器上去。

用ffplay播放我们的局域网直播流rtmp://192.168.1.102:1935/onzhou/live

ffplay rtmp://192.168.1.102:1935/onzhou/live

存在的问题

目前只处理了视频编码推流部分,为了更方便的了解推流过程,音频编码暂未处理,后续会有音频相关的内容。

参考:
项目地址:ffmpeg-camera-stream
https://github.com/byhook/ffmpeg4android

参考:
https://blog.youkuaiyun.com/leixiaohua1020/article/details/47056051
https://blog.youkuaiyun.com/leixiaohua1020/article/details/39803457
https://blog.youkuaiyun.com/bixinwei22/article/details/78770090

Android平台RTMP直播推流源码实战项目 看完这一整套流程,你会发现直播系统就像一台精密仪器,每个齿轮都在高速运转。从摄像头采集到GPU滤镜处理,从硬件编码到FLV封装,再到RTMP传输,任何一个环节出问题都会导致卡顿、花屏甚至中断。但这正是它的魅力所在——你不仅是在写代码,更是在构建一种实时沟通的可能性。每一次成功的推流,都是无数工程师智慧的结晶。所以,下次当你轻松说出“我开播啦”的时候,不妨想想背后这套复杂的系统正在默默为你服务 😊🎥✨本文还有配套的精品资源,点击获取。 阅读详情

相关推荐

Android使用ffmpeg4.0+实现camera和record合成mp4

使用采用camera2采集编码为mpeg4 音频采用AudioRecoder采集编码为aac编码 加入了libyuv做nv21转i420

FFmpeg之55:Android+Libx264摄像头麦克风直播推流

FFmpeg系列之55:Android+Libx264+AAC摄像头麦克风直播推流我将带领大家来系统学习Android的直播推流知识,包括但不限于:Android读取摄像头边预览边存储YUV、Android实现Libx264及编码案例实战、Nginx+RTMP直播服务器的搭建 、ffmpeg推流 、Android摄像头麦克风并直播推流案例实战、RTMP协议及librtmp开源库详解 。 --------------------------------------------------------作为音视频开发小白,需要从最基本的图片和PCM开始,循序渐进,逐步到后期的编解码、软编硬编、视频特效、直播应用等。大家在学习过程中需要多动手,多思考,建议先系统的看一遍视频,然后第二遍跟着一起练习,然后再总结分析提升。

android平台camera采集数据利用ffmpeg第三方库进行编码

该demo就是通过JNI技术调用ffmpeg接口将android平台camera采集的数据进行编码。采用ffmpeg编码是因为ffmpeg编码格式很是丰富。

基于ffmpeg模拟监控摄像头输出rtsp视频流并opencv播放

在进行视频流项目(视频监控)研发时,通常是在台式机上研发,并不能与摄像头直接进行连接,故需要使用ffmpeg将已经录制好的mp4视频推送为rtsp视频流,然后进行模拟开发。本博文涉及到ffmpeg(推流)、EasyDarwin(视频流服务器)、oepncv(代码拉流)、vlc(播放rtsp视频流)。

m0_74259636的博客 5215

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

m0_68167062的博客 2261

深入AOSP构建系统:从BUILD_FINGERPRINT的生成逻辑,看如何定制你的Android设备标识

本文深入解析AOSP构建系统中的设备指纹生成机制,详细介绍了BUILD_FINGERPRINT的生成逻辑及其在Android设备中的关键作用。通过分析多个指纹属性的区别与联系,以及如何定制设备标识,帮助开发者更好地理解和掌握Android系统构建过程。

weixin_27017211的博客 217

直播--android端推流实现二

了解本章内容之前,需要了解H.264编码原理。链接地址 H.264编码原理 上面讲到了如何将推流需要的库rtmpDump、x264集成到项目中,本节讲述视频推流实现,上一张推流的流程图: 流程图看到,首先我们摄像头采集到的数据,会通过VideoChannel.cpp将NV21数据编码成I420数据。并将I420数据按照rtmp协议规则将数据封装成packet中,将packet放入队...

qinbin2015的博客 1880

科研效率革命:Zotero与Web of Science联动实现千篇文献一键获取

本文详细介绍了如何利用Zotero与Web of Science联动实现科研文献的高效管理,包括批量导入、智能下载和自动整理千篇文献的技巧。通过精准检索、RIS文件导出和Zotero插件设置,科研人员可以大幅提升文献获取和管理的效率,特别适合开题报告和综述写作等场景。

weixin_29219539的博客 253

Android FFmpeg Camera2 推流直播

前言自己花了点时间实现了一个使用FFmpeg将Camera2视频数据推送到RTMP服务的简单Demo,在这里分享下,里面用到知识很多都是之前博客中用到的,难度不大。

金戈鐡馬 2403

解决ublox无法定位问题(一直输出$GxTXT)

最近开始研究有关RTK技术的项目,于是先购买了两块ublox公司的NEO-M8T-0型号芯片。(注意NEO-M8T还细分为NEO-M8T-0和NEO-M8T-2,前者不支持RTCM输出,后者支持RTCM输出) 首先都是按常规步骤,直接把芯片模块串口接电脑调试,这边推荐是用官网的u-center调试软件。GPS芯片大多数都是上电后串口自动发送定位等信息,这两块芯片有一块正常情...

众缈之门的博客 7359

Yolov2 训练时anchor是如何使用的?build_target

0.简略描述 1.将真值检测框变换到特征图尺度上 2.计算以特征图每个网格中心为中心点时所有锚框与真值检测框的交并比 3.取与真值检测框交并比最大的锚框的id,及真值检测框中心所在cell的id,有此,可将真值检测框与预测结果对应起来 4.计算位置回归损失,置信度损失,分类损失 1,网络的输出 图1 yolov2 网络对于检测框位置大小的输出是: 对于1个batch网络的输出的shape为[batch_size, H*W*num_anchors, 5+num_classes], 其中: num_

机器人视觉感知 2817

Android 使用ffmpeg软编码 将摄像头采集视频编码成视频文件

Android 适用ffmpeg软编码 将摄像头采集视频编码成视频文件。 这次代码实现的是视频采集的功能,Android 通过jni 调用ffmpeg 编码yuv数据变成视频文件。 先上代码: //编码器上下文保存的实体 struct EnCodeBean { FILE *f; AVFrame *frame; AVPacket *pkt; AVCodecContext *c = NULL; int width=0; int height=0; }; EnC

一航jason 1650

OpenClaw进阶实战(十二):电商比价工作流(一)——数据采集与竞品监控

本文介绍了基于OpenClaw的电商比价系统实现方案。该系统可自动化监控淘宝、京东、拼多多等平台的竞品价格波动,包含以下核心功能: 多平台价格采集:通过各平台官方API(淘宝客、京东联盟、多多进宝)获取商品价格,并提供模拟数据模式便于测试 数据存储与分析:使用SQLite存储历史价格数据,支持查询最近价格和价格变化趋势 模块化设计:采用技能化架构,分离了平台适配层(淘宝/京东/拼多多)、数据存储层和核心业务逻辑 扩展性:预留了价格波动告警接口(钉钉通知),为后续动态调价提供数据支撑 系统特点: 支持真实AP

PeterPan的博客 214

android4.0 USB Camera实例(六)ffmpeg mpeg编码

前面本来说是做h264编码的 研究了两天发现ffmpeg里的h264编码似乎是要信赖第三方库x264 还是怎么简单怎么来吧所以就整了个mpeg编码 ffmpeg移植前面我有一篇ffmpeg解码里已经给了 具体链接在这http://blog.csdn.net/hclydao/article/details/18546757 怎么使用那里面也已经说了 这里主要是通过ffmpeg将yuv422格式转换...

hclydao的专栏 7769

45 - Android系统源码-crosvm 实战:基于 KVM 的 Rust 安全虚拟机监控器核心技术

摘要:crosvm 是 Google 开发的基于 KVM 的轻量级虚拟机监控器(VMM),采用 Rust 语言编写,专为 ChromeOS 和 Android 设计。其核心特点包括: 安全架构:通过 Rust 的内存安全特性和多进程隔离(每个设备运行在独立沙箱进程)大幅降低攻击面 性能优化:基于 KVM 的硬件虚拟化支持,结合异步 I/O (io_uring/epoll) 实现高效设备模拟 模块化设计:清晰的 trait 抽象层(Hypervisor/Vm/Vcpu)支持跨平台扩展 Android 集成

保持专注,保持热爱 225

NVIDIA Isaac GR00T N1 人形机器人强化学习入门教程(二)详细步骤

LeRobot 格式 1.1 加载数据集 要加载数据集,我们需要定义以下 3 项内容: 在数据中显示图像帧 我们还可以对 LeRobotSingleDataset 类中的数据进行一系列转换。下面展示了如何对数据进行转换。 现在看看应用转换后的数据有何不同。 例如,对状态和操作进行规范化和串联,对视频图像进行裁剪、调整大小和颜色抖动。 二、GR00T 推断 本教程介绍如何使用 GR00

2506_90492529的博客 3012

Android 引入FFmpeg 读取RTSP流 解封装获取H264原始数据

之前 写了Android中怎么引入FFMmpeg的例子 。 本编文章将会写一个简单的demo实现ffmpeg拉去rtsp流并在界面中打印前五个字节 实际效果下图: 看下目录结构: 很简单 应用进去之后有一个主界面MainActivity 主界面有一个按钮点击一下进入一个新的界面会显示解封装的h264数据的包的大小 和前5个字节 MainActivity内容如下: sdfsdf package com.qmcy.demux; import androidx.appcomp..

baoecit的博客 7696

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

FFmpeg简单使用:视频编码 ---- YUV转H264

tune是x264中重要性仅次于preset的选项,它是视觉优化的参数,tune可以理解为视频偏好(或者视频类型),tune不是⼀个单⼀的参数,⽽是由⼀组参数构成-tune来改变参数设置。⼀个编码速度稍慢的预设会提供更⾼的压缩效率(压缩效率是以⽂件⼤⼩来衡量的)。这就是说,假如你想得到⼀个指定⼤⼩的⽂件或者采⽤恒定⽐特率编码模式,你可以采⽤⼀个较慢的预设来获得更好的质量。从本地读取YUV数据编码为h264格式的数据,然后再存⼊到本地,编码后的数据有带startcode。获取到编码后的AVPacket数据。

huabiaochen的博客 4572

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

android ffmpeg推拉流 ffmpeg推流rtp

非AMD模块输出,将非标准的AMD模块"垫"成可用的模块,例如:在老版本的jquery中,是没有继承AMD规范的,所以不能直接require["jquery"],这时候就需要shim,比如我要是用老版本jquery类库,但是他并没有实现AMD规范,那我们可以这样配置。如果一个服务端实现了WebRTC客户端的能力,那么它也可以被认为是一个Peer,与用户浏览器的WebRTC客户端创建连接,获得客户端推送过来的媒体数据,就完成了Peer to Server的转换。该文件可以用于该视频流的播放。

liveweb888的博客 852

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

android平台Camera采集数据ffmpeg进行编码探究

在PC机上,Camera采集数据利用ffmpeg进行编码往往没有任何问题,但是到了android平台或多或少会出现一系列问题,下面就针对这些问题总结一下。   1、在PC和android平台最大的差别就是架构不一样,这就是导致出现一系列问题的“罪魁祸首”的原因:    android端采用的精简指令导致编码运算时间远远长于PC机。   2、不管是本地编码存储还是编码之后发送到后台直播,这些都

此时此刻,走走停停 1907

android ffmpeg编码推流,Android音视频(三)FFmpeg Camera2推流直播

自己花了点时间实现了一个使用FFmpeg将Camera2视频数据推送到RTMP服务的简单Demo,在这里分享下,里面用到知识很多都是之前博客中用到的,难度不大。1、 定义方法定义了三个JNI方法public class FFmpegHandler {private FFmpegHandler() {}private static class SingletonInstance {private s...

weixin_39978444的博客 702

android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件

首先使用av_register_all()函数注册所有的编码器和复用器(理解为格式封装器)。该步骤必须放在所有ffmpeg代码前第一个执行 2、avformat_alloc_output_context2():初始化包含有输出码流(AVStream)和解复用器(AVInputFormat)的AVFormatContext 3、avio_open( )打开输出文件 ...

handy周 4097

Android音视频开发 -> ffmpeg+h264+avfilter+camera推流旋转角度

大体思路 ffmpeg推流相关组件初始化 avFilter初始化 264编码器初始化 接收摄像头的实时数据开始推流,这个数据格式需要转换摄像头是NV21的需要转换成YUV420P 公共变量 //解封装全局上下文 AVFormatContext *avFormatContext; //流通道,存储音视频流 AVStream *video_st; //解码信息上下文 AVCodecContext *pCodecCtx; //解码器返回指针对象 AVCodec *pCodec; //解码前数据结构体 AVPa

aagjkgluhgyy的博客 1118

SPSS中字符串转为数值后数据消失的解决办法

问题描述 今天在做SPSS数据分析的过程中,在EXCEL中复制数据到SPSS后类型为字符串,考虑到数据可能以文本形式存储,故先在EXCEL中选择性粘贴为数值,再复制到SPSS中,结果仍为字符串格式,并且在SPSS中将列的数据类型设置为数值格式后数据消失。 原因及解决办法 原数据为小数数据,录入时小数点与数字之间存在空格,在EXCEL中通过查找替换把空格替换掉再复制到SPSS中即为数值类型了。 总结 在复制到SPSS中识别为字符串类型后,首先应该检查原数据是否为文本类型,或者存在隐藏的文本字符,如本问题中的空

lazyn的博客 1万+

Android视频编码器(1)——CameraYUV送给ffmpeg进行软编码,保存为h264

http://blog.csdn.net/a992036795/article/details/54020491 一、目的 在Android上使用FFmpeg将摄像头采集的YUV裸流编码为h264。 二、环境准备 1、使用FFmpeg动态库(这个动态库需要有libx264的实现,否则可能会出现寻找编码器失败异常)。关于如何获得这样的一个动态库可以参考http://blog.csdn.net/a99...

weixin_30781775的博客 355

Android视频编码器

一、目的 在Android上使用FFmpeg将摄像头采集的YUV裸流编码为h264。 二、环境准备 1、使用FFmpeg动态库(这个动态库需要有libx264的实现,否则可能会出现寻找编码器失败异常)。关于如何获得这样的一个动态库可以参考http://blog.csdn.net/a992036795/article/details/53941436 2、Android开发环境(我用的是And

BlueBerry的专栏 4384
上一篇: android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件
下一篇: android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器
handy周
handy周 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄14年 507粉丝 240原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值