add back frame
[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 drawGrid();
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 };
45
46
47 class ImageOperation {
48 public:
49     ImageOperation(uint8_t *r, int width, int height);
50
51
52 private:
53     int width;
54     int height;
55     uint8_t *raw;
56 };
57
58 YuvPlayer::YuvPlayer(int w, int h, const string& t)
59 : width(w)
60 , height(h)
61 , title(t)
62 {
63     SDL_Init(SDL_INIT_VIDEO);
64     window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
65     if (window == nullptr){
66         cout << "create error: " << SDL_GetError() << endl;
67     }
68     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
69     texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
70 }
71
72 YuvPlayer::~YuvPlayer()
73 {
74     SDL_DestroyTexture(texture);
75     SDL_DestroyRenderer(renderer);
76     SDL_DestroyWindow(window);
77     SDL_Quit();
78 }
79
80 void YuvPlayer::play(string file, int fps)
81 {
82     size_t frame_size = width * height * 3 / 2;
83     uint8_t *raw = (uint8_t *) malloc(sizeof(uint8_t) * frame_size);
84     ifstream in (file, ios::binary);
85     if (!in.is_open()) {
86         cout << "failed to open " << file << endl;
87     }
88
89
90     SDL_Event event;
91     bool quit = false;
92     bool isPlaying = true;
93     int interval = 1000 / fps;
94
95     while(!quit) {
96         interval = isPlaying ? 1000 / fps : -1;
97         SDL_WaitEventTimeout(&event, interval);
98         if(fps > 0 && isPlaying){
99             if(readFrame(in, raw, frame_size)){
100                 updateRenderer(raw);
101             }
102         }
103         switch(event.type){
104             case SDL_QUIT:
105                 quit = true;
106                 break;
107             case SDL_KEYDOWN:
108                 switch(event.key.keysym.sym){
109                     case SDLK_q:
110                         quit = true;
111                         break;
112                     case SDLK_RIGHT:
113                         if(readFrame(in, raw, frame_size)){
114                             updateRenderer(raw);
115                         }
116                         break;
117                     case SDLK_LEFT:
118                         frameCnt = frameCnt >= 2 ? frameCnt - 2 : 0;
119                         in.seekg(frameCnt * frame_size);
120                         if(readFrame(in, raw, frame_size)){
121                             updateRenderer(raw);
122                         }
123
124                         break;
125                     case SDLK_p:
126                         isPlaying = !isPlaying;
127                         break;
128                     case SDLK_g:
129                         drawGrid();
130                         break;
131                     default:
132                         break;
133                 }
134             default:
135                 break;
136         }
137     }
138     free(raw);
139 }
140
141 void YuvPlayer::drawGrid()
142 {
143 //    cout << "drawGrid" << endl;
144 //    SDL_RenderClear(renderer);
145 //                    SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
146 //
147 //    if(SDL_RenderDrawLine(renderer, 10, 10, 200, 200) == -1){
148 //        cout << "draw line error" << endl;
149 //    }
150 //    SDL_RenderCopy(renderer, texture, nullptr, nullptr);
151 //    SDL_RenderPresent(renderer);
152
153 //    SDL_UpdateTexture(texture, nullptr, raw, width * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_IYUV));
154     SDL_RenderClear(renderer);
155     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
156     SDL_RenderPresent(renderer);
157 }
158
159
160 bool YuvPlayer::readFrame(ifstream& in, uint8_t *raw, size_t size)
161 {
162     last_pos = in.tellg();
163     bool ret = (bool) in.read(reinterpret_cast<char *>(raw), size);
164     if (ret){
165         frameCnt++;
166         stringstream ss;
167         ss << this->title << " [" << setw(4) << to_string(frameCnt) << "]";
168         SDL_SetWindowTitle(window, ss.str().c_str());
169     }else {
170         cout << "read error" << endl;
171     }
172     return ret;
173 }
174
175 void YuvPlayer::updateRenderer(uint8_t *raw)
176 {    
177     if (frameCnt == 10){
178         memset(raw, 0x00, 1280 * 720);
179     }
180     SDL_UpdateTexture(texture, nullptr, raw, width * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_IYUV));
181     SDL_RenderClear(renderer);
182     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
183     SDL_RenderPresent(renderer);
184 }
185
186 int main(int argc, char* argv[])
187 {
188     string fname = "out.yuv";
189     YuvPlayer player(1280, 720);
190     //player.play(fname, 25);
191     player.play(fname);
192 }