ffpemg使用四:ffmepg解码+Qt显示即播放器的实现

        上篇实现了对录屏数据的推流,现在要做一个播放器对推流的数据进行播放。同样此篇不介绍流媒体服务器的搭建(另行介绍)。

        代码十分简单,只需要完成对接收数据的解码即可,这些在第一篇已经介绍,唯一注意的一点是,Qt不支持yuv格式,需要对解码的yuv做rgb的转换;转换的具体原理不再讲,虽然很简单,只是做矩阵运算,但因为是乘法运算,cup处理乘除法的效率极低,所以仍推荐用ffmpeg的转换函数sws_scale(),它里面有多媒体的优化指令,效率要高。

       思路:用线程对接受的网络数据解码,然后发送至主线程,主线程进行一张张图片的展示;(个人感觉这种播放方式效率其实不高,推流至流媒体的时候应该已是flv格式,若如此可用Qt的Phonen框架进行播放,但还没验证,目前先用这种方法实现)

上代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include 
#include 

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}
//按钮用于开始线程,线程用于ffmpeg解码
void MainWindow::on_pushButton_clicked()
{
    connect(&mythread,&ffmpegThread::sendImage ,this,&MainWindow::showImage);
    mythread.start();

}
//此为槽,用于接收ffmpeg解码线程发送的解码一帧数据的信号
void MainWindow::showImage(QImage image)
{
    pix=QPixmap::fromImage(image.scaled(image.width(),image.height()));
    update();
}
//重载 显示
void MainWindow::paintEvent(QPaintEvent *e)
{
    ui->label->setPixmap(pix);
}#include "ffmpegthread.h"
#include 

extern "C"
{
#include 
#include 
#include 
#include 
#include 
#include 
#include 
}

ffmpegThread::ffmpegThread()
{
}
//线程 可见与《ffempeg使用一》基本一致;区别是打开的文件来自url,另外需要对解码后的yuv420p转为rgb,因为Qt不支持操作yuv
void ffmpegThread::run()
{
    AVFormatContext *pFormatCtx;
    int             i, videoindex;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();
    avdevice_register_all();

    char filepath[]="rtmp://192.168.80.31:8811/myapp/test5";
    if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
        printf("Couldn't open input stream.\n");
    }
    if(avformat_find_stream_info(pFormatCtx,NULL)<0)
    {
        printf("Couldn't find stream information.\n");
    }
    videoindex=-1;
    for(i=0; inb_streams; i++)
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoindex=i;
            break;
        }
    if(videoindex==-1)
    {
        printf("Didn't find a video stream.\n");
    }
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        printf("Codec not found.\n");
    }
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
    {
        printf("Could not open codec.\n");
    }
    AVFrame *pFrame,*pFrameYUV,*pFrameRGB;
    pFrame=av_frame_alloc();
    pFrameYUV=av_frame_alloc();
    pFrameRGB=av_frame_alloc();

    unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
    unsigned char *rgbBuffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height));

    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);//以上就是为pFrameRGB挂上buffer。这个buffer是用于存缓冲数据的

    //
    int ret, got_picture;

    AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));

#if OUTPUT_YUV420P
    FILE *fp_yuv=fopen("output.yuv","wb+");
#endif

    struct SwsContext *img_convert_ctx,*img_convert_ctx_rgb;
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

    //需要注意的地方
    avpicture_fill((AVPicture *)pFrameRGB,rgbBuffer,AV_PIX_FMT_RGB32,pCodecCtx->width, pCodecCtx->height);
    img_convert_ctx_rgb=sws_getContext(pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_RGB32,SWS_BICUBIC, NULL, NULL, NULL);
    for (;;) {
            if(av_read_frame(pFormatCtx, packet)>=0)
            {
                if(packet->stream_index==videoindex)
                {
                    ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//用packet充填pFrame,pFrame->data = packet->data?  pFrame->linesize=packet->linesize
                    if(ret < 0){
                        printf("Decode Error.\n");
                    }
                    if(got_picture)
                    {
                         sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

                        if(img_convert_ctx_rgb != NULL)
                        {
                            //转码为rgb32
                            sws_scale(img_convert_ctx_rgb,pFrameYUV->data,pFrameYUV->linesize,0,pCodecCtx->height,pFrameRGB->data,pFrameRGB->linesize);
                            //构造QImage,用于主页面显示
                            QImage image((uchar *)pFrameRGB->data[0],pCodecCtx->width, pCodecCtx->height,QImage::Format_ARGB32);
                            emit sendImage(image);
                        }
                    }


                }
                av_free_packet(packet);
            }
    }

    if(img_convert_ctx)
        sws_freeContext(img_convert_ctx);
    if(img_convert_ctx_rgb)
        sws_freeContext(img_convert_ctx_rgb);
    av_free(rgbBuffer);
    av_free(out_buffer);
    av_free(pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}


你可能感兴趣的:(linux)