2013-03-26 10 views
7

Ho notato nella documentazione di iOS per AVAssetWriterInput è possibile passare nil per il dizionario outputSettings per specificare che i dati di input non devono essere ricodificati.Utilizzo di AVAssetWriter con unità NAL raw

Le impostazioni utilizzate per la codifica del supporto aggiunto all'output. Passare a zero per specificare che i campioni aggiunti non devono essere ricodificati.

voglio approfittare di questa caratteristica per passare in un flusso di H.264 Nalles prime, ma sto avendo difficoltà ad adattarsi ai miei flussi di byte grezzi in un CMSampleBuffer che posso passare nel metodo di AVAssetWriterInput appendSampleBuffer. Il mio flusso di NAL contiene solo NAL SPS/PPS/IDR/P (1, 5, 7, 8). Non sono stato in grado di trovare documentazione o una risposta definitiva su come utilizzare i dati H264 pre-codificati con AVAssetWriter. Il file video risultante non può essere riprodotto.

Come posso impacchettare correttamente le unità NAL in CMSampleBuffers? Devo usare un prefisso del codice di avviamento? Un prefisso di lunghezza? Devo assicurarmi di inserire solo un valore NAL per CMSampleBuffer? Il mio obiettivo finale è creare un contenitore MP4 o MOV con H264/AAC.

Ecco il codice che ho giocato con:

-(void)addH264NAL:(NSData *)nal 
{ 
    dispatch_async(recordingQueue, ^{ 
     //Adapting the raw NAL into a CMSampleBuffer 
     CMSampleBufferRef sampleBuffer = NULL; 
     CMBlockBufferRef blockBuffer = NULL; 
     CMFormatDescriptionRef formatDescription = NULL; 
     CMItemCount numberOfSampleTimeEntries = 1; 
     CMItemCount numberOfSamples = 1; 


     CMVideoFormatDescriptionCreate(kCFAllocatorDefault, kCMVideoCodecType_H264, 480, 360, nil, &formatDescription); 
     OSStatus result = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, [nal length], kCFAllocatorDefault, NULL, 0, [nal length], kCMBlockBufferAssureMemoryNowFlag, &blockBuffer); 
     if(result != noErr) 
     { 
      NSLog(@"Error creating CMBlockBuffer"); 
      return; 
     } 
     result = CMBlockBufferReplaceDataBytes([nal bytes], blockBuffer, 0, [nal length]); 
     if(result != noErr) 
     { 
      NSLog(@"Error filling CMBlockBuffer"); 
      return; 
     } 
     const size_t sampleSizes = [nal length]; 
     CMSampleTimingInfo timing = { 0 }; 
     result = CMSampleBufferCreate(kCFAllocatorDefault, blockBuffer, YES, NULL, NULL, formatDescription, numberOfSamples, numberOfSampleTimeEntries, &timing, 1, &sampleSizes, &sampleBuffer); 

     if(result != noErr) 
     { 
      NSLog(@"Error creating CMSampleBuffer"); 
     } 
     [self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeVideo]; 
    }); 
} 

Si noti che sto chiamando CMSampleBufferSetOutputPresentationTimeStamp sul tampone campione all'interno del metodo writeSampleBuffer con quello che credo sia un tempo valido prima che io sono in realtà cercando di aggiungerlo.

Qualsiasi aiuto è apprezzato.

+0

Almeno una parte del mio problema era come avevo a che fare con CMSampleTimingInfo.Ho detto che stavo usando 'setOutputPresentationTimeStamp' per riempire un timestamp reale. Ora mi rendo conto che ho bisogno di compilare anche gli altri campi di CMSampleTimingInfo. Sto impostando 'decodeTimeStamp' su' kCMTimeInvalid' e 'duration' su' CMTimeMake (1, 30) '. Ora ho un contenitore video ricercabile con un tempo totale adeguato, ma non c'è video (test in VLC). – bsirang

risposta

3

Sono riuscito a far funzionare la riproduzione video in VLC ma non in QuickTime. Ho usato un codice simile a quello che ho postato sopra per ottenere H.264 NAL in CMSampleBuffers.

ho avuto due problemi principali:

  1. non stavo installando correttamente CMSampleTimingInfo (come il mio commento di cui sopra Uniti).
  2. Non stavo compilando correttamente i dati NAL non corretti (non sono sicuro di dove sia documentato, se è ovunque).

Per risolvere il n. 1, ho impostato timing.duration = CMTimeMake(1, fps); dove fps è la frequenza fotogrammi prevista. Ho quindi impostato timing.decodeTimeStamp = kCMTimeInvalid; per indicare che i campioni verranno forniti in ordine di decodifica. Infine, ho impostato timing.presentationTimeStamp calcolando il tempo assoluto, che ho utilizzato anche con startSessionAtSourceTime.

Per risolvere # 2, attraverso tentativi ed errori ho scoperto che dare le mie unità NAL il seguente modulo ha funzionato:

[7 8 5] [1] [1] [1]..... [7 8 5] [1] [1] [1]..... (repeating) 

in cui ogni unità NAL è preceduto da un codice di avvio a 32 bit pari 0x00000001.

Presumibilmente per lo stesso motivo è non giocare in QuickTime, sto ancora problemi spostando il file .mov risultante all'album di foto (il metodo ALAssetLibraryvideoAtPathIsCompatibleWithSavedPhotosAlbum sta fallendo affermando che il "film non poteva essere giocata." Speriamo qualcuno con un'idea di cosa sta succedendo può commentare Grazie

+0

Dato che questo funziona solo in VLC, c'è la possibilità che io debba cambiare il modo in cui sto impacchettando gli NAL nei buffer per far funzionare QuickTime. Qualcuno ha qualche idea? – bsirang

+0

Ho preso un dump esadecimale del file mov risultante e sembra che esista un atomo mdat nella parte superiore del file e un paio di atomi avc1 verso il basso, ma nessun atomo avcC. All'interno dell'atomo mdat sembrava essere il flusso di NALs che descrivo in questa risposta (separati dal prefisso 0x00000001). Suppongo che QuickTime si rifiuti di riprodurre il file perché i dati sotto mdat sono nel formato Annex B e perché non esiste un atomo avcC. Devo capire come inviare i dati a AVAssetWriterInput correttamente. – bsirang

+0

Sono riuscito a far funzionare tutto. Sto anteponendo la lunghezza NAL a ciascun NAL (invece di 0x00000001), e sono anche riuscito a passare correttamente i dati avcC nel contenitore mov. Vedi la mia domanda e la risposta qui: http://stackoverflow.com/questions/15673379/avassetwriterinput-h-264-passthrough-to-quicktime-mov-passing-in-sps-pps-to/ – bsirang

Problemi correlati