mirror of
https://github.com/DevonCrawford/Video-Editing-Automation.git
synced 2026-01-09 14:07:55 -05:00
Optimized Clip and VideoContext data structures
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
101
include/Clip.h
101
include/Clip.h
@@ -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 ***************/
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
217
src/Clip.c
217
src/Clip.c
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user