Skip to content

Instantly share code, notes, and snippets.

@FMudanyali
Last active June 7, 2025 01:30
Show Gist options
  • Save FMudanyali/6ca2a4c6cb1b903885044dbfa513ce53 to your computer and use it in GitHub Desktop.
Save FMudanyali/6ca2a4c6cb1b903885044dbfa513ce53 to your computer and use it in GitHub Desktop.
Bad Apple CLI (far from perfect)
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <time.h>
#include <libavformat/avformat.h>
#include <unistd.h>
void draw_bmp(uint32_t frame_count, char *filename, uint8_t framerate){
FILE *bmp_handle;
FILE *buffer_handle;
buffer_handle = fopen("buffer.txt","w");
struct winsize w;
ioctl(0, TIOCGWINSZ, &w);
uint16_t lines = w.ws_row;
uint16_t columns = w.ws_col;
uint8_t pixel;
uint16_t batch = 1;
uint32_t filejump = 0;
uint32_t size, offset, static_offset, width, height;
char buf[0x100];
// Repeat for all frames.
for(int i = 1; i <= frame_count; ++i){
if(batch==1){
snprintf(buf, sizeof(buf), "ffmpeg -i \"%s\" -vf \"select=between(n\\,%d\\,%d)\" -vsync 0 -pix_fmt bgr8 -y temp-termplayer/bitmap%%01d.bmp >/dev/null 2>/dev/null", filename, filejump, filejump+1999);
system(buf);
}
snprintf(buf, sizeof(buf), "temp-termplayer/bitmap%d.bmp", batch);
bmp_handle = fopen(buf,"r");
// size
fseek(bmp_handle,0x02, SEEK_SET);
fread(&size,4,1,bmp_handle);
// offset
fseek(bmp_handle,0x0A,SEEK_SET);
fread(&static_offset,4,1,bmp_handle);
// width, height
fseek(bmp_handle,0x12,SEEK_SET);
fread(&width,4,1,bmp_handle);
fread(&height,4,1,bmp_handle);
uint16_t new_width;
// Respect aspect ratio. Note: not working so always true
if((float)(width/height) < (float)(16/9) || 1){
new_width = lines*3;
} else {
new_width = columns;
}
uint16_t width_offset = (columns - new_width)/2;
// Read according to the terminal resolution.
uint16_t jump_width = width/new_width;
uint16_t jump_height = height/lines;
//printf("width %d, height %d, lines %d, columns %d, new_width %d, jump_width %d, jump_height %d\n",width,height,lines,columns,new_width,jump_width,jump_height);
//return;
// BMP stores image upside down. Set offset to last horizontal line.
offset = static_offset + width * height - width;
// Y axis.
for(int j = 1; j < lines+1; ++j){
// X axis.
fseek(bmp_handle,offset,SEEK_SET);
for(int k = 0; k < width_offset+4; ++k){
fputc(' ',buffer_handle);
}
for(int k = 1; k < new_width-7; ++k){
fread(&pixel,1,1,bmp_handle);
if (pixel > 235) fputc('#',buffer_handle);
else if (pixel > 148) fputc('+',buffer_handle);
else if (pixel > 96) fputc(':',buffer_handle);
else if (pixel > 0) fputc('.',buffer_handle);
else fputc(' ',buffer_handle);
fseek(bmp_handle,jump_width,SEEK_CUR);
}
for(int k = new_width+width_offset; k < columns+3; ++k){
fputc(' ',buffer_handle);
}
fputc('\n',buffer_handle);
// Go up one line.
offset = static_offset + width*height - (j*width*jump_height);
}
fclose(bmp_handle);
++batch;
if(batch == 2000){
batch = 1;
filejump += 2000;
}
}
fclose(buffer_handle);
system("rm temp-termplayer/*.bmp >/dev/null 2>/dev/null");
buffer_handle = fopen("buffer.txt","r");
printf("\n");
char line[columns];
uint8_t line_count = 0;
int pid = fork();
if (pid == 0){
#ifdef __linux
system("aplay temp-termplayer/audio.wav >/dev/null 2>/dev/null");
#else //macos
system("afplay temp-termplayer/audio.wav >/dev/null 2>/dev/null");
#endif
return;
}
while(fgets(line, sizeof(line), buffer_handle)){
printf("%s",line);
++line_count;
if (line_count == lines*2){
line_count = 0;
usleep((33000));
}
}
fclose(buffer_handle);
}
int main(int argc, char *argv[]){
if (argc < 2){
printf("Provide a video please.\n");
return 0;
}
AVFormatContext *pFormatCtx = NULL;
if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
return -1;
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
return -1;
uint32_t duration = (uint32_t)(pFormatCtx->duration/1000000);
uint8_t framerate = pFormatCtx->streams[0]->r_frame_rate.num / pFormatCtx->streams[0]->r_frame_rate.den;
uint32_t frame_count = framerate * duration;
char buf[0x100];
system("mkdir temp-termplayer >/dev/null 2>/dev/null");
snprintf(buf, sizeof(buf), "ffmpeg -i \"%s\" -y temp-termplayer/audio.wav >/dev/null 2>/dev/null", argv[1]);
system(buf);
draw_bmp(frame_count, argv[1], framerate);
return 0;
}
@Rendevior
Copy link

Woah this is Really Spot on, Thanks bud.

@FMudanyali
Copy link
Author

Yoo thank you too!

@TheFederalAgentsGlow
Copy link

[felix@ArtwixLinwux ~]$ gcc bad_apple.c -o bad_apple /usr/bin/ld: /tmp/ccZy4GpA.o: in function main':
bad_apple.c:(.text+0x71a): undefined reference to avformat_open_input' /usr/bin/ld: bad_apple.c:(.text+0x73c): undefined reference to avformat_find_stream_info'
collect2: error: ld returned 1 exit status`

Can someone help me?
Thanks

@FMudanyali
Copy link
Author

[felix@ArtwixLinwux ~]$ gcc bad_apple.c -o bad_apple /usr/bin/ld: /tmp/ccZy4GpA.o: in function main': bad_apple.c:(.text+0x71a): undefined reference to avformat_open_input' /usr/bin/ld: bad_apple.c:(.text+0x73c): undefined reference to avformat_find_stream_info' collect2: error: ld returned 1 exit status`

Can someone help me? Thanks

You probably forgot to install libavformat, in Artix/arch, i think it should be in ffmpeg package. Try compiling after installing that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment