1 /*
2  * Common Plugin code for OpenH323/OPAL
3  *
4  * This code is based on the following files from the OPAL project which
5  * have been removed from the current build and distributions but are still
6  * available in the CVS "attic"
7  *
8  *    src/codecs/h263codec.cxx
9  *    include/codecs/h263codec.h
10 
11  * The original files, and this version of the original code, are released under the same
12  * MPL 1.0 license. Substantial portions of the original code were contributed
13  * by Salyens and March Networks and their right to be identified as copyright holders
14  * of the original code portions and any parts now included in this new copy is asserted through
15  * their inclusion in the copyright notices below.
16  *
17  * Copyright (C) 2006 Post Increment
18  * Copyright (C) 2005 Salyens
19  * Copyright (C) 2001 March Networks Corporation
20  * Copyright (C) 1999-2000 Equivalence Pty. Ltd.
21  *
22  * The contents of this file are subject to the Mozilla Public License
23  * Version 1.0 (the "License"); you may not use this file except in
24  * compliance with the License. You may obtain a copy of the License at
25  * http://www.mozilla.org/MPL/
26  *
27  * Software distributed under the License is distributed on an "AS IS"
28  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
29  * the License for the specific language governing rights and limitations
30  * under the License.
31  *
32  * The Original Code is Open H323 Library.
33  *
34  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
35  *
36  * Contributor(s): Guilhem Tardy (gtardy@salyens.com)
37  *                 Craig Southeren (craigs@postincrement.com)
38  *                 Matthias Schneider (ma30002000@yahoo.de)
39  */
40 #include "dyna.h"
41 
Open(const char * name)42 bool DynaLink::Open(const char *name)
43 {
44   // At first we try without a path
45   if (InternalOpen("", name))
46     return true;
47 
48   // Try the current directory
49   if (InternalOpen(".", name))
50     return true;
51 
52   // try directories specified in PTLIBPLUGINDIR
53   char ptlibPath[1024];
54   char * env = ::getenv("PTLIBPLUGINDIR");
55   if (env != NULL)
56     strcpy(ptlibPath, env);
57   else
58 #ifdef P_DEFAULT_PLUGIN_DIR
59     strcpy(ptlibPath, P_DEFAULT_PLUGIN_DIR);
60 #elif _WIN32
61     strcpy(ptlibPath, "C:\\PTLib_Plugins");
62 #else
63     strcpy(ptlibPath, "/usr/local/lib");
64 #endif
65   char * p = ::strtok(ptlibPath, DIR_TOKENISER);
66   while (p != NULL) {
67     if (InternalOpen(p, name))
68       return true;
69     p = ::strtok(NULL, DIR_TOKENISER);
70   }
71 
72   return false;
73 }
74 
InternalOpen(const char * dir,const char * name)75 bool DynaLink::InternalOpen(const char * dir, const char *name)
76 {
77   char path[1024];
78   memset(path, 0, sizeof(path));
79 
80   // Copy the directory to "path" and add a separator if necessary
81   if (strlen(dir) > 0) {
82     strcpy(path, dir);
83     if (path[strlen(path)-1] != DIR_SEPARATOR[0])
84       strcat(path, DIR_SEPARATOR);
85   }
86   strcat(path, name);
87 
88   if (strlen(path) == 0) {
89     PTRACE(1, m_codecString, "DynaLink: dir '" << (dir != NULL ? dir : "(NULL)")
90            << "', name '" << (name != NULL ? name : "(NULL)") << "' resulted in empty path");
91     return false;
92   }
93 
94   // Load the Libary
95 #ifdef _WIN32
96 # ifdef UNICODE
97   // must be called before using avcodec lib
98   USES_CONVERSION;
99   m_hDLL = LoadLibraryEx(A2T(path), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
100 # else
101   // must be called before using avcodec lib
102   m_hDLL = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
103 # endif /* UNICODE */
104 #else
105   // must be called before using avcodec lib
106   m_hDLL = dlopen((const char *)path, RTLD_NOW);
107 #endif /* _WIN32 */
108 
109   // Check for errors
110   if (m_hDLL == NULL) {
111 #ifndef _WIN32
112     const char * err = dlerror();
113     if (err != NULL)
114       PTRACE(1, m_codecString, "dlopen error " << err);
115     else
116       PTRACE(1, m_codecString, "dlopen error loading " << path);
117 #else /* _WIN32 */
118     PTRACE(1, m_codecString, "Error loading " << path);
119 #endif /* _WIN32 */
120     return false;
121   }
122 
123 #ifdef _WIN32
124   GetModuleFileName(m_hDLL, path, sizeof(path));
125 #endif
126 
127   PTRACE(1, m_codecString, "Successfully loaded '" << path << "'");
128   return true;
129 }
130 
Close()131 void DynaLink::Close()
132 {
133   if (m_hDLL != NULL) {
134 #ifdef _WIN32
135     FreeLibrary(m_hDLL);
136 #else
137     dlclose(m_hDLL);
138 #endif /* _WIN32 */
139     m_hDLL = NULL;
140   }
141 }
142 
GetFunction(const char * name,Function & func)143 bool DynaLink::GetFunction(const char * name, Function & func)
144 {
145   if (m_hDLL == NULL)
146     return false;
147 
148 #ifdef _WIN32
149 
150   #ifdef UNICODE
151     USES_CONVERSION;
152     FARPROC p = GetProcAddress(m_hDLL, A2T(name));
153   #else
154     FARPROC p = GetProcAddress(m_hDLL, name);
155   #endif /* UNICODE */
156   if (p == NULL) {
157     PTRACE(1, m_codecString, "Error linking function " << name << ", error=" << GetLastError());
158     return false;
159   }
160 
161   func = (Function)p;
162 
163 #else // _WIN32
164 
165   void * p = dlsym(m_hDLL, (const char *)name);
166   if (p == NULL) {
167     PTRACE(1, m_codecString, "Error linking function " << name << ", error=" << dlerror());
168     return false;
169   }
170   func = (Function &)p;
171 
172 #endif // _WIN32
173 
174   return true;
175 }
176 
177 
178 #if PLUGINCODEC_TRACING
logCallbackFFMPEG(void * avcl,int severity,const char * fmt,va_list arg)179 static void logCallbackFFMPEG(void * avcl, int severity, const char* fmt , va_list arg)
180 {
181   int level;
182   if (severity <= AV_LOG_FATAL)
183     level = 0;
184   else if (severity <= AV_LOG_ERROR)
185     level = 1;
186   else if (severity <= AV_LOG_WARNING)
187     level = 2;
188   else if (severity <= AV_LOG_INFO)
189     level = 3;
190   else if (severity <= AV_LOG_VERBOSE)
191     level = 4;
192   else
193     level = 5;
194 
195   if (PTRACE_CHECK(level)) {
196     char buffer[512];
197     int len = vsnprintf(buffer, sizeof(buffer), fmt, arg);
198     if (len > 0) {
199       // Drop extra trailing line feed
200       --len;
201       if (buffer[len] == '\n')
202         buffer[len] = '\0';
203       // Check for bogus errors, everything works so what does this mean?
204       if (strstr(buffer, "Too many slices") == 0 && strstr(buffer, "Frame num gap") == 0) {
205         PluginCodec_LogFunctionInstance(level, __FILE__, __LINE__, "FFMPEG", buffer);
206       }
207     }
208   }
209 }
210 #endif
211 
212 
FFMPEGLibrary(AVCodecID codec)213 FFMPEGLibrary::FFMPEGLibrary(AVCodecID codec)
214 {
215   m_codec = codec;
216   if (m_codec==AV_CODEC_ID_H264)
217       snprintf( m_codecString, sizeof(m_codecString), "H264");
218   if (m_codec==AV_CODEC_ID_H263P)
219       snprintf( m_codecString, sizeof(m_codecString), "H263+");
220   if (m_codec==AV_CODEC_ID_MPEG4)
221       snprintf( m_codecString, sizeof(m_codecString), "MPEG4");
222   m_isLoadedOK = false;
223 }
224 
~FFMPEGLibrary()225 FFMPEGLibrary::~FFMPEGLibrary()
226 {
227   m_libAvcodec.Close();
228   m_libAvutil.Close();
229 }
230 
231 #define CHECK_AVUTIL(name, func) \
232       (seperateLibAvutil ? \
233         m_libAvutil.GetFunction(name,  (DynaLink::Function &)func) : \
234         m_libAvcodec.GetFunction(name, (DynaLink::Function &)func) \
235        ) \
236 
237 
Load()238 bool FFMPEGLibrary::Load()
239 {
240   WaitAndSignal m(processLock);
241   if (IsLoaded())
242     return true;
243 
244   bool seperateLibAvutil = false;
245 
246 #ifdef LIBAVCODEC_LIB_NAME
247   if (m_libAvcodec.Open(LIBAVCODEC_LIB_NAME))
248     seperateLibAvutil = true;
249   else
250 #endif
251   if (m_libAvcodec.Open("libavcodec"))
252     seperateLibAvutil = false;
253   else if (m_libAvcodec.Open("avcodec-" AV_STRINGIFY(LIBAVCODEC_VERSION_MAJOR)))
254     seperateLibAvutil = true;
255   else {
256     PTRACE(1, m_codecString, "Failed to load FFMPEG libavcodec library");
257     return false;
258   }
259 
260   if (seperateLibAvutil &&
261         !(
262 #ifdef LIBAVUTIL_LIB_NAME
263           m_libAvutil.Open(LIBAVUTIL_LIB_NAME) ||
264 #endif
265           m_libAvutil.Open("libavutil") ||
266           m_libAvutil.Open("avutil-" AV_STRINGIFY(LIBAVUTIL_VERSION_MAJOR))
267         ) ) {
268     PTRACE(1, m_codecString, "Failed to load FFMPEG libavutil library");
269     return false;
270   }
271 
272   strcpy(m_libAvcodec.m_codecString, m_codecString);
273   strcpy(m_libAvutil.m_codecString,  m_codecString);
274 
275   if (!m_libAvcodec.GetFunction("avcodec_init", (DynaLink::Function &)Favcodec_init))
276     return false;
277 
278   if (!m_libAvcodec.GetFunction("av_init_packet", (DynaLink::Function &)Fav_init_packet))
279     return false;
280 
281   if (!m_libAvcodec.GetFunction("avcodec_register_all", (DynaLink::Function &)Favcodec_register_all))
282     return false;
283 
284   if (!m_libAvcodec.GetFunction("avcodec_find_encoder", (DynaLink::Function &)Favcodec_find_encoder))
285     return false;
286 
287   if (!m_libAvcodec.GetFunction("avcodec_find_decoder", (DynaLink::Function &)Favcodec_find_decoder))
288     return false;
289 
290   if (!m_libAvcodec.GetFunction("avcodec_alloc_context", (DynaLink::Function &)Favcodec_alloc_context))
291     return false;
292 
293   if (!m_libAvcodec.GetFunction("avcodec_alloc_frame", (DynaLink::Function &)Favcodec_alloc_frame))
294     return false;
295 
296   if (!m_libAvcodec.GetFunction("avcodec_open", (DynaLink::Function &)Favcodec_open))
297     return false;
298 
299   if (!m_libAvcodec.GetFunction("avcodec_close", (DynaLink::Function &)Favcodec_close))
300     return false;
301 
302   if (!m_libAvcodec.GetFunction("avcodec_encode_video", (DynaLink::Function &)Favcodec_encode_video))
303     return false;
304 
305   if (!m_libAvcodec.GetFunction("avcodec_decode_video2", (DynaLink::Function &)Favcodec_decode_video))
306     return false;
307 
308   if (!m_libAvcodec.GetFunction("avcodec_set_dimensions", (DynaLink::Function &)Favcodec_set_dimensions))
309     return false;
310 
311   if (!CHECK_AVUTIL("av_free", Favcodec_free))
312     return false;
313 
314   if(!m_libAvcodec.GetFunction("avcodec_version", (DynaLink::Function &)Favcodec_version))
315     return false;
316 
317   if (!CHECK_AVUTIL("av_log_set_level", FAv_log_set_level))
318     return false;
319 
320   if (!CHECK_AVUTIL("av_log_set_callback", FAv_log_set_callback))
321     return false;
322 
323   // must be called before using avcodec lib
324 
325   unsigned libVer = Favcodec_version();
326   if (libVer != LIBAVCODEC_VERSION_INT) {
327     PTRACE(2, m_codecString, "Warning: compiled against libavcodec headers from version "
328            << LIBAVCODEC_VERSION_MAJOR << '.' << LIBAVCODEC_VERSION_MINOR << '.' << LIBAVCODEC_VERSION_MICRO
329            << ", loaded "
330            << (libVer >> 16) << ((libVer>>8) & 0xff) << (libVer & 0xff));
331   }
332   else {
333     PTRACE(3, m_codecString, "Loaded libavcodec version "
334            << (libVer >> 16) << ((libVer>>8) & 0xff) << (libVer & 0xff));
335   }
336 
337   Favcodec_init();
338   Favcodec_register_all ();
339 
340 #if PLUGINCODEC_TRACING
341   AvLogSetLevel(AV_LOG_DEBUG);
342   AvLogSetCallback(&logCallbackFFMPEG);
343 #endif
344 
345   m_isLoadedOK = true;
346   PTRACE(4, m_codecString, "Successfully loaded libavcodec library and verified functions");
347 
348   return true;
349 }
350 
AvcodecFindEncoder(enum AVCodecID id)351 AVCodec *FFMPEGLibrary::AvcodecFindEncoder(enum AVCodecID id)
352 {
353   return Favcodec_find_encoder(id);
354 }
355 
AvcodecFindDecoder(enum AVCodecID id)356 AVCodec *FFMPEGLibrary::AvcodecFindDecoder(enum AVCodecID id)
357 {
358   WaitAndSignal m(processLock);
359 
360   return Favcodec_find_decoder(id);
361 }
362 
AvcodecAllocContext(void)363 AVCodecContext *FFMPEGLibrary::AvcodecAllocContext(void)
364 {
365   WaitAndSignal m(processLock);
366 
367   return Favcodec_alloc_context();
368 }
369 
AvcodecAllocFrame(void)370 AVFrame *FFMPEGLibrary::AvcodecAllocFrame(void)
371 {
372   WaitAndSignal m(processLock);
373 
374   return Favcodec_alloc_frame();
375 }
376 
AvcodecOpen(AVCodecContext * ctx,AVCodec * codec,AVDictionary ** options)377 int FFMPEGLibrary::AvcodecOpen(AVCodecContext *ctx, AVCodec *codec, AVDictionary **options)
378 {
379   WaitAndSignal m(processLock);
380 
381   return Favcodec_open(ctx, codec);
382 }
383 
AvcodecClose(AVCodecContext * ctx)384 int FFMPEGLibrary::AvcodecClose(AVCodecContext *ctx)
385 {
386   WaitAndSignal m(processLock);
387 
388   return Favcodec_close(ctx);
389 }
390 
AvcodecEncodeVideo(AVCodecContext * ctx,BYTE * buf,int buf_size,const AVFrame * pict)391 int FFMPEGLibrary::AvcodecEncodeVideo(AVCodecContext *ctx, BYTE *buf, int buf_size, const AVFrame *pict)
392 {
393   int res;
394 
395   res = Favcodec_encode_video(ctx, buf, buf_size, pict);
396 
397   PTRACE(6, m_codecString, "DYNA\tEncoded into " << res << " bytes, max " << buf_size);
398   return res;
399 }
400 
AvcodecDecodeVideo(AVCodecContext * ctx,AVFrame * pict,int * got_picture_ptr,BYTE * buf,int buf_size)401 int FFMPEGLibrary::AvcodecDecodeVideo(AVCodecContext *ctx, AVFrame *pict, int *got_picture_ptr, BYTE *buf, int buf_size)
402 {
403   AVPacket avpkt;
404   Fav_init_packet(&avpkt);
405   avpkt.data = buf;
406   avpkt.size = buf_size;
407 
408   return Favcodec_decode_video(ctx, pict, got_picture_ptr, &avpkt);
409 }
410 
AvcodecFree(void * ptr)411 void FFMPEGLibrary::AvcodecFree(void * ptr)
412 {
413   WaitAndSignal m(processLock);
414 
415   Favcodec_free(ptr);
416 }
417 
AvSetDimensions(AVCodecContext * s,int width,int height)418 void FFMPEGLibrary::AvSetDimensions(AVCodecContext *s, int width, int height)
419 {
420   WaitAndSignal m(processLock);
421 
422   Favcodec_set_dimensions(s, width, height);
423 }
424 
AvLogSetLevel(int level)425 void FFMPEGLibrary::AvLogSetLevel(int level)
426 {
427   FAv_log_set_level(level);
428 }
429 
AvLogSetCallback(void (* callback)(void *,int,const char *,va_list))430 void FFMPEGLibrary::AvLogSetCallback(void (*callback)(void*, int, const char*, va_list))
431 {
432   FAv_log_set_callback(callback);
433 }
434 
IsLoaded()435 bool FFMPEGLibrary::IsLoaded()
436 {
437   return m_isLoadedOK;
438 }
439