3516DV300 推流

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 PTS
            AVRational 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));
            //Parameters
            pkt.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 list
    avformat_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 PTS
            AVRational 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));
            //Parameters
            pkt.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 list
    avformat_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. 效果
    能推流出来,但是延时不行,而且越来越大(无线和有线都是这样)。
    在这里插入图片描述

  3. 延时问题
    设置ffmpeg推流的帧率为30帧,无线局域网测试延时在1s左右。

 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;
        }
        av_opt_set(ictx->streams[i]->priv_data,"tune","zerolatency",0);
        ictx->streams[i]->avg_frame_rate.num = 30;
        ictx->streams[i]->r_frame_rate.num = 30;
        ictx->streams[i]->r_frame_rate.den = 1;
        
    }

TODO

  1. 优化推流延时
  2. 提升画质(提高码率)
  3. 经过摄像头的图像有变形

参考文章

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值