#include <stdio.h>
#include <SDL2/SDL.h>
#include <sys/time.h>
#include <time.h>
#include <mpi.h>
#include <signal.h>
int dest;
const int bpp=24; //bits per pixel 像素深度
//用24个二进制位来表示颜色,此时能表示16777216种颜色
//RGB三种颜色各自的精度都更高了(RGB各8位),叫RGB888,也叫RGB24
int screen_w=500,screen_h=500; //窗口高度和宽度
const int pixel_w=176*2,pixel_h=144*2; //图片分辨率
double difftimeval(const struct timeval *start, const struct timeval *end)
{ //计算时间差的函数 参数分别是起始时间和结束时间
double d;
time_t s; //秒
suseconds_t u; //微秒
s = start->tv_sec - end->tv_sec;
u = start->tv_usec - end->tv_usec;
//if (u < 0)
// --s;
d = s;
d *= 1000000.0; //1秒=10^6秒
d += u;
return d;
}
//将RGB24 / BGR24转换为RGB32 / BGR32
//并在需要时更改Endian
//big endian 数据存储:高字节在低地址, 低字节在高地址
//small endian 数据存储:高字节在高地址, 低字节在低地址
void CONVERT_24to32(unsigned char *image_in,unsigned char *image_out,int w,int h){
int i,j;
for(i =0;i<h;i++)
for(j=0;j<w;j++){
//内存中的ARGB格式大字节序(低地址保存高MSB,这里是A):A | R | G | B
//ARGB格式Little Endian(低地址,低MSB,这里是B)在内存中:B | G | R | A
//Big Endian or Small Endian
//"ARGB" order:high bit -> low bit.
if(SDL_BYTEORDER==SDL_LIL_ENDIAN){ //如果是内存中的rgb格式是小端
//Little Endian (x86): R|G|B --> B|G|R|A
image_out[(i*w+j)*4+0]=image_in[(i*w+j)*3+2];
image_out[(i*w+j)*4+1]=image_in[(i*w+j)*3+1];
image_out[(i*w+j)*4+2]=image_in[(i*w+j)*3];
image_out[(i*w+j)*4+3]='0';
}else{ //如果是内存中的rgb格式是大端
//Big Endian: R|G|B --> A|R|G|B
image_out[(i*w+j)*4]='0';
memcpy(image_out+(i*w+j)*4+1,image_in+(i*w+j)*3,3); //从存储区image_in+(i*w+j)*3复制3个字符到存储区image_out+(i*w+j)*4+1
}
}
}
//Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
int thread_exit=0;
//事件队列自身是由一系列的SDL_Event结构体组成,一个SDL_Event对应一个等待事件
int refresh_video(void *opaque){
while (thread_exit==0) { //SDL_Event是个联合体,是SDL中所有事件处理的核心
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(50);
}
return 0;
}
int main(int argc, char* argv[])
{
struct timeval start,end;
gettimeofday(&start,NULL); //SDL2库函数,计算时间差
printf("start: %f\n%f\n",start.tv_sec,start.tv_usec); //开始的时间(分别以秒和微秒的形式来表示)
int blocks = pixel_h*pixel_w/176/144; //每一块的大小
int c = 9999; //the sum
int num ;
int myrank;
unsigned char buffer[pixel_w*pixel_h*bpp/8];//middle
//BPP=32
unsigned char buffer_convert[pixel_w*pixel_h*4];//big
unsigned char yuv_buffer[pixel_w*pixel_h*bpp/8/2]; //small
signal(2,SIG_DFL);
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
MPI_Comm_size(MPI_COMM_WORLD, &num);//返回通信的进程数
//unsigned char yuv_buffer[pixel_w*pixel_h*bpp/8/2];
//printf("num is %d\n",num);
//printf("myrank is %d\n",myrank);
if(myrank ==0)
{
printf("I am (0):%d\n",myrank);
c=0;
FILE *fp=NULL;
fp=fopen("x.yuv","rb+"); //打开一个yuv格式的文件
if(fp==NULL){
printf("cannot open this file\n");
return -1;
}
while(1)
{
if (fread(yuv_buffer, 1, pixel_w*pixel_h*bpp/8/2, fp) != pixel_w*pixel_h*bpp/8/2) //fread函数返回值是实际读取元素的数目
{ //yuv_buffer:读取的数据存放的内存的指针
//1:每次读取的字节数
//pixel_w*pixel_h*bpp/8/2:读取的次数
// Loop
break;
}
/*int m=0;
for(;m<1000;m++){
printf("%d ",yuv_buffer[m]);
}if(m!=0&&m%100==0)printf("\n");*/
//num 0: process broadcast
c++;
dest = (c-1)%(num-1)+1; //目标进程的rank值
int f=0;
for(;f<blocks;f++)//将图片分配到所有的进程
MPI_Send(yuv_buffer+f*pixel_w*pixel_h*bpp/8/2/blocks,pixel_w*pixel_h*bpp/8/2/blocks,MPI_CHAR,dest,0,MPI_COMM_WORLD);
//第一次发送的是每一帧图片
//yuv_buffer+f*pixel_w*pixel_h*bpp/8/2/blocks:发送缓冲区的起始地址
//pixel_w*pixel_h*bpp/8/2/blocks:需要发送信息的元素个数
//MPI_CHAR:发送消息的数据类型
//dest:目标进程的rank号
}
printf("read finished!\n");
//printf("c is %d\n",c);
}
//MPI_BCAST是从一个序列号为0的进程将一条消息广播发送到组内的所有进程
MPI_Bcast(&c,1,MPI_INT,0,MPI_COMM_WORLD);
if(myrank==0)
{
//SDL_Init是SDL运行的初始
if(SDL_Init(SDL_INIT_VIDEO)) { //初始化视频子系统
printf( "Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
SDL_Window *screen;
//SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
//SDL_CreateWindow函数参数分别表示:窗口标题、窗口x、y坐标、窗口的宽和高以及一些其他属性
if(!screen) {
printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
return -1;
}
//创建窗口渲染器
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
//screen指渲染的目标窗口,设置“-1”则初始化默认的渲染设备,最后一个参数为0默认使用SDL_RENDERER_ACCELERATED(使用硬件加速)
Uint32 pixformat=0;
pixformat= SDL_PIXELFORMAT_BGR888; //创建纹理的格式
SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
//sdlRenderer:目标渲染器 pixformat:纹理的格式
//SDL_TEXTUREACCESS_STREAMING:变化频繁
SDL_Rect sdlRect;
SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
SDL_Event event;
int source =1;
//printf("c is %d\n",c);
while(1)
{
SDL_WaitEvent(&event);
if(event.type==REFRESH_EVENT)
{
//Process 0 accepts buffer in turn
//printf ("wait %d return\n",(source - 1)%(num-1)+1);
int f = 0;
for(;f<blocks;f++)
{
MPI_Recv(buffer_convert+f*pixel_w*pixel_h*4/blocks,pixel_w*pixel_h*4/blocks,MPI_CHAR,(source - 1)%(num-1)+1,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE); //accepting buffer_convert
//第一个参数:接收缓冲区的起始地址 第二个参数:需要接收元素的个数 (source - 1)%(num-1)+1:源进程的rank值
}
//printf(" accept %d finished\n",(source - 1)%(num-1)+1);
SDL_UpdateTexture( sdlTexture, NULL, buffer_convert, pixel_w*4);
//sdlTexture:目标纹理 buffer_convert:像素数据 pixel_w*4:一行像素数据的字节数
if(source == c)
{
//printf("exit!!!!00000000000000\n");
break;
}
source++;
//printf("source !!!!%d\n",source);
//FIX: If window is resize
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = screen_w;
sdlRect.h = screen_h;
SDL_RenderClear( sdlRenderer );
SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);
SDL_RenderPresent( sdlRenderer );
}
else if(event.type==SDL_WINDOWEVENT){
//If Resize
SDL_GetWindowSize(screen,&screen_w,&screen_h);
}
else if(event.type==SDL_QUIT){
break;
}
}
}
else if(myrank!=0)
//pYUV = yuv_buffer pBGR24 = buffer width= pixel_w height= pixel_h
{
int count =0; //Number of frames made by each process
//每个进程处理的帧数目
while(1)
{
if(myrank <= c%(num-1)) //num是进程总数
{
if(count == (c/(num-1))+1)
{
break;
}
}
else
{
//printf("xxxx%d\n",c/(num-1));
if(count == (c/(num-1)))
{
//printf("count weituichu%d\n",count);
//printf("%d myrank exit\n",myrank);
break;
}
}
int f=0;
for(;f<blocks;f++)
{
MPI_Recv(yuv_buffer+f*pixel_w*pixel_h*bpp/8/2/blocks,pixel_w*pixel_h*bpp/8/2/blocks,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
//收到一些图片帧
}
printf("%d accept finished\n",myrank);
if (pixel_w < 1 || pixel_h < 1 || yuv_buffer == NULL || buffer == NULL)
return 0;
const long len = pixel_w * pixel_h;
unsigned char* yData = yuv_buffer;
unsigned char* uData = &yData[len];
unsigned char* vData = &uData[len >> 2];
int bgr[3];
int yIdx,uIdx,vIdx,idx;
int i,j,k;
for(i=0;i<pixel_h;i++)
for (j = 0;j < pixel_w;j++){
yIdx = i * pixel_w + j;
vIdx = (i/2) * (pixel_w/2) + (j/2);
uIdx = vIdx;
//计算rgb yuv解码 yuv采用4:2:2方式采样
bgr[0] = (int)(yData[yIdx] + 1.732446 * (uData[vIdx] - 128)); // b weight
bgr[1] = (int)(yData[yIdx] - 0.698001 * (uData[uIdx] - 128) - 0.703125 * (vData[vIdx] - 128)); // g weight
bgr[2] = (int)(yData[yIdx] + 1.370705 * (vData[uIdx] - 128)); // r weight
for (k = 0;k < 3;k++){
idx = (i * pixel_w + j) * 3 + k;
if(bgr[k] >= 0 && bgr[k] <= 255)
buffer[idx] = bgr[k];
else
buffer[idx] = (bgr[k] < 0)?0:255;
}
}
CONVERT_24to32(buffer,buffer_convert,pixel_w,pixel_h);
//buffer_convert return to process 0
//printf("%dtranscode finished \n",myrank);
for(f=0;f<blocks;f++)
{
MPI_Send(buffer_convert+f*pixel_w*pixel_h*4/blocks,pixel_w*pixel_h*4/blocks,MPI_CHAR,0,0,MPI_COMM_WORLD);
//第二次发送,发送的数据是每一帧里面的数据
}
count++;
printf("%dsend to process 0 \n",myrank);
}
}
gettimeofday(&end, NULL); //get the end_time
//计算结束的时间
printf("myrank %d exit\n",myrank);
if(myrank == 0)
printf("%.0f\n", difftimeval(&end, &start)); //计算时间的函数
MPI_Finalize();
return 0;
}