diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bc2c252 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "CCLArgs"] + path = CCLArgs + url = https://github.com/RuiDGPires/CCLArgs diff --git a/CCLArgs b/CCLArgs new file mode 160000 index 0000000..d08b78c --- /dev/null +++ b/CCLArgs @@ -0,0 +1 @@ +Subproject commit d08b78c0d91e9d4d1b2326251c82e734cc75d85e diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51e95d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +C_FILES := $(wildcard src/*.c) +H_FILES := $(wildcard src/*.h) $(wildcard src/include/*.h) +TARGET := vis +CC := gcc + +$(TARGET): $(C_FILES) $(H_FILES) + gcc $(C_FILES) -o $@ + +.PHONY: clean +clean: + rm $(TARGET) diff --git a/a.mp4 b/a.mp4 new file mode 100644 index 0000000..b78bd34 Binary files /dev/null and b/a.mp4 differ diff --git a/buildnrun.sh b/buildnrun.sh new file mode 100755 index 0000000..7641aa7 --- /dev/null +++ b/buildnrun.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +VIDEO_FILE=output.mp4 + +make +./vis ${VIDEO_FILE} +vlc ${VIDEO_FILE} >/dev/null 2>&1 diff --git a/src/canvas.c b/src/canvas.c new file mode 100644 index 0000000..cd20139 --- /dev/null +++ b/src/canvas.c @@ -0,0 +1,60 @@ +#include +#include +#include "canvas.h" +#include "err.h" + +u32 point_dist_sqrd(point_t p1, point_t p2) { + i32 dx = (i32) (p1.x - p2.x), dy = (i32) (p1.y - p2.y); + return dx*dx + dy*dy; +} + +canvas_t *canvas_create(u32 width, u32 height) { + u32 *buffer = (u32*) malloc(sizeof(u32) * width * height); + canvas_t *canvas = canvas_from_buffer(buffer, width, height); + canvas->is_static = FALSE; + return canvas; +} + +canvas_t *canvas_from_buffer(u32 *buffer, u32 width, u32 height) { + canvas_t *canvas = (canvas_t *) malloc(sizeof(canvas_t)); + canvas->width = width; + canvas->height = height; + canvas->buffer = buffer; + canvas->is_static = TRUE; + return canvas; +} + +void canvas_destroy(canvas_t **canvas) { + if ((*canvas)->is_static == FALSE) + free((*canvas)->buffer); + + free(*canvas); + *canvas = NULL; +} + +void canvas_fill(canvas_t *canvas, color_t color) { + for (usize i = 0; i < canvas->width * canvas->height; i++) { + canvas->buffer[i] = color; + } +} + +void canvas_dump(canvas_t *canvas, int fd) { + write(fd, canvas->buffer, sizeof(color_t)*canvas->width*canvas->height); +} + +void canvas_draw_point(canvas_t *canvas, point_t point, color_t color) { + canvas->buffer[point.x + point.y*canvas->width] = color; +} + +void canvas_draw_circle(canvas_t *canvas, point_t center, u32 radius, color_t color) { + ERR_ASSERT(center.x - radius >= 0 && center.x + radius < canvas->width && center.y - radius >= 0 && center.y + radius < canvas->height, "Invalid circle"); + for (u32 x = center.x - radius; x < center.x + radius; x++){ + for (u32 y = center.y - radius; y < center.y + radius; y++){ + point_t p = (point_t) {.x = x, .y = y}; + + if (point_dist_sqrd(p, center) <= radius*radius) { + canvas_draw_point(canvas, p, color); + } + } + } +} diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 index 0000000..1d0f6c0 --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,37 @@ +#ifndef __CANVAS_H__ +#define __CANVAS_H__ + +#include "defs.h" + +typedef u32 color_t; + +#define RGBA(r, g, b, a) ((((r)&0xFF)<<(8*0)) | (((g)&0xFF)<<(8*1)) | (((b)&0xFF)<<(8*2)) | (((a)&0xFF)<<(8*3))) + +#define COLOR_RED RGBA(0xFF, 0x00, 0x00, 0xFF) +#define COLOR_GREEN RGBA(0x00, 0xFF, 0x00, 0xFF) +#define COLOR_BLUE RGBA(0x00, 0x00, 0xFF, 0xFF) +#define COLOR_BLACK RGBA(0x00, 0x00, 0x00, 0xFF) +#define COLOR_WHITE RGBA(0xFF, 0xFF, 0xFF, 0xFF) + +typedef struct { + u32 width, height; + color_t *buffer; + u8 is_static; +} canvas_t; + +typedef struct { + usize x, y; +} point_t; + +u32 point_dist_sqrd(point_t, point_t); + +canvas_t *canvas_create(u32, u32); +canvas_t *canvas_from_buffer(u32 *, u32, u32); +void canvas_destroy(canvas_t **); + +void canvas_fill(canvas_t *, color_t); +void canvas_dump(canvas_t *, int); +void canvas_draw_point(canvas_t *, point_t, color_t); +void canvas_draw_circle(canvas_t *, point_t, u32, color_t); + +#endif diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..08e4ca4 --- /dev/null +++ b/src/defs.h @@ -0,0 +1,23 @@ +#ifndef __DEFS_H__ +#define __DEFS_H__ + +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define STR2(v) #v +#define STR(v) STR2(v) + +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int32_t i32; +typedef int16_t i16; +typedef int8_t i8; + +typedef size_t usize; + +#endif diff --git a/src/err.h b/src/err.h new file mode 100644 index 0000000..f5972ef --- /dev/null +++ b/src/err.h @@ -0,0 +1,14 @@ +#ifndef __ERR_H__ +#define __ERR_H__ + +#include +#include +#include +#include + +#define ERR(...) {fprintf(stderr, "ERR: " __VA_ARGS__); fprintf(stderr, "\n"); exit(1);} + +#define ERR_ASSERT(cond, ...) {if (!(cond)) ERR(__VA_ARGS__)} +#define ERR_ASSERT_CLN(cond, cln, ...) {if (!(cond)) {{cln;} ERR(__VA_ARGS__)}} + +#endif diff --git a/src/ffmpeg.c b/src/ffmpeg.c new file mode 100644 index 0000000..eae681a --- /dev/null +++ b/src/ffmpeg.c @@ -0,0 +1,51 @@ +#include +#include +#include "ffmpeg.h" +#include "err.h" + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +#define MAX_CHARS_DIM 100 +#define MAX_CHARS_FPS 4 + +int open_ffmpeg(const char *filename, u32 width, u32 height, u32 fps) { + int pipefd[2]; + ERR_ASSERT(pipe(pipefd) >= 0, "Unable to open pipe"); + + int child = fork(); + + char dims_s[MAX_CHARS_DIM]; + char fps_s[MAX_CHARS_FPS]; + snprintf(dims_s, MAX_CHARS_DIM, "%ux%u", width, height); + snprintf(fps_s, MAX_CHARS_FPS, "%u", fps); + + ERR_ASSERT(child >= 0, "Unable to fork"); + + if (child == 0) { + ERR_ASSERT(dup2(pipefd[PIPE_READ], STDIN_FILENO) >= 0, "Unable to reopen read pipe as stdin"); + close(pipefd[PIPE_WRITE]); + + ERR_ASSERT(execlp("ffmpeg", + "ffmpeg", + "-loglevel", "verbose", + "-y", + "-f", "rawvideo", + "-pix_fmt", "rgba", + "-s", dims_s, + "-r", fps_s, + "-an", + "-i", "-", + "-c:v", "libx264", + + filename, + // ... + NULL + ) >= 0, "Unable to open FFMPEG: %s", strerror(errno)); + assert(0 && "Unreachable"); + } + + close(pipefd[PIPE_READ]); + return pipefd[PIPE_WRITE]; +} + diff --git a/src/ffmpeg.h b/src/ffmpeg.h new file mode 100644 index 0000000..5c848ca --- /dev/null +++ b/src/ffmpeg.h @@ -0,0 +1,8 @@ +#ifndef __FFMPEG_H__ +#define __FFMPEG_H__ + +#include "defs.h" + +int open_ffmpeg(const char *, u32 width, u32 height, u32 fps); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..888164d --- /dev/null +++ b/src/main.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include "../CCLArgs/cclargs.h" +#include "ffmpeg.h" +#include "defs.h" +#include "err.h" +#include "wav.h" +#include "canvas.h" + +#define WIDTH 600 +#define HEIGHT 400 +#define FPS 30 +#define DURATION 5 + +#define MAX_FILENAME 100 + +#define USAGE "vis " +#define DEFAULT_NAME "a.mp4" + +int main(ARGS) { + char *output_file = DEFAULT_NAME; + + BEGIN_PARSE_ARGS("") + ARG_STRING(output_file, "-o") + END_PARSE_ARGS + + color_t pixels[WIDTH*HEIGHT]; + canvas_t *canvas = canvas_from_buffer(pixels, WIDTH, HEIGHT); + + int outfd = open_ffmpeg(output_file, WIDTH, HEIGHT, FPS); + + canvas_fill(canvas, COLOR_BLUE); + canvas_draw_circle(canvas, (point_t){.x = WIDTH/2, .y = HEIGHT/2}, HEIGHT/3, COLOR_RED); + + for (usize i = 0; i < FPS * DURATION; i++) { + canvas_dump(canvas, outfd); + } + + close(outfd); + canvas_destroy(&canvas); + wait(NULL); + printf("Operation completed\n"); + return 0; +} diff --git a/src/wav.c b/src/wav.c new file mode 100644 index 0000000..fa00b44 --- /dev/null +++ b/src/wav.c @@ -0,0 +1,47 @@ +#include + +#include "wav.h" +#include "err.h" + +#define BUFFER_SIZE 4096 + +// Function to read audio file and retrieve its contents +wav_t *wav_from_file(const char* filename) { + FILE* file = fopen(filename, "rb"); + ERR_ASSERT(file != NULL, "Failed to open file"); + + wav_t *wav = (wav_t *) malloc(sizeof(wav_t)); + wav_header_t header; + + // Read the WAV file header + size_t headerSize = fread(&header, 1, sizeof(wav_header_t), file); + + ERR_ASSERT_CLN(headerSize == sizeof(wav_header_t), fclose(file), "Invalid WAV File"); + + // Check if the file format is valid + ERR_ASSERT_CLN(header.chunkID[0] == 'R' && header.chunkID[1] == 'I' && header.chunkID[2] == 'F' && header.chunkID[3] == 'F' && header.format[0] == 'W' && header.format[1] == 'A' && header.format[2] == 'V' && header.format[3] == 'E', + fclose(file), "Invalid WAV file format"); + + // Allocate memory to store audio data + u32 dataSize = header.subchunk2Size; + + wav->header = header; + u8* buffer = (uint8_t*)malloc(sizeof(u8) * dataSize); + + ERR_ASSERT_CLN(buffer != NULL, fclose(file), "Failed to allocate memory"); + wav->buffer = buffer; + + // Read the audio data into the buffer + usize bytesRead = fread(buffer, 1, dataSize, file); + fclose(file); + + ERR_ASSERT_CLN(bytesRead == dataSize, free(buffer), "Error occurred while reading the audio data") + + return wav; +} + +void destroy_wav(wav_t **wav) { + free((*wav)->buffer); + free(*wav); + *wav = NULL; +} diff --git a/src/wav.h b/src/wav.h new file mode 100644 index 0000000..40c56e0 --- /dev/null +++ b/src/wav.h @@ -0,0 +1,31 @@ +#ifndef __WAV_H__ +#define __WAV_H__ + +#include "defs.h" + +// WAV file header structure +typedef struct { + char chunkID[4]; + u32 chunkSize; + char format[4]; + char subchunk1ID[4]; + u32 subchunk1Size; + u16 audioFormat; + u16 numChannels; + u32 sampleRate; + u32 byteRate; + u16 blockAlign; + u16 bitsPerSample; + char subchunk2ID[4]; + u32 subchunk2Size; +} wav_header_t; + +typedef struct { + wav_header_t header; + u8 *buffer; +} wav_t; + +wav_t *wav_from_file(const char *); +void wav_destroy(wav_t **); + +#endif