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