fe7f45e7540a8e8ab51092117d9aed24d8d7f931
[yuv-player.git] / yuvPlayer.cpp
1 #include<iostream>
2 #include<fstream>
3 #include<thread>
4 #include<sstream>
5 #include<iomanip>
6 #include "SDL2/SDL.h"
7
8 using namespace std;
9
10 /*
11  * Convert MP4 to YUV
12  * ffmpeg -i big_buck_bunny_720p_10mb.mp4 -c:v rawvideo -pix_fmt yuv420p out.yuv
13  *
14  * Play YUV with ffplay
15  * ffplay -f rawvideo -pixel_format yuv420p -video_size 1280x720 -i ./out.yuv
16  *
17  * Usage:
18  *      1. press 'P' to pause or play as the given framerate
19  *      2. press 'q' to quit
20  *      3. press RIGHT to next frame, LEFT to previous frame
21  *      4. color space transform
22  */
23
24 class YuvPlayer 
25 {
26 public:
27     YuvPlayer(int width, int height, const string& title = "YUV Player");
28     void play(string fname, int fps = -1);
29     void toggleGrid();
30     ~YuvPlayer();
31
32 private:
33     bool readFrame(ifstream& in, uint8_t *raw, size_t size);
34     void updateRenderer(uint8_t *raw);
35     string file;
36     int width = 1280;
37     int height = 720;
38     SDL_Window *window;
39     SDL_Renderer *renderer;
40     SDL_Texture *texture;
41     unsigned int frameCnt = 0;
42     string title;
43     ifstream::pos_type last_pos;
44     bool isShowingGrid = false;
45     uint8_t *raw;
46 };
47
48
49 class ImageOperation {
50 public:
51     ImageOperation(uint8_t *r, int width, int height);
52
53
54 private:
55     int width;
56     int height;
57     uint8_t *raw;
58 };
59
60 YuvPlayer::YuvPlayer(int w, int h, const string& t)
61 : width(w)
62 , height(h)
63 , title(t)
64 {
65     SDL_Init(SDL_INIT_VIDEO);
66     window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
67     if (window == nullptr){
68         cout << "create error: " << SDL_GetError() << endl;
69     }
70     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
71     texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
72 }
73
74 YuvPlayer::~YuvPlayer()
75 {
76     SDL_DestroyTexture(texture);
77     SDL_DestroyRenderer(renderer);
78     SDL_DestroyWindow(window);
79     SDL_Quit();
80 }
81
82 void YuvPlayer::play(string file, int fps)
83 {
84     size_t frame_size = width * height * 3 / 2;
85     raw = (uint8_t *) malloc(sizeof(uint8_t) * frame_size);
86     ifstream in (file, ios::binary);
87     if (!in.is_open()) {
88         cout << "failed to open " << file << endl;
89     }
90
91
92     SDL_Event event;
93     bool quit = false;
94     bool isPlaying = true;
95     int interval = 1000 / fps;
96
97     while(!quit) {
98         interval = isPlaying ? 1000 / fps : -1;
99         SDL_WaitEventTimeout(&event, interval);
100         if(fps > 0 && isPlaying){
101             if(readFrame(in, raw, frame_size)){
102                 updateRenderer(raw);
103             }
104         }
105         switch(event.type){
106             case SDL_QUIT:
107                 quit = true;
108                 break;
109             case SDL_KEYDOWN:
110                 switch(event.key.keysym.sym){
111                     case SDLK_q:
112                         quit = true;
113                         break;
114                     case SDLK_RIGHT:
115                         if(readFrame(in, raw, frame_size)){
116                             updateRenderer(raw);
117                         }
118                         break;
119                     case SDLK_LEFT:
120                         frameCnt = frameCnt >= 2 ? frameCnt - 2 : 0;
121                         in.seekg(frameCnt * frame_size);
122                         if(readFrame(in, raw, frame_size)){
123                             updateRenderer(raw);
124                         }
125
126                         break;
127                     case SDLK_p:
128                         isPlaying = !isPlaying;
129                         break;
130                     case SDLK_g:
131                         toggleGrid();
132                         break;
133                     default:
134                         break;
135                 }
136             default:
137                 break;
138         }
139     }
140     free(raw);
141 }
142
143 void YuvPlayer::toggleGrid()
144 {
145     isShowingGrid = !isShowingGrid;
146     updateRenderer(raw);
147 }
148
149
150 bool YuvPlayer::readFrame(ifstream& in, uint8_t *raw, size_t size)
151 {
152     last_pos = in.tellg();
153     bool ret = (bool) in.read(reinterpret_cast<char *>(raw), size);
154     if (ret){
155         frameCnt++;
156         stringstream ss;
157         ss << this->title << " [" << setw(4) << to_string(frameCnt) << "]";
158         SDL_SetWindowTitle(window, ss.str().c_str());
159     }else {
160         cout << "read error" << endl;
161     }
162     return ret;
163 }
164
165 void YuvPlayer::updateRenderer(uint8_t *raw)
166 {    
167     SDL_UpdateTexture(texture, nullptr, raw, width * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_IYUV));
168     SDL_RenderClear(renderer);
169     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
170
171     if(isShowingGrid){
172         SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0xFF, 0xFF );             
173         //SDL_RenderDrawLine( renderer, 0, height / 2, width, height / 2 );
174         SDL_RenderDrawLine( renderer, 0, height / 3, width, height / 3 );
175         SDL_RenderDrawLine( renderer, 0, height * 2 / 3, width, height * 2 / 3 );
176
177         SDL_RenderDrawLine( renderer, width / 3, 0, width / 3, height );
178         SDL_RenderDrawLine( renderer, width * 2 / 3, 0, width * 2 / 3, height );
179
180     }
181
182     SDL_RenderPresent(renderer);
183 }
184
185 int main(int argc, char* argv[])
186 {
187     string fname = "out.yuv";
188     YuvPlayer player(1280, 720);
189     player.play(fname, 25);
190     //player.play(fname);
191 }