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

IGS精密星历高效获取与解析指南

本文详细介绍了IGS精密星历在高精度定位中的核心作用,对比了IGS Final、IGR和IGU等不同产品的精度与时效性差异。指南提供了从武汉大学、NASA CDDIS等数据中心高效下载数据的实战方法,并深入解析了SP3文件格式,帮助用户掌握精密星历的获取、解析与自动化处理技巧,以支持厘米级定位应用。

garlic的博客 839

李宏毅机器学习笔记第17周_概述领域自适应(Domain Adaptation)

Domain Adaptation

peaunt1的博客 1104

直播--android端推流实现二

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

qinbin2015的博客 1880

k最近邻算法——电影分类及案例详解

如图3-7所示,若将国际象棋棋盘放在二维直角坐标系中,格子的边长定义为1,坐标的x轴及y轴和棋盘方格平行,原点恰落在某一格的中心点,则王从一个位置走到其他位置需要的步数恰为二个位置的切比雪夫距离,因此切比雪夫距离也称为棋盘距离。关于这里的距离度量,这个电影分类的例子有2个特征,也就是2维的,可以使用我们高中学过的两点距离公式(两点距离公式其实就是欧氏距离在二维空间上的公式,也就是欧氏距离n的值为2的情况,欧氏距离也称为欧几里德距离)来计算距离,如图3-3所示。对数据进行归一化,避免量纲对计算距离的影响。

ZhShy 4557

Android FFmpeg Camera2 推流直播

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

金戈鐡馬 2403

SpringBoot集成SpringSecurity5和OAuth2 — 2、OAuth2客户端【SSO单点登录时作为客户端】

SpringBoot集成SpringSecurity5和OAuth2 — 2、OAuth2客户端【SSO单点登录时作为客户端】

Mr_XiMu的博客 3742

告别环境配置噩梦:用Docker Desktop + WSL2在Windows上5分钟搞定vLLM运行环境

本文详细介绍了如何在Windows系统上使用Docker Desktop和WSL2快速搭建vLLM开发环境,5分钟内完成配置。通过容器化技术解决传统环境配置中的依赖冲突、环境污染等问题,提供一键复用的高效方案,特别适合AI开发者快速部署和测试vLLM框架。

weixin_28728495的博客 502

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

Docker一键部署Synopsys EDA全家桶(VCS/Verdi 2018版)保姆级教程

本文提供了一份详尽的Docker容器化部署Synopsys EDA工具链(VCS/Verdi 2018版)的保姆级教程。通过环境隔离与标准化,解决传统安装的依赖冲突问题,并重点攻克了容器内GUI图形界面显示的核心难题。教程涵盖从Docker基础安装、镜像获取、容器启动配置,到使用X11转发或VNC实现Verdi完美显示的完整实践,旨在帮助芯片设计工程师快速构建稳定、可移植的一键化EDA工作环境。

weixin_29281941的博客 198

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

cesium 实现经纬线绘制和经纬度标注

废话不对数直接上线效果图及代码 1.效果图 2.实现代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=

一碗老面的博客 2569

OpenClaw插件配置错误修复完全指南

插件文件名是插件目录结构正确package.json配置正确OpenClaw的插件系统还在快速迭代,偶尔会遇到这种配置问题。看错误信息检查配置文件检查实际文件对比差异修复问题大部分问题都能解决。查看官方文档:https://openclaw.ai/docs提Issue:https://github.com/openclaw/openclaw/issues加入Discord社区求助快速修复命令汇总。

Maynor的博客 4094

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

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

baoecit的博客 7696

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

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

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

huabiaochen的博客 4572

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

android ffmpeg推拉流 ffmpeg推流rtp

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

liveweb888的博客 852

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

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+h264+avfilter+camera推流旋转角度

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

aagjkgluhgyy的博客 1118

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

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

handy周 4097

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

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

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

lazyn的博客 1万+

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、付费专栏及课程。

余额充值