四、像素格式转换和多路渲染
1. RGB与YUV格式互转
1.1 相关API
创建上下文
struct SwsContext *sws_getCachedContext(struct SwsContext *context, //转换上下文,NULL新创建,非NULL判断与现有参数是否一致,一致则返回,不一致则重新创建int srcW, int srcH, enum AVPixelFormat srcFormat,int dstW, int dstH, enum AVPixelFormat dstFormat,int flags, //选择支持变化的算法,双线性插值SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param //过滤器,赋空即可
);
转换格式
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, //Y为厚度,赋空即可,H为Heighuint8_t *const dst[], const int dstStride[]
);
1.2 实例
步骤:
- 申请三个空间分别存放Y、U、V数据、申请一个空间存放RGBA数据
- 读取文件中的YUV数据存放到申请的空间
- 通过API将YUV数据空间的数据转到RGBA空间数据
- 将RGBA空间的数据存储到输出文件
注意:将YUV的三个空间存入文件时,无法每帧写入一次,因为YUV再文件中的存储方式不是按帧存储的。
#include
#include
extern "C"
{
#include
}#pragma comment(lib, "swscale.lib")using namespace std;int main()
{//打开文件ifstream ifs;ofstream ofs;/*YUV to RGBA*/ifs.open("400_300_25.yuv", ios::binary);ofs.open("800_600_25.rgba", ios::binary);//申请YUV空间int yuv_width = 400;int yuv_height = 300;int rgba_width = 800;int rgba_height = 600;unsigned char* yuv_data[3] = { 0 };yuv_data[0] = new unsigned char[yuv_width * yuv_height];yuv_data[1] = new unsigned char[yuv_width * yuv_height / 4];yuv_data[2] = new unsigned char[yuv_width * yuv_height / 4];int yuv_linesize[3] = { yuv_width, yuv_width / 2, yuv_width / 2 };//申请RGBA空间unsigned char* rgba_data[1] = { new unsigned char[rgba_width * rgba_height * 4] };int rgba_linesize[1] = { rgba_width * 4 };//创建上下文SwsContext* sws_ctx = nullptr;sws_ctx = sws_getCachedContext(sws_ctx,yuv_width, yuv_height,AV_PIX_FMT_YUV420P,rgba_width, rgba_height,AV_PIX_FMT_RGBA,SWS_BILINEAR,0, 0, 0);//开始转换for(int i = 0 ; i < 25; i++){//读取YUVifs.read((char *)yuv_data[0], yuv_width * yuv_height); //读Yifs.read((char *)yuv_data[1], yuv_width * yuv_height / 4); //读Uifs.read((char *)yuv_data[2], yuv_width * yuv_height / 4); //读Vif (ifs.gcount() == 0)break;//开始转换 int ret = sws_scale(sws_ctx, yuv_data, yuv_linesize, 0, yuv_height, rgba_data, rgba_linesize);std::cout << ret << " ";ofs.write((char *)rgba_data[0], rgba_width * rgba_height * 4);}//关闭文件ifs.close();ofs.close();/*RGBA to YUV*///打开文件ifs.open("800_600_25.rgba", ios::binary);//ofs.open("400_300_25_new.yuv", ios::binary);memset(rgba_data[0], 0, rgba_width * rgba_height * 4);memset(yuv_data[0], 0, yuv_width * yuv_height);memset(yuv_data[1], 0, yuv_width * yuv_height / 4);memset(yuv_data[2], 0, yuv_width * yuv_height / 4);//创建上下文sws_ctx = sws_getCachedContext(sws_ctx,rgba_width, rgba_height,AV_PIX_FMT_RGBA,yuv_width, yuv_height,AV_PIX_FMT_YUV420P,SWS_BILINEAR,nullptr, nullptr, nullptr);//开始转换while (true){//读取数据ifs.read((char *)rgba_data[0], rgba_width * rgba_height * 4);if (ifs.gcount() == 0){break;}//开始转换int ret = sws_scale(sws_ctx,rgba_data, rgba_linesize,0, rgba_height,yuv_data, yuv_linesize);std::cout << ret << " " << flush;//ofs.write((char*)yuv_data[0], yuv_height * yuv_height);//ofs.write((char*)yuv_data[1], yuv_height * yuv_height / 4);//ofs.write((char*)yuv_data[2], yuv_height * yuv_height / 4);}ifs.close();//ofs.close();sws_freeContext(sws_ctx);delete yuv_data[0];delete yuv_data[1];delete yuv_data[2];delete rgba_data[0];
}
2. 多路播放器


上述画面应该是对齐产生的问题
如何支持多路渲染
- 创建多个XVideoView对象,每个对象都对应一个WinId创建的窗口。
- 渲染时,两个对象调用各自的接口进行渲染。
- 若要实现不同的频率,可以通过时间轮定时器的方法,也可以通过对不同任务遍历次数的不同来实现。
- 在读取文件时直接创建AVFrame,并把内容读到其中,然后根据文件的参数就可以直接进行初始化,初始化结束后就可以返回含有数据的AVFrame
#include "xvideo.h"
#include
#include "XVideoView.h"
#include
#include
#include
#include #pragma comment(lib, "SDL2.lib")using namespace std;static vector<XVideoView *> view;XVideo::XVideo(QWidget *parent): QWidget(parent)
{ui.setupUi(this);connect(this, SIGNAL(view_sig()), this, SLOT(view_slot()));//创建多个xview对象view.push_back(XVideoView::createXvideo());view.push_back(XVideoView::createXvideo());view[0]->setWinId((void*)ui.video1->winId());view[1]->setWinId((void*)ui.video2->winId());//cout << "current index" << ui.pix_fmt->currentIndex() << endl;//创建线程th_ = std::thread(&XVideo::Main, this);
}/*打开文件、根据设置的参数初始化渲染窗口*/
void XVideo::open(int i)
{//创建打开文件窗口QFileDialog fd;auto file_name = fd.getOpenFileName();if (file_name.isEmpty()){return;}cout << file_name.toLocal8Bit().data() << endl;//根据名字在第i个窗口打开文件view[i]->openFile(file_name.toStdString());//初始化第i个渲染窗口int width = 0;int height = 0;int pix_fmt_idx = 0;if (i == 0) //获取第i个窗口 要打开文件的参数{width = ui.width->value();height = ui.height->value();pix_fmt_idx = ui.pix_fmt->currentIndex();}if (i == 1){width = ui.width_2->value();height = ui.height_2->value();pix_fmt_idx = ui.pix_fmt_2->currentIndex();}auto fmt = XVideoView::XVIDEO_PIX_FMT_YUV420P;switch (pix_fmt_idx){case 0:fmt = XVideoView::XVIDEO_PIX_FMT_YUV420P;break;case 1:fmt = XVideoView::XVIDEO_PIX_FMT_RGBA;break;case 2:fmt = XVideoView::XVIDEO_PIX_FMT_ARGB;case 3:fmt = XVideoView::XVIDEO_PIX_FMT_BGRA;case 4:fmt = XVideoView::XVIDEO_PIX_FMT_RGB24;break;default:break;}//初始化第i个窗口view[i]->xvideo_init(width, height, fmt);
}void XVideo::Main()
{while (!is_exit){Msleep(10);view_sig();}
}void XVideo::view_slot()
{//cout << "this is view_slot" << endl;//设置帧率:设定一个时间,若时间到则执行view[i],若未到则执行continue//获取设定的帧率static int fps_arr[32] = { 0 }; //可以共32路视频static int last_time[32] = { 0 };fps_arr[0] = ui.set_fps->value();fps_arr[1] = ui.set_fps_2->value();//cout << "fps_arr[0] " << fps_arr[0] << endl;//cout << "fps_arr[1] " << fps_arr[1] << endl;for (int i = 0; i < view.size(); i++){if (fps_arr[i] <= 0) continue;//若当前时间 - 上次渲染的时间 < 设定的时间,则等待auto set_time = 1000 / fps_arr[i];//cout << "NowTime() - last_time[i] " << NowTime() - last_time[i] << endl;if (NowTime() - last_time[i] < set_time){//cout << "set_time " << set_time << endl;continue;}last_time[i] = NowTime();AVFrame* frame = nullptr;frame = view[i]->readFile();if (!frame) {//cout << i << " view_slot readFile->frame is nullptr" << endl;continue;};view[i]->xvideo_draw(frame);//显示帧率stringstream ss;ss << "fps: " << view[i]->render_fps();if (i == 0){ui.scale_fps->setText(ss.str().c_str());}else{ui.scale_fps_2->setText(ss.str().c_str());}}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
