Comment lire depuis - «unsigned char const *» lorsque vous utilisez Media Foundation?
J'ai une telle implémentation
void coAudioPlayerSampleGrabber::test(SoundDataType dataType,
unsigned char const * pData,
int64_t dataLen)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
IMFSourceReader *pReader = NULL;
IMFByteStream * spByteStream = NULL;
HRESULT hr = S_OK;
// Initialize the COM library.
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Initialize the Media Foundation platform.
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
}
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
if (FAILED(hr))
{
printf("Error MFCreateMFByteStreamOnStreamEx");
}
IMFAttributes * Atrr = NULL;
hr = MFCreateAttributes(&Atrr, 10);
if (FAILED(hr))
{
printf("Error MFCreateAttributes");
}
hr = Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);
if (FAILED(hr))
{
printf("Error Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)");
}
hr = MFCreateSourceReaderFromByteStream(spByteStream, Atrr, &pReader);
if (FAILED(hr))
{
printf("Error MFCreateSourceReaderFromByteStream");
}
if (FAILED(hr))
{
printf("Error opening input file");
}
IMFMediaType *pAudioType = NULL; // Represents the PCM audio format.
hr = ConfigureAudioStream(dataType, pReader, &pAudioType);
if (FAILED(hr))
{
printf("Error ConfigureAudioStream");
}
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
BYTE *pAudioData = NULL;
DWORD cbBuffer = 0;
std::vector<SampleData> samples_vec;
while (true)
{
DWORD dwFlags = 0;
hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &dwFlags, NULL, &pSample);
if (FAILED(hr)) { break; }
if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
printf("Type change - not supported by WAVE file format.\n");
break;
}
if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of input file.\n");
break;
}
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (FAILED(hr)) { break; }
hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
if (FAILED(hr)) { break; }
//Do something with the pAudioData which is an array of unsigned chars of lenth cbBuffer
SampleData tmp;
tmp.pAudioData = new byte[cbBuffer];
memcpy(tmp.pAudioData, pAudioData, cbBuffer);
tmp.cbBuffer = cbBuffer;
samples_vec.push_back(tmp);
// Unlock the buffer.
hr = pBuffer->Unlock();
pAudioData = NULL;
if (FAILED(hr)) { break; }
}
SafeRelease(&pReader);
SafeRelease(&pSample);
SafeRelease(&pBuffer);
SafeRelease(&spByteStream);
SafeRelease(&Atrr);
// Shut down Media Foundation.
MFShutdown();
CoUninitialize();
}
Donc, comme vous pouvez le voir, j'ai un pointeur sur les données et la taille, ce sont en fait mes données dont j'ai besoin pour les décoder. Le problème est qu'ici
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
Je suis une erreur access violation
et pour autant que je vois est ce que je tente de convertir pData
à IUnknown*
. La question est - Comment le convertir correctement?
Réponses
Vous ne pouvez pas couper les coins comme ça:
unsigned char const * pData;
...
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
IUnknown
n'est pas encore un autre alias sophistiqué pour un octet. Vous êtes censé fournir littéralement un pointeur d'interface représentant le flux, comme documenté.
Media Foundation vous offre des moyens de lire à partir d'octets de mémoire. Vous devez créer un créer un flux réel, IStream
ou IRandomAccessStream
ou IMFByteStream
par docuemntation. Fournissez également que IMFAttributes
vous avez créé les attributs appropriés pour spécifier le type de données (qui, sinon, dans le cas d'un fichier, sont dérivés de l'extension ou du type MIME), puis l'API Source Reader serait en mesure de traiter les octets de mémoire comme source de données de fichier multimédia et décodeur approprié décoderait l'audio en données PCM (similaire à cela ).
Quelque chose que vous pouvez faire très rapidement: CreateStreamOnHGlobalcréer une IStream
implémentation et copier vos octets dans le tampon sous-jacent (voir la documentation). Ensuite MFCreateMFByteStreamOnStreamcréerait un IMFByteStream
wrappr dessus, et vous pouvez utiliser ce wrapper comme MFCreateSourceReaderFromByteStream
argument.
J'utilisais une classe VectorStream pour ce genre de choses. Certaines fonctions ne sont pas implémentées, mais vous devriez avoir l'idée de base.
class VectorStream : public IStream
{
public:
bool ReadOnly = false;
ULONG r = 1;
std::vector<char> d;
size_t p = 0;
VectorStream()
{
}
void Clear()
{
d.clear();
p = 0;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IStream))
{
*ppvObject = (IStream*)this;
r++;
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return ++r;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
return --r;
}
HRESULT __stdcall Clone(
IStream** ppstm
)
{
return E_NOTIMPL;
}
HRESULT __stdcall Commit(
DWORD grfCommitFlags
)
{
return S_OK;
}
HRESULT __stdcall CopyTo(
IStream* pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER* pcbRead,
ULARGE_INTEGER* pcbWritten
)
{
return E_NOINTERFACE;
}
HRESULT __stdcall LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall Revert()
{
return E_NOTIMPL;
}
HRESULT __stdcall Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER* plibNewPosition
)
{
LARGE_INTEGER lo = { 0 };
if (dwOrigin == STREAM_SEEK_SET)
{
p = dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_CUR)
{
p += dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_END)
{
p = d.size() - dlibMove.QuadPart;
}
if (p >= d.size())
p = d.size();
if (plibNewPosition)
plibNewPosition->QuadPart = p;
return S_OK;
}
HRESULT __stdcall SetSize(
ULARGE_INTEGER libNewSize
)
{
d.resize(libNewSize.QuadPart);
return S_OK;
}
int eb = 0;
HRESULT __stdcall Stat(
STATSTG* pstatstg,
DWORD grfStatFlag
)
{
pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.QuadPart = d.size();
pstatstg->grfLocksSupported = true;
return S_OK;
}
unsigned long long readbytes = 0;
HRESULT __stdcall Read(
void* pv,
ULONG cb,
ULONG* pcbRead
)
{
auto av = d.size() - p;
if (cb < av)
av = cb;
memcpy(pv, d.data() + p, av);
p += av;
if (pcbRead)
*pcbRead = (ULONG)av;
// if (av < cb)
// return S_FALSE;
return S_OK;
}
HRESULT __stdcall Write(
const void* pv,
ULONG cb,
ULONG* pcbWritten
)
{
if (ReadOnly)
return STG_E_ACCESSDENIED;
if (d.size() < (p + cb))
{
auto exc = (p + cb) - d.size();
d.resize(d.size() + exc);
}
memcpy(d.data() + p, pv, cb);
p += cb;
if (pcbWritten)
*pcbWritten = cb;
return S_OK;
}
};
Il encapsule std :: vector <> dans un IStream.