GetOutputStatus says output is ready, ProcessOutput says NEED_MORE_INPUT - c

I am getting stuck in this situation where I get conflicting information:
hr = pDecoder->lpVtbl->ProcessInput(pDecoder, dwInputStreamID, pSample, dwFlags);
if (FAILED(hr) ... )
//ProcessInput went well, no warnings from here.
hr = pDecoder->lpVtbl->GetOutputStatus(pDecoder, &dwFlags);
if (SUCCEEDED(hr)) {
if (dwFlags == MFT_OUTPUT_STATUS_SAMPLE_READY) {
// I get to here, sample is ready, yay!
}
}
dwFlags = 0;
hr = pDecoder->lpVtbl->GetInputStatus(pDecoder, 0, &dwFlags);
if (SUCCEEDED(hr)) {
if (dwFlags == MFT_INPUT_STATUS_ACCEPT_DATA) {
//...
} else {
// we go here, input does not accept more data it seems.
// Sounds ok, we read the output that is ready and then we fill in more
}
}
dwFlags = 0;
hr = pDecoder->lpVtbl->ProcessOutput(pDecoder,
dwFlags,
1,
pOutputSamples,
&pdwStatus
);
if (FAILED(hr)) {
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
// Ok, but why did GetOutputStatus say we were ready then?
}
}
// Calling GetOutputStatus to see whats going on
hr = pDecoder->lpVtbl->GetOutputStatus(pDecoder, &dwFlags);
if (SUCCEEDED(hr)) {
if (dwFlags == MFT_OUTPUT_STATUS_SAMPLE_READY) {
// nope.
} else {
// Now dwFlags is 0.
}
}
hr = pDecoder->lpVtbl->GetInputStatus(pDecoder, 0, &dwFlags);
if (SUCCEEDED(hr)) {
if (dwFlags == MFT_INPUT_STATUS_ACCEPT_DATA) {
// this time we go here, we can now give more input again.
// but we got no data from ProcessOutput
} else {
//...
}
}
dwFlags = 0;
Looking at the data media sample I've sent to processOutput to be filled in it simply writes a null terminator '\0' in the beginning of the buffer but otherwise it does not write any output.
GetOutputStatus
If the method returns the MFT_OUTPUT_STATUS_SAMPLE_READY flag, it
means you can generate one or more output samples by calling
IMFTransform::ProcessOutput.
...
After the client has set valid media
types on all of the streams, the MFT should always be in one of two
states: Able to accept more input, or able to produce more output.
I got no errors earlier while setting up the decoders input and output streams so Im thinking the streams should be good. And I've not got any warnings while sending in the input media either so Im thinking I should be in a valid state. But the behaviour does not seem to match what I think the documentation is suggesting. Also I only have 1 input and 1 output stream if that is of interest.
So how could this happen? I have conflicting information from the tool. Is the data ready but I am reading it wrong, or is there something else going on?
Edit:
There were a few comments asking for more information, and one asking for a minimum complete example so I decided to try it out. Below is a small c program that runs all the things I run and it simulates my environment by reading input from a file and sending it in the same way I am getting my data. I have striped out almost all error handling, removed helper functions and hardcoded a few things. This program reproduces the issue. I am running this in Visual Studio 2015.
#include <stdlib.h>
//windows media foundation test
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <stdio.h>
#include <mferror.h>
int chunk_handler(unsigned char* pBuf, unsigned short length);
IMFTransform *pDecoder = NULL;
DWORD dwInputStreamID;
DWORD dwOutputStreamID;
// inspierd by FindDecoder here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms701774(v=vs.85).aspx
HRESULT FindDecoder(
IMFTransform **ppDecoder // Receives a pointer to the decoder.
)
{
HRESULT hr = S_OK;
UINT32 count = 0;
IMFActivate **ppActivate = NULL;
MFT_REGISTER_TYPE_INFO inputType = { 0 };
inputType.guidMajorType = MFMediaType_Audio;
inputType.guidSubtype = MFAudioFormat_AAC;
hr = MFTEnumEx(
MFT_CATEGORY_AUDIO_DECODER,
MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER,
&inputType, // Input type
NULL, // Output type
&ppActivate,
&count
);
if (SUCCEEDED(hr) && count == 0)
{
hr = MF_E_TOPO_CODEC_NOT_FOUND;
}
// Create the first decoder in the list.
if (SUCCEEDED(hr))
{
hr = ppActivate[0]->lpVtbl->ActivateObject(ppActivate[0], &IID_IMFTransform, (IUnknown**)ppDecoder);
}
for (UINT32 i = 0; i < count; i++)
{
ppActivate[i]->lpVtbl->Release(ppActivate[i]);
}
CoTaskMemFree(ppActivate);
return hr;
}
int main()
{
UINT32 samplesPerSec = 44100;
UINT32 bitsPerSample = 16;
UINT32 cChannels = 2;
HRESULT hr = S_OK;
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
DWORD dwFlags = MFSTARTUP_FULL;
hr = MFStartup(MF_VERSION, dwFlags);
// Create decoder
pDecoder = NULL;
hr = FindDecoder(&pDecoder);
// Create input and output audio types
IMFMediaType *pMediaIn = NULL; // Pointer to an encoded audio type.
IMFMediaType *pMediaOut = NULL; // Receives a matching PCM audio type.
/* Create pMediaIn */
// Calculate derived values.
UINT32 blockAlign = cChannels * (bitsPerSample / 8);
UINT32 bytesPerSecond = blockAlign * samplesPerSec;
// Create the empty media type.
hr = MFCreateMediaType(&pMediaIn);
// Set attributes on the type.
hr = pMediaIn->lpVtbl->SetGUID(pMediaIn, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
hr = pMediaIn->lpVtbl->SetGUID(pMediaIn, &MF_MT_SUBTYPE, &MFAudioFormat_AAC);
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0x2a); // value can be found in my onenote, (AAC Profile, Level 4)
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AAC_PAYLOAD_TYPE, 3); // (LOAS/LATM)
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AUDIO_NUM_CHANNELS, cChannels);
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec);
// blockAlign, bytesPerSecond and independent samples were commented out previously
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
hr = pMediaIn->lpVtbl->SetUINT32(pMediaIn, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
//first 12 bytes:
//wPayloadType  = 0 (raw AAC)
//wAudioProfileLevelIndication  = 0x29 (AAC Profile, Level 2)
//wStructType  = 0
// The last two bytes of MF_MT_USER_DATA contain the value of AudioSpecificConfig(), as defined by MPEG - 4.
// 00010 0100 0010 000
//AudioSpecificConfig.audioObjectType = 2 (AAC LC) (5 bits)
//AudioSpecificConfig.samplingFrequencyIndex = 4 (4 bits) (44100hz)
//AudioSpecificConfig.channelConfiguration = 2 (4 bits)
//GASpecificConfig.frameLengthFlag = 0 (1 bit)
//GASpecificConfig.dependsOnCoreCoder = 0 (1 bit)
//GASpecificConfig.extensionFlag = 0 (1 bit)
UINT8 audioSpecificConfig[] = { 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x10 };
hr = pMediaIn->lpVtbl->SetBlob(pMediaIn, &MF_MT_USER_DATA, audioSpecificConfig, 14);
/* Create pMediaOut */
// Create the empty media type.
hr = MFCreateMediaType(&pMediaOut);
// Set attributes on the type.
hr = pMediaOut->lpVtbl->SetGUID(pMediaOut, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
hr = pMediaOut->lpVtbl->SetGUID(pMediaOut, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_AUDIO_NUM_CHANNELS, cChannels);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
hr = pMediaOut->lpVtbl->SetUINT32(pMediaOut, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
// Figure out streamcounts and ID's
DWORD inputStreamCount = 0;
DWORD outputStreamCount = 0;
hr = pDecoder->lpVtbl->GetStreamCount(pDecoder, &inputStreamCount, &outputStreamCount); // both StreamCounts == 1
DWORD dwInputID[1] = { 0 }; //hardcoded
DWORD dwOutputID[1] = { 0 }; //hardcoded
hr = pDecoder->lpVtbl->GetStreamIDs(pDecoder, inputStreamCount, dwInputID, outputStreamCount, dwOutputID);
if (FAILED(hr)) {
if (hr == E_NOTIMPL) {
// This is expected and quite ok.
}
}
dwInputStreamID = dwInputID[0];
dwOutputStreamID = dwOutputID[0];
// configure decoder for the two audio types
hr = pDecoder->lpVtbl->SetInputType(pDecoder, dwInputStreamID, pMediaIn, 0);
dwFlags = 0;
hr = pDecoder->lpVtbl->SetOutputType(pDecoder, dwOutputStreamID, pMediaOut, dwFlags);
/*one time setup is now done.*/
// simulate sending in the first couple of chunks that I can get while trying to decode audio
// Reading this from file, again this is just read from a file in this example, in my real application I get the data sent to me in audio frame chunks.
// For example the first "chunk" of data is:
// 47fc 0000 b090 8003 0020 2066 0001 9800 0de1 2000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 001c
errno_t err;
FILE *file = NULL;
fopen_s(&file, "input.txt", "rb");
unsigned char line[10000]; //big enough
#define NR_OF_INPUTS 14
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
int sizes[NR_OF_INPUTS] = { 42, 42, 42, 42, 42, 340, 708, 503, 477, 493, 499, 448, 640, 511}; // lengths of the data
int i, j;
for (i = 0; i < NR_OF_INPUTS; i++) {
fread(line, sizeof(char), sizes[i], file);
printf("Input chunk number: %d\n", i);
for (j = 0; j < sizes[i]; j++) {
printf(" %02x", line[j]);
}
printf("\n\n");
chunk_handler(line, sizes[i]);
}
fclose(file);
return 0;
}
int chunk_handler(unsigned char* pBuf, unsigned short length) {
const UINT SamplesPerSecond = 44100;
const UINT ChannelCount = 2;
const UINT SampleCount = length * ChannelCount;
const UINT BitsPerSample = 16;
const UINT BufferLength = BitsPerSample / 8 * ChannelCount * length;
const LONGLONG sampleDuration = (long long)length * (long long)10000000 / SamplesPerSecond; // in hns (hecto nano second?) 0.000 000 1. (Duration of the sample, in 100-nanosecond units., see IMFSample)
HRESULT hr = S_OK;
DWORD dwFlags = 0;
/* Setup for processInput */
IMFSample *pSample = NULL;
IMFMediaBuffer *pInputBuffer = NULL;
hr = MFCreateMemoryBuffer(
length, // Amount of memory to allocate, in bytes.
&pInputBuffer
);
BYTE *pData = NULL;
hr = pInputBuffer->lpVtbl->Lock(pInputBuffer, &pData, NULL, NULL);
memcpy_s(pData, length, pBuf, length);
hr = pInputBuffer->lpVtbl->SetCurrentLength(pInputBuffer, length);
hr = pInputBuffer->lpVtbl->Unlock(pInputBuffer);
hr = MFCreateSample(&pSample);
hr = pSample->lpVtbl->AddBuffer(pSample, pInputBuffer);
//hr = pSample->lpVtbl->SetUINT32(pSample, &MFSampleExtension_Discontinuity, TRUE);
/* Setup for processOutput */
#define SAMPLES_PER_BUFFER 1
MFT_OUTPUT_DATA_BUFFER pOutputSamples[SAMPLES_PER_BUFFER];
MFT_OUTPUT_STREAM_INFO streamInfo = { 0,0,0 };
MFT_OUTPUT_STREAM_INFO *pStreamInfo = &streamInfo;
DWORD pdwStatus = 0;
hr = pDecoder->lpVtbl->GetOutputStreamInfo(pDecoder, dwOutputStreamID, pStreamInfo);
IMFSample *pOutSample = NULL;
DWORD minimumSizeOfBuffer = pStreamInfo->cbSize;
IMFMediaBuffer *pOutputBuffer = NULL;
// code checking for if MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLE and such, Turns out client (me) ne4ed to provide sample so lets do that
// Create the media buffer.
hr = MFCreateMemoryBuffer(
minimumSizeOfBuffer, // Amount of memory to allocate, in bytes.
&pOutputBuffer
);
hr = MFCreateSample(&pOutSample);
hr = pOutSample->lpVtbl->AddBuffer(pOutSample, pOutputBuffer);
pOutputSamples[0].pSample = pOutSample;
pOutputSamples[0].dwStreamID = dwOutputStreamID;
pOutputSamples[0].dwStatus = 0;
pOutputSamples[0].pEvents = NULL;
//INPUT
hr = pDecoder->lpVtbl->ProcessInput(pDecoder, dwInputStreamID, pSample, dwFlags);
if (FAILED(hr)) {
if (hr == MF_E_NOTACCEPTING) {
printf("Input cannot take more data\n");
}
printf("error in ProcessInput\n");
}
//OUTPUT
hr = pDecoder->lpVtbl->ProcessOutput(pDecoder,
dwFlags,
SAMPLES_PER_BUFFER,
pOutputSamples,
&pdwStatus
);
if (FAILED(hr)) {
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
// this is ok, just need to make more calls to ProcessInput
printf("ProcessOutput needs more input\n");
}
else {
printf("error in ProcessOutput\n");
}
}
return 0;
}
The "file.txt" references in the code should contain the following inserted as hex:
47FC0000B090800300202066000198000DE120000000000000000000000000000000000000000000001C47FC0000B090800300202066000198000DE120000000000000000000000000000000000000000000001C47FC0000B090800300202066000198000DE120000000000000000000000000000000000000000000001C47FC0000B090800300202066000198000DE120000000000000000000000000000000000000000000001C47FC0000B090800300202066200198800DE120000000000000000000000000000000000000000000001C47FC0000B090800300FF4A214DE73987D722230566AD966E80B72A99797BFFB7978EB3DB9E248875BA38E42B7924EF58A2CCC578931AE67157BF6E7E3DC51B70265C888DA8CB0074753F9F0F3EF9B0F70CD8D5B2363C3B9CE1275DE0E4E7313C6F88FC611D87A932D4263BDFB8C74B5E9052DD5046AF66EA3AB55F9E2186ABCC5A72A3664F1CA21CC678AC24CBECD3797D7C2C50B335556E7DF5E51E5BECBEA1337CA71ACB7ACFD9EDABE47139CB0695F8D575EAB0E461BB5336ED00E2F0F5F381CAD2E9E2C0750FAE43460E9B372CCD016EE6E5547FF4F89EBACF3BE2EA21D7E9038E190CA4D46DB54633FCB9331E830DC6AFB7A1325F0E79DD1C6D886320F8C728DAA34AD00CA999CC089702E7CD7FD8EDEB1FEFFAB63F8D9E798CD483BE697B314BF749F17967FE9F46688D45D8D6B926300840223B9AD2B7291B288518835BB2629B61D79EAB5760D16FBEA3B909D4E7F56747FC0000B090800300FFFFB4216BD505EE8504B0D098342810840D0AF6CCCD71DEADAD6EB2F7117B85CAE255CC5C39130EF0EA7A7F7565275CB6CCBB9E70843BA4847444976535677F95480D0AEC0D0A63F40F1BF7B645AB70307D0262A388BBB2DE0F90E12FF5B46A2EAEB1C8E85C50061833EF9324731B4F7D29580BE0FA0DFAB6AA78341FF25422B0A7039F64C34BA063763E54D489805ADE242371C3C865F17360ADFD3956E6A6AC18D8463879AC4FA70D0ADAC9C5E146F5BB6541F8ACB2EEA53D1EF35EB0ED715F7C4B9A1A072908A66CE92A76328E43010B3820EC9E255C388BC5DE3E02D1393EA9FC70CD44555DE9F6E419C20F804F0361AC702CFFBDD6DEC4BBF6C7736AA1F207CFFC9934E9DBF039FE469D1B2B50B4507E8F6326BA9BD42B71A1A7AE74D23FF1F89CDAF27D614D3C2F8AD767004AC2421E11D8B86B058DEF88EB9A9C8E8C35CF980D0A56664CF27385181987F7878D06AB6F7E46200764841CB0820195C383613579C8836BA8622C06DF3889D200773EDC28AF89E3656DEE0699E66DDB9E7070EFB96BB024F2B4155A9E7806649A8C00A383586956581A0602015141282C140B100CF358BE372EEF5B65E51ACDEB2EDC4A9ADAD00D10420408761E859304BCD8828E72EEEC4AD4219560E77946692F3C51EA9BE12AC6431DFF674CF94931D357B6EF90E912A3B9ED135C6D620EBF7D887A38CC78E67B543838ABA936E67848A2744AF716CB8955C1CE9FB4784271584E49DE019496B654C6B413D74AA09ACFDF68233F39F176931E10A093B0C47961BC72F87C31FC3EA92BF55438EB90345122D1DE3BA14653C459A53C0E5CA3D8ED1097DDD3D5DB39C645D5E07396C76131C03B329A3C98E65A19E63380B51105F21089088038309AE0E4A17C6D6D827D5D85FF804805BAFC4441580F7FE5872EC3DD9C9C00312022F67A4D40AF900D0A1A5427DA204E113C93931E9A0209F7CD2303102E9F674A3B2B706E47FC0000B090800300FFEA210BD5159E8F6982B1602C703386BC713BE1AA54E75B0BA9092E29726E03838C74AADE6D55F03227C9F0F84F90159F2C64D517A77D6F1D69258146F8A3397C3BBA296084B027A9E6581390F1A00E4A3A26E67944B2F09DDEF851344D3BF2DA8A8B11C1C00750A7B34A310D04481F02AE190D0A41F83BD088CE2ED2BE9FE1819099C7A22D5C6738C32DE20F2201D6A603A2082DA21D6A677E2E471044B72C64C6085C734BD1C8D9E1287E93A588C4BAFB075E0D0A3751BC5ED4009F7F7E2500A8394D0B3D0D0A18F9CA85996BB1651362C72A739DE23A9ECDCF6CE771C523B42CB0A698213E554967559681AB800574D36050440CA4D00E48AA71748E65F5CAF6BAF7B01A64803C453ED2093582B6236DDE26AB2B2BC500002A129D1A56FA19400000E22952C4AE3BA298876484D89D4A832819C4D733AF5E6BADCACAE37CA17624B936E2A77A0914FB393C13DB11C2EF9C7E039F0718E7FFA578FDD875ADE3B4F3FAB20CF08AC721C4182E60E5A2D4DBCDA1810F88D64ADDA7CAD3981ABD3AD8E114BD505EBD8D5D7CBC9967899697B38D411ABAE11B391718EE9BCFAB917196FCC2B910B9170351CBAEB97577CD6F1D3215B0E72EAC739176AEAD6009AEBF56B882BE32319DFDFD33D5F28BA0706BA273601740829B8BADCE4537BD6BEF9BE0F9D2CBE60884F9225109718E47FC0000B090800300FFCF210BD51DB18561A4C0D9683033CD5F8EB5EB55ABDC64ADD244B26A26489960D9D84969A878658210307F2D01CE29359116C0739E92EA87B35B0D0AD5F223F143BE0D5D6EB2B69CCACFDECB8C36B5D9B771E03295998B9A62498D887106EC5E918E0C30076F5ECE6403391363A62CB44CBB6EFFD74CAC0CF90D0A2ADDC490E2306145701D85B1A9CA12C258CB7D81532D99FA8DF3C7A1894357950D0A0C0B7CBDB8D297200CFAAD4AEBC92BBDEE087AA9BFF6A49C2B7A7BC66900EBD78F859330033EB9C009C649B8A0E99E774E3ACC0C01DED63CC2247BB5A3C857D2D50D73D92C5BD345ECB6E9A6FEC7000D0AC2F870CEA2AB20BE4057C9EF9922083AA2ED64350DB514A84A0405E3CE51B6FAE53316CBFA2C87B393C5CDAEFB9561FB12DEC4C75F3C69C876464D094EC255825E57DF5AED77158BAEE979A9AA4BBCE372507AA149F8A2108A682471F2BD48D3DDDA49E896AFEA79BBCBCB96E00759FB9E77B110866D32F8C3061C4655E056BF8FD5F0417D1BF296F735F0D682437F67D9BCC05D6E8BD6E725050C41133E9D9D3E76280444FC260D435C631302720B6EA53BE9E7B890405D4564310F7F2C48027535A15DF807777F1EEF7F57DC0BDBBB398004C1CC5D159991BDAB0373926719A000006A1C47FC0000B090800300FFDE210BD515B290C2A0C1944C741819E4E755755C632A537335ADAD2EF5544B5481F1B472C55D552D4982F5FF89A5E18B3853B70D0A6380EF43AED06FBE5510F067C8E66CC6E0568C614076560EDDA09A3150EC4091D6FFEC2D78A38EF6FD5D52D0DC9F2BD2F3DC2DF9FA413F130355403920A91E9D2FA1FBAF09A7C239059DF6D3A72B24E8B96340288C68DEB4D9D44E9D2DAB0D3F8BBF15CDA44D549DCF8345F55DC594A21615BD6128E17D700240980B9DE7FC2456E7B7D5A0999AAD8EEEFF8B970E78B5BC34559F98FBA90D9C28BE444F0D0A8DEC59EB7D38104926B66CAC98E8B6DD3E69A2BA899B77D4C002DAC600A5734B62A006A1886E511063F09E93ADF3800B8E1CEBC528B8630900AE86759E7D1F08DF9DE47E57E93EC05714FDAAC43FCF03E57A551D29094162A8506AA31021E738E784DB8DDF350E667536B3875554BD5609E7C846792B5B0D0AA9DCCACFB143A6AB0B087917695136C22505859DDFB9E2CF3FC73FBDBEE77C1E853B74A0C023755C2BBA329D4C1D0298AC33342083E328F7F6663D5403C12943925C1007AAD0789F0E429D909DE53DA00D0ADEE57C77A966388A04C096FE3D08E41189037D5C2F32D71C015B46F31DF3F1D6855556F7A9339D0D0ADB788178227218CEB505E03BB5C7EEAD6A0BC63CFF7E9F4F00DD1C47FC0000B090800300FFE6210BD50D9E936181B0A0AC270B1102C451019A943352D95553994D5EEEEA5EB55448342299BD823F916D8AE11EB398B2D5CEB34CFAD3B2818D3F9E30884B0D0AEC3A05147354117537040109F46A27CD52806C4279C1C3E9FD9F6620C3ABF2AD8702767FBE226C934BDCD0E2BBD40E4003136CB1A3CCFD951FB2D8E9DE5415B55384B798B48C32469B56B54506917A7627332D8C4FD7A85897697186EBFCEBBE26D3C6D94D107198D32BAC82FCD0B081BFF7B43C4D8CF9786BE3221B39DB69080052494AF6E6A310BB6D8A50A45AD99DC00D0AAD604E4169612A8E75F96CB437EBE93645816D58F03BF46FEA654056FBABAE703A4899000C90F7F748200D39BD4FF897B19092B0BA000066B80E8357461289899CAF12055C66FEBE9E118E572C4494A438208A0F45632A1022503349AC6F8BE32B19399249BBBA4EB46DAA258D847BADAF69EADE38B19AA09DBD707825ABFE12D1E74C88B545354477A140A7CF3E83A47350CCA0796ED91F102C2378BD91B2A8820D9C0430C8096CC6C007279AF9984B6EAAC420D41DC51656027437981BC26ADA054E2EC7F2E24AF461733B014002A18EEFB2635C38700D0A89F872EACE26AF3B019F86B15383BFC72075F52EBE1F0CE3BFF5E3AE75AE38C4CA402304400714037E466C2802534AF14B0600101370D000705BE2E047FC0000B090800300FFB6212BD4ED9E936182D0A08CB04353DEC47192B753BE0975256A5945A507ED211FBFABFBDFD8B7E5E14B401438D2ED27B9D8E7E30651A8224007FF029525ACBEC13E7B2D6503DC54CAA43A1E35D7D264D0DB50F3789F04142A8902C1D6E0ED0080C561CD098EA1EDED08A04A8EBA2993001374E57D54528315752B83C7116C3725458CB05078123B8D8F3CB6E0976FB8F0A914A051B66CE31EDF5153AD9A011948859002F1A81CA6529E33155B64EAE03D2599AB5A501F05A72EAB90002CC2FA2BAE2E929CC7659A8099E80D6B9E79CA3C6E40B8FC25B18C6D689E819DBB335A7405E539405ECEAA41395B6D315146F14C910D40EC95B768B86204D44640B5A806AAF52AC2DDD423CC4B4E108F5844E6235D051E16A18131548023200C44065F1F6B5E5DEB2F2B75BD5D59755ABBA55CB04027445D19A4858A1C48F732E11169DB549AEC4BE6E3910C01EB3DBF85B3585D7E4DCAD92DFBBCEF54298CE02321D79BA0DAB0010D88105387DFFF664000066AC6CCFABF22F7401899A064D249404E84C90B989B5405C6FB534842FA37DAAE4679C9B902EBA4675EEE5DADF46E00251781D8F6DC5D6BCE23F1FC2194128B1316D08805038047FC0000B090800300FFFF72214DDEFFFFFF3336A9B1A2D1C81B54A1DA62A80B8F112FA2FF1FF13779A9C4DDF41DBF58B878CF6840F504F3FED85D1608DE4BDA7D0748C2711E387AFD2C656A7C078531B2A9B2D5A7F07E8671F9E7E41B3ED4F031EE923A47C8FF0D0A94305B24B1CEEA67EC5A3A74EA9B6F7285B3C68E0FB587FE1A30DBFBD5170532508F2D095022535C81BAF3C8068A2E476984FA49121FB15851DE70AA3EDAC7A4F285AC068F1F39BC124BAB7BCBEDB59A0D0A7EAB05AEE759E63F48C64EEAB770833C3352FBCE1E417534B457F411155A5694BBA5D190B8C934CE6B5DF88DAB9777303994614BDDA9A50CBC15E0AFEC1F3DA00DEBBC25304658E57ACDCCFE743CCF2D97096C43ABBA3EFDA47676B38B3C7E308D54B45A3B8D8470BF98EFCED3C9D5A375EE4566E39C6807A0072BA6F9F19E8B25EFEA994AFFE67886B80DAFE0FBF9A26768E3D4EA63974BA55CB83B15CBBDD95E3D3007C2009000009AA31113136E40D46506CD162A80AF9893DB1AFDFFECABE78BE1BB807B20B5E81EF71CC44F05FBA36F78B60957374E4DA34C8A7A9236A3EF391BC15CD9ED7957AB2422E89444BCBAE7B93F9D6E23D8FA2DDEE93FAE64FB7FB824389C1F8096FA407C88C3081FE7CB3FEBCE03728C752131676EA6B105B4681D75368D4F245EE81EACF386076278C95924087B56FC0E2AD55F77A6A44B28A9B25AF286BEBD27226A18C2E2BA296FD959452A32E9231ACF2CEE5C050478175B83696AFAE0118C5984F3F724A329DCE25230C9ABA45FA9EADB6E64C57E9609F22B4D3E8651E60DDF127A6010A3320BA3D0BFA22D921572043923A011330D0A003A5A67F67D3F2FB39FFDFF0D0ACF6763F88F838C001F3CFD9D523FCDFB3B02F19000007047FC0000B090800300FFF2216BD4FDBE8D0E620840EC8BC78BDFD139EED7968BC6B495922D9438B20F89CE72499D90F50FD31ABA4B912523B81ABD5BC3F1AE5F9FF34E75F97E68E4876E1AE381C96FE987C9F366AB724F3C634CA9AA4A1E27758C5D5EA32DF47634CE4B8EAC1DA6522172C6BEF6B78B6C21A968325B87FA4B8C75DFC9AF7E3AD024A6D45DF651B19D99133869232C0CA24BEF0DA6CA2133CC0277D02C0FBD2104C9A58C22A45E6E0520C1A55DEE48AA9B600D0AEA6BEE37AE66ACB8DDE4E53C23D36D4448C397749D19F2B1E68428D0A7C57EB91A4860B12C4D69769DF1D4C69CD4D04F9D72934AB58E51072C4B09925BD7DA02259693C4C0FC25218F6A78216AA094DB7AA3619D6F9E696E7B25510006A6DC24FD5BDB1348F57C63CAB3DF759729524CEF7E40014C5637D7AE61000322198D20A41142EC4C45431504650433AAD667339FAB5F7DC6B22D2AB8D456EEA5B63493B898F6DF0F70DD916C86A6D462BB14C15C776CF8EDA49868E510773CC69988D806E1870859E0B344AB8F1B6C3BFE417BF7E9884F2128F5F4DC3B63E1238E6312D76F2E8E576057340BD2E2A4ADD6484457609AA80209CD4DBE126F9A509DD011A9FA7DBD617DCF84B948BB7B616CEFDBE094B829D525FBCA7B2582A52421BAA94AC8A8A667836D98C5D5711A92FFE8B8F97433278764D5D5D597F7BE5013C0CD605C1C47FC0000B090800300FFC2210BD50DBA0D618431602A200B04C40656A92FD4DF152AAAEB591245A242AAED06E8F0672C5946157169B9AB6ACB4F1866BA4137814BE13F05A2411098969DCFF61E40B4BE944463971C375920B2BEBF3A52F0E2D1869C9A888FA900465D2C74FC76F45CA4CE0D0A140D0A695E04899CC9250D0A8F80C42290A86140C5B248EC8EB7392B6E14215BF0C9CA0341490623A1391B0D914C92E7395009A2E15134A7125C2557891339CE05826194D5D4EDAC6B192FF34B8600071E6E14E38CD3852EEEB194F2D963C817DF43D0C0E0FA9128E525AF7A10B9257719C014437538E585FC4A8F51E58128CCBE659EF01E420D0A1A6AFDDE784FA351F1AFD5009DF4FB669871E7BA8C8A2CD84E5DC355583B2A7A9F0FD2DE041D1BA072F00292D30563A2D4802108215EDB8E3F4D6F52AEB76D622D5A45CBCCAE01AC8707447199D2B8193647757C0014E4BC4E8DD9D251E0755086332FE1E122E318533919E1171A85F17DEC4B7F30EA40005ECF1322EF9705A2DADB354EF0D71653C09BA5771CF866F6B4692BFB53A20D0A36ED3BD63B5397E1AB1C76A6066610535CEC5857AA165F8A3BDE3AFE62C14540A8AFF1CAFD7351D7638E0D0AFE577D1B1753015C98CF3AD1588056C07EF11C47FC0000B090800300FFFF14210BD51DD612C2A238A04A1032F86DAF9E39F6BEEF61256A2492AE5A492E507266D8A485B1C91050CF618CD9D24CE9422880B92E969794BD6EE21043A9B997E8E5D02CAD01D5EDFCD8B871220B37D4CE28394314B723411472F6DA23E15CA10D66D7147D38C0B31AF672573349490915B6D97078FB5216D535437AFA64A678C4507414C17AE5E54217F0928AE120AA4785CF3F2AFE23CE722FEC6DAF8231ECE7B282285F116A70137C711FA2C90995AE22233874438CA69F0827AC2318B4DB7DD3825F4DCD72EFCA13BEECA7B33B500C24F86B032995348674CDE93D20D28DC69B44836A2268C6383600AD36D5EC1C7BEC2C0513E4629551BA966ADF93A8F5685BEEAF417384CBA8556AA4C9C797361D1FF6F65CAF91AA2A7180000D0AECFCF8A27CB9FCBB33CA6E75A45F9A5D5566D68ED32A7169E09938465DC13D5FF9F6F1C635A8AF3F648535B98D4154A08C4C15080982A2032FE3337AED5ED399CD5E5D719A8BAB9BE2173A6034F9A257E49449860F1F3234D1E7A7C1FA3154C6FC7C53088642EC3B6454AAE6AFB85BA6A3437CBE2624C9CDC9D46A9C00CAC1300557BF14621033ED6CCE4F846AB973EBF9F216D31DFC2A6205E3B65BF0EC84AFBF58E701778C6E78EF8004EE98EBEF8ADEFF856C00089D7AAD2B65E220AFE016A1473108E0CE9461F75AEAE24B9753B810A8C348A9870D7373F8FB3AFEE07C3521BFCCF7E51132B76548165BFAB62A73300FBF40003847FC0000B090800300FFFF1F210BD51DC6890761C0E84A281819D5E7377DB9F3795855CBA5DDB24BB9217C503B377F9A2BEC89E1CFC791C41158ED857519F6B98E872BBEA9B614500C6FCF7E468CB64F31AE089CDE696E45B29D9F3874F083E36F6AA0CA5F849622858B8F4153CB0721ABACB0FCF4B0A209E092CB70A10D0AC689F786783E85E022E8E02D4CC2AC1AE0984E701926788007936A163026CEC02DA74C159B3AB81003236120A2630E9E85980966AF95D712FAEBCA35EF9F4918972FF646A17B757FB28F2A67007C26B281C925832AE31703971AC6C931F10CD6DACABB61D49A8BF071B4947D952A33789A89A54C5757C1DF155B58C62BE42982A928E8ACE39C75DA8B0D2F0B518C7D975D846EC28236543C505AFB3D7ECA81A80000673F2EBE8DECEDFDFACAD8AB7AE33B43B2F05E722499D77A4D223A2DF7DC42437AC0B03D271F05379CE739CEA7B4B1A82C210D894C8452889828103BDB59BBBEF3BF699372A389556E1BB6AE495E3DBCE02863569A20106734826201CEDD6DBF328E8EA8E13833AFA5CE678A43ECACAE056F438204DE3F666ACC594CF22AC0A0238671CB13CA31BBEF5D1E38EC8CEFA372BA8EF29CB1EA825590EEC95BE0AB98BC4DFBFA60C7DFD18CA5FD6F1D94832B9D4A2F46EB40E777B2C321FC8BE45BBD477B3BFE94D7BFABB61DDDE62B2F679F007BB8ED7ACFD9D5C7D935F1E9E3FE9F1800AFEDF1FFAFE5F672FF3DE2FE50E7B83AFA400004B8275F20987F35768CCDAEE5D92358534EF57847FC0000B090800300FFE9210BD525A68D6181D098B4180B0E02C451308C20679BA6FCFBD73E655EF6B8B2692A4B092F554152C8CC9205E29FA1F9D772C440C559DA53E5DFD1398ACB4BE64512620F63F0F92B20EEC15947AB0E2032125AE3D054070D0A7FF21BED4335F82519E41E12FF8F0051C5B625EFCA1005A8AEBEFF8DE407E4DB9655E7ECB95ED8C7CB496524916480007916D9278CC3EBC697721DFF0D0869C9A008D1326C2993079D09DCB1D3D0AB84EF3ED249260EDBB1F2C37F7F6D7A3BBB6E69EF7291A667BAAAD6B918D4A92544E23A89B38AA35E781A4BEA47FAEFD482A873DD4D7CE903BB5C9B7826170537DD2C9D293450B2E49701CCE49E9CFE1DDF8D98C4700B9808559CD6F7BE9E000D01A4B1F4FA8B8787C3FCF282FAD6CCDC960542EB9CF77D5D75DC63F7AF27A51920A741D9A156250D0A115402408880CEA5CBBF9DEFCDE71CF3347189C52AEB8CB2BE302116B6FAA464C548A09BF3AC27F9AC0093C55F0004D671C4A53924BB758CF48C9F302A31E1B0E340339630896052D4E6D2C418E38203E952003C4B7F9635A743DE4D2C284D206EA7E25CEB9AAAD095F1306F11D7F2FE5323ABBE05EFBE031D2D467BF711A847074702B60B910C639FB30BC600066000DF8F6856EEE57D3C2B35BDCEBBFAD20365E9598E8854AB6361A0790B5FE6DB3F2DA27493AE106C01C0
The input file is also here:
http://filebin.ca/3DACc6lkf882/input.txt
Edit 2:
Here is a sample from the MFTrace with some extra traces put in there:
23816,3D70 10:43:31.09829 CKernel32ExportDetours::OutputDebugStringA # ----------------------------------ProcessInput----------------------------------
23816,3D70 10:43:31.09832 CMFTransformDetours::ProcessInput #00000229F1932698 Stream ID 0, Sample #00000229F19D2160, Time 0ms, Duration 0ms, Buffers 1, Size 640B,
23816,3D70 10:43:31.09832 CMFTransformDetours::ProcessMessage #00000229F1932698 Message type=0x10000000 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, param=00000000
23816,3D70 10:43:31.09832 CKernel32ExportDetours::OutputDebugStringA # GetOutputStatus says MFT_OUTPUT_STATUS_SAMPLE_READY
23816,3D70 10:43:31.09833 CKernel32ExportDetours::OutputDebugStringA # GetInputStatus says does NOT accept data
23816,3D70 10:43:31.09834 CKernel32ExportDetours::OutputDebugStringA # ----------------------------------ProcessOutput----------------------------------
23816,3D70 10:43:31.09834 CMFTransformDetours::ProcessMessage #00000229F1932698 Message type=0x10000000 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, param=00000000
23816,3D70 10:43:31.09835 CMFTransformDetours::ProcessOutput #00000229F1932698 failed hr=0xC00D6D72 MF_E_TRANSFORM_NEED_MORE_INPUT
23816,3D70 10:43:31.09835 CKernel32ExportDetours::OutputDebugStringA # ProcessOutput needs more input
23816,3D70 10:43:31.09836 CKernel32ExportDetours::OutputDebugStringA # GetInputStatus says MFT_INPUT_STATUS_ACCEPT_DATA

As mentioned in the comments, after spending a bit of time with your code, I was able to reproduce the behavior you reported. However, this was also true when I used arbitrary files for input. Admittedly, I was surprised to see that the decoder reported a status of MFT_OUTPUT_STATUS_SAMPLE_READY. Therefore, I agree with Ben regarding valid AAC data.
Instead of parsing the samples manually, perhaps you could simply use an IMFSourceReader to provide the samples for you, which would look similar to the following:
ERROR checking omitted for brevity
HRESULT configure_reader(IMFSourceReader *reader, IMFMediaType **resultingMediaType)
{
HRESULT hr = S_OK;
IMFMediaType *pcmMediaType = NULL;
IMFMediaType *partialType = NULL;
// Create a partial media type that specifies uncompressed PCM audio.
hr = MFCreateMediaType(&partialType);
hr = partialType->lpVtbl->SetGUID(partialType, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
hr = partialType->lpVtbl->SetGUID(partialType, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
// Set this type on the source reader. The source reader will load the necessary decoder.
hr = reader->lpVtbl->SetCurrentMediaType(reader,
(DWORD) MF_SOURCE_READER_FIRST_AUDIO_STREAM,
NULL,
partialType);
// Get the complete uncompressed format.
hr = reader->lpVtbl->GetCurrentMediaType(reader,
(DWORD) MF_SOURCE_READER_FIRST_AUDIO_STREAM,
&pcmMediaType);
// Ensure the stream is selected.
hr = reader->lpVtbl->SetStreamSelection(reader,
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
TRUE);
if (resultingMediaType)
{
*resultingMediaType = pcmMediaType;
(*resultingMediaType)->lpVtbl->AddRef(*resultingMediaType);
}
(void) pcmMediaType->lpVtbl->Release(pcmMediaType);
(void) partialType->lpVtbl->Release(partialType);
return hr;
}
// . . . . . . . . . . . . . . . . . . . . . .
void process_aac_audio()
{
HRESULT hr = S_OK;
IMFSourceReader *reader = NULL;
hr = MFCreateSourceReaderFromURL(L"input.aac",
NULL,
&reader);
IMFMediaType *pcm_media_type = NULL;
hr = configure_reader(reader, &pcm_media_type);
assert(pcm_media_type);
DWORD total_bytes = 0;
DWORD buffer_length = 0;
BYTE *audioData = NULL;
IMFSample *sample = NULL;
IMFMediaBuffer *mediaBuffer = NULL;
while (1)
{
DWORD dwFlags = 0;
hr = reader->lpVtbl->ReadSample(reader,
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0,
NULL,
&dwFlags,
NULL,
&sample);
if (FAILED(hr)) { break; }
if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
printf("Type change \n");
// TODO:
}
if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of input file. \n");
break;
}
if (sample == NULL)
{
printf("No sample \n");
continue;
}
hr = sample->lpVtbl->ConvertToContiguousBuffer(sample, &mediaBuffer);
hr = mediaBuffer->lpVtbl->Lock(mediaBuffer, &audioData, NULL, &buffer_length);
// TODO: process buffer
hr = mediaBuffer->lpVtbl->Unlock(mediaBuffer);
audioData = NULL;
total_bytes += buffer_length; // <-- update running total
(void) sample->lpVtbl->Release(sample);
(void) mediaBuffer->lpVtbl->Release(mediaBuffer);
}
if (sample)
(void) sample->lpVtbl->Release(sample);
if (mediaBuffer)
(void) mediaBuffer->lpVtbl->Release(mediaBuffer);
}
In this case the decoder was loaded and employed for you to deliver PCM samples directly. The file you provided (input.txt) failed, however other valid aac files worked fine.
You may be dealing with a stream instead of a file. In that situation, create a source with code similar to the following:
HRESULT create_media_source(IMFByteStream *byteStream, IMFMediaSource **ppSource)
{
MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;
IMFSourceResolver* sourceResolver = NULL;
IUnknown* source = NULL;
HRESULT hr = MFCreateSourceResolver(&sourceResolver);
hr = sourceResolver->lpVtbl->CreateObjectFromByteStream(sourceResolver,
byteStream,
NULL, // URL
MF_RESOLUTION_MEDIASOURCE,
NULL, // IPropertyStore
&objectType,
&source);
// get the IMFMediaSource interface from the media source.
hr = source->lpVtbl->QueryInterface(source, &IID_IMFMediaSource, ppSource);
(void) sourceResolver->lpVtbl->Release(sourceResolver);
(void) source->lpVtbl->Release(source);
return hr;
}
However, note that CreateObjectFromByteStream needs a way to identify the content type. You can either implement IMFAttributes [specifically GetAllocatedString] on your byte stream or pass in a URL

Related

Working with byte array I get an "invalid conversion from char* to byte"

I am reading and writing to an RFID tag using MFRC522.h
I can currently read the UID of a card and dump it to "UIDChar"
The UID of a card typically is 8 characters.
UID Example: 467EE9A9
I can use the mfrc522.MIFARE_SetUid function to write this UID to a new card. In order to do this I have to set the newUID to:
0x46,0x7E,0xE9,0xA9f
I have written this into my code.
What I am wanting to do is convert the UID string into a byte array so that I can use that in place of my manually written 0x46,0x7E,0xE9,0xA9.
I use the convert function to convert the UID into that format.
It can that be displayed with "buf".
Serial.println(buf);
Now my problem. If I replace the
byte newUid[] = {0x46,0x7E,0xE9,0xA9f};
with
byte newUid[] = {buf};
I get the error
invalid conversion from 'char*' to 'byte {aka unsigned char}'
How can I set my "newUid" as "buf"?
#define SS_PIN 0 //D2
#define RST_PIN 2 //D1
#include <SPI.h>
#include <MFRC522.h>
/* For RFID */
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
char buf[40]; // For string to byte array convertor
void convert(char *s)
{
int i, j, k;
buf[0] = 0x0;
for (j = 0, i = 0, k = 0; j < strlen(s); j++)
{
if (i++ == 0) {
buf[k++] = '0';
buf[k++] = 'x';
}
buf[k++] = s[j];
if (i == 2) {
if(j != strlen(s) -1) buf[k++] = ',';
i = 0;
}
}
buf[k] = 0x0;
}
void clone() {
/* RFID Read */
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent())
{
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial())
{
return;
}
//Show UID on serial monitor
Serial.println();
Serial.print(" UID tag :");
// Very basic UID dump
unsigned int hex_num;
hex_num = mfrc522.uid.uidByte[0] << 24;
hex_num += mfrc522.uid.uidByte[1] << 16;
hex_num += mfrc522.uid.uidByte[2] << 8;
hex_num += mfrc522.uid.uidByte[3];
// Get UID
int NFC_id = (int)hex_num;
Serial.print(NFC_id, HEX);
// Convert UID to string using an int and a base (hexadecimal)
String stringUID = String(NFC_id, HEX);
char UIDChar[10];
stringUID.toCharArray(UIDChar,10);
delay(1000);
Serial.println();
// Convert to uppercase
for (int i = 0; i < strlen(UIDChar); i++ )
{
if ( UIDChar[i] == NULL ) break;
UIDChar[i] = toupper(UIDChar[i]);
}
//Serial.print( &UIDChar[0] );
Serial.println();
convert(UIDChar);
Serial.println(buf);
/* RFID Write */
// Set new UID
// Change your UID hex string to 4 byte array
// I get error if I use byte newUid[] = {buf};
/* ERROR HERE */
byte newUid[] = {0x46,0x7E,0xE9,0xA9};
if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) {
Serial.println( "Wrote new UID to card." );
}
// Halt PICC and re-select it so DumpToSerial doesn't get confused
mfrc522.PICC_HaltA();
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
return;
}
// Dump the new memory contents
Serial.println( "New UID and contents:" );
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}
void setup() {
Serial.begin ( 115200 );
/* RFID */
SPI.begin(); // Initiate SPI bus
mfrc522.PCD_Init(); // Initiate MFRC522
clone();
}
void loop() {
}
When you write
byte newUid[] = {buf};
you are trying to initialise newUid with a single element (there's only one item inside your {}), and that element is buf, which is a char* (or a char[]). That's why you get the error - you are trying to assign an array with one char* to a variable whose elements are bytes.
Without reading your full code in detail, I don't know why you are trying to do this assignment, rather than just use your buf array as it is. But to fix the problem, you probably just want to use
byte* newUid = buf;

FFMpeg libswscale XBGR32 to NV12 almost working but colors are wrong

I am getting a Linx DMA-BUF from the GPU in XBGR32 format and I need to use FFMpeg's libswscale to convert it on the ARM to NV12.
I've been able to almost get it working based on various SO posts and the documentation.
Here is the LCD screen that is the actual output of the GPU:
Here is the encoded data from the NV12 frame created by libswscale - it should show the same colors as the screen:
Please note that the pictures were not taken at exactly the same time.
What am I doing wrong?
Here is the relevant part of my code:
/* Prepare to mmap the GBM BO */
union gbm_bo_handle handleUnion = gbm_bo_get_handle(bo);
struct drm_omap_gem_info req;
req.handle = handleUnion.s32;
ret = drmCommandWriteRead(m_DRMController.fd, DRM_OMAP_GEM_INFO,&req, sizeof(req));
if (ret) {
qDebug() << "drmCommandWriteRead(): Cannot set write/read";
}
// Perform actual memory mapping of GPU output
gpuMmapFrame = (char *)mmap(0, req.size, PROT_READ | PROT_WRITE, MAP_SHARED,m_DRMController.fd, req.offset);
assert(gpuMmapFrame != MAP_FAILED);
// Use ffmpeg to do the SW BGR32 to NV12
static SwsContext *swsCtx = NULL;
int width = RNDTO2( convertWidth );
int height = RNDTO2( convertHeight );
int ystride = RNDTO32 ( width );
int uvstride = RNDTO32 ( width / 2 );
int uvsize = uvstride * ( height / 2 );
void *plane[] = { y, u, u + uvsize, 0 };
int stride[] = { ystride, uvstride, uvstride, 0 };
// Output of GPU is XBGR32
// #define V4L2_PIX_FMT_XBGR32 v4l2_fourcc('X', 'R', '2', '4') /* 32 BGRX-8-8-8-8 */
swsCtx = sws_getCachedContext( swsCtx, convertWidth, convertHeight, AV_PIX_FMT_BGR32, width, height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR , NULL, NULL, NULL );
int linesize[1] = { convertWidth * 4 };
const uint8_t *inData[1] = { (uint8_t*)gpuMmapFrame };
if ( gpuMmapFrame ) {
sws_scale( swsCtx, (const uint8_t *const *)inData, linesize, 0, convertHeight, (uint8_t *const *)plane, stride );
}
// Unmap it
munmap(gpuMmapFrame,req.size);
// Now send the frame to the encoder
dce.submitFrameToEncode(swEncodeBufferNV12);

How to access individual channels with core audio from an external audio interface

I am learning CoreAudio and I am just going through some of the examples on Apple's documentation and figuring out how to set things up and what not. So far I am able to connect to the default connected audio input device and output it to the default output device. I connected a 2 channel interface and was able to output the input from it and output it as well.
However I was searching through their API references and examples, but could not find any thing substantial to access the individual input channels from my interface.
I was able to hack away and extract the samples from the AudioBufferList in my Render Callback function and manipulate it that way but I am wondering if there is a correct way or a more official way of accessing the data from each individual input channels.
EDIT:
This is the user data that I found from an example that I am using:
typedef struct MyAUGraphPlayer
{
AudioStreamBasicDescription streamFormat;
AUGraph graph;
AudioUnit inputUnit;
AudioUnit outputUnit;
AudioBufferList * inputBuffer;
CARingBuffer * ringBuffer;
Float64 firstInputSampleTime;
Float64 firstOutputSampleTime;
Float64 inToOutSampleTimeOffset;
} MyAUGraphPlayer;
This is how i set up the input unit:
void CreateInputUnit(MyAUGraphPlayer * player)
{
//Generates a description that matches audio HAL
AudioComponentDescription inputcd = {0};
inputcd.componentType = kAudioUnitType_Output;
inputcd.componentSubType = kAudioUnitSubType_HALOutput;
inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
UInt32 deviceCount = AudioComponentCount ( &inputcd );
printf("Found %d devices\n", deviceCount);
AudioComponent comp = AudioComponentFindNext(NULL, &inputcd);
if(comp == NULL) {
printf("Can't get output unit\n");
exit(1);
}
OSStatus status;
status = AudioComponentInstanceNew(comp, &player->inputUnit);
assert(status == noErr);
//Explicitly enable Input and disable output
UInt32 disableFlag = 0;
UInt32 enableFlag = 1;
AudioUnitScope outputBus = 0;
AudioUnitScope inputBus = 1;
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableFlag,
sizeof(enableFlag))
assert(status == noErr);
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
outputBus,
&disableFlag,
sizeof(enableFlag));
assert(status == noErr);
printf("Finished enabling input and disabling output on an inputUnit\n");
//Get the default Audio input Device
AudioDeviceID defaultDevice = kAudioObjectUnknown;
UInt32 propertySize = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;
status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&defaultDeviceProperty,
0,
NULL,
&propertySize,
&defaultDevice);
assert(status == noErr);
//Set the current device property of the AUHAL
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
outputBus,
&defaultDevice,
sizeof(defaultDevice));
assert(status == noErr);
//Get the AudioStreamBasicDescription from Input AUHAL
propertySize = sizeof(AudioStreamBasicDescription);
status = AudioUnitGetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&player->streamFormat,
&propertySize);
assert(status == noErr);
//Adopt hardware input sample rate
AudioStreamBasicDescription deviceFormat;
status = AudioUnitGetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
inputBus,
&deviceFormat,
&propertySize);
assert(status == noErr);
player->streamFormat.mSampleRate = deviceFormat.mSampleRate;
printf("Sample Rate %f...\n", deviceFormat.mSampleRate);
propertySize = sizeof(AudioStreamBasicDescription);
status = AudioUnitSetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&player->streamFormat,
propertySize);
assert(status == noErr);
//Calculating Capture buffer size for an I/O unit
UInt32 bufferSizeFrames = 0;
propertySize = sizeof(UInt32);
status = AudioUnitGetProperty(player->inputUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
0,
&bufferSizeFrames,
&propertySize);
assert(status == noErr);
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
//Create AudioBufferList to receive capture data
UInt32 propSize = offsetof(AudioBufferList, mBuffers[0]) +
(sizeof(AudioBuffer) * player->streamFormat.mChannelsPerFrame);
//Malloc buffer lists
player->inputBuffer = (AudioBufferList *) malloc(propSize);
player->inputBuffer->mNumberBuffers = player->streamFormat.mChannelsPerFrame;
//Pre malloc buffers for AudioBufferLists
for(UInt32 i = 0; i < player->inputBuffer->mNumberBuffers; i++){
player->inputBuffer->mBuffers[i].mNumberChannels = 1;
player->inputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
player->inputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
//Create the ring buffer
player->ringBuffer = new CARingBuffer();
player->ringBuffer->Allocate(player->streamFormat.mChannelsPerFrame,
player->streamFormat.mBytesPerFrame,
bufferSizeFrames * 3);
printf("Number of channels: %d\n", player->streamFormat.mChannelsPerFrame);
printf("Number of buffers: %d\n", player->inputBuffer->mNumberBuffers);
//Set render proc to supply samples
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = InputRenderProc;
callbackStruct.inputProcRefCon = player;
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callbackStruct,
sizeof(callbackStruct);
assert(status == noErr);
status = AudioUnitInitialize(player->inputUnit);
assert(status == noErr);
player->firstInputSampleTime = -1;
player->inToOutSampleTimeOffset = -1;
printf("Finished CreateInputUnit()\n");
}
So this is my render callback function where I am accessing the individual buffers. :
OSStatus GraphRenderProc(void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
MyAUGraphPlayer * player = (MyAUGraphPlayer *) inRefCon;
if(player->firstOutputSampleTime < 0.0) {
player->firstOutputSampleTime = inTimeStamp->mSampleTime;
if((player->firstInputSampleTime > -1.0) &&
(player->inToOutSampleTimeOffset < 0.0)) {
player->inToOutSampleTimeOffset = player->firstInputSampleTime - player->firstOutputSampleTime;
}
}
//Copy samples out of ring buffer
OSStatus outputProcErr = noErr;
outputProcErr = player->ringBuffer->Fetch(ioData,
inNumberFrames,
inTimeStamp->mSampleTime + player->inToOutSampleTimeOffset);
//BUT THIS IS NOT HOW IT IS SUPPOSED TO WORK
Float32 * data = (Float32 *) ioData->mBuffers[0].mData;
Float32 * data2 = (Float32 *) ioData->mBuffers[1].mData;
for(int frame = 0; frame < inNumberFrames; frame++)
{
Float32 sample = data[frame] + data2[frame];
data[frame] = data2[frame] = sample;
}
return outputProcErr;
}
Although your code looks overly complicated for the task it seems to manage, I'll try to answer your question:
There's nothing wrong with your concept of retrieving sample data inside a callback. It would though be insufficient if dealing with multichannel audio devices. How many channels the device has and which is the channel layout, format etc. you query through AudioStreamBasicDescription for given device. This property serves for the initialization of the rest of your processing chain. You allocate audio buffers on initialization, or let the program do it for you (please read documentation).
In case you find more comfortable using extra buffers to copy to just for data crunching and DSP, you can manage it inside your callback like this (simplified code):
Float32 buf[streamFormat.mChanelsPerFrame][inNumberFrames];
for(int ch; ch < streamFormat.mChanelsPerFrame; ch++){
Float32 data = (Float32 *)ioData->mBuffers[ch].mData;
memcpy(buf[ch], data, inNumberFrames*sizeof(Float32));
}

Thales Connect HSM - DeriveKey operation

I'm trying to perform ECDH using a Thales Connect HSM. Part of that requires a DeriveKey operation which in turn requires importing a template key (which holds an ACL that will be attached to the result of the DeriveKey operation).
Import of the template key fails in spite of trying all the methods I could find in the API documentation - I get Status_InvalidParameter as the response. Hoping someone has used the API for something similar (trying to open a support channel with Thales in parallel).
// Generate nested ACL
M_ACL nestedAcl;
memset(&nestedAcl, 0, sizeof(M_ACL));
nestedAcl.n_groups = 1;
M_PermissionGroup nestedAclPermGroup;
memset(&nestedAclPermGroup, 0, sizeof(M_PermissionGroup));
nestedAclPermGroup.n_actions = 1;
nestedAclPermGroup.flags = 0x0;
nestedAclPermGroup.n_limits = 0;
nestedAclPermGroup.certifier = NULL;
M_Action nestedAclAction0;
memset(&nestedAclAction0, 0, sizeof(M_Action));
nestedAclAction0.type = Act_OpPermissions;
nestedAclAction0.details.oppermissions.perms =
Act_OpPermissions_Details_perms_Encrypt;
nestedAclPermGroup.actions = &nestedAclAction0;
nestedAcl.groups = &nestedAclPermGroup;
M_ByteBlock nestedAclBytes;
ret = NFastApp_MarshalACL(app, *connection, worldInfo, &nestedAcl, &nestedAclBytes);
if (ret != Status_OK)
{
printf("Failed to create nested ACL: ret = %d \n", ret);
}
// Import template key
M_Command importTemplateKeyCmd;
M_Reply importTemplateKeyReply;
importTemplateKeyCmd.cmd = Cmd_Import;
importTemplateKeyCmd.args.import.data.type = KeyType_DKTemplate;
importTemplateKeyCmd.args.import.data.data.dktemplate.nested_acl = nestedAclBytes;
char appdataTemplateKey[] = "02020202";
memset(&appdataTemplateKey, 0, sizeof(M_AppData));
memcpy(importTemplateKeyCmd.args.import.appdata.bytes, appdataTemplateKey, strlen(appdataTemplateKey) < 64 ? strlen(appdataTemplateKey) : 63);
// Generate the import command ACL
NFKM_MakeACLParams templateKeyAclParams;
memset(&templateKeyAclParams, 0, sizeof(templateKeyAclParams));
templateKeyAclParams.f = 0x0;
templateKeyAclParams.op_base = Act_DeriveKey;
templateKeyAclParams.timelimit = 0;
M_ACL* templateKeyAcl;
templateKeyAcl = malloc(sizeof(M_ACL));
templateKeyAcl->n_groups = 1;
/*
M_PermissionGroup* templateKeyAclPermissionGroup;
templateKeyAclPermissionGroup = malloc(sizeof(M_PermissionGroup));
templateKeyAclPermissionGroup->flags = 0x0;
templateKeyAclPermissionGroup->n_limits = 0;
templateKeyAclPermissionGroup->n_actions = 1;
templateKeyAclPermissionGroup->certifier = NULL;
M_Action* templateKeyAction;
templateKeyAction = malloc(sizeof(M_Action));
templateKeyAction->type = Act_DeriveKey;
templateKeyAction->details.derivekey.flags = 0x0;
templateKeyAction->details.derivekey.role = DeriveRole_TemplateKey;
templateKeyAction->details.derivekey.mech = Mech_Any;
templateKeyAction->details.derivekey.n_otherkeys = 0;
templateKeyAclPermissionGroup->actions = templateKeyAction;
templateKeyAcl->groups = templateKeyAclPermissionGroup;
*/
// NF_MarshalFast_ACL(*connection, templateKeyAcl);
NFKM_MkACLHandle mkAclHandle;
mkAclHandle = malloc(sizeof(mkAclHandle));
NFKM_mkacl_create(app, *connection, &mkAclHandle, 0);
NFKM_mkacl_pgroup(mkAclHandle, 0x0, NULL);
NFKM_mkacl_derivekey(mkAclHandle, 0x0, DeriveRole_TemplateKey, DeriveMech_Any);
NFKM_mkacl_setacl(mkAclHandle, templateKeyAcl);
importTemplateKeyCmd.args.import.acl = *templateKeyAcl;
ret = (M_Status)NFastApp_Transact(*connection, 0, &importTemplateKeyCmd, &importTemplateKeyReply, 0);
if (ret != Status_OK)
{
printf("Failed to import template key: %d (%d)\n", ret, importTemplateKeyReply.status);
}
The approach that ended up working (suggested by Thales) was to break up the operation into two parts - do a CMD_Decrypt with Mech_ECDHKeyExchange to derive the shared secret and then a CMD_Hash on the resultant x-coordinate to derive the key.

Resampling PCM file data with soxr and libsndfile crashes

I'm building an app that, in part, needs to resample any input PCM audio file that isn't 44100Hz to 44.1 (or at least make a best effort to do so).
To handle the resampling I'm using soxr. soxr has no dependencies and is lightweight, which is ideal in this case, but it offers no native file I/O. I have very limited experience with IO streams in C, so I'm hitting a wall. The app is being designed modularly, so I need the resample process to create an output file that can then be passed on to other processors, rather than simply dealing with the output stream directly.
In order to create that output file, I'm trying to take the data generated by the soxr resampling process, and pass it to libsndfile, which should be able to write the audio out to a file.
Below is an extremely verbose explanation of where I'm at, though I'm at a loss for why it's crashing. I suspect it has something to do with how buffers are being allocated and used. (Note: The input file is being read with sndfile prior to this code)
(Here's a single gist of the entire thing)
Basic resampler options
// Use "high quality" resampling
unsigned int q_recipe = SOXR_HQ;
// No
unsigned long q_flags = 0;
// Create the q_spec
soxr_quality_spec_t q_spec = soxr_quality_spec(q_recipe, q_flags);
Map the sndfile format to a soxr format
soxr_datatype_t itype;
// Get the SFINFO format
int iformat = self.inputFileInfo.format;
// Set the soxr itype to the corresponding format
if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) {
itype = SOXR_FLOAT32_S;
} else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) {
itype = SOXR_FLOAT64_S;
} else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) {
itype = SOXR_INT32_S;
} else {
itype = SOXR_INT16_S;
}
Setup soxr IO spec
// Always want the output to match the input
soxr_datatype_t otype = itype;
soxr_io_spec_t io_spec = soxr_io_spec(itype, otype);
Threading
// A single thread is fine
soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(1);
Construct the resampler
soxr_error_t error;
// Input rate can be read from the SFINFO
double const irate = self.inputFileInfo.samplerate;
// Output rate is defined elsewhere, but this generally = 44100
double const orate = self.task.resampler.immutableConfiguration.targetSampleRate;
// Channel count also comes from SFINFO
unsigned chans = self.inputFileInfo.channels;
// Put it all together
soxr_t soxr = soxr_create(irate, orate, chans, &error, &io_spec, &q_spec, &runtime_spec);
Read, resample & write
I'm not really confident in any of the following code, but I've triple checked the math and everything seems to meet the expectations of the libraries' APIs.
// Frames in sndfile are called Samples in soxr
// One frame is 1 item per channel
// ie frame_items = 1 item * channels
size_t const iframeitems = (1 * chans);
// item size is the data type size of the input type
//
size_t iitemsize;
if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) {
iitemsize = sizeof(Float32);
} else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) {
iitemsize = sizeof(Float64);
} else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) {
iitemsize = sizeof(int32_t);
} else {
iitemsize = sizeof(int16_t);
}
// frame size is item size * items per frame (channels)
// eg for 2 channel 16 bit, frame size = 2 * 2
size_t const iframesize = (iframeitems * iitemsize);
// Number of frames to read (arbitrary)
sf_count_t const ireqframes = 1024;
// Size of the buffer is number of frames * size per frame
size_t const ibufsize = iframesize * ireqframes;
void *ibuf = malloc(ibufsize);
// Output
//////////////////////////////
// These match the input
size_t const oframeitems = iframeitems;
size_t const oitemsize = iitemsize;
// frame size is item size * items per frame (channels)
size_t const oframesize = (oframeitems * oitemsize);
// Number of frames expected after resampling
// eg
// orate = 44100
// irate = 48000
// ireqframe = 1024
// expect fewer frames (downsample)
// (44100 / 4800) * 1024 = 940.8
// Add 0.5 to deal with rounding?
sf_count_t const oexpframes = (ireqframes * (orate / irate)) + 0.5;
// Size of the buffer is number of frames * size per frame
size_t const obufsize = oframesize * oexpframes;
void *obuf = malloc(obufsize);
// Go
//////////////////////////////
size_t total_resample_output_frame_count = 0;
size_t need_input = 1;
sf_count_t num_frames_written = 0;
do {
sf_count_t num_frames_read = 0;
size_t actual_resample_output_samples = 0;
// Read the input file based on its type
// num_frames_read should be 1024
if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) {
num_frames_read = sf_readf_int(self.inputFile, ibuf, ireqframes);
} else if (otype == SOXR_FLOAT32_S) {
num_frames_read = sf_readf_float(self.inputFile, ibuf, ireqframes);
} else {
num_frames_read = sf_readf_double(self.inputFile, ibuf, ireqframes);
}
// If there were no frames left to read we're done
if (num_frames_read == 0) {
// passing NULL input buffer to soxr_process indicates End-of-input
ibuf = NULL;
need_input = 0;
}
// Run the resampling on frames read from the input file
error = soxr_process(soxr, ibuf, num_frames_read, NULL, obuf, oexpframes, &actual_resample_output_samples);
total_resample_output_frame_count += actual_resample_output_samples;
// Write the resulting data to output file
// num_frames_written should = actual_resample_output_samples
if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) {
num_frames_written = sf_writef_int(self.outputFile, obuf, actual_resample_output_samples);
} else if (otype == SOXR_FLOAT32_S) {
num_frames_written = sf_writef_float(self.outputFile, obuf, actual_resample_output_samples);
} else {
num_frames_written = sf_writef_double(self.outputFile, obuf, actual_resample_output_samples);
}
} while (!error && need_input);
soxr_delete(soxr);
free(obuf), free(ibuf);
This gives and EXC_BAD_ACCESS on soxr_process. I have no idea what else to try at this point.
The _S in data types like SOXR_INT32_S mean that you're using split channels, and from the example 4-split-channels.c it seems that in that case you need to pass an array of pointers, one for each channel.
However, in the code above you just pass a single allocated block of memory so I'm guessing you're expecting interleaved channel data. Perhaps you can try changing the _S to _I.

Resources