/*************************************************************************** * Copyright (C) 2007 by Nicholas Thomas * * nick@lupine.me.uk * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #define OUTPUT_FPS 25 // We output at 25fps, non-interlaced. TODO: make this CLI configurable (implies switch to getopts) #define OUTPUT_MS_PER_FRAME 1000 / OUTPUT_FPS #define ERR_NONE 0 #define ERR_CMD_ARGS 1 #define ERR_IO 2 typedef struct { u_int32_t x; u_int32_t y; u_int32_t width; u_int32_t height; u_int8_t ***data; } InputRect; typedef struct { u_int32_t delta_t; u_int32_t num_rects; InputRect *rects; } InputFrame; void bail(const char *error_message, int retval) { fprintf(stderr, error_message); fcloseall(); exit(retval); } inline int read8(FILE *data, u_int8_t *buf) { if (fread(buf, sizeof(u_int8_t), 1, data) != 1) return ERR_IO; else return ERR_NONE; } inline int read32(FILE *data, u_int32_t *buf) { if (fread(buf, sizeof(u_int32_t), 1, data) != 1) return ERR_IO; else return ERR_NONE; } // Reads header information from the file. int getHeader(FILE *data, u_int32_t *height, u_int32_t *width) { read32(data, width); read32(data, height); } // This function writes the frame to output as YUV data. int writeOutputFrame(FILE *output, u_int8_t ***data, u_int32_t width, u_int32_t height) { // TODO: Error checking // YUV values apparently need clamping to the 0-255 range // In this function, we need to convert from RGBA, upside-down left to right pixel data to // YUV format u_int8_t *Ys; u_int8_t *Us; u_int8_t *Vs; unsigned long x, y; long temp; Ys = malloc(sizeof(u_int8_t) * width * height); // Us = malloc((sizeof(u_int8_t) * width * height) / 4); // Vs = malloc((sizeof(u_int8_t) * width * height) / 4); for (x = 0; x < width; x++) // Don't forget, our data are bottom-to-top for(y = 0; y < height; y++) // Magic conversion factors { Ys[(y*width)+x] = (0.257 * data[x][y][0]) + // R (0.504 * data[x][y][1]) + // G (0.098 * data[x][y][2]) + 16; // B /* Ys[(y*width)+x] = (0.257 * data[(y*width*4) + (x*4) + 0]) + // R (0.504 * data[(y*width*4) + (x*4) + 1]) + // G (0.098 * data[(y*width*4) + (x*4) + 2]) + 16; // B */ /* // Only execute the below code for 1/4 of the pixels. Even only. if (y% 2 ==0 && x % 2 == 0 && y +1 != height) { Us[((y/2)*(width/2))+(x/2)] = ((-(0.148 * data[(y*width*4) + (x*4) + 0]) - // R (0.291 * data[(y*width*4) + (x*4) + 1]) + // G (0.439 * data[(y*width*4) + (x*4) + 2]) + 128) + // B (-(0.148 * data[(y*width*4) + ((x+1)*4) + 0]) - // R (0.291 * data[(y*width*4) + ((x+1)*4) + 1]) + // G (0.439 * data[(y*width*4) + ((x+1)*4) + 2]) + 128) + // B (-(0.148 * data[((y+1)*width*4) + (x*4) + 0]) - // R (0.291 * data[((y+1)*width*4) + (x*4) + 1]) + // G (0.439 * data[((y+1)*width*4) + (x*4) + 2]) + 128) + // B (-(0.148 * data[((y+1)*width*4) + ((x+1)*4) + 0]) - // R (0.291 * data[((y+1)*width*4) + ((x+1)*4) + 1]) + // G (0.439 * data[((y+1)*width*4) + ((x+1)*4) + 2]))) / 4; // B Vs[((y/2)*(width/2))+(x/2)] = (((0.439 * data[(y*width*4) + (x*4) + 0]) - // R (0.368 * data[(y*width*4) + (x*4) + 1]) - // G (0.071 * data[(y*width*4) + (x*4) + 2]) + 128) + // B ((0.439 * data[(y*width*4) + ((x+1)*4) + 0]) - // R (0.368 * data[(y*width*4) + ((x+1)*4) + 1]) - // G (0.071 * data[(y*width*4) + ((x+1)*4) + 2]) + 128) + // B ((0.439 * data[((y+1)*width*4) + (x*4) + 0]) - // R (0.368 * data[((y+1)*width*4) + (x*4) + 1]) - // G (0.071 * data[((y+1)*width*4) + (x*4) + 2]) + 128) + // B ((0.439 * data[((y+1)*width*4) + ((x+1)*4) + 0]) - // R (0.368 * data[((y+1)*width*4) + ((x+1)*4) + 1]) - // G (0.071 * data[((y+1)*width*4) + ((x+1)*4) + 2]) + 128))/4; // B }*/ } fwrite(Ys, sizeof(u_int8_t), height * width, output); // fwrite(Us, sizeof(u_int8_t), (height * width)/4, output); // fwrite(Vs, sizeof(u_int8_t), (height * width)/4, output); free(Ys); // free(Us); // free(Vs); return ERR_NONE; } int readInputRect(FILE *input, InputRect *rect) { unsigned long n, x, y; // TODO: Error checking // These read correctly read32(input, &rect->x); read32(input, &rect->y); read32(input, &rect->width); read32(input, &rect->height); fprintf(stderr, "New rect. delta x: %i; y: %i; width: %i; height: %i\n", rect->x, rect->y, rect->width, rect->height); rect->data = malloc(rect->width * sizeof(u_int8_t **)); // NOTE: 4 due to RGBA for (x = 0; x < rect->width; x++) { rect->data[x] = malloc(rect->height * sizeof(u_int8_t *)); for (y = 0; y < rect->height; y++) rect->data[x][y] = malloc(4 * sizeof(u_int8_t)); } for (x = 0; y < rect->width; x++) for (y = 0; y < rect->height; y++) { fread(&rect->data[x][y][0], sizeof(u_int8_t), 1, input); fread(&rect->data[x][y][1], sizeof(u_int8_t), 1, input); fread(&rect->data[x][y][2], sizeof(u_int8_t), 1, input); fread(&rect->data[x][y][3], sizeof(u_int8_t), 1, input); } return ERR_NONE; } int readInputFrame(FILE *input, InputFrame *buf) { unsigned long n; // TODO: Error checking read32(input, &buf->delta_t); read32(input, &buf->num_rects); fprintf(stderr, "New frame. delta t: %i; number of rects: %i\n", buf->delta_t, buf->num_rects); buf->rects = (InputRect *) malloc(sizeof(InputRect) * buf->num_rects); for (n = 0; n < buf->num_rects; n++) readInputRect(input, &buf->rects[n]); return ERR_NONE; } int applyInputFrame(u_int8_t ***raw_frame, InputFrame *data, u_int32_t frame_height, u_int32_t frame_width) { // TODO: Error-checking unsigned long i, j, x, y; for (i = 0; i < data->num_rects; i++) { InputRect *rect = &data->rects[i]; for (x = 0; y < rect->width; x++) for (y = 0; y < rect->height; y++) { raw_frame[x+rect->x][y+rect->y][0] = rect->data[x][y][0]; raw_frame[x+rect->x][y+rect->y][1] = rect->data[x][y][1]; raw_frame[x+rect->x][y+rect->y][2] = rect->data[x][y][2]; raw_frame[x+rect->x][y+rect->y][3] = rect->data[x][y][3]; } /* for (y = 0; y < rect->height; y++) memcpy(&raw_frame[((rect->y + y) * frame_width * 4) + (rect->x * 4)], &rect->data[y * rect->width * 4], sizeof(u_int8_t) * rect->width * 4); */ // memcpy(raw_frame, rect->data, sizeof(u_int8_t) * rect->height * rect->width * 4); /* for (j = 0; j < rect->height; j++) { int rect_line = rect->y + j; int frame_line = (frame_height -1) - rect_line; u_int8_t *raw_frame_line = raw_frame + 4 * frame_width * frame_line; // go to the correct line of the frame. u_int8_t *rect_data_line = rect->data + 4 * rect->width * j; // go to the correct line of the rect data. memcpy(raw_frame_line + 4 * rect->x, rect_data_line, sizeof(u_int8_t) * rect->width * 4); }*/ } } int freeInputFrame(InputFrame *frame) { //TODO: Error-checking unsigned long n, x, y; for (n = 0; n < frame->num_rects; n++) for (x = 0; x < frame->rects[n].width; x++) { for (y = 0; y < frame->rects[n].height; y++) free(frame->rects[n].data[x][y]); free(frame->rects[n].data[x]); } // free(frame->rects[n].data); free(frame->rects); } int main(int argc, char *argv[]) { FILE *input; FILE *output; int err, x, y; unsigned long n; char *s; unsigned long current_input_video_time = 0; unsigned long outputted_frames = 0; InputFrame input_buf; u_int8_t ***raw_frame; // The full frame at current_input_video_time u_int32_t video_height = 0; u_int32_t video_width = 0; switch (argc) { case 1: // Where no command-line arguments are given, assume input from stdin and output from stdout { input = stdin; output = stdout; } break; case 3: // If input is specified, output must also be specified as rsc2raw input_file output_file { input = fopen(argv[1], "r"); output = fopen(argv[2], "w"); } break; default: // Bail out with an error. { fprintf(stderr, "Failure parsing command-line arguments.\n"); fprintf(stderr, "Format: rsc2raw [ ]\n"); fprintf(stderr, "Running \"rsc2raw\" with no arguments results in processing from stdin to stdout\n"); fcloseall(); exit(ERR_CMD_ARGS); }; break; } // Check assignment worked properly if (input == NULL || output == NULL) exit(ERR_IO); err = getHeader(input, &video_width, &video_height); if (err != ERR_NONE) bail("Failure reading header - probably an invalid input file.\n", err); raw_frame = malloc(video_width * sizeof(u_int8_t **)); for (x = 0; x < video_width; x++) { raw_frame[x] = malloc(video_height * sizeof(u_int8_t *)); for (y = 0; y < video_height; y++) raw_frame[x][y] = malloc(4 * sizeof(u_int8_t)); // NOTE: 4 due to RGBA } // Now keep going until we either run out of input or hit an error. while(feof(input) == 0) { int i_err = ferror(input); int o_err = ferror(output); if (i_err != 0 || o_err != 0) fprintf(stderr, "Error occured reading/writing last frame! i: %i; o: %i\n", i_err, o_err); // We want to output a new frame every (1000/OUTPUT_FPS) 40 ms. Our input frames have variable delta time while (current_input_video_time < outputted_frames * OUTPUT_MS_PER_FRAME) { // TODO: Error-checking readInputFrame(input, &input_buf); applyInputFrame(raw_frame, &input_buf, video_height, video_width); current_input_video_time += input_buf.delta_t; freeInputFrame(&input_buf); } writeOutputFrame(output, raw_frame, video_width, video_height); outputted_frames++; } // TODO: Full memory deallocation for (x = 0; x < video_width; x++) { for (y = 0; y < video_height; y++) free(raw_frame[x][y]); free(raw_frame[x]); } free(raw_frame); fcloseall(); return ERR_NONE; }