2017-10-12 5 views
0

私のC#プロジェクトでは、画像とオーディオをmp4コンテナベースにエンコードするためのマネージC++ライブラリを作成しましたMSDNチュートリアルSinkWriterにあります。結果が正常かどうかをテストするために、私は600フレームを提供するメソッドを作成しました。このフレームは、毎秒60フレームの10秒のビデオを表します。MF SinkWriter mp4ファイルからの再生時間は、オーディオサンプルを追加する時間の半分です。画像の再生速度も2倍です

画像は私が毎秒変化を与えると私のオーディオファイルは、私が直面しています問題は、出力ビデオがactualyのみ5秒の長さであるということである10

までカウントする声が含まれています。ビデオのメタデータは10秒であることを示していますが、そうではありません。また、音声はほとんど5までカウントされます。

音声部分のない画像サンプルだけを書き込むと、ビデオの所要時間は約10秒です。

私はここで何が欠けていますか?

ここに私のアプリケーションのいくつかの部分があります。

これは600フレームを作成するために使用しているc#の部分です。次に、C#の部分でもPushFrameメソッドを呼び出します。

var videoFrameCount = 10 * FPS; 
SetBinaryImage(); 

for (int i = 0; i <= videoFrameCount; i++) 
{ 
    // New picture every second 
    if (i > 0 && i % FPS == 0) 
    { 
     SetBinaryImage(); 
    } 

    PushFrame(); 
} 

PushFrameメソッドは、イメージとオーディオデータをSinkWriterが提供するポインタにコピーします。それから私はSinkWriterのPushFrameメソッドを呼び出します。ここで

private void PushFrame() 
{ 
    try 
    { 
     encodeStopwatch.Reset(); 
     encodeStopwatch.Start(); 

     // Video 
     var frameBufferHandler = GCHandle.Alloc(frameBuffer, GCHandleType.Pinned); 
     frameBufferPtr = frameBufferHandler.AddrOfPinnedObject(); 
     CopyImageDataToPointer(BinaryImage, ScreenWidth, ScreenHeight, frameBufferPtr); 

     // Audio 
     var audioBufferHandler = GCHandle.Alloc(audioBuffer, GCHandleType.Pinned); 
     audioBufferPtr = audioBufferHandler.AddrOfPinnedObject(); 
     var readLength = audioBuffer.Length; 

     if (BinaryAudio.Length - (audioOffset + audioBuffer.Length) < 0) 
     { 
      readLength = BinaryAudio.Length - audioOffset; 
     } 

     if (!EndOfFile) 
     { 
      Marshal.Copy(BinaryAudio, audioOffset, (IntPtr)audioBufferPtr, readLength); 
      audioOffset += audioBuffer.Length; 

     } 

     if (readLength < audioBuffer.Length && !EndOfFile) 
     { 
      EndOfFile = true; 
     } 

     unsafe 
     { 
      // Copy video data 
      var yuv = SinkWriter.VideoCapturerBuffer(); 
      SinkWriter.Encode((byte*)frameBufferPtr, ScreenWidth, ScreenHeight, (int)SWPF.SWPF_RGB, yuv); 

      // Copy audio data 
      var audioDestPtr = SinkWriter.AudioCapturerBuffer(); 
      SinkWriter.EncodeAudio((byte*)audioBufferPtr, audioDestPtr); 

      SinkWriter.PushFrame(); 
     } 

     encodeStopwatch.Stop(); 
     Console.WriteLine($"YUV frame generated in: {encodeStopwatch.TakeTotalMilliseconds()} ms"); 
    } 
    catch (Exception ex) 
    { 
    } 
} 

は、私はC++の中でSinkWriterに追加いくつかの部品です。オーディオの再生が機能するので、オーディオ部分のMediaTypesはOKです。

rtStartとrtDurationこのように定義される:エンコーダから

LONGLONG rtStart = 0; 
UINT64 rtDuration; 
MFFrameRateToAverageTimePerFrame(fps, 1, &rtDuration); 

二つのバッファがこの

int SinkWriter::Encode(Byte * rgbBuf, int w, int h, int pxFormat, Byte * yufBuf) 
{ 
    const LONG cbWidth = 4 * VIDEO_WIDTH; 
    const DWORD cbBuffer = cbWidth * VIDEO_HEIGHT; 

    // Create a new memory buffer. 
    HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pFrameBuffer); 

    // Lock the buffer and copy the video frame to the buffer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pFrameBuffer->Lock(&yufBuf, NULL, NULL); 
    } 

    if (SUCCEEDED(hr)) 
    { 
     // Calculate the stride 
     DWORD bitsPerPixel = GetBitsPerPixel(pxFormat); 
     DWORD bytesPerPixel = bitsPerPixel/8; 
     DWORD stride = w * bytesPerPixel; 

     // Copy image in yuv pointer 
     hr = MFCopyImage(
      yufBuf,      // Destination buffer. 
      stride,     // Destination stride. 
      rgbBuf,  // First row in source image. 
      stride,     // Source stride. 
      stride,     // Image width in bytes. 
      h    // Image height in pixels. 
     ); 
    } 

    if (pFrameBuffer) 
    { 
     pFrameBuffer->Unlock(); 
    } 

    // Set the data length of the buffer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pFrameBuffer->SetCurrentLength(cbBuffer); 
    } 

    if (SUCCEEDED(hr)) 
    { 
     return 0; 
    } 
    else 
    { 
     return -1; 
    } 

    return 0; 
} 

int SinkWriter::EncodeAudio(Byte * src, Byte * dest) 
{ 
    DWORD samplePerSecond = AUDIO_SAMPLES_PER_SECOND * AUDIO_BITS_PER_SAMPLE * AUDIO_NUM_CHANNELS; 
    DWORD cbBuffer = samplePerSecond/1000; 

    // Create a new memory buffer. 
    HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pAudioBuffer); 

    // Lock the buffer and copy the video frame to the buffer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pAudioBuffer->Lock(&dest, NULL, NULL); 
    } 

    CopyMemory(dest, src, cbBuffer); 

    if (pAudioBuffer) 
    { 
     pAudioBuffer->Unlock(); 
    } 

    // Set the data length of the buffer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pAudioBuffer->SetCurrentLength(cbBuffer); 
    } 

    if (SUCCEEDED(hr)) 
    { 
     return 0; 
    } 
    else 
    { 
     return -1; 
    } 

    return 0; 
} 

ように使用される。これはSinkWriterを通過SinkWriterのPushFrame方法、 streamIndex、audioIndex、rtStart、およびrtDurationをWriteFrameメソッドに追加します。

int SinkWriter::PushFrame() 
{ 
    if (initialized) 
    { 
     HRESULT hr = WriteFrame(ptrSinkWriter, stream, audio, rtStart, rtDuration); 
     if (FAILED(hr)) 
     { 
      return -1; 
     } 

     rtStart += rtDuration; 

     return 0; 
    } 

    return -1; 
} 

ここでは、ビデオとオーディオのサンプルを組み合わせたWriteFrameメソッドについて説明します。

HRESULT SinkWriter::WriteFrame(IMFSinkWriter *pWriter, DWORD streamIndex, DWORD audioStreamIndex, const LONGLONG& rtStart, const LONGLONG& rtDuration) 
{ 
    IMFSample *pVideoSample = NULL; 

    // Create a media sample and add the buffer to the sample. 
    HRESULT hr = MFCreateSample(&pVideoSample); 

    if (SUCCEEDED(hr)) 
    { 
     hr = pVideoSample->AddBuffer(pFrameBuffer); 
    } 
    if (SUCCEEDED(hr)) 
    { 
     pVideoSample->SetUINT32(MFSampleExtension_Discontinuity, FALSE); 
    } 
    // Set the time stamp and the duration. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pVideoSample->SetSampleTime(rtStart); 
    } 
    if (SUCCEEDED(hr)) 
    { 
     hr = pVideoSample->SetSampleDuration(rtDuration); 
    } 

    // Send the sample to the Sink Writer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pWriter->WriteSample(streamIndex, pVideoSample); 
    } 

    // Audio 
    IMFSample *pAudioSample = NULL; 

    if (SUCCEEDED(hr)) 
    { 
     hr = MFCreateSample(&pAudioSample); 
    } 

    if (SUCCEEDED(hr)) 
    { 
     hr = pAudioSample->AddBuffer(pAudioBuffer); 
    } 

    // Set the time stamp and the duration. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pAudioSample->SetSampleTime(rtStart); 
    } 
    if (SUCCEEDED(hr)) 
    { 
     hr = pAudioSample->SetSampleDuration(rtDuration); 
    } 
    // Send the sample to the Sink Writer. 
    if (SUCCEEDED(hr)) 
    { 
     hr = pWriter->WriteSample(audioStreamIndex, pAudioSample); 
    } 


    SafeRelease(&pVideoSample); 
    SafeRelease(&pFrameBuffer); 
    SafeRelease(&pAudioSample); 
    SafeRelease(&pAudioBuffer); 
    return hr; 
} 
+0

'pVideoSample-> SetSampleTime'引数はおそらくあなたが望むものの半分です。少なくとも、デバッガでこれをチェックして、そのことを判断する必要があります。 –

+0

「SetSampleTime」は、AudioSampleを削除すると、ビデオの再生時間と有効時間が有効になるため、問題ありません。しかし、私は時間を2倍にしようとします。 編集: 私はそれを倍増させようとしましたが、ビデオの作成はもうできません。重複フレームの後、残りのフレームを生成するまでに時間がかかります。 – datoml

答えて

0

問題は、オーディオのバッファサイズの計算が間違っていたことでした。私は1ミリ秒のためのバッファサイズを持っていた私の質問に

var avgBytesPerSecond = sampleRate * 2 * channels; 
var avgBytesPerMillisecond = avgBytesPerSecond/1000; 
var bufferSize = avgBytesPerMillisecond * (1000/60); 
audioBuffer = new byte[bufferSize]; 

: これは右の計算です。だから、MFフレームワークが画像をスピードアップして、オーディオがうまく聞こえます。私はバッファサイズを固定した後、ビデオはまさに私が予想した時間を持ち、サウンドにもエラーはありません。

関連する問題