2014-01-13 2 views
11

MediaCodec을 사용하여 화면 캡처와 오디오를 녹음하는 앱을 쓰고 있습니다. 필자는 MediaMuxer를 사용하여 mp4 파일을 만들기 위해 비디오와 오디오를 혼합합니다. 필자는 비디오와 오디오를 별도로 기록하는 데 성공했지만, 라이브를 함께 멀티플렉싱하면 결과가 예상치 않게됩니다. 오디오는 비디오없이 재생되거나 비디오는 오디오 직후에 재생됩니다. 내 생각 엔 타임 스탬프에 문제가있는 것 같지만 정확히 무엇을 파악할 수없는 것입니다. 이미 그 예를 보았습니다 : https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audiotest/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments과 bigflake.com에있는 것들이 답을 찾을 수 없었습니다. 에오디오와 비디오를 다중화 할 수 없습니다.

mInputSurface.setPresentationTime(mSurfaceTexture.getTimestamp()); 
drainVideoEncoder(false); 

그리고이 : 나는 비디오 타임 스탬프를 설정하려면이 옵션을 사용

 mVideoEncoder = MediaCodec.createEncoderByType(Preferences.MIME_TYPE); 
    mVideoEncoder.configure(mVideoFormat, null, null, 
      MediaCodec.CONFIGURE_FLAG_ENCODE); 
    mInputSurface = new InputSurface(mVideoEncoder.createInputSurface(), 
      mSavedEglContext); 
    mVideoEncoder.start(); 
    if (recordAudio){ 
     audioBufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, 
     AudioFormat.ENCODING_PCM_16BIT); 
     mAudioRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, 
     AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, audioBufferSize); 
     mAudioRecorder.startRecording(); 

     mAudioEncoder = MediaCodec.createEncoderByType("audio/mp4a-latm"); 
     mAudioEncoder.configure(mAudioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
     mAudioEncoder.start(); 
    } 
    try { 
     String fileId = String.valueOf(System.currentTimeMillis()); 
     mMuxer = new MediaMuxer(dir.getPath() + "/Video" 
       + fileId + ".mp4", 
       MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 
    } catch (IOException ioe) { 
     throw new RuntimeException("MediaMuxer creation failed", ioe); 
    } 
    mVideoTrackIndex = -1; 
    mAudioTrackIndex = -1; 
    mMuxerStarted = false; 

:

mVideoFormat = createMediaFormat(); 

    private static MediaFormat createVideoFormat() { 
    MediaFormat format = MediaFormat.createVideoFormat(
      Preferences.MIME_TYPE, mScreenWidth, mScreenHeight); 
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, 
      MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); 
    format.setInteger(MediaFormat.KEY_BIT_RATE, Preferences.BIT_RATE); 
    format.setInteger(MediaFormat.KEY_FRAME_RATE, Preferences.FRAME_RATE); 
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 
      Preferences.IFRAME_INTERVAL); 
    return format; 
} 
    mAudioFormat = createAudioFormat(); 

    private static MediaFormat createAudioFormat() { 
    MediaFormat format = new MediaFormat(); 
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); 
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 
    format.setInteger(MediaFormat.KEY_BIT_RATE, 64000); 
    return format; 
} 

오디오 및 비디오 인코더, 먹서 :

여기 내 미디어 형식의 구성이다 오디오 시간 스탬프 설정 :

lastQueuedPresentationTimeStampUs = getNextQueuedPresentationTimeStampUs(); 

if(endOfStream) 
    mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, audioBuffer.length, lastQueuedPresentationTimeStampUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 
else 
    mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, audioBuffer.length, lastQueuedPresentationTimeStampUs, 0); 


    mAudioBufferInfo.presentationTimeUs = getNextDeQueuedPresentationTimeStampUs(); 
    mMuxer.writeSampleData(mAudioTrackIndex, encodedData, 
          mAudioBufferInfo); 
    lastDequeuedPresentationTimeStampUs = mAudioBufferInfo.presentationTimeUs; 


    private static long getNextQueuedPresentationTimeStampUs(){ 
    long nextQueuedPresentationTimeStampUs = (lastQueuedPresentationTimeStampUs > lastDequeuedPresentationTimeStampUs) 
      ? (lastQueuedPresentationTimeStampUs + 1) : (lastDequeuedPresentationTimeStampUs + 1); 
    Log.i(TAG, "nextQueuedPresentationTimeStampUs: " + nextQueuedPresentationTimeStampUs); 
    return nextQueuedPresentationTimeStampUs; 
} 


private static long getNextDeQueuedPresentationTimeStampUs(){ 
    Log.i(TAG, "nextDequeuedPresentationTimeStampUs: " + (lastDequeuedPresentationTimeStampUs + 1)); 
    lastDequeuedPresentationTimeStampUs ++; 
    return lastDequeuedPresentationTimeStampUs; 
} 

나는 "timestampUs XXX < lastTimestampUs XXX"오류를 방지하기 위해이 예 https://github.com/OnlyInAmerica/HWEncoderExperiments/blob/audiotest/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments/AudioEncodingTest.java에서했다

누군가가 나에게 문제를하시기 바랍니다 알아내는 데 도움이 수 있습니까?

답변

7

비디오에 시스템 제공 타임 스탬프를 사용하는 것처럼 보이지만 오디오에 대한 간단한 카운터입니다. 어떻게 든 비디오 타임 스탬프가 모든 프레임에 오디오를 시드하는 데 사용되는 경우가 아니면 위의 그림과는 다릅니다.

오디오와 비디오를 동기화하여 재생하려면 오디오와 비디오 프레임에 동일한 프리젠 테이션 타임 스탬프가 있어야합니다.이 타임 스탬프는 동시에 제공해야합니다.

본 내용은 related question입니다.

+0

덕분에 많은 fadden, 나는 오디오 및 비디오 동기화 할 수 있었다! 당신은 대단하다 :) – Alexey

+0

나는 하나 더 작은 문제를 가지고있다. 오디오없이 오디오를 재생하면 내 녹음이 더 빨리 재생됩니다. 그리고 오디오 트랙이 왜곡 된 것처럼 보입니다. 오디오가 각 비디오 프레임과 청크에 의해 청크로 작성되기 때문에 이런 일이 발생한다고 생각합니다. 기록 할 청크의 크기는 fps 및 샘플 속도에 따라 계산됩니다. 내 fps가 크게 달라지기 때문에 오디오는 불균등 한 덩어리 때문에 왜곡됩니다. 어쩌면 당신은 그것을 다루는 방법에 대한 제안이 있습니까? – Alexey

+0

불행히도 나는 오디오를 많이하지 않았다. 오디오 타임 스탬프는 현재 어디에서 제공됩니까? 비디오 타임 스탬프 (SurfaceTexture 타임 스탬프)에 대한 좋은 출처가 있습니다. 오디오와 비슷한 것을 사용하고 있거나, System.nanoTime()으로 위장한 것일 것입니다. (미디어 물건은 일반적으로 System.nanoTime()과 같은 시스템 단조 클럭을 사용하지만 일부 부분은 나노초가 아닌 마이크로 초를 사용합니다. – fadden

0

솔루션이 오디오 샘플을 반복적으로 읽는 것일 수 있습니다. 새로운 비디오 프레임이 N 개의 오디오 샘플마다 사용 가능한지 확인할 수 있으며, 새로운 비디오 프레임이 도착하자마자 동일한 타임 스탬프로 muxer에 전달할 수 있습니다.

int __buffer_offset = 0; 
final int CHUNK_SIZE = 100; /* record 100 samples each iteration */ 
while (!__new_video_frame_available) { 
    this._audio_recorder.read(__recorded_data, __buffer_offset, CHUNK_SIZE); 
    __buffer_offset += CHUNK_SIZE; 
} 

나는 그렇게 생각한다.

친절한 안부 볼프람

관련 문제