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; i 
             
               nb_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); } 
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值