zoukankan      html  css  js  c++  java
  • 【FFMPEG】使用FFMPEG+H264实现RTP传输数据

    开发环境:
    WINDOWS7 32bit
    MINGW
    eclipse juno cdt
    1、首先你要编译好FFMPEG,
    a) 方法一:可以去官网下载源码,用MINGW编译(编译时记得支持H264,当然,事先得下载并编译好libx264,视频技术论坛里有很多介绍)
    b) 方法二:更加省心省力的方法是,下载别人已经编译好的资源,如ZeranoeFFmpeg的,下载他的dev版本,包含了头文件,链接库等必须的东西,当然,这东西已经是支持H264的了。
    2、以下的就是代码部分了:
    a) 先声明必要的变量:
    1. AVFormatContext *fmtctx;
    2. AVStream *video_st;
    3. AVCodec *video_codec; 
    4. const int FPS = 25; /* 25 images/s */ 
    5. const char *RDIP = “127.0.0.1”;        
    6. unsigned int  RDPORT = 5678;
    7. const unsigned int OUTWIDTH = 720;       
    8. const unsigned int OUTHEIGHT = 480;    
    9. av_register_all();                   
    10. avformat_network_init();
    复制代码

    b) 初始化AV容器
    1. fmtctx = avformat_alloc_context();
    复制代码

    c) 获得输出格式,这里是RTP网络流
    1. fmtctx->oformat = av_guess_format("rtp", NULL, NULL);
    复制代码

    d)打开网络流
    1. snprintf(fmtctx->filename, sizeof(fmtctx->filename),"rtp://%s:%d",RDIP,RDPORT);
    2. avio_open(&fmtctx->pb,fmtctx->filename, AVIO_FLAG_WRITE)
    复制代码

    e) 开始添加H264视频流
    1. video_st = NULL;video_st = add_video_stream(fmtctx, &video_codec, AV_CODEC_ID_H264);
    复制代码

    其中,add_video_stream函数为:
    1. add_video_stream(AVFormatContext *oc,AVCodec **codec, enum AVCodecID codec_id)
    2. {
    3. AVCodecContext *c; 
    4. AVStream *st;     
    5. /* find the video encoder */
    6. *codec = avcodec_find_encoder(codec_id);      
    7. st = avformat_new_stream(oc, *codec);
    8. c = st->codec;
    9. avcodec_get_context_defaults3(c, *codec);
    10. c->codec_id = codec_id;
    11. c->width = OUTWIDTH;
    12. c->height = OUTHEIGHT;
    13. c->time_base.den = FPS;
    14. c->time_base.num = 1;
    15. c->pix_fmt = PIX_FMT_YUV420P;
    16. if(oc->oformat->flags & AVFMT_GLOBALHEADER)
    17. c->flags|= CODEC_FLAG_GLOBAL_HEADER;
    18. av_opt_set(c->priv_data, "preset", "ultrafast", 0);
    19. av_opt_set(c->priv_data, "tune","stillimage,fastdecode,zerolatency",0);
    20. av_opt_set(c->priv_data, "x264opts","crf=26:vbv-maxrate=728:vbv-bufsize=364:keyint=25",0);return st;}
    21. // OPEN THE CODE
    22. avcodec_open2(video_st->codec, video_codec, NULL);
    23. /* Write the stream header, if any. */
    24. avformat_write_header(fmtctx, NULL);
    复制代码

    f) 现在,就可以不断的编码数据,并发生数据了
    1. AVFrame* m_pYUVFrame = avcodec_alloc_frame();     
    2. while(1) //这里设置成无限循环,你可以设置成250,或其他数进行测试,观看结果    
    3. {
    4. fill_yuv_image(m_pYUVFrame, video_st->codec->frame_number,OUTWIDTH, OUTHEIGHT);        
    5. /* encode the image */
    6. AVPacket pkt;
    7. int got_output = 0;
    8. av_init_packet(&pkt);
    9. pkt.data = NULL;    // packet data will be allocated by the encoder
    10. pkt.size = 0;
    11. pkt.pts = AV_NOPTS_VALUE;
    12. pkt.dts =AV_NOPTS_VALUE;
    13. m_pYUVFrame->pts = video_st->codec->frame_number;
    14. ret = avcodec_encode_video2(c, &pkt,frame, &got_output);
    15. if (ret < 0) {fprintf(stderr, "Error encoding video frame: %s ", av_err2str(ret));
    16. exit(1);
    17. }
    18. /* If size is zero, it means the image was buffered. */
    19. if (got_output) 
    20. {
    21. if (c->coded_frame->key_frame)pkt.flags |= AV_PKT_FLAG_KEY;
    22. pkt.stream_index = st->index;
    23. if (pkt.pts != AV_NOPTS_VALUE )
    24. {
    25. pkt.pts = av_rescale_q(pkt.pts,video_st->codec->time_base, video_st->time_base);
    26. }
    27. if(pkt.dts !=AV_NOPTS_VALUE )
    28. {
    29. pkt.dts = av_rescale_q(pkt.dts,video_st->codec->time_base, video_st->time_base);
    30. }
    31. /* Write the compressed frame to the media file. */
    32. ret = av_interleaved_write_frame(oc,&pkt);

    33. else {
    34. ret = 0;
    35. }
    36. }
    复制代码

    g) Fill_yuv_image函数:
    1. /* Prepare a dummy image. */
    2. static void fill_yuv_image(AVPicture *pict,int frame_index,int width, int height)
    3. {
    4. int x, y, i;
    5. i = frame_index;
    6. /* Y */
    7. for (y = 0; y < height; y++)
    8. for (x = 0; x < width; x++)
    9. pict->data[0][y * pict->linesize[0] +x] = x + y + i * 3;
    10. /* Cb and Cr */
    11. for (y = 0; y < height / 2; y++) 
    12. {
    13. for (x = 0; x < width / 2; x++) 
    14. {
    15. pict->data[1][y * pict->linesize[1] +x] = 128 + y + i * 2;
    16. pict->data[2][y * pict->linesize[2] +x] = 64 + x + i * 5;
    17. }
    18. }
    19. }
    复制代码

    h) 打印sdp信息,仅需一次,打印的sdp信息,用在VLC播放器结束网络视频流时用到
    1. //打印sdp信息
    2. char sdp[2048];
    3. av_sdp_create(&fmtctx,1, sdp, sizeof(sdp));
    4. printf("%s ",sdp);
    5. fflush(stdout);
    复制代码

    i)最后,做一些清理工作
    1. avcodec_free_frame(&m_pYUVFrame);
    2. av_write_trailer(fmtctx);     
    3. /* Free the streams. */
    4. for (unsigned int i = 0; i< fmtctx->nb_streams;i++) 
    5. {
    6. av_freep(&fmtctx->streams->codec);
    7. av_freep(&fmtctx->streams);
    8. }
    9. if(!(fmtctx->oformat->flags& AVFMT_NOFILE))
    10. /* Close the output file. */
    11. avio_close(fmtctx->pb);            
    12. /*free the stream */     
    13. av_free(fmtctx);
    复制代码

    3、编译代码,记得添加库文件,运行一次代码,不用死循环,设置不用循环,因为是要让他打印出sdp文件的信息。得到sdp信息,比如我精简成如下:
    1. c=IN IP4 127.0.0.1
    2. m=video 56782 RTP/AVP 96
    3. a=rtpmap:96 H264/90000
    4. a=framerate:25
    5. a=fmtp:96 packetization-mode=1
    复制代码

    把这些信息保存到一个文本文件,并改名为sdp后缀,如mySDP.sdp。
    4、从官网下载VLC播放器,重新运行上述的代码,这一次要循环,具体循环多久,你自己决定,这一次是正式测试了。代码跑起来后,把刚刚的sdp文件用VLC打开,直接把sdp文件拖到VLC播放器中就行了。等待缓冲,就可以看到效果了。
    5、代码中剩掉了出错检查部分,请自行添加。
    6、关于IP地址,这里是127.0.0.1,是供本机测试,可以改成制定的接受数据的电脑IP地址,或者广播地址IP地址。
    7、经本人测试,局域网内不同电脑间测试,刚开始播放良好,进过一段时间后,开始出现吊针现象。经多次修改av_opt_set里面的参数(是修改x264的参数配置)也无法达到较理想的想过。
  • 相关阅读:
    #Leetcode# 700. Search in a Binary Search Tree
    很多很多书上代码
    #Leetcode# 104. Maximum Depth of Binary Tree
    #Leetcode# 12. Integer to Roman
    PAT-2018年冬季考试-乙级
    PAT 1035 插入与归并
    PAT 1058 选择题
    PAT 1052 卖个萌
    CodeForces Round #521 (Div.3) E. Thematic Contests
    2017Nowcoder Girl初赛重现赛
  • 原文地址:https://www.cnblogs.com/huty/p/8518764.html
Copyright © 2011-2022 走看看