I have an audio player app written in Swift for macOS, and it uses a static binary build of FFmpeg's command line executable tool, but does not link to its code statically or dynamically. My app invokes the bundled FFmpeg binary as an external (child) process (as if it were installed separately on my machine and I were invoking it from the command line). So, this implies that ffmpeg and my app do not share memory space (this seems important from a legal standpoint).
From the Apple documentation for the Process class (used to invoke FFmpeg within my app):
It's as if my app has a companion app bundled with it, so that my users don't need to install FFmpeg separately on their own (which they could do). My app simply invokes it as a black box without having any knowledge of its APIs or linking with any of its library files.
Now, I have done some research and understand that FFmpeg is licensed under LGPL (see: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). And, the terms of LGPL are somewhat clear in the cases of "static linking" and "dynamic linking" of the library (in my case, FFmpeg) with app code. However, my scenario, it seems, is neither of the two, and there isn't much documentation out there addressing it.
I have currently not licensed my own app in any way. It (and its source code) is available for free download on GitHub. So, if I include FFmpeg in the way described above, and demonstrated below, what must I do to comply with the LGPL license terms ? Thanks.
To demonstrate my usage and bundling of FFmpeg, here is the (simplified for this discussion) essence of the app code that invokes the bundled FFmpeg binary, and a snapshot image of how I include the ffmpeg binary in my Xcode project: (Swift 4.2.1)
From the Apple documentation for the Process class (used to invoke FFmpeg within my app):
Using the Process class, your program can run another program as a subprocess and can monitor that program’s execution. A Process object creates a separate executable entity; it differs from Thread in that it does not share memory space with the process that creates it.
It's as if my app has a companion app bundled with it, so that my users don't need to install FFmpeg separately on their own (which they could do). My app simply invokes it as a black box without having any knowledge of its APIs or linking with any of its library files.
Now, I have done some research and understand that FFmpeg is licensed under LGPL (see: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). And, the terms of LGPL are somewhat clear in the cases of "static linking" and "dynamic linking" of the library (in my case, FFmpeg) with app code. However, my scenario, it seems, is neither of the two, and there isn't much documentation out there addressing it.
I have currently not licensed my own app in any way. It (and its source code) is available for free download on GitHub. So, if I include FFmpeg in the way described above, and demonstrated below, what must I do to comply with the LGPL license terms ? Thanks.
To demonstrate my usage and bundling of FFmpeg, here is the (simplified for this discussion) essence of the app code that invokes the bundled FFmpeg binary, and a snapshot image of how I include the ffmpeg binary in my Xcode project: (Swift 4.2.1)
Code:
import Foundation
class MyFFMpegWrapper {
// "ffmpeg" refers to a static binary build of FFmpeg. It is included as a bundled resource in this app.
static let ffmpegBinaryPath: String = Bundle.main.url(forResource: "ffmpeg", withExtension: "")!.path
// Transcodes an input audio file (e.g. WMA / OGG) to a supported output file (e.g. MP3) using FFmpeg
static func transcode(_ inputFile: URL) -> URL? {
let outputFile = URL(fileURLWithPath: inputFile.path + "-transcoded.mp3")
// This is equivalent to running "ffmpeg -i inputFile.ogg outputFile.mp3" on the command line
let result = runCommand(cmd: ffmpegBinaryPath, args: "-i", inputFile.path, outputFile.path)
if result == 0 {
// Success !
return outputFile
}
// Failed
return nil
}
// Helper function that runs a command as a child process and returns the result (exit code) of command execution
private static func runCommand(cmd : String, args : String...) -> Int32 {
let task = Process()
task.launchPath = cmd
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
}
Last edited: