У меня проблемы с слиянием сегментов, которые поступают из живой записи через AvcaptureSession. Во время захвата AvassetWritter использует свой делегат, чтобы сбрасывать деку на диск. Я использую эту реализацию делегата: < /p>
protocol SegmentedWriterDelegate: AnyObject, Sendable {
func onSegmentCompleted(atIndex index: UInt, url: URL)
}
final actor SegmentedWriter: NSObject, AVAssetWriterDelegate {
private let queue = DispatchSerialQueue(label: "my serial queue", qos: .utility)
nonisolated var unownedExecutor: UnownedSerialExecutor {
queue.asUnownedSerialExecutor()
}
public static var sessionDir: String {
"LastRecordingSession"
}
public static var outputDir: URL {
URL.documentsDirectory.appending(path: sessionDir, directoryHint: .isDirectory)
}
public static var fileFormat: String {
"segment_%.4i.mp4"
}
private var initializationData = Data()
private var segmentIndex: UInt = 0
private let fileManager: FileManager
private weak var delegate: SegmentedWriterDelegate?
init(fileManager: FileManager = .default) {
self.fileManager = fileManager
try? fileManager.createDirectory(at: SegmentedWriter.outputDir, withIntermediateDirectories: true)
}
func observe(using delegate: SegmentedWriterDelegate) {
self.delegate = delegate
}
nonisolated func assetWriter(
_ writer: AVAssetWriter,
didOutputSegmentData segmentData: Data,
segmentType: AVAssetSegmentType
) {
queue.sync {
assumeIsolated {
$0.onNewSegment(segmentData: segmentData, segmentType: segmentType)
}
}
}
private func onNewSegment(segmentData: Data, segmentType: AVAssetSegmentType) {
logger.debug("SR new segment \(segmentType.rawValue)")
switch segmentType {
case .initialization:
initializationData = segmentData
case .separable:
let fileName = String(format: Self.fileFormat, segmentIndex)
let fileURL = Self.outputDir.appendingPathComponent(fileName)
let mp4Data = initializationData + segmentData
try? mp4Data.write(to: fileURL)
delegate?.onSegmentCompleted(atIndex: segmentIndex, url: fileURL)
segmentIndex += 1
@unknown default:
break
}
}
}
< /code>
Таким образом, результатом являются сегменты в некотором каталоге. Теперь в конце сеанса записи мне нужно объединить все эти файлы в одну композицию и экспортировать его в один файл MP4. Я знаю, что могу записать один файл прямо из AvcaptureSession, но эти сегменты необходимы в другом процессе. Пока я думаю, как объединить все эти сегменты без каких -либо аудиоприликов/сокращений, потому что время от времени решение ниже клипов при объединении точек, когда сегменты объединяются. Вот что у меня есть до сих пор .. < /p>
Логика для создания композиции < /p>
private func _run() async throws -> AVComposition {
let segments = try getSegmentURLs(from: segmentsDirectory, order: .forward)
guard segments.count > 0 else {
throw SegmentComposerError.noSegments
}
let composition = AVMutableComposition()
let options: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
var cursorTime: CMTime = .zero
for segmentURL in segments {
let segment: AVAsset = AVURLAsset(url: segmentURL, options: options)
let segmentDuration = try await segment.load(.duration)
print(">>> segment \(segmentURL.lastPathComponent) - duration: \(segmentDuration)")
let segmentTimeRange = CMTimeRange(start: .zero, duration: segmentDuration)
try await composition.insertTimeRange(segmentTimeRange, of: segment, at: cursorTime)
cursorTime = CMTimeAdd(cursorTime, segmentDuration)
}
return composition
}
< /code>
Чего я не уверен, что во время создания этой композиции оператор печати выводит длительность сегментов, и эти сегменты имеют разные временные рамки. Как это, например: < /p>
>>> segment segment_0000.mp4 - duration: CMTime(value: 859, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0001.mp4 - duration: CMTime(value: 3581, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0002.mp4 - duration: CMTime(value: 264600, timescale: 44100, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0003.mp4 - duration: CMTime(value: 3601, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0004.mp4 - duration: CMTime(value: 264600, timescale: 44100, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0005.mp4 - duration: CMTime(value: 3601, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0006.mp4 - duration: CMTime(value: 1180, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
< /code>
Так что иногда он составляет 600, а иногда он равен скорости дискретизации аудио. Аспиратор, который создает эти сегменты, настроен таким образом: < /p>
protocol WriterCreator: Sendable {
func createWriter(delegate: AVAssetWriterDelegate) -> AVAssetWriter
}
struct SegmentedWriterCreator: WriterCreator {
/// Default value 6 seconds
static let defaultSegmentInterval = CMTime(value: 6, timescale: 1)
private let segmentInterval: CMTime
init(segmentInterval: CMTime = SegmentedWriterCreator.defaultSegmentInterval) {
self.segmentInterval = segmentInterval
}
func createWriter(delegate: AVAssetWriterDelegate) -> AVAssetWriter {
let writer = AVAssetWriter(contentType: .mpeg4Movie)
writer.outputFileTypeProfile = .mpeg4AppleHLS
writer.preferredOutputSegmentInterval = segmentInterval
writer.initialSegmentStartTime = .zero
writer.delegate = delegate
return writer
}
}
< /code>
И вот как я пишу эту композицию на диск. < /p>
class AssetExporter: AsyncRunner {
enum AssetExporterError: Swift.Error {
case failed(reason: String)
}
private let asset: AVAsset
private let presetName: String
private var exportSession: AVAssetExportSession?
private let outputURL: URL
init(_ asset: AVComposition, toURL outputURL: URL, presetName: String = AVAssetExportPresetPassthrough) {
self.asset = asset
self.outputURL = outputURL
self.presetName = presetName
}
@discardableResult
func run() async -> Result {
do {
let url = try await _run()
return .success(url)
} catch {
return .failure(error)
}
}
private func _run() async throws -> URL {
self.exportSession = AVAssetExportSession(asset: asset, presetName: presetName)
guard let exportSession = self.exportSession else {
throw AssetExporterError.failed(reason: "Can not create export session")
}
if #available(iOS 18, *) {
try await exportSession.export(to: outputURL, as: .mp4)
} else {
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
await exportSession.export()
}
return outputURL
}
}
< /code>
Я не уверен, как диагностировать, где проблема в этом потоке. Это во время захвата и автора активов или его входной конфигурации. Это в сегментере, который пишет эти сегменты на диск, или, может быть, в композиторе, где создается AvComposition. У меня проблема заключается в том, что окончательный выходной звук MP4 -файла является заметной обрезкой в точках слияния между образцами. Не всегда, иногда это не клипов, но в большинстве случаев это зажигает. Любая помощь или идеи, что проверить, приветствуется. Может быть, мне следует создать композиционные треки отдельно - видео только и затем аудио -трек?
Подробнее здесь: https://stackoverflow.com/questions/797 ... omposition
Правильно слияние сегментов MP4 от AvassetWritter с использованием AvmutableCoposion ⇐ IOS
Программируем под IOS
1753427750
Anonymous
У меня проблемы с слиянием сегментов, которые поступают из живой записи через AvcaptureSession. Во время захвата AvassetWritter использует свой делегат, чтобы сбрасывать деку на диск. Я использую эту реализацию делегата: < /p>
protocol SegmentedWriterDelegate: AnyObject, Sendable {
func onSegmentCompleted(atIndex index: UInt, url: URL)
}
final actor SegmentedWriter: NSObject, AVAssetWriterDelegate {
private let queue = DispatchSerialQueue(label: "my serial queue", qos: .utility)
nonisolated var unownedExecutor: UnownedSerialExecutor {
queue.asUnownedSerialExecutor()
}
public static var sessionDir: String {
"LastRecordingSession"
}
public static var outputDir: URL {
URL.documentsDirectory.appending(path: sessionDir, directoryHint: .isDirectory)
}
public static var fileFormat: String {
"segment_%.4i.mp4"
}
private var initializationData = Data()
private var segmentIndex: UInt = 0
private let fileManager: FileManager
private weak var delegate: SegmentedWriterDelegate?
init(fileManager: FileManager = .default) {
self.fileManager = fileManager
try? fileManager.createDirectory(at: SegmentedWriter.outputDir, withIntermediateDirectories: true)
}
func observe(using delegate: SegmentedWriterDelegate) {
self.delegate = delegate
}
nonisolated func assetWriter(
_ writer: AVAssetWriter,
didOutputSegmentData segmentData: Data,
segmentType: AVAssetSegmentType
) {
queue.sync {
assumeIsolated {
$0.onNewSegment(segmentData: segmentData, segmentType: segmentType)
}
}
}
private func onNewSegment(segmentData: Data, segmentType: AVAssetSegmentType) {
logger.debug("SR new segment \(segmentType.rawValue)")
switch segmentType {
case .initialization:
initializationData = segmentData
case .separable:
let fileName = String(format: Self.fileFormat, segmentIndex)
let fileURL = Self.outputDir.appendingPathComponent(fileName)
let mp4Data = initializationData + segmentData
try? mp4Data.write(to: fileURL)
delegate?.onSegmentCompleted(atIndex: segmentIndex, url: fileURL)
segmentIndex += 1
@unknown default:
break
}
}
}
< /code>
Таким образом, результатом являются сегменты в некотором каталоге. Теперь в конце сеанса записи мне нужно объединить все эти файлы в одну композицию и экспортировать его в один файл MP4. Я знаю, что могу записать один файл прямо из AvcaptureSession, но эти сегменты необходимы в другом процессе. Пока я думаю, как объединить все эти сегменты без каких -либо аудиоприликов/сокращений, потому что время от времени решение ниже клипов при объединении точек, когда сегменты объединяются. Вот что у меня есть до сих пор .. < /p>
Логика для создания композиции < /p>
private func _run() async throws -> AVComposition {
let segments = try getSegmentURLs(from: segmentsDirectory, order: .forward)
guard segments.count > 0 else {
throw SegmentComposerError.noSegments
}
let composition = AVMutableComposition()
let options: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
var cursorTime: CMTime = .zero
for segmentURL in segments {
let segment: AVAsset = AVURLAsset(url: segmentURL, options: options)
let segmentDuration = try await segment.load(.duration)
print(">>> segment \(segmentURL.lastPathComponent) - duration: \(segmentDuration)")
let segmentTimeRange = CMTimeRange(start: .zero, duration: segmentDuration)
try await composition.insertTimeRange(segmentTimeRange, of: segment, at: cursorTime)
cursorTime = CMTimeAdd(cursorTime, segmentDuration)
}
return composition
}
< /code>
Чего я не уверен, что во время создания этой композиции оператор печати выводит длительность сегментов, и эти сегменты имеют разные временные рамки. Как это, например: < /p>
>>> segment segment_0000.mp4 - duration: CMTime(value: 859, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0001.mp4 - duration: CMTime(value: 3581, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0002.mp4 - duration: CMTime(value: 264600, timescale: 44100, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0003.mp4 - duration: CMTime(value: 3601, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0004.mp4 - duration: CMTime(value: 264600, timescale: 44100, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0005.mp4 - duration: CMTime(value: 3601, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
>>> segment segment_0006.mp4 - duration: CMTime(value: 1180, timescale: 600, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)
< /code>
Так что иногда он составляет 600, а иногда он равен скорости дискретизации аудио. Аспиратор, который создает эти сегменты, настроен таким образом: < /p>
protocol WriterCreator: Sendable {
func createWriter(delegate: AVAssetWriterDelegate) -> AVAssetWriter
}
struct SegmentedWriterCreator: WriterCreator {
/// Default value 6 seconds
static let defaultSegmentInterval = CMTime(value: 6, timescale: 1)
private let segmentInterval: CMTime
init(segmentInterval: CMTime = SegmentedWriterCreator.defaultSegmentInterval) {
self.segmentInterval = segmentInterval
}
func createWriter(delegate: AVAssetWriterDelegate) -> AVAssetWriter {
let writer = AVAssetWriter(contentType: .mpeg4Movie)
writer.outputFileTypeProfile = .mpeg4AppleHLS
writer.preferredOutputSegmentInterval = segmentInterval
writer.initialSegmentStartTime = .zero
writer.delegate = delegate
return writer
}
}
< /code>
И вот как я пишу эту композицию на диск. < /p>
class AssetExporter: AsyncRunner {
enum AssetExporterError: Swift.Error {
case failed(reason: String)
}
private let asset: AVAsset
private let presetName: String
private var exportSession: AVAssetExportSession?
private let outputURL: URL
init(_ asset: AVComposition, toURL outputURL: URL, presetName: String = AVAssetExportPresetPassthrough) {
self.asset = asset
self.outputURL = outputURL
self.presetName = presetName
}
@discardableResult
func run() async -> Result {
do {
let url = try await _run()
return .success(url)
} catch {
return .failure(error)
}
}
private func _run() async throws -> URL {
self.exportSession = AVAssetExportSession(asset: asset, presetName: presetName)
guard let exportSession = self.exportSession else {
throw AssetExporterError.failed(reason: "Can not create export session")
}
if #available(iOS 18, *) {
try await exportSession.export(to: outputURL, as: .mp4)
} else {
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
await exportSession.export()
}
return outputURL
}
}
< /code>
Я не уверен, как диагностировать, где проблема в этом потоке. Это во время захвата и автора активов или его входной конфигурации. Это в сегментере, который пишет эти сегменты на диск, или, может быть, в композиторе, где создается AvComposition. У меня проблема заключается в том, что окончательный выходной звук MP4 -файла является заметной обрезкой в точках слияния между образцами. Не всегда, иногда это не клипов, но в большинстве случаев это зажигает. Любая помощь или идеи, что проверить, приветствуется. Может быть, мне следует создать композиционные треки отдельно - видео только и затем аудио -трек?
Подробнее здесь: [url]https://stackoverflow.com/questions/79714334/properly-merge-mp4-segments-from-avassetwritter-using-avmutablecomposition[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия