FFmpeg入门 - rtmp推流

2023-05-16

FFmpeg入门 - 视频播放_音视频开发老马的博客-CSDN博客介绍了怎样用ffmpeg去播放视频.

里面用于打开视频流的avformat_open_input函数除了打开本地视频之外,实际上也能打开rtmp协议的远程视频,实现拉流:


./demo -p 本地视频路径
​
./demo -p rtmp://服务器ip/视频流路径  

这篇文章我们来讲下怎样实现推流,然后和之前的demo代码配合就能完成推流、拉流的整个过程,实现直播。

rtmp服务器

整个直播的功能分成下面三个模块:

截屏2022-09-08 下午9.49.50.png

从上图我们可以看到rtmp是需要服务器做转发的,我们选用开源的srs.直接从github上把它的源码拉下来编译,然后直接启动即可:

git clone git@github.com:ossrs/srs.git
cd srs/trunk
./configure
make
./etc/init.d/srs start

如果是本地的电脑,这个时候就能在局域网内直接用它的内网ip去访问了.但如果是腾讯云、阿里云之类的云服务器还需要配置安全组开放下面几个端口的访问权限:

listen              1935;
max_connections     1000;
#srs_log_tank        file;
#srs_log_file        ./objs/srs.log;
daemon              on;
http_api {
    enabled         on;
    listen          1985;
}
http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}
rtc_server {
    enabled on;
    listen 8000; # UDP port
    # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
    candidate $CANDIDATE;
}
...

当然如果这几个端口已经被占用的话可以修改配置文件conf/srs.conf去修改

服务器到这里就准备好了,浏览器访问下面网址对srs进行调试、配置:

http://服务器ip:8080/players/rtc_publisher.html http://服务器ip:1985/console/ng_index.html

推流

文末名片免费领取音视频开发学习资料,内容包括(C/C++,Linux 服务器开发,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

准备输出流

我们选择推送本地的视频到rtmp服务器,所以第一步仍然是打开本地视频流:

bool VideoSender::Send(const string& srcUrl, const string& destUrl) {
    ...
    // 打开文件流读取文件头解析出视频信息如轨道信息、时长等
    // mFormatContext初始化为NULL,如果打开成功,它会被设置成非NULL的值
    // 这个方法实际可以打开多种来源的数据,url可以是本地路径、rtmp地址等
    // 在不需要的时候通过avformat_close_input关闭文件流
    if(avformat_open_input(&inputFormatContext, srcUrl.c_str(), NULL, NULL) < 0) {
        cout << "open " << srcUrl << " failed" << endl;
        break;
    }
​
    // 对于没有文件头的格式如MPEG或者H264裸流等,可以通过这个函数解析前几帧得到视频的信息
    if(avformat_find_stream_info(inputFormatContext, NULL) < 0) {
        cout << "can't find stream info in " << srcUrl << endl;
        break;
    }
​
    // 打印输入视频信息
    av_dump_format(inputFormatContext, 0, srcUrl.c_str(), 0);
    ...
}

本地视频打开之后,我们创建输出视频流上下文,然后为输出流创建轨道,最后打开输出视频流:

// 创建输出流上下文,outputFormatContext初始化为NULL,如果打开成功,它会被设置成非NULL的值,在不需要的时候使用avformat_free_context释放
// 输出流使用flv格式
if(avformat_alloc_output_context2(&outputFormatContext, NULL, "flv", destUrl.c_str()) < 0) {
    cout << "can't alloc output context for " << destUrl << endl;
    break;
}
​
// 拷贝编解码参数
if(!createOutputStreams(inputFormatContext, outputFormatContext)) {
    break;
}
​
// 打印输出视频信息
av_dump_format(outputFormatContext, 0, destUrl.c_str(), 1);
​
// 打开输出流,结束的时候使用avio_close关闭
if(avio_open(&outputFormatContext->pb, destUrl.c_str(), AVIO_FLAG_WRITE) < 0) {
    cout << "can't open avio " << destUrl << endl;
    break;
}

这里有个createOutputStreams用于根据本地视频文件的轨道信息,为输出流创建同样的轨道:

static bool createOutputStreams(AVFormatContext* inputFormatContext, AVFormatContext* outputFormatContext) {
    // 遍历输入流的所有轨道,拷贝编解码参数到输出流
    for(int i = 0 ; i < inputFormatContext->nb_streams ; i++) {
        // 为输出流创建轨道
        AVStream* stream = avformat_new_stream(outputFormatContext, NULL);
        if(NULL == stream) {
            cout << "can't create stream, index " << i << endl;
            return false;
        }
​
        // 编解码参数在AVCodecParameters中保存,从输入流拷贝到输出流
        if(avcodec_parameters_copy(stream->codecpar, inputFormatContext->streams[i]->codecpar) < 0) {
            cout << "can't copy codec paramters, stream index " << i << endl;
            return false;
        }
​
        // codec_tag代表了音视频数据采用的码流格式,不同的封装格式如flv、mp4等的支持情况是不一样的
        // 上面的avcodec_parameters_copy将输出流的codec_tag从输入拷贝过来变成了一样的
        // 由于我们输出流在avformat_alloc_output_context2的时候写死了flv格式
        // 如果输入流不是flv而是mp4等格式的话就可能会出现mp4里某种codec_tag在flv不支持导致推流失败的情况
        // 这里我们可以用av_codec_get_id从输出流的oformat的支持的codec_tag列表里面查找codec_id
        // 如果和codecpar的codec_id不一致的话代表不支持
        if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != stream->codecpar->codec_id) {
            // 这里将codec_tag设置为0,FFmpeg会根据编码codec_id从封装格式的codec_tag列表中找到一个codec_tag
            stream->codecpar->codec_tag = 0;
        }
    }
    return true;
}

codec_id和codec_tag

这里可以看到对于编码器有codec_id和codec_tag两个字段去描述,codec_id代表的是数据的编码类型.而codec_tag用于更详细的描述编解码的格式信息,它对应的是FourCC(Four-Character Codes)数据。

例如codec_id都是AV_CODEC_ID_RAWVIDEO的裸数据,但它可能是YUV的裸数据也可能是RGB的裸数据:

// libavformat/isom.c
{ AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* uncompressed RGB */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* uncompressed YUV422 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* uncompressed 8-bit 4:2:2 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2VUY but byte-swapped */

又例如codec_id都是AV_CODEC_ID_H264,但实际上也有许多细分类型:

// libavformat/isom.c
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '2') },
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') },
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '4') },
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'p') }, /* AVC-Intra  50M 720p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'q') }, /* AVC-Intra  50M 720p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '2') }, /* AVC-Intra  50M 1080p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '3') }, /* AVC-Intra  50M 1080p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '5') }, /* AVC-Intra  50M 1080i50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '6') }, /* AVC-Intra  50M 1080i60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'p') }, /* AVC-Intra 100M 720p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'q') }, /* AVC-Intra 100M 720p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '2') }, /* AVC-Intra 100M 1080p25/50 */

可以看出来codec_tag是通过4个字母去表示的,我们来看看MKTAG的定义:


#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))  

最终它得到的是一个整数,例如MKTAG('a', 'v', 'c', '1')得到的值是0x31637661

  • 0x31 =1

  • 0x63 = c

  • 0x76 = v

  • 0x61 = a

我们可以用av_fourcc2str这个函数将最终的整数转换回字符串

回过头来看看这个判断:

if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != stream->codecpar->codec_id)

大部分情况下如果codec_tag在输出流不支持的情况下av_codec_get_id拿到的是AV_CODEC_ID_NONE,所以大部分情况可以等价于:

if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != AV_CODEC_ID_NONE)

不过也存在都是MKTAG('l', 'p', 'c', 'm'),但codec_id可能是AV_CODEC_ID_PCM_S16BE或者AV_CODEC_ID_PCM_S16LE的情况:

{ AV_CODEC_ID_PCM_S16BE,       MKTAG('l', 'p', 'c', 'm') },
{ AV_CODEC_ID_PCM_S16LE,       MKTAG('l', 'p', 'c', 'm') },

所以最好还是和原本的codec_id做比较会靠谱点。

写入视频数据

接着就是视频数据的写入了,主要有三个步骤,写入文件头、读取本地视频包并写入输出视频流、写入文件结尾:

// 设置flvflags为no_duration_filesize用于解决下面的报错
// [flv @ 0x14f808e00] Failed to update header with correct duration.
// [flv @ 0x14f808e00] Failed to update header with correct filesize
AVDictionary * opts = NULL;
av_dict_set(&opts, "flvflags", "no_duration_filesize", 0);
if(avformat_write_header(outputFormatContext, opts ? &opts : NULL) < 0) {
    cout << "write header to " << destUrl << " failed" << endl;
    break;
}
​
// 创建创建AVPacket接收数据包
// 无论是压缩的音频流还是压缩的视频流,都是由一个个数据包组成的
// 解码的过程实际就是从文件流中读取一个个数据包传给解码器去解码
// 对于视频,它通常应包含一个压缩帧
// 对于音频,它可能是一段压缩音频、包含多个压缩帧
// 在不需要的时候可以通过av_packet_free释放
packet = av_packet_alloc();
if(NULL == packet) {
    cout << "can't alloc packet" << endl;
    break;
}
​
...
​
// 从文件流里面读取出数据包,这里的数据包是编解码层的压缩数据
while(av_read_frame(inputFormatContext, packet) >= 0) {
    // 我们以视频轨道为基准去同步时间
    // 如果时间还没有到就添加延迟,避免向服务器推流速度过快
    ...
​
    // 往输出流写入数据
    av_interleaved_write_frame(outputFormatContext, packet);
​
    // 写入成之后压缩数据包的数据就不需要了,将它释放
    av_packet_unref(packet);
}
​
// 写入视频尾部信息
av_write_trailer(outputFormatContext);

帧同步

由于av_read_frame这里读取出来的是未解码的压缩数据速度很快,如果不做控制一下子就发送完成了,会造成数据堆积在服务器上。这里我们忽略网络传输耗时,依然通过视频包的pts做一定的同步:

while(av_read_frame(inputFormatContext, packet) >= 0) {
    // 我们以视频轨道为基准去同步时间
    // 如果时间还没有到就添加延迟,避免向服务器推流速度过快
    if(videoStreamIndex == packet->stream_index) {
        if(AV_NOPTS_VALUE == packet->pts) {
            // 有些视频流不带pts数据,按30fps将间隔统一成32ms
            av_usleep(32000);
        } else {
            // 带pts数据的视频流,我们计算出每一帧应该在什么时候播放
            int64_t nowTime = av_gettime() - startTime;
            int64_t pts = packet->pts * 1000 * 1000 * timeBaseFloat;
            if(pts > nowTime) {
                av_usleep(pts - nowTime);
            }
        }
    }
    // 往输出流写入数据
    av_interleaved_write_frame(outputFormatContext, packet);
​
    // 写入成之后压缩数据包的数据就不需要了,将它释放
    av_packet_unref(packet);
}

资源释放

等视频流读写完成之后就是最后的资源释放收尾工作了:

if(NULL != packet) {
    av_packet_free(&packet);
}
​
if(NULL != outputFormatContext) {
    if(NULL != outputFormatContext->pb) {
        avio_close(outputFormatContext->pb);
    }
    avformat_free_context(outputFormatContext);
}
​
if(NULL != inputFormatContext) {
    avformat_close_input(&inputFormatContext);
}

其他

源码和上篇博客的是同一个仓库,编译之后可以通过-s参数推流到服务器:

./demo -s video.flv rtmp://服务器ip/live/livestream

推流的同时就能使用-p参数去拉流进行实时播放:

./demo -p rtmp://服务器ip/live/livestream

这个demo只是简单的将本地视频文件推到服务器,实际上我们可以对他做些修改就能实现将摄像头的视频流推到服务器了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FFmpeg入门 - rtmp推流 的相关文章

  • 有没有办法在转码之前使用 ffmpeg 确定文件的编码?

    我计划使用 ffmpeg 确保上传到我网站的所有视频文件都编码为 mp4 h264 我不想自动处理每个文件 而是希望通过仅处理那些还不是 mp4 h264 的文件来最小化处理开销 有没有一种简单的方法可以使用 ffmpeg 或其他命令行实用
  • 从视频或音频文件中删除人声

    有没有一种方法可以从音频 视频中删除人声 所以最终音乐就留在上面了 我想使用任何软件 如 adobe 等 或使用命令行 如 ffmpeg sox 来执行此操作 但我更喜欢命令行来轻松调整设置 我从事卡拉 OK 工作有一段时间了 没有办法可靠
  • FFmpeg - 找不到 ExecuteBinaryResponseHandler - Android/Java

    我正在尝试制作一个用于反应原生的模块 它将把视频变成 gif 我对 android studios java 几乎没有经验 但我很想了解更多 我在用这个图书馆 https github com WritingMinds ffmpeg and
  • Android 上的 FFmpeg

    我已经在 Android 上编译了 FFmpeg libffmpeg so 现在我必须构建一个像 RockPlayer 这样的应用程序 或者使用现有的 Android 多媒体框架来调用 FFmpeg 您有在 Android StageFri
  • FFMPEG - 连续的非单调 DTS

    我有几个需要连接的文件 有时文件工作和连接似乎没有问题 然后在其他文件上 文件不会连接 我得到 非单调 DTS 我一直在谷歌上搜索我应该对这些文件进行哪些处理 以便它们正确连接 但我仍然没有找到 有没有办法让所有文件的 DTS 完全相同 我
  • 有没有简单的方法来提取附件 b 格式的 h264 原始流?

    当我使用命令行使用 ffmpeg 提取视频流时 ffmpeg i 一些文件 vcodec copy an f rawvideo h264 什么 out h264 对于 Adob e Media Encoder 生成的某些媒体文件 只有 m4
  • OpenCV 3.0.0 使用 FFMPEG 时出错

    我使用 OpenCV 一段时间了 但是 我最近将系统更改为没有任何管理员权限的集群 问题是这样的 在我的主文件夹中 我安装了 FFMPEG ffmpeg 网站上提供的最新稳定版本 我将它安装在 HOME 中 因此在 HOME lib 中安装
  • 如何使用ijkplayer库

    我要使用 ijkplayergithub链接 https github com bbcallen ijkplayer 我下载了这个 然后通过 文件 gt 导入 gt 常规 gt 现有项目到工作区 将其导入到 eclipse 中 之后我有三个
  • 如何使用ffmpeg从avi生成gif? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试使用以下命令将视频的一部分提取到动画 gif 中 ffmpeg i video avi t 5 out gif 它会生成一个 g
  • 将每分钟的 MP3 导出为单独的 WAV

    这绝对是一个奇怪的问题 但我正在寻找一种方法 将 60 分钟的 mp3 混合拆分为 60 个单独的 1 分钟长的 wav 文件 以便与 Echonest 等音频指纹识别 API 一起使用 这是否可以在单个 ffmpeg 命令中实现 或者我是
  • ffmpeg通过添加框或边框来扩展(而不是调整大小)视频大小

    假设我有一个特殊分辨率的视频 例如 1280x718 我想将其更改为 1280x720 但我宁愿只在顶部和底部添加一行 而不是将 718 像素垂直插值到 720 所以基本上 我正在寻找一种方法告诉 ffmpeg 创建 1280x720 的输
  • 使用 MediaRecorder 录制屏幕特定视图

    我想录制特定的屏幕视频View链接只想记录里面执行的动作LinearLayout 现在 MediaRecorder正在录制整个屏幕 如何录制屏幕的特定部分 MediaRecorder 通过媒体投影API 记录整个屏幕 至少从 Android
  • 使用 ffmpeg 从 unix 命令批量将 wav 文件转换为 16 位

    我有一个由许多子文件夹组成的文件夹 每个子文件夹都有其他子文件夹 其中包含 wav 文件 我想像这样转换所有文件 ffmpeg i BmBmGG BmBmBmBm wav acodec pcm s16le ar 44100 BmBmGG B
  • 将 H264 视频转换为原始 YUV 格式

    是否可以使用 ffmpeg 从 H264 编码视频创建原始 YUV 视频 我想用 matlab 打开视频并逐帧访问 Luma Cb 和 Cr 分量 是的 您可以 您只需指定像素格式即可 要获取格式的完整列表 ffmpeg pix fmts
  • C# - 捕获 RTP 流并发送到语音识别

    我正在努力实现的目标 在 C 中捕获 RTP 流 将该流转发到 System Speech SpeechRecognitionEngine 我正在创建一个基于 Linux 的机器人 它将接受麦克风输入 将其发送给 Windows 机器 Wi
  • 用于裁剪和转置视频的 FFMPEG 命令放大后质量较差

    我正在尝试将尺寸通常为 960x720 的 mp4 视频转换为方形 480 480 视频 但它通常看起来被压扁 命令是 y i s vf crop 480 480 transpose d threads 5 metadata s v rot
  • 如何在Android项目中使用libffmpeg.so?

    我正在尝试在 Android 中创建一个屏幕录制应用程序 为此 我使用 FFmpeg 我已经创建了 libffmpeg so 文件 现在我想在 Android 项目中使用相同的方法来调用它的本机函数 我怎样才能做到这一点 本教程提供了有关此
  • H264 字节流到图像文件

    第一次来这里所以要温柔 我已经在给定的 H 264 字节流上工作了几个星期 一般注意事项 字节流不是来自文件 它是从外部源实时提供给我的 字节流使用 Android 的媒体编解码器进行编码 当将流写入扩展名为 H264的文件时 VLC能够正
  • 使用 ffmpeg 或 OpenCV 处理原始图像

    看完之后维基百科页面 http en wikipedia org wiki Raw image format原始图像格式 是任何图像的数字负片 为了查看或打印 相机图像传感器的输出具有 进行处理 即转换为照片渲染 场景 然后以标准光栅图形格
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp

随机推荐

  • git diff命令之后,如何退出

    git diff命令是对比两次文件修改了什么 但如何退出呢 xff1f 输入q 按enter键盘
  • Float类型出现舍入误差的原因

    首先是float累加产生误差的原因 xff0c 该部分转自 xff1a http blog csdn net zhrh0096 article details 38589067 1 浮点数IEEE 754表示方法 要搞清楚float累加为什
  • React之antd Form回显数据

    转自 xff1a https blog csdn net welkin qing article details 110004969 文章目录 一 antd4如何回显数据 1 定义变量2 保存接口数据到form变量中3 form显示数据4
  • equals()方法和hashCode()方法

    1 hashCode 简介 该方法主要是利用一定的规则生成对象的哈希码 xff0c 也称散列码 它是是由对象导出的一个整数值 xff0c 是没有规律的 关于hashCode 使用的哈希算法 xff0c 越糟糕的哈希算法越容易产生哈希碰撞 产
  • 手把手教你基于STM32的BootLoader的OTA远程升级

    本文系21ic论坛蓝V作者小叶三千原创撰写 上次发过SD卡的Bootloader离线升级后 xff0c 应大家的要求 xff0c 这次就讲一下STM32的OTA远程升级 OTA又叫空中下载技术 xff0c 是通过移动通信的空中接口实现对移动
  • 转知乎,感觉非常棒,适合普通人c++学习路线图

    作者 xff1a 刘凯新 链接 xff1a https www zhihu com question 23447320 answer 39322848 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业
  • InfluxDB和IotDB介绍与性能对比

    InfluxDB简介 InfluxDB 是用Go语言编写的一个开源分布式时序 事件和指标数据库 xff0c 无需外部依赖 用于存储和分析时间序列数据的开源数据库 适合存储设备性能 日志 物联网传感器等带时间戳的数据 其设计目标是实现分布式和
  • CVSNT SERVER Configuration

    title CVSNT SERVER Configuration 64 author H819 64 version 0 5 Copyright free reference note author name and the article
  • JavaScript高级使用(对象、BOM、封装)

    JavaScript高级 今天给大家介绍以下几个内容 xff1a JavaScript 面向对象JavaScript 内置对象JavaScript BOMJavaScript 封装 JavaScript面向对象 其实JavaScript中的
  • Python函数(函数定义、函数调用)用法详解

    Python 中 xff0c 函数的应用非常广泛 xff0c 前面章节中我们已经接触过多个函数 xff0c 比如 input print range len 函数等等 xff0c 这些都是 Python 的内置函数 xff0c 可以直接使用
  • zsh配置

    原本用WindTerm 43 bash xff0c WindTerm提供了高亮 自动记忆补全等功能 xff0c 基本上也够用 WindTerm还是比较早期阶段 xff0c 功能细节上还有待完善 xff0c 稳定性方面也有些小问题 比如用vi
  • Android CHRE (Context Hub Runtime Environment)简介

    当前的ARM处理的多个核心 xff0c Android系统运行在速度最快的大核上 xff0c 通常叫AP xff08 Application Processor xff09 AP主要为性能 体验优化 xff0c 相对来说能耗高 处理器中还有
  • Docker pull 命令

    Docker pull 命令 Docker 命令大全 docker pull 从镜像仓库中拉取或者更新指定镜像 语法 docker pull OPTIONS NAME TAG 64 DIGEST OPTIONS说明 xff1a a 拉取所有
  • strrchr函数的实现

    lt span style 61 34 font size 18px 34 gt include lt stdio h gt include lt string h gt char strrchr char const s1 int ch
  • UCOSIII概述

    又给自己挖了一个小坑 xff0c 今天开始学习UCOS xff0c 本篇文章只是作为学习笔记 xff0c 并不是什么教程 文章目录 序言源码概览配置文件UCOSIII与移植相关代码文件UCOS与CPU相关代码文件UCOSIII库文件UCOS
  • UCOSIII中的消息传递

    文章目录 序言什么是消息队列消息队列相关函数OSQCreate OSQPend OSQPost 消息队列实验总结 序言 前面我们介绍了信号量 xff0c 通过信号量我们能够解决优先级反转 xff0c 资源共享冲突等问题 xff0c 但是我们
  • Qt之可视化QSS生成器(初探)

    简述 QSS是Qt的样式表 xff0c 类似于CSS xff0c 目前主要支持CSS2 写代码调样式非常不直观 xff0c 因此需要一个所见即所得的可视化样式生成器 xff0c 网上有很多CSS样式生成器 xff0c 最适合网页开发人员的1
  • 【秒懂音视频开发】26_RTMP服务器搭建

    流媒体 基本概念 流媒体 xff08 Streaming media xff09 xff0c 也叫做 xff1a 流式媒体 是指将一连串的多媒体数据压缩后 xff0c 经过互联网分段发送数据 xff0c 在互联网上即时传输影音以供观赏的一种
  • 论文笔记之PPDM(Parallel Point Detection and Matching for Real-time Human-Object Interaction Detection)

    分为两分支 xff0c 一个用于点 xff08 人 物 交互三个点 xff09 检测 xff0c 一个用于点匹配 xff0c 达到了实时的效果 CVPR2020接收 论文地址 xff1a https arxiv org pdf 1912 1
  • FFmpeg入门 - rtmp推流

    FFmpeg入门 视频播放 音视频开发老马的博客 CSDN博客介绍了怎样用ffmpeg去播放视频 里面用于打开视频流的avformat open input函数除了打开本地视频之外 实际上也能打开rtmp协议的远程视频 实现拉流 demo