Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2025 MuseScore Limited
9 : *
10 : * This program is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "ffmpeglibhandler.h"
24 :
25 : #include "io/fileinfo.h"
26 : #include "global/dlib.h"
27 :
28 : #include "log.h"
29 :
30 : namespace muse::media::ffmpeg::v5 {
31 : #define RESOLVE_FROM(lib, name) do { \
32 : name = reinterpret_cast<decltype(name)>(getSymbol(lib, #name)); \
33 : if (!name) { LOGW() << "FFmpeg: missing symbol " #name; clearFunctions(); return false; } \
34 : } while (0)
35 :
36 0 : FFmpegLibHandler::~FFmpegLibHandler()
37 : {
38 0 : unload();
39 0 : }
40 :
41 0 : void* FFmpegLibHandler::getSymbol(void* lib, const char* name) const
42 : {
43 0 : if (!lib) {
44 0 : return nullptr;
45 : }
46 0 : return muse::getLibFunc(lib, name);
47 : }
48 :
49 0 : bool FFmpegLibHandler::loadApi()
50 : {
51 0 : if (!m_avUtilLibrary || !m_avCodecLibrary || !m_avFormatLibrary || !m_swsScaleLibrary || !m_swResampleLibrary) {
52 0 : return false;
53 : }
54 :
55 0 : clearFunctions();
56 :
57 : // libavutil
58 0 : RESOLVE_FROM(m_avUtilLibrary, av_rescale_q);
59 0 : RESOLVE_FROM(m_avUtilLibrary, av_image_fill_arrays);
60 0 : RESOLVE_FROM(m_avUtilLibrary, av_image_get_buffer_size);
61 0 : RESOLVE_FROM(m_avUtilLibrary, av_opt_set_int);
62 0 : RESOLVE_FROM(m_avUtilLibrary, av_frame_alloc);
63 0 : RESOLVE_FROM(m_avUtilLibrary, av_frame_free);
64 0 : RESOLVE_FROM(m_avUtilLibrary, av_malloc);
65 :
66 : // libavformat
67 0 : RESOLVE_FROM(m_avFormatLibrary, av_write_trailer);
68 0 : RESOLVE_FROM(m_avFormatLibrary, av_interleaved_write_frame);
69 0 : RESOLVE_FROM(m_avFormatLibrary, av_read_frame);
70 0 : RESOLVE_FROM(m_avFormatLibrary, avio_open);
71 0 : RESOLVE_FROM(m_avFormatLibrary, avio_close);
72 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_new_stream);
73 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_write_header);
74 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_open_input);
75 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_find_stream_info);
76 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_close_input);
77 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_free_context);
78 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_alloc_output_context2);
79 0 : RESOLVE_FROM(m_avFormatLibrary, avformat_alloc_context);
80 0 : RESOLVE_FROM(m_avFormatLibrary, avio_alloc_context);
81 0 : RESOLVE_FROM(m_avFormatLibrary, avio_context_free);
82 :
83 : // libavcodec
84 0 : RESOLVE_FROM(m_avCodecLibrary, av_packet_alloc);
85 0 : RESOLVE_FROM(m_avCodecLibrary, av_packet_free);
86 0 : RESOLVE_FROM(m_avCodecLibrary, av_packet_unref);
87 0 : RESOLVE_FROM(m_avCodecLibrary, av_packet_rescale_ts);
88 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_alloc_context3);
89 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_find_encoder);
90 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_find_decoder);
91 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_free_context);
92 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_open2);
93 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_parameters_from_context);
94 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_parameters_to_context);
95 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_parameters_copy);
96 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_send_frame);
97 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_receive_packet);
98 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_send_packet);
99 0 : RESOLVE_FROM(m_avCodecLibrary, avcodec_receive_frame);
100 :
101 : // libswscale
102 0 : RESOLVE_FROM(m_swsScaleLibrary, sws_getCachedContext);
103 0 : RESOLVE_FROM(m_swsScaleLibrary, sws_scale);
104 0 : RESOLVE_FROM(m_swsScaleLibrary, sws_freeContext);
105 :
106 0 : return functionsValid();
107 : }
108 :
109 0 : bool FFmpegLibHandler::loadLib(const io::path_t& avUtilPath, const io::path_t& avCodecPath, const io::path_t& avFormatPath,
110 : const io::path_t& swScalePath, const io::path_t& swResamplePath)
111 : {
112 0 : unload();
113 :
114 : // Load avutil first, then swresample, swscale, avcodec, avformat (dependency order)
115 0 : if (!tryLoadPath(m_avUtilLibrary, avUtilPath)
116 0 : || !tryLoadPath(m_swResampleLibrary, swResamplePath)
117 0 : || !tryLoadPath(m_swsScaleLibrary, swScalePath)
118 0 : || !tryLoadPath(m_avCodecLibrary, avCodecPath)
119 0 : || !tryLoadPath(m_avFormatLibrary, avFormatPath)) {
120 0 : unload();
121 0 : return false;
122 : }
123 :
124 0 : LOGI() << "FFmpeg loaded: avutil=" << avUtilPath << ", avcodec=" << avCodecPath << ", avformat=" << avFormatPath
125 0 : << ", swscale=" << swScalePath << ", swresample=" << swResamplePath;
126 :
127 0 : return true;
128 : }
129 :
130 0 : void FFmpegLibHandler::unload()
131 : {
132 0 : closeLib(m_avFormatLibrary);
133 0 : closeLib(m_swResampleLibrary);
134 0 : closeLib(m_swsScaleLibrary);
135 0 : closeLib(m_avCodecLibrary);
136 0 : closeLib(m_avUtilLibrary);
137 :
138 0 : clearFunctions();
139 0 : }
140 :
141 0 : void FFmpegLibHandler::closeLib(void*& lib)
142 : {
143 0 : if (lib) {
144 0 : muse::closeLib(lib);
145 0 : lib = nullptr;
146 : }
147 0 : }
148 :
149 0 : bool FFmpegLibHandler::tryLoadPath(void*& lib, const io::path_t& fullPath)
150 : {
151 0 : if (!io::FileInfo::exists(fullPath)) {
152 0 : return false;
153 : }
154 :
155 0 : void* loaded = muse::loadLib(fullPath);
156 0 : if (loaded) {
157 0 : lib = loaded;
158 0 : return true;
159 : }
160 :
161 0 : return false;
162 : }
163 :
164 0 : bool FFmpegLibHandler::functionsValid() const
165 : {
166 0 : return av_rescale_q
167 0 : && av_image_fill_arrays && av_image_get_buffer_size
168 0 : && av_opt_set_int
169 0 : && avformat_new_stream
170 0 : && avformat_write_header && av_write_trailer
171 0 : && avio_open && avio_close && av_interleaved_write_frame
172 0 : && avformat_open_input && avformat_find_stream_info
173 0 : && avformat_close_input && avformat_free_context
174 0 : && avformat_alloc_output_context2 && av_read_frame
175 0 : && avformat_alloc_context && avio_alloc_context && avio_context_free
176 0 : && avcodec_alloc_context3 && avcodec_find_encoder && avcodec_find_decoder && avcodec_free_context
177 0 : && avcodec_open2 && avcodec_parameters_from_context && avcodec_parameters_to_context && avcodec_parameters_copy
178 0 : && avcodec_send_frame && avcodec_receive_packet && avcodec_send_packet && avcodec_receive_frame
179 0 : && av_frame_alloc && av_frame_free
180 0 : && av_packet_alloc && av_packet_free && av_packet_unref && av_packet_rescale_ts
181 0 : && av_malloc
182 0 : && sws_getCachedContext && sws_scale && sws_freeContext;
183 : }
184 :
185 0 : void FFmpegLibHandler::clearFunctions()
186 : {
187 0 : av_rescale_q = nullptr;
188 0 : av_image_fill_arrays = nullptr;
189 0 : av_image_get_buffer_size = nullptr;
190 0 : av_opt_set_int = nullptr;
191 0 : av_malloc = nullptr;
192 0 : avformat_new_stream = nullptr;
193 0 : avformat_write_header = nullptr;
194 0 : av_write_trailer = nullptr;
195 0 : avio_open = nullptr;
196 0 : avio_close = nullptr;
197 0 : av_interleaved_write_frame = nullptr;
198 0 : avformat_open_input = nullptr;
199 0 : avformat_find_stream_info = nullptr;
200 0 : avformat_close_input = nullptr;
201 0 : avformat_free_context = nullptr;
202 0 : avformat_alloc_output_context2 = nullptr;
203 0 : av_read_frame = nullptr;
204 0 : avformat_alloc_context = nullptr;
205 0 : avio_alloc_context = nullptr;
206 0 : avio_context_free = nullptr;
207 0 : avcodec_alloc_context3 = nullptr;
208 0 : avcodec_find_encoder = nullptr;
209 0 : avcodec_find_decoder = nullptr;
210 0 : avcodec_free_context = nullptr;
211 0 : avcodec_open2 = nullptr;
212 0 : avcodec_parameters_from_context = nullptr;
213 0 : avcodec_parameters_to_context = nullptr;
214 0 : avcodec_parameters_copy = nullptr;
215 0 : avcodec_send_frame = nullptr;
216 0 : avcodec_receive_packet = nullptr;
217 0 : avcodec_send_packet = nullptr;
218 0 : avcodec_receive_frame = nullptr;
219 0 : av_frame_alloc = nullptr;
220 0 : av_frame_free = nullptr;
221 0 : av_packet_alloc = nullptr;
222 0 : av_packet_free = nullptr;
223 0 : av_packet_unref = nullptr;
224 0 : av_packet_rescale_ts = nullptr;
225 0 : sws_getCachedContext = nullptr;
226 0 : sws_scale = nullptr;
227 0 : sws_freeContext = nullptr;
228 0 : }
229 : }
|