ffmpeg-kit
ffmpeg-kit copied to clipboard
Custom-Built ffmpeg-kit-react-native: NativeModule === null - why?
Hi, I try to build a custom version of the ffmpeg-kit-react-native package on macOS, for iOS devices as target.
I did the following steps:
- forked your repo
brew install autoconf automake libtool pkg-config curl git doxygen nas./ios.sh -xln -s ~/git/ffmpeg-kit/prebuilt/bundle-apple-xcframework-ios ~/git/ffmpeg-kit/bundle-apple-xcframework-ios- change ffmpeg-kit-react-native.prodspec to (according to https://github.com/arthenica/ffmpeg-kit/issues/537#issuecomment-1873589426)
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = package["name"]
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.authors = package["author"]
s.platform = :ios
s.requires_arc = true
s.static_framework = true
s.source = { :path => '/~/git/ffmpeg-kit/bundle-apple-xcframework-ios' }
# https://github.com/arthenica/ffmpeg-kit/issues/537#issuecomment-1873589426
s.source_files = 'FFmpegKitReactNativeModule.m', 'FFmpegKitReactNativeModule.h', 'bundle-apple-xcframework-ios/**/*.{h,m,swift}'
s.vendored_frameworks = 'bundle-apple-xcframework-ios/ffmpegkit.xcframework',
'bundle-apple-xcframework-ios/libavcodec.xcframework',
'bundle-apple-xcframework-ios/libavdevice.xcframework',
'bundle-apple-xcframework-ios/libavfilter.xcframework',
'bundle-apple-xcframework-ios/libavformat.xcframework',
'bundle-apple-xcframework-ios/libavutil.xcframework',
'bundle-apple-xcframework-ios/libswresample.xcframework',
'bundle-apple-xcframework-ios/libswscale.xcframework'
s.ios.deployment_target = '12.1'
s.dependency "React-Core"
- changing package.json of my target app to use my local build:
"ffmpeg-kit-react-native": "file:/~/git/ffmpeg-kit/react-native"
building works fine, however, when I run the app, the first time an ffmpeg function is begin used, I get the error Invariant Violation, message: new NativeEventEmitter()requires a non-null argument., stack: Invariant Violation:new NativeEventEmitter() requires a non-null argument.
I tracked down the exact location of the error:
It seems like the FfmpegKitReactNativeModule.m is not being loaded. But if I look in the node_modules folder of the target app, it is all there:
Can anybody give me a hint on what could be the problem? Native Modules are not really my core competence, so I'm a bit lost here.
I was running into the same issue. Started to go down another route now. Would love to know the answer to this
@cvincentcoleman which other route is there?
@vpume - I'm working on figuring this out now too.
The native library is not linked because the pods are not installing properly.
The root .podspec should have a link to the source.
It doesn't matter where that zip is, it can be anywhere from a vps, google drive, amazon s3 so make sure you create a root .podspec that points to that zip:
here's my root podspec in my private ffmpeg-kit clone, the zip file is a plain zip of the bundle-apple-xcframework-ios folder found inside the prebuilt.
require "json"
Pod::Spec.new do |s|
s.name = "APP_NAME-ffmpeg-kit-ios-video" # Match your file name
s.version = "6.0.3"
s.summary = "FFmpeg Kit iOS Https Shared Framework"
s.description = "Includes FFmpeg with gmp and gnutls libraries enabled."
s.homepage = "https://github.com/<github name or company>ffmpeg-kit"
s.license = { :type => "LGPL-3.0", :text => "Licensed under the LGPL-3.0" }
s.authors = "<author_name>"
s.source = {
:http => "https://<your server url>/ffmpeg-kit-video-6.0-ios-xcframework.zip"
}
s.platform = :ios
s.ios.deployment_target = "15.1"
s.requires_arc = true
s.static_framework = true
s.libraries = [
"z",
"bz2",
"c++",
"iconv"
]
s.frameworks = [
"AudioToolbox",
"AVFoundation",
"CoreMedia",
"VideoToolbox"
]
s.pod_target_xcconfig = {
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64'
}
s.user_target_xcconfig = {
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64'
}
s.vendored_frameworks = [
"bundle-apple-xcframework-ios/ffmpegkit.xcframework",
"bundle-apple-xcframework-ios/libavcodec.xcframework",
"bundle-apple-xcframework-ios/libavdevice.xcframework",
"bundle-apple-xcframework-ios/libavfilter.xcframework",
"bundle-apple-xcframework-ios/libavformat.xcframework",
"bundle-apple-xcframework-ios/libavutil.xcframework",
"bundle-apple-xcframework-ios/libswresample.xcframework",
"bundle-apple-xcframework-ios/libswscale.xcframework",
]
end
create a repo named CocoaPodsSpec on github and make it private.
then on your terminal: pod repo add NAME_IT_WHATEVER [email protected]:<github user name of company>/CocoaPodsSpecs.git
followed by:
pod repo push NAME_IT_WHATEVER APP_NAME-ffmpeg-kit-ios-video.podspec
then create another repo with only the react-native folder from ffmpeg-kit repo, put that on the .podspec: (name of the repo is ffmpeg-kit-react-native)
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "ffmpeg-kit-react-native"
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.authors = package["author"]
s.platform = :ios
s.requires_arc = true
s.static_framework = true
s.source = { :git => "[email protected]:<github username or company>/ffmpeg-kit-react-native.git", :tag => "react.native.v#{s.version}-<your app name>" }
s.default_subspec = 'video'
s.dependency "React-Core"
s.subspec 'video' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'APP_NAME-ffmpeg-kit-ios-video', "6.0.3"
ss.ios.deployment_target = '15.1'
end
end
Create a release and tag it, upload a zip on your ffmpeg-kit-react-native repo that follows the naming convention above:
git tag -a react.native.v6.0.3-<your app name> -m "v6.0.3-<<your app name>>"
then git push
on the app project, yarn add the ffmpeg-kit-react-native from above eg. yarn add ffmpeg-kit-react-native@"ssh://[email protected]/<github username or company>/ffmpeg-kit-react-native"
open the ios/Podfile and at the very top add your private repo holding the CocoaPodsSpec:
source '[email protected]:<github username or company>/CocoaPodsSpecs.git'
source 'https://cdn.cocoapods.org/'
pod install and voila, all set.
ps: you might need to add the github package registry authtoken:
$ nano ~/.npmrc
and add these lines:
//npm.pkg.github.com/:_authToken=<YOUR_PERSONAL_ACCESS_TOKEN>
github username or company:registry=https://npm.pkg.github.com/
For anyone using expo.
Follow the steps above but at the end instead of updating the ios/Podfile you'll need to create a plugin for updating the Expo Podfile at build.
import path from 'path'
import fs from 'fs'
import {
withDangerousMod,
ConfigPlugin,
} from '@expo/config-plugins'
const CUSTOM_SOURCES = `source 'https://cdn.cocoapods.org/'
source '[email protected]:<Your GitHub>/CocoaPodsSpec.git'
`
const customFfmpegPodSource: ConfigPlugin = (config) => {
return withDangerousMod(config, [
'ios',
async (config) => {
const podfilePath = path.join(
config.modRequest.projectRoot,
'ios',
'Podfile',
)
if (fs.existsSync(podfilePath)) {
let contents = fs.readFileSync(podfilePath, 'utf-8')
// Only inject if both sources are not already present
if (
!contents.includes(
"source '[email protected]:<Your GitHub>/CocoaPodsSpec.git'",
)
) {
contents = `${CUSTOM_SOURCES}${contents}`
fs.writeFileSync(podfilePath, contents)
console.log('✅ Injected CocoaPods sources into Podfile.')
} else {
console.log('ℹ️ CocoaPods sources already present.')
}
} else {
console.warn(`⚠️ Podfile not found at ${podfilePath}`)
}
return config
},
])
}
Import this or put it in app.config.ts and then wrap your config with withPlugins like this:
import {
withPlugins,
} from '@expo/config-plugins'
export default ({ config }: ConfigContext): ExpoConfig =>
withPlugins({ ...your config object... }, [customFfmpegPodSource])