Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SimulatorVideoRecorder #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,18 @@ let package = Package(
.product(name: "Starscream", package: "Starscream"),
.product(name: "SynchronousWaiter", package: "CommandLineToolkit"),
"EmceePluginModels",
"SimulatorVideoRecorder",
]
),
.target(
name: "EmceePluginModels"
)
),
.target(
name: "SimulatorVideoRecorder",
dependencies: [
.product(name: "PathLib", package: "CommandLineToolkit"),
.product(name: "ProcessController", package: "CommandLineToolkit"),
]
),
]
)
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,8 @@ exit(try main())
```

`Plugin` class will automatically stop streaming events when web socket gets disconnected, allowing `join()` method to return.

## Helpers

You can use `SimulatorVideoRecorder` to capture the video of the simulator.
You should cancel all ongoing recording after you receive `didRun` event.
Binary file removed Sources/.DS_Store
Binary file not shown.
14 changes: 14 additions & 0 deletions Sources/SimulatorVideoRecorder/CancellableRecording.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) Avito Tech LLC
*/

import Foundation
import PathLib

public protocol CancellableRecording {
/// Stops recording and returns a path to a file where video is stored.
func stopRecording() -> AbsolutePath

/// Cancels recording and does not write any data to the file. Thus, does not return any path.
func cancelRecording()
}
36 changes: 36 additions & 0 deletions Sources/SimulatorVideoRecorder/CancellableRecordingImpl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) Avito Tech LLC
*/

import Foundation
import PathLib
import ProcessController

class CancellableRecordingImpl: CancellableRecording {
private let outputPath: AbsolutePath
private let recordingProcess: ProcessController

public init(
outputPath: AbsolutePath,
recordingProcess: ProcessController
) {
self.outputPath = outputPath
self.recordingProcess = recordingProcess
}

func stopRecording() -> AbsolutePath {
recordingProcess.interruptAndForceKillIfNeeded {}
recordingProcess.waitForProcessToDie()
return outputPath
}

func cancelRecording() {
recordingProcess.terminateAndForceKillIfNeeded()
recordingProcess.waitForProcessToDie()

let fileManager = FileManager()
if fileManager.fileExists(atPath: outputPath.pathString) {
try? fileManager.removeItem(atPath: outputPath.pathString)
}
}
}
55 changes: 55 additions & 0 deletions Sources/SimulatorVideoRecorder/SimulatorVideoRecorder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) Avito Tech LLC
*/

import Foundation
import PathLib
import ProcessController

public final class SimulatorVideoRecorder {
public enum CodecType: String {
case h264
case hevc
}

private let processControllerProvider: ProcessControllerProvider
private let simulatorUuid: String
private let simulatorSetPath: AbsolutePath

public init(
processControllerProvider: ProcessControllerProvider,
simulatorUuid: String,
simulatorSetPath: AbsolutePath
) {
self.processControllerProvider = processControllerProvider
self.simulatorUuid = simulatorUuid
self.simulatorSetPath = simulatorSetPath
}

public func startRecording(
codecType: CodecType,
outputPath: AbsolutePath
) throws -> CancellableRecording {
let processController = try processControllerProvider.createProcessController(
subprocess: Subprocess(
arguments: [
"/usr/bin/xcrun",
"simctl",
"--set",
simulatorSetPath,
"io",
simulatorUuid,
"recordVideo",
"--codec=\(codecType.rawValue)",
outputPath
]
)
)
try processController.start()

return CancellableRecordingImpl(
outputPath: outputPath,
recordingProcess: processController
)
}
}