使用ffmpeg实现服务端和客户端的RTMP推流拉流

本文介绍了FFmpeg如何使用RTMP协议进行实时流媒体传输,包括推流到RTMP服务器的命令行指令和C语言API示例,以及从RTMP服务器拉流的过程。特别关注了H.264和AAC编码的应用以及注意事项。
摘要由CSDN通过智能技术生成

参考ffmpeg官方协议文档:FFmpeg 协议文档

RTSP(Real-Time Streaming Protocol)和RTMP(Real-Time Messaging Protocol)都是用于实时流媒体传输的协议。

RTMP使用单一的持久连接(通常是TCP),在该连接上同时传输控制信息和媒体数据,其相对较灵活,支持多种编码格式和传输方式。最初设计用于Flash播放器,但后来也被其他应用广泛采用RTMP曾经在直播、在线视频等领域非常流行。然而,由于Adobe停止支持Flash Player,并且HTML5和其他技术的兴起,RTMP的使用逐渐减少。

使用命令行实现

推流到RTMP服务器:

使用以下命令推流到RTMP服务器,将 [RTMP_URL] 替换为实际的RTMP服务器地址。

ffmpeg -i input_video.mp4 -c:v libx264 -c:a aac -strict -2 -f flv [RTMP_URL]

  • -i input_video.mp4:指定输入视频文件。
  • -c:v libx264:使用H.264编码器。
  • -c:a aac:使用AAC音频编码器。
  • -f flv:指定输出格式为FLV。
  • [RTMP_URL]:实际的RTMP服务器地址。

拉流从RTMP服务器:

ffmpeg -i [RTMP_URL] -c copy output_video.mp4

  • -i [RTMP_URL]:指定输入源为RTMP服务器地址。
  • -c copy:使用原始编码参数进行拷贝,以保留原始编码。
  • output_video.mp4:指定输出视频文件。

注意事项

替换 [RTMP_URL] 为实际的RTMP服务器地址。这个地址通常以 rtmp:// 开头,后面是服务器地址和应用程序路径。
输入和输出文件的格式和编码参数可以根据您的需求进行调整。
在实际应用中,可能需要提供更多的参数,比如设置视频分辨率、码率、帧率等。

调用API编程实现

服务端推流(push_to_rtmp_server.c):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <input_file> <rtmp_url>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *input_file = argv[1];
    const char *rtmp_url = argv[2];

    av_register_all();
    avformat_network_init();

    AVFormatContext *input_ctx = NULL;
    if (avformat_open_input(&input_ctx, input_file, NULL, NULL) != 0) {
        fprintf(stderr, "Error opening input file\n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(input_ctx, NULL) < 0) {
        fprintf(stderr, "Error finding stream information\n");
        return EXIT_FAILURE;
    }

    AVOutputFormat *output_fmt = av_guess_format("flv", NULL, NULL);
    if (!output_fmt) {
        fprintf(stderr, "Error guessing output format\n");
        return EXIT_FAILURE;
    }

    AVFormatContext *output_ctx = NULL;
    if (avformat_alloc_output_context2(&output_ctx, output_fmt, NULL, rtmp_url) < 0) {
        fprintf(stderr, "Error allocating output context\n");
        return EXIT_FAILURE;
    }

    for (int i = 0; i < input_ctx->nb_streams; i++) {
        AVStream *in_stream = input_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(output_ctx, in_stream->codec->codec);
        if (!out_stream) {
            fprintf(stderr, "Error creating output stream\n");
            return EXIT_FAILURE;
        }

        if (avcodec_copy_context(out_stream->codec, in_stream->codec) != 0) {
            fprintf(stderr, "Error copying codec context\n");
            return EXIT_FAILURE;
        }
    }

    if (avio_open(&output_ctx->pb, rtmp_url, AVIO_FLAG_WRITE) < 0) {
        fprintf(stderr, "Error opening output URL\n");
        return EXIT_FAILURE;
    }

    if (avformat_write_header(output_ctx, NULL) < 0) {
        fprintf(stderr, "Error writing header\n");
        return EXIT_FAILURE;
    }

    AVPacket packet;
    int frame_index = 0;
    int64_t start_time = av_gettime();
    while (1) {
        AVStream *in_stream, *out_stream;
        if (av_read_frame(input_ctx, &packet) < 0)
            break;

        in_stream = input_ctx->streams[packet.stream_index];
        out_stream = output_ctx->streams[packet.stream_index];

        packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
        packet.pos = -1;

        if (av_interleaved_write_frame(output_ctx, &packet) < 0) {
            fprintf(stderr, "Error writing frame\n");
            return EXIT_FAILURE;
        }

        printf("Write %8d frames to output URL\n", frame_index);
        av_packet_unref(&packet);
        frame_index++;
    }

    av_write_trailer(output_ctx);

    avio_close(output_ctx->pb);
    avformat_free_context(output_ctx);

    avformat_close_input(&input_ctx);

    return EXIT_SUCCESS;
}

客户端拉流(pull_from_rtmp_server.c):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavformat/avformat.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <rtmp_url> <output_file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *rtmp_url = argv[1];
    const char *output_file = argv[2];

    av_register_all();
    avformat_network_init();

    AVFormatContext *input_ctx = NULL;
    if (avformat_open_input(&input_ctx, rtmp_url, NULL, NULL) != 0) {
        fprintf(stderr, "Error opening input URL\n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(input_ctx, NULL) < 0) {
        fprintf(stderr, "Error finding stream information\n");
        return EXIT_FAILURE;
    }

    av_dump_format(input_ctx, 0, rtmp_url, 0);

    AVFormatContext *output_ctx = NULL;
    if (avformat_alloc_output_context2(&output_ctx, NULL, NULL, output_file) < 0) {
        fprintf(stderr, "Error allocating output context\n");
        return EXIT_FAILURE;
    }

    for (int i = 0; i < input_ctx->nb_streams; i++) {
        AVStream *in_stream = input_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(output_ctx, in_stream->codec->codec);
        if (!out_stream) {
            fprintf(stderr, "Error creating output stream\n");
            return EXIT_FAILURE;
        }

        if (avcodec_copy_context(out_stream->codec, in_stream->codec) != 0) {
            fprintf(stderr, "Error copying codec context\n");
            return EXIT_FAILURE;
        }
    }

    if (avio_open(&output_ctx->pb, output_file, AVIO_FLAG_WRITE) < 0) {
        fprintf(stderr, "Error opening output file\n");
        return EXIT_FAILURE;
    }

    if (avformat_write_header(output_ctx, NULL) < 0) {
        fprintf(stderr, "Error writing header\n");
        return EXIT_FAILURE;
    }

    AVPacket packet;
    while (1) {
        if (av_read_frame(input_ctx, &packet) < 0)
            break;

        AVStream *in_stream, *out_stream;
        in_stream = input_ctx->streams[packet.stream_index];
        out_stream = output_ctx->streams[packet.stream_index];

        packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
        packet.pos = -1;

        if (av_interleaved_write_frame(output_ctx, &packet) < 0) {
            fprintf(stderr, "Error writing frame\n");
            return EXIT_FAILURE;
        }

        av_packet_unref(&packet);
    }

    av_write_trailer(output_ctx);

    avio_close(output_ctx->pb);
    avformat_free_context(output_ctx);

    avformat_close_input(&input_ctx);

    return EXIT_SUCCESS;
}

编译和执行

gcc push_to_rtmp_server.c -o push_to_rtmp_server -lavformat -lavcodec -lswscale -lavutil
gcc pull_from_rtmp_server.c -o pull_from_rtmp_server -lavformat -lavcodec -lswscale -lavutil

#执行服务端推流
./push_to_rtmp_server input_video.mp4 rtmp://example.com/live/stream

#执行客户端拉流
./pull_from_rtmp_server rtmp://example.com/live/stream output_video.mp4

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

稚肩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值