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

参考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 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s  \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 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s  \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

你可能感兴趣的:(音视频开发,ffmpeg)