Skip to content

Commit

Permalink
new features (see pr comment) (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
SkwalExe authored Aug 12, 2022
1 parent 7f49b86 commit 9d6b22b
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 87 deletions.
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Build
```bash
# 📂 ascii-video-player/
make
# ⚙️ binary -> build/ascii-video-player
```

Add binary to bin folder
Expand All @@ -23,12 +24,6 @@ make install

### Build from source - Linux 🐧 & Windows 🪟

**Clone this repo**

```bash
git clone https://github.com/SkwalExe/ascii-video-player.git
```

Create a build folder

```bash
Expand Down Expand Up @@ -66,6 +61,25 @@ sudo cp ascii-video-player /usr/local/bin/

![](images/usage.png)

## Examples ✨

**The two videos are provided in the repository.**
### **And yes, all this in your terminal.**

```bash
# 📂 ascii-video-player
ascii-video-player bad-apple.mp4
```

![](images/1.gif)

```bash
# 📂 ascii-video-player
ascii-video-player bad-apple-amv-colored.mp4 -b
```

![](images/2.gif)

# Uninstall 🗑

## With make
Expand Down
Binary file added bad-apple-amv-colored.mp4
Binary file not shown.
Binary file modified images/1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/usage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 37 additions & 40 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <iostream>
#include <fstream>
#include <cstring>
#include "play_video.h"
#include "utils.h"
Expand All @@ -13,19 +12,10 @@ typedef enum
help
} command;

bool file_exists(char *fileName)
{
ifstream infile(fileName);
return infile.good();
}

int main(int argc, char *argv[])
{
command cmd = play;
char *file = nullptr;
bool show_status = false;
bool stretch = false;
float enlargement = 1.8;
Params params;

for (int i = 1; i < argc; i++)
{
Expand All @@ -40,45 +30,56 @@ int main(int argc, char *argv[])
}
else if (strcmp(arg, "--show-status") == 0 || strcmp(arg, "-s") == 0)
{
show_status = true;
params.show_status = true;
}
else if (strcmp(arg, "--enlargement") == 0 || strcmp(arg, "-e") == 0)
else if (strcmp(arg, "--stretch") == 0 || strcmp(arg, "-t") == 0)
{
enlargement = atof(argv[++i]);
if (enlargement <= 0)
{
cout << RED << "[ x ] : Invalid enlargement factor: " << enlargement << RESET << endl;
exit(1);
}
i++;
params.stretch = true;
}
else if (strcmp(arg, "--stretch") == 0 || strcmp(arg, "-t") == 0)
else if (strcmp(arg, "--colors") == 0 || strcmp(arg, "-c") == 0)
{
params.colors = true;
}
else if (strcmp(arg, "--blocks") == 0 || strcmp(arg, "-b") == 0)
{
params.blocks = true;
params.colors = true;
}
else if (strcmp(arg, "--inverse") == 0 || strcmp(arg, "-i") == 0)
{
params.inverse = true;
}
else if (strcmp(arg, "--black-level") == 0 || strcmp("-l", arg) == 0)
{
params.black_level = atoi(argv[++i]);
}
else if (strcmp(arg, "--color-background") == 0 || strcmp(arg, "-g") == 0)
{
stretch = true;
params.color_background = true;
}
else if (file != nullptr)
else if (params.file != nullptr)
{
std::cout << RED << "[ x ] : Invalid argument: " << arg << RESET << endl;
cmd = help;
}
else
{
if (file_exists(arg))
if (!file_exists(arg))
{
file = arg;
std::cout << RED << "[ x ] : File not found: " << arg << RESET << endl;
exit(1);
}
else
{
std::cout << RED << "[ x ] : File not found: " << arg << RESET << endl;
exit(1);
params.file = arg;
}
}
}

switch (cmd)
{
case version:
std::cout << PURPLE << "ascii-video-player => " << YELLOW << "0.1.0" << std::endl;
std::cout << PURPLE << "ascii-video-player => " << YELLOW << "0.2.0" << std::endl;
break;
case help:
cout << WHITE << "video-ascii-player" << RESET << endl;
Expand All @@ -92,27 +93,23 @@ int main(int argc, char *argv[])
cout << PURPLE << "\t-v, --version: " << YELLOW << "Display version" << RESET << endl;
cout << PURPLE << "\t-h, --help: " << YELLOW << "Display this message" << RESET << endl;
cout << PURPLE << "\t-s, --show-status: " << YELLOW << "Show player status" << RESET << endl;
cout << PURPLE << "\t-e, --enlargement: " << YELLOW << "Set the enlargement factor [D: 1.8]" << RESET << endl;
cout << PURPLE << "\t-t, --stretch: " << YELLOW << "Ignore the enlargement factor and stretch the video to fit the terminal" << RESET << endl;
cout << PURPLE << "\t-t, --stretch: " << YELLOW << "Stretch the video to fit the terminal" << RESET << endl;
cout << PURPLE << "\t-c, --colors: " << YELLOW << "Enable colors" << RESET << endl;
cout << PURPLE << "\t-b, --blocks: " << YELLOW << "Use colored blocks instead of characters" << RESET << endl;
cout << PURPLE << "\t-i, --inverse: " << YELLOW << "Inverse black & white" << RESET << endl;
cout << PURPLE << "\t-l, --black-level: " << YELLOW << "The intensity of black pixels/chars [D: 2]" << RESET << endl;
cout << PURPLE << "\t-g, --color-background: " << YELLOW << "Color the background instead of the character" << RESET << endl;
cout << PURPLE << "\t[file]: " << YELLOW << "The video file to play" << RESET << endl;
cout << PURPLE << "━━━━━━━━━━━━━━━━━" << RESET << endl;
cout << YELLOW << "What is the enlargement factor?" << RESET << endl;
cout << PURPLE << "When you play a video with a regular video player, "
"every pixel is a square which has the same height and width. \n"
"But when you play it in your terminal, the pixels are not squares anymore but characters. \n"
"And depending on your terminal font, the characters may be thinner or smaller than regular pixels. \n"
"We use the enlargement factor to compensate this gap."
<< RESET << endl;
cout << PURPLE << "━━━━━━━━━━━━━━━━━" << RESET << endl;
break;
case play:
if (file == nullptr)
if (params.file == nullptr)
{
std::cout << RED << "[ x ] : No file specified" << RESET << std::endl;
exit(1);
}

play_video(file, show_status, enlargement, stretch);
play_video(params);
clear();
cout << YELLOW << "Tadaaaa!" << RESET << endl;
break;
Expand Down
124 changes: 88 additions & 36 deletions src/play_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,40 @@
using namespace std;
using namespace cv;

char ASCII_CHARS[] = "@#S%?+;:,. ";
char ASCII_CHARS[200] = "@#%?+;:,.";

int array_length(char const *array)
{
int i = 0;
while (array[i] != '\0')
i++;
return i;
}

void fit_image(Mat& image, int max_width, int max_height, float enlargement, bool stretch)
void fit_image(Mat& image, int max_width, int max_height, bool stretch)
{
if (stretch)
{
resize(image, image, Size(max_width, max_height), 0, 0, INTER_LINEAR);
return;
}

max_width -= 1;
max_height -= 1;

resize(image, image, Size(), 2, 1, INTER_LINEAR);

double resize_factor_1 = (double)max_width / image.cols;
double resize_factor_2 = (double)max_height / image.rows;
double resize_factor = min(resize_factor_1, resize_factor_2);
resize(image, image, Size(), resize_factor*enlargement, resize_factor, INTER_AREA);

resize(image, image, Size(), resize_factor, resize_factor, INTER_AREA);

}


void display_frame(Mat frame)
void display_frame(Mat frame, bool inverse)
{
static int chars_array_len = array_length(ASCII_CHARS) - 1;
for (int x = 0; x < frame.rows; x++)
{
for (int y = 0; y < frame.cols; y++)
{
int pixel_gray_value = frame.at<uchar>(x, y);
int pixel_gray_value = 255 - frame.at<uchar>(x, y);
if (inverse)
pixel_gray_value = 255 - pixel_gray_value;
int char_index = chars_array_len * pixel_gray_value / 255;
cout << ASCII_CHARS[char_index];
}
Expand All @@ -51,15 +53,56 @@ void display_frame(Mat frame)
}


void display_frame_with_colors(Mat frame, bool blocks, bool color_background)
{
static int chars_array_len = array_length(ASCII_CHARS) - 1;
resize(frame, frame, Size(), (blocks ? 0.5 : 1), 1, INTER_LINEAR);

for (int x = 0; x < frame.rows; x++)
{
for (int y = 0; y < frame.cols; y++)
{
Vec3b pixel_color = frame.at<Vec3b>(x, y);

if (blocks)
{
cout << "\x1b[48;2;"
<< (int)pixel_color[0] << ";" << (int)pixel_color[1] << ";" << (int)pixel_color[2]
<< "m" << " " << "\x1b[0m";
}
else {
int pixel_gray_value = (pixel_color[0] + pixel_color[1] + pixel_color[2]) / 3;
int char_index = chars_array_len * pixel_gray_value / 255;

cout << "\x1b[" << (color_background ? "48" : "38") << ";2;"
<< (int) pixel_color[0] << ";" << (int) pixel_color[1] << ";" << (int) pixel_color[2]
<< "m" << ASCII_CHARS[char_index] << "\x1b[0m";
}
}
cout << "\n";
}
cout << flush;
}


void play_video(char *file, bool show_status, float enlargement, bool stretch)
void play_video(Params params)
{
VideoCapture cap(file);
if (array_length(ASCII_CHARS) + params.black_level > 200)
{
cout << RED << "[ x ] : Black level cannot be greater than " << (200 - array_length(ASCII_CHARS)) << RESET << endl;
exit(1);
}
for (int i = 0; i < params.black_level; i++)
{
ASCII_CHARS[array_length(ASCII_CHARS)] = ' ';
}

VideoCapture cap(params.file);

if (!cap.isOpened())
{
clear();
cout << RED << "[ x ] : Invalid video: " << file << RESET << endl;
cout << RED << "[ x ] : Invalid video: " << params.file << RESET << endl;
exit(1);
}

Expand All @@ -69,14 +112,12 @@ void play_video(char *file, bool show_status, float enlargement, bool stretch)
double frame_duration = duration*1000 / frame_count;
int casted_frame_duration = (int)frame_duration;
int width, height;
get_terminal_size(width, height);
if (show_status)
height -= 6;


clear();

cout << PURPLE <<"Starting..." << endl;
cout << PURPLE << "File: " << YELLOW << file << endl;
cout << PURPLE << "File: " << YELLOW << params.file << endl;
cout << PURPLE << "Duration: " << YELLOW << duration << " seconds / " << duration/60 << " minutes" << endl;
cout << PURPLE << "FPS: " << YELLOW << fps << endl;
cout << PURPLE << "Frame count: " << YELLOW << frame_count << endl;
Expand All @@ -88,29 +129,40 @@ void play_video(char *file, bool show_status, float enlargement, bool stretch)
cout << PURPLE << "\nPress enter to start..." << RESET << endl;
cin.get();


sleep(1000);

Mat frame;
int precedent_width = 0;
int precedent_height = 0;
// for each frame
for (int i = 0; i < frame_count; i++)
{
cap.read(frame);
cvtColor(frame, frame, COLOR_BGR2GRAY);
fit_image(frame, width, height, enlargement, stretch);
// double the frame width (1px = 2chars)
//resize(frame, frame, Size(), 2, 1, INTER_LINEAR);

if (!params.colors)
cvtColor(frame, frame, COLOR_BGR2GRAY);

get_terminal_size(width, height);
if (params.show_status)
height -= 5;
fit_image(frame, width, height, params.stretch);


clear();
if (show_status) {
cout << i << "th frame / " << frame_count << "\n";
cout << "progression : " << (i * 100 / frame_count) << "%" << "\n";
cout << "frame duration : " << frame_duration << "\n";
cout << "frame duration <casted> : " << (int) frame_duration << "\n";
cout << "frame width : " << frame.cols << "\n";
cout << "frame height : " << frame.rows << "\n";
if (params.show_status) {
cout << RED << i << "th frame / " << frame_count << "\n";
cout << PURPLE << "progression : " << YELLOW << (i * 100 / frame_count) << "%" << "\n";
cout << PURPLE << "frame duration : " << YELLOW << frame_duration << "\n";
cout << PURPLE << "frame/terminal width : " << YELLOW << frame.cols << " / " << width << "\n";
cout << PURPLE << "frame/terminal height : " << YELLOW << frame.rows << " / " << height << "\n\n";
}
display_frame(frame);
if (params.colors)
display_frame_with_colors(frame, params.blocks, params.color_background);
else
display_frame(frame, params.inverse);

sleep(casted_frame_duration);
get_terminal_size(width, height);
if (show_status)
height -= 6;


}
}
4 changes: 3 additions & 1 deletion src/play_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef ASCII_VIDEO_PLAYER_PLAY_VIDEO_H
#define ASCII_VIDEO_PLAYER_PLAY_VIDEO_H

void play_video(char *file, bool show_status, float enlargement, bool stretch);
#include "utils.h"

void play_video(Params params);

#endif // ASCII_VIDEO_PLAYER_PLAY_VIDEO_H
4 changes: 2 additions & 2 deletions src/terminal_size.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ void get_terminal_size(int& width, int& height) {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
width = (int)(csbi.srWindow.Right-csbi.srWindow.Left);
width = (int)(csbi.srWindow.Right-csbi.srWindow.Left-1);
height = (int)(csbi.srWindow.Bottom-csbi.srWindow.Top);
#elif defined(__linux__)
struct winsize w;
ioctl(fileno(stdout), TIOCGWINSZ, &w);
width = (int)(w.ws_col);
height = (int)(w.ws_row-1);
height = (int)(w.ws_row-2);
#endif // Windows/Linux
}
Loading

0 comments on commit 9d6b22b

Please sign in to comment.