概述
ubuntu18.04 基于ffmpeg的Demo
- 感谢雷神
- 一、ubuntu18.04 搭建Nginx和RTMP服务
- 二、源码修改
- 1.发送端源码(雷神)
- 注意:
- 编译:
- 运行
- 2.接收数据
- 注意
- 编译
- 运行
- 总结
- 流媒体播放器
感谢雷神
本文仅作为个人学习过程中的总结记录,不做其他任何用途!向雷神致敬!
本demo是在在雷神的《最简单的基于FFmpeg的推流器》、《最简单的基于FFMPEG的推流器附件:收流器》有点小修改后纯linux版本。
最简单的基于FFmpeg的推流器
最简单的基于FFMPEG的推流器附件:收流器
具体实现原理参照雷神的blog。
一、ubuntu18.04 搭建Nginx和RTMP服务
安装Nginx和RTMP:
请参照:Ubuntu18.04搭建本地RTMP服务器librtmp+nginx,推送flv文件播放
注意:
./configure --prefix=/usr/local/nginx --add-module=…/nginx-rtmp-module-master/
–add-module=…/nginx-rtmp-module-master 表示在nginx服务中增加rtmp模块,这点不能忽略。
端口查询命令:
1)sudo netstat -nultp
2)sudo netstat -anp |grep 22
nginx相关操作命令
sudo service nginx start # 启动
sudo service nginx reload # 重载
sudo service nginx restart # 重启
sudo service nginx stop # 停止
sudo /usr/local/nginx/sbin/nginx -v # 查看版本
sudo /usr/local/nginx/sbin/nginx # 启动
sudo /usr/local/nginx/sbin/nginx -s stop # 停止
sudo /usr/local/nginx/sbin/nginx -s reload # 重启
在 nginx.conf 配置文件中,注意修改下图的标记的端口号;
如果不修改,会发现nginx无法启动,提示端口占用;
cd /usr/local/nginx/conf
vim nginx.conf
二、源码修改
1.发送端源码(雷神)
/**
* 最简单的基于FFmpeg的推流器(推送RTMP)
* Simplest FFmpeg Streamer (Send RTMP)
*
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本例子实现了推送本地视频至流媒体服务器(以RTMP为例)。
* 是使用FFmpeg进行流媒体推送最简单的教程。
*
* This example stream local media files to streaming media
* server (Use RTMP as example).
* It's the simplest FFmpeg streamer.
*
*/
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#ifdef __cplusplus
};
#endif
#endif
int main(int argc, char* argv[])
{
AVOutputFormat *ofmt = NULL;
//Input AVFormatContext and Output AVFormatContext
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, i;
int videoindex=-1;
int frame_index=0;
int64_t start_time=0;
//in_filename = "cuc_ieschool.mov";
//in_filename = "cuc_ieschool.mkv";
//in_filename = "cuc_ieschool.ts";
//in_filename = "cuc_ieschool.mp4";
//in_filename = "cuc_ieschool.h264";
in_filename = "cuc_ieschool.flv";//输入URL(Input file URL)
//in_filename = "shanghai03_p.h264";
//out_filename = "rtmp://localhost/publishlive/livestream";//输出 URL(Output URL)[RTMP]
out_filename = "rtmp://localhost/live/room";
//out_filename = "rtp://233.233.233.233:6666";//输出 URL(Output URL)[UDP]
av_register_all();
//Network
avformat_network_init();
//Input
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
printf( "Could not open input file.");
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
printf( "Failed to retrieve input stream information");
goto end;
}
for(i=0; i<ifmt_ctx->nb_streams; i++)
if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
videoindex=i;
break;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
//Output
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP
//avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP
if (!ofmt_ctx) {
printf( "Could not create output contextn");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
//Create output AVStream according to input AVStream
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
printf( "Failed allocating output streamn");
ret = AVERROR_UNKNOWN;
goto end;
}
//Copy the settings of AVCodecContext
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0) {
printf( "Failed to copy context from input to output stream codec contextn");
goto end;
}
out_stream->codec->codec_tag = 0;
//if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
// out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
//Dump Format------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
//Open output URL
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
printf( "Could not open output URL '%s'", out_filename);
goto end;
}
}
//Write file header
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
printf( "Error occurred when opening output URLn");
goto end;
}
start_time=av_gettime();
while (1) {
AVStream *in_stream, *out_stream;
//Get an AVPacket
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if(pkt.pts==AV_NOPTS_VALUE){
//Write PTS
AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
//Duration between 2 frames (us)
int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
//Parameters
pkt.pts=(double)(frame_index*calc_duration)/(double) (av_q2d(time_base1)*AV_TIME_BASE);
pkt.dts=pkt.pts;
pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
}
//Important:Delay
if(pkt.stream_index==videoindex){
AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
AVRational time_base_q={1,AV_TIME_BASE};
int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
int64_t now_time = av_gettime() - start_time;
if (pts_time > now_time)
av_usleep(pts_time - now_time);
}
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
/* copy packet */
//Convert PTS/DTS
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
//Print to Screen
if(pkt.stream_index==videoindex){
printf("Send %8d video frames to output URLn",frame_index);
frame_index++;
}
//ret = av_write_frame(ofmt_ctx, &pkt);
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
printf( "Error muxing packetn");
break;
}
av_free_packet(&pkt);
}
//Write file trailer
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf( "Error occurred.n");
return -1;
}
return 0;
}
注意:
1. out_filename = "rtmp://localhost/live/room"; 输出到RTMP的地址为 nginx.conf配置文件中配置的RTMP服务中的地址(不知道怎么描述这个地址,也能理解中空间吧)。
2. in_filename = "cuc_ieschool.flv"; 输入时,注意视频文件所在的路劲;
3. //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
// out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
这2行代码注释掉了,“CODEC_FLAG_GLOBAL_HEADER” 变量在linux下没有,window下有;查了不少资料,没找到具体原因。懂这块的,请不吝指点!
编译:
gcc simplest_ffmpeg_streamer.cpp -g -o simplest_ffmpeg_streamer.out
-I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavformat -lavcodec -lavutil
/usr/local/ffmpeg/include和 /usr/local/ffmpeg/lib 注意修改为自己的ffmpeg的实际安装路径。
运行
错误提示:
1.RTMP读服务为开启
解决:
开启nginx 服务:
sudo /usr/local/nginx/sbin/nginx
- flv文件推送完成后
该错误可以忽略,不影响文件发送。查过资料,好像与ffmpeg发送flv的机制有关,没弄懂。
2.接收数据
/**
* 最简单的基于FFmpeg的收流器(接收RTMP)
* Simplest FFmpeg Receiver (Receive RTMP)
*
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本例子将流媒体数据(以RTMP为例)保存成本地文件。
* 是使用FFmpeg进行流媒体接收最简单的教程。
*
* This example saves streaming media data (Use RTMP as example)
* as a local file.
* It's the simplest FFmpeg stream receiver.
*
*/
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#ifdef __cplusplus
};
#endif
#endif
//'1': Use H.264 Bitstream Filter
#define USE_H264BSF 0
int main(int argc, char* argv[])
{
AVOutputFormat *ofmt = NULL;
//Input AVFormatContext and Output AVFormatContext
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, i;
int videoindex=-1;
int frame_index=0;
//in_filename = "rtmp://live.hkstv.hk.lxdns.com/live/hks";
in_filename = "rtmp://localhost/live/room";
//in_filename = "rtp://233.233.233.233:6666";
//out_filename = "receive.ts";
//out_filename = "receive.mkv";
out_filename = "receive.flv";
av_register_all();
//Network
avformat_network_init();
//Input
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
printf( "Could not open input file.");
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
printf( "Failed to retrieve input stream information");
goto end;
}
for(i=0; i<ifmt_ctx->nb_streams; i++)
if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
videoindex=i;
break;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
//Output
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); //RTMP
if (!ofmt_ctx) {
printf( "Could not create output contextn");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
//Create output AVStream according to input AVStream
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
printf( "Failed allocating output streamn");
ret = AVERROR_UNKNOWN;
goto end;
}
//Copy the settings of AVCodecContext
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0) {
printf( "Failed to copy context from input to output stream codec contextn");
goto end;
}
out_stream->codec->codec_tag = 0;
//if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
//out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
//Dump Format------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
//Open output URL
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
printf( "Could not open output URL '%s'", out_filename);
goto end;
}
}
//Write file header
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
printf( "Error occurred when opening output URLn");
goto end;
}
#if USE_H264BSF
AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif
while (1) {
AVStream *in_stream, *out_stream;
//Get an AVPacket
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
/* copy packet */
//Convert PTS/DTS
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
//Print to Screen
if(pkt.stream_index==videoindex){
printf("Receive %8d video frames from input URLn",frame_index);
frame_index++;
#if USE_H264BSF
av_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
}
//ret = av_write_frame(ofmt_ctx, &pkt);
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
printf( "Error muxing packetn");
break;
}
av_free_packet(&pkt);
}
#if USE_H264BSF
av_bitstream_filter_close(h264bsfc);
#endif
//Write file trailer
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf( "Error occurred.n");
return -1;
}
return 0;
}
注意
- in_filename = “rtmp://localhost/live/room”; 表示获取数据流的路径,与发送端匹配。也可以是其他RTMP路径;(其他的路径可以尝试,部分代码需要修改)
- out_filename = “receive.flv”; 输出流为“*.flv”文件。 也可以是其他文件,需要修改代码,这个涉及到ffmpeg的文件格式转换。
- //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
//out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
这2行代码注释,“CODEC_FLAG_GLOBAL_HEADER”,linux下没找到这个变量。
编译
gcc simplest_ffmpeg_receiver.cpp -g -o simplest_ffmpeg_receiver.out
-I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavformat -lavcodec -lavutil
/usr/local/ffmpeg/include和 /usr/local/ffmpeg/lib 注意修改为自己的ffmpeg的实际安装路径。
运行
运行结束后会在当前目录下找到“*.flv”文件。
总结
流媒体播放器
smplayer
具体安装方法略。
作为ffmpeg的初学者,需要一套可执行程序,帮助自己理解ffmepg。
在摸索的过程中,总结了以上内容,希望对大家有用!
文中引用了几位大神的blog。
感谢!
最后
以上就是有魅力鸵鸟为你收集整理的ubuntu18.04 基于ffmpeg4.3的Demo感谢雷神一、ubuntu18.04 搭建Nginx和RTMP服务二、源码修改总结的全部内容,希望文章能够帮你解决ubuntu18.04 基于ffmpeg4.3的Demo感谢雷神一、ubuntu18.04 搭建Nginx和RTMP服务二、源码修改总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复