ReplayKit ile yayın yaparken CMSampleBuffer'ı .mov biçimine dönüştürün
Aug 19 2020
Lütfen bana kodumda neyin yanlış olabileceğini söyle. Ekranımı dosyaya (newFile.mov) yayınlamaya ve ardından bazı verilerle birlikte sunucuya göndermeye çalışıyorum, ancak kaydetmiyor ve yakalamayı durdurduktan sonra dosya çeliği boş.
Bu, yayını yakalayan ve aldığım ve sunucuya gönderdiğim gruptaki dosyaya kaydeden BExtensionUpload kodum
import ReplayKit
class SampleHandler: RPBroadcastSampleHandler {
let appIdentifier = "com.group.CY"
var videoWriterInput: AVAssetWriterInput!
var audioWriterInput: AVAssetWriterInput!
var videoWriter: AVAssetWriter!
var sessionAtSourceTime: CMTime!
var outputFileLocation: URL?
override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
do {
outputFileLocation = videoFileLocation()
videoWriter = try AVAssetWriter(outputURL: outputFileLocation!, fileType: AVFileType.mov)
videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: [
AVVideoCodecKey : AVVideoCodecType.h264,
AVVideoWidthKey : 720,
AVVideoHeightKey : 1280,
AVVideoCompressionPropertiesKey : [
AVVideoAverageBitRateKey : 2300000,
],
])
videoWriterInput.expectsMediaDataInRealTime = true
if videoWriter.canAdd(videoWriterInput) {
videoWriter.add(videoWriterInput)
} else {
print("no input added")
}
audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
audioWriterInput.expectsMediaDataInRealTime = true
if videoWriter.canAdd(audioWriterInput!) {
videoWriter.add(audioWriterInput!)
}
videoWriter.startWriting()
} catch let error {
debugPrint(error.localizedDescription)
}
}
override func broadcastFinished() {
super.broadcastFinished()
videoWriterInput.markAsFinished()
videoWriter.finishWriting {
()
}
}
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
super.processSampleBuffer(sampleBuffer, with: sampleBufferType)
if writable,
sessionAtSourceTime == nil {
sessionAtSourceTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
videoWriter.startSession(atSourceTime: sessionAtSourceTime!)
}
switch sampleBufferType {
case .video:
if videoWriterInput.isReadyForMoreMediaData {
videoWriterInput.append(sampleBuffer)
}
case .audioApp:
if audioWriterInput.isReadyForMoreMediaData {
audioWriterInput?.append(sampleBuffer)
}
case .audioMic:
print("mic")
@unknown default:
print("unknown")
}
}
func videoFileLocation() -> URL? {
let fileManager = FileManager.default
do {
if let container = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appIdentifier) {
let videoContainer = container.appendingPathComponent("Video")
try? fileManager.createDirectory(at: videoContainer, withIntermediateDirectories: false, attributes: nil)
let videoOutputUrl = videoContainer.appendingPathComponent("newFile").appendingPathExtension("mov")
if fileManager.fileExists(atPath: videoOutputUrl.path) {
try fileManager.removeItem(at: videoOutputUrl)
}
fileManager.createFile(atPath: videoOutputUrl.path, contents: nil, attributes: nil)
return videoOutputUrl
}
} catch {
print(error)
}
return nil
}
}
Yanıtlar
1 AntonDanilov Aug 27 2020 at 07:00
Bu kod benim için çalışıyor, ancak uygulama gruplarındaki dizinimin yanı sıra PHPhotoLibrary'yi kullanmak zorunda kaldım. Uygulama grupları dizini dosya oluşturmanın tek yolu değil, istediğiniz herhangi bir dizini kullanabilirsiniz. Umarım birine yardımcı olabilir.
import ReplayKit
import Photos
class SampleHandler: RPBroadcastSampleHandler {
let appIdentifier = "group.CY"
let fileManager = FileManager.default
var videoWriterInput: AVAssetWriterInput!
var microphoneWriterInput: AVAssetWriterInput!
var videoWriter: AVAssetWriter!
var sessionBeginAtSourceTime: CMTime!
var isRecording = false
var outputFileLocation: URL!
override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
guard !isRecording else { return }
isRecording = true
sessionBeginAtSourceTime = nil
setUpWriter()
}
func setUpWriter() {
let width = UIScreen.main.bounds.width * 2
let height = UIScreen.main.bounds.height * 2
self.outputFileLocation = videoFileLocation()
// Add the video input
videoWriter = try? AVAssetWriter.init(outputURL: self.outputFileLocation, fileType: AVFileType.mp4)
let videoCompressionPropertys = [
AVVideoAverageBitRateKey: width * height * 10.1
]
let videoSettings: [String: Any] = [
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: width,
AVVideoHeightKey: height,
AVVideoCompressionPropertiesKey: videoCompressionPropertys
]
videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
videoWriterInput.expectsMediaDataInRealTime = true
// Add the microphone input
var acl = AudioChannelLayout()
memset(&acl, 0, MemoryLayout<AudioChannelLayout>.size)
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
let audioOutputSettings: [String: Any] =
[ AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey : 44100,
AVNumberOfChannelsKey : 1,
AVEncoderBitRateKey : 64000,
AVChannelLayoutKey : Data(bytes: &acl, count: MemoryLayout<AudioChannelLayout>.size)]
microphoneWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: audioOutputSettings)
microphoneWriterInput.expectsMediaDataInRealTime = true
if videoWriter.canAdd(videoWriterInput) {
videoWriter.add(videoWriterInput)
}
if videoWriter.canAdd(microphoneWriterInput) {
videoWriter.add(microphoneWriterInput)
}
videoWriter.startWriting()
}
override func broadcastFinished() {
guard isRecording else { return }
isRecording = false
sessionBeginAtSourceTime = nil
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
videoWriterInput.markAsFinished()
microphoneWriterInput.markAsFinished()
var finishedWriting = false
videoWriter.finishWriting {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "xxx")
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: self.outputFileLocation)
}) { completed, error in
if completed {
NSLog("Video \(self.outputFileLocation.path ?? "") has been moved to camera roll")
}
if error != nil {
NSLog("ERROR:::Cannot move the video \(self.outputFileLocation.path ?? "") to camera roll, error: \(error!.localizedDescription)")
}
finishedWriting = true
}
while finishedWriting == false {
// NSLog("DEBUG:::Waiting to finish writing...")
}
dispatchGroup.leave()
}
dispatchGroup.wait() // <= blocks the thread here
}
override func finishBroadcastWithError(_ error: Error) {
let e = error
print(e)
}
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
let writable = canWrite()
if writable, sessionBeginAtSourceTime == nil {
sessionBeginAtSourceTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
videoWriter.startSession(atSourceTime: sessionBeginAtSourceTime!)
}
if writable {
switch sampleBufferType {
case .video:
if videoWriterInput.isReadyForMoreMediaData {
videoWriterInput.append(sampleBuffer)
}
case .audioApp:
print("audio")
case .audioMic:
if microphoneWriterInput.isReadyForMoreMediaData {
microphoneWriterInput.append(sampleBuffer)
}
@unknown default:
print("unknown")
}
}
}
func videoFileLocation() -> URL {
let documentsPath = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appIdentifier)!
let videoOutputUrl = documentsPath
.appendingPathComponent("Library")
.appendingPathComponent("Caches")
.appendingPathComponent("mobile")
.appendingPathExtension("mp4")
do {
if fileManager.fileExists(atPath: videoOutputUrl.path) {
try fileManager.removeItem(at: videoOutputUrl)
}
} catch { print(error) }
return videoOutputUrl
}
func canWrite() -> Bool {
return videoWriter != nil && isRecording && videoWriter?.status == .writing
}
}
Nicole Kidman, Michael Keaton ve Val Kilmer'in Batman Olarak Paylaştığı Bu 1 Çekici Özelliğe Bayıldı
Charly Reynolds Yakın Zamandaki Vokal Kord Ameliyatını Açıkladı: 'Şarkı Söylemekte Sorun Yaşıyordum'
Tom Girardi Dolandırıcılık Suçlamalarından Yargılanma Yetkisinin Belirlenmesi İçin Duruşmaya Katıldı