wineqtdecoder: Add processing for audio in the movie.

This commit is contained in:
Aric Stewart 2011-04-05 12:56:02 -05:00 committed by Alexandre Julliard
parent 2c9875e4c1
commit f1da93da80
1 changed files with 271 additions and 15 deletions

View File

@ -151,12 +151,16 @@ typedef struct QTSplitter {
QTInPin pInputPin;
QTOutPin *pVideo_Pin;
QTOutPin *pAudio_Pin;
ALLOCATOR_PROPERTIES props;
Movie pQTMovie;
QTVisualContextRef vContext;
MovieAudioExtractionRef aSession;
HANDLE runEvent;
DWORD outputSize;
FILTER_STATE state;
} QTSplitter;
@ -165,7 +169,7 @@ static const IPinVtbl QT_OutputPin_Vtbl;
static const IPinVtbl QT_InputPin_Vtbl;
static const IBaseFilterVtbl QT_Vtbl;
static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt);
static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
static HRESULT QT_RemoveOutputPins(QTSplitter *This);
/*
@ -188,8 +192,10 @@ static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
if (This->pVideo_Pin)
IPin_AddRef((IPin*)This->pVideo_Pin);
return (IPin*)This->pVideo_Pin;
case 2: /* TODO: Audio */
return NULL;
case 2:
if (This->pAudio_Pin)
IPin_AddRef((IPin*)This->pAudio_Pin);
return (IPin*)This->pAudio_Pin;
default:
return NULL;
}
@ -199,6 +205,7 @@ static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
{
QTSplitter *This = (QTSplitter *)iface;
int c = 1;
if (This->pAudio_Pin) c++;
if (This->pVideo_Pin) c++;
return c;
}
@ -230,7 +237,10 @@ IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
This->pVideo_Pin = NULL;
This->pAudio_Pin = NULL;
This->state = State_Stopped;
This->aSession = NULL;
This->runEvent = CreateEventW(NULL, 0, 0, NULL);
piInput = &This->pInputPin.pin.pinInfo;
piInput->dir = PINDIR_INPUT;
@ -273,6 +283,9 @@ static void QT_Destroy(QTSplitter *This)
DisposeMovie(This->pQTMovie);
if (This->vContext)
QTVisualContextRelease(This->vContext);
if (This->aSession)
MovieAudioExtractionEnd(This->aSession);
CloseHandle(This->runEvent);
ExitMovies();
CoTaskMemFree(This);
@ -339,7 +352,89 @@ static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
return hr;
}
static DWORD WINAPI QTSplitter_thread_reader(LPVOID data)
static OSErr QT_Create_Extract_Session(QTSplitter *filter)
{
AudioStreamBasicDescription aDesc;
OSErr err;
err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
if (err != noErr)
{
ERR("Failed to begin Extraction session %i\n",err);
return err;
}
err = MovieAudioExtractionGetProperty(filter->aSession,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
sizeof(AudioStreamBasicDescription), &aDesc, NULL);
if (err != noErr)
{
MovieAudioExtractionEnd(filter->aSession);
filter->aSession = NULL;
ERR("Failed to get session description %i\n",err);
return err;
}
aDesc.mFormatID = kAudioFormatLinearPCM;
aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
kAudioFormatFlagIsPacked;
aDesc.mFramesPerPacket = 1;
aDesc.mChannelsPerFrame = 2;
aDesc.mBitsPerChannel = 16;
aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
aDesc.mSampleRate = 48000;
err = MovieAudioExtractionSetProperty(filter->aSession,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
sizeof(AudioStreamBasicDescription), &aDesc);
if (aDesc.mFormatID != kAudioFormatLinearPCM)
{
ERR("Not PCM Wave\n");
err = -1;
}
if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
kAudioFormatFlagIsPacked)
{
ERR("Unhandled Flags\n");
err = -1;
}
if (aDesc.mFramesPerPacket != 1)
{
ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
err = -1;
}
if (aDesc.mChannelsPerFrame != 2)
{
ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
err = -1;
}
if (aDesc.mBitsPerChannel != 16)
{
ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
err = -1;
}
if (aDesc.mSampleRate != 48000)
{
ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
err = -1;
}
if (err != noErr)
{
ERR("Failed to create Extraction Session\n");
MovieAudioExtractionEnd(filter->aSession);
filter->aSession = NULL;
}
return err;
}
static DWORD WINAPI QTSplitter_thread(LPVOID data)
{
QTSplitter *This = (QTSplitter *)data;
HRESULT hr = S_OK;
@ -348,9 +443,21 @@ static DWORD WINAPI QTSplitter_thread_reader(LPVOID data)
OSStatus err;
TimeRecord tr;
if (This->pAudio_Pin)
{
/* according to QA1469 a movie has to be fully loaded before we
can reliably start the Extraction session */
while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
MoviesTask(This->pQTMovie,1000);
QT_Create_Extract_Session(This);
}
WaitForSingleObject(This->runEvent, -1);
This->state = State_Running;
GetMovieTime(This->pQTMovie, &tr);
do
{
LONGLONG tStart=0, tStop=0;
@ -379,8 +486,90 @@ static DWORD WINAPI QTSplitter_thread_reader(LPVOID data)
time = (float)next_time / tr.scale;
tStop = time * 10000000;
/* Deliver Audio */
if (This->pAudio_Pin && ((BaseOutputPin*)This->pAudio_Pin)->pin.pConnectedTo && This->aSession)
{
int data_size=0;
BYTE* ptr;
IMediaSample *sample = NULL;
AudioBufferList aData;
UInt32 flags;
UInt32 frames;
WAVEFORMATEX* pvi;
float duration;
static int audio_frame_size = 0;
pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
if (audio_frame_size == 0)
audio_frame_size = (pvi->wBitsPerSample * pvi->nChannels) / 8;
tr.value = SInt64ToWide(movie_time);
err = MovieAudioExtractionSetProperty(This->aSession,
kQTPropertyClass_MovieAudioExtraction_Movie,
kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
sizeof(TimeRecord), &tr);
if (err != noErr)
{
ERR("Failed to set audio media time\n");
goto audio_error;
}
hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->pAudio_Pin, &sample, NULL, NULL, 0);
if (FAILED(hr))
{
ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
goto audio_error;
}
hr = IMediaSample_GetPointer(sample, &ptr);
if (FAILED(hr))
{
ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
goto audio_error;
}
duration = (float)next_time / tr.scale;
time = (float)movie_time / tr.scale;
duration -= time;
frames = pvi->nSamplesPerSec * duration;
TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
data_size = IMediaSample_GetSize(sample);
if (data_size < frames * audio_frame_size )
FIXME("Audio buffer is too small\n");
aData.mNumberBuffers = 1;
aData.mBuffers[0].mNumberChannels = pvi->nChannels;
aData.mBuffers[0].mDataByteSize = data_size;
aData.mBuffers[0].mData = ptr;
err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
TRACE("Got %i frames\n",(int)frames);
IMediaSample_SetActualDataLength(sample, frames * audio_frame_size);
IMediaSample_SetMediaTime(sample, &tStart, &tStop);
if (tStart)
IMediaSample_SetTime(sample, &tStart, &tStop);
else
IMediaSample_SetTime(sample, NULL, NULL);
hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
TRACE("Audio Delivered (%x)\n",hr);
audio_error:
if (sample)
IMediaSample_Release(sample);
}
else
TRACE("Audio Pin not connected or no Audio\n");
/* Deliver Video */
if (QTVisualContextIsNewImageAvailable(This->vContext,0))
if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
{
err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
if (err == noErr)
@ -437,12 +626,17 @@ static DWORD WINAPI QTSplitter_thread_reader(LPVOID data)
CVPixelBufferRelease(pixelBuffer);
}
}
else
TRACE("No video to deliver\n");
movie_time = next_time;
} while (hr == S_OK);
This->state = State_Stopped;
OutputQueue_EOS(This->pVideo_Pin->queue);
if (This->pAudio_Pin)
OutputQueue_EOS(This->pAudio_Pin->queue);
if (This->pVideo_Pin)
OutputQueue_EOS(This->pVideo_Pin->queue);
return hr;
}
@ -452,7 +646,6 @@ static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
HRESULT hr = S_OK;
QTSplitter *This = (QTSplitter *)iface;
HRESULT hr_any = VFW_E_NOT_CONNECTED;
DWORD tid;
TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
@ -463,12 +656,15 @@ static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->pVideo_Pin);
if (SUCCEEDED(hr))
hr_any = hr;
if (This->pAudio_Pin)
hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->pAudio_Pin);
if (SUCCEEDED(hr))
hr_any = hr;
hr = hr_any;
LeaveCriticalSection(&This->filter.csFilter);
CreateThread(NULL, 0, QTSplitter_thread_reader, This, 0, &tid);
TRACE("Created thread 0x%08x\n",tid);
SetEvent(This->runEvent);
return hr;
}
@ -522,6 +718,13 @@ static HRESULT QT_RemoveOutputPins(QTSplitter *This)
IPin_Release((IPin*)This->pVideo_Pin);
This->pVideo_Pin = NULL;
}
if (This->pAudio_Pin)
{
hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
TRACE("Disconnect: %08x\n", hr);
IPin_Release((IPin*)This->pAudio_Pin);
This->pAudio_Pin = NULL;
}
BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
return S_OK;
@ -631,7 +834,7 @@ static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
piOutput.pFilter = (IBaseFilter *)filter;
lstrcpyW(piOutput.achName,szwVideoOut);
hr = QT_AddPin(filter, &piOutput, &amt);
hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
if (FAILED(hr))
ERR("Failed to add Video Track\n");
else
@ -640,6 +843,45 @@ static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
return hr;
}
static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
{
AM_MEDIA_TYPE amt;
WAVEFORMATEX* pvi;
PIN_INFO piOutput;
HRESULT hr = S_OK;
static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
ZeroMemory(&amt, sizeof(amt));
amt.formattype = FORMAT_WaveFormatEx;
amt.majortype = MEDIATYPE_Audio;
amt.subtype = MEDIASUBTYPE_PCM;
amt.bTemporalCompression = 0;
amt.cbFormat = sizeof(WAVEFORMATEX);
amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
ZeroMemory(amt.pbFormat, amt.cbFormat);
pvi = (WAVEFORMATEX*)amt.pbFormat;
pvi->cbSize = sizeof(WAVEFORMATEX);
pvi->wFormatTag = WAVE_FORMAT_PCM;
pvi->nChannels = 2;
pvi->nSamplesPerSec = 48000;
pvi->wBitsPerSample = 16;
pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
piOutput.dir = PINDIR_OUTPUT;
piOutput.pFilter = (IBaseFilter *)filter;
lstrcpyW(piOutput.achName,szwAudioOut);
hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
if (FAILED(hr))
ERR("Failed to add Audio Track\n");
else
TRACE("Audio Pin %p\n",filter->pAudio_Pin);
return hr;
}
static HRESULT QT_Process_Movie(QTSplitter* filter)
{
HRESULT hr = S_OK;
@ -648,6 +890,7 @@ static HRESULT QT_Process_Movie(QTSplitter* filter)
Handle dataRef = NULL;
Track trk;
short id = 0;
DWORD tid;
TRACE("Trying movie connect\n");
@ -655,7 +898,9 @@ static HRESULT QT_Process_Movie(QTSplitter* filter)
ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieIdleImportOK, &id, dataRef, 'WINE');
err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
DisposeHandle(dataRef);
if (err != noErr)
{
@ -679,6 +924,14 @@ static HRESULT QT_Process_Movie(QTSplitter* filter)
if (!SUCCEEDED(hr))
return hr;
trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
TRACE("%p is a audio track\n",trk);
if (trk)
hr = QT_Process_Audio_Track(filter, trk);
CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
TRACE("Created thread 0x%08x\n",tid);
return hr;
}
@ -1032,16 +1285,19 @@ static const OutputQueueFuncTable output_OutputQueueFuncTable = {
OutputQueueImpl_ThreadProc
};
static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt)
static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
{
HRESULT hr;
IPin **target;
target = (IPin**)&This->pVideo_Pin;
if (video)
target = (IPin**)&This->pVideo_Pin;
else
target = (IPin**)&This->pAudio_Pin;
if (*target != NULL)
{
ERR("We we already have a video pin\n");
FIXME("We already have a %s pin\n",(video)?"video":"audio");
return E_FAIL;
}