ffmpeg录制直播流_流媒体网站开发

ffmpeg录制直播流_流媒体网站开发FFmpeg 开发系列连载: FFmpeg 开发(01):FFmpeg 编译和集成 FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放 FFmpeg 开发(03

该文章首发于微信公众号:字节流动

FFmpeg 开发系列连载:

前面 FFmpeg 系列的文章中,已经实现了音视频的播放、录制已经添加滤镜等功能,本文将用 FFmpeg 实现流媒体的边播放边录制功能。

FFmpeg 实现流媒体的边播放边录制功能

流媒体

流媒体(英语:Streaming media)是指将一连串的多媒体资料压缩后,经过互联网分段发送资料,在互联网上即时传输影音以供观赏的一种技术与过程,此技术使得资料数据包得以像流水一样发送,如果不使用此技术,就必须在使用前下载整个媒体文件。

流媒体在播放前并不下载整个文件,只将开始部分内容存入内存,流式媒体的数据流随时传送随时播放,只是在开始时有一些延迟。

流媒体实现的关键技术就是流式传输,流式传输分为:实时流和顺序流。

顺序流传输是顺序下载,在下载文件的同时用户可观看在线媒体,在给定时刻,用户只能观看已下载的那部分,而不能跳到还未下载的前头部分,在传输期间不根据用户连接的速度对下载顺序做调整。

实时流传输指保证媒体信号带宽与网络连接匹配,使媒体可被实时观看到,实时流式传输根据网络情况调整输出音视频的质量从而实现媒体的持续的实时传送,用户可快进或后退以观看前面或后面的内容。

FFmpeg 播放流媒体

FFmpeg 中对影音数据的处理,可以划分为协议层、容器层、编码层与原始数据层四个层次:

协议层:提供网络协议收发功能,可以接收或推送含封装格式的媒体流。

协议层由 libavformat 库及第三方库(如 librtmp)提供支持。

容器层:处理各种封装格式 (MP4,FLV 等)。容器层由 libavformat 库提供支持。

编码层:处理音视频编码及解码。 编码层由各种丰富的编解码器(libavcodec 库及第三方编解码库(如 libx264))提供支持。

原始数据层:处理未编码的原始音视频帧。

FFmpeg 中 libavformat 库提供了丰富的协议处理及封装格式处理功能,在打开输入/输出时,FFmpeg 会根据 输入 URL / 输出 URL 探测输入/输出格式,选择合适的协议和封装格式。

例如,如果输出 URL 是 “rtmp://122.125.10.22/live”,那么 FFmpeg 打开输出时,会确定使用 rtmp 协议,封装格式为 flv。

FFmpeg 中打开输入/输出的内部处理细节用户不必关注,不同之处主要在于输入/输出 URL 形式不同,若 URL 携带 “rtmp://”、”rpt://”、”udp://”等前缀,则表示涉及流处理;否则,处理的是本地文件。

由于 FFmpeg 对不同的传输协议进行了封装,使用 FFmpeg 播放流媒体和播放本地文件,流程上没有区别(针对 FFmpeg 4.2.2 以上版本)。

 FFmpeg 播放流媒体

FFmpeg 边播放边录制

FFmpeg 边播放边录制有两种实现方式:

  • 在解复用时获取到编码数据包,然后重新复用打包;
  • 在解码后获取到原始数据,然后对原始数据处理(如添加滤镜),最后对处理后的数据进行编码打包。

本文采用对解码后的原始数据进行重新编码的方式,实现边播放边录制。

视频录制,这里我们可以直接使用在前文 FFmpeg 视频录制中定义的类,启动录制之后,只需要不断往里面塞视频帧即可。

class SingleVideoRecorder {
public:
    SingleVideoRecorder(const char* outUrl, int frameWidth, int frameHeight, long bitRate, int fps);
    ~SingleVideoRecorder();

	//开始录制
    int StartRecord();
	//接收视频数据
    int OnFrame2Encode(VideoFrame *inputFrame);
	//停止录制
    int StopRecord();

private:
	...
};

然后在解码类中进行录制。

/** * * Created by 公众号:字节流动 on 2021/3/16. * https://github.com/githubhaohao/LearnFFmpeg * 最新文章首发于公众号:字节流动,有疑问或者技术交流可以添加微信 Byte-Flow ,领取视频教程, 拉你进技术交流群 * * */

#include "VideoDecoder.h"

void VideoDecoder::OnDecoderReady() {
    LOGCATE("VideoDecoder::OnDecoderReady");
    m_VideoWidth = GetCodecContext()->width;
    m_VideoHeight = GetCodecContext()->height;

    if(m_VideoRender != nullptr) {
        int dstSize[2] = {0};
        m_VideoRender->Init(m_VideoWidth, m_VideoHeight, dstSize);
        m_RenderWidth = dstSize[0];
        m_RenderHeight = dstSize[1];

		//初始化
        int fps = 25;
        long videoBitRate = m_RenderWidth * m_RenderHeight * fps * 0.2;
        m_pVideoRecorder = new SingleVideoRecorder("/sdcard/learnffmpeg_output.mp4", m_RenderWidth, m_RenderHeight, videoBitRate, fps);
		//开始录制
        m_pVideoRecorder->StartRecord();

    } else {
        LOGCATE("VideoDecoder::OnDecoderReady m_VideoRender == null");
    }
}

void VideoDecoder::OnDecoderDone() {
    LOGCATE("VideoDecoder::OnDecoderDone");

    if(m_VideoRender)
        m_VideoRender->UnInit();

	//视频播放结束,停止录制
    if(m_pVideoRecorder != nullptr) {
        m_pVideoRecorder->StopRecord();
        delete m_pVideoRecorder;
        m_pVideoRecorder = nullptr;
    }

}

void VideoDecoder::OnFrameAvailable(AVFrame *frame) {
    LOGCATE("VideoDecoder::OnFrameAvailable frame=%p", frame);
    if(m_VideoRender != nullptr && frame != nullptr) {
        NativeImage image;
        sws_scale(m_SwsContext, frame->data, frame->linesize, 0,
                  m_VideoHeight, m_RGBAFrame->data, m_RGBAFrame->linesize);
        image.format = IMAGE_FORMAT_RGBA;
        image.width = m_RenderWidth;
        image.height = m_RenderHeight;
        image.ppPlane[0] = m_RGBAFrame->data[0];
        image.pLineSize[0] = image.width * 4;

        m_VideoRender->RenderVideoFrame(&image);

		//不断塞视频帧
        if(m_pVideoRecorder != nullptr) {
            m_pVideoRecorder->OnFrame2Encode(&image);
        }
    }

}

完整的实现代码可以参考项目:

https://github.com/githubhaohao/LearnFFmpeg

参考文章

zh.wikipedia.org/wiki/%E6%B5…

zhuanlan.zhihu.com/p/29567587

www.cnblogs.com/leisure_chn…

实现代码路径

LearnFFmpeg

技术交流

技术交流/获取视频教程可以添加我的微信:Byte-Flow

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13567.html

(0)

相关推荐

  • js的面试题及答案_js面试题大汇总

    js的面试题及答案_js面试题大汇总本文针对目前常见的面试题,仅提供了相应的核心原理及思路,部分边界细节未处理。后续会持续更新,希望对你有所帮助。 1. 实现一个call函数 2. 实现一个apply函数 3. 实现一个bind函数 4. instanceof的原理 5. Object.create的基本实现原理…

    2023-07-23
    125
  • WIKI·常用数据库默认端口

    WIKI·常用数据库默认端口阅文时长 | 0.05分钟 字数统计 | 96字符 主要内容 | 1、引言&背景 2、声明与参考资料 『WIKI·常用数据库默认端口』 编写人 | SCscHero 编写时间 | 2

    2023-04-18
    149
  • 共享存储服务nfs[通俗易懂]

    共享存储服务nfs[通俗易懂]NFS客户端挂载目录上创建、删除、查看数据操作,等价于在服务端进行的创建、删除、查看数据操作。NFS网络文件系统重点要了解两个重要服务。

    2023-07-04
    113
  • 实战python:15个项目助力你成为高级开发者

    实战python:15个项目助力你成为高级开发者Python是一种直观、易于学习的编程语言,非常适合初学者。它的开放源代码和丰富的库、框架、工具使得它成为各种领域的开发人员的首选语言。如果您已经具备一些编程基础,那么挑战一些实际项目会有助于提高您的技能和知识,以成为高级开发者。下面我们将介绍15个实战项目,帮助您从新手迈向高手。

    2024-03-07
    66
  • 高考志愿代码是什么?高考志愿代码大全2022(完整版)[通俗易懂]

    高考志愿代码是什么?高考志愿代码大全2022(完整版)[通俗易懂]现在就快要进入紧张的高考志愿阶段了,这段时间各位朋友的问题层出不穷,还有很多朋友不知道志愿代码是什么,下面我给大家整理了一下2022年最新的志愿

    2022-12-14
    211
  • mysql innodb引擎中varchar和char的区别[亲测有效]

    mysql innodb引擎中varchar和char的区别[亲测有效]
    一. 行记录格式 innodb在存储数据的时候是以行的形式存储的,版本相关,并且有固定的格式。可以通过下面的语句查询当前所用版本的行记录格式: show t…

    2023-04-08
    148
  • mysql 注入漏洞_sql渗透

    mysql 注入漏洞_sql渗透函数名称 函数功能 函数名称 函数功能 system_user() 系统名称 concat() 没有分隔符的连接 user() 用户名 concat_ws() 含有分隔符的连接字符串 current_

    2023-04-15
    144
  • mysql数据库监控工具navicat-monitor

    mysql数据库监控工具navicat-monitorhttps://www.navicat.com.cn/download/navicat-monitor 下载 Navicat Monitor 2 Windows | macOS | macOS Ho…

    2023-02-09
    136

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注