2013-03-26 24 views
7

Tôi nhận thấy trong tài liệu iOS cho AVAssetWriterInput bạn có thể chuyển nil cho từ điển outputSettings để xác định rằng dữ liệu đầu vào không được mã hóa lại.Sử dụng AVAssetWriter với các đơn vị NAL thô

Cài đặt được sử dụng để mã hóa phương tiện được nối vào đầu ra. Vượt qua nil để chỉ định rằng các mẫu được thêm vào sẽ không được mã hóa lại.

Tôi muốn tận dụng lợi thế của tính năng này để vượt qua trong một dòng H.264 NALs liệu, nhưng tôi đang gặp khó khăn thích nghi với dòng byte liệu của tôi vào một CMSampleBuffer mà tôi có thể vượt qua thành phương pháp appendSampleBuffer AVAssetWriterInput của. Luồng NAL của tôi chỉ chứa SPS/PPS/IDR/P NAL (1, 5, 7, 8). Tôi đã không thể tìm thấy tài liệu hoặc một câu trả lời kết luận về cách sử dụng dữ liệu H264 được mã hóa trước bằng AVAssetWriter. Không thể phát tệp video kết quả.

Làm cách nào để tôi có thể đóng gói đúng các đơn vị NAL vào CMSampleBuffers? Tôi có cần sử dụng tiền tố mã bắt đầu không? Tiền tố chiều dài? Tôi có cần đảm bảo rằng tôi chỉ đặt một NAL cho mỗi CMSampleBuffer? Mục tiêu cuối cùng của tôi là tạo vùng chứa MP4 hoặc MOV với H264/AAC.

Dưới đây là đoạn code tôi đã chơi với:

-(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]; 
    }); 
} 

Lưu ý rằng tôi gọi CMSampleBufferSetOutputPresentationTimeStamp trên đệm mẫu bên trong của phương pháp writeSampleBuffer với những gì tôi nghĩ là một thời gian hợp lệ trước khi tôi thực sự cố gắng nối thêm.

Mọi trợ giúp đều được đánh giá cao.

+0

Ít nhất một phần vấn đề của tôi là cách tôi xử lý CMSampleTimingInfo.Tôi đã đề cập đến tôi đã sử dụng 'setOutputPresentationTimeStamp' để điền vào một dấu thời gian thực. Bây giờ tôi nhận ra rằng tôi cũng cần phải điền vào các trường khác của CMSampleTimingInfo. Tôi đang thiết lập 'decodeTimeStamp' thành' kCMTimeInvalid' và 'duration' thành' CMTimeMake (1, 30) '. Bây giờ tôi nhận được một thùng chứa video có thể tìm kiếm với tổng thời gian thích hợp, nhưng không có video (thử nghiệm trong VLC). – bsirang

Trả lời

3

Tôi đã quản lý để phát lại video hoạt động trong VLC chứ không phải QuickTime. Tôi đã sử dụng mã tương tự với những gì tôi đã đăng ở trên để nhận H.264 NAL vào CMSampleBuffers.

tôi có hai vấn đề chính:

  1. tôi đã không thiết lập CMSampleTimingInfo chính xác (như nhận xét của tôi trên tiểu bang).
  2. Tôi đã không đóng gói dữ liệu NAL thô chính xác (không chắc chắn nơi tài liệu này được ghi lại, nếu ở bất kỳ đâu).

Để giải quyết # 1, tôi đặt timing.duration = CMTimeMake(1, fps); trong đó fps là tốc độ khung hình mong đợi. Sau đó tôi đặt timing.decodeTimeStamp = kCMTimeInvalid; để có nghĩa là các mẫu sẽ được đưa ra theo thứ tự giải mã. Cuối cùng, tôi đặt timing.presentationTimeStamp bằng cách tính thời gian tuyệt đối mà tôi cũng đã sử dụng với startSessionAtSourceTime.

Để giải quyết # 2, thông qua thử và sai tôi thấy rằng cho đơn vị NAL của tôi trong các hình thức sau đây làm việc:

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

đâu mỗi đơn vị NAL được bắt đầu bằng một mã bắt đầu 32-bit bằng 0x00000001.

Có lẽ vì lý do giống như nó không phát trong QuickTime, tôi vẫn gặp sự cố khi di chuyển tệp .mov sang album ảnh (phương pháp ALAssetLibraryvideoAtPathIsCompatibleWithSavedPhotosAlbum không nói rằng "Phim không thể phát". ai đó có ý tưởng về những gì đang diễn ra có thể bình luận. Cảm ơn!

+0

Cho rằng điều này chỉ hoạt động trong VLC, có một cơ hội mà tôi cần phải thay đổi cách tôi đóng gói các NAL vào bộ đệm để cho QuickTime hoạt động. Ai có ý tưởng gì không? – bsirang

+0

Tôi lấy một hexdump của tập tin mov kết quả và có vẻ như tồn tại một nguyên tử mdat về phía trên cùng của tệp cũng như một vài nguyên tử avc1 về phía dưới, nhưng không có nguyên tử avcC. Bên trong nguyên tử mdat dường như là dòng NAL mà tôi mô tả trong câu trả lời này (cách nhau bằng tiền tố 0x00000001). Tôi giả định QuickTime từ chối phát tệp vì dữ liệu trong mdat ở định dạng Phụ lục B và vì không tồn tại nguyên tử avcC. Tôi cần tìm ra cách nạp dữ liệu vào AVAssetWriterInput đúng cách. – bsirang

+0

Tôi đã xoay xở để làm mọi thứ. Tôi đang chuẩn bị chiều dài NAL cho mỗi NAL (thay vì 0x00000001), và tôi cũng quản lý để truyền dữ liệu avcC đúng cách vào thùng chứa mov. Xem câu hỏi và câu trả lời của tôi tại đây: http://stackoverflow.com/questions/15673379/avassetwriterinput-h-264-passthrough-to-quicktime-mov-passing-in-sps-pps-to/ – bsirang