#include<iostream>
#include<fstream>
+#include<thread>
+#include<sstream>
+#include<iomanip>
#include "SDL2/SDL.h"
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:
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;
}
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;
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;
}
}
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);
}