ffmpeg拉流 —— RTMP拉流例程

参考:最简单的基于FFMPEG的推流器附件:收流器


rtmp拉流例程:
#include <stdio.h>
#include "libavformat/avformat.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"

// rtmp拉流,保存为out.flv文件
#define RTMP_ADDR "rtmp://127.0.0.1:1935/live/1234"

void receive_rtmp(const char *out_file) {
    // 输入rtmp url
    AVFormatContext *ifmt_ctx = NULL;
    if (avformat_open_input(&ifmt_ctx, RTMP_ADDR, 0, 0) < 0) {
        printf("failed to open input file\n");
        goto _Error;
    }
    printf("open input\n");

    if (avformat_find_stream_info(ifmt_ctx, 0) < 0) {
        printf("failed to find stream info\n");
        goto _Error;
    }
    printf("find stream info\n");

    int video_idx = -1; 
    video_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_idx < 0) {
        printf("failed to find stream_index\n");
        goto _Error;
    }
    av_dump_format(ifmt_ctx, 0, RTMP_ADDR, 0);
        
    
    // 输出 xxx.flv文件
    AVFormatContext *ofmt_ctx = NULL;
    if (avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_file) < 0) {
        printf("failed to alloc output context\n");
        goto _Error;
    }
    for (int i = 0; i < ifmt_ctx->nb_streams; i++) { 
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
        avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);

        out_stream->codecpar->codec_tag = 0;
        //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
        //    out_stream->codecpar->flags |= CODEC_FLAG_GLOBAL_HEADER;
        //}
    }
    av_dump_format(ofmt_ctx, 0, out_file, 1);


    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&ofmt_ctx->pb, out_file, AVIO_FLAG_WRITE) < 0) {
            printf("failed to open out_file:%s\n", RTMP_ADDR);
            goto _Error;
        }
    }
    if (avformat_write_header(ofmt_ctx, NULL) < 0) {
        printf("failed to write header\n");
        goto _Error;
    }


    int frame_index = 0;
    AVPacket *packet = av_packet_alloc();
    while (1) {
        if (av_read_frame(ifmt_ctx, packet) < 0) break;
        
        // rescale time base
        av_packet_rescale_ts(packet, ifmt_ctx->streams[packet->stream_index]->time_base, \
                ofmt_ctx->streams[packet->stream_index]->time_base);
        packet->pos = -1;
        if (packet->stream_index == video_idx) {
            printf("receive %6d video frames\n", frame_index);
            frame_index++;
        }

        if (av_interleaved_write_frame(ofmt_ctx, packet) < 0) {
            printf("failed to write frame to url\n");
            break;
        }

        av_packet_unref(packet);
    }
    
    av_write_trailer(ofmt_ctx);
	printf("pull stream end\n");
	
_Error:
    if (ifmt_ctx) {
        avformat_close_input(&ifmt_ctx);
    }
    if (ofmt_ctx) {
        if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) 
            avio_close(ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);
    }
    if (packet) av_packet_free(&packet);
}


int main(int argc, char const* argv[])
{
    receive_rtmp(argv[1]); 
    return 0;
}

测试运行:

1、先运行拉流代码,程序会阻塞在avformat_find_input函数

2、然后使用ffmpeg命令将flv文件推流到rtmp://127.0.0.1:1935/live/1234。

image-20210527232906770


遗留问题:拉流程序无法退出循环,没有rtmp流时,程序阻塞在av_read_frame函数

解决思路:

1、人为打断,在一段时间内没有读到数据,直接退出。

使用select机制,监控网络中是否有数据可读,超时时退出循环?
(无法得到socket fd,所以不能知道网络是否有数据可读。即使可以监控,也只能用在另一个线程上进行,但是主线程仍是阻塞的,如何退出?好像只能直接终止主线程。)

本来还想换个角度,监控输出文件的写,超时时退出,但是输出文件的状态一直是可写的,,,planB失败。

补充:追踪输出文件描述符

ofmt_ctx->pb是AVIOContext结构体,管理输入输出数据
--> void *opaque:URLContext结构体
	--> void *priv_data; 文件描述符

其中opaque指向了URLContext。但是注意,这个结构体并不在FFMPEG提供的头文件中,而是在FFMPEG的源代码中。

从ffmpeg的结构和库函数的角度,无法得到avio_open打开的输出文件的文件描述符fd。其它办法:根据pid和文件名获取已打开文件的fd


2、想办法让av_read_frame立即返回,能否像文件操作那样设置NONBLOCK使读操作非阻塞。
在进入循环前设置NONBLOCK,无效。
ifmt_ctx->avio_flags |= AVIO_FLAG_NONBLOCK;

3、额。。。最后发现:有退出循环,av_read_frame阻塞一段时间后会返回失败。

image-20210528234426414


参考:

FFmpeg 解码 API 超时设置

当视频流地址能打开,但是视频流中并没有流内容的时候,可能会导致整体执行流程阻塞在 avformat_open_input 或者 av_read_frame 方法上。

ffmpeg之文件操作——AVIOContext, URLContext, URLProtocol
image-20210528002854154

FFmpeg 结构体学习(七): AVIOContext 分析

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值