近期中视频流处理,需实现视频在QT中显示,视频源利用Gstreamer采集,得到一帧YUV视频数据帧:
bool yuv420pToRgb24(char* ch_YuvData,uint i_width,uint i_height,uchar* ch_RgbData) { int temp = 0; int numOfPixel = i_width * i_height; int positionOfV = numOfPixel; int positionOfU = numOfPixel/4 + numOfPixel; for(int i=0; i<i_height; i++) { int startY = i*i_width; int step = (i/2)*(i_width/2); int startV = positionOfV + step; int startU = positionOfU + step; for(int j = 0; j < i_width; j++) { int Y = startY + j; int V = startV + j/2; int U = startU + j/2; int index = Y*3; temp = ((ch_YuvData[Y]&0xff) + 1.4075 * ((ch_YuvData[V]&0xff)-128)); ch_RgbData[index+2] = temp<0 ? 0 : (temp > 255 ? 255 : temp); temp = ((ch_YuvData[Y]&0xff) - 0.3455* ((ch_YuvData[U]&0xff)-128) - 0.7169*((ch_YuvData[V]&0xff)-128)); ch_RgbData[index+1] = temp<0 ? 0 : (temp > 255 ? 255 : temp); temp = ((ch_YuvData[Y]&0xff) + 1.779* ((ch_YuvData[U]&0xff)-128)); ch_RgbData[index+0] =temp<0 ? 0 : (temp > 255 ? 255 : temp); } } return true; }
char* ch_YuvData---原始视频帧YUV
uint i_width ---视频宽度
uint i_height ---视频高度
uchar* ch_RgbData ---转换后RGB24
该转换方式亲测可用视频YUV为I420格式,具体格式排列自行百度;由于后期需移植嵌入式设备,该方法CPU占用率略高,且转换效率低,双循环,640*480就要执行307200次,这里我把数据都转int尽量避免浮点运算。网上也有大神写了优化方案,自行百度;
引用FFMPEG,将一帧数据转换rgb后使用qt中QImage显示一帧数据;
#ifndef VOIDSHOWIMAGE_H #define VOIDSHOWIMAGE_H #include <QWidget> #include <QLabel> #include <QImage> extern "C" { #include "libavutil/frame.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" } class VoidShowImage:public QWidget { public: VoidShowImage(QWidget *parent =nullptr); ~VoidShowImage(); void showImage(QByteArray arrayData); private: // 初始化 void initPlay(); // 转换数据 void switchYuvToRgb(void* p_yuvData, int len); private slots: void paintEvent(QPaintEvent *); private: QLabel *pq_videoShowLabel; char *pq_YuvData; uint8_t *yuvBuffer; AVFrame *pFrame ; AVFrame *pFrameRGB; uint8_t * rgbBuffer; SwsContext *img_convert_ctx; int m_iWidth; int m_iHeight; }; #endif // VOIDSHOWIMAGE_H
#include "VoidShowImage.h" #include <QGridLayout> #include <QDebug> #include <QPainter> VoidShowImage::VoidShowImage(QWidget *parent): QWidget (parent) { m_iWidth = 640; m_iHeight = 480; initPlay(); pq_YuvData = new char[m_iWidth * m_iHeight * 3 / 2]; memset(pq_YuvData,0,m_iWidth * m_iHeight * 3 /2); QGridLayout *pq_gridLayout = new QGridLayout; pq_videoShowLabel = new QLabel(this); pq_gridLayout->addWidget(pq_videoShowLabel); this->setLayout(pq_gridLayout); } VoidShowImage::~VoidShowImage() { sws_freeContext(img_convert_ctx); } void VoidShowImage::showImage(QByteArray arrayData) { if (arrayData.isEmpty()) return; memcpy(pq_YuvData,arrayData.data(),arrayData.size()); switchYuvToRgb(pq_YuvData,arrayData.length()); update(); } void VoidShowImage::paintEvent(QPaintEvent *) { //把这个RGB数据 用QImage加载 QPainter painter(this); QImage tmpImg((uchar *)rgbBuffer,m_iWidth,m_iHeight,QImage::Format_RGB32); QImage img = tmpImg.scaled(this->size(),Qt::IgnoreAspectRatio); int x = this->width() - img.width(); int y = this->height() - img.height(); painter.drawImage(QPoint(x,y),img); //画出图像 } // 初始化 void VoidShowImage::initPlay() { //width和heigt为传入的分辨率的大小 int yuvSize = m_iWidth * m_iHeight * 3 /2; yuvBuffer = (uint8_t *)malloc(yuvSize); //为每帧图像分配内存 pFrame = av_frame_alloc(); pFrameRGB = av_frame_alloc(); int numBytes = avpicture_get_size(PIX_FMT_RGB32, m_iWidth,m_iHeight); rgbBuffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t)); avpicture_fill((AVPicture *) pFrameRGB, rgbBuffer, PIX_FMT_RGB32,m_iWidth, m_iHeight); //设置图像转换上下文 img_convert_ctx = sws_getContext(m_iWidth, m_iHeight, AV_PIX_FMT_YUV420P, m_iWidth, m_iHeight, PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr); } // 转换数据 void VoidShowImage::switchYuvToRgb(void* p_yuvData, int len) { avpicture_fill((AVPicture *) pFrame, (uint8_t *)p_yuvData, AV_PIX_FMT_YUV420P, m_iWidth, m_iHeight);//这里的长度和高度跟之前保持一致 //转换图像格式,将解压出来的YUV420P的图像转换为RGB的图像 sws_scale(img_convert_ctx, (uint8_t const * const *) pFrame->data, pFrame->linesize, 0, m_iHeight, pFrameRGB->data, pFrameRGB->linesize); }
还没有评论,来说两句吧...