summaryrefslogtreecommitdiff
path: root/working
diff options
context:
space:
mode:
Diffstat (limited to 'working')
-rw-r--r--working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.cpp1
-rw-r--r--working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.h55
-rw-r--r--working/ffmpeg_fas-wrapper/ffmpeg_fas.c948
-rw-r--r--working/ffmpeg_fas-wrapper/ffmpeg_fas.h130
-rw-r--r--working/ffmpeg_fas-wrapper/private_errors.h32
-rw-r--r--working/ffmpeg_fas-wrapper/seek_indices.c319
6 files changed, 1485 insertions, 0 deletions
diff --git a/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.cpp b/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.cpp
new file mode 100644
index 0000000..f1b81c2
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.cpp
@@ -0,0 +1 @@
+#include "ffmpeg-fas_wrapper.h"
diff --git a/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.h b/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.h
new file mode 100644
index 0000000..4c8f96e
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/ffmpeg-fas_wrapper.h
@@ -0,0 +1,55 @@
+#ifndef ffmpeg_fas_wrapper_H
+#define ffmpeg_fas_wrapper_H
+
+#include <string>
+#include <iostream>
+#include "ffmpeg_fas.c"
+
+
+namespace ffmpeg_fas {
+ class decoder
+ {
+ public:
+ decoder(){
+ fas_initialize(FAS_TRUE,FAS_RGB24);
+ loaded=false;
+ framerate=0.0f;
+ numFrames=0;
+ }
+ bool open(std::string& filename){
+ fas_error_type e=fas_open_video(&context,filename.c_str());
+ if (e==FAS_SUCCESS){
+ loaded=true;
+ framerate=(((float)context->format_context->streams[context->stream_idx]->r_frame_rate.num)/((float)context->format_context->streams[context->stream_idx]->r_frame_rate.den));
+ numFrames=context->format_context->streams[context->stream_idx]->nb_frames;
+ if (numFrames<1){
+ //some codecs don't keep this info in the header
+ numFrames = (int)(( context->format_context->duration / (double)AV_TIME_BASE ) * framerate );
+ //this approach still doesn't seem to give quite the right answer- comes out a little too big
+ //could alternatively just redefine the length if the reader fails
+ }
+ }
+ else {
+ std::cerr<<"ffmpeg_fas ERROR: "<<fas_error_message(e)<<std::endl;
+ loaded=true;
+ }
+ return loaded;
+ }
+ float getFrameRate(){
+ return framerate;
+ }
+ int getNumberOfFrames();
+ int getNumberOfChannels();
+ int getWidth();
+ int getHeight();
+ bool fetchFrame(int width,int height,int wanted);
+ void cleanup(); //necessary?
+
+ fas_raw_image_type frame;
+ fas_context_ref_type context;
+ bool loaded;
+ float framerate;
+ int numFrames;
+ };
+}
+#endif
diff --git a/working/ffmpeg_fas-wrapper/ffmpeg_fas.c b/working/ffmpeg_fas-wrapper/ffmpeg_fas.c
new file mode 100644
index 0000000..19e9655
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/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/working/ffmpeg_fas-wrapper/ffmpeg_fas.h b/working/ffmpeg_fas-wrapper/ffmpeg_fas.h
new file mode 100644
index 0000000..0c51103
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/ffmpeg_fas.h
@@ -0,0 +1,130 @@
+/*****************************************************************************
+ * 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
+
+
+#ifndef UINT64_C
+#define UINT64_C(c) (c ## ULL)
+#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/working/ffmpeg_fas-wrapper/private_errors.h b/working/ffmpeg_fas-wrapper/private_errors.h
new file mode 100644
index 0000000..9369ae3
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/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/working/ffmpeg_fas-wrapper/seek_indices.c b/working/ffmpeg_fas-wrapper/seek_indices.c
new file mode 100644
index 0000000..cb877c3
--- /dev/null
+++ b/working/ffmpeg_fas-wrapper/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;
+}