Skip to content

Commit

Permalink
Merge pull request media-kit#447 from birros/fix/darwin-memory-leaks
Browse files Browse the repository at this point in the history
fix(darwin): fix memory leaks
  • Loading branch information
birros authored Sep 5, 2023
2 parents 8fd22cc + 68198f9 commit e12521f
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@
public protocol ResizableTextureProtocol: NSObject, FlutterTexture {
func resize(_ size: CGSize)
func render(_ size: CGSize)
func dispose()
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ public class SafeResizableTexture:
}
}

public func dispose() {
return locked {
return child.dispose()
}
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
return child.copyPixelBuffer()
}
Expand Down
10 changes: 5 additions & 5 deletions media_kit_video/common/darwin/Classes/plugin/TextureSW.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class TextureSW: NSObject, FlutterTexture, ResizableTextureProtocol {
}
}

deinit {
disposePixelBuffer()
disposeMPV()
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
let textureContext = textureContexts.current
if textureContext == nil {
Expand All @@ -38,11 +43,6 @@ public class TextureSW: NSObject, FlutterTexture, ResizableTextureProtocol {
return Unmanaged.passRetained(textureContext!.pixelBuffer)
}

public func dispose() {
disposePixelBuffer()
disposeMPV()
}

private func initMPV() {
let api = UnsafeMutableRawPointer(
mutating: (MPV_RENDER_API_TYPE_SW as NSString).utf8String
Expand Down
36 changes: 21 additions & 15 deletions media_kit_video/common/darwin/Classes/plugin/VideoOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,20 @@ public class VideoOutput: NSObject {
}
}

deinit {
worker.cancel()

disposed = true
disposeTextureId()
}

public func setSize(width: Int64?, height: Int64?) {
worker.enqueue {
self.width = width
self.height = height
}
}

public func dispose() {
worker.enqueue {
self._dispose()
}
}

private func _dispose() {
disposed = true

disposeTextureId()
texture.dispose()
}

private func _init() {
let enableHardwareAcceleration =
VideoOutput.isSimulator ? false : enableHardwareAcceleration
Expand All @@ -97,14 +91,26 @@ public class VideoOutput: NSObject {
texture = SafeResizableTexture(
TextureHW(
handle: handle,
updateCallback: updateCallback
// Use `weak self` to prevent memory leaks
updateCallback: { [weak self]() in
guard let that = self else {
return
}
that.updateCallback()
}
)
)
} else {
texture = SafeResizableTexture(
TextureSW(
handle: handle,
updateCallback: updateCallback
// Use `weak self` to prevent memory leaks
updateCallback: { [weak self]() in
guard let that = self else {
return
}
that.updateCallback()
}
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ public class VideoOutputManager: NSObject {
return
}

videoOutput!.dispose()
self.videoOutputs[handle] = nil
}
}
35 changes: 33 additions & 2 deletions media_kit_video/common/darwin/Classes/plugin/Worker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ class Worker {
private let lock = NSRecursiveLock()
private var thread: Thread!
private var queue = [Job]()
private var canceled: Bool = false

init() {
thread = Thread(block: loop)
thread.start()
}

public func cancel() {
signalCancel()
thread.cancel()
}

public func enqueue(_ job: @escaping Job) {
locked {
queue.append(job)
Expand All @@ -24,14 +30,39 @@ class Worker {
while true {
semaphore.wait()

let job = locked {
queue.removeFirst()
if isCanceled() {
return
}

let job = getFirstJob()
job()
}
}

private func signalCancel() {
locked {
canceled = true
}

semaphore.signal()
}

private func isCanceled() -> Bool {
let c = locked {
canceled
}

return c
}

private func getFirstJob() -> Job {
let job = locked {
queue.removeFirst()
}

return job
}

private func locked<T>(do block: () -> T) -> T {
lock.lock()
defer {
Expand Down
20 changes: 10 additions & 10 deletions media_kit_video/ios/Classes/plugin/TextureHW.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@ public class TextureHW: NSObject, FlutterTexture, ResizableTextureProtocol {
self.initMPV()
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
let textureContext = textureContexts.current
if textureContext == nil {
return nil
}

return Unmanaged.passRetained(textureContext!.pixelBuffer)
}

public func dispose() {
deinit {
disposePixelBuffer()
disposeMPV()
OpenGLESHelpers.deleteTextureCache(textureCache)
Expand All @@ -49,6 +40,15 @@ public class TextureHW: NSObject, FlutterTexture, ResizableTextureProtocol {
OpenGLESHelpers.deleteContext(context)
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
let textureContext = textureContexts.current
if textureContext == nil {
return nil
}

return Unmanaged.passRetained(textureContext!.pixelBuffer)
}

private func initMPV() {
EAGLContext.setCurrent(context)
defer {
Expand Down
4 changes: 4 additions & 0 deletions media_kit_video/ios/Classes/plugin/gles/OpenGLESHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ public class OpenGLESHelpers {
// automatically memory managed
}

// BUG: `glDeleteTextures` does not release `CVOpenGLESTexture`.
// `CVOpenGLESTextureCache` retains a direct or indirect reference to
// `IOSurface`, which causes a memory leak until `CVOpenGLESTextureCache` is
// released.
static public func deleteTexture(
_ context: EAGLContext,
_ texture: CVOpenGLESTexture
Expand Down
20 changes: 10 additions & 10 deletions media_kit_video/macos/Classes/plugin/TextureHW.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,7 @@ public class TextureHW: NSObject, FlutterTexture, ResizableTextureProtocol {
self.initMPV()
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
let textureContext = textureContexts.current
if textureContext == nil {
return nil
}

return Unmanaged.passRetained(textureContext!.pixelBuffer)
}

public func dispose() {
deinit {
disposePixelBuffer()
disposeMPV()
OpenGLHelpers.deleteTextureCache(textureCache)
Expand All @@ -53,6 +44,15 @@ public class TextureHW: NSObject, FlutterTexture, ResizableTextureProtocol {
OpenGLHelpers.deleteContext(context)
}

public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
let textureContext = textureContexts.current
if textureContext == nil {
return nil
}

return Unmanaged.passRetained(textureContext!.pixelBuffer)
}

private func initMPV() {
CGLSetCurrentContext(context)
defer {
Expand Down
4 changes: 4 additions & 0 deletions media_kit_video/macos/Classes/plugin/gl/OpenGLHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ public class OpenGLHelpers {
// automatically memory managed
}

// BUG: `glDeleteTextures` does not release `CVOpenGLTexture`.
// `CVOpenGLTextureCache` retains a direct or indirect reference to
// `IOSurface`, which causes a memory leak until `CVOpenGLTextureCache` is
// released.
static public func deleteTexture(
_ context: CGLContextObj,
_ texture: CVOpenGLTexture
Expand Down

0 comments on commit e12521f

Please sign in to comment.