Мультиплексирование аудиопотоков AAC и видеопотоков h.264 в mp4 с помощью AVFoundation

Для OSX и IOS у меня есть потоки закодированных в реальном времени видео (h.264) и аудио (AAC) данных, и я хочу иметь возможность мультиплексировать их вместе в mp4.

Я использую AVAssetWriter для мультиплексирования.

У меня работает видео, но звук по-прежнему звучит как беспорядочные помехи. Вот что я пытаюсь сделать прямо сейчас (для краткости пропуская здесь некоторые проверки ошибок):

Я инициализирую писатель:

   NSURL *url = [NSURL fileURLWithPath:mContext->filename];
   NSError* err = nil;
   mContext->writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:&err];

Я инициализирую аудиовход:

     NSDictionary* settings;
     AudioChannelLayout acl;
     bzero(&acl, sizeof(acl));
     acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
     settings = nil; // set output to nil so it becomes a pass-through

     CMAudioFormatDescriptionRef audioFormatDesc = nil;
     {
        AudioStreamBasicDescription absd = {0};
        absd.mSampleRate = mParameters.audioSampleRate; //known sample rate
        absd.mFormatID = kAudioFormatMPEG4AAC;
        absd.mFormatFlags = kMPEG4Object_AAC_Main;
        CMAudioFormatDescriptionCreate(NULL, &absd, 0, NULL, 0, NULL, NULL, &audioFormatDesc);
     }

     mContext->aacWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:settings sourceFormatHint:audioFormatDesc];
     mContext->aacWriterInput.expectsMediaDataInRealTime = YES;
     [mContext->writer addInput:mContext->aacWriterInput];

И запускаем писатель:

   [mContext->writer startWriting];
   [mContext->writer startSessionAtSourceTime:kCMTimeZero];

Затем у меня есть обратный вызов, в котором я получаю пакет с отметкой времени (миллисекунды) и std::vector<uint8_t> с данными, содержащими 1024 сжатых выборки. Я удостоверяюсь, что isReadyForMoreMediaData верно. Затем, если мы впервые получаем обратный вызов, я настраиваю CMAudioFormatDescription:

   OSStatus error = 0;

   AudioStreamBasicDescription streamDesc = {0};
   streamDesc.mSampleRate = mParameters.audioSampleRate;
   streamDesc.mFormatID = kAudioFormatMPEG4AAC;
   streamDesc.mFormatFlags = kMPEG4Object_AAC_Main;
   streamDesc.mChannelsPerFrame = 2;  // always stereo for us
   streamDesc.mBitsPerChannel = 0;
   streamDesc.mBytesPerFrame = 0;
   streamDesc.mFramesPerPacket = 1024; // Our AAC packets contain 1024 samples per frame
   streamDesc.mBytesPerPacket = 0;
   streamDesc.mReserved = 0;

   AudioChannelLayout acl;
   bzero(&acl, sizeof(acl));
   acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
   error = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &streamDesc, sizeof(acl), &acl, 0, NULL, NULL, &mContext->audioFormat);

И, наконец, я создаю CMSampleBufferRef и отправляю его:

   CMSampleBufferRef buffer = NULL;
   CMBlockBufferRef blockBuffer;
   CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, packet.data.size(), kCFAllocatorDefault, NULL, 0, packet.data.size(), kCMBlockBufferAssureMemoryNowFlag, &blockBuffer);
   CMBlockBufferReplaceDataBytes((void*)packet.data.data(), blockBuffer, 0, packet.data.size());

   CMTime duration = CMTimeMake(1024, mParameters.audioSampleRate);
   CMTime pts = CMTimeMake(packet.timestamp, 1000);
   CMSampleTimingInfo timing = {duration , pts, kCMTimeInvalid };

   size_t sampleSizeArray[1] = {packet.data.size()};

   error = CMSampleBufferCreate(kCFAllocatorDefault, blockBuffer, true, NULL, nullptr, mContext->audioFormat, 1, 1, &timing, 1, sampleSizeArray, &buffer);       

   // First input buffer must have an appropriate kCMSampleBufferAttachmentKey_TrimDurationAtStart since the codec has encoder delay'
   if (mContext->firstAudioFrame)
   {
      CFDictionaryRef dict = NULL;
      dict = CMTimeCopyAsDictionary(CMTimeMake(1024, 44100), kCFAllocatorDefault);
      CMSetAttachment(buffer, kCMSampleBufferAttachmentKey_TrimDurationAtStart, dict, kCMAttachmentMode_ShouldNotPropagate);
      // we must trim the start time on first audio frame...
      mContext->firstAudioFrame = false;
   }

   CMSampleBufferMakeDataReady(buffer);

   BOOL ret = [mContext->aacWriterInput appendSampleBuffer:buffer];

Я предполагаю, что часть, к которой я больше всего подозреваю, это мой вызов CMSampleBufferCreate. Кажется, мне нужно передать массив размеров выборки, иначе я сразу же получаю это сообщение об ошибке при проверке статуса моего писателя:

Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-12735), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x604001e50770 {Error Domain=NSOSStatusErrorDomain Code=-12735 "(null)"}}

Где основная ошибка выглядит как kCMSampleBufferError_BufferHasNoSampleSizes.

Я заметил в документации Apple пример создания буфера с данными AAC: https://developer.apple.com/documentation/coremedia/1489723-cmsamplebuffercreate?language=objc

В своем примере они указывают длинный массив sampleSizeArray с записью для каждого отдельного образца. Это необходимо? У меня нет этой информации с этим обратным вызовом. И в нашей реализации Windows нам не нужны были эти данные. Итак, я попытался отправить package.data.size() в качестве размера выборки, но это не кажется правильным и, конечно, не дает приятного звука.

Любые идеи? Либо настройки моих вызовов здесь, либо разные API, которые я должен использовать для мультиплексирования потоков закодированных данных.

Спасибо!


person Sully    schedule 02.05.2018    source источник
comment
Я понятия не имею, как помочь, но это яркий пример того, как написать свой первый вопрос в stackoverflow! :D   -  person Jonathan    schedule 02.05.2018


Ответы (1)


Если вы не хотите перекодировать, не передавайте словарь outputSetting. Вы должны передать nil туда: mContext->aacWriterInput = [AVAssetWriterInput assetsWriterInputWithMediaType:AVMediaTypeAudio outputSettings:nil sourceFormatHint:audioFormatDesc];

Это объясняется где-то в этой статье: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html

person lordlefrog    schedule 19.11.2018