直播系统的组成部分三:拉流—简单的拉流器

qi.wei

发布于 2020.03.04 00:03 阅读 2696 评论 0

直播系统的组成部分三:拉流—简单的拉流器

 

 

文章分为以下几个部分:

    1.拉流的实现

    2.代码分析

 

 

 

 

拉流的实现

    拉流是将直播推流到流媒体服务器的流数据拉取到本地进行解码播放。拉流之前首先需要有一个拉流地址,为了方便测试,我们这里直接用湖南卫视的拉流地址,是一个rtmp的地址:

    rtmp://58.200.131.2:1935/livetv/hunantv

    对应的格式是flv格式。

    我在写拉流器的时候由于对FFMPEG整体掌握的不是很熟练,现在重新整理发现拉流的实现和本地视频的解码其实相差的并不多,只是打开文件的方式不同,设置的参数不同。之前写的本地视频的解码可以对照着看一下:

    http://www.lindasoft.com/view/article/details?articleId=638

 

 

 

代码实现:


extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

#include "libavutil/pixfmt.h"

#include "libswscale/swscale.h"

}



#include <stdio.h>



int main(int argc, char *argv[])

{

AVFormatContext *pFormatCtx;

    AVCodecContext *pCodecCtx ;

    AVCodec * pCodec;



    AVFrame *pFrame, *pFrameRGB;

    AVPacket  *packet;

    uint8_t *out_buffer;



    static struct SwsContext *img_convert_ctx;



    int videoStream, i, numBytes;

    int ret, got_picture;



    avformat_network_init();

    //初始化ffmpeg,之后才能正常使用编码器和解码器

    av_register_all();



    //Allocate an AVFormatContext,

    pFormatCtx = avformat_alloc_context();



    //ffmpeg取rtsp流时av_read_frame阻塞的解决办法 设置参数优化

    AVDictionary* avdic=NULL;

    av_dict_set(&avdic, "burrer sice", "102400", 0);    //设置缓存大小,1080p可将值调大

    av_dict_set(&avdic, "rtsp_transport", "udp", 0);    //以udp方式打开,如果以tcp方式打开将udp替换为tcp

    av_dict_set(&avdic, "stimeout", "2000000", 0);      //设置超时断开连接世界,单位微秒

    av_dict_set(&avdic, "max_delay", "500000", 0);      //设置最大时延



    //rstp地址,可根据实际情况修改,这里我们用湖南卫视的收流地址

    char url[]="rtmp://58.200.131.2:1935/livetv/hunantv";



    if(avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {



        qDebug("can't open the f1le. n") ;return;

    }



    if(avformat_find_stream_info(pFormatCtx, NULL)< 0) {



        qDebug("Could't rind stream infomation. n");return;

    }





    videoStream = -1;

    //遍历流信息,直到找到视频流

    for (i = 0; i< pFormatCtx->nb_streams; i++) {



        if (pFormatCtx->streams [i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {



            videoStream = i;

        }

    }

    //如果没找到videoStream = 初值-1

    if (videoStream == -1) {



        qDebug("Didn't find a video stream. \n") ;return;

    }



    //查找视频解码器

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);



    pCodecCtx->bit_rate = 0;    //初始化为0

    pCodecCtx->time_base.num = 1;//下面两行,一秒钟25帧

    pCodecCtx->time_base.den = 10;

    pCodecCtx->frame_number = 1;//每包一个视频帧



    if (pCodec == NULL) {



        qDebug("Codec not found. \n");return;

    }



    //打开视频解码器

    if (avcodec_open2(pCodecCtx, pCodec, NULL)< 0) {



        qDebug("Could not open codec.\n") ;return;

    }





    pFrame=av_frame_alloc();



    pFrameRGB = av_frame_alloc() ;



    //设置图像转换的结构体变量

    ///这里我们改成了 将解码后的YUV数据转换成RGB32

    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,

                                     pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,

                                     PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);



    numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);



    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB32,

                   pCodecCtx->width, pCodecCtx->height);



    int y_size = pCodecCtx->width * pCodecCtx->height;



    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet

    av_new_packet(packet, y_size); //分配packet的数据



    av_dump_format(pFormatCtx, 0, url, 0); //输出视频信息



    av_init_packet(packet);

    while (1)

    {

        if(av_read_frame(pFormatCtx, packet)<0){

            qDebug()<<"av_ read_ frame < 0" ;break;//这里人为读取完了

        }

        ret = -1;

        if(packet->stream_index == videoStream)

        {



            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);



            if(ret<0)

            {

                qDebug("decnd- error . (n");

                return;

            }

            else

            {

                qDebug()<<"ret"<<ret;

            }



            if (got_picture)

            {



                qDebug()<<"获取到图像";



                qDebug()<<"pFrame->linesize"<<pFrame->linesize;

                qDebug()<<"pCodecCtx->height"<<pCodecCtx->height;

                qDebug()<<"pFrameRGB->linesize"<<pFrameRGB->linesize;

                sws_scale(img_convert_ctx,

                          (uint8_t const * const *)pFrame->data,

                          pFrame->linesize,0, pCodecCtx->height, pFrameRGB->data ,pFrameRGB->linesize);



                //把RGB数据用QImage加载

                QImage tmpImg((uchar *)out_buffer , pCodecCtx->width, pCodecCtx->height , QImage::Format_RGB32);

                QImage image = tmpImg.copy();



                //emit sig_GetOneFrame(image);



                //提取出图像中的R数据

                for(int i=0;i <pCodecCtx->width; i++)

                {

                    for(int j=0;j<pCodecCtx->height;j++)

                    {

                        QRgb rgb=image .pixel(i,j);

                        int r=qRed(rgb);

                        image.setPixel(i,j,qRgb(r,0,0));

                    }

                }

                //emit sig_GetRFrame(image);

            }

            else qDebug()<< "got_picture"<<got_picture ;



        }

        else qDebug()<< "packct->stream_index not video stream" ;



        av_free_packet(packet);//释放资源否则内存一直上升

        //msleep(100);

    }

    av_free(out_buffer);

    av_free(pFrameRGB);

    avcodec_close(pCodecCtx);

    avformat_close_input(&pFormatCtx);

return 0;

}