上篇实现了对录屏数据的推流,现在要做一个播放器对推流的数据进行播放。同样此篇不介绍流媒体服务器的搭建(另行介绍)。
代码十分简单,只需要完成对接收数据的解码即可,这些在第一篇已经介绍,唯一注意的一点是,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);
}