Fix some key event issue
[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     ~YuvPlayer();
30
31 private:
32     bool readFrame(ifstream& in, uint8_t *raw, size_t size);
33     void updateRenderer(uint8_t *raw);
34     string file;
35     int width = 1280;
36     int height = 720;
37     SDL_Window *window;
38     SDL_Renderer *renderer;
39     SDL_Texture *texture;
40     long frameCnt = 0;
41     string title;
42 };
43
44 YuvPlayer::YuvPlayer(int w, int h, const string& t)
45 : width(w)
46 , height(h)
47 , title(t)
48 {
49     SDL_Init(SDL_INIT_VIDEO);
50     window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
51     if (window == nullptr){
52         cout << "create error: " << SDL_GetError() << endl;
53     }
54     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
55     texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
56 }
57
58 YuvPlayer::~YuvPlayer()
59 {
60     SDL_DestroyTexture(texture);
61     SDL_DestroyRenderer(renderer);
62     SDL_DestroyWindow(window);
63     SDL_Quit();
64 }
65
66 void YuvPlayer::play(string file, int fps)
67 {
68     size_t frame_size = width * height * 3 / 2;
69     uint8_t *raw = (uint8_t *) malloc(sizeof(uint8_t) * frame_size);
70     ifstream in (file, ios::binary);
71     if (!in.is_open()) {
72         cout << "failed to open " << file << endl;
73     }
74     cout << "fps " << fps << endl;
75
76     SDL_Event event;
77     bool quit = false;
78     bool isPlaying = true;
79     int interval = 1000 / fps;
80
81     while(!quit) {
82         interval = isPlaying ? 1000 / fps : -1;
83         SDL_WaitEventTimeout(&event, interval);
84         if(fps > 0 && isPlaying){
85             if(readFrame(in, raw, frame_size)){
86                 updateRenderer(raw);
87             }
88         }
89         switch(event.type){
90             case SDL_QUIT:
91                 quit = true;
92                 break;
93             case SDL_KEYDOWN:
94                 switch(event.key.keysym.sym){
95                     case SDLK_q:
96                         quit = true;
97                         break;
98                     case SDLK_RIGHT:
99                         cout << "Right click" << endl;
100                         if(readFrame(in, raw, frame_size)){
101                             updateRenderer(raw);
102                         }
103                         break;
104                     case SDLK_p:
105                         isPlaying = !isPlaying;
106                         break;
107                     default:
108                         break;
109                 }
110             default:
111                 break;
112         }
113     }
114     free(raw);
115 }
116
117 bool YuvPlayer::readFrame(ifstream& in, uint8_t *raw, size_t size)
118 {
119     bool ret = (bool) in.read(reinterpret_cast<char *>(raw), size);
120     if (ret){
121         frameCnt++;
122         stringstream ss;
123         ss << this->title << " [" << setw(4) << to_string(frameCnt) << "]";
124         cout << "read frame " << frameCnt << endl;
125         SDL_SetWindowTitle(window, ss.str().c_str());
126     }
127     return ret;
128 }
129
130 void YuvPlayer::updateRenderer(uint8_t *raw)
131 {    
132     SDL_UpdateTexture(texture, nullptr, raw, width * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_IYUV));
133     SDL_RenderClear(renderer);
134     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
135     SDL_RenderPresent(renderer);
136 }
137
138 int main(int argc, char* argv[])
139 {
140     string fname = "out.yuv";
141     YuvPlayer player(1280, 720);
142     player.play(fname, 25);
143 }