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.
@@ -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"
@@ -0,0 +1,6 @@
1
+ /* vim: set tabstop=2 expandtab: */
2
+ #pragma once
3
+
4
+ #include <stdio.h>
5
+ #include <libavcodec/avcodec.h>
6
+ #include <libavformat/avformat.h>
@@ -0,0 +1,3 @@
1
+ module VideoFile
2
+ VERSION = "0.1.0"
3
+ end