1 /*
2 * ffmpeglib.c - Interface to access the ffmpeg libs.
3 *
4 * Written by
5 * Andreas Matthies <andreas.matthies@gmx.net>
6 * Christian Vogelgsang <chris@vogelgsang.org>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29
30 #ifdef HAVE_FFMPEG
31
32 #include "archdep.h"
33 #include "gfxoutputdrv/ffmpeglib.h"
34 #include "log.h"
35 #include "uiapi.h"
36 #include "dynlib.h"
37
38 #ifndef STATIC_FFMPEG
39
40 /* define major version if its not already defined */
41 #ifndef LIBAVCODEC_VERSION_MAJOR
42 #define LIBAVCODEC_VERSION_MAJOR 51
43 #endif
44 #ifndef LIBAVFORMAT_VERSION_MAJOR
45 #define LIBAVFORMAT_VERSION_MAJOR 52
46 #define NO_AVFORMAT_CHECK 1
47 #endif
48 #ifndef LIBAVUTIL_VERSION_MAJOR
49 #define LIBAVUTIL_VERSION_MAJOR 49
50 #define NO_AVUTIL_CHECK 1
51 #endif
52 #ifndef LIBSWSCALE_VERSION_MAJOR
53 #define LIBSWSCALE_VERSION_MAJOR 0
54 #endif
55 #ifndef LIBSWRESAMPLE_VERSION_MAJOR
56 #define LIBSWRESAMPLE_VERSION_MAJOR 0
57 #endif
58 #ifndef LIBAVRESAMPLE_VERSION_MAJOR
59 #define LIBAVRESAMPLE_VERSION_MAJOR 2
60 #endif
61
62 #define AVCODEC_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(avcodec, LIBAVCODEC_VERSION_MAJOR)
63 #define AVFORMAT_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(avformat, LIBAVFORMAT_VERSION_MAJOR)
64 #define AVUTIL_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(avutil, LIBAVUTIL_VERSION_MAJOR)
65 #define SWSCALE_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(swscale, LIBSWSCALE_VERSION_MAJOR)
66 #define SWRESAMPLE_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(swresample, LIBSWRESAMPLE_VERSION_MAJOR)
67 #define AVRESAMPLE_SO_NAME ARCHDEP_MAKE_SO_NAME_VERSION(avresample, LIBAVRESAMPLE_VERSION_MAJOR)
68
69 static void *avcodec_so = NULL;
70 static void *avformat_so = NULL;
71 static void *avutil_so = NULL;
72 static void *swscale_so = NULL;
73
74 #ifndef HAVE_FFMPEG_AVRESAMPLE
75 static void *swresample_so = NULL;
76 #else
77 static void *avresample_so = NULL;
78 #endif
79
80 /* macro for getting functionpointers from avcodec */
81 #define GET_SYMBOL_AND_TEST_AVCODEC( _name_ ) \
82 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(avcodec_so, #_name_); \
83 if (!lib->p_##_name_) { \
84 log_debug("getting symbol " #_name_ " failed!"); \
85 return -1; \
86 }
87
88 /* macro for getting functionpointers from avformat */
89 #define GET_SYMBOL_AND_TEST_AVFORMAT( _name_ ) \
90 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(avformat_so, #_name_ ); \
91 if (!lib->p_##_name_) { \
92 log_debug("getting symbol " #_name_ " failed!"); \
93 return -1; \
94 }
95
96 /* macro for getting functionpointers from avutil */
97 #define GET_SYMBOL_AND_TEST_AVUTIL( _name_ ) \
98 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(avutil_so, #_name_ ); \
99 if (!lib->p_##_name_) { \
100 log_debug("getting symbol " #_name_ " failed!"); \
101 return -1; \
102 }
103
104 /* macro for getting functionpointers from swscale */
105 #define GET_SYMBOL_AND_TEST_SWSCALE( _name_ ) \
106 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(swscale_so, #_name_ ); \
107 if (!lib->p_##_name_) { \
108 log_debug("getting symbol " #_name_ " failed!"); \
109 return -1; \
110 }
111
112 #ifndef HAVE_FFMPEG_AVRESAMPLE
113 /* macro for getting functionpointers from swresample */
114 #define GET_SYMBOL_AND_TEST_SWRESAMPLE( _name_ ) \
115 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(swresample_so, #_name_); \
116 if (!lib->p_##_name_) { \
117 log_debug("getting symbol " #_name_ " failed!"); \
118 return -1; \
119 }
120 #else
121 /* macro for getting functionpointers from avresample */
122 #define GET_SYMBOL_AND_TEST_AVRESAMPLE( _name_ ) \
123 lib->p_##_name_ = (_name_##_t)vice_dynlib_symbol(avresample_so, #_name_); \
124 if (!lib->p_##_name_) { \
125 log_debug("getting symbol " #_name_ " failed!"); \
126 return -1; \
127 }
128 #endif
129
check_version(const char * lib_name,void * handle,const char * symbol,unsigned ver_inc)130 static int check_version(const char *lib_name, void *handle, const char *symbol, unsigned ver_inc)
131 {
132 ffmpeg_version_t version_func;
133 unsigned ver_lib;
134 const char *result_msgs[] = { "full match","major.minor matches","major matches","unsupported" };
135 enum { FULL_MATCH=0, MAJOR_MINOR_MATCH=1, MAJOR_MATCH=2, NO_MATCH=3 } result;
136
137 version_func = (ffmpeg_version_t)vice_dynlib_symbol(handle, symbol);
138 if (version_func == NULL) {
139 log_debug("ffmpeg %s: version function '%s' not found! error: %s", lib_name, symbol, vice_dynlib_error());
140 return -1;
141 }
142
143 ver_lib = version_func();
144
145 /* version matches exactly */
146 if (ver_lib == ver_inc) {
147 result = FULL_MATCH;
148 } else {
149 /* compare major.minor */
150 ver_lib >>= 8;
151 ver_inc >>= 8;
152 if (ver_lib == ver_inc) {
153 result = MAJOR_MINOR_MATCH;
154 } else {
155 /* compare major */
156 ver_lib >>= 8;
157 ver_inc >>= 8;
158 if (ver_lib == ver_inc) {
159 result = MAJOR_MATCH;
160 } else {
161 result = NO_MATCH;
162 }
163 }
164 }
165
166 log_debug("ffmpeg %8s lib has version %06x, VICE expects %06x: %s",
167 lib_name, ver_lib, ver_inc, result_msgs[result]);
168
169 /* now decide what level of matching fails */
170 if (result == NO_MATCH) {
171 return -1;
172 }
173 return 0;
174 }
175
load_avcodec(ffmpeglib_t * lib)176 static int load_avcodec(ffmpeglib_t *lib)
177 {
178 if (!avcodec_so) {
179 avcodec_so = vice_dynlib_open(AVCODEC_SO_NAME);
180
181 if (!avcodec_so) {
182 log_debug("opening dynamic library " AVCODEC_SO_NAME " failed! error: %s", vice_dynlib_error());
183 return -1;
184 }
185
186 GET_SYMBOL_AND_TEST_AVCODEC(av_init_packet);
187 GET_SYMBOL_AND_TEST_AVCODEC(avcodec_open2);
188 GET_SYMBOL_AND_TEST_AVCODEC(avcodec_close);
189 GET_SYMBOL_AND_TEST_AVCODEC(avcodec_find_encoder);
190 GET_SYMBOL_AND_TEST_AVCODEC(avcodec_encode_audio2);
191 GET_SYMBOL_AND_TEST_AVCODEC(avcodec_encode_video2);
192 GET_SYMBOL_AND_TEST_AVCODEC(avpicture_fill);
193 GET_SYMBOL_AND_TEST_AVCODEC(avpicture_get_size);
194 GET_SYMBOL_AND_TEST_AVCODEC(av_packet_rescale_ts);
195 }
196
197 return check_version("avcodec", avcodec_so, "avcodec_version", LIBAVCODEC_VERSION_INT);
198 }
199
free_avcodec(ffmpeglib_t * lib)200 static void free_avcodec(ffmpeglib_t *lib)
201 {
202 if (avcodec_so) {
203 if (vice_dynlib_close(avcodec_so) != 0) {
204 log_debug("closing dynamic library " AVCODEC_SO_NAME " failed!");
205 }
206 }
207 avcodec_so = NULL;
208
209 lib->p_avcodec_open2 = NULL;
210 lib->p_avcodec_close = NULL;
211 lib->p_avcodec_find_encoder = NULL;
212 lib->p_avcodec_encode_audio2 = NULL;
213 lib->p_avcodec_encode_video2 = NULL;
214 lib->p_avpicture_fill = NULL;
215 lib->p_avpicture_get_size = NULL;
216 }
217
load_avformat(ffmpeglib_t * lib)218 static int load_avformat(ffmpeglib_t *lib)
219 {
220 if (!avformat_so) {
221 avformat_so = vice_dynlib_open(AVFORMAT_SO_NAME);
222
223 if (!avformat_so) {
224 log_debug("opening dynamic library " AVFORMAT_SO_NAME " failed! error: %s", vice_dynlib_error());
225 return -1;
226 }
227
228 GET_SYMBOL_AND_TEST_AVFORMAT(av_register_all);
229 GET_SYMBOL_AND_TEST_AVFORMAT(avformat_alloc_context);
230 GET_SYMBOL_AND_TEST_AVFORMAT(avformat_new_stream);
231 GET_SYMBOL_AND_TEST_AVFORMAT(avformat_write_header);
232 GET_SYMBOL_AND_TEST_AVFORMAT(av_interleaved_write_frame);
233 GET_SYMBOL_AND_TEST_AVFORMAT(av_write_trailer);
234 GET_SYMBOL_AND_TEST_AVFORMAT(avio_open);
235 GET_SYMBOL_AND_TEST_AVFORMAT(avio_close);
236 GET_SYMBOL_AND_TEST_AVFORMAT(av_dump_format);
237 GET_SYMBOL_AND_TEST_AVFORMAT(av_guess_format);
238 }
239
240 #ifdef NO_AVFORMAT_CHECK
241 return 0;
242 #else
243 return check_version("avformat", avformat_so, "avformat_version", LIBAVFORMAT_VERSION_INT);
244 #endif
245 }
246
free_avformat(ffmpeglib_t * lib)247 static void free_avformat(ffmpeglib_t *lib)
248 {
249 if (avformat_so) {
250 if (vice_dynlib_close(avformat_so) != 0) {
251 log_debug("closing dynamic library " AVFORMAT_SO_NAME " failed! error: %s", vice_dynlib_error());
252 }
253 }
254 avformat_so = NULL;
255
256 lib->p_av_init_packet = NULL;
257 lib->p_av_register_all = NULL;
258 lib->p_avformat_alloc_context = NULL;
259 lib->p_avformat_new_stream = NULL;
260 lib->p_avformat_write_header = NULL;
261 lib->p_av_interleaved_write_frame = NULL;
262 lib->p_av_write_trailer = NULL;
263 lib->p_avio_open = NULL;
264 lib->p_avio_close = NULL;
265 lib->p_av_dump_format = NULL;
266 lib->p_av_guess_format = NULL;
267 }
268
load_avutil(ffmpeglib_t * lib)269 static int load_avutil(ffmpeglib_t *lib)
270 {
271 if (!avutil_so) {
272 avutil_so = vice_dynlib_open(AVUTIL_SO_NAME);
273
274 if (!avutil_so) {
275 log_debug("opening dynamic library " AVUTIL_SO_NAME " failed! error: %s", vice_dynlib_error());
276 return -1;
277 }
278
279 GET_SYMBOL_AND_TEST_AVUTIL(av_free);
280 GET_SYMBOL_AND_TEST_AVUTIL(av_frame_alloc);
281 GET_SYMBOL_AND_TEST_AVUTIL(av_frame_get_buffer);
282 GET_SYMBOL_AND_TEST_AVUTIL(av_compare_ts);
283 GET_SYMBOL_AND_TEST_AVUTIL(av_get_channel_layout_nb_channels);
284 GET_SYMBOL_AND_TEST_AVUTIL(av_opt_set_int);
285 #ifndef HAVE_FFMPEG_AVRESAMPLE
286 GET_SYMBOL_AND_TEST_AVUTIL(av_opt_set_sample_fmt);
287 #endif
288 GET_SYMBOL_AND_TEST_AVUTIL(av_rescale_rnd);
289 GET_SYMBOL_AND_TEST_AVUTIL(av_rescale_q);
290 GET_SYMBOL_AND_TEST_AVUTIL(av_d2q);
291 GET_SYMBOL_AND_TEST_AVUTIL(av_frame_make_writable);
292 GET_SYMBOL_AND_TEST_AVUTIL(av_frame_free);
293 GET_SYMBOL_AND_TEST_AVUTIL(av_dict_set);
294 GET_SYMBOL_AND_TEST_AVUTIL(av_dict_free);
295 }
296
297 #ifdef NO_AVUTIL_CHECK
298 return 0;
299 #else
300 return check_version("avutil", avutil_so, "avutil_version", LIBAVUTIL_VERSION_INT);
301 #endif
302 }
303
free_avutil(ffmpeglib_t * lib)304 static void free_avutil(ffmpeglib_t *lib)
305 {
306 if (avutil_so) {
307 if (vice_dynlib_close(avutil_so) != 0) {
308 log_debug("closing dynamic library " AVUTIL_SO_NAME " failed! error: %s", vice_dynlib_error());
309 }
310 }
311 avutil_so = NULL;
312
313 lib->p_av_free = NULL;
314 lib->p_av_frame_alloc = NULL;
315 lib->p_av_frame_get_buffer = NULL;
316 lib->p_av_compare_ts = NULL;
317 lib->p_av_get_channel_layout_nb_channels = NULL;
318 lib->p_av_opt_set_int = NULL;
319 lib->p_av_opt_set_sample_fmt = NULL;
320 lib->p_av_rescale_rnd = NULL;
321 lib->p_av_rescale_q = NULL;
322 lib->p_av_d2q = NULL;
323 lib->p_av_frame_make_writable = NULL;
324 lib->p_av_frame_free = NULL;
325 lib->p_av_dict_set = NULL;
326 lib->p_av_dict_free = NULL;
327 }
328
load_swscale(ffmpeglib_t * lib)329 static int load_swscale(ffmpeglib_t *lib)
330 {
331 if (!swscale_so) {
332 swscale_so = vice_dynlib_open(SWSCALE_SO_NAME);
333
334 if (!swscale_so) {
335 log_debug("opening dynamic library " SWSCALE_SO_NAME " failed! error: %s", vice_dynlib_error());
336 return -1;
337 }
338
339 GET_SYMBOL_AND_TEST_SWSCALE(sws_getContext);
340 GET_SYMBOL_AND_TEST_SWSCALE(sws_freeContext);
341 GET_SYMBOL_AND_TEST_SWSCALE(sws_scale);
342 }
343
344 return check_version("swscale", swscale_so, "swscale_version", LIBSWSCALE_VERSION_INT);
345 }
346
free_swscale(ffmpeglib_t * lib)347 static void free_swscale(ffmpeglib_t *lib)
348 {
349 if (swscale_so) {
350 if (vice_dynlib_close(swscale_so) != 0) {
351 log_debug("closing dynamic library " SWSCALE_SO_NAME " failed! error: %s", vice_dynlib_error());
352 }
353 }
354 swscale_so = NULL;
355
356 lib->p_sws_getContext = NULL;
357 lib->p_sws_freeContext = NULL;
358 lib->p_sws_scale = NULL;
359 }
360
361 #ifndef HAVE_FFMPEG_AVRESAMPLE
load_swresample(ffmpeglib_t * lib)362 static int load_swresample(ffmpeglib_t *lib)
363 {
364 if (!swresample_so) {
365 swresample_so = vice_dynlib_open(SWRESAMPLE_SO_NAME);
366
367 if (!swresample_so) {
368 log_debug("opening dynamic library " SWRESAMPLE_SO_NAME " failed! error: %s", vice_dynlib_error());
369 return -1;
370 }
371
372 GET_SYMBOL_AND_TEST_SWRESAMPLE(swr_alloc);
373 GET_SYMBOL_AND_TEST_SWRESAMPLE(swr_init);
374 GET_SYMBOL_AND_TEST_SWRESAMPLE(swr_convert);
375 GET_SYMBOL_AND_TEST_SWRESAMPLE(swr_get_delay);
376 GET_SYMBOL_AND_TEST_SWRESAMPLE(swr_free);
377 }
378
379 return check_version("swresample", swresample_so, "swresample_version", LIBSWRESAMPLE_VERSION_INT);
380 }
381
free_swresample(ffmpeglib_t * lib)382 static void free_swresample(ffmpeglib_t *lib)
383 {
384 if (swresample_so) {
385 if (vice_dynlib_close(swresample_so) != 0) {
386 log_debug("closing dynamic library " SWRESAMPLE_SO_NAME " failed! error: %s", vice_dynlib_error());
387 }
388 }
389 swresample_so = NULL;
390
391 lib->p_swr_alloc = NULL;
392 lib->p_swr_init = NULL;
393 lib->p_swr_convert = NULL;
394 lib->p_swr_get_delay = NULL;
395 lib->p_swr_free = NULL;
396 }
397 #else
load_avresample(ffmpeglib_t * lib)398 static int load_avresample(ffmpeglib_t *lib)
399 {
400 if (!avresample_so) {
401 avresample_so = vice_dynlib_open(AVRESAMPLE_SO_NAME);
402
403 if (!avresample_so) {
404 log_debug("opening dynamic library " AVRESAMPLE_SO_NAME " failed! error: %s", vice_dynlib_error());
405 return -1;
406 }
407
408 GET_SYMBOL_AND_TEST_AVRESAMPLE(avresample_alloc_context);
409 GET_SYMBOL_AND_TEST_AVRESAMPLE(avresample_open);
410 GET_SYMBOL_AND_TEST_AVRESAMPLE(avresample_convert);
411 GET_SYMBOL_AND_TEST_AVRESAMPLE(avresample_get_delay);
412 GET_SYMBOL_AND_TEST_AVRESAMPLE(avresample_free);
413 }
414
415 return check_version("avresample", avresample_so, "avresample_version", LIBAVRESAMPLE_VERSION_INT);
416 }
417
free_avresample(ffmpeglib_t * lib)418 static void free_avresample(ffmpeglib_t *lib)
419 {
420 if (avresample_so) {
421 if (vice_dynlib_close(avresample_so) != 0) {
422 log_debug("closing dynamic library " AVRESAMPLE_SO_NAME " failed! error: %s", vice_dynlib_error());
423 }
424 }
425 avresample_so = NULL;
426
427 lib->p_avresample_alloc_context = NULL;
428 lib->p_avresample_open = NULL;
429 lib->p_avresample_convert = NULL;
430 lib->p_avresample_get_delay = NULL;
431 lib->p_avresample_free = NULL;
432 }
433 #endif
434
ffmpeglib_open(ffmpeglib_t * lib)435 int ffmpeglib_open(ffmpeglib_t *lib)
436 {
437 int result;
438
439 result = load_avformat(lib);
440 if (result != 0) {
441 free_avformat(lib);
442 return result;
443 }
444
445 result = load_avcodec(lib);
446 if (result != 0) {
447 free_avformat(lib);
448 free_avcodec(lib);
449 return result;
450 }
451
452 result = load_avutil(lib);
453 if (result != 0) {
454 free_avformat(lib);
455 free_avcodec(lib);
456 free_avutil(lib);
457 return result;
458 }
459
460 result = load_swscale(lib);
461 if (result != 0) {
462 free_avformat(lib);
463 free_avcodec(lib);
464 free_avutil(lib);
465 free_swscale(lib);
466 return result;
467 }
468
469 #ifndef HAVE_FFMPEG_AVRESAMPLE
470 result = load_swresample(lib);
471 if (result != 0) {
472 free_avformat(lib);
473 free_avcodec(lib);
474 free_avutil(lib);
475 free_swscale(lib);
476 free_swresample(lib);
477 return result;
478 }
479 #else
480 result = load_avresample(lib);
481 if (result != 0) {
482 free_avformat(lib);
483 free_avcodec(lib);
484 free_avutil(lib);
485 free_swscale(lib);
486 free_avresample(lib);
487 return result;
488 }
489 #endif
490
491 return 0;
492 }
493
ffmpeglib_close(ffmpeglib_t * lib)494 void ffmpeglib_close(ffmpeglib_t *lib)
495 {
496 free_avformat(lib);
497 free_avcodec(lib);
498 free_avutil(lib);
499 free_swscale(lib);
500 #ifndef HAVE_FFMPEG_AVRESAMPLE
501 free_swresample(lib);
502 #else
503 free_avresample(lib);
504 #endif
505 }
506 #else
ffmpeglib_open(ffmpeglib_t * lib)507 int ffmpeglib_open(ffmpeglib_t *lib)
508 {
509 return 0;
510 }
511
ffmpeglib_close(ffmpeglib_t * lib)512 void ffmpeglib_close(ffmpeglib_t *lib)
513 {
514 }
515 #endif
516 #endif /* #ifdef HAVE_FFMPEG */
517