#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; }