3516DV300 推流
创始人
2024-02-19 22:18:30
0

3516DV300 推流

基于ffmpeg将编码后的264文件(或者直接推流)推流出去,使用ffplay进行播放和验证。
ffmpeg版本:N-109124-g63db6a02a7
RELEASE:5.1.git

ffmpeg udp文件推流 命令行

这里用的是开发板编码出的码流,直接用vlc可以进行播放,但是用ffmpeg udp推流后就不能用vlc正常播放了,不知道啥原因。文件是海思例程保存的264文件。

  1. 推流
    地址和端口是远端的
ffmpeg -re -i stream_chn0.h264 -vcodec copy -f mpegts udp://192.168.3.13:1234
  1. 播放
    本地端口
ffplay -protocol_whitelist "file,udp,rtp" -i udp://192.168.3.13:1234 -fflags nobuffer

ffmpeg udp文件推流 代码

读取文件,并推流到远端端口。

int file_udp_main()
{AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;AVStream *in_stream, *out_stream;AVPacket pkt;int ret, i;int videoindex = -1;int frame_index = 0;int64_t start_time = 0;const char *in_filename = "stream_chn0.h264";//输入URL(Input file URL)const char *out_filename = "udp://192.168.3.13:1234";//输出 URL  udp	// 打开输入文件ifmt_ctx = NULL;if ((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0){return ret;}ifmt_ctx->probesize = 1024*1024;ifmt_ctx->max_analyze_duration = 3000000; //最大分析3秒ifmt_ctx->flags |= AVFMT_FLAG_NOBUFFER; //不缓存, 减小直播延时ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0){printf("avformat_find_stream_info failed\n");goto end;}for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++){if (ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){//HTTP—FLV只支持 AAC和MP3 音频格式if ((ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AAC&& ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_MP3)|| ifmt_ctx->streams[i]->codecpar->sample_rate == 0){hflv_audio = false;}else{hflv_audio = true;}}else if (ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){//HTTP—FLV只支持 h264 视频格式if (ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_H264){goto end;}videoindex = i;}}if (videoindex == -1){goto end;}av_dump_format(ifmt_ctx, 0, in_filename, 0);avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);if (!ofmt_ctx){printf( "Could not create output context\n");goto end;}printf("avformat_alloc_output_context2\n");//将输入流音视频编码信息复制到输出流中for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++){if (!hflv_audio){if(ifmt_ctx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){continue;}}AVStream *in_stream = ifmt_ctx->streams[i];AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);if (!out_stream){goto end;}if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0){goto end;}out_stream->codecpar->codec_tag = 0;}printf("copy codec context \n");av_dump_format(ofmt_ctx, 0, out_filename, 1);if (!(ofmt_ctx->oformat->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;}printf("avio_open \n");}ret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0){printf( "Error occurred when opening output URL\n");goto end;}printf("start push stream \n");while (1){//获取每一帧数据ret = av_read_frame(ifmt_ctx, &pkt);if (ret < 0){break;}if (!hflv_audio && pkt.stream_index != videoindex){av_packet_unref(&pkt);continue;}in_stream = ifmt_ctx->streams[pkt.stream_index];if (!hflv_audio && pkt.stream_index == videoindex){out_stream = ofmt_ctx->streams[0];pkt.stream_index = 0;}else{out_stream = ofmt_ctx->streams[pkt.stream_index];}if (pkt.pts == AV_NOPTS_VALUE){//Write PTSAVRational time_base1 = in_stream->time_base;//Duration between 2 frames (us)int64_t calc_duration = (int64_t)((double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate));//Parameterspkt.pts = (int64_t)((double)(frame_index * calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE));pkt.dts = pkt.pts;pkt.duration = (int64_t)((double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE));}//指定时间戳pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,(enum 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,(enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);pkt.pos = -1;if (pkt.stream_index == videoindex){            //printf("Send %8d video frames to output URL, [%d]\n",frame_index, pkt.flags);frame_index++;}ret = av_interleaved_write_frame(ofmt_ctx, &pkt);if (ret < 0){printf("Error muxing packet.error code %d\n", ret);break;}//释放 packet,否则会内存泄露av_packet_unref(&pkt);// 添加延时,不然很快就播放完了av_usleep(40000);}av_write_trailer(ofmt_ctx);end:// 该函数会释放用户自定义的IO buffer// 上面不再释放,否则会corrupted double-linked listavformat_close_input(&ifmt_ctx);avformat_free_context(ifmt_ctx);if (ofmt_ctx && !(ofmt_ctx->flags & AVFMT_NOFILE))avio_close(ofmt_ctx->pb);if (ofmt_ctx){avformat_free_context(ofmt_ctx);}return 0;
}

效果还可以,代码里面要加一个延时,不然文件一下子就读完了。
在这里插入图片描述

ffmpeg udp内存推流 代码

采用回调函数的方式读取文件,然后进行推流。和文件推流类似,区别在于使用了回调函数进行数据的读取(ffmpeg会不断调用该函数进行数据的获取)。


#define READ_DATA_LEN (1920*1080)
#define true 1
#define false 0
int fd;
int hflv_audio = 0;int read_init(char *filename)
{fd = open(filename,O_RDONLY);if(fd < 0){perror("open file failed");return -1;}return 1;
}int read_packet(void *opaque, uint8_t *buf, int buf_size)
{return  read(fd,buf,buf_size);
}int mem_udp_main()
{char *in_filename = "stream_chn0.h264";char *out_filename = "udp://192.168.3.13:1234";read_init(in_filename);int ret = 0;int videoindex = -1;AVPacket pkt;int frame_index = 0;AVStream *in_stream, *out_stream;AVFormatContext *ictx = NULL;const AVInputFormat* ifmt = NULL;AVFormatContext *octx = NULL;AVOutputFormat *ofmt = NULL;AVIOContext *avio = NULL;unsigned char * iobuffer = (unsigned char *)av_malloc(READ_DATA_LEN);avio = avio_alloc_context(iobuffer, READ_DATA_LEN, 0, NULL, read_packet, NULL, NULL);if (!avio){printf( "avio_alloc_context for input failed\n");goto end;}//探测流封装格式ret = av_probe_input_buffer(avio, &ifmt, "", NULL, 0, 0);if (ret < 0){printf("av_probe_input_buffer failed\n");goto end;}printf("av_probe_input_buffer format:%s[%s]\n",ifmt->name, ifmt->long_name);ictx = avformat_alloc_context();ictx->pb = avio;ictx->flags=AVFMT_FLAG_CUSTOM_IO;ret = avformat_open_input(&ictx, "", NULL, NULL);if (ret < 0){printf("avformat_open_input failed\n");goto end;}//获取音频视频的信息ictx->probesize = 1024*1024;ictx->max_analyze_duration = 3000000; //最大分析3秒ictx->flags |= AVFMT_FLAG_NOBUFFER; //不缓存, 减小直播延时ret = avformat_find_stream_info(ictx, NULL);if (ret < 0){printf("avformat_find_stream_info failed\n");goto end;}for(unsigned int i = 0; i < ictx->nb_streams; i++){if (ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){//HTTP—FLV只支持 AAC和MP3 音频格式if ((ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AAC&& ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_MP3)|| ictx->streams[i]->codecpar->sample_rate == 0){hflv_audio = false;}else{hflv_audio = true;}}else if (ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){// //HTTP—FLV只支持 h264 视频格式// if (ictx->streams[i]->codecpar->codec_id != AV_CODEC_ID_H264)// {//     goto end;// }videoindex = i;}}if (videoindex == -1){goto end;}av_dump_format(ictx, 0, "", 0);avformat_alloc_output_context2(&octx, NULL, "mpegts", out_filename);if (!octx){printf( "Could not create output context\n");goto end;}printf("avformat_alloc_output_context2\n");//将输入流音视频编码信息复制到输出流中for (unsigned int i = 0; i < ictx->nb_streams; i++){if (!hflv_audio){if(ictx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){continue;}}AVStream *in_stream = ictx->streams[i];AVStream *out_stream = avformat_new_stream(octx, NULL);if (!out_stream){goto end;}if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0){goto end;}out_stream->codecpar->codec_tag = 0;}printf("copy codec context \n");av_dump_format(octx, 0, out_filename, 1);//打开输出URL,准备推流if (!(octx->oformat->flags & AVFMT_NOFILE)){ret = avio_open(&octx->pb, out_filename, AVIO_FLAG_WRITE);if (ret < 0){printf( "Could not open output URL '%s'", out_filename);goto end;}printf("avio_open \n");}ret = avformat_write_header(octx, NULL);if (ret < 0){printf( "Error occurred when opening output URL\n");goto end;}printf("start push stream \n");while (1){//获取每一帧数据ret = av_read_frame(ictx, &pkt);if (ret < 0){break;}if (!hflv_audio && pkt.stream_index != videoindex){av_packet_unref(&pkt);continue;}in_stream = ictx->streams[pkt.stream_index];if (!hflv_audio && pkt.stream_index == videoindex){out_stream = octx->streams[0];pkt.stream_index = 0;}else{out_stream = octx->streams[pkt.stream_index];}if (pkt.pts == AV_NOPTS_VALUE){//Write PTSAVRational time_base1 = in_stream->time_base;//Duration between 2 frames (us)int64_t calc_duration = (int64_t)((double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate));//Parameterspkt.pts = (int64_t)((double)(frame_index * calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE));pkt.dts = pkt.pts;pkt.duration = (int64_t)((double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE));}//指定时间戳pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,(enum 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,(enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);pkt.pos = -1;if (pkt.stream_index == videoindex){            //printf("Send %8d video frames to output URL, [%d]\n",frame_index, pkt.flags);frame_index++;}ret = av_interleaved_write_frame(octx, &pkt);if (ret < 0){printf("Error muxing packet.error code %d\n", ret);break;}//释放 packet,否则会内存泄露av_packet_unref(&pkt);av_usleep(40000);}av_write_trailer(octx);end:// 该函数会释放用户自定义的IO buffer// 上面不再释放,否则会corrupted double-linked listavformat_close_input(&ictx);avformat_free_context(ictx);if (octx && !(ofmt->flags & AVFMT_NOFILE))avio_close(octx->pb);if (octx){avformat_free_context(octx);}}

效果感觉和直接文件推流差不多:
在这里插入图片描述

开发板推流

这里的主要难点是编码数据的获取和ffmpeg库(需要交叉编译)的链接。

  1. 编码数据的获取
    例程里面已经有了数据的获取例程,把它保存到一个队列里面。
    使用有名管道。
  • 数据写入:
    根据文件名创建有名管道,将编码数据写入。
    在这里插入图片描述
    在这里插入图片描述

  • 数据读取
    直接读取文件即可。
    和上面的从内存中读取文件一模一样。
    在这里插入图片描述

  1. ffmpeg静态库的链接
    这一步要修改Makefile,仿照SDK中的例子进行修改:
    文件:
    在这里插入图片描述
    Makefile 修改:
    在这里插入图片描述

  2. 效果
    能推流出来,但是延时不行,而且越来越大(无线和有线都是这样)。
    在这里插入图片描述

TODO

  1. 优化推流延时,应该是推流帧率(25fps)小于实际帧率(30fps)(另外HDMI的延时不稳定在0ms到15ms之间变化,这个不知道为啥不稳定)
  2. 提升画质(提高码率)
  3. 经过摄像头的图像有变形

参考文章

  1. ffmpeg-pusher
  2. 使用FFmpeg命令行进行UDP、RTP推流(H264、TS),ffplay接收
  3. 基于FFmpeg的推流器(UDP推流)
  4. ffmpeg内存读取数据推流rtmp

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
带头循环双向链表来咯!!! 前言:继上文,我们了解了结构最简单的一种链表---单链表那么我们今天就来...