Resize video avec AVFoundation

AlakAlak Membre
juin 2015 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,

 

J'ai récemment et pour la premiere fois mis les mains dans AVFoundation pour resize une video (avec sa bande son).

 

Je suis parvenu a quelque chose qui fonctionne mais qui comporte quelques bug qui font crasher mon app.

 

Ma classe qui gère le resize est composé de 3 methods :

 



func resizeVideo(originUrl: NSURL, destinationUrl: NSURL, size: CGSize, completion: (success: Bool) -> ()) {
println("Write Started")

self.completion = completion

var error: NSError? = nil;

var videoWriter = AVAssetWriter(URL: destinationUrl, fileType: AVFileTypeMPEG4, error: &error)

let videoCleanApertureSettings = [AVVideoCleanApertureWidthKey: 360, AVVideoCleanApertureHeightKey: 640, AVVideoCleanApertureHorizontalOffsetKey: 10, AVVideoCleanApertureVerticalOffsetKey: 10]
let videoAspectRatioSettings = [AVVideoPixelAspectRatioHorizontalSpacingKey: 3, AVVideoPixelAspectRatioVerticalSpacingKey: 3]
let codecSettings = [AVVideoAverageBitRateKey: 2000000, AVVideoMaxKeyFrameIntervalKey: 1, AVVideoCleanApertureKey: videoCleanApertureSettings]

let audioOutputSettings = [AVFormatIDKey: kAudioFormatMPEG4AAC, AVNumberOfChannelsKey: 1, AVSampleRateKey: 44100, AVEncoderBitRateKey: 64000]
var audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: audioOutputSettings)
audioWriterInput.expectsMediaDataInRealTime = false

let videoSettings = [AVVideoCodecKey: AVVideoCodecH264, AVVideoCompressionPropertiesKey: codecSettings, AVVideoWidthKey: size.width, AVVideoHeightKey: size.height]
var videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings as [NSObject : AnyObject])
assert(videoWriter.canAddInput(videoWriterInput), "fail")
videoWriterInput.expectsMediaDataInRealTime = false
videoWriter.addInput(videoWriterInput)
videoWriter.addInput(audioWriterInput)

var avAsset: AVAsset = AVURLAsset(URL: originUrl, options: nil)

var aerror: NSError? = nil;
var reader = AVAssetReader(asset: avAsset, error: &aerror)
var videoTrack = avAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as! AVAssetTrack
let videoOptions = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
var asset_reader_output = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoOptions)

var audioTrack = avAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack
var asset_audio_reader_output = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: [AVFormatIDKey: kAudioFormatLinearPCM])

reader.addOutput(asset_reader_output)
reader.addOutput(asset_audio_reader_output)

videoWriter.startWriting()
videoWriter.startSessionAtSourceTime(kCMTimeZero)
reader.startReading()

for (index, output) in enumerate(reader.outputs) {
self.requestMediaDataForTrack(index, writer: videoWriter, reader: reader)
}
}


func requestMediaDataForTrack(i: Int, writer: AVAssetWriter, reader: AVAssetReader) {
var output = reader.outputs[i] as! AVAssetReaderOutput
var input: AVAssetWriterInput = writer.inputs[i] as! AVAssetWriterInput

input.requestMediaDataWhenReadyOnQueue(dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0), usingBlock: { () -> Void in
println("requestMediaDataWhenReadyOnQueue")
while input.readyForMoreMediaData {

var buffer: CMSampleBufferRef?
buffer = output.copyNextSampleBuffer()
println("read")
if reader.status == .Reading && buffer != nil {
println("write")
if !input.appendSampleBuffer(buffer!) {
reader.cancelReading()
}
} else {
input.markAsFinished()
switch reader.status {
case .Reading:
break
case .Completed:
self.complete(writer)
break
case .Cancelled:
writer.cancelWriting()
break
case .Failed, .Unknown:
writer.cancelWriting()
break
}
break
}
}
})
}



func complete(writer: AVAssetWriter) {
writer.finishWritingWithCompletionHandler({ () -> Void in
switch writer.status {
case .Completed:
println("Completed")
if let completion = self.completion {
completion(success: true)
}
default:
println(writer.error)
if let completion = self.completion {
completion(success: false)
}
}
})
}

Et donc de temps en temps j'ai un des deux crash suivant :


 



 


 


Fatal Exception: NSInternalInconsistencyException
*** -[AVAssetWriterInput appendSampleBuffer:] A sample buffer cannot be appended when readyForMoreMediaData is NO.



 


 



 


 


Fatal Exception: NSRangeException
Cannot remove an observer <AVAssetWriterInputWritingHelper 0x17046c340> for the key path "readyForMoreMediaData" from <AVAssetWriterInputWritingHelper 0x17046c340> because it is not registered as an observer.


 


Vous auriez une idée, ou une piste sur ce que je fais mal?


 


Merci d'avance.


Mots clés:

Réponses

  • CéroceCéroce Membre, Modérateur

    J'ai eu pas mal de surprises avec AVFoundation sous OS X avec certains codec.


    Notamment, le statut de l'AVItem qui passait sans arrêt de ready à  not ready.


     



     


     


    *** -[AVAssetWriterInput appendSampleBuffer:] A sample buffer cannot be appended when readyForMoreMediaData is NO.

    Je dirais que tu essaies de lire des données mais que le reader n'est pas prêt à  t'en donner. A priori, il faut attendre que le flag repasse à  YES.


     



     


     


    Cannot remove an observer <AVAssetWriterInputWritingHelper 0x17046c340> for the key path "readyForMoreMediaData" from <AVAssetWriterInputWritingHelper 0x17046c340> because it is not registered as an observer.

     


    Là , tu ne donnes pas le code qui fait le KVO, mais ça me semble assez explicite.


  • AlakAlak Membre
    juin 2015 modifié #3

    Merci de tes réponses, pour le premier j'ai pourtant bien :



    while input.readyForMoreMediaData { 

    Pour le 2eme je n'ai pas de KVO :/


  • CéroceCéroce Membre, Modérateur

    pour le premier j'ai pourtant bien :

    while input.readyForMoreMediaData { 

    il n'est pas impossible que le flag ait changé entre temps, surtout avec une queue basse priorité, même si c'est très bizarre. Pourrais-tu refaire la comparaison juste avant l'appel à  appendSampleBuffer() ?

    Pour le 2eme je n'ai pas de KVO :/

    T'as raison, on dirait qu'une classe interne (AVAssetWriterInputWritingHelper) fait le KVO. Ce qui est encore plus bizarre, c'est que l'instance s'observe elle-même.


    As-tu essayé avec des vidéos encodées avec d'autres codecs ? Tu testes sous quel OS ?
  • AlakAlak Membre

    C'est sur iOS 8, avec une video record par l'iPhone.


  • AlakAlak Membre
    juillet 2015 modifié #6

    Bon je dois mal m'y prendre je vais tous recommencer.


     


    J'ai une video (j'ai l'url du media qui est sur le disque). Que dois-je utiliser pour resize la video (qui a une bande audio) ?


     


    Alak


  • Bon pour info j'ai modifié la class "CyanifyOperation" du sample code d'apple : AVReaderWriterOfflineAudioVideoProcessing (que j'ai du repasser en swift 1.2 car il été en 2.0)


     


    https://github.com/Alak/ResizeOperation


Connectez-vous ou Inscrivez-vous pour répondre.