Optimized Clip and VideoContext data structures

This commit is contained in:
Devon Crawford
2019-03-05 00:05:27 -05:00
parent baf1c55753
commit 190865a15c
14 changed files with 463 additions and 245 deletions

View File

@@ -37,7 +37,7 @@ int main(int argc, char **argv) {
ret = add_files(&orig_seq, files, num_files);
if(ret < 0) {
fprintf(stderr, "failed to add files\n");
return ret;
goto end;
}
char *str = print_sequence(&orig_seq);
@@ -72,6 +72,7 @@ int main(int argc, char **argv) {
}
ret = write_sequence(&new_seq, &op);
free_output_params(&op);
if(ret < 0) {
fprintf(stderr, "Failed to write new sequence to output file[%s]\n", op.filename);
goto end;
@@ -98,9 +99,6 @@ int random_edit(Sequence *os, Sequence *ns, RandSpliceParams *par) {
fprintf(stderr, "random_edit() error: cut_len_var[%d] must be less than cut_len_avg[%d]\n", par->cut_len_var, par->cut_len_avg);
return -1;
}
printf("get_sequence_duration: %ld\n", get_sequence_duration(ns));
printf("par->duration: %ld\n", par->duration);
printf("if %d\n", get_sequence_duration(ns) > par->duration);
if(get_sequence_duration(ns) > par->duration) {
return 0;
}
@@ -125,7 +123,6 @@ int random_cut(Sequence *os, Sequence *ns, RandSpliceParams *par) {
fprintf(stderr, "random_cut() error: Failed to pick frames\n");
return ret;
}
printf("pick_frames.. s: %d, e: %d\n", s, e);
return cut_remove_insert(os, ns, s, e);
}
@@ -155,32 +152,26 @@ int cut_remove_insert(Sequence *os, Sequence *ns, int start_index, int end_index
fprintf(stderr, "cut_remove_insert() error: Failed to find clip at cut center index[%d]\n", cut_center_index);
return -1;
}
Clip *cut_copy = alloc_clip(cut->url);
Clip *cut_copy = copy_clip_vc(cut);
if(cut_copy == NULL) {
fprintf(stderr, "cut_remove_insert() error: Failed to allocate cut_copy\n");
return -1;
}
ret = set_clip_bounds_pts(cut_copy, cut->orig_start_pts, cut->orig_end_pts);
if(ret < 0) {
free_clip(cut_copy);
free(cut_copy);
cut_copy = NULL;
free_clip(&cut_copy);
fprintf(stderr, "cut_remove_insert() error: Failed to set clip bounds on cut copy\n");
return ret;
}
ret = sequence_insert_clip_sorted(ns, cut_copy);
if(ret < 0) {
free_clip(cut_copy);
free(cut_copy);
cut_copy = NULL;
fprintf(stderr, "cut_remove_insert() error: failed to add clip[%s] to new sequence\n", cut_copy->url);
free_clip(&cut_copy);
fprintf(stderr, "cut_remove_insert() error: failed to add clip[%s] to new sequence\n", cut_copy->vid_ctx->url);
return ret;
}
ret = sequence_ripple_delete_clip(os, cut);
if(ret < 0) {
free_clip(cut_copy);
free(cut_copy);
cut_copy = NULL;
free_clip(&cut_copy);
fprintf(stderr, "cut_remove_insert() error: Failed to delete clip from original sequence\n");
return ret;
}
@@ -264,7 +255,7 @@ int add_files(Sequence *seq, char **files, int num_files) {
fprintf(stderr, "add_files() error: failed to add clip[%s] to sequence\n", files[i]);
return ret;
}
printf("added clip[%s] to original sequence\n", files[i]);
printf("added clip[%s] to original sequence\n", curr->vid_ctx->url);
}
return 0;
}

View File

@@ -37,9 +37,6 @@ int main(int argc, char **argv) {
time_taken = ((double)t)/(CLOCKS_PER_SEC/1000);
printf("Completed in %fms.\n", time_taken);
free_clip(clip1);
free(clip1);
clip1 = NULL;
free_clip(&clip1);
return 0;
}

View File

@@ -42,9 +42,7 @@ int main(int argc, char **argv) {
if(ret < 0) {
printf("Failed to open video output\n");
close_video_output(&oc, false);
free_clip(clip1);
free(clip1);
clip1 = NULL;
free_clip(&clip1);
return -1;
}
@@ -65,10 +63,7 @@ int main(int argc, char **argv) {
avcodec_parameters_free(&audio_par);
close_video_output(&oc, true);
free_clip(clip1);
free(clip1);
clip1 = NULL;
free_clip(&clip1);
free_sequence(&seq);
return ret;

View File

@@ -13,7 +13,7 @@ int cut(AVFormatContext *fmt_ctx, int64_t startFrame, int64_t duration);
int main(int argc, char **argv) {
VideoContext vid_ctx;
open_video(&vid_ctx, argv[1]);
open_video_context(&vid_ctx, argv[1]);
/* dump input information to stderr */
av_dump_format(vid_ctx.fmt_ctx, 0, argv[1], 0);
@@ -45,6 +45,6 @@ int main(int argc, char **argv) {
// free_clip(&clip);
// free video context
free_video_context(&vid_ctx);
close_video_context(&vid_ctx);
return 0;
}

View File

@@ -13,7 +13,6 @@
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/stat.h>
/**
Clip stores a reference to a video file and its data within an editing sequence.
@@ -40,41 +39,6 @@ typedef struct Clip {
*/
int64_t orig_start_pts, orig_end_pts;
/*
pts of seek, absolute to original video pts.
time_base is the same as VideoContext video_stream time_base
RANGE:
>= orig_start_pts and < orig_end_pts
*/
int64_t seek_pts;
/*
By default this is start_frame_pts, but will change when reading packets.
(We track this by video packets seen and seek usage)
*/
int64_t curr_pts;
/*
filename
*/
char *url;
/*
video context open or closed state
*/
bool open;
/*
Timebases fetched when the file is first opened.
After the file is open, get timebase from here to avoid opening the file again
*/
AVRational video_time_base, audio_time_base;
/*
frames per second of original video
*/
double fps;
/********** EDIT SEQUENCE DATA **********/
/*
Position of clip in the edit sequence!! (pts of first/last packet in clip)
@@ -92,13 +56,24 @@ typedef struct Clip {
bool done_reading_video, done_reading_audio;
/*
File stats such as creation time.
"sb.st_mtime" is what we want.
This will show the last modified date of the file (creation time).
counted by clip_read_frame()
*/
struct stat file_stats;
int64_t frame_index;
} Clip;
/**
* Allocate a new clip pointing to the same VideoContext as Clip param
* @param src new clip will point to same VideoContext as this clip
* @return >= 0 on success
*/
Clip *copy_clip_vc(Clip *src);
/**
* Allocate a new Clip without a VideoContext
* @return NULL on fail, not NULL on success
*/
Clip *alloc_clip_internal();
/**
* Allocate clip on heap, initialize default values and open the clip.
* It is important to open the clip when first created so we can set default
@@ -109,15 +84,25 @@ typedef struct Clip {
Clip *alloc_clip(char *url);
/**
Initialize Clip object to full length of original video file
Return >= 0 on success
*/
* Initialize clip without video context
* @param clip Clip to initialize
* @return >= 0 on success
*/
int init_clip_internal(Clip *clip);
/**
* Initialize Clip with new VideoContext
* @param clip Clip to be initialized
* @param url filename
* @return >= 0 on success
*/
int init_clip(Clip *clip, char *url);
/*
Open a clip (VideoContext) to read data from the original file
Return >= 0 if OK, < 0 on fail
*/
/**
* Open a clip (VideoContext) to read data from the original file
* @param clip Clip with videoContext to be opened
* @return >= 0 on success
*/
int open_clip(Clip *clip);
int open_clip_bounds(Clip *clip, int64_t start_idx, int64_t end_idx);
@@ -218,6 +203,15 @@ int64_t clip_ts_audio(Clip *clip, int64_t pkt_ts);
*/
int64_t get_clip_end_frame_idx(Clip* clip);
/**
* Determine if VideoContext is out of clip bounds.
* If this is the case, then VideoContext was used by another clip and needs
* to be reset on the current clip with seek_clip_pts(clip, 0);
* @param clip Clip
* @return true if VideoContext is out of clip bounds
*/
bool is_vc_out_bounds(Clip *clip);
/**
* Detects if we are done reading the current packet stream..
* if true.. then the packet in parameter should be skipped over!
@@ -228,6 +222,15 @@ int64_t get_clip_end_frame_idx(Clip* clip);
*/
bool done_curr_pkt_stream(Clip *clip, AVPacket *pkt);
/**
* Detects if VideoContext seek is out of Clip bounds
* This would occur when another clip uses the same VideoContext, in this case
* we need to seek to the start of clip which will reset the seek pts
* @param clip Clip
* @return true if seek if outside of clip bounds
*/
bool vc_seek_out_of_bounds(Clip *clip);
/**
Read a single AVPacket from clip.
Only returns packets if they are within clip boundaries (start_frame_pts - end_frame_pts)
@@ -329,9 +332,9 @@ AVCodecParameters *get_clip_audio_params(Clip *clip);
int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc);
/**
Frees data within clip structure (does not free Clip allocation itself)
Frees data within clip structure and the Clip allocation itself
*/
void free_clip(Clip *clip);
void free_clip(Clip **clip);
/*************** LINKED LIST FUNCTIONS ***************/
/**

View File

@@ -74,6 +74,23 @@ int init_sequence(Sequence *seq, double fps, int sample_rate);
*/
int init_sequence_cmp(Sequence *seq, double fps, int sample_rate, int (*compareFunc)(const void* first,const void* second));
/**
* Allocate a clip within a sequence and use a reference to the same videoContext
* for clips with the same url (filename)
* @param seq Sequence where clip will be added (used to find videoContext)
* @param url filename of clip to create
* @return >= 0 on success
*/
Clip *seq_alloc_clip(Sequence *seq, char *url);
/**
* Find clip with the search url in a sequence
* @param seq Sequence containing clips to be searched
* @param url search key
* @return NULL on fail, not NULL on success
*/
Clip *find_clip(Sequence *seq, char *url);
/**
* Get duration of sequence in frames (defined by fps)
* @param seq Sequence

View File

@@ -15,6 +15,13 @@
#include "VideoContext.h"
#include <string.h>
/**
* Check if AVRational is valid
* @param r AVRational to check
* @return true if invalid
*/
bool valid_rational(AVRational r);
/**
* Convert video pts into frame index
* @param vid_ctx VideoContext

View File

@@ -14,6 +14,8 @@
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#include <stdbool.h>
#include <sys/stat.h>
enum PacketStreamType { DEC_STREAM_NONE = -1, DEC_STREAM_VIDEO, DEC_STREAM_AUDIO };
@@ -40,6 +42,54 @@ typedef struct VideoContext {
internal use only. used in clip_read_frame()
*/
enum PacketStreamType last_decoder_packet_stream;
/*
video context open or closed state
*/
bool open;
/*
File stats such as creation time.
"sb.st_mtime" is what we want.
This will show the last modified date of the file (creation time).
*/
struct stat file_stats;
/*
filename
*/
char *url;
/*
Timebases fetched when the file is first opened.
After the file is open, get timebase from here to avoid opening the file again
*/
AVRational video_time_base, audio_time_base;
/*
frames per second of original video
*/
double fps;
/*
pts of seek, absolute to original video pts.
time_base is the same as VideoContext video_stream time_base
RANGE:
>= orig_start_pts and < orig_end_pts of Clip.
*/
int64_t seek_pts;
/*
By default this is start_frame_pts, but will change when reading packets.
(We track this by video packets seen and seek usage)
*/
int64_t curr_pts;
/*
number of clips associated with this VideoContext.
We must only free a VideoContext when the last clip using it is freed
*/
int clip_count;
} VideoContext;
# define VIDEO_CONTEXT_STREAM_TYPES_LEN 2
@@ -51,8 +101,9 @@ AVRational get_audio_time_base(VideoContext *vid_ctx);
/*
out @param vid_ctx - allocated context containing format, streams and codecs
in @param filename - name of video file
Return >= 0 if OK, < 0 on fail
*/
int open_video(VideoContext *vid_ctx, char *filename);
int open_video_context(VideoContext *vid_ctx, char *filename);
/* Return >=0 if OK, < 0 on fail */
int open_format_context(VideoContext *vid_ctx, char *filename);
@@ -67,7 +118,21 @@ int open_codec_context(VideoContext *vid_ctx, enum AVMediaType type);
void init_video_context(VideoContext *vid_ctx);
void free_video_context(VideoContext *vid_ctx);
/** free codecs and ffmpeg struct data inside VideoContext **/
void close_video_context(VideoContext *vid_ctx);
/**
* Free all videoContext data (including url)
* @param vid_ctx VideoContext
*/
void free_video_context(VideoContext **vc);
/**
* Check if AVRational is valid
* @param r AVRational to check
* @return true if invalid
*/
bool valid_rational(AVRational r);
/**
* Print most data within an AVCodecContext

View File

@@ -8,6 +8,39 @@
#include "Clip.h"
/**
* Allocate a new clip pointing to the same VideoContext as Clip param.
* Returned Clip has its own Clip fields, but references the same VideoContext as src.
* This is useful for using the same video file with multiple clips
* @param src new clip will point to same VideoContext as this clip
* @return >= 0 on success
*/
Clip *copy_clip_vc(Clip *src) {
if(src == NULL || src->vid_ctx == NULL) {
fprintf(stderr, "copy_clip_vc() error: Invalid params\n");
return NULL;
}
Clip *copy = alloc_clip_internal();
if(copy == NULL) {
fprintf(stderr, "copy_clip_vc() error: Failed to allocate new clip\n");
return NULL;
}
copy->vid_ctx = src->vid_ctx;
++(copy->vid_ctx->clip_count);
return copy;
}
/**
* Allocate a new Clip without a VideoContext
* @return NULL on fail, not NULL on success
*/
Clip *alloc_clip_internal() {
Clip *clip = malloc(sizeof(struct Clip));
if(clip == NULL || init_clip_internal(clip) < 0) {
return NULL;
}
return clip;
}
/**
* Allocate clip on heap, initialize default values and open the clip.
* It is important to open the clip when first created so we can set default
@@ -17,78 +50,88 @@
*/
Clip *alloc_clip(char *url) {
Clip *clip = malloc(sizeof(struct Clip));
if(clip == NULL || init_clip(clip, url) < 0 || open_clip(clip) < 0) {
if(clip == NULL ) {
return NULL;
} else if(init_clip(clip, url) < 0 || open_clip(clip) < 0) {
free_clip(&clip);
}
return clip;
}
/**
Initialize Clip object to full length of original video file
Return >= 0 on success
*/
int init_clip(Clip *clip, char *url) {
clip->url = NULL, clip->vid_ctx = NULL;
clip->url = malloc(strlen(url) + 1);
if(clip->url == NULL) {
fprintf(stderr, "Failed to allocate clip url[%s]\n", url);
* Initialize clip without video context
* @param clip Clip to initialize
* @return >= 0 on success
*/
int init_clip_internal(Clip *clip) {
if(clip == NULL) {
fprintf(stderr, "init_clip_internal() error: Invalid params\n");
return -1;
}
strcpy(clip->url, url);
VideoContext *vid_ctx = (VideoContext *)malloc(sizeof(struct VideoContext));
if(vid_ctx == NULL) {
fprintf(stderr, "Failed to allocate vid_ctx\n");
return -1;
}
init_video_context(vid_ctx);
clip->vid_ctx = vid_ctx;
clip->vid_ctx = NULL;
clip->orig_start_pts = 0;
clip->orig_end_pts = -1;
clip->seek_pts = 0;
clip->open = false;
clip->start_pts = -1;
clip->end_pts = -1;
clip->done_reading_video = false;
clip->done_reading_audio = false;
clip->fps = 0;
clip->curr_pts = 0;
if(stat(url, &(clip->file_stats)) != 0) {
return -1;
}
clip->frame_index = 0;
return 0;
}
/*
Open a clip (VideoContext) to read data from the original file
Return >= 0 if OK, < 0 on fail
*/
/**
* Initialize Clip with new VideoContext
* @param clip Clip to be initialized
* @param url filename
* @return >= 0 on success
*/
int init_clip(Clip *clip, char *url) {
int ret = init_clip_internal(clip);
if(ret < 0) {
return ret;
}
clip->vid_ctx = (VideoContext *)malloc(sizeof(struct VideoContext));
if(clip->vid_ctx == NULL) {
fprintf(stderr, "init_clip() error: Failed to allocate vid_ctx\n");
return -1;
}
init_video_context(clip->vid_ctx);
clip->vid_ctx->clip_count = 1;
clip->vid_ctx->url = malloc(strlen(url) + 1);
if(clip->vid_ctx->url == NULL) {
fprintf(stderr, "init_clip() error: Failed to allocate video_context filename[%s]\n", url);
return -1;
}
strcpy(clip->vid_ctx->url, url);
return 0;
}
/**
* Open a clip (VideoContext) to read data from the original file
* @param clip Clip with videoContext to be opened
* @return >= 0 on success
*/
int open_clip(Clip *clip) {
if(clip == NULL) {
fprintf(stderr, "open_clip() error: NULL param\n");
return -1;
}
if(!clip->open) {
if(!(clip->vid_ctx->open)) {
int ret;
if((ret = open_video(clip->vid_ctx, clip->url)) < 0) {
fprintf(stderr, "open_clip() error: Failed to open VideoContext for clip[%s]\n", clip->url);
if((ret = open_video_context(clip->vid_ctx, clip->vid_ctx->url)) < 0) {
fprintf(stderr, "open_clip() error: Failed to open VideoContext for clip[%s]\n", clip->vid_ctx->url);
// free_video_context(&(clip->vid_ctx));
return ret;
}
clip->open = true;
AVStream *video_stream = get_video_stream(clip->vid_ctx);
if(clip->orig_end_pts == -1) {
clip->orig_end_pts = video_stream->duration;
}
clip->video_time_base = get_video_time_base(clip->vid_ctx);
clip->audio_time_base = get_audio_time_base(clip->vid_ctx);
int64_t frame_duration = video_stream->duration / video_stream->nb_frames;
clip->fps = clip->video_time_base.den / (double)frame_duration;
init_internal_vars(clip);
ret = seek_clip_pts(clip, 0);
if(ret < 0) {
return ret;
}
printf("OPEN CLIP [%s]\n", clip->url);
}
return 0;
}
@@ -102,11 +145,7 @@ int open_clip_bounds(Clip *clip, int64_t start_idx, int64_t end_idx) {
}
void close_clip(Clip *clip) {
if(clip->open) {
free_video_context(clip->vid_ctx);
clip->open = false;
printf("CLOSE CLIP [%s]\n", clip->url);
}
close_video_context(clip->vid_ctx);
}
/**
@@ -166,8 +205,8 @@ int set_clip_start(Clip *clip, int64_t pts) {
int ret = seek_video_pts(clip->vid_ctx, pts);
if(ret >= 0) {
clip->orig_start_pts = pts;
clip->seek_pts = pts;
clip->curr_pts = pts;
clip->vid_ctx->seek_pts = pts;
clip->vid_ctx->curr_pts = pts;
}
return ret;
}
@@ -219,15 +258,15 @@ int seek_clip_pts(Clip *clip, int64_t pts) {
}
int ret;
if((ret = seek_video_pts(clip->vid_ctx, abs_pts)) < 0) {
fprintf(stderr, "seek_clip_pts() error: Failed to seek to pts[%ld] on clip[%s]\n", abs_pts, clip->url);
fprintf(stderr, "seek_clip_pts() error: Failed to seek to pts[%ld] on clip[%s]\n", abs_pts, clip->vid_ctx->url);
return ret;
}
clip->seek_pts = abs_pts;
clip->vid_ctx->seek_pts = abs_pts;
if((ret = cov_video_pts(clip->vid_ctx, abs_pts)) < 0) {
fprintf(stderr, "seek_clip_pts error: Failed to convert pts to frame index\n");
return ret;
}
clip->curr_pts = clip->seek_pts;
clip->vid_ctx->curr_pts = clip->vid_ctx->seek_pts;
return 0;
}
@@ -281,6 +320,17 @@ int64_t get_clip_end_frame_idx(Clip* clip) {
return cov_video_pts(clip->vid_ctx, clip->orig_end_pts - clip->orig_start_pts);
}
/**
* Determine if VideoContext is out of clip bounds.
* If this is the case, then VideoContext was used by another clip and needs
* to be reset on the current clip with seek_clip_pts(clip, 0);
* @param clip Clip
* @return true if VideoContext is out of clip bounds
*/
bool is_vc_out_bounds(Clip *clip) {
return clip->vid_ctx->seek_pts != clip->orig_start_pts;
}
/**
* Detects if we are done reading the current packet stream..
* if true.. then the packet in parameter should be skipped over!
@@ -294,6 +344,18 @@ bool done_curr_pkt_stream(Clip *clip, AVPacket *pkt) {
|| (clip->done_reading_video && pkt->stream_index == clip->vid_ctx->video_stream_idx);
}
/**
* Detects if VideoContext seek is out of Clip bounds
* This would occur when another clip uses the same VideoContext, in this case
* we need to seek to the start of clip which will reset the seek pts
* @param clip Clip
* @return true if seek if outside of clip bounds
*/
bool vc_seek_out_of_bounds(Clip *clip) {
return !(clip->vid_ctx->seek_pts >= clip->orig_start_pts &&
clip->vid_ctx->seek_pts < clip->orig_end_pts);
}
/**
Read a single AVPacket from clip.
Only returns packets if they are within clip boundaries (start_frame_pts - end_frame_pts)
@@ -338,7 +400,7 @@ int clip_read_packet(Clip *clip, AVPacket *pkt) {
}
} else {
*pkt = tmpPkt;
clip->curr_pts = pkt->pts;
clip->vid_ctx->curr_pts = pkt->pts;
}
} else if(tmpPkt.stream_index == vid_ctx->audio_stream_idx) {
if(tmpPkt.pts >= audio_end_pts) {
@@ -416,7 +478,7 @@ int64_t compare_clips_sequential(Clip *f, Clip *s) {
fprintf(stderr, "compare_clips_sequential() error: params cannot be NULL\n");
return -2;
}
double diff = difftime(f->file_stats.st_mtime, s->file_stats.st_mtime);
double diff = difftime(f->vid_ctx->file_stats.st_mtime, s->vid_ctx->file_stats.st_mtime);
if(diff < 0.01 && diff > -0.01) {
// if equal date & time
// compare start pts
@@ -436,8 +498,8 @@ int64_t compare_clips_sequential(Clip *f, Clip *s) {
* @return time_base of clip video stream
*/
AVRational get_clip_video_time_base(Clip *clip) {
if(!clip->open) {
fprintf(stderr, "Failed to get video time_base: clip[%s] is not open\n", clip->url);
if(!clip->vid_ctx->open) {
fprintf(stderr, "Failed to get video time_base: clip[%s] is not open\n", clip->vid_ctx->url);
return (AVRational){-1, -1};
}
return get_video_time_base(clip->vid_ctx);
@@ -449,8 +511,8 @@ AVRational get_clip_video_time_base(Clip *clip) {
* @return time_base of clip audio stream
*/
AVRational get_clip_audio_time_base(Clip *clip) {
if(!clip->open) {
fprintf(stderr, "Failed to get audio time_base: clip[%s] is not open\n", clip->url);
if(!clip->vid_ctx->open) {
fprintf(stderr, "Failed to get audio time_base: clip[%s] is not open\n", clip->vid_ctx->url);
return (AVRational){-1, -1};
}
return get_audio_time_base(clip->vid_ctx);
@@ -462,8 +524,8 @@ AVRational get_clip_audio_time_base(Clip *clip) {
* @return video AVStream on success, NULL on failure
*/
AVStream *get_clip_video_stream(Clip *clip) {
if(!clip->open) {
fprintf(stderr, "Failed to get video stream: clip[%s] is not open\n", clip->url);
if(!clip->vid_ctx->open) {
fprintf(stderr, "Failed to get video stream: clip[%s] is not open\n", clip->vid_ctx->url);
return NULL;
}
return get_video_stream(clip->vid_ctx);
@@ -475,8 +537,8 @@ AVStream *get_clip_video_stream(Clip *clip) {
* @return audio AVStream on success, NULL on failure
*/
AVStream *get_clip_audio_stream(Clip *clip) {
if(!clip->open) {
fprintf(stderr, "Failed to get audio stream: clip[%s] is not open\n", clip->url);
if(!clip->vid_ctx->open) {
fprintf(stderr, "Failed to get audio stream: clip[%s] is not open\n", clip->vid_ctx->url);
return NULL;
}
return get_audio_stream(clip->vid_ctx);
@@ -493,7 +555,7 @@ AVCodecParameters *get_clip_video_params(Clip *clip) {
if(ret < 0) {
avcodec_parameters_free(&par);
par = NULL;
fprintf(stderr, "Failed to get clip[%s] video params\n", clip->url);
fprintf(stderr, "Failed to get clip[%s] video params\n", clip->vid_ctx->url);
} else {
if(par->extradata) {
free(par->extradata);
@@ -515,7 +577,7 @@ AVCodecParameters *get_clip_audio_params(Clip *clip) {
if(ret < 0) {
avcodec_parameters_free(&par);
par = NULL;
fprintf(stderr, "Failed to get clip[%s] audio params\n", clip->url);
fprintf(stderr, "Failed to get clip[%s] audio params\n", clip->vid_ctx->url);
} else{
if(par->extradata) {
free(par->extradata);
@@ -538,7 +600,7 @@ AVCodecParameters *get_clip_audio_params(Clip *clip) {
*/
int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc) {
*sc = NULL;
if(oc == NULL) {
if(oc == NULL || oc->vid_ctx == NULL) {
fprintf(stderr, "cut_clip_internal() error: Invalid params\n");
return -1;
}
@@ -555,20 +617,14 @@ int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc) {
if(ret < 0) {
return ret;
}
*sc = alloc_clip(oc->url);
*sc = copy_clip_vc(oc);
if(*sc == NULL) {
fprintf(stderr, "cut_clip_internal() error: failed to allocate new clip\n");
return -1;
}
ret = open_clip(*sc);
if(ret < 0) {
return ret;
}
ret = set_clip_bounds_pts(*sc, oc->orig_start_pts + pts, sc_orig_end_pts);
if(ret < 0) {
free_clip(*sc);
free(*sc);
*sc = NULL;
free_clip(sc);
fprintf(stderr, "cut_clip_internal() error: Failed to set bounds on new clip\n");
return ret;
}
@@ -576,16 +632,19 @@ int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc) {
}
/**
Frees data within clip structure (does not free Clip allocation itself)
Frees data within clip structure and the Clip allocation itself
*/
void free_clip(Clip *clip) {
if(clip->open) {
close_clip(clip);
void free_clip(Clip **clip) {
Clip *c = *clip;
if(c->vid_ctx) {
if(c->vid_ctx->clip_count <= 1) {
free_video_context(&(c->vid_ctx));
} else {
--(c->vid_ctx->clip_count);
}
}
free(clip->vid_ctx);
free(clip->url);
clip->vid_ctx = NULL;
clip->url = NULL;
free(*clip);
*clip = NULL;
}
/*************** LINKED LIST FUNCTIONS ***************/
@@ -619,9 +678,7 @@ void list_delete_clip(void *toBeDeleted) {
return;
}
Clip *clip = (Clip *) toBeDeleted;
free_clip(clip);
free(clip);
clip = NULL;
free_clip(&clip);
}
/**

View File

@@ -28,22 +28,25 @@ int clip_read_frame(Clip *clip, AVFrame *frame, enum AVMediaType *frame_type) {
ret = avcodec_receive_frame(vid_ctx->video_codec_ctx, frame);
if(ret == 0) {
// success, frame was returned from decoder
if(frame->pts < clip->seek_pts) {
printf("skip video frame[%ld] before seek[%ld]\n", frame->pts, clip->seek_pts);
if(frame->pts < clip->vid_ctx->seek_pts) {
printf("skip video frame[%ld] before seek[%ld]\n", frame->pts, clip->vid_ctx->seek_pts);
handle_ret = 1;
} else {
++(clip->frame_index);
handle_ret = 0;
}
} else if(ret == AVERROR(EAGAIN)) {
// output is not available in this state - user must try to send new input
ret = clip_send_packet(clip); // if no more packets or error
if(ret < 0) {
clip->frame_index = 0;
return ret;
}
handle_ret = 1;
} else {
// legitimate decoding error
fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
clip->frame_index = 0;
return -1;
}
} else if(vid_ctx->last_decoder_packet_stream == DEC_STREAM_AUDIO) {
@@ -51,7 +54,7 @@ int clip_read_frame(Clip *clip, AVFrame *frame, enum AVMediaType *frame_type) {
ret = avcodec_receive_frame(vid_ctx->audio_codec_ctx, frame);
if(ret == 0) {
// success, frame was returned from decoder
int64_t seek_pts = cov_video_to_audio_pts(clip->vid_ctx, clip->seek_pts);
int64_t seek_pts = cov_video_to_audio_pts(clip->vid_ctx, clip->vid_ctx->seek_pts);
if(frame->pts < seek_pts) {
printf("skip audio frame[%ld] before seek[%ld]\n", frame->pts, seek_pts);
handle_ret = 1;
@@ -62,18 +65,21 @@ int clip_read_frame(Clip *clip, AVFrame *frame, enum AVMediaType *frame_type) {
// output is not available in this state - user must try to send new input
ret = clip_send_packet(clip); // if no more packets or error
if(ret < 0) {
clip->frame_index = 0;
return ret;
}
handle_ret = 1;
} else {
// legitimate decoding error
fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
clip->frame_index = 0;
return -1;
}
} else {
ret = clip_send_packet(clip);
// if no more packets or error
if(ret < 0) {
clip->frame_index = 0;
return ret;
}
handle_ret = 1;
@@ -95,7 +101,7 @@ int example_clip_read_frames(Clip *clip) {
return AVERROR(ENOMEM);
}
while(clip_read_frame(clip, frame, &type) >= 0) {
printf("clip: %s | ", clip->url);
printf("clip: %s | ", clip->vid_ctx->url);
if(type == AVMEDIA_TYPE_VIDEO) {
printf("Video frame! pts: %ld, frame: %ld\n", frame->pts,
cov_video_pts(clip->vid_ctx, frame->pts));
@@ -117,51 +123,13 @@ int example_clip_read_frames(Clip *clip) {
* @return true if frame is before seek position, false otherwise
*/
bool frame_before_seek(Clip *clip, AVFrame *frame, enum AVMediaType type) {
int64_t seek_pts = clip->seek_pts;
int64_t seek_pts = clip->vid_ctx->seek_pts;
if(type == AVMEDIA_TYPE_AUDIO) {
seek_pts = cov_video_to_audio_pts(clip->vid_ctx, seek_pts);
}
return frame->pts < seek_pts;
}
/**
* Handle receive frame return, deciding to send another packet
* @param clip Clip to read packets
* @param ret return value from calling avcodec_receive_frame()
* @return >= 0 on success
*/
// int handle_receive_frame(Clip *clip, AVFrame *frame, int ret, enum AVMediaType *type) {
// if(ret == 0) {
// // success, frame was returned from decoder
// if(frame_before_seek(clip, frame, *type)) {
// int64_t seek_pts = clip->seek_pts;
// if(*type == AVMEDIA_TYPE_AUDIO) {
// seek_pts = cov_video_to_audio_pts(clip->vid_ctx, seek_pts);
// printf("skip audio frame[%ld] before seek[%ld]\n", frame->pts, seek_pts);
// } else {
// printf("skip video frame[%ld] before seek[%ld]\n", frame->pts, seek_pts);
// }
//
// // skip frame if its before seek
// // (solving the precise seek issue by decoding I-frames and discarding frames before seek time)
// return 1;
// }
// return 0;
// } else if(ret == AVERROR(EAGAIN)) {
// // output is not available in this state - user must try to send new input
// ret = clip_send_packet(clip);
// // if no more packets or error
// if(ret < 0) {
// return ret;
// }
// return 1;
// } else {
// // legitimate decoding error
// fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
// return -1;
// }
// }
/**
* Send clip packet to decoder
* @param clip Clip to read packets

View File

@@ -371,8 +371,10 @@ void set_audio_out_params(AudioOutParams *op, AVCodecContext *c) {
* @param op OutputParameters to be freed
*/
void free_output_params(OutputParameters *op) {
free(op->filename);
op->filename = NULL;
if(op->filename != NULL) {
free(op->filename);
op->filename = NULL;
}
}
/**

View File

@@ -43,6 +43,48 @@ int init_sequence_cmp(Sequence *seq, double fps, int sample_rate, int (*compareF
return 0;
}
/**
* Allocate a clip within a sequence and use a reference to the same videoContext
* for clips with the same url (filename)
* @param seq Sequence where clip will be added (used to find videoContext)
* @param url filename of clip to create
* @return >= 0 on success
*/
Clip *seq_alloc_clip(Sequence *seq, char *url) {
Clip *clip = malloc(sizeof(struct Clip));
if(clip == NULL) {
return NULL;
}
Clip *seq_clip = find_clip(seq, url);
if(seq_clip == NULL) {
init_clip(clip, url);
} else {
init_clip_internal(clip);
clip->vid_ctx = seq_clip->vid_ctx;
}
return clip;
}
/**
* Find clip with the search url in a sequence
* @param seq Sequence containing clips to be searched
* @param url search key
* @return NULL on fail, not NULL on success
*/
Clip *find_clip(Sequence *seq, char *url) {
if(seq == NULL || url == NULL) {
fprintf(stderr, "find_clip() error: Invalid params\n");
}
Node *currNode = seq->clips.head;
while(currNode != NULL) {
Clip *clip = (Clip *) currNode->data;
if(strcmp(clip->vid_ctx->url, url) == 0) {
return clip;
}
currNode = currNode->next;
}
return NULL;
}
/**
* Get duration of sequence in frames (defined by fps)
* @param seq Sequence
@@ -95,7 +137,7 @@ void sequence_add_clip(Sequence *seq, Clip *clip, int start_frame_index) {
* @return >= 0 on success
*/
void sequence_add_clip_pts(Sequence *seq, Clip *clip, int64_t start_pts) {
printf("sequence add clip [%s], start_pts: %ld\n", clip->url, start_pts);
printf("sequence add clip [%s], start_pts: %ld\n", clip->vid_ctx->url, start_pts);
if(seq == NULL || clip == NULL) {
fprintf(stderr, "sequence_add_clip error: parameters cannot be NULL");
return;
@@ -315,7 +357,7 @@ int64_t seq_frame_within_clip(Sequence *seq, Clip *clip, int frame_index) {
// if sequence frame is within the clip
if(pts_diff >= 0 && seq_pts < clip->end_pts) {
// convert relative pts to clip time_base
AVRational clip_tb = clip->video_time_base;
AVRational clip_tb = clip->vid_ctx->video_time_base;
if(clip_tb.num < 0 || clip_tb.den < 0) {
return -1;
}
@@ -383,7 +425,7 @@ int sequence_read_packet(Sequence *seq, AVPacket *pkt, bool close_clips_flag) {
int ret = clip_read_packet(curr_clip, &tmpPkt);
// End of clip!
if(ret < 0) {
printf("End of clip[%s]\n", curr_clip->url);
printf("End of clip[%s]\n", curr_clip->vid_ctx->url);
if(close_clips_flag) {
close_clip(curr_clip);
}
@@ -398,18 +440,13 @@ int sequence_read_packet(Sequence *seq, AVPacket *pkt, bool close_clips_flag) {
} else {
// move onto next clip
Clip *next_clip = (Clip *) ((Node *)next)->data;
open_clip(next_clip);
ret = open_clip(next_clip);
if(ret < 0) {
return ret;
}
return sequence_read_packet(seq, pkt, close_clips_flag);
}
} else {
// Convert original packet timestamps into sequence timestamps
// if(tmpPkt.stream_index == curr_clip->vid_ctx->video_stream_idx) {
// tmpPkt.pts = video_pkt_to_seq_ts(seq, curr_clip, tmpPkt.pts);
// tmpPkt.dts = video_pkt_to_seq_ts(seq, curr_clip, tmpPkt.dts);
// } else if(tmpPkt.stream_index == curr_clip->vid_ctx->audio_stream_idx) {
// tmpPkt.pts = audio_pkt_to_seq_ts(seq, curr_clip, tmpPkt.pts);
// tmpPkt.dts = audio_pkt_to_seq_ts(seq, curr_clip, tmpPkt.dts);
// }
// valid packet down here
*pkt = tmpPkt;
return tmpPkt.stream_index;
@@ -473,7 +510,7 @@ int64_t video_pkt_to_seq_ts(Sequence *seq, Clip *clip, int64_t orig_pkt_ts) {
int64_t clip_ts = clip_ts_video(clip, orig_pkt_ts);
AVRational clip_tb = get_clip_video_time_base(clip);
if(clip_tb.num < 0 || clip_tb.den < 0) {
fprintf(stderr, "video time_base is invalid for clip[%s]\n", clip->url);
fprintf(stderr, "video time_base is invalid for clip[%s]\n", clip->vid_ctx->url);
return -1;
}
// rescale clip_ts to sequence time_base
@@ -493,7 +530,7 @@ int64_t audio_pkt_to_seq_ts(Sequence *seq, Clip *clip, int64_t orig_pkt_ts) {
int64_t clip_ts = clip_ts_audio(clip, orig_pkt_ts);
AVRational clip_tb = get_clip_audio_time_base(clip);
if(clip_tb.num < 0 || clip_tb.den < 0) {
fprintf(stderr, "audio time_base is invalid for clip[%s]\n", clip->url);
fprintf(stderr, "audio time_base is invalid for clip[%s]\n", clip->vid_ctx->url);
return -1;
}
// rescale clip_ts into sequence time_base
@@ -527,8 +564,8 @@ char *print_sequence(Sequence *seq) {
Node *currNode = seq->clips.head;
for(int i = 0; currNode != NULL; i++) {
Clip *c = (Clip *) currNode->data;
sprintf(buf, "Clip [%d]\nurl: %s\nstart_pts: %ld\nend_pts: %ld\norig_start_pts: %ld\norig_end_pts: %ld\n",
i, c->url, c->start_pts, c->end_pts, c->orig_start_pts, c->orig_end_pts);
sprintf(buf, "Clip [%d]\nurl: %s\nstart_pts: %ld\nend_pts: %ld\norig_start_pts: %ld\norig_end_pts: %ld\nvid_ctx: %p\n",
i, c->vid_ctx->url, c->start_pts, c->end_pts, c->orig_start_pts, c->orig_end_pts, c->vid_ctx);
catVars(&str, 1, buf);
currNode = currNode->next;
}
@@ -548,7 +585,7 @@ void example_sequence_read_packets(Sequence *seq, bool close_clips_flag) {
if(clip == NULL) {
printf("clip == NULL, printing raw pkt pts: %ld\n", pkt.pts);
} else {
printf("clip: %s | ", clip->url);
printf("clip: %s | ", clip->vid_ctx->url);
if(pkt.stream_index == clip->vid_ctx->video_stream_idx) {
printf("Video packet! pts: %ld, dts: %ld, frame: %ld\n", pkt.pts, pkt.dts,

View File

@@ -25,11 +25,19 @@ int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_t
return -1;
}
Clip *curr_clip = (Clip *) currNode->data; // current clip
bool first_frame = (curr_clip->curr_pts == curr_clip->orig_start_pts);
int ret = clip_read_frame(curr_clip, frame, frame_type);
int ret;
// If VideoContext was used by another clip, and is now out of bounds of current clip. Reset seek
if(is_vc_out_bounds(curr_clip)) {
printf("vc_out_bounds.. seeking to start of clip\n");
ret = seek_clip_pts(curr_clip, 0);
if(ret < 0) {
return ret;
}
}
ret = clip_read_frame(curr_clip, frame, frame_type);
// End of clip!
if(ret < 0) {
printf("End of clip[%s]\n", curr_clip->url);
printf("End of clip[%s]\n", curr_clip->vid_ctx->url);
if(close_clips_flag) {
close_clip(curr_clip);
}
@@ -59,8 +67,7 @@ int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_t
// Convert original packet timestamps into sequence timestamps
if(*frame_type == AVMEDIA_TYPE_VIDEO) {
// set first frame to be an I frame
if(first_frame) {
printf("\nI FRAME!\n\n");
if(curr_clip->frame_index == 1) {
frame->key_frame = 1;
frame->pict_type = AV_PICTURE_TYPE_I;
}
@@ -112,7 +119,7 @@ void clear_frame_decoding_garbage(AVFrame *f) {
if(clip == NULL) {
printf("clip == NULL, printing raw frame pts: %ld\n", frame->pts);
} else {
printf("clip: %s | ", clip->url);
printf("clip: %s | ", clip->vid_ctx->url);
if(type == AVMEDIA_TYPE_VIDEO) {
printf("Video frame! pts: %ld, frame: %ld\n", frame->pts,
cov_video_pts(clip->vid_ctx, frame->pts));

View File

@@ -34,15 +34,23 @@ AVRational get_audio_time_base(VideoContext *vid_ctx) {
return get_audio_stream(vid_ctx)->time_base;
}
void init_video_context(VideoContext *vid_ctx) {
vid_ctx->fmt_ctx = NULL;
vid_ctx->video_codec = NULL;
vid_ctx->audio_codec = NULL;
vid_ctx->video_codec_ctx = NULL;
vid_ctx->audio_codec_ctx = NULL;
vid_ctx->video_stream_idx = -1;
vid_ctx->audio_stream_idx = -1;
vid_ctx->last_decoder_packet_stream = DEC_STREAM_NONE;
void init_video_context(VideoContext *vc) {
vc->fmt_ctx = NULL;
vc->video_codec = NULL;
vc->audio_codec = NULL;
vc->video_codec_ctx = NULL;
vc->audio_codec_ctx = NULL;
vc->video_stream_idx = -1;
vc->audio_stream_idx = -1;
vc->last_decoder_packet_stream = DEC_STREAM_NONE;
vc->open = false;
vc->url = NULL;
vc->video_time_base = (AVRational){0,0};
vc->audio_time_base = (AVRational){0,0};
vc->fps = 0;
vc->seek_pts = 0;
vc->curr_pts = 0;
vc->clip_count = 0;
}
/*
@@ -50,7 +58,7 @@ void init_video_context(VideoContext *vid_ctx) {
in @param filename - name of video file
Return >= 0 if OK, < 0 on fail
*/
int open_video(VideoContext *vid_ctx, char *filename) {
int open_video_context(VideoContext *vid_ctx, char *filename) {
int ret;
if((ret = open_format_context(vid_ctx, filename)) < 0) {
return ret;
@@ -68,17 +76,51 @@ int open_video(VideoContext *vid_ctx, char *filename) {
return -1;
}
}
if(stat(filename, &(vid_ctx->file_stats)) != 0) {
fprintf(stderr, "open_video_context() error: Failed to get file stats\n");
return -1;
}
AVStream *video_stream = get_video_stream(vid_ctx);
vid_ctx->video_time_base = get_video_time_base(vid_ctx);
vid_ctx->audio_time_base = get_audio_time_base(vid_ctx);
if(!valid_rational(vid_ctx->video_time_base) || !valid_rational(vid_ctx->audio_time_base)) {
fprintf(stderr, "open_video_context() error: Invalid timebase for video[%d/%d] or audio [%d/%d]\n",
vid_ctx->video_time_base.num, vid_ctx->video_time_base.den, vid_ctx->audio_time_base.num, vid_ctx->audio_time_base.den);
return -1;
}
// if stream duration is invalid.. estimate it.
if(video_stream->duration <= 0 || video_stream->nb_frames <= 0) {
AVRational avg_fps = video_stream->avg_frame_rate;
if(!valid_rational(avg_fps)) {
fprintf(stderr, "open_video_context() error: Invalid duration[%ld], nb_frames[%ld] and avg_frame_rate[%d/%d]\n", video_stream->duration, video_stream->nb_frames, avg_fps.num, avg_fps.den);
return -1;
}
vid_ctx->fps = avg_fps.num / (double)avg_fps.den;
double seconds = vid_ctx->fmt_ctx->duration / (double)AV_TIME_BASE;
video_stream->duration = seconds * vid_ctx->video_time_base.den;
video_stream->nb_frames = seconds * vid_ctx->fps;
} else {
int64_t frame_duration = video_stream->duration / video_stream->nb_frames;
vid_ctx->fps = vid_ctx->video_time_base.den / (double)frame_duration;
}
printf("OPEN VIDEO CONTEXT [%s]\n", filename);
return 0;
}
/* Return >=0 if OK, < 0 on fail */
int open_format_context(VideoContext *vid_ctx, char *filename) {
init_video_context(vid_ctx);
if(vid_ctx->fmt_ctx) {
fprintf(stderr, "open_format_context() error: Invalid params. vid_ctx->fmt_ctx must be NULL (initialized with init_video_context())\n");
return -1;
}
// open input file and allocate format context
if(avformat_open_input(&(vid_ctx->fmt_ctx), filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", filename);
return -1;
}
vid_ctx->open = true;
// retrieve stream information
if(avformat_find_stream_info(vid_ctx->fmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information for file [%s]\n", filename);
@@ -144,11 +186,41 @@ int open_codec_context(VideoContext *vid_ctx, enum AVMediaType type) {
}
}
/** free data inside VideoContext **/
void free_video_context(VideoContext *vid_ctx) {
avcodec_free_context(&(vid_ctx->video_codec_ctx));
avcodec_free_context(&(vid_ctx->audio_codec_ctx));
avformat_close_input(&(vid_ctx->fmt_ctx));
/** free codecs and ffmpeg struct data inside VideoContext **/
void close_video_context(VideoContext *vc) {
if(vc->open) {
avcodec_free_context(&(vc->video_codec_ctx));
avcodec_free_context(&(vc->audio_codec_ctx));
avformat_close_input(&(vc->fmt_ctx));
vc->open = false;
printf("CLOSE VIDEO CONTEXT [%s]\n", vc->url);
}
}
/**
* Free all videoContext data (including url)
* @param vid_ctx VideoContext
*/
void free_video_context(VideoContext **vc) {
if(vc == NULL || *vc == NULL) {
return;
}
close_video_context(*vc);
if((*vc)->url != NULL) {
free((*vc)->url);
(*vc)->url = NULL;
}
free(*vc);
*vc = NULL;
}
/**
* Check if AVRational is valid
* @param r AVRational to check
* @return true if invalid
*/
bool valid_rational(AVRational r) {
return r.den > 0 && r.num > 0;
}
/**