Как использовать AVAssetReader и AVAssetWriter для нескольких дорожек (аудио и видео) одновременно? - PullRequest
9 голосов
/ 09 марта 2011

Я знаю, как использовать AVAssetReader и AVAssetWriter, и успешно использовал их для захвата видеодорожки из одного фильма и перекодирования в другой.Тем не менее, я хотел бы сделать это и со звуком.Нужно ли создавать AVAssetExportSession после завершения первоначального транскодирования, или есть какой-то способ переключения между дорожками во время сеанса записи?Я не хотел бы иметь дело с издержками AVAssetExportSession.

Я спрашиваю, потому что, используя метод стиля тяги - (while ([assetWriterInput isReadyForMoreMediaData]) {...} - предполагает только одну дорожку.Как его можно использовать для более чем одной дорожки, то есть для аудио и видео дорожки?

Ответы [ 3 ]

8 голосов
/ 09 апреля 2011

AVAssetWriter автоматически чередует запросы на связанных AVAssetWriterInput с, чтобы интегрировать различные дорожки в выходной файл. Просто добавьте AVAssetWriterInput для каждого из ваших треков, а затем наберите requestMediaDataWhenReadyOnQueue:usingBlock: для каждого из ваших AVAssetWriterInput s.

Вот мой метод, который вызывает requestMediaDataWhenReadyOnQueue:usingBlock:. Я вызываю этот метод из цикла по количеству пар ввода / вывода, которые у меня есть. (Отдельный метод хорош как для читабельности кода, так и потому, что, в отличие от цикла, каждый вызов устанавливает отдельный кадр стека для блока.)

Вам нужен только один dispatch_queue_t, и вы можете использовать его для всех треков. Обратите внимание, что вам определенно следует , а не вызывать dispatch_async из вашего блока, потому что requestMediaDataWhenReadyOnQueue:usingBlock: ожидает, что блок будет блокироваться, пока он не заполнит столько данных, сколько займет AVAssetWriterInput. Вы не хотите возвращаться раньше.

- (void)requestMediaDataForTrack:(int)i {
  AVAssetReaderOutput *output = [[_reader outputs] objectAtIndex:i];
  AVAssetWriterInput *input = [[_writer inputs] objectAtIndex:i];

  [input requestMediaDataWhenReadyOnQueue:_processingQueue usingBlock:
    ^{
      [self retain];
      while ([input isReadyForMoreMediaData]) {
        CMSampleBufferRef sampleBuffer;
        if ([_reader status] == AVAssetReaderStatusReading &&
            (sampleBuffer = [output copyNextSampleBuffer])) {

          BOOL result = [input appendSampleBuffer:sampleBuffer];
          CFRelease(sampleBuffer);

          if (!result) {
            [_reader cancelReading];
            break;
          }
        } else {
          [input markAsFinished];

          switch ([_reader status]) {
            case AVAssetReaderStatusReading:
              // the reader has more for other tracks, even if this one is done
              break;

            case AVAssetReaderStatusCompleted:
              // your method for when the conversion is done
              // should call finishWriting on the writer
              [self readingCompleted];
              break;

            case AVAssetReaderStatusCancelled:
              [_writer cancelWriting];
              [_delegate converterDidCancel:self];
              break;

            case AVAssetReaderStatusFailed:
              [_writer cancelWriting];
              break;
          }

          break;
        }
      }
    }
  ];
}
1 голос
/ 14 марта 2011

Вы пытались использовать два AVAssetWriterInputs и проталкивать образцы через рабочую очередь?Вот примерный набросок.

processing_queue = dispatch_queue_create("com.mydomain.gcdqueue.mediaprocessor", NULL);

[videoAVAssetWriterInput requestMediaDataWhenReadyOnQueue:myInputSerialQueue usingBlock:^{
    dispatch_asyc(processing_queue, ^{process video});
}];

[audioAVAssetWriterInput requestMediaDataWhenReadyOnQueue:myInputSerialQueue usingBlock:^{
    dispatch_asyc(processing_queue, ^{process audio});
}];
0 голосов
/ 16 сентября 2012

Вы можете использовать группы рассылки!

Посмотрите пример AVReaderWriter для MacOSX ...

Я цитирую прямо из образца RWDocument.m:

- (BOOL)startReadingAndWritingReturningError:(NSError **)outError
{
    BOOL success = YES;
    NSError *localError = nil;

    // Instruct the asset reader and asset writer to get ready to do work
    success = [assetReader startReading];
    if (!success)
        localError = [assetReader error];
    if (success)
    {
        success = [assetWriter startWriting];
        if (!success)
            localError = [assetWriter error];
    }

    if (success)
    {
        dispatch_group_t dispatchGroup = dispatch_group_create();

        // Start a sample-writing session
        [assetWriter startSessionAtSourceTime:[self timeRange].start];

        // Start reading and writing samples
        if (audioSampleBufferChannel)
        {
            // Only set audio delegate for audio-only assets, else let the video channel drive progress
            id <RWSampleBufferChannelDelegate> delegate = nil;
            if (!videoSampleBufferChannel)
                delegate = self;

            dispatch_group_enter(dispatchGroup);
            [audioSampleBufferChannel startWithDelegate:delegate completionHandler:^{
                dispatch_group_leave(dispatchGroup);
            }];
        }
        if (videoSampleBufferChannel)
        {
            dispatch_group_enter(dispatchGroup);
            [videoSampleBufferChannel startWithDelegate:self completionHandler:^{
                dispatch_group_leave(dispatchGroup);
            }];
        }

        // Set up a callback for when the sample writing is finished
        dispatch_group_notify(dispatchGroup, serializationQueue, ^{
            BOOL finalSuccess = YES;
            NSError *finalError = nil;

            if (cancelled)
            {
                [assetReader cancelReading];
                [assetWriter cancelWriting];
            }
            else
            {
                if ([assetReader status] == AVAssetReaderStatusFailed)
                {
                    finalSuccess = NO;
                    finalError = [assetReader error];
                }

                if (finalSuccess)
                {
                    finalSuccess = [assetWriter finishWriting];
                    if (!finalSuccess)
                        finalError = [assetWriter error];
                }
            }

            [self readingAndWritingDidFinishSuccessfully:finalSuccess withError:finalError];
        });

        dispatch_release(dispatchGroup);
    }

    if (outError)
        *outError = localError;

    return success;
}
...