mirror of
https://github.com/DevonCrawford/Video-Editing-Automation.git
synced 2026-01-10 06:28:02 -05:00
Merge pull request #8 from DevonCrawford/feature/randomSplice
Feature/random splice
This commit is contained in:
5
Makefile
5
Makefile
@@ -116,6 +116,11 @@ OBJS_BASE= VideoContext Clip ClipDecode OutputContext Timebase \
|
||||
$(DBE)test-sequence-encode: $$(call EXE_OBJS,$$@,$(OBJS_BASE))
|
||||
$(LINK_EXE)
|
||||
|
||||
OBJS_BASE=Sequence LinkedListAPI Clip Util VideoContext Timebase \
|
||||
OutputContext SequenceEncode SequenceDecode ClipDecode
|
||||
$(DBE)random-splice: $$(call EXE_OBJS,$$@,$(OBJS_BASE))
|
||||
$(LINK_EXE)
|
||||
|
||||
# $(1) = name of exe
|
||||
# $(2) = the list of basename object files that the executable needs to run, without .o
|
||||
define EXE_OBJS
|
||||
|
||||
358
examples/random-splice.c
Normal file
358
examples/random-splice.c
Normal file
@@ -0,0 +1,358 @@
|
||||
#include "RandomSplice.h"
|
||||
|
||||
/**
|
||||
* valgrind --leak-check=yes bin/examples/random-splice test-resources/sequence/out7.mov 30 48000 test-resources/sequence/ 300 80 40
|
||||
*/
|
||||
int main(int argc, char **argv) {
|
||||
if(argc < 8) {
|
||||
printf("usage: %s output_file fps sample_rate source_dir duration cut_len_avg cut_len_var\n", argv[0]);
|
||||
printf("\nExplanation\n------------\n");
|
||||
printf("output_file(string): output filename of encoded edit (ex. out.mov)\n");
|
||||
printf("fps(int): frames per second to use in sequence. All frame parameters are based on this (ex. 30 for 30fps)\n");
|
||||
printf("sample_rate(int): audio sample rate (ex. 48000 for 48kHz)\n");
|
||||
printf("source_dir(string): directory of raw video files that will be used in the edit\n");
|
||||
printf("duration(int): duration of output file (in frames - fps defined above)\n");
|
||||
printf("cut_len_avg(int): average length of cuts (in frames)\n");
|
||||
printf("cut_len_var(int): variability of average cuts used by the random number generator for max and min range\n");
|
||||
return -1;
|
||||
}
|
||||
RandSpliceParams par;
|
||||
strcpy(par.output_file, argv[1]);
|
||||
sscanf(argv[2], "%lf", &(par.fps));
|
||||
par.sample_rate = atoi(argv[3]);
|
||||
strcpy(par.source_dir, argv[4]);
|
||||
par.duration = atoi(argv[5]);
|
||||
par.cut_len_avg = atoi(argv[6]);
|
||||
par.cut_len_var = atoi(argv[7]);
|
||||
par.pick_frames_recur = 0;
|
||||
|
||||
int num_files, ret = 0;
|
||||
char **files = get_filenames_in_dir(par.source_dir, &num_files);
|
||||
srand(time(NULL));
|
||||
|
||||
Sequence orig_seq, new_seq;
|
||||
init_sequence_cmp(&orig_seq, par.fps, par.sample_rate, &list_compare_clips_sequential);
|
||||
init_sequence_cmp(&new_seq, par.fps, par.sample_rate, &list_compare_clips_sequential);
|
||||
|
||||
ret = add_files(&orig_seq, files, num_files);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "failed to add files\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *str = print_sequence(&orig_seq);
|
||||
printf("====== ORIG_SEQ =====\n%s\n", str);
|
||||
free(str);
|
||||
str = NULL;
|
||||
|
||||
ret = random_edit(&orig_seq, &new_seq, &par);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "random_edit() error: Failed to finish edit\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
str = print_sequence(&new_seq);
|
||||
printf("====== NEW_SEQ =====\n%s\n", str);
|
||||
free(str);
|
||||
str = NULL;
|
||||
|
||||
if(new_seq.clips.head != NULL) {
|
||||
// output parameters
|
||||
OutputParameters op;
|
||||
VideoOutParams vp;
|
||||
AudioOutParams ap;
|
||||
Clip *clip1 = (Clip *) (new_seq.clips.head->data);
|
||||
set_video_out_params(&vp, clip1->vid_ctx->video_codec_ctx);
|
||||
vp.codec_id = AV_CODEC_ID_NONE;
|
||||
vp.bit_rate = -1;
|
||||
set_audio_out_params(&ap, clip1->vid_ctx->audio_codec_ctx);
|
||||
if(set_output_params(&op, par.output_file, vp, ap) < 0) {
|
||||
fprintf(stderr, "Failed to set output params\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = write_sequence(&new_seq, &op);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "Failed to write new sequence to output file[%s]\n", op.filename);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
// print_str_arr(files, num_files);
|
||||
free_str_arr(&files, num_files);
|
||||
free_sequence(&orig_seq);
|
||||
free_sequence(&new_seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a random edit. This function calls itself recursively to continue making
|
||||
* cuts until the output sequence is our desired length
|
||||
* @param os original sequence (input)
|
||||
* @param ns new sequence (output)
|
||||
* @param par parameters from user to control algorithm
|
||||
* @return 0 on success (fully edited sequence), < 0 on fail
|
||||
*/
|
||||
int random_edit(Sequence *os, Sequence *ns, RandSpliceParams *par) {
|
||||
if(par->cut_len_var > par->cut_len_avg) {
|
||||
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;
|
||||
}
|
||||
int ret = random_cut(os, ns, par);
|
||||
if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return random_edit(os, ns, par);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a random cut from original sequence and place that clip into new sequence
|
||||
* @param os original sequence (input)
|
||||
* @param ns new sequence (output)
|
||||
* @param par parameters from user to control algorithm
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int random_cut(Sequence *os, Sequence *ns, RandSpliceParams *par) {
|
||||
int s, e;
|
||||
int ret = pick_frames(os, par, &s, &e);
|
||||
if(ret < 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut a clip out of original sequence and insert it into new sequence in sorted order
|
||||
* @param os Original sequence
|
||||
* @param ns New Sequence
|
||||
* @param start_index start frame of cut in original sequence
|
||||
* @param end_index end frame of cut in original sequence
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int cut_remove_insert(Sequence *os, Sequence *ns, int start_index, int end_index) {
|
||||
int cut_center_index = start_index + ((end_index - start_index) / 2);
|
||||
int ret = cut_clip(os, start_index);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "cut_remove_insert() error: Failed to cut clip at start index[%d]\n", start_index);
|
||||
return ret;
|
||||
}
|
||||
ret = cut_clip(os, end_index);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "cut_remove_insert() error: Failed to cut clip at end index[%d]\n", end_index);
|
||||
return ret;
|
||||
}
|
||||
Clip *cut = NULL;
|
||||
find_clip_at_index(os, cut_center_index, &cut);
|
||||
if(!cut) {
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
ret = sequence_ripple_delete_clip(os, cut);
|
||||
if(ret < 0) {
|
||||
free_clip(cut_copy);
|
||||
free(cut_copy);
|
||||
cut_copy = NULL;
|
||||
fprintf(stderr, "cut_remove_insert() error: Failed to delete clip from original sequence\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly pick start and end frames given user parameters
|
||||
* @param seq Original sequence to pick frames
|
||||
* @param par user parameters
|
||||
* @param start_index output index of start frame for cut
|
||||
* @param end_index output index of end frame for cut
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int pick_frames(Sequence *seq, RandSpliceParams *par, int *start_index, int *end_index) {
|
||||
if(par->pick_frames_recur > PICK_FRAMES_RECUR_LIMIT) {
|
||||
fprintf(stderr, "pick_frames() error: par->pick_frames_recur[%d] > limit[%d]\nThis should never happen\n", par->pick_frames_recur, PICK_FRAMES_RECUR_LIMIT);
|
||||
return -1;
|
||||
}
|
||||
int64_t seq_dur = get_sequence_duration(seq);
|
||||
if(seq_dur <= 0) {
|
||||
fprintf(stderr, "pick_frames() error: sequence duration[%ld] is invalid\n", seq_dur);
|
||||
return -1;
|
||||
}
|
||||
int s = rand_range(0, seq_dur - par->cut_len_avg - 1);
|
||||
int e_var;
|
||||
if(par->cut_len_var == 0) {
|
||||
e_var = 0;
|
||||
} else {
|
||||
e_var = rand_range((-1)*(par->cut_len_var), par->cut_len_var);
|
||||
}
|
||||
int e = s + par->cut_len_avg + e_var;
|
||||
if(e > seq_dur) {
|
||||
e = seq_dur;
|
||||
}
|
||||
Clip *sc = NULL, *se = NULL;
|
||||
find_clip_at_index(seq, s, &sc);
|
||||
find_clip_at_index(seq, e, &se);
|
||||
if(!sc || !se) {
|
||||
fprintf(stderr, "pick_frames() error: clip does not exist at start[%d] or end[%d] index\n", s, e);
|
||||
return -1;
|
||||
}
|
||||
// If start and end index do not lie on the same clip.. try again
|
||||
if(compare_clips_sequential(sc, se) != 0) {
|
||||
++(par->pick_frames_recur);
|
||||
return pick_frames(seq, par, start_index, end_index);
|
||||
}
|
||||
*start_index = s;
|
||||
*end_index = e;
|
||||
par->pick_frames_recur = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random number between range in the uniform distribution
|
||||
* @param min low bound (inclusive)
|
||||
* @param max high bound (inclusive)
|
||||
* @return random number between min and max on a uniform distribution
|
||||
*/
|
||||
int rand_range(int min, int max) {
|
||||
int diff = max-min;
|
||||
return (int) (((double)(diff+1)/RAND_MAX) * rand() + min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all files from string array into sequence!
|
||||
* @param seq Sequence
|
||||
* @param files string array of filenames (videos to be added)
|
||||
* @param num_files number strings in files array
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int add_files(Sequence *seq, char **files, int num_files) {
|
||||
int ret;
|
||||
for(int i = 0; i < num_files; i++) {
|
||||
Clip *curr = alloc_clip(files[i]);
|
||||
if(curr == NULL) {
|
||||
printf("add_files() warning: failed to allocate clip[%s]\n", files[i]);
|
||||
}
|
||||
ret = sequence_insert_clip_sorted(seq, curr);
|
||||
if(ret < 0) {
|
||||
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]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* print string array
|
||||
* @param str string array
|
||||
* @param rows number of strings
|
||||
*/
|
||||
void print_str_arr(char **str, int rows) {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
printf("%s\n", str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free array of strings
|
||||
* @param str pointer to array of strings
|
||||
* @param rows number of strings in array
|
||||
*/
|
||||
void free_str_arr(char ***str, int rows) {
|
||||
char **s = *str;
|
||||
if(s != NULL) {
|
||||
for(int i = 0; i < rows; i++) {
|
||||
free(s[i]);
|
||||
s[i] = NULL;
|
||||
}
|
||||
free(*str);
|
||||
*str = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all filenames in directory
|
||||
* @param name of directory to search
|
||||
* @param rows number of files
|
||||
* @return array of strings (filenames)
|
||||
*/
|
||||
char **get_filenames_in_dir(char *dirname, int *rows) {
|
||||
char buf[4096];
|
||||
buf[0] = 0;
|
||||
*rows = 0;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
if ((dir = opendir (dirname)) != NULL) {
|
||||
/* print all the files and directories within directory */
|
||||
while ((ent = readdir (dir)) != NULL) {
|
||||
strcat(buf, dirname);
|
||||
strcat(buf, ent->d_name);
|
||||
if(!is_regular_file(buf)) {
|
||||
buf[0] = 0;
|
||||
continue;
|
||||
}
|
||||
buf[0] = 0;
|
||||
++(*rows);
|
||||
}
|
||||
} else {
|
||||
perror ("");
|
||||
return NULL;
|
||||
}
|
||||
rewinddir(dir);
|
||||
|
||||
char **str = malloc(sizeof(char *) * (*rows));
|
||||
|
||||
/* print all the files and directories within directory */
|
||||
int i = 0;
|
||||
while ((ent = readdir (dir)) != NULL) {
|
||||
strcat(buf, dirname);
|
||||
strcat(buf, ent->d_name);
|
||||
if(!is_regular_file(buf)) {
|
||||
buf[0] = 0;
|
||||
continue;
|
||||
}
|
||||
str[i] = malloc(sizeof(char) * (strlen(buf) + 1));
|
||||
strcpy(str[i++], buf);
|
||||
buf[0] = 0;
|
||||
}
|
||||
closedir (dir);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if path is file or directory
|
||||
* @param path filename
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int is_regular_file(const char *path) {
|
||||
struct stat path_stat;
|
||||
stat(path, &path_stat);
|
||||
return S_ISREG(path_stat.st_mode);
|
||||
}
|
||||
@@ -41,13 +41,20 @@ typedef struct Clip {
|
||||
int64_t orig_start_pts, orig_end_pts;
|
||||
|
||||
/*
|
||||
pts of seek, absolute to original video 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
|
||||
>= 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
|
||||
*/
|
||||
@@ -56,12 +63,17 @@ typedef struct Clip {
|
||||
video context open or closed state
|
||||
*/
|
||||
bool open;
|
||||
|
||||
/*
|
||||
Current location of the seek pointer within the original file data.
|
||||
By default this is start_frame_pts, but will change when reading packets.
|
||||
(We track this by video packets seen and seek usage)
|
||||
Timebases fetched when the file is first opened.
|
||||
After the file is open, get timebase from here to avoid opening the file again
|
||||
*/
|
||||
int64_t current_frame_idx;
|
||||
AVRational video_time_base, audio_time_base;
|
||||
|
||||
/*
|
||||
frames per second of original video
|
||||
*/
|
||||
double fps;
|
||||
|
||||
/********** EDIT SEQUENCE DATA **********/
|
||||
/*
|
||||
@@ -88,7 +100,9 @@ typedef struct Clip {
|
||||
} Clip;
|
||||
|
||||
/**
|
||||
* Allocate clip on heap and initialize to default values
|
||||
* 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
|
||||
* values such as clip->orig_end_pts by reading file contents (length of video)
|
||||
* @param url filename
|
||||
* @return NULL on error, not NULL on success
|
||||
*/
|
||||
|
||||
@@ -111,7 +111,16 @@ as a pointer to the first and last element of the list.
|
||||
**/
|
||||
void insertSorted(List* list, void* toBeAdded);
|
||||
|
||||
|
||||
/** Uses the comparison function pointer to place the element in the
|
||||
* appropriate position in the list. Also return the node of inserted element
|
||||
* should be used as the only insert function if a sorted list is required.
|
||||
*@pre List exists and has memory allocated to it. Node to be added is valid.
|
||||
*@post The node to be added will be placed immediately before or after the first occurrence of a related node
|
||||
*@param list a pointer to the dummy head of the list containing function pointers for delete and compare, as well
|
||||
as a pointer to the first and last element of the list.
|
||||
*@param toBeAdded a pointer to data that is to be added to the linked list
|
||||
**/
|
||||
Node *insertSortedGetNode(List *list, void *toBeAdded);
|
||||
|
||||
/** Removes data from from the list, deletes the node and frees the memory,
|
||||
* changes pointer values of surrounding nodes to maintain list structure.
|
||||
|
||||
137
include/RandomSplice.h
Normal file
137
include/RandomSplice.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef _RANDOM_SPLICE_
|
||||
#define _RANDOM_SPLICE_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "OutputContext.h"
|
||||
|
||||
#define PICK_FRAMES_RECUR_LIMIT 50
|
||||
|
||||
typedef struct RandSpliceParams {
|
||||
|
||||
/*************** SEQUENCE PARAMS ***************/
|
||||
/*
|
||||
output filename
|
||||
*/
|
||||
char output_file[1024];
|
||||
/*
|
||||
frames per second to use in sequence
|
||||
*/
|
||||
double fps;
|
||||
/*
|
||||
audio sample rate
|
||||
*/
|
||||
int sample_rate;
|
||||
|
||||
/*************** ALGORITHM PARAMS ***************/
|
||||
/*
|
||||
directory to fetch video files
|
||||
*/
|
||||
char source_dir[1024];
|
||||
/*
|
||||
duration of output video (in frames)
|
||||
*/
|
||||
int64_t duration;
|
||||
/*
|
||||
average length of cuts (in frames)
|
||||
*/
|
||||
int cut_len_avg;
|
||||
/*
|
||||
variability of average cuts
|
||||
*/
|
||||
int cut_len_var;
|
||||
|
||||
/*************** INTERNAL ONLY ***************/
|
||||
int pick_frames_recur;
|
||||
} RandSpliceParams;
|
||||
|
||||
/**
|
||||
* Make a random edit. This function calls itself recursively to continue making
|
||||
* cuts until the output sequence is our desired length
|
||||
* @param os original sequence (input)
|
||||
* @param ns new sequence (output)
|
||||
* @param par parameters from user to control algorithm
|
||||
* @return 0 on success (fully edited sequence), < 0 on fail
|
||||
*/
|
||||
int random_edit(Sequence *os, Sequence *ns, RandSpliceParams *par);
|
||||
|
||||
/**
|
||||
* Make a random cut from original sequence and place that clip into new sequence
|
||||
* @param os original sequence (input)
|
||||
* @param ns new sequence (output)
|
||||
* @param par parameters from user to control algorithm
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int random_cut(Sequence *os, Sequence *ns, RandSpliceParams *par);
|
||||
|
||||
/**
|
||||
* Cut a clip out of original sequence and insert it into new sequence in sorted order
|
||||
* @param os Original sequence
|
||||
* @param ns New Sequence
|
||||
* @param start_index start frame of cut in original sequence
|
||||
* @param end_index end frame of cut in original sequence
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int cut_remove_insert(Sequence *os, Sequence *ns, int start_index, int end_index);
|
||||
|
||||
/**
|
||||
* Randomly pick start and end frames given user parameters
|
||||
* @param seq Original sequence to pick frames
|
||||
* @param par user parameters
|
||||
* @param start_index output index of start frame for cut
|
||||
* @param end_index output index of end frame for cut
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int pick_frames(Sequence *seq, RandSpliceParams *par, int *start_index, int *end_index);
|
||||
|
||||
/**
|
||||
* Generate random number between range in the uniform distribution
|
||||
* @param min low bound (inclusive)
|
||||
* @param max high bound (inclusive)
|
||||
* @return random number between min and max on a uniform distribution
|
||||
*/
|
||||
int rand_range(int min, int max);
|
||||
|
||||
/**
|
||||
* Add all files from string array into sequence!
|
||||
* @param seq Sequence
|
||||
* @param files string array of filenames (videos to be added)
|
||||
* @param num_files number strings in files array
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int add_files(Sequence *seq, char **files, int num_files);
|
||||
|
||||
/**
|
||||
* print string array
|
||||
* @param str string array
|
||||
* @param rows number of strings
|
||||
*/
|
||||
void print_str_arr(char **str, int rows);
|
||||
|
||||
/**
|
||||
* Free array of strings
|
||||
* @param str pointer to array of strings
|
||||
* @param rows number of strings in array
|
||||
*/
|
||||
void free_str_arr(char ***str, int rows);
|
||||
|
||||
/**
|
||||
* Get all filenames in directory
|
||||
* @param name of directory to search
|
||||
* @param rows number of files
|
||||
* @return array of strings (filenames)
|
||||
*/
|
||||
char **get_filenames_in_dir(char *dirname, int *rows);
|
||||
|
||||
/**
|
||||
* Tests if path is file or directory
|
||||
* @param path filename
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int is_regular_file(const char *path);
|
||||
|
||||
#endif
|
||||
@@ -64,6 +64,16 @@ typedef struct Sequence {
|
||||
*/
|
||||
int init_sequence(Sequence *seq, double fps, int sample_rate);
|
||||
|
||||
/**
|
||||
* Initialize a new sequence, list of clips along with a custom compare function (for insertSorted)
|
||||
* @param seq Sequence to initialize
|
||||
* @param fps frames per second
|
||||
* @param sample_rate sample_rate of the audio stream
|
||||
* @param compareFunc custom compare function used in sorting and searching
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int init_sequence_cmp(Sequence *seq, double fps, int sample_rate, int (*compareFunc)(const void* first,const void* second));
|
||||
|
||||
/**
|
||||
* Get duration of sequence in frames (defined by fps)
|
||||
* @param seq Sequence
|
||||
@@ -103,6 +113,25 @@ void sequence_add_clip_pts(Sequence *seq, Clip *clip, int64_t start_pts);
|
||||
*/
|
||||
void sequence_append_clip(Sequence *seq, Clip *clip);
|
||||
|
||||
/**
|
||||
* Insert clip sorted by:
|
||||
* 1. Date & time of file
|
||||
* 2. clip->orig_start_pts
|
||||
* This function will generate the sequence pts for the clip (clip->start_pts and clip->end_pts)
|
||||
* @param seq Sequence
|
||||
* @param clip Clip to insert
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int sequence_insert_clip_sorted(Sequence *seq, Clip *clip);
|
||||
|
||||
/**
|
||||
* Shift clips sequence pts to after the current node (insert clip function)
|
||||
* @param seq Sequence
|
||||
* @param curr_node current clip which should shift all following nodes
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int shift_clips_after(Sequence *seq, Node *curr_node);
|
||||
|
||||
/**
|
||||
* Delete a clip from a sequence and move all following clips forward
|
||||
* @param seq Sequence
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
*/
|
||||
int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_type, bool close_clips_flag);
|
||||
|
||||
/**
|
||||
* Clear fields on AVFrame from decoding
|
||||
* @param f AVFrame to initialize
|
||||
*/
|
||||
void clear_frame_decoding_garbage(AVFrame *f);
|
||||
|
||||
/*************** EXAMPLE FUNCTIONS ***************/
|
||||
/**
|
||||
* Test example showing how to read frames from sequence
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define FFMPEG_SEEK_FLAG AVSEEK_FLAG_BACKWARD
|
||||
|
||||
#include "VideoContext.h"
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Convert video pts into frame index
|
||||
@@ -38,4 +39,11 @@ int64_t get_audio_frame_pts(VideoContext *vid_ctx, int frameIndex);
|
||||
|
||||
int64_t cov_video_to_audio_pts(VideoContext *vid_ctx, int videoFramePts);
|
||||
|
||||
/**
|
||||
* Print an AVRational struct
|
||||
* @param tb timebase to print
|
||||
* @return string allocated on heap. to be freed by caller
|
||||
*/
|
||||
char *print_time_base(AVRational *tb);
|
||||
|
||||
#endif
|
||||
48
src/Clip.c
48
src/Clip.c
@@ -9,13 +9,15 @@
|
||||
#include "Clip.h"
|
||||
|
||||
/**
|
||||
* Allocate clip on heap and initialize to default values
|
||||
* 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
|
||||
* values such as clip->orig_end_pts by reading file contents (length of video)
|
||||
* @param url filename
|
||||
* @return NULL on error, not NULL on success
|
||||
*/
|
||||
Clip *alloc_clip(char *url) {
|
||||
Clip *clip = malloc(sizeof(struct Clip));
|
||||
if(clip == NULL || init_clip(clip, url) < 0) {
|
||||
if(clip == NULL || init_clip(clip, url) < 0 || open_clip(clip) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return clip;
|
||||
@@ -45,11 +47,12 @@ int init_clip(Clip *clip, char *url) {
|
||||
clip->orig_end_pts = -1;
|
||||
clip->seek_pts = 0;
|
||||
clip->open = false;
|
||||
clip->current_frame_idx = 0;
|
||||
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;
|
||||
}
|
||||
@@ -68,15 +71,23 @@ int open_clip(Clip *clip) {
|
||||
if(!clip->open) {
|
||||
int ret;
|
||||
if((ret = open_video(clip->vid_ctx, clip->url)) < 0) {
|
||||
fprintf(stderr, "Failed to open VideoContext for clip[%s]\n", clip->url);
|
||||
fprintf(stderr, "open_clip() error: Failed to open VideoContext for clip[%s]\n", clip->url);
|
||||
return ret;
|
||||
}
|
||||
clip->open = true;
|
||||
AVStream *video_stream = get_video_stream(clip->vid_ctx);
|
||||
if(clip->orig_end_pts == -1) {
|
||||
clip->orig_end_pts = get_video_stream(clip->vid_ctx)->duration;
|
||||
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);
|
||||
seek_clip_pts(clip, 0);
|
||||
ret = seek_clip_pts(clip, 0);
|
||||
if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
printf("OPEN CLIP [%s]\n", clip->url);
|
||||
}
|
||||
return 0;
|
||||
@@ -155,8 +166,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->current_frame_idx = 0;
|
||||
clip->seek_pts = pts;
|
||||
clip->curr_pts = pts;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -203,20 +214,20 @@ int seek_clip_pts(Clip *clip, int64_t pts) {
|
||||
int64_t abs_pts = pts + clip->orig_start_pts;
|
||||
if(pts < 0 || abs_pts > clip->orig_end_pts) {
|
||||
int64_t endBound = get_clip_end_frame_idx(clip);
|
||||
fprintf(stderr, "seek pts[%ld] outside of clip bounds (0 - %ld)\n", pts, endBound);
|
||||
fprintf(stderr, "seek_clip_pts() error: seek pts[%ld] outside of clip bounds (0 - %ld)\n", pts, endBound);
|
||||
return -1;
|
||||
}
|
||||
int ret;
|
||||
if((ret = seek_video_pts(clip->vid_ctx, abs_pts)) < 0) {
|
||||
fprintf(stderr, "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->url);
|
||||
return ret;
|
||||
}
|
||||
clip->seek_pts = abs_pts;
|
||||
if((ret = cov_video_pts(clip->vid_ctx, abs_pts)) < 0) {
|
||||
fprintf(stderr, "Failed to convert pts to frame index\n");
|
||||
fprintf(stderr, "seek_clip_pts error: Failed to convert pts to frame index\n");
|
||||
return ret;
|
||||
}
|
||||
clip->current_frame_idx = ret;
|
||||
clip->curr_pts = clip->seek_pts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -327,11 +338,7 @@ int clip_read_packet(Clip *clip, AVPacket *pkt) {
|
||||
}
|
||||
} else {
|
||||
*pkt = tmpPkt;
|
||||
if((ret = cov_video_pts(vid_ctx, tmpPkt.pts)) < 0) {
|
||||
fprintf(stderr, "Failed to convert pts to frame index\n");
|
||||
return ret;
|
||||
}
|
||||
clip->current_frame_idx = ret;
|
||||
clip->curr_pts = pkt->pts;
|
||||
}
|
||||
} else if(tmpPkt.stream_index == vid_ctx->audio_stream_idx) {
|
||||
if(tmpPkt.pts >= audio_end_pts) {
|
||||
@@ -410,7 +417,6 @@ int64_t compare_clips_sequential(Clip *f, Clip *s) {
|
||||
return -2;
|
||||
}
|
||||
double diff = difftime(f->file_stats.st_mtime, s->file_stats.st_mtime);
|
||||
printf("difftime: %f\n", diff);
|
||||
if(diff < 0.01 && diff > -0.01) {
|
||||
// if equal date & time
|
||||
// compare start pts
|
||||
@@ -539,8 +545,9 @@ int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc) {
|
||||
AVStream *vid_stream = get_clip_video_stream(oc);
|
||||
int64_t frame_duration = vid_stream->duration / vid_stream->nb_frames;
|
||||
if((pts < frame_duration) || (pts >= (oc->orig_end_pts - oc->orig_start_pts))) {
|
||||
printf("cut_clip_internal(): pts out of range/cannot cut less than one frame\n");
|
||||
return 1;
|
||||
printf("cut_clip_internal(): pts out of range/cannot cut less than one frame.. ");
|
||||
printf("pts: %ld, frame_duration: %ld\n", pts, frame_duration);
|
||||
return -1;
|
||||
}
|
||||
// set orig_end_pts of original clip
|
||||
int64_t sc_orig_end_pts = oc->orig_end_pts;
|
||||
@@ -548,7 +555,6 @@ int cut_clip_internal(Clip *oc, int64_t pts, Clip **sc) {
|
||||
if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*sc = alloc_clip(oc->url);
|
||||
if(*sc == NULL) {
|
||||
fprintf(stderr, "cut_clip_internal() error: failed to allocate new clip\n");
|
||||
@@ -596,7 +602,7 @@ char *list_print_clip(void *toBePrinted) {
|
||||
} else {
|
||||
Clip *clip = (Clip *) toBePrinted;
|
||||
char buf[1024];
|
||||
sprintf(buf, "ptr: %ld\nstart_frame_pts: %ld\nend_frame_pts: %ld\n",
|
||||
sprintf(buf, "start_pts: %ld\norig_start_pts: %ld\norig_end_pts: %ld\n",
|
||||
clip->start_pts, clip->orig_start_pts, clip->orig_end_pts);
|
||||
char *str = malloc(sizeof(char) * (strlen(buf) + 1));
|
||||
strcpy(str, buf);
|
||||
|
||||
145
src/ClipDecode.c
145
src/ClipDecode.c
@@ -20,19 +20,66 @@
|
||||
*/
|
||||
int clip_read_frame(Clip *clip, AVFrame *frame, enum AVMediaType *frame_type) {
|
||||
VideoContext *vid_ctx = clip->vid_ctx;
|
||||
int ret;
|
||||
// try to receive frame from decoder (from clip_send_packet())
|
||||
if(vid_ctx->last_decoder_packet_stream == DEC_STREAM_VIDEO) {
|
||||
*frame_type = AVMEDIA_TYPE_VIDEO;
|
||||
ret = avcodec_receive_frame(vid_ctx->video_codec_ctx, frame);
|
||||
return handle_receive_frame(clip, frame, ret, frame_type);
|
||||
} else if(vid_ctx->last_decoder_packet_stream == DEC_STREAM_AUDIO) {
|
||||
*frame_type = AVMEDIA_TYPE_AUDIO;
|
||||
ret = avcodec_receive_frame(vid_ctx->audio_codec_ctx, frame);
|
||||
return handle_receive_frame(clip, frame, ret, frame_type);
|
||||
} else {
|
||||
return clip_send_packet_get_frame(clip, frame, frame_type);
|
||||
}
|
||||
int ret, handle_ret = 0;
|
||||
do {
|
||||
// try to receive frame from decoder (from clip_send_packet())
|
||||
if(vid_ctx->last_decoder_packet_stream == DEC_STREAM_VIDEO) {
|
||||
*frame_type = AVMEDIA_TYPE_VIDEO;
|
||||
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);
|
||||
handle_ret = 1;
|
||||
} else {
|
||||
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) {
|
||||
return ret;
|
||||
}
|
||||
handle_ret = 1;
|
||||
} else {
|
||||
// legitimate decoding error
|
||||
fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
|
||||
return -1;
|
||||
}
|
||||
} else if(vid_ctx->last_decoder_packet_stream == DEC_STREAM_AUDIO) {
|
||||
*frame_type = AVMEDIA_TYPE_AUDIO;
|
||||
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);
|
||||
if(frame->pts < seek_pts) {
|
||||
printf("skip audio frame[%ld] before seek[%ld]\n", frame->pts, seek_pts);
|
||||
handle_ret = 1;
|
||||
} else {
|
||||
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) {
|
||||
return ret;
|
||||
}
|
||||
handle_ret = 1;
|
||||
} else {
|
||||
// legitimate decoding error
|
||||
fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ret = clip_send_packet(clip);
|
||||
// if no more packets or error
|
||||
if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
handle_ret = 1;
|
||||
}
|
||||
} while(handle_ret == 1);
|
||||
return handle_ret;
|
||||
}
|
||||
|
||||
/*************** EXAMPLE FUNCTIONS ***************/
|
||||
@@ -83,47 +130,37 @@ bool frame_before_seek(Clip *clip, AVFrame *frame, enum AVMediaType type) {
|
||||
* @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 clip_read_frame(clip, frame, type);
|
||||
}
|
||||
return 0;
|
||||
} else if(ret == AVERROR(EAGAIN)) {
|
||||
// output is not available in this state - user must try to send new input
|
||||
return clip_send_packet_get_frame(clip, frame, type);
|
||||
} else {
|
||||
// legitimate decoding error
|
||||
fprintf(stderr, "Error decoding frame (%s)\n", av_err2str(ret));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single clip packet and get the decoded frame
|
||||
* @param clip Clip to read packet
|
||||
* @param frame decoded frame output
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int clip_send_packet_get_frame(Clip *clip, AVFrame *frame, enum AVMediaType *type) {
|
||||
int ret = clip_send_packet(clip);
|
||||
// if no more packets or error
|
||||
if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return clip_read_frame(clip, frame, type);
|
||||
}
|
||||
// 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
|
||||
|
||||
@@ -176,6 +176,52 @@ void insertSorted(List *list, void *toBeAdded){
|
||||
return;
|
||||
}
|
||||
|
||||
/** Uses the comparison function pointer to place the element in the
|
||||
* appropriate position in the list. Also return the node of inserted element
|
||||
* should be used as the only insert function if a sorted list is required.
|
||||
*@pre List exists and has memory allocated to it. Node to be added is valid.
|
||||
*@post The node to be added will be placed immediately before or after the first occurrence of a related node
|
||||
*@param list a pointer to the dummy head of the list containing function pointers for delete and compare, as well
|
||||
as a pointer to the first and last element of the list.
|
||||
*@param toBeAdded a pointer to data that is to be added to the linked list
|
||||
**/
|
||||
Node *insertSortedGetNode(List *list, void *toBeAdded){
|
||||
if (list == NULL || toBeAdded == NULL){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (list->head == NULL){
|
||||
insertBack(list, toBeAdded);
|
||||
return list->head;
|
||||
}
|
||||
|
||||
if (list->compare(toBeAdded, list->head->data) <= 0){
|
||||
insertFront(list, toBeAdded);
|
||||
return list->head;
|
||||
}
|
||||
|
||||
if (list->compare(toBeAdded, list->tail->data) > 0){
|
||||
insertBack(list, toBeAdded);
|
||||
return list->tail;
|
||||
}
|
||||
|
||||
Node* currNode = list->head;
|
||||
|
||||
while (currNode != NULL){
|
||||
if (list->compare(toBeAdded, currNode->data) <= 0){
|
||||
Node* newNode = initializeNode(toBeAdded);
|
||||
newNode->next = currNode;
|
||||
newNode->previous = currNode->previous;
|
||||
currNode->previous->next = newNode;
|
||||
currNode->previous = newNode;
|
||||
(list->length)++;
|
||||
return newNode;
|
||||
}
|
||||
currNode = currNode->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* deleteDataFromList(List* list, void* toBeDeleted){
|
||||
if (list == NULL || toBeDeleted == NULL){
|
||||
return NULL;
|
||||
|
||||
@@ -18,11 +18,23 @@
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int init_sequence(Sequence *seq, double fps, int sample_rate) {
|
||||
if(seq == NULL) {
|
||||
fprintf(stderr, "sequence is NULL, cannot initialize");
|
||||
return init_sequence_cmp(seq, fps, sample_rate, &list_compare_clips);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new sequence, list of clips along with a custom compare function (for insertSorted)
|
||||
* @param seq Sequence to initialize
|
||||
* @param fps frames per second
|
||||
* @param sample_rate sample_rate of the audio stream
|
||||
* @param compareFunc custom compare function used in sorting and searching
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int init_sequence_cmp(Sequence *seq, double fps, int sample_rate, int (*compareFunc)(const void* first,const void* second)) {
|
||||
if(seq == NULL || compareFunc == NULL) {
|
||||
fprintf(stderr, "init_sequence_cmp() error: params cannot be NULL\n");
|
||||
return -1;
|
||||
}
|
||||
seq->clips = initializeList(&list_print_clip, &list_delete_clip, &list_compare_clips);
|
||||
seq->clips = initializeList(&list_print_clip, &list_delete_clip, compareFunc);
|
||||
seq->clips_iter = createIterator(seq->clips);
|
||||
seq->video_time_base = (AVRational){1, fps * SEQ_VIDEO_FRAME_DURATION};
|
||||
seq->audio_time_base = (AVRational){1, sample_rate};
|
||||
@@ -115,6 +127,57 @@ void sequence_append_clip(Sequence *seq, Clip *clip) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert clip sorted by:
|
||||
* 1. Date & time of file
|
||||
* 2. clip->orig_start_pts
|
||||
* This function will generate the sequence pts for the clip (clip->start_pts and clip->end_pts)
|
||||
* @param seq Sequence
|
||||
* @param clip Clip to insert
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int sequence_insert_clip_sorted(Sequence *seq, Clip *clip) {
|
||||
if(seq == NULL || clip == NULL) {
|
||||
fprintf(stderr, "sequence_insert_clip_sorted() error: parameters cannot be NULL\n");
|
||||
return -1;
|
||||
}
|
||||
Node *node = insertSortedGetNode(&(seq->clips), clip);
|
||||
if(node == NULL) {
|
||||
fprintf(stderr, "sequence_insert_clip_sorted() error: could not insert clip in sorted order\n");
|
||||
return -1;
|
||||
}
|
||||
seq->clips_iter.current = seq->clips.head;
|
||||
if(node->previous == NULL) {
|
||||
move_clip_pts(seq, clip, 0);
|
||||
} else {
|
||||
move_clip_pts(seq, clip, ((Clip *) (node->previous->data))->end_pts);
|
||||
}
|
||||
return shift_clips_after(seq, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift clips sequence pts to after the current node (insert clip function)
|
||||
* @param seq Sequence
|
||||
* @param curr_node current clip which should shift all following nodes
|
||||
* @return >= 0 on success
|
||||
*/
|
||||
int shift_clips_after(Sequence *seq, Node *curr_node) {
|
||||
if(seq == NULL || curr_node == NULL) {
|
||||
fprintf(stderr, "shift_clips_after() error: parameters cannot be NULL\n");
|
||||
return -1;
|
||||
}
|
||||
Clip *curr = (Clip *) (curr_node->data);
|
||||
int64_t shift = curr->end_pts - curr->start_pts;
|
||||
Node *node = curr_node->next;
|
||||
while(node != NULL) {
|
||||
Clip *next = (Clip *) (node->data);
|
||||
next->start_pts += shift;
|
||||
next->end_pts += shift;
|
||||
node = node->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a clip from a sequence and move all following clips forward
|
||||
* @param seq Sequence
|
||||
@@ -222,7 +285,6 @@ int64_t find_clip_at_index(Sequence *seq, int frame_index, Clip **found_clip) {
|
||||
Node *currNode = seq->clips.head;
|
||||
while(currNode != NULL) {
|
||||
Clip *clip = (Clip *) currNode->data;
|
||||
open_clip(clip);
|
||||
int64_t clip_pts;
|
||||
// If clip is found at this frame index (in sequence)
|
||||
if((clip_pts = seq_frame_within_clip(seq, clip, frame_index)) >= 0) {
|
||||
@@ -251,9 +313,9 @@ int64_t seq_frame_within_clip(Sequence *seq, Clip *clip, int frame_index) {
|
||||
int64_t seq_pts = seq_frame_index_to_pts(seq, frame_index);
|
||||
int64_t pts_diff = seq_pts - clip->start_pts; // find pts relative to the clip!
|
||||
// if sequence frame is within the clip
|
||||
if(pts_diff >= 0 && seq_pts <= clip->end_pts) {
|
||||
if(pts_diff >= 0 && seq_pts < clip->end_pts) {
|
||||
// convert relative pts to clip time_base
|
||||
AVRational clip_tb = get_clip_video_time_base(clip);
|
||||
AVRational clip_tb = clip->video_time_base;
|
||||
if(clip_tb.num < 0 || clip_tb.den < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -273,7 +335,6 @@ int sequence_seek(Sequence *seq, int frame_index) {
|
||||
Node *currNode = seq->clips.head;
|
||||
while(currNode != NULL) {
|
||||
Clip *clip = (Clip *) currNode->data;
|
||||
open_clip(clip);
|
||||
int64_t clip_pts;
|
||||
// If clip is found at this frame index (in sequence)
|
||||
if((clip_pts = seq_frame_within_clip(seq, clip, frame_index)) >= 0) {
|
||||
|
||||
@@ -25,6 +25,7 @@ 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);
|
||||
// End of clip!
|
||||
if(ret < 0) {
|
||||
@@ -38,7 +39,14 @@ int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_t
|
||||
if(next == NULL) {
|
||||
// We're done reading all clips! (reset to start)
|
||||
printf("We're done reading all clips! (reset to start)\n");
|
||||
sequence_seek(seq, 0);
|
||||
if(seq->clips.head != NULL) {
|
||||
open_clip((Clip *)(seq->clips.head->data));
|
||||
}
|
||||
ret = sequence_seek(seq, 0);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "sequence_read_frame() error: Failed to seek to the start of sequence\n");
|
||||
return ret;
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
// move onto next clip
|
||||
@@ -47,8 +55,15 @@ int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_t
|
||||
return sequence_read_frame(seq, frame, frame_type, close_clips_flag);
|
||||
}
|
||||
} else {
|
||||
clear_frame_decoding_garbage(frame);
|
||||
// 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");
|
||||
frame->key_frame = 1;
|
||||
frame->pict_type = AV_PICTURE_TYPE_I;
|
||||
}
|
||||
frame->pts = video_pkt_to_seq_ts(seq, curr_clip, frame->pts);
|
||||
// frame->pkt_dts = video_pkt_to_seq_ts(seq, curr_clip, frame->pkt_dts);
|
||||
} else if(*frame_type == AVMEDIA_TYPE_AUDIO) {
|
||||
@@ -59,6 +74,27 @@ int sequence_read_frame(Sequence *seq, AVFrame *frame, enum AVMediaType *frame_t
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear fields on AVFrame from decoding
|
||||
* @param f AVFrame to initialize
|
||||
*/
|
||||
void clear_frame_decoding_garbage(AVFrame *f) {
|
||||
f->key_frame = 0;
|
||||
f->pict_type = AV_PICTURE_TYPE_NONE;
|
||||
f->sample_aspect_ratio = (AVRational){0,1};
|
||||
f->coded_picture_number = 0;
|
||||
f->display_picture_number = 0;
|
||||
f->opaque = NULL;
|
||||
f->repeat_pict = 0;
|
||||
f->interlaced_frame = 0;
|
||||
f->side_data = NULL;
|
||||
f->nb_side_data = 0;
|
||||
f->flags = 0;
|
||||
f->decode_error_flags = 0;
|
||||
f->private_ref = NULL;
|
||||
f->opaque_ref = NULL;
|
||||
}
|
||||
|
||||
/*************** EXAMPLE FUNCTIONS ***************/
|
||||
/**
|
||||
* Test example showing how to read frames from sequence
|
||||
|
||||
@@ -145,10 +145,6 @@
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// clear_frame_encoding_garbage(oc->buffer_frame);
|
||||
// oc->buffer_frame->key_frame = 1;
|
||||
oc->buffer_frame->pict_type = AV_PICTURE_TYPE_NONE;
|
||||
// supply a raw video or audio frame to the encoder
|
||||
if(type == AVMEDIA_TYPE_VIDEO) {
|
||||
ret = avcodec_send_frame(oc->video.codec_ctx, oc->buffer_frame);
|
||||
|
||||
@@ -78,6 +78,23 @@ int64_t cov_video_to_audio_pts(VideoContext *vid_ctx, int videoFramePts) {
|
||||
get_audio_stream(vid_ctx)->time_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an AVRational struct
|
||||
* @param tb timebase to print
|
||||
* @return string allocated on heap. to be freed by caller
|
||||
*/
|
||||
char *print_time_base(AVRational *tb) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%d/%d\n", tb->num, tb->den);
|
||||
char *str = malloc(sizeof(char) * (strlen(buf) + 1));
|
||||
if(str == NULL) {
|
||||
fprintf(stderr, "print_time_base(): Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
strcpy(str, buf);
|
||||
return str;
|
||||
}
|
||||
|
||||
// int seek_audio(VideoContext *vid_ctx, int frame) {
|
||||
// AVStream *audio_stream = get_audio_stream(vid_ctx);
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user