Add luma only
[yuv-player.git] / yuvPlayer.cpp
index 322975e..fb8c1a9 100644 (file)
@@ -1,5 +1,8 @@
 #include<iostream>
 #include<fstream>
+#include<thread>
+#include<sstream>
+#include<iomanip>
 #include "SDL2/SDL.h"
 
 using namespace std;
@@ -11,13 +14,20 @@ using namespace std;
  * Play YUV with ffplay
  * ffplay -f rawvideo -pixel_format yuv420p -video_size 1280x720 -i ./out.yuv
  *
+ * Usage:
+ *      1. press 'P' to pause or play as the given framerate
+ *      2. press 'q' to quit
+ *      3. press RIGHT to next frame, LEFT to previous frame
+ *      4. color space transform
  */
 
 class YuvPlayer 
 {
 public:
-    YuvPlayer(int width, int height);
-    void play(string fname);
+    YuvPlayer(int width, int height, const string& title = "YUV Player");
+    void play(string fname, int fps = -1);
+    void toggleGrid();
+    void toggleYumaOnly();
     ~YuvPlayer();
 
 private:
@@ -29,14 +39,33 @@ private:
     SDL_Window *window;
     SDL_Renderer *renderer;
     SDL_Texture *texture;
+    unsigned int frameCnt = 0;
+    string title;
+    ifstream::pos_type last_pos;
+    bool isShowingGrid = false;
+    bool isLumOnly = false;
+    uint8_t *raw;
 };
 
-YuvPlayer::YuvPlayer(int w, int h)
+
+class ImageOperation {
+public:
+    ImageOperation(uint8_t *r, int width, int height);
+
+
+private:
+    int width;
+    int height;
+    uint8_t *raw;
+};
+
+YuvPlayer::YuvPlayer(int w, int h, const string& t)
 : width(w)
 , height(h)
+, title(t)
 {
     SDL_Init(SDL_INIT_VIDEO);
-    window = SDL_CreateWindow("YUV Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
+    window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
     if (window == nullptr){
         cout << "create error: " << SDL_GetError() << endl;
     }
@@ -46,23 +75,49 @@ YuvPlayer::YuvPlayer(int w, int h)
 
 YuvPlayer::~YuvPlayer()
 {
+    SDL_DestroyTexture(texture);
+    SDL_DestroyRenderer(renderer);
     SDL_DestroyWindow(window);
     SDL_Quit();
 }
 
-void YuvPlayer::play(string file)
+// https://wiki.videolan.org/YUV/, YCrCb
+// I420, YUV420p,  YYYY YYYY UU VV
+static void lumaOnly(uint8_t *raw, int w, int h)
+{
+    memset(raw + w * h, 128, w * h / 2); // UV 128
+    //memset(raw + w * h, 0, w * h / 2); // UV 128
+    //memset(raw + w * h, 0, w * h / 2); // UV 0, G only
+    //memset(raw + w * h, 255, w * h / 2); // UV 0, R only
+    //memset(raw + w * h, 128, w * h / 4); // U 128
+    //for (int i = 0; i < w * h; i++){
+    //    raw[i] = raw[i] / 2; // Y to 1/2
+    //}
+}
+
+void YuvPlayer::play(string file, int fps)
 {
     size_t frame_size = width * height * 3 / 2;
-    uint8_t *raw = (uint8_t *) malloc(sizeof(uint8_t) * frame_size);
+    raw = (uint8_t *) malloc(sizeof(uint8_t) * frame_size);
     ifstream in (file, ios::binary);
     if (!in.is_open()) {
         cout << "failed to open " << file << endl;
     }
 
+
     SDL_Event event;
     bool quit = false;
+    bool isPlaying = true;
+    int interval = 1000 / fps;
+
     while(!quit) {
-        SDL_WaitEvent(&event);
+        interval = isPlaying ? 1000 / fps : -1;
+        SDL_WaitEventTimeout(&event, interval);
+        if(fps > 0 && isPlaying){
+            if(readFrame(in, raw, frame_size)){
+                updateRenderer(raw);
+            }
+        }
         switch(event.type){
             case SDL_QUIT:
                 quit = true;
@@ -73,9 +128,26 @@ void YuvPlayer::play(string file)
                         quit = true;
                         break;
                     case SDLK_RIGHT:
-                        readFrame(in, raw, frame_size);
-                        updateRenderer(raw);
+                        if(readFrame(in, raw, frame_size)){
+                            updateRenderer(raw);
+                        }
+                        break;
+                    case SDLK_LEFT:
+                        frameCnt = frameCnt >= 2 ? frameCnt - 2 : 0;
+                        in.seekg(frameCnt * frame_size);
+                        if(readFrame(in, raw, frame_size)){
+                            updateRenderer(raw);
+                        }
+
                         break;
+                    case SDLK_p:
+                        isPlaying = !isPlaying;
+                        break;
+                    case SDLK_g:
+                        toggleGrid();
+                        break;
+                    case SDLK_y:
+                        toggleYumaOnly();
                     default:
                         break;
                 }
@@ -85,24 +157,67 @@ void YuvPlayer::play(string file)
     }
     free(raw);
 }
+void YuvPlayer::toggleYumaOnly()
+{
+    isLumOnly = !isLumOnly;
+    updateRenderer(this->raw);
+}
+
+void YuvPlayer::toggleGrid()
+{
+    isShowingGrid = !isShowingGrid;
+    updateRenderer(raw);
+}
+
 
 bool YuvPlayer::readFrame(ifstream& in, uint8_t *raw, size_t size)
 {
-    in.read(reinterpret_cast<char *>(raw), size);
-    return true;
+    last_pos = in.tellg();
+    bool ret = (bool) in.read(reinterpret_cast<char *>(raw), size);
+    if (ret){
+        frameCnt++;
+        stringstream ss;
+        ss << this->title << " [" << setw(4) << to_string(frameCnt) << "]";
+        SDL_SetWindowTitle(window, ss.str().c_str());
+    }else {
+        cout << "read error" << endl;
+    }
+    return ret;
 }
 
 void YuvPlayer::updateRenderer(uint8_t *raw)
 {    
+    if (isLumOnly){
+        lumaOnly(raw, width, height);
+    }
     SDL_UpdateTexture(texture, nullptr, raw, width * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_IYUV));
     SDL_RenderClear(renderer);
     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
+
+    if(isShowingGrid){
+        SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0xFF, 0xFF );            
+        //SDL_RenderDrawLine( renderer, 0, height / 2, width, height / 2 );
+        SDL_RenderDrawLine( renderer, 0, height / 3, width, height / 3 );
+        SDL_RenderDrawLine( renderer, 0, height * 2 / 3, width, height * 2 / 3 );
+
+        SDL_RenderDrawLine( renderer, width / 3, 0, width / 3, height );
+        SDL_RenderDrawLine( renderer, width * 2 / 3, 0, width * 2 / 3, height );
+
+    }
+
     SDL_RenderPresent(renderer);
 }
 
-int main()
+int main(int argc, char* argv[])
 {
-    string fname = "out.yuv";
+//    string fname(argv[1] ? argv[1] : "out.yuv");
+//    size_t width  = atoi(argv[2] ? argv[2] : "1280");
+//    size_t height = atoi(argv[3] ? argv[3] : "720");
+//
+//    int fps = atoi(argv[4] ? argv[4] : "20");
+//    cout << fname << "\n" << width << "\n" << height << "\n" << fps << "\n" << endl;
+//
     YuvPlayer player(1280, 720);
-    player.play(fname);
+    player.play("out.yuv", 25);
+    //player.play(fname);
 }