想不到又涉足到流媒体开发了,之前毕业设计也做过一个基于Linux的无线图传demo,是把mjpg-streamer精简化了,使用了http协议,这个demo是开源的,注释都写好了,清晰易懂。
有兴趣的见无线图传mjpg-streamer精简版(开源)
言归正传,我负责的部分是在linux下的拉流。本想直接抛弃ffmpeg这个大型怪物,单纯用个librtmp拉流,简单又方便,可是这个库好多年没更新了…用的openssl版本还是老版本,装了老半天装不上。暂时放弃,先做个demo出来,于是还是编译了ffmpeg。在百度逛了一圈,除了雷神的博客还值得一看- -其他的资料真的很少,我也不知道是不会搜还是怎么样。雷神的博客好是好,关键是有点久远了,不得不说现在的软件更新真的很快,ffmpeg换了一些api,还没入门的我也搞了一天,才把demo搞出来。
本篇博客是参考雷神的最简单的基于FFMPEG的推流器附件:收流器,修改API得以运行,并使用cmake方式生成可执行文件,解决了新手都会遇到的undefined reference to
问题。
这个demo的功能是从芒果台接收视频流并转成flv格式保存下来,采用的协议是rtmp,停止运行按ctrl+c
即可。
直接上源码吧(主要是还没搞懂hhh)!
main.cpp
:
#include
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include
#include
#include
#ifdef __cplusplus
}
#endif
using namespace std;
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://58.200.131.2:1935/livetv/hunantv";
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;
}
//? ctx=context
for(i=0; i<ifmt_ctx->nb_streams; i++)
if(ifmt_ctx->streams[i]->codecpar->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 context\n");
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);
AVCodec *codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
AVStream *out_stream = avformat_new_stream(ofmt_ctx , codec);
if (!out_stream) {
printf( "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
AVCodecContext *p_codec_ctx = avcodec_alloc_context3(codec);
ret = avcodec_parameters_to_context(p_codec_ctx , in_stream->codecpar);
//Copy the settings of AVCodecContext
if (ret < 0) {
printf( "Failed to copy context from input to output stream codec context\n");
goto end;
}
p_codec_ctx->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
p_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
ret = avcodec_parameters_from_context(out_stream->codecpar, p_codec_ctx);
if(ret < 0){
av_log(NULL , AV_LOG_ERROR , "eno:[%d] error to paramters codec paramter \n" , ret);
}
}
//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 URL\n");
goto end;
}
//拉流
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 URL\n",frame_index);
frame_index++;
}
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
printf( "Error muxing packet\n");
break;
}
av_packet_unref(&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;
}
贴心的我已经帮大家准备好编译命令了,不管是直接编译还是cmake。
你可以直接编译:
g++ main.cpp -o tests -lavcodec -lavformat -lavutil
也可以用Cmake:
CMakeLists.txt
:解决undefined reference错误,参考此处
cmake_minimum_required(VERSION 3.10)
project(version1_0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-D__STDC_CONSTANT_MACROS")
find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h)
find_library(AVCODEC_LIBRARY avcodec)
find_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h)
find_library(AVFORMAT_LIBRARY avformat)
find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h)
find_library(AVUTIL_LIBRARY avutil)
find_path(AVDEVICE_INCLUDE_DIR libavdevice/avdevice.h)
find_library(AVDEVICE_LIBRARY avdevice)
add_executable(version1_0 main.cpp )
target_include_directories(version1_0 PRIVATE ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${AVDEVICE_INCLUDE_DIR})
target_link_libraries(version1_0 PRIVATE ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} ${AVUTIL_LIBRARY} ${AVDEVICE_LIBRARY})
完结,妥妥的,除了雷神的博客和ffmpeg的api之外,我参考了ffmpeg里面的remuxing.c的exmple,它做的事情和我这个做的事情差不多。接下来就要靠大家深挖了!
接下来准备实现flv解析h.264–>YUV–>RGB,然后用opencv进行处理,请留意我下一篇博客。
如果我的文章对你有帮助,欢迎关注,点赞,评论。