diff options
| -rw-r--r-- | rotord/Makefile | 2 | ||||
| -rw-r--r-- | rotord/src/ffmpeg-fas_wrapper.cpp | 1 | ||||
| -rw-r--r-- | rotord/src/ffmpeg-fas_wrapper.h | 5 | ||||
| -rw-r--r-- | rotord/src/ffmpeg_fas.c | 948 | ||||
| -rw-r--r-- | rotord/src/ffmpeg_fas.h | 125 | ||||
| -rw-r--r-- | rotord/src/private_errors.h | 32 | ||||
| -rwxr-xr-x | rotord/src/rotor.h | 1 | ||||
| -rw-r--r-- | rotord/src/seek_indices.c | 319 | ||||
| -rw-r--r-- | rotord/src/seek_indices.h | 94 | ||||
| -rw-r--r-- | rotord/src/utils.cpp | 2 | ||||
| -rw-r--r-- | rotord/src/utils.h | 1 |
11 files changed, 1528 insertions, 2 deletions
diff --git a/rotord/Makefile b/rotord/Makefile index 25159b0..29378ad 100644 --- a/rotord/Makefile +++ b/rotord/Makefile @@ -3,7 +3,7 @@ #http://docs.gstreamer.com/display/GstSDK/Installing+on+Linux #MY_CFLAGS = -fpermissive -std=c++11 -Wno-error -I /opt/gstreamer-sdk/include/gstreamer-0.10/ -I /opt/gstreamer-sdk/include/glib-2.0 -I /opt/gstreamer-sdk/lib/glib-2.0/include -I /opt/gstreamer-sdk/include/libxml2 $(shell pkg-config gstreamer-0.10 gstreamer-video-0.10 gstreamer-base-0.10 --cflags) -MY_CFLAGS = -fpermissive -std=c++11 -I /usr/include/opencv -I /usr/include/cairo -I /usr/include/jsoncpp +MY_CFLAGS = -Wswitch -fpermissive -std=c++11 -I /usr/include/opencv -I /usr/include/cairo -I /usr/include/jsoncpp #-Wno-error $(shell pkg-config gstreamer-0.10 gstreamer-video-0.10 gstreamer-base-0.10 --cflags) # -I ../ffmpeg diff --git a/rotord/src/ffmpeg-fas_wrapper.cpp b/rotord/src/ffmpeg-fas_wrapper.cpp new file mode 100644 index 0000000..f1b81c2 --- /dev/null +++ b/rotord/src/ffmpeg-fas_wrapper.cpp @@ -0,0 +1 @@ +#include "ffmpeg-fas_wrapper.h" diff --git a/rotord/src/ffmpeg-fas_wrapper.h b/rotord/src/ffmpeg-fas_wrapper.h new file mode 100644 index 0000000..b70cd2d --- /dev/null +++ b/rotord/src/ffmpeg-fas_wrapper.h @@ -0,0 +1,5 @@ +#ifndef ffmpeg-fas_wrapper_H +#define ffmpeg-fas_wrapper_H + + +#endif diff --git a/rotord/src/ffmpeg_fas.c b/rotord/src/ffmpeg_fas.c new file mode 100644 index 0000000..19e9655 --- /dev/null +++ b/rotord/src/ffmpeg_fas.c @@ -0,0 +1,948 @@ +/***************************************************************************** + * Copyright 2008. Pittsburgh Pattern Recognition, Inc. + * + * This file is part of the Frame Accurate Seeking extension library to + * ffmpeg (ffmpeg-fas). + * + * ffmpeg-fas is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * The ffmpeg-fas library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ffmpeg-fas library. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#include "ffmpeg_fas.h" + +#if defined( _WIN32 ) && defined( STATIC_DLL ) +extern "C" +{ + #include "libavformat/avformat.h" + #include "libavcodec/avcodec.h" + //int img_convert(AVPicture *dst, int dst_pix_fmt, const AVPicture *src, int src_pix_fmt, int src_width, int src_height); +} +#else +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" +#endif /* _WIN32 && STATIC_DLL */ + +#include "seek_indices.h" +#include "private_errors.h" + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#define FIRST_FRAME_INDEX 0 +#define NUM_POSSIBLE_ERRORS 9 + +enum PixelFormat fmt; + +/**** Private Types ***********************************************************/ + +typedef struct fas_context_struct { + fas_boolean_type is_video_active; + fas_boolean_type is_frame_available; + + int current_frame_index; + + seek_table_type seek_table; + + /* ffmpeg */ + AVFormatContext *format_context; + AVCodecContext *codec_context; + int stream_idx; + + AVFrame *frame_buffer; // internal buffer + + AVFrame *rgb_frame_buffer; // extra AVFrames (for color conversion) + uint8_t *rgb_buffer; // actual data buffer for rgb_frame_buffer (needs to be freed seperately) + fas_boolean_type rgb_already_converted; // color_convert(frame_buffer) == rgb_frame_buffer (so we don't need to repeat conversions) + + AVFrame *gray8_frame_buffer; + uint8_t *gray8_buffer; + fas_boolean_type gray8_already_converted; + + int64_t current_dts; // decoding timestamp of the most recently parsed packet + int64_t previous_dts; // for previous packet (always use previous packet for seek_table (workaround)) + int64_t keyframe_packet_dts; // dts of most recent keyframe packet + int64_t first_dts; // for very first packet (needed in seek, for first keyframe) + +} fas_context_type; + +static char* invalid_error_code = "not a valid error code"; +static char *gbl_error_strings[NUM_POSSIBLE_ERRORS] = +{ + "fas_success", + "fas_failure", + "fas_invalid_argument", + "fas_out_of_memory", + "fas_unsupported_format", + "fas_unsupported_codec", + "fas_no_more_frames", + "fas_decoding_error", + "fas_seek_error" +}; + +char* fas_error_message(fas_error_type error) +{ + if ((error < 0) || (error >= NUM_POSSIBLE_ERRORS)) + return invalid_error_code; + + return gbl_error_strings[error]; +} + +void private_show_warning (const char *message); +fas_error_type private_show_error (const char *message, fas_error_type error); +fas_error_type private_convert_to_rgb (fas_context_ref_type ctx); +fas_error_type private_seek_to_nearest_key (fas_context_ref_type context, int target_index, int offset); +fas_error_type private_complete_seek_table (fas_context_ref_type context); + + +void fas_set_logging (fas_boolean_type logging) +{ + if (logging == FAS_TRUE) + { + SHOW_ERROR_MESSAGES = 1; + SHOW_WARNING_MESSAGES = 1; +#ifndef _WIN32 + av_log_set_level(AV_LOG_INFO); //TJR 080913 +#endif + } + else + { + SHOW_ERROR_MESSAGES = 0; + SHOW_WARNING_MESSAGES = 0; +#ifndef _WIN32 + av_log_set_level(AV_LOG_QUIET); //TJR 080913 +#endif + } +} + +/* Set the output image colorspace */ +void fas_set_format(fas_color_space_type format) +{ + switch (format) + { + case FAS_GRAY8: + fmt = PIX_FMT_GRAY8; + break; + case FAS_ARGB32: + fmt = PIX_FMT_RGB32_1; + break; + case FAS_ABGR32: + fmt = PIX_FMT_BGR32_1; + break; + case FAS_YUV420P: + fmt = PIX_FMT_YUV420P; + break; + case FAS_YUYV422: + fmt = PIX_FMT_YUYV422; + break; + case FAS_UYVY422: + fmt = PIX_FMT_UYVY422; + break; + case FAS_YUV422P: + fmt = PIX_FMT_YUV422P; + break; + case FAS_YUV444P: + fmt = PIX_FMT_YUV444P; + break; + case FAS_RGB24: + fmt = PIX_FMT_RGB24; + break; + case FAS_BGR24: + fmt = PIX_FMT_BGR24; + break; + default: + fmt = PIX_FMT_RGB24; + break; + } +} + + +void fas_initialize (fas_boolean_type logging, fas_color_space_type format) +{ + fas_set_logging(logging); + fas_set_format(format); + av_register_all(); + + return; +} + +/* fas_open_video */ + +fas_error_type fas_open_video (fas_context_ref_type *context_ptr, char *file_path) +{ + if (NULL == context_ptr) + return private_show_error ("NULL context pointer provided", FAS_INVALID_ARGUMENT); + + // seek_error_type seek_error; + fas_context_ref_type fas_context; + + *context_ptr = NULL; // set returned context to NULL in case of error + + fas_context = (fas_context_ref_type)malloc (sizeof (fas_context_type)); + memset(fas_context, 0, sizeof(fas_context_type)); + + if (NULL == fas_context) + return private_show_error ("unable to allocate buffer", FAS_OUT_OF_MEMORY); + + fas_context->is_video_active = FAS_TRUE; + fas_context->is_frame_available = FAS_TRUE; + fas_context->current_frame_index = FIRST_FRAME_INDEX - 1; + fas_context->current_dts = AV_NOPTS_VALUE; + fas_context->previous_dts = AV_NOPTS_VALUE; + fas_context->keyframe_packet_dts = AV_NOPTS_VALUE; + fas_context->first_dts = AV_NOPTS_VALUE; + + fas_context->seek_table = seek_init_table (-1); /* default starting size */ + + if (avformat_open_input( &(fas_context->format_context), file_path, NULL, NULL ) != 0) //TJR updated from av_open_file 080913 + { + fas_close_video(fas_context); + return private_show_error ("failure to open file", FAS_UNSUPPORTED_FORMAT); + } + + if (avformat_find_stream_info (fas_context->format_context,NULL) < 0) //TJR updated from av_find_stream_info 080913 + { + fas_close_video(fas_context); + return private_show_error ("could not extract stream information", FAS_UNSUPPORTED_FORMAT); + } + + if (SHOW_WARNING_MESSAGES) + av_dump_format(fas_context->format_context, 0, file_path, 0); + + int stream_idx; + for (stream_idx = 0; stream_idx < fas_context->format_context->nb_streams; stream_idx++) + { + if (fas_context->format_context->streams[stream_idx]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + fas_context->stream_idx = stream_idx; + fas_context->codec_context = fas_context->format_context->streams[stream_idx]->codec; + break; + } + } + + if (fas_context->codec_context == 0) + { + fas_close_video(fas_context); + return private_show_error ("failure to find a video stream", FAS_UNSUPPORTED_FORMAT); + } + + AVCodec *codec = avcodec_find_decoder (fas_context->codec_context->codec_id); + + if (!codec) + { + fas_context->codec_context = 0; + fas_close_video(fas_context); + return private_show_error("failed to find correct video codec", FAS_UNSUPPORTED_CODEC); + } + + if (avcodec_open2(fas_context->codec_context, codec,NULL) < 0) //TJR 080913 added NULL options dictionary + { + fas_context->codec_context = 0; + fas_close_video(fas_context); + return private_show_error ("failed to open codec", FAS_UNSUPPORTED_CODEC); + } + + fas_context->frame_buffer = avcodec_alloc_frame (); + if (fas_context->frame_buffer == NULL) + { + fas_close_video(fas_context); + return private_show_error ("failed to allocate frame buffer", FAS_OUT_OF_MEMORY); + } + + fas_context->rgb_frame_buffer = avcodec_alloc_frame (); + if (fas_context->rgb_frame_buffer == NULL) + { + fas_close_video(fas_context); + return private_show_error ("failed to allocate rgb frame buffer", FAS_OUT_OF_MEMORY); + } + + fas_context->gray8_frame_buffer = avcodec_alloc_frame (); + if (fas_context->gray8_frame_buffer == NULL) + { + fas_close_video(fas_context); + return private_show_error ("failed to allocate gray8 frame buffer", FAS_OUT_OF_MEMORY); + } + + fas_context->rgb_buffer = 0; + fas_context->gray8_buffer = 0; + fas_context->rgb_already_converted = FAS_FALSE; + fas_context->gray8_already_converted = FAS_FALSE; + + *context_ptr = fas_context; + + + if (FAS_SUCCESS != fas_step_forward(*context_ptr)) + return private_show_error ("failure decoding first frame", FAS_NO_MORE_FRAMES); + + if (!fas_frame_available(*context_ptr)) + return private_show_error ("couldn't find a first frame (no valid frames in video stream)", FAS_NO_MORE_FRAMES); + + + + return FAS_SUCCESS; +} + +/* fas_close_video */ +fas_error_type fas_close_video (fas_context_ref_type context) +{ + if (NULL == context) + return private_show_error ("NULL context provided for fas_close_video()", FAS_INVALID_ARGUMENT); + + if (!(context->is_video_active)) + { + private_show_warning ("Redundant attempt to close an inactive video"); + return FAS_SUCCESS; + } + + if (context->codec_context) + if (avcodec_find_decoder (context->codec_context->codec_id)) + avcodec_close(context->codec_context); + + if (context->format_context) + avformat_close_input (&context->format_context); + + if (context->rgb_frame_buffer) + av_free (context->rgb_frame_buffer); + + if (context->gray8_frame_buffer) + av_free (context->gray8_frame_buffer); + + if (context->rgb_buffer) + av_free(context->rgb_buffer); + + if (context->gray8_buffer) + av_free(context->gray8_buffer); + + if (context->frame_buffer) + av_free (context->frame_buffer); + + seek_release_table (&(context->seek_table)); + + context->is_video_active = FAS_FALSE; + + free (context); + + return FAS_SUCCESS; +} + + +/* fas_step_forward */ +fas_error_type fas_step_forward (fas_context_ref_type context) +{ + if ((NULL == context) || (FAS_TRUE != context->is_video_active)) { + return private_show_error ("invalid or unopened context", FAS_INVALID_ARGUMENT); + } + + if (!context->is_frame_available) + { + private_show_warning ("tried to advance after end of frames"); + return FAS_SUCCESS; + } + + context->current_frame_index++; + + AVPacket packet; + while (FAS_TRUE) + { + if (av_read_frame(context->format_context, &packet) < 0) + { + /* finished */ + context->is_frame_available = FAS_FALSE; + context->seek_table.completed = seek_true; + return FAS_SUCCESS; + } + + int frameFinished; + if (packet.stream_index == context->stream_idx) + { + context->previous_dts = context->current_dts; + context->current_dts = packet.dts; + + /* seek support: set first_dts */ + if (context->first_dts == AV_NOPTS_VALUE) + context->first_dts = packet.dts; + + /* seek support: set key-packet info to previous packet's dts, when possible */ + /* note this -1 approach to setting the packet is a workaround for a common failure. setting + to 0 would work just incur a huge penalty in videos that needed -1. Might be worth testing. + */ + if (packet.flags & AV_PKT_FLAG_KEY) + { + //fprintf(stderr, "Packet: (F:%d %lld %lld)\n", context->current_frame_index, packet.pts, packet.dts); + + if (context->previous_dts == AV_NOPTS_VALUE) + context->keyframe_packet_dts = packet.dts; + else + context->keyframe_packet_dts = context->previous_dts; + } + + avcodec_decode_video2(context->codec_context, context->frame_buffer, &frameFinished, &packet); + + if (frameFinished) + { + /* seek support: (try to) add entry to seek_table */ + if (context->frame_buffer->key_frame) + { + // fprintf(stderr, "Frame : (PXX F%d: %lld %lld)\n", context->current_frame_index, packet.pts, packet.dts); + + seek_entry_type entry; + entry.display_index = context->current_frame_index; + entry.first_packet_dts = context->keyframe_packet_dts; + entry.last_packet_dts = packet.dts; + + if (fas_get_frame_index(context) == FIRST_FRAME_INDEX) + entry.first_packet_dts = context->first_dts; + + seek_append_table_entry(&context->seek_table, entry); + } + + if (context->current_frame_index - FIRST_FRAME_INDEX + 1 > context->seek_table.num_frames) + context->seek_table.num_frames = context->current_frame_index - FIRST_FRAME_INDEX + 1; + + break; + } + } + + av_free_packet(&packet); + } + + context->rgb_already_converted = FAS_FALSE; + context->gray8_already_converted = FAS_FALSE; + av_free_packet(&packet); + return FAS_SUCCESS; +} + +/* fas_get_frame_index */ + +int fas_get_frame_index (fas_context_ref_type context) +{ + if (NULL == context) + return private_show_error ("NULL context provided for fas_get_frame_index()", FAS_INVALID_ARGUMENT); + + if (FAS_TRUE != context->is_video_active) + return private_show_error ("No video is open for fas_get_frame_index()", FAS_INVALID_ARGUMENT); + + return context->current_frame_index; +} + + +/* fas_get_frame */ + +fas_error_type fas_get_frame (fas_context_ref_type context, fas_raw_image_type *image_ptr) +{ + int buffer_size; + fas_error_type fas_error; + + if (NULL == context || FAS_FALSE == context->is_video_active) + return private_show_error ("null context or inactive video", FAS_INVALID_ARGUMENT); + + if (NULL == image_ptr) + return private_show_error ("null image_ptr on get_frame", FAS_INVALID_ARGUMENT); + + if (!fas_frame_available(context)) + return private_show_error ("no frame available for extraction", FAS_NO_MORE_FRAMES); + + memset (image_ptr, 0, sizeof (fas_raw_image_type)); + + switch (fmt) + { + case PIX_FMT_RGB24: + image_ptr->bytes_per_line = context->codec_context->width * 3; + image_ptr->color_space = FAS_RGB24; + break; + case PIX_FMT_BGR24: + image_ptr->bytes_per_line = context->codec_context->width * 3; + image_ptr->color_space = FAS_BGR24; + break; + case PIX_FMT_ARGB: + image_ptr->bytes_per_line = context->codec_context->width * 4; + image_ptr->color_space = FAS_ARGB32; + break; + case PIX_FMT_ABGR: + image_ptr->bytes_per_line = context->codec_context->width * 4; + image_ptr->color_space = FAS_ABGR32; + break; + case PIX_FMT_YUV420P: + image_ptr->bytes_per_line = (context->codec_context->width * 3) >> 1; + image_ptr->color_space = FAS_YUV420P; + break; + case PIX_FMT_YUYV422: + image_ptr->bytes_per_line = context->codec_context->width * 2; + image_ptr->color_space = FAS_YUYV422; + break; + case PIX_FMT_UYVY422: + image_ptr->bytes_per_line = context->codec_context->width * 2; + image_ptr->color_space = FAS_UYVY422; + break; + case PIX_FMT_YUV422P: + image_ptr->bytes_per_line = context->codec_context->width * 2; + image_ptr->color_space = FAS_YUV422P; + break; + case PIX_FMT_YUV444P: + image_ptr->bytes_per_line = context->codec_context->width * 3; + image_ptr->color_space = FAS_YUV444P; + break; + default: //iinserted TJR - to suppress warnings + break; + } + + buffer_size = image_ptr->bytes_per_line * context->codec_context->height; + + image_ptr->data = (unsigned char *)malloc (buffer_size); + if (NULL == image_ptr->data) + return private_show_error ("unable to allocate space for RGB image", FAS_OUT_OF_MEMORY); + + image_ptr->width = context->codec_context->width; + image_ptr->height = context->codec_context->height; + + + fas_error = private_convert_to_rgb(context); + + int j; + unsigned char *from; + unsigned char *to; + for (j=0;j<context->codec_context->height; j++) + { + from = context->rgb_frame_buffer->data[0] + j*context->rgb_frame_buffer->linesize[0]; + to = image_ptr->data + j*image_ptr->bytes_per_line; + + memcpy(to, from, image_ptr->bytes_per_line); + } + + if (FAS_SUCCESS != fas_error) + return private_show_error ("unable to convert image to RGB", FAS_FAILURE); + + return FAS_SUCCESS; +} + +/* fas_free_frame */ + +void fas_free_frame (fas_raw_image_type image) +{ + if (NULL == image.data) + return; + + free (image.data); + + return; +} + +/* fas_get_seek_table */ +seek_table_type fas_get_seek_table (fas_context_ref_type context) +{ + seek_table_type null_table; + + null_table.array = NULL; + null_table.completed = seek_false; + null_table.num_frames = -1; + null_table.num_entries = 0; + null_table.allocated_size = 0; + + if (NULL == context || FAS_FALSE == context->is_video_active) + return null_table; + + return context->seek_table; +} + +/* fas_put_seek_table */ +fas_error_type fas_put_seek_table (fas_context_ref_type context, seek_table_type table) +{ + if (NULL == context || FAS_FALSE == context->is_video_active) + return private_show_error ("null context or inactive video", FAS_INVALID_ARGUMENT); + + seek_release_table (&context->seek_table); + context->seek_table = seek_copy_table(table); + + return FAS_SUCCESS; +} + +/* private_complete_seek_table */ +fas_error_type private_complete_seek_table (fas_context_ref_type context) +{ + if ((NULL == context) || (FAS_FALSE == context->is_video_active)) + return private_show_error ("invalid or unopened context", FAS_INVALID_ARGUMENT); + + if (context->seek_table.completed) + return FAS_SUCCESS; + + fas_error_type fas_error = fas_seek_to_nearest_key (context, context->seek_table.num_frames + FIRST_FRAME_INDEX - 1); + if (FAS_SUCCESS != fas_error) + return private_show_error("failed when trying to complete seek table (1) (first frame not labeled keyframe?)", fas_error); + + while (fas_frame_available(context)) + { + // printf("%d\n", context->seek_table.num_frames); + fas_step_forward(context); + } + + if (!context->seek_table.completed) + return private_show_error("failed when trying to complete seek table (2)", FAS_SEEK_ERROR); + + return FAS_SUCCESS; +} + +/* fas_seek_to_frame */ +fas_error_type fas_seek_to_frame (fas_context_ref_type context, int target_index) +{ + + fas_error_type fas_error; + + if ((NULL == context) || (FAS_FALSE == context->is_video_active)) + return private_show_error ("invalid or unopened context", FAS_INVALID_ARGUMENT); + + // printf("seeking to %d (from %d)!\n", target_index, context->current_frame_index); + if (target_index == context->current_frame_index) + return FAS_SUCCESS; + + fas_error = fas_seek_to_nearest_key (context, target_index); + + if (fas_error != FAS_SUCCESS) + return private_show_error ("error advancing to key frame before seek", fas_error); + + if (fas_get_frame_index(context) > target_index) + return private_show_error ("error advancing to key frame before seek (index isn't right)", fas_error); + + while (fas_get_frame_index(context) < target_index) + { + if (fas_frame_available(context)) + fas_step_forward(context); + else + return private_show_error ("error advancing to request frame (probably out of range)", FAS_SEEK_ERROR); + } + + + return FAS_SUCCESS; +} + +/* fas_seek_to_nearest_key */ + +fas_error_type fas_seek_to_nearest_key (fas_context_ref_type context, int target_index) +{ + return private_seek_to_nearest_key(context, target_index,0); +} + +/* private_seek_to_nearest_key */ + +fas_error_type private_seek_to_nearest_key (fas_context_ref_type context, int target_index, int offset) +{ + if ((NULL == context) || (FAS_TRUE != context->is_video_active)) + return private_show_error ("invalid or unopened context", FAS_INVALID_ARGUMENT); + + // printf("HERE: from: %d to: %d offset: %d\n", context->current_frame_index, target_index, offset); + fas_error_type fas_error; + seek_entry_type seek_entry; + seek_error_type seek_error = seek_get_nearest_entry (&(context->seek_table), &seek_entry, target_index, offset); + + if (seek_error != seek_no_error) + return private_show_error ("error while searching seek table", FAS_SEEK_ERROR); + + if (seek_entry.display_index == context->current_frame_index) + return FAS_SUCCESS; + + // printf("HERE: from: %d to: %d (%d) offset: %d\n", context->current_frame_index, target_index, seek_entry.display_index, offset); + // printf("trying to seek to %d (%lld->%lld)\n", seek_entry.display_index, seek_entry.first_packet_dts, seek_entry.last_packet_dts); + + // if something goes terribly wrong, return bad current_frame_index + context->current_frame_index = -2; + context->is_frame_available = FAS_TRUE; + + int flags = 0; + if (seek_entry.first_packet_dts <= context->current_dts) + flags = AVSEEK_FLAG_BACKWARD; + + // printf("av_seek_frame: %lld\n", seek_entry.first_packet_dts); + if (av_seek_frame(context->format_context, context->stream_idx, seek_entry.first_packet_dts, flags) < 0) + return private_show_error("seek to keyframe failed", FAS_SEEK_ERROR); + + + avcodec_flush_buffers (context->codec_context); + + fas_error = fas_step_forward (context); + + if (fas_error != FAS_SUCCESS || !context->is_frame_available) + { + // something bad has happened, try previous keyframe + private_show_warning("processing of seeked keyframe failed, trying previous keyframe"); + return private_seek_to_nearest_key(context, target_index, offset + 1); + } + + while (context->current_dts < seek_entry.last_packet_dts) + { + //printf("frame-times: current: %lld target: %lld is_key: %d\n", context->current_dts, seek_entry.last_packet_dts, context->frame_buffer->key_frame); + fas_error = fas_step_forward(context); + if (fas_error != FAS_SUCCESS) + return private_show_error ("unable to process up to target frame (fas_seek_to_frame)", fas_error); + } + + // printf("keyframe vitals: %d looking_for: %lld at: %lld\n", seek_entry.display_index, seek_entry.last_packet_dts, context->current_dts); + if (context->current_dts != seek_entry.last_packet_dts) + { + /* seek to last key-frame, but look for this one */ + private_show_warning("missed keyframe, trying previous keyframe"); + return private_seek_to_nearest_key(context, target_index, offset + 1); + } + + /* Ideally, we could just check if the frame decoded is of the correct time stamp... but... we need several ugly workarounds: + + 1) Some videos have bad keyframes that don't get decoded properly. In this cases, we need to go back a keyframe. + + 2) Other times, none of the frames are labeled keyframes. In these cases, we need to allow seeking to frame 0 + even when it's not labeled as a keyframe. Messy set of conditions. + */ + + if ((!context->frame_buffer->key_frame) && (seek_entry.display_index != 0)) + { + private_show_warning("found keyframe, but not labeled as keyframe, so trying previous keyframe."); + /* seek & look for previous keyframe */ + /* REMOVE FROM TABLE? */ + return private_seek_to_nearest_key(context, seek_entry.display_index - 1, 0); + } + + context->current_frame_index = seek_entry.display_index; + + return FAS_SUCCESS; +} + +/* fas_get_frame_count */ + +int fas_get_frame_count_fast (fas_context_ref_type context) +{ + + if (NULL == context || FAS_FALSE == context->is_video_active) + { + private_show_error ("NULL or invalid context", FAS_INVALID_ARGUMENT); + return -1; + } + + if (context->seek_table.completed == seek_true) + return context->seek_table.num_frames; + + return -1; +} + +int fas_get_frame_count (fas_context_ref_type context) +{ + int fast = fas_get_frame_count_fast(context); + if (fast >= 0) + return fast; + + int current_frame = fas_get_frame_index(context); + + fas_error_type fas_error; + + fas_error = private_complete_seek_table(context); + if (FAS_SUCCESS != fas_error) + { + private_show_error("failed in get_frame_count trying to complete the seek table", fas_error); + return -1; + } + + // seek_show_raw_table(stderr, context->seek_table); + + fas_error = fas_seek_to_frame(context, current_frame); + if (FAS_SUCCESS != fas_error) + { + private_show_error("failed in get_frame_count when trying to seek back to original location", fas_error); + return -1; + } + + fast = fas_get_frame_count_fast(context); + if (fast < 0) + private_show_warning("get_frame_count failed"); + + return fast; +} + +/* fas_frame_available */ + +fas_boolean_type fas_frame_available (fas_context_ref_type context) +{ + if (NULL == context) + { + private_show_error ("NULL context provided for fas_get_frame_index()", FAS_INVALID_ARGUMENT); + return FAS_FALSE; + } + + if (!context->is_video_active) + return FAS_FALSE; + + return context->is_frame_available; +} + + +/* private_show_error */ + +fas_error_type private_show_error (const char *message, fas_error_type error) +{ + if (SHOW_ERROR_MESSAGES) + fprintf (stderr, " ===> ffmpeg_fas: %s\n", message); + return error; +} + +void private_show_warning (const char *message) +{ + if (SHOW_WARNING_MESSAGES) + fprintf (stderr, " ---- ffmpeg_fas: %s\n", message); + return; +} + + +/* private_convert_to_rgb */ + +fas_error_type private_convert_to_rgb (fas_context_ref_type ctx) +{ + if (ctx->rgb_already_converted) + return FAS_SUCCESS; + + + if (ctx->rgb_buffer == 0) + { + int numBytes = avpicture_get_size(fmt, ctx->codec_context->width, + ctx->codec_context->height); + ctx->rgb_buffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t)); + avpicture_fill((AVPicture *) ctx->rgb_frame_buffer, ctx->rgb_buffer, fmt, + ctx->codec_context->width, ctx->codec_context->height); + } + // +/* + if (!sws_ctx) { + sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P, + c->width, c->height, c->pix_fmt, + sws_flags, NULL, NULL, NULL); + if (!sws_ctx) { + //fprintf(stderr, + // "Could not initialize the conversion context\n"); + exit(1); + } + } +*/ + +//int img_convert ( AVPicture * dst, +//int dst_pix_fmt, +//const AVPicture * src, +//int src_pix_fmt, +//int src_width, +//int src_height +//) + + //maybe tricky to convert: use sws_ + sws_scale(Sctx, // sws context + ctx->frame_buffer->data, // src slice + ctx->frame_buffer->linesize, // src stride + 0, // src slice origin y + ctx->frame_buffer->height, // src slice height + ctx->rgb_frame_buffer->data, // dst + ctx->rgb_frame_buffer->linesize ); // dst stride + //instead of + //if (img_convert((AVPicture *) ctx->rgb_frame_buffer, fmt, (AVPicture *) ctx->frame_buffer, + // ctx->codec_context->pix_fmt, + // ctx->codec_context->width, ctx->codec_context->height) < 0) + // private_show_error("error converting to rgb", FAS_DECODING_ERROR); + + ctx->rgb_already_converted = FAS_TRUE; + + return FAS_SUCCESS; +} + + +/* private_convert_to_gray8 */ +/* +fas_error_type private_convert_to_gray8 (fas_context_ref_type ctx) +{ + if (ctx->gray8_already_converted) + return FAS_SUCCESS; + + if (ctx->gray8_buffer == 0) + { + int numBytes = avpicture_get_size(PIX_FMT_GRAY8, ctx->codec_context->width, + ctx->codec_context->height); + ctx->gray8_buffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t)); + avpicture_fill((AVPicture *) ctx->gray8_frame_buffer, ctx->gray8_buffer, PIX_FMT_GRAY8, + ctx->codec_context->width, ctx->codec_context->height); + } + + if (img_convert((AVPicture *) ctx->gray8_frame_buffer, PIX_FMT_GRAY8, (AVPicture *) ctx->frame_buffer, + ctx->codec_context->pix_fmt, + ctx->codec_context->width, ctx->codec_context->height) < 0) + private_show_error("error converting to gray8", FAS_DECODING_ERROR); + + ctx->gray8_already_converted = FAS_TRUE; + + return FAS_SUCCESS; +} +*/ + +int fas_get_current_width(fas_context_ref_type context) +{ + return context->codec_context->width; +} + +int fas_get_current_height(fas_context_ref_type context) +{ + return context->codec_context->height; +} + +unsigned long long fas_get_frame_duration(fas_context_ref_type context) +{ + if (context->format_context->streams[context->stream_idx]->time_base.den != context->format_context->streams[context->stream_idx]->r_frame_rate.num + || context->format_context->streams[context->stream_idx]->time_base.num != context->format_context->streams[context->stream_idx]->r_frame_rate.den) + { + double frac = (double)(context->format_context->streams[context->stream_idx]->r_frame_rate.den) / (double)(context->format_context->streams[context->stream_idx]->r_frame_rate.num); + return (unsigned long long)(frac*10000000.); + } + else + { + return (unsigned long long)(((double)(context->format_context->streams[context->stream_idx]->time_base.num)) + /((double)(context->format_context->streams[context->stream_idx]->time_base.den))*10000000.); + } +} + +fas_error_type fas_fill_gray8_ptr(fas_context_ref_type context, unsigned char *y) +{ + /* this conversion also seems to screw up sometimes -- pal8 -> gray8? legodragon.avi */ + //if (private_convert_to_gray8(context) != FAS_SUCCESS) TJR commented out + // return FAS_FAILURE; + + int width = context->codec_context->width; + int height = context->codec_context->height; + int i; + for (i=0;i < height; i++) + memcpy(y + width * i, context->gray8_frame_buffer->data[0] + context->gray8_frame_buffer->linesize[0] * i, width); + + return FAS_SUCCESS; +} + +fas_error_type fas_fill_420p_ptrs (fas_context_ref_type context, unsigned char *y, unsigned char *u, unsigned char *v) +{ + AVFrame *p = context->frame_buffer; + + /* 411p to 420p conversion fails!? ... so i left this -ldb */ + if (context->codec_context->pix_fmt != PIX_FMT_YUV420P) + return FAS_FAILURE; + + int width = context->codec_context->width; + int height = context->codec_context->height; + int i; + for (i=0;i < height / 2; i++) + { + memcpy(y + width * (2*i) , p->data[0] + p->linesize[0] * (2*i) , width); + memcpy(y + width * (2*i + 1), p->data[0] + p->linesize[0] * (2*i + 1), width); + memcpy(u + width / 2 * i, p->data[1] + p->linesize[1] * i, width / 2); + memcpy(v + width / 2 * i, p->data[2] + p->linesize[2] * i, width / 2); + } + + return FAS_SUCCESS; +} + diff --git a/rotord/src/ffmpeg_fas.h b/rotord/src/ffmpeg_fas.h new file mode 100644 index 0000000..953c8b4 --- /dev/null +++ b/rotord/src/ffmpeg_fas.h @@ -0,0 +1,125 @@ +/***************************************************************************** + * Copyright 2008. Pittsburgh Pattern Recognition, Inc. + * + * This file is part of the Frame Accurate Seeking extension library to + * ffmpeg (ffmpeg-fas). + * + * ffmpeg-fas is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * The ffmpeg-fas library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ffmpeg-fas library. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef FFMPEG_FAS_H +#define FFMPEG_FAS_H + +/* If C++ then we need to __extern "C". Compiler defines __cplusplus */ +#ifdef __cplusplus +#define __extern extern "C" +#else +#define __extern extern +#endif + +#include "seek_indices.h" +#include <libswscale/swscale.h> + + +static struct SwsContext *Sctx; +static int sws_flags = SWS_BICUBIC; + + +typedef enum +{ + FAS_GRAY8 = 1, + FAS_RGB24 = 2, + FAS_BGR24 = 3, + FAS_ARGB32 = 4, + FAS_ABGR32 = 5, + FAS_YUV420P = 6, + FAS_YUYV422 = 7, + FAS_UYVY422 = 8, + FAS_YUV422P = 9, + FAS_YUV444P = 10, +} fas_color_space_type; + +typedef struct +{ + unsigned char *data; + int width; + int height; + int bytes_per_line; + fas_color_space_type color_space; +} fas_raw_image_type; + + +/********************************************************************** + * Video IO Types + **********************************************************************/ + +typedef struct fas_context_struct* fas_context_ref_type; + +typedef enum +{ + FAS_SUCCESS, + FAS_FAILURE, + FAS_INVALID_ARGUMENT, + FAS_OUT_OF_MEMORY, + FAS_UNSUPPORTED_FORMAT, + FAS_UNSUPPORTED_CODEC, + FAS_NO_MORE_FRAMES, + FAS_DECODING_ERROR, + FAS_SEEK_ERROR, +} fas_error_type; + +typedef enum +{ + FAS_FALSE = 0, + FAS_TRUE = 1 +} fas_boolean_type; + + +__extern void fas_initialize (fas_boolean_type logging, fas_color_space_type format); +__extern void fas_set_format (fas_color_space_type format); + +__extern fas_error_type fas_open_video (fas_context_ref_type *context_ptr, char *file_path); +__extern fas_error_type fas_close_video (fas_context_ref_type context); + +__extern char* fas_error_message (fas_error_type error); + +__extern fas_boolean_type fas_frame_available (fas_context_ref_type context); +__extern int fas_get_frame_index (fas_context_ref_type context); +__extern fas_error_type fas_step_forward (fas_context_ref_type context); + +__extern fas_error_type fas_get_frame (fas_context_ref_type context, fas_raw_image_type *image_ptr); +__extern void fas_free_frame (fas_raw_image_type image); + +__extern fas_error_type fas_seek_to_nearest_key (fas_context_ref_type context, int target_index); +__extern fas_error_type fas_seek_to_frame (fas_context_ref_type context, int target_index); + +__extern int fas_get_frame_count (fas_context_ref_type context); +__extern int fas_get_frame_count_fast (fas_context_ref_type context); + +__extern fas_error_type fas_put_seek_table (fas_context_ref_type context, seek_table_type table); +__extern seek_table_type fas_get_seek_table (fas_context_ref_type context); + +/* will extract raw 420p if the video is in that format -- needs to be alloced ahead of time*/ +__extern fas_error_type fas_fill_420p_ptrs (fas_context_ref_type context, unsigned char *y, unsigned char *u, unsigned char *v); + +/* will extract gray8 data from movie (will convert to ensure you get it) -- need to be alloc'ed ahead of time*/ +__extern fas_error_type fas_fill_gray8_ptr(fas_context_ref_type context, unsigned char *y); + +__extern int fas_get_current_width(fas_context_ref_type context); +__extern int fas_get_current_height(fas_context_ref_type context); + +__extern unsigned long long fas_get_frame_duration(fas_context_ref_type context); + +#endif diff --git a/rotord/src/private_errors.h b/rotord/src/private_errors.h new file mode 100644 index 0000000..9369ae3 --- /dev/null +++ b/rotord/src/private_errors.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright 2008. Pittsburgh Pattern Recognition, Inc. + * + * This file is part of the Frame Accurate Seeking extension library to + * ffmpeg (ffmpeg-fas). + * + * ffmpeg-fas is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * The ffmpeg-fas library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ffmpeg-fas library. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef FAS_PRIVATE_ERROR_H +#define FAS_PRIVATE_ERROR_H + +#if defined( _WIN32 ) && defined( STATIC_DLL ) +static int SHOW_ERROR_MESSAGES; +static int SHOW_WARNING_MESSAGES; +#else +int SHOW_ERROR_MESSAGES; +int SHOW_WARNING_MESSAGES; +#endif /* _WIN32 && STATIC_DLL */ +#endif diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h index 310809f..b2f50e6 100755 --- a/rotord/src/rotor.h +++ b/rotord/src/rotor.h @@ -29,6 +29,7 @@ Definitions of base classes and types for rotor rendering graph #include "utils.h" #include "cvimage.h" #include "libavwrapper.h" +#include "ffmpeg-fas_wrapper.h" //using namespace cv; namespace Rotor { diff --git a/rotord/src/seek_indices.c b/rotord/src/seek_indices.c new file mode 100644 index 0000000..cb877c3 --- /dev/null +++ b/rotord/src/seek_indices.c @@ -0,0 +1,319 @@ +/***************************************************************************** + * Copyright 2008. Pittsburgh Pattern Recognition, Inc. + * + * This file is part of the Frame Accurate Seeking extension library to + * ffmpeg (ffmpeg-fas). + * + * ffmpeg-fas is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * The ffmpeg-fas library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ffmpeg-fas library. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "seek_indices.h" +#include "private_errors.h" + +/**** Defines *****************************************************************/ + +#define DEFAULT_INITIAL_SIZE 100 + +static seek_error_type private_show_error (const char *message, seek_error_type error); +static seek_error_type private_resize_table (seek_table_type *table, int new_size); + + +/* + * seek_init_table + */ + +int compare_seek_tables(seek_table_type t1, seek_table_type t2) +{ + int i; + + // printf("n_entries=(%d %d)\n", t1.num_entries, t2.num_entries); + + if (t1.num_entries != t2.num_entries) + return 0; + + if (t1.completed != t2.completed) + return 0; + + if (t1.num_frames != t2.num_frames) + return 0; + + for (i=0;i<t1.num_entries;i++) + { + // printf("(%d %d) (%lld %lld) (%lld %lld)\n", + // t1.array[i].display_index, t2.array[i].display_index, + // t1.array[i].decode_time, t2.array[i].decode_time, + // t1.array[i].display_time, t2.array[i].display_time); + if ((t1.array[i].display_index != t2.array[i].display_index) || + (t1.array[i].last_packet_dts != t2.array[i].last_packet_dts) || + (t1.array[i].first_packet_dts != t2.array[i].first_packet_dts)) + return 0; + } + + return 1; +} + +seek_table_type seek_init_table (int initial_size) +{ + seek_table_type table; + + if (initial_size < 0) + initial_size = DEFAULT_INITIAL_SIZE; + + table.num_entries = 0; + table.num_frames = -1; + table.completed = seek_false; + + table.array = (seek_entry_type *)malloc (initial_size * sizeof(seek_entry_type)); + + if (NULL == table.array) + table.allocated_size = 0; + else + table.allocated_size = initial_size; + + return table; +} + +/* + * seek_release_table + */ + +void seek_release_table (seek_table_type *table) +{ + table->num_entries = 0; + table->num_frames = -1; + table->completed = seek_false; + + if (NULL == table || NULL == table->array) + return; + + free (table->array); + return; +} + +/* + * seek_copy_table + */ + +seek_table_type seek_copy_table (seek_table_type source) +{ + seek_table_type dest; + dest.num_entries = source.num_entries; + dest.num_frames = source.num_frames; + dest.completed = source.completed; + + if (NULL == source.array) + { + dest.array = NULL; + dest.allocated_size = 0; + return dest; + } + + dest.array = (seek_entry_type *)malloc (source.num_entries * sizeof(seek_entry_type)); + + if (NULL == dest.array) + { + dest.array = NULL; + dest.allocated_size = 0; + return dest; + } + + dest.allocated_size = source.num_entries; + + int i; + for (i=0;i<source.num_entries;i++) + dest.array[i] = source.array[i]; + + return dest; +} + +seek_error_type seek_append_table_entry (seek_table_type *table, seek_entry_type entry) +{ + + if (NULL == table || NULL == table->array) + return private_show_error("null or invalid seek table", seek_bad_argument); + + if (table->num_entries != 0) + if (table->array[table->num_entries - 1].display_index >= entry.display_index) + return seek_no_error; + + if (table->num_entries == table->allocated_size) + { + seek_error_type error = private_resize_table (table, table->num_entries * 2); + if (error != seek_no_error) + return private_show_error ("unable to resize seek table", error); + } + + table->array[table->num_entries] = entry; + table->num_entries++; + + return seek_no_error; +} + +/* + * seek_get_nearest_entry + */ + +seek_error_type seek_get_nearest_entry (seek_table_type *table, seek_entry_type *entry, int display_index, int offset) +{ + /* using offset>0 returns a modified seek_entry that sets the 'time-to-seek' to be $offset keyframes in the past. + */ + + if (NULL == table || NULL == table->array || table->num_entries <= 0) { + return private_show_error ("NULL or invalid seek table", seek_bad_argument); + } + + if (NULL == entry) { + return private_show_error ("NULL entry buffer (for return)", seek_bad_argument); + } + + if (display_index < table->array[0].display_index) + return private_show_error ("tried to seek to frame index before first frame", seek_bad_argument); + + int i; + for (i=0; i < table->num_entries; i++) + if (table->array[i].display_index > display_index) + break; + + i = i-1; + + if (i<offset) /* target was lower than first element (including offset) */ + return private_show_error ("target index out of table range (too small)", seek_bad_argument); + + *entry = table->array[i]; + (*entry).first_packet_dts = table->array[i-offset].first_packet_dts; + + return seek_no_error; +} + + +/* read raw file */ +seek_table_type read_table_file(char *name) +{ + seek_table_type ans = { NULL, (seek_boolean_type) 0, (seek_boolean_type) 0 }; + + FILE *table_file = fopen(name, "r"); + if (table_file == NULL) + return ans; + + int completed_flag; + fscanf(table_file, "%d %d %d\n", &ans.num_frames, &ans.num_entries, &completed_flag); + + if (completed_flag == 1) + ans.completed = seek_true; + else + ans.completed = seek_false; + + ans.allocated_size = ans.num_entries; + ans.array = (seek_entry_type*) malloc (ans.allocated_size * sizeof(seek_entry_type)); + + int i; + for (i=0;i<ans.num_entries;i++) + fscanf(table_file, "%d %lld %lld\n", &(ans.array[i].display_index), &(ans.array[i].first_packet_dts), &(ans.array[i].last_packet_dts)); + + fclose(table_file); + return ans; +} + +seek_error_type seek_show_raw_table (FILE* file, seek_table_type table) +{ + seek_entry_type *entry; + int index; + + if (NULL == table.array || table.num_entries <= 0) + return private_show_error ("NULL or invalid seek table", seek_bad_argument); + + int completed_flag = 0; + if (table.completed == seek_true) + completed_flag = 1; + + fprintf(file, "%d %d %d\n", table.num_frames, table.num_entries, completed_flag); + for (index = 0; index < table.num_entries; index++) + { + entry = &(table.array[index]); + + fprintf (file, "%d %lld %lld\n", entry->display_index, entry->first_packet_dts, entry->last_packet_dts); + } + return seek_no_error; +} + +seek_error_type seek_show_table (seek_table_type table) +{ + seek_entry_type *entry; + int index; + + if (NULL == table.array || table.num_entries <= 0) { + return private_show_error ("NULL or invalid seek table", seek_bad_argument); + } + + int completed_flag = 0; + if (table.completed == seek_true) + completed_flag = 1; + + fprintf (stderr, "--- Seek Table Dump ---\n"); + fprintf (stderr, "n_frames: %d n_entries: %d completed: %d\n",table.num_frames, table.num_entries, completed_flag); + for (index = 0; index < table.num_entries; index++) + { + entry = &(table.array[index]); + + fprintf (stderr, " %04d --> %08lld (%08lld)\n", entry->display_index, entry->first_packet_dts, entry->last_packet_dts); + } + + fprintf (stderr, "-----------------------\n"); + + return seek_no_error; +} + +/* + * private_show_error + */ + +static seek_error_type private_show_error (const char *message, seek_error_type error) +{ + if (SHOW_ERROR_MESSAGES) + fprintf (stderr, " ===> seek_indices: %s\n", message); + + return error; +} + +/* + * private_resize_table + */ + +static seek_error_type private_resize_table (seek_table_type *table, int new_size) +{ + seek_entry_type *new_array = NULL; + + if (table == NULL || new_size < 0) { + return private_show_error ("invalid argument for private_resize_table()", seek_malloc_failed); + } + + new_array = (seek_entry_type *)malloc (sizeof (seek_entry_type) * new_size); + if (NULL == new_array) { + return private_show_error ("unable to allocate more space for table", seek_malloc_failed); + } + + memcpy (new_array, table->array, table->allocated_size * sizeof (seek_entry_type)); + free (table->array); + + table->allocated_size = new_size; + table->array = new_array; + + return seek_no_error; +} diff --git a/rotord/src/seek_indices.h b/rotord/src/seek_indices.h new file mode 100644 index 0000000..e908979 --- /dev/null +++ b/rotord/src/seek_indices.h @@ -0,0 +1,94 @@ +/***************************************************************************** + * Copyright 2008. Pittsburgh Pattern Recognition, Inc. + * + * This file is part of the Frame Accurate Seeking extension library to + * ffmpeg (ffmpeg-fas). + * + * ffmpeg-fas is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * The ffmpeg-fas library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ffmpeg-fas library. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef FAS_SEEK_INDICES_H +#define FAS_SEEK_INDICES_H + +#include <stdint.h> +#include <stdio.h> + +/* If C++ then we need to __extern "C". Compiler defines __cplusplus */ +#ifdef __cplusplus +#define __extern extern "C" +#else +#define __extern extern +#endif + + +/********************************************************************** + * Seek Table Types + **********************************************************************/ + +typedef enum +{ + seek_no_error, + seek_unknown_error, + seek_bad_argument, + seek_malloc_failed, +} seek_error_type; + +typedef enum +{ + seek_false = 0, + seek_true = 1 +} seek_boolean_type; + +typedef struct +{ + int display_index; + int64_t first_packet_dts; + int64_t last_packet_dts; +} seek_entry_type; + +typedef struct +{ + seek_entry_type *array; + seek_boolean_type completed; + int num_frames; // total number of frames + int num_entries; // ie, number of seek-points (keyframes) + int allocated_size; +} seek_table_type; + + + +/********************************************************************** + * Seek Table Functions + **********************************************************************/ + + +__extern seek_table_type seek_init_table (int initial_size); +__extern void seek_release_table (seek_table_type *table); + +__extern seek_table_type seek_copy_table (seek_table_type source); +__extern int compare_seek_tables(seek_table_type t1, seek_table_type t2); + +__extern seek_error_type seek_append_table_entry (seek_table_type *table, seek_entry_type entry); + +__extern seek_error_type seek_get_nearest_entry (seek_table_type *table, seek_entry_type *entry, int display_index, int offset); + +__extern seek_error_type seek_show_table (seek_table_type table); /* human readable */ +__extern seek_error_type seek_show_raw_table (FILE *file, seek_table_type table); + +__extern seek_table_type read_table_file(char *name); /* read raw file */ + +#endif + +/**** End of File *****************************************************/ diff --git a/rotord/src/utils.cpp b/rotord/src/utils.cpp index cd12aa4..53c79ee 100644 --- a/rotord/src/utils.cpp +++ b/rotord/src/utils.cpp @@ -28,7 +28,7 @@ bool fgreater(const float u,const float v){ else return false; }; -//---------------------------------------- +//----------------------------------------with thanks to openframeworks template <> string toHex(const string& value) { ostringstream out; diff --git a/rotord/src/utils.h b/rotord/src/utils.h index d9df488..a36088b 100644 --- a/rotord/src/utils.h +++ b/rotord/src/utils.h @@ -18,6 +18,7 @@ bool fgreater_or_equal(const float u,const float v); bool fless(const float u,const float v); bool fgreater(const float u,const float v); +//----------------------------------------with thanks to openframeworks template <class T> std::string toString(const T& value){ |
