【问题标题】:ffmpeg create RTP streamffmpeg 创建 RTP 流
【发布时间】:2017-04-11 01:17:18
【问题描述】:

我正在尝试使用 ffmpeg 进行编码和流式传输(libavcodec/libavformat - 带有 Zeranoe 构建的 MSVC x64)

这是我的代码,很大程度上改编自编码示例,删除了错误处理

#include "stdafx.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>

#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
}
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")

int main() {
    avcodec_register_all();
    av_register_all();
    avformat_network_init();

    AVCodecID codec_id = AV_CODEC_ID_H264;
    AVCodec *codec;
    AVCodecContext *c = NULL;
    int i, ret, x, y, got_output;
    AVFrame *frame;
    AVPacket pkt;

    codec = avcodec_find_encoder(codec_id);
    c = avcodec_alloc_context3(codec);

    c->bit_rate = 400000;
    c->width = 352;
    c->height = 288;
    c->time_base.num = 1;
    c->time_base.den = 25;
    c->gop_size = 25;
    c->max_b_frames = 1;
    c->pix_fmt = AV_PIX_FMT_YUV420P;
    c->codec_type = AVMEDIA_TYPE_VIDEO;
    c->flags = CODEC_FLAG_GLOBAL_HEADER;

    if (codec_id == AV_CODEC_ID_H264) {
        ret = av_opt_set(c->priv_data, "preset", "ultrafast", 0);
        ret = av_opt_set(c->priv_data, "tune", "zerolatency", 0);
    }

    avcodec_open2(c, codec, NULL)

    frame = av_frame_alloc();
    frame->format = c->pix_fmt;
    frame->width = c->width;
    frame->height = c->height;
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
        c->pix_fmt, 32);

    AVFormatContext* avfctx;
    AVOutputFormat* fmt = av_guess_format("rtp", NULL, NULL);

    ret = avformat_alloc_output_context2(&avfctx, fmt, fmt->name,
        "rtp://127.0.0.1:49990");

    printf("Writing to %s\n", avfctx->filename);

    avio_open(&avfctx->pb, avfctx->filename, AVIO_FLAG_WRITE)

    struct AVStream* stream = avformat_new_stream(avfctx, codec);
    stream->codecpar->bit_rate = 400000;
    stream->codecpar->width = 352;
    stream->codecpar->height = 288;
    stream->codecpar->codec_id = AV_CODEC_ID_H264;
    stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    stream->time_base.num = 1;
    stream->time_base.den = 25;

    avformat_write_header(avfctx, NULL);
    char buf[200000];
    AVFormatContext *ac[] = { avfctx };
    av_sdp_create(ac, 1, buf, 20000);
    printf("sdp:\n%s\n", buf);
    FILE* fsdp;
    fopen_s(&fsdp, "test.sdp", "w");
    fprintf(fsdp, "%s", buf);
    fclose(fsdp);
    system("PAUSE");
    system("start "" \"C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe\" test.sdp");

    int j = 0;
    for (i = 0; i < 10000; i++) {
        av_init_packet(&pkt);
        pkt.data = NULL;    // packet data will be allocated by the encoder
        pkt.size = 0;
        fflush(stdout);
        /* prepare a dummy image */
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }
        /* Cb and Cr */
        for (y = 0; y < c->height / 2; y++) {
            for (x = 0; x < c->width / 2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }
        frame->pts = i;
        /* encode the image */
        ret = avcodec_send_frame(c, frame);
        ret = avcodec_receive_packet(c, &pkt);

        if (ret == AVERROR_EOF) {
            got_output = false;
            printf("Stream EOF\n");
        } else if(ret == AVERROR(EAGAIN)) {
            got_output = false;
            printf("Stream EAGAIN\n");
        } else {
            got_output = true;
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", j++, pkt.size);
            av_interleaved_write_frame(avfctx, &pkt);
            av_packet_unref(&pkt);
        }

        Sleep(40);
    }

    // end
    ret = avcodec_send_frame(c, NULL);

    /* get the delayed frames */
    for (; ; i++) {
        fflush(stdout);
        ret = avcodec_receive_packet(c, &pkt);
        if (ret == AVERROR_EOF) {
            printf("Stream EOF\n");
            break;
        } else if (ret == AVERROR(EAGAIN)) {
            printf("Stream EAGAIN\n");
            got_output = false;
        } else {
            got_output = true;
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", j++, pkt.size);
            av_interleaved_write_frame(avfctx, &pkt);
            av_packet_unref(&pkt);
        }
    }

    avcodec_close(c);
    av_free(c);
    av_freep(&frame->data[0]);
    av_frame_free(&frame);
    printf("\n");
    system("pause");
    return 0;
}

但是 VLC(使用生成的 SDP 文件打开)无法播放流。消息有这个

core error: ES_OUT_RESET_PCR called

后跟重复

packetizer_h264 warning: waiting for SPS/PPS
core debug: Buffering <some percent>%

我做错了什么?

【问题讨论】:

    标签: c++ ffmpeg rtp


    【解决方案1】:

    在ffmpeg源码中挖了几个小时,找到了解决办法:

    1. 不要使用CODEC_FLAG_GLOBAL_HEADER 标志
    2. 在每个 av_interleaved_write_frame 之前使用 avformat_write_header

    现在 VLC 可以正确播放流

    【讨论】:

    • Codec AV_CODEC_FLAG_GLOBAL_HEADER 标志应该设置当且仅当复用器描述包含标志AVFMT_GLOBALHEADER。对于 rtp,它恰好没有设置。最好使用avcodec_parameters_from_context 而不是手动填充编解码器。你应该只在开始时调用一次avformat_write_header;如果你必须反复这样做,那么你做错了。
    • @AndreyTurkin 我已将其更改为使用avcodec_parameters_from_context 。但是如果没有重复调用avformat_write_header,VLC 不会播放流(卡在缓冲 0%)并且 gstreamer 滞后(更具体地说,如果在 x 时间间隔内调用avformat_write_header,那么 gstreamer 将重复卡住x 时间量(最多约 1 秒),然后向前跳)。我想知道我做错了什么。
    猜你喜欢
    • 2021-09-27
    • 2018-03-01
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    • 2011-10-02
    • 2017-08-11
    相关资源
    最近更新 更多