2016-11-27 178 views
3

我想編碼和流使用的ffmpeg(libavcodec的/ libavformat流 - MSVC的x64與Zeranoe版本)的ffmpeg創建RTP流

這裏是我的代碼,從編碼例如在很大程度上適應,錯誤處理去除

#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>% 

我在做什麼錯?

回答

1

在FFmpeg源挖了幾個小時後,我找到了解決辦法:

  1. 每個av_interleaved_write_frame

現在VLC播放前不要使用CODEC_FLAG_GLOBAL_HEADER標誌

  • 使用avformat_write_header該流正確

  • +0

    當且僅當複用器描述包含標誌「AVFMT_GL」時,應該設置編解碼器「AV_CODEC_FLAG_GLOBAL_HEADER」標誌OBALHEADER'。對於rtp,它恰好沒有設置。使用'avcodec_parameters_from_context'而不是手動填充codecpar要好得多。你應該只在開始時調用'avformat_write_header'一次;如果你必須反覆這樣做,那麼你做錯了什麼。 –

    +0

    @AndreyTurkin我已經改變它使用'avcodec_parameters_from_context'。但是,如果不重複調用'avformat_write_header',VLC不會播放流(緩存爲0%),並且gstreamer滯後(更具體地說,如果在x時間間隔內調用'avformat_write_header',那麼gstreamer將反覆卡住x時間(最多〜1s)然後跳轉)。我想知道我做錯了什麼。 – DankMemes