oxframe/oxframe.c

303 lines
8.4 KiB
C
Raw Normal View History

/* -*- tab-width:2;c-file-style:"cc-mode"; -*- */
/*
* oxframe.c -- dump frame from a theora file
* Copyright (C) 20010 <j@mailb.org>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <oggplay/oggplay.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <X11/Xlib.h>
#include <Imlib2.h>
typedef enum {
oxImageNotSet,
oxPNG,
oxJPG,
} oxImageFormat;
typedef struct _oxstate {
const char *input;
const char *output;
oxImageFormat format;
long frame_pos;
long duration;
int image_width;
int image_height;
} oxstate;
static void usage(void){
fprintf(stderr,
"Usage: oxframe [options]\n\n"
"Options: \n\n"
" -h, --help show this help message and exit\n"
" -x WIDTH, --width=WIDTH\n"
" scale image to given width\n"
" -y HEIGHT, --height=HEIGHT\n"
" scale image to given height\n"
" -p POS, --pos=POS frame position in seconds, float\n"
" -i INPUT, --input=INPUT\n"
" video input\n"
" -o OUTPUT, --output=OUTPUT\n"
" path to save frame to, jpg, png supported\n"
" (defaults to png)\n"
" -f FORMAT, --output=FORMAT\n"
" output format, jpg or png\n"
" if not provided detected from output extension\n"
);
exit(0);
}
void write_image_file(OggPlayRGBChannels *data, oxstate * state) {
Imlib_Image *frame = NULL,
*image = NULL;
frame = imlib_create_image_using_data(data->rgb_width, data->rgb_height,
(unsigned int *)data->ptro);
if (state->image_width > 0 && state->image_height < 0) {
state->image_height = data->rgb_height * state->image_width / data->rgb_width;
}
if (state->image_height > 0 && state->image_width < 0) {
state->image_width = data->rgb_width * state->image_height / data->rgb_height;
}
if (state->image_width > 0) {
image = imlib_create_image(state->image_width, state->image_height);
imlib_context_set_image(image);
imlib_blend_image_onto_image(frame, 0,
0, 0, data->rgb_width, data->rgb_height,
0, 0,
state->image_width, state->image_height);
} else {
imlib_context_set_image(frame);
}
if(state->format == oxPNG)
imlib_image_set_format("png");
else
imlib_image_set_format("jpg");
imlib_save_image(state->output);
imlib_free_image_and_decache();
imlib_context_set_image(frame);
imlib_free_image_and_decache();
if (state->image_width > 0) {
2010-04-23 18:47:26 +00:00
imlib_context_set_image(image);
imlib_free_image_and_decache();
}
}
void write_frame (OggPlay * player, int track_num,
OggPlayVideoData * video_data, oxstate * state) {
OggPlayYUVChannels from;
OggPlayRGBChannels to;
from.ptry = video_data->y;
from.ptru = video_data->u;
from.ptrv = video_data->v;
oggplay_get_video_y_size(player, track_num, &(from.y_width),
&(from.y_height));
oggplay_get_video_uv_size(player, track_num, &(from.uv_width),
&(from.uv_height));
/*
printf("size: %dx%d %dx%d\n", from.y_width, from.y_height, from.uv_width,
from.uv_height);
*/
to.ptro = malloc(from.y_width * from.y_height * 4);
to.rgb_width = from.y_width;
to.rgb_height = from.y_height;
oggplay_yuv2bgra (&from, &to);
write_image_file(&to, state);
free(to.ptro);
}
int dump_frame_callback (OggPlay *player, int num_tracks,
OggPlayCallbackInfo **track_info, void *user) {
int i;
OggPlayDataHeader ** headers;
OggPlayVideoData * video_data;
OggPlayDataType type;
oxstate * state = (oxstate *)user;
for (i = 0; i < num_tracks; i++) {
type = oggplay_callback_info_get_type(track_info[i]);
headers = oggplay_callback_info_get_headers(track_info[i]);
switch (type) {
case OGGPLAY_INACTIVE:
break;
case OGGPLAY_YUV_VIDEO:
if (oggplay_callback_info_get_required(track_info[i]) < 1) {
2010-04-23 18:47:26 +00:00
//fprintf(stderr, "oops\n");
break;
}
long pt = oggplay_callback_info_get_presentation_time(headers[0]);
//printf("time: %ld %ld %ld\n", pt, state->duration, state->duration-pt);
if (pt >= state->frame_pos) {
2010-04-26 16:32:40 +00:00
video_data = oggplay_callback_info_get_video_data(headers[0]);
write_frame(player, i, video_data, state);
2010-04-26 16:32:40 +00:00
exit(0);
} else if (state->duration - pt < 500) {
2010-04-26 16:32:40 +00:00
video_data = oggplay_callback_info_get_video_data(headers[0]);
write_frame(player, i, video_data, state);
}
2010-04-26 16:32:40 +00:00
break;
default:
break;
}
}
return 0;
}
void init_state(oxstate *state) {
state->input = NULL;
state->output = NULL;
state->frame_pos = 0;
state->duration = 0;
state->image_width = -1;
state->image_height = -1;
state->format = oxImageNotSet;
}
int main (int argc, char * argv[]) {
int c,long_option_index;
OggPlay * player;
OggPlayReader * reader = NULL;
int i;
int fps_num = 25;
int fps_denom = 1;
int granuleshift = 6;
long max_num, offset;
oxstate state;
const char *optstring = "h:x:y:p:i:o:f:";
struct option options [] = {
{"help",required_argument,NULL,'h'},
{"width",required_argument,NULL,'x'},
{"height",required_argument,NULL,'y'},
{"pos",required_argument,NULL,'p'},
{"input",required_argument,NULL,'i'},
{"output",required_argument,NULL,'o'},
{"format",required_argument,NULL,'f'},
{NULL,0,NULL,0}
};
init_state(&state);
while(1) {
c=getopt_long(argc, argv, optstring, options, &long_option_index);
if(c == EOF)
break;
switch(c) {
case 'h':
usage();
break;
case 'x':
state.image_width = atoi(optarg);
break;
case 'y':
state.image_height = atoi(optarg);
break;
case 'p':
state.frame_pos = 1000 * atof(optarg);
break;
case 'i':
state.input = optarg;
reader = oggplay_file_reader_new(state.input);
break;
case 'o':
state.output = optarg;
case 'f':
if (strstr(optarg, "jpg") == NULL)
state.format = oxPNG;
else
state.format = oxJPG;
break;
}
}
if(argc < 3) {
usage();
}
if(state.format == oxImageNotSet) {
if (strstr(&(state.output[strlen(state.output)-4]), ".jpg") == NULL)
state.format = oxPNG;
else
state.format = oxJPG;
}
player = oggplay_open_with_reader(reader);
if (state.input == NULL) {
fprintf (stderr, "please provide input file\n");
exit (1);
}
if (player == NULL) {
fprintf (stderr, "could not initialise oggplay with this file\n");
exit (1);
}
for (i = 0; i < oggplay_get_num_tracks (player); i++) {
if (oggplay_get_track_type (player, i) == OGGZ_CONTENT_THEORA) {
oggplay_set_callback_num_frames (player, i, 1);
oggplay_get_video_fps(player, i, &fps_denom, &fps_num);
}
oggplay_set_track_active(player, i);
}
oggplay_set_data_callback(player, dump_frame_callback, &state);
max_num = 1 << granuleshift;
2010-04-23 18:47:26 +00:00
offset = (1000 * max_num * fps_denom) / fps_num;
state.duration = oggplay_get_duration(player);
2010-04-26 16:32:40 +00:00
/*
2010-04-26 16:32:40 +00:00
if (frame_pos > duration) {
fprintf (stderr, "can not seek to frame later than duration\n");
exit (1);
}
*/
if(state.frame_pos - offset > 0) {
if (oggplay_seek(player, state.frame_pos - offset) == E_OGGPLAY_CANT_SEEK) {
fprintf (stderr, "failed to seeek to %ld\n", state.frame_pos);
exit (1);
}
}
oggplay_start_decoding(player);
2010-04-26 16:32:40 +00:00
oggplay_close (player);
return 0;
}