- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在录制屏幕,我想将麦克风音频和应用程序音频中的声音组合成一个带有 ONE stero 音轨的视频。与 AVAssetWriter
我有设置,它会创建一个包含两个单独音轨的视频文件;一个用于设备音频的立体声轨道和一个用于麦克风音频的单声道。这不好。
我还尝试获取生成的视频文件并使用单独的音频 AVAssetTrack
重建一个新的视频文件s 合并为一个,使用 AVMutableCompositionTrack
小号 insertTimeRange(
如下所示。但这不会合并轨道,无论我尝试什么,它只是将它们连接起来(按顺序,而不是相互重叠)。
请有人告诉我如何记录最初与 AVAssetWriter 合并的轨道。或者以后如何将它们合并。网上没有任何东西可以讨论并完成它。很多文章引用使用insertTimeRange(
但此功能连接轨道。请帮忙。
到目前为止我使用的代码:
func startRecording(withFileName fileName: String, recordingHandler: @escaping (Error?) -> Void) {
let sharedRecorder = RPScreenRecorder.shared()
currentlyRecordingURL = URL(fileURLWithPath: CaptureArchiver.filePath(fileName))
guard currentlyRecordingURL != nil else { return }
desiredMicEnabled = RPScreenRecorder.shared().isMicrophoneEnabled
assetWriter = try! AVAssetWriter(outputURL: currentlyRecordingURL!, fileType: AVFileType.mp4)
let appAudioOutputSettings = [
AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 2,
AVSampleRateKey : 44100.0,
AVEncoderBitRateKey: 192000
] as [String : Any]
let micAudioOutputSettings = [
AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 1,
AVSampleRateKey : 44100.0,
AVEncoderBitRateKey: 192000
] as [String : Any]
let adjustedWidth = ceil(UIScreen.main.bounds.size.width/4)*4
let videoOutputSettings: Dictionary<String, Any> = [
AVVideoCodecKey : AVVideoCodecType.h264,
AVVideoWidthKey : adjustedWidth,
AVVideoHeightKey : UIScreen.main.bounds.size.height
]
let audioInput_app = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: appAudioOutputSettings)
audioInput_app.expectsMediaDataInRealTime = true
if assetWriter.canAdd(audioInput_app) { assetWriter.add(audioInput_app) }
self.audioInput_app = audioInput_app
let audioInput_mic = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: micAudioOutputSettings)
audioInput_mic.expectsMediaDataInRealTime = true
if assetWriter.canAdd(audioInput_mic) { assetWriter.add(audioInput_mic) }
self.audioInput_mic = audioInput_mic
let videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoOutputSettings)
videoInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(videoInput) { assetWriter.add(videoInput) }
self.videoInput = videoInput
RPScreenRecorder.shared().startCapture(handler: { [unowned self] (sample, bufferType, error) in
if CMSampleBufferDataIsReady(sample) {
DispatchQueue.main.async { [unowned self] in
if self.assetWriter.status == AVAssetWriter.Status.unknown {
self.assetWriter.startWriting()
#if DEBUG
let status = self.assetWriter.status
log(self, message: "LAUNCH assetWriter.status[\(status.rawValue)]:\(String(describing: self.readable(status)))")
#endif
self.assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sample))
} else if self.assetWriter.status == AVAssetWriter.Status.failed {
recordingHandler(error)
return
} else {
switch bufferType {
case .audioApp:
if let audioInput_app = self.audioInput_app {
if audioInput_app.isReadyForMoreMediaData { audioInput_app.append(sample) }
}
case .audioMic:
if let audioInput_mic = self.audioInput_mic {
if audioInput_mic.isReadyForMoreMediaData { audioInput_mic.append(sample) }
}
case .video:
if let videoInput = self.videoInput {
if videoInput.isReadyForMoreMediaData { videoInput.append(sample) }
}
@unknown default:
fatalError("Unknown RPSampleBufferType:\(bufferType)")
}
}
}
}
}) { [unowned self] (error) in
recordingHandler(error)
if error == nil && self.desiredMicEnabled == true && RPScreenRecorder.shared().isMicrophoneEnabled == false {
self.viewController.mic_cap_denied = true
} else {
self.viewController.mic_cap_denied = false
}
}
}
func mergeAudioTracksInVideo(_ videoURL: URL, completion: @escaping ((Bool) -> Void)) {
let sourceAsset = AVURLAsset(url: videoURL)
let sourceVideoTrack: AVAssetTrack = sourceAsset.tracks(withMediaType: AVMediaType.video)[0]
let sourceAudioTrackApp: AVAssetTrack = sourceAsset.tracks(withMediaType: AVMediaType.audio)[0]
let sourceAudioTrackMic: AVAssetTrack = sourceAsset.tracks(withMediaType: AVMediaType.audio)[1]
let comp: AVMutableComposition = AVMutableComposition()
guard let newVideoTrack: AVMutableCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaType.video,
preferredTrackID: kCMPersistentTrackID_Invalid) else {
completion(false)
return
}
newVideoTrack.preferredTransform = sourceVideoTrack.preferredTransform
guard let newAudioTrack: AVMutableCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaType.audio,
preferredTrackID: kCMPersistentTrackID_Invalid) else {
completion(false)
return
}
//THE MIXING //THIS STILL RESULTS IN TWO SEPARATE AUDIO TRACKS //LOOKS LIKE THIS IS MORE ABOUT VOLUME LEVELS
let mix = AVMutableAudioMix()
let audioMixInputParamsMic = AVMutableAudioMixInputParameters()
audioMixInputParamsMic.trackID = sourceAudioTrackMic.trackID
audioMixInputParamsMic.setVolume(1.0, at: CMTime.zero)
let audioMixInputParamsApp = AVMutableAudioMixInputParameters()
audioMixInputParamsApp.trackID = sourceAudioTrackApp.trackID
audioMixInputParamsApp.setVolume(1.0, at: CMTime.zero)
mix.inputParameters.append(audioMixInputParamsMic)
mix.inputParameters.append(audioMixInputParamsApp)
///////
let timeRange: CMTimeRange = CMTimeRangeMake(start: CMTime.zero, duration: sourceAsset.duration)
do {
try newVideoTrack.insertTimeRange(timeRange, of: sourceVideoTrack, at: CMTime.zero)
try newAudioTrack.insertTimeRange(timeRange, of: sourceAudioTrackMic, at: CMTime.zero)
try newAudioTrack.insertTimeRange(timeRange, of: sourceAudioTrackApp, at: CMTime.zero)
} catch {
completion(false)
return
}
let exporter: AVAssetExportSession = AVAssetExportSession(asset: comp, presetName: AVAssetExportPresetHighestQuality)!
exporter.audioMix = mix
exporter.outputFileType = AVFileType.mp4
exporter.outputURL = videoURL
removeFileAtURLIfExists(url: videoURL)
exporter.exportAsynchronously(completionHandler: {
switch exporter.status {
case AVAssetExportSession.Status.failed:
#if DEBUG
log(self, message: "1000000000failed \(String(describing: exporter.error))")
#endif
case AVAssetExportSession.Status.cancelled:
#if DEBUG
log(self, message: "1000000000cancelled \(String(describing: exporter.error))")
#endif
case AVAssetExportSession.Status.unknown:
#if DEBUG
log(self, message: "1000000000unknown\(String(describing: exporter.error))")
#endif
case AVAssetExportSession.Status.waiting:
#if DEBUG
log(self, message: "1000000000waiting\(String(describing: exporter.error))")
#endif
case AVAssetExportSession.Status.exporting:
#if DEBUG
log(self, message: "1000000000exporting\(String(describing: exporter.error))")
#endif
default:
#if DEBUG
log(self, message: "1000000000-----Mutable video exportation complete.")
#endif
}
completion(true)
})
}
最佳答案
在这个类中使用以及在以下函数中使用后用于记录开始和停止:
https://gist.github.com/mspvirajpatel/f7e1e258f3c1fff96917d82fa9c4c137
import AVFoundation
import ReplayKit
var rpScreenRecorder = RPScreenRecorder.shared()
var rpScreenWriter = RPScreenWriter()
func startRecord() {
rpScreenRecorder.isMicrophoneEnabled = true
rpScreenRecorder.startCapture(handler: { cmSampleBuffer, rpSampleBufferType, error in
if let error = error {
} else {
self.rpScreenWriter.writeBuffer(cmSampleBuffer, rpSampleType: rpSampleBufferType)
}
}) { error in
}
}
func stopRecording() {
rpScreenRecorder.stopCapture { error in
if let error = error {
} else {
self.rpScreenWriter.finishWriting(completionHandler: { url, error in
if let url = url {
print("\(url)")
}
})
}
}
}
关于Swift AVAssetWriter 将带有麦克风音频和设备音频的视频录制成带有 ONE 音轨 AVAssetTrack 的视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56518897/
我想使用 li 和 ul 制作一个多级下拉列表,以便显示我博客中按年和月排序的所有文章。我希望我的下拉菜单看起来像 Google Blogspot 下拉菜单: 这是我的 CSS 和 HTML 代码 u
我在 Win 7 64 机器上将 CodeBlocks 与 gcc 4.7.2 和 gmp 5.0.5 结合使用。开始使用 gmpxx 后,我看到一个奇怪的段错误,它不会出现在 +、- 等运算符中,但
我正在使用 tern 为使用 CodeMirror 运行的窗口提供一些增强的智能感知,它工作正常,但我遇到了一个问题,我想添加一些自定义“types”,可以这么说,这样下拉列表中它们旁边就有图标了。我
我正在尝试让我的 PC 成为 Android 2.3.4 设备的 USB 主机,以便能够在不需要实际“附件”的情况下开发 API。为此,我需要将 PC 设置为 USB 主机和“设备”(在我的例子中是运
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
我在设置服务器方面几乎是个新手,但遇到了一个问题。我有一个 Ubuntu 16.04 VPS 并安装了 Apache2 和 Tomcat7。我正在为 SSL 使用 LetsEncrypt 和 Cert
我在一个基于谷歌地图的项目上工作了超过 6 个月。我使用的是 Google Maps API V1 及其开发人员 API key 。当我尝试发布应用程序时,我了解到 Google API V1 已被弃
我是 Python 的新手,所以如果我对一些简单的事情感到困惑,请原谅。 我有一个这样的对象: class myObject(object): def __init__(self):
这个问题已经有答案了: How can I access object properties containing special characters? (2 个回答) 已关闭 9 年前。 我正在尝
我有下面的 CSS。我想要的是一种流体/液体(因为缺乏正确的术语)css。我正在为移动设备开发,当我改变模式时 从纵向 View 到陆地 View ,我希望它流畅。现在的图像 在陆地 View 中效
我正在尝试使用可以接受参数的缓存属性装饰器。 我查看了这个实现:http://www.daniweb.com/software-development/python/code/217241/a-cac
这个问题在这里已经有了答案: Understanding slicing (36 个答案) 关闭 6 年前。 以a = [1,2,3,4,5]为例。根据我的直觉,我认为 a[::-1] 与 a[0:
mysqldump -t -u root -p mytestdb mytable --where=datetime LIKE '2014-09%' 这就是我正在做的事情,它会返回: mysqldum
我正在制作销售税计算器,除了总支付金额部分外,其他一切都正常。在我的程序中,我希望能够输入一个数字并获得该项目的税额我还希望能够获得支付的总金额,包括交易中的税金。到目前为止,我编写的代码完成了所有这
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
我是否必须进行任何额外的设置才能让 apache-airflow 在任务失败时向我发送电子邮件。我的配置文件中有以下内容(与默认值保持不变): [email] email_backend = airf
这个问题在这里已经有了答案: What does the $ symbol do in VBA? (5 个回答) 3年前关闭。 使用返回字符串(如 Left)的内置函数有什么区别吗?或使用与 $ 相同
我有一个用VB6编写的应用程序,我需要使用一个用.NET编写的库。有什么方法可以在我的应用程序上使用该库吗? 谢谢 最佳答案 这取决于。您可以控制.NET库吗? 如果是这样,则可以修改您的库,以便可以
当我创建一个以 ^ 开头的类方法时,我尝试调用它,它给了我一个错误。 class C { method ^test () { "Hi" } } dd C.new.test; Too m
我已经使用 bower 安装了 angularjs 和 materialjs。 凉亭安装 Angular Material 并将“ngMaterial”注入(inject)我的应用程序,但出现此错误。
我是一名优秀的程序员,十分优秀!