video_file 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +84 -0
- data/.travis.yml +7 -0
- data/CMakeLists.txt +28 -0
- data/CMakeLists.txt.user +375 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +339 -0
- data/README.md +48 -0
- data/Rakefile +16 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/video_file_ext/ext.c +243 -0
- data/ext/video_file_ext/extconf.rb +8 -0
- data/ext/video_file_ext/vf-core.h +21 -0
- data/ext/video_file_ext/vf-file.c +124 -0
- data/ext/video_file_ext/vf-file.h +50 -0
- data/ext/video_file_ext/vf-thumbnailer.c +432 -0
- data/ext/video_file_ext/vf-thumbnailer.h +54 -0
- data/ext/video_file_ext/vf-util.c +1 -0
- data/ext/video_file_ext/vf-util.h +6 -0
- data/lib/video_file/version.rb +3 -0
- data/lib/video_file.rb +16 -0
- data/video_file.gemspec +33 -0
- metadata +151 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#define VF_WARN(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)
|
4
|
+
|
5
|
+
#ifdef VF_ENABLE_DEBUG
|
6
|
+
#define VF_DEBUG(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)
|
7
|
+
#else
|
8
|
+
#define VF_DEBUG(format, ...)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#ifndef ABS
|
12
|
+
#define ABS(a) (((a) < 0) ? -(a) : (a))
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#ifndef MAX
|
16
|
+
#define MAX(a, b) ((a < b) ? (b) : (a))
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#ifndef MIN
|
20
|
+
#define MIN(a, b) ((a < b) ? (a) : (b))
|
21
|
+
#endif
|
@@ -0,0 +1,124 @@
|
|
1
|
+
//#include <glib.h>
|
2
|
+
|
3
|
+
#include "vf-file.h"
|
4
|
+
#include "vf-core.h"
|
5
|
+
#include <pthread.h>
|
6
|
+
|
7
|
+
void
|
8
|
+
vf_file_destroy(VfFile *video_file)
|
9
|
+
{
|
10
|
+
pthread_mutex_lock(&video_file->codec_mutex);
|
11
|
+
if(video_file->video_codec_context != NULL)
|
12
|
+
avcodec_close(video_file->video_codec_context);
|
13
|
+
|
14
|
+
if(video_file->audio_codec_context != NULL)
|
15
|
+
avcodec_close(video_file->audio_codec_context);
|
16
|
+
pthread_mutex_unlock(&video_file->codec_mutex);
|
17
|
+
|
18
|
+
if(video_file->format_context != NULL) {
|
19
|
+
avformat_close_input(&video_file->format_context);
|
20
|
+
}
|
21
|
+
|
22
|
+
pthread_mutex_destroy(&video_file->codec_mutex);
|
23
|
+
free(video_file->filename);
|
24
|
+
}
|
25
|
+
|
26
|
+
static void
|
27
|
+
vf_file_load_metadata(VfFile *file)
|
28
|
+
{
|
29
|
+
int dar_num;
|
30
|
+
int dar_den;
|
31
|
+
|
32
|
+
file->metadata.width = file->video_codec_context->width;
|
33
|
+
file->metadata.height = file->video_codec_context->height;
|
34
|
+
|
35
|
+
file->metadata.duration = ((double)file->format_context->duration) / (double)AV_TIME_BASE;
|
36
|
+
file->metadata.fps = av_q2d(file->video_stream->r_frame_rate);
|
37
|
+
|
38
|
+
file->metadata.par = av_q2d(file->video_stream->sample_aspect_ratio);
|
39
|
+
|
40
|
+
av_reduce(&dar_num, &dar_den,
|
41
|
+
file->video_codec_context->width * file->video_stream->sample_aspect_ratio.num,
|
42
|
+
file->video_codec_context->height * file->video_stream->sample_aspect_ratio.den,
|
43
|
+
1024*1024);
|
44
|
+
|
45
|
+
file->metadata.dar = (double) dar_num / (double) dar_den;
|
46
|
+
}
|
47
|
+
|
48
|
+
bool
|
49
|
+
vf_file_init(VfFile *video_file, const char *filename)
|
50
|
+
{
|
51
|
+
AVFormatContext *format_context = NULL;
|
52
|
+
AVCodecContext *video_codec_context = NULL;
|
53
|
+
AVCodecContext *audio_codec_context = NULL;
|
54
|
+
AVStream *video_stream = NULL;
|
55
|
+
AVStream *audio_stream = NULL;
|
56
|
+
unsigned i;
|
57
|
+
int ret;
|
58
|
+
|
59
|
+
video_file->filename = strdup(filename);
|
60
|
+
|
61
|
+
pthread_mutex_init(&video_file->codec_mutex, NULL);
|
62
|
+
|
63
|
+
if((ret = avformat_open_input(&format_context, filename, NULL, NULL)) != 0)
|
64
|
+
{
|
65
|
+
VF_SET_AV_ERROR(video_file, ret);
|
66
|
+
VF_DEBUG("av_open_input_file (%s, %d)\n", filename, ret);
|
67
|
+
goto fail;
|
68
|
+
}
|
69
|
+
|
70
|
+
pthread_mutex_lock(&video_file->codec_mutex);
|
71
|
+
if((ret = avformat_find_stream_info(format_context, NULL)) < 0)
|
72
|
+
{
|
73
|
+
VF_SET_AV_ERROR(video_file, ret);
|
74
|
+
VF_WARN( "stream_info");
|
75
|
+
goto fail;
|
76
|
+
}
|
77
|
+
pthread_mutex_unlock(&video_file->codec_mutex);
|
78
|
+
|
79
|
+
for(i = 0; i < format_context->nb_streams; i++)
|
80
|
+
{
|
81
|
+
if(video_stream == NULL && format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
|
82
|
+
{
|
83
|
+
video_stream = format_context->streams[i];
|
84
|
+
}
|
85
|
+
|
86
|
+
if(audio_stream == NULL && format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
87
|
+
{
|
88
|
+
audio_stream = format_context->streams[i];
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
if(video_stream == NULL)
|
93
|
+
{
|
94
|
+
VF_WARN( "video stream");
|
95
|
+
goto fail;
|
96
|
+
}
|
97
|
+
|
98
|
+
video_codec_context = avcodec_alloc_context3(NULL);
|
99
|
+
audio_codec_context = avcodec_alloc_context3(NULL);
|
100
|
+
|
101
|
+
avcodec_parameters_to_context(video_codec_context, video_stream->codecpar);
|
102
|
+
|
103
|
+
if(audio_stream != NULL) {
|
104
|
+
avcodec_parameters_to_context(audio_codec_context, audio_stream->codecpar);
|
105
|
+
}
|
106
|
+
|
107
|
+
// if(video_codec_context->time_base.num > 1000 && video_codec_context->time_base.den == 1)
|
108
|
+
// video_codec_context->time_base.den=1000;
|
109
|
+
|
110
|
+
video_file->format_context = format_context;
|
111
|
+
video_file->video_codec_context = video_codec_context;
|
112
|
+
video_file->audio_codec_context = video_codec_context;
|
113
|
+
video_file->audio_stream = audio_stream;
|
114
|
+
video_file->video_stream = video_stream;
|
115
|
+
|
116
|
+
vf_file_load_metadata(video_file);
|
117
|
+
|
118
|
+
return true;
|
119
|
+
|
120
|
+
fail:
|
121
|
+
VF_DEBUG( "VideoFile creation failed");
|
122
|
+
return false;
|
123
|
+
|
124
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
/* vim: set tabstop=2 expandtab: */
|
2
|
+
#pragma once
|
3
|
+
|
4
|
+
#include <libavcodec/avcodec.h>
|
5
|
+
#include <libavformat/avformat.h>
|
6
|
+
#include <stdio.h>
|
7
|
+
#include <stdbool.h>
|
8
|
+
|
9
|
+
|
10
|
+
#define VF_SET_AV_ERROR(obj, error) \
|
11
|
+
do { \
|
12
|
+
obj->last_error = error; \
|
13
|
+
av_strerror(error, obj->last_error_str, sizeof(obj->last_error_str)); \
|
14
|
+
} while(0);
|
15
|
+
|
16
|
+
typedef struct _VfFile VfFile;
|
17
|
+
|
18
|
+
struct _VfFile
|
19
|
+
{
|
20
|
+
AVFormatContext* format_context;
|
21
|
+
AVCodecContext* video_codec_context;
|
22
|
+
AVCodecContext* audio_codec_context;
|
23
|
+
char* filename;
|
24
|
+
|
25
|
+
AVStream* video_stream;
|
26
|
+
AVStream* audio_stream;
|
27
|
+
|
28
|
+
pthread_mutex_t codec_mutex;
|
29
|
+
|
30
|
+
struct {
|
31
|
+
int height;
|
32
|
+
int width;
|
33
|
+
int fps_num;
|
34
|
+
int fps_den;
|
35
|
+
|
36
|
+
double duration;
|
37
|
+
double fps;
|
38
|
+
double dar;
|
39
|
+
double par;
|
40
|
+
} metadata;
|
41
|
+
|
42
|
+
int last_error;
|
43
|
+
char last_error_str[1024];
|
44
|
+
};
|
45
|
+
|
46
|
+
bool
|
47
|
+
vf_file_init(VfFile *video_file, const char* filename);
|
48
|
+
|
49
|
+
void
|
50
|
+
vf_file_destroy(VfFile* video_file);
|
@@ -0,0 +1,432 @@
|
|
1
|
+
#include "vf-thumbnailer.h"
|
2
|
+
#include "vf-core.h"
|
3
|
+
#include <libavutil/imgutils.h>
|
4
|
+
|
5
|
+
#define FRAME_ALIGN 16
|
6
|
+
|
7
|
+
static void
|
8
|
+
planes_to_jpeg(VfThumbnailer* thumbnailer,
|
9
|
+
int quality,
|
10
|
+
unsigned char** rdata,
|
11
|
+
size_t* size);
|
12
|
+
|
13
|
+
static int64_t
|
14
|
+
frame_monotony(VfThumbnailer* thumbnailer, AVFrame* frame);
|
15
|
+
|
16
|
+
static bool
|
17
|
+
vf_file_open_codec(VfFile *video_file, VfThumbnailer *thumbnailer)
|
18
|
+
{
|
19
|
+
AVCodec *codec;
|
20
|
+
int ret;
|
21
|
+
|
22
|
+
VF_DEBUG( "CODEC MUTEX");
|
23
|
+
pthread_mutex_lock(&video_file->codec_mutex);
|
24
|
+
VF_DEBUG( "CODEC MUTEX LOCKED");
|
25
|
+
if(video_file->video_codec_context->codec == NULL)
|
26
|
+
{
|
27
|
+
VF_DEBUG( "FIND DECODER");
|
28
|
+
codec = avcodec_find_decoder(video_file->video_codec_context->codec_id);
|
29
|
+
VF_DEBUG( "/FIND DECODER");
|
30
|
+
if(codec == NULL)
|
31
|
+
{
|
32
|
+
VF_DEBUG( "Could not find codec");
|
33
|
+
return false;
|
34
|
+
}
|
35
|
+
|
36
|
+
VF_DEBUG( "OPEN CODEC");
|
37
|
+
if((ret = avcodec_open2(video_file->video_codec_context, codec, NULL)) < 0)
|
38
|
+
{
|
39
|
+
VF_DEBUG( "Could not open codec (%d)\n", ret);
|
40
|
+
VF_SET_AV_ERROR(thumbnailer, ret);
|
41
|
+
return false;
|
42
|
+
}
|
43
|
+
VF_DEBUG( "/OPEN CODEC");
|
44
|
+
}
|
45
|
+
pthread_mutex_unlock(&video_file->codec_mutex);
|
46
|
+
return true;
|
47
|
+
}
|
48
|
+
|
49
|
+
void
|
50
|
+
vf_thumbnailer_destroy(VfThumbnailer* thumbnailer)
|
51
|
+
{
|
52
|
+
unsigned n;
|
53
|
+
// jpeg_destroy_compress(&thumbnailer->compressor);
|
54
|
+
|
55
|
+
if(thumbnailer->scaled_frames != NULL) {
|
56
|
+
for (n = 0; n < thumbnailer->n; n++) {
|
57
|
+
// free(thumbnailer->scaled_frames[n]->data);
|
58
|
+
av_frame_free(&thumbnailer->scaled_frames[n]);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
av_frame_free(&thumbnailer->frame);
|
62
|
+
av_frame_free(&thumbnailer->joined_frame);
|
63
|
+
|
64
|
+
av_free(thumbnailer->scaled_frames_buffer);
|
65
|
+
av_free(thumbnailer->joined_frames_buffer);
|
66
|
+
|
67
|
+
tjDestroy(thumbnailer->tjInstance);
|
68
|
+
}
|
69
|
+
|
70
|
+
static void set_tj_error(VfThumbnailer *thumbnailer) {
|
71
|
+
thumbnailer->last_error = tjGetErrorCode(thumbnailer->tjInstance);
|
72
|
+
strncpy(thumbnailer->last_error_str, tjGetErrorStr2(thumbnailer->tjInstance), sizeof(thumbnailer->last_error_str));
|
73
|
+
VF_DEBUG("JPEG error: %s\n", thumbnailer->last_error_str);
|
74
|
+
}
|
75
|
+
|
76
|
+
bool
|
77
|
+
vf_thumbnailer_init(VfThumbnailer* thumbnailer,
|
78
|
+
VfFile* file,
|
79
|
+
int width,
|
80
|
+
unsigned n)
|
81
|
+
{
|
82
|
+
unsigned int i;
|
83
|
+
int height;
|
84
|
+
size_t buffer_size;
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
VF_DEBUG("OPEN CODEC");
|
89
|
+
|
90
|
+
if(!vf_file_open_codec(file, thumbnailer)) {
|
91
|
+
return false;
|
92
|
+
}
|
93
|
+
|
94
|
+
VF_DEBUG("/OPEN CODEC");
|
95
|
+
|
96
|
+
if(width <= 0) {
|
97
|
+
width = file->video_codec_context->width;
|
98
|
+
}
|
99
|
+
|
100
|
+
thumbnailer->video_file = file;
|
101
|
+
thumbnailer->width = width;
|
102
|
+
thumbnailer->position = -1;
|
103
|
+
thumbnailer->n = n;
|
104
|
+
|
105
|
+
|
106
|
+
if (file->video_stream->sample_aspect_ratio.num &&
|
107
|
+
av_cmp_q(file->video_stream->sample_aspect_ratio,
|
108
|
+
file->video_stream->codecpar->sample_aspect_ratio)) {
|
109
|
+
/* AVRational display_aspect_ratio;
|
110
|
+
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
|
111
|
+
codec_context->width * video_stream->sample_aspect_ratio.num,
|
112
|
+
codec_context->height * video_stream->sample_aspect_ratio.den,
|
113
|
+
1024*1024);*/
|
114
|
+
|
115
|
+
height = width *
|
116
|
+
(file->video_stream->sample_aspect_ratio.den *
|
117
|
+
file->video_codec_context->height) /
|
118
|
+
(file->video_stream->sample_aspect_ratio.num *
|
119
|
+
file->video_codec_context->width);
|
120
|
+
} else {
|
121
|
+
/* Fallback to 16:9 */
|
122
|
+
height = (int)(width / 1.7777);
|
123
|
+
}
|
124
|
+
|
125
|
+
// height = size *
|
126
|
+
// format_context->streams[thumbnailer->video_stream_index]->sample_aspect_ratio.den
|
127
|
+
// /
|
128
|
+
// format_context->streams[thumbnailer->video_stream_index]->sample_aspect_ratio.num;
|
129
|
+
|
130
|
+
thumbnailer->sws_ctx = sws_getContext(file->video_codec_context->width,
|
131
|
+
file->video_codec_context->height,
|
132
|
+
file->video_codec_context->pix_fmt,
|
133
|
+
width,
|
134
|
+
height,
|
135
|
+
AV_PIX_FMT_YUV444P,
|
136
|
+
SWS_FAST_BILINEAR,
|
137
|
+
NULL,
|
138
|
+
NULL,
|
139
|
+
NULL);
|
140
|
+
|
141
|
+
if (thumbnailer->sws_ctx == NULL) {
|
142
|
+
VF_DEBUG("Could not create sws context");
|
143
|
+
goto fail;
|
144
|
+
}
|
145
|
+
|
146
|
+
thumbnailer->width = width;
|
147
|
+
thumbnailer->height = height;
|
148
|
+
thumbnailer->frame = av_frame_alloc();
|
149
|
+
|
150
|
+
thumbnailer->scaled_frames = av_mallocz_array(n, sizeof(AVFrame*));
|
151
|
+
size_t frame_size =
|
152
|
+
(size_t) av_image_get_buffer_size(AV_PIX_FMT_YUV444P, width, height, FRAME_ALIGN);
|
153
|
+
buffer_size = n * frame_size;
|
154
|
+
thumbnailer->scaled_frames_buffer = av_mallocz((size_t)buffer_size);
|
155
|
+
|
156
|
+
thumbnailer->joined_frame = av_frame_alloc();
|
157
|
+
|
158
|
+
int joined_buffer_size =
|
159
|
+
av_image_get_buffer_size(AV_PIX_FMT_YUV444P, width * (int)n, height, FRAME_ALIGN);
|
160
|
+
thumbnailer->joined_frames_buffer = av_mallocz((size_t)joined_buffer_size);
|
161
|
+
|
162
|
+
av_image_fill_arrays(thumbnailer->joined_frame->data,
|
163
|
+
thumbnailer->joined_frame->linesize,
|
164
|
+
thumbnailer->joined_frames_buffer,
|
165
|
+
AV_PIX_FMT_YUV444P, width * (int)n, height, FRAME_ALIGN);
|
166
|
+
|
167
|
+
|
168
|
+
for (i = 0; i < n; i++) {
|
169
|
+
size_t offset = (size_t)frame_size * i;
|
170
|
+
thumbnailer->scaled_frames[i] = av_frame_alloc();
|
171
|
+
av_image_fill_arrays(thumbnailer->scaled_frames[i]->data,
|
172
|
+
thumbnailer->scaled_frames[i]->linesize,
|
173
|
+
thumbnailer->scaled_frames_buffer + offset,
|
174
|
+
AV_PIX_FMT_YUV444P,
|
175
|
+
width,
|
176
|
+
height,
|
177
|
+
FRAME_ALIGN);
|
178
|
+
}
|
179
|
+
|
180
|
+
thumbnailer->tjInstance = tjInitCompress();
|
181
|
+
if (thumbnailer->tjInstance == NULL) {
|
182
|
+
set_tj_error(thumbnailer);
|
183
|
+
goto fail;
|
184
|
+
}
|
185
|
+
|
186
|
+
return true;
|
187
|
+
|
188
|
+
fail:
|
189
|
+
VF_DEBUG("Thumbnailer creation failed");
|
190
|
+
return false;
|
191
|
+
}
|
192
|
+
|
193
|
+
bool
|
194
|
+
vf_thumbnailer_get_frame(VfThumbnailer* thumbnailer,
|
195
|
+
double seconds,
|
196
|
+
unsigned char** data,
|
197
|
+
size_t* size,
|
198
|
+
bool accurate,
|
199
|
+
bool filter_monoton)
|
200
|
+
{
|
201
|
+
AVPacket* packet;
|
202
|
+
AVFrame* frame;
|
203
|
+
AVFormatContext* format_context;
|
204
|
+
AVCodecContext* codec_context;
|
205
|
+
VfFile* file = thumbnailer->video_file;
|
206
|
+
|
207
|
+
bool done = false;
|
208
|
+
bool first_decoded_frame;
|
209
|
+
unsigned n = 0;
|
210
|
+
int video_stream_index;
|
211
|
+
|
212
|
+
VF_DEBUG("FILTER MONOTONY: %d", filter_monoton);
|
213
|
+
|
214
|
+
if(!vf_file_open_codec(file, thumbnailer)) {
|
215
|
+
VF_DEBUG("opening codec failed");
|
216
|
+
goto fail;
|
217
|
+
}
|
218
|
+
|
219
|
+
packet = &thumbnailer->packet;
|
220
|
+
|
221
|
+
format_context = file->format_context;
|
222
|
+
codec_context = file->video_codec_context;
|
223
|
+
video_stream_index = file->video_stream->index;
|
224
|
+
frame = thumbnailer->frame;
|
225
|
+
|
226
|
+
int64_t position = (int64_t)(AV_TIME_BASE * seconds);
|
227
|
+
if (format_context->duration < position || position < 0) {
|
228
|
+
goto fail;
|
229
|
+
}
|
230
|
+
|
231
|
+
if (format_context->start_time != AV_NOPTS_VALUE)
|
232
|
+
position += format_context->start_time;
|
233
|
+
|
234
|
+
int frames_not_taken_because_of_monoty = 0;
|
235
|
+
|
236
|
+
int64_t video_position = av_rescale_q(position, AV_TIME_BASE_Q, file->video_stream->time_base);
|
237
|
+
int64_t seek_position = video_position;
|
238
|
+
|
239
|
+
reseek: {
|
240
|
+
VF_DEBUG("seeking to %ld...\n", seek_position);
|
241
|
+
int ret = avformat_seek_file(format_context,
|
242
|
+
video_stream_index, //-1,
|
243
|
+
0,
|
244
|
+
seek_position,
|
245
|
+
seek_position,
|
246
|
+
0);
|
247
|
+
|
248
|
+
if(ret < 0) {
|
249
|
+
VF_DEBUG("seek_file error: %d", ret);
|
250
|
+
VF_SET_AV_ERROR(thumbnailer, ret);
|
251
|
+
goto fail;
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
first_decoded_frame = true;
|
256
|
+
|
257
|
+
while(!done) {
|
258
|
+
|
259
|
+
int ret = av_read_frame(format_context, packet);
|
260
|
+
if(ret < 0) {
|
261
|
+
VF_DEBUG("read_frame error: %d", ret);
|
262
|
+
VF_SET_AV_ERROR(thumbnailer, ret);
|
263
|
+
goto fail;
|
264
|
+
}
|
265
|
+
|
266
|
+
int64_t prev_packet_pts = AV_NOPTS_VALUE;
|
267
|
+
if (packet->stream_index == video_stream_index) {
|
268
|
+
int ret;
|
269
|
+
ret = avcodec_send_packet(codec_context, packet);
|
270
|
+
VF_DEBUG("send ret: %d (ts: %ld)\n", ret, packet->pts);
|
271
|
+
if(ret < 0) {
|
272
|
+
if(ret == AVERROR(EAGAIN) || ret == AVERROR_INVALIDDATA) {
|
273
|
+
goto next_frame;
|
274
|
+
} else {
|
275
|
+
VF_SET_AV_ERROR(thumbnailer, ret);
|
276
|
+
goto fail;
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
ret = avcodec_receive_frame(codec_context, frame);
|
281
|
+
VF_DEBUG("recv ret: %d (ts: %ld)\n", ret, packet->pts);
|
282
|
+
if(ret < 0) {
|
283
|
+
if(ret == AVERROR(EAGAIN)) {
|
284
|
+
goto next_frame;
|
285
|
+
} else {
|
286
|
+
VF_SET_AV_ERROR(thumbnailer, ret);
|
287
|
+
goto fail;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
if(packet->pts != AV_NOPTS_VALUE && packet->pts >= video_position) {
|
292
|
+
VF_DEBUG("possible decode frame %ld\n", packet->pts);
|
293
|
+
|
294
|
+
if(first_decoded_frame) {
|
295
|
+
if(accurate && (packet->pts > video_position && !(prev_packet_pts < video_position))) {
|
296
|
+
VF_DEBUG("FIRST DECODED FRAME IS BEYOND target pos: %ld > %ld\n", packet->pts, video_position);
|
297
|
+
int64_t seek_delta = av_rescale_q(5 * AV_TIME_BASE, AV_TIME_BASE_Q, file->video_stream->time_base);
|
298
|
+
VF_DEBUG("reseeking, old seek pos: %ld", seek_position);
|
299
|
+
seek_position = MAX(0, seek_position - seek_delta);
|
300
|
+
VF_DEBUG("reseeking, new seek pos: %ld", seek_position);
|
301
|
+
av_packet_unref(packet);
|
302
|
+
avcodec_flush_buffers(codec_context);
|
303
|
+
goto reseek;
|
304
|
+
} else {
|
305
|
+
first_decoded_frame = false;
|
306
|
+
}
|
307
|
+
}
|
308
|
+
VF_DEBUG("recv ret: %d\n", ret);
|
309
|
+
|
310
|
+
clock_t c = clock();
|
311
|
+
VF_DEBUG("decode: %lf\n", (double)(clock() - c) / CLOCKS_PER_SEC);
|
312
|
+
VF_DEBUG("GOT_FRAME! (%d/%d): %ld\n", n, thumbnailer->n, frame->pts);
|
313
|
+
|
314
|
+
sws_scale(thumbnailer->sws_ctx,
|
315
|
+
(const uint8_t* const*)frame->data,
|
316
|
+
frame->linesize,
|
317
|
+
0,
|
318
|
+
codec_context->height,
|
319
|
+
(uint8_t* const*)thumbnailer->scaled_frames[n]->data,
|
320
|
+
thumbnailer->scaled_frames[n]->linesize);
|
321
|
+
|
322
|
+
VF_DEBUG("scale: %lf\n", (double)(clock() - c) / CLOCKS_PER_SEC);
|
323
|
+
|
324
|
+
if (filter_monoton) {
|
325
|
+
int64_t monotony =
|
326
|
+
frame_monotony(thumbnailer, thumbnailer->scaled_frames[n]);
|
327
|
+
VF_DEBUG("MONOTONY: %ld\n", monotony);
|
328
|
+
|
329
|
+
if (monotony < 40000 && frames_not_taken_because_of_monoty < 125) {
|
330
|
+
frames_not_taken_because_of_monoty++;
|
331
|
+
goto next_frame;
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
frames_not_taken_because_of_monoty = 0;
|
336
|
+
n++;
|
337
|
+
VF_DEBUG("FRAMES: %u/%u\n", n, thumbnailer->n);
|
338
|
+
if (n == thumbnailer->n) {
|
339
|
+
done = 1;
|
340
|
+
}
|
341
|
+
}
|
342
|
+
prev_packet_pts = packet->pts;
|
343
|
+
}
|
344
|
+
|
345
|
+
next_frame:
|
346
|
+
av_packet_unref(packet);
|
347
|
+
}
|
348
|
+
|
349
|
+
VF_DEBUG("encoding as jpeg...\n");
|
350
|
+
|
351
|
+
planes_to_jpeg(thumbnailer, 80, data, size);
|
352
|
+
|
353
|
+
return true;
|
354
|
+
|
355
|
+
fail:
|
356
|
+
*data = NULL;
|
357
|
+
*size = 0;
|
358
|
+
return false;
|
359
|
+
}
|
360
|
+
|
361
|
+
static void
|
362
|
+
planes_to_jpeg(VfThumbnailer* thumbnailer,
|
363
|
+
int quality,
|
364
|
+
unsigned char** rdata,
|
365
|
+
size_t* size)
|
366
|
+
{
|
367
|
+
|
368
|
+
tjhandle tjInstance = thumbnailer->tjInstance;
|
369
|
+
int flags = TJFLAG_FASTDCT;
|
370
|
+
|
371
|
+
|
372
|
+
|
373
|
+
// join frames
|
374
|
+
|
375
|
+
for (unsigned i = 0; i < 3; i++) {
|
376
|
+
for (unsigned j = 0; j < thumbnailer->n; j++) {
|
377
|
+
av_image_copy_plane(thumbnailer->joined_frame->data[i] + j * (unsigned) thumbnailer->width,
|
378
|
+
thumbnailer->joined_frame->linesize[i],
|
379
|
+
thumbnailer->scaled_frames[j]->data[i],
|
380
|
+
thumbnailer->scaled_frames[j]->linesize[i],
|
381
|
+
thumbnailer->width,
|
382
|
+
thumbnailer->height);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
const unsigned char** planes = (unsigned char **) thumbnailer->joined_frame->data;
|
387
|
+
int* strides = thumbnailer->joined_frame->linesize;
|
388
|
+
*rdata = NULL;
|
389
|
+
|
390
|
+
int error = tjCompressFromYUVPlanes(tjInstance,
|
391
|
+
planes,
|
392
|
+
(int)thumbnailer->n * thumbnailer->width,
|
393
|
+
strides,
|
394
|
+
thumbnailer->height,
|
395
|
+
TJSAMP_444,
|
396
|
+
rdata,
|
397
|
+
size,
|
398
|
+
quality,
|
399
|
+
flags);
|
400
|
+
if (error < 0) {
|
401
|
+
set_tj_error(thumbnailer);
|
402
|
+
goto error;
|
403
|
+
}
|
404
|
+
|
405
|
+
return;
|
406
|
+
error:
|
407
|
+
*rdata = NULL;
|
408
|
+
*size = 0;
|
409
|
+
}
|
410
|
+
|
411
|
+
static int64_t
|
412
|
+
frame_monotony(VfThumbnailer *thumbnailer, AVFrame *frame)
|
413
|
+
{
|
414
|
+
int64_t monotony;
|
415
|
+
|
416
|
+
int i, width, height;
|
417
|
+
width = thumbnailer->width;
|
418
|
+
height = thumbnailer->height;
|
419
|
+
|
420
|
+
monotony = 0;
|
421
|
+
|
422
|
+
for(i = width + 1; i < height * (width - 1); i+= 16)
|
423
|
+
{
|
424
|
+
|
425
|
+
monotony += ABS(frame->data[0][i] - frame->data[0][i - 1]) +
|
426
|
+
ABS( *(frame->data[0] + width + i) - *(frame->data[0] + width
|
427
|
+
+ i - 1)) + ABS( *(frame->data[0] - width + i) -
|
428
|
+
*(frame->data[0] - width + i - 1));
|
429
|
+
}
|
430
|
+
|
431
|
+
return monotony;
|
432
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
/* vim: set tabstop=2 expandtab: */
|
2
|
+
#pragma once
|
3
|
+
|
4
|
+
#include <stddef.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <libavcodec/avcodec.h>
|
7
|
+
#include <libavformat/avformat.h>
|
8
|
+
#include <libswscale/swscale.h>
|
9
|
+
#include <pthread.h>
|
10
|
+
#include <turbojpeg.h>
|
11
|
+
|
12
|
+
#include "vf-file.h"
|
13
|
+
|
14
|
+
typedef struct _VfThumbnailer VfThumbnailer;
|
15
|
+
|
16
|
+
struct _VfThumbnailer
|
17
|
+
{
|
18
|
+
VfFile *video_file;
|
19
|
+
|
20
|
+
int width;
|
21
|
+
int height;
|
22
|
+
unsigned n;
|
23
|
+
|
24
|
+
AVFrame* frame;
|
25
|
+
AVFrame** scaled_frames;
|
26
|
+
AVFrame *joined_frame;
|
27
|
+
uint8_t *scaled_frames_buffer;
|
28
|
+
uint8_t *joined_frames_buffer;
|
29
|
+
AVPacket packet;
|
30
|
+
int64_t position;
|
31
|
+
|
32
|
+
struct SwsContext* sws_ctx;
|
33
|
+
|
34
|
+
tjhandle *tjInstance;
|
35
|
+
|
36
|
+
int last_error;
|
37
|
+
char last_error_str[1024];
|
38
|
+
|
39
|
+
};
|
40
|
+
|
41
|
+
bool
|
42
|
+
vf_thumbnailer_init(VfThumbnailer *thumbnailer, VfFile* file, int width, unsigned n);
|
43
|
+
|
44
|
+
bool
|
45
|
+
vf_thumbnailer_get_frame(VfThumbnailer* thumbnailer,
|
46
|
+
double seconds,
|
47
|
+
unsigned char** data,
|
48
|
+
size_t* size,
|
49
|
+
bool accurate,
|
50
|
+
bool filter_monoton
|
51
|
+
);
|
52
|
+
|
53
|
+
void
|
54
|
+
vf_thumbnailer_destroy(VfThumbnailer* thumbnailer);
|
@@ -0,0 +1 @@
|
|
1
|
+
#include "vf-util.h"
|