yuv文件並行解析播放


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM