Related
I have made an sqlite3 database that saves entries that sensors create. I am checking on leaks with valgrind, now I was wondering how to 'simulate' a broken SQL server. I am using this function:
rc = sqlite3_prepare_v2(conn, sql, -1, &res, 0);
This function works correctly, is there a way to simulate it failing? Setting rc to another value manually is an option, but doesn't make sense since a database does get created while the program thinks it doesn't.
I will add the code bellow, although I don't think this is necessary for this question.
This is the insert function that inserts a sensor into the database. This is where I want to test what happens when the prepare function fails.
void *insert_sensor(void *argument) {
storagemgr_insert_argument_t *storagemgr_insert_argument = (storagemgr_insert_argument_t *) argument;
DBCONN *conn = storagemgr_insert_argument->db_connection;
sqlite3_stmt *res = NULL;
uint16_t sensor_id_read;
double temperature_value_read;
time_t timestamp_read;
sensor_data_t data;
int buffer_working_check;
data.id = 0;
data.value = 0;
data.ts = 0;
buffer_working_check = 1;
while (connmgr_loopt_bool == 1 || buffer_working_check == 0) { //connmgr aan -> moet blijven lopen, connmgr uit maar nog data beschikbaar -> moet blijven lopen
pthread_mutex_lock(&lock);
// printf("gelockt in storagemgr\n");
buffer_working_check = sbuffer_read(shared_buffer, &data, 0, 1);
pthread_mutex_unlock(&lock);
int failed_tries = 0;
if (data.id != 0 && buffer_working_check != -1 && buffer_working_check != 1) {
res = NULL;
sensor_id_read = data.id;
temperature_value_read = data.value;
timestamp_read = data.ts;
while(failed_tries<3) {
char *sql = "INSERT INTO "TO_STRING(TABLE_NAME)" (sensor_id, sensor_value, timestamp) VALUES (#sensor_id, #sensor_value, #timestamp)";
rc = sqlite3_prepare_v2(conn, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
int idx1 = sqlite3_bind_parameter_index(res, "#sensor_id");
sqlite3_bind_int(res, idx1, sensor_id_read);
int idx2 = sqlite3_bind_parameter_index(res, "#sensor_value");
sqlite3_bind_double(res, idx2, temperature_value_read);
int idx = sqlite3_bind_parameter_index(res, "#timestamp");
sqlite3_bind_int(res, idx, timestamp_read);
break;
}
else {
write_to_fifo("Insert to the SQL server failed, trying again. ", 4);
failed_tries++;
// char *err_msg = 0;
//
// fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(conn));
// sqlite3_free(err_msg);
// sqlite3_close(conn);
// return NULL;
}
sleep(2);
}
}
if (0<failed_tries && failed_tries <= 2) { //het mag =2, want als het =2 en de db faalt dan wordt het eerst geincrementeert naar 3
write_to_fifo("Connection to SQL server re-established. ", 5);
}
if(failed_tries>2) {
db_connection_failed_bool = 1;
sqlite3_close(conn);
write_to_fifo("Connection to SQL server lost. ", 6);
break;
}
if (res != NULL) {
sqlite3_step(res);
sqlite3_finalize(res);
res = NULL;
}
}
return NULL;
}
If you want to keep the code pure, you could always pass the insert function an invalid file path or invalid file name. If you are trying to test the recovery loop, and still want it to remain pure, you can pass the invalid file path and have the recovery loop try to build the path. SQLite won’t build a database in a folder that doesn’t exist when using sqlite3_prepare_v2.
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
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));
}
In my app i need to do a DirSync with the active directory. However, in my search preferences when i try to give the search preference parameter for page size, it shoots me an error (0x80005008, ADS_BAD_PARAMETER). If i dont give that, it works fine. I am giving the code snippet below:
HRESULT DoDirSyncSearch(
LPWSTR pszSearchFilter, // Search filter.
LPWSTR *pAttributeNames, // Attributes to retrieve.
DWORD dwAttributes, // Number of attributes.
PUCHAR *ppCookie, // Pointer to previous cookie.
PULONG pulCookieLength, // Length of previous cookie.
LPWSTR szDC) // Name of DC to bind to.
{
IADs *pRootDSE = NULL;
IDirectorySearch *pSearch = NULL;
ADS_SEARCH_HANDLE hSearch = NULL;
ADS_SEARCHPREF_INFO arSearchPrefs[4];
ADS_PROV_SPECIFIC dirsync;
ADS_SEARCH_COLUMN col;
HRESULT hr = S_OK;
VARIANT var;
BOOL bUpdate = FALSE;
DWORD dwCount = 0;
BOOL noMoreData = false;
DWORD dwError = ERROR_SUCCESS;
WCHAR szError[512];
WCHAR szProvider[512];
// Validate input parameters.
if (!pulCookieLength || !ppCookie || !szDC)
{
wprintf(L"Invalid parameter.\n");
return E_FAIL;
}
LPOLESTR szDSPath = new OLECHAR[MAX_PATH];
LPOLESTR szServerPath = new OLECHAR[MAX_PATH];
VariantInit(&var);
// If cookie is non-NULL, this is an update. Otherwise, it is a full-read.
if (*ppCookie)
bUpdate = TRUE;
CoInitialize(NULL);
// If there is a DC name from the previous USN sync,
// include it in the binding string.
if (szDC[0])
{
wcsncpy_s(szServerPath,MAX_PATH,L"LDAP://",MAX_PATH);
wcsncat_s(szServerPath, MAX_PATH,szDC,MAX_PATH-wcslen(szServerPath));
wcsncat_s(szServerPath, MAX_PATH,L"/",MAX_PATH-wcslen(szServerPath));
}
else
wcsncpy_s(szServerPath, MAX_PATH,L"LDAP://",MAX_PATH);
// Bind to root DSE.
wcsncpy_s(szDSPath,MAX_PATH,szServerPath,MAX_PATH);
wcsncat_s(szDSPath, MAX_PATH,L"rootDSE",MAX_PATH-wcslen(szDSPath));
wprintf(L"RootDSE binding string: %s\n", szDSPath);
hr = ADsGetObject(szDSPath,
IID_IADs,
(void**)&pRootDSE);
if (FAILED(hr))
{
wprintf(L"failed to bind to rootDSE: 0x%x\n", hr);
goto cleanup;
}
// Save the name of the DC connected to in order to connect to
// the same DC on the next dirsync operation.
if (! szDC[0])
{
hr = pRootDSE->Get(CComBSTR("DnsHostName"), &var);
wcsncpy_s(szServerPath,MAX_PATH,L"LDAP://",MAX_PATH);
wcsncat_s(szServerPath, MAX_PATH,var.bstrVal, MAX_PATH-wcslen(szServerPath));
wcsncat_s(szServerPath, MAX_PATH,L"/", MAX_PATH-wcslen(szServerPath));
}
// Get an IDirectorySearch pointer to the root of the domain partition.
hr = pRootDSE->Get(CComBSTR("defaultNamingContext"), &var);
wcsncpy_s(szDSPath,MAX_PATH,szServerPath,MAX_PATH);
wcsncat_s(szDSPath, MAX_PATH,var.bstrVal, MAX_PATH - wcslen(szDSPath));
hr = ADsGetObject(szDSPath, IID_IDirectorySearch, (void**) &pSearch);
if (FAILED(hr))
{
wprintf(L"failed to get IDirectorySearch: 0x%x\n", hr);
goto cleanup;
}
while(noMoreData == false) {
// Initialize the structure to pass in the cookie.
// On the first call, the cookie is NULL and the length is zero.
// On later calls, the cookie and length are the values returned by
// the previous call.
dirsync.dwLength = *pulCookieLength;
dirsync.lpValue = *ppCookie;
arSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
arSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
arSearchPrefs[0].vValue.Integer = ADS_SCOPE_SUBTREE;
arSearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_DIRSYNC;
arSearchPrefs[1].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
arSearchPrefs[1].vValue.ProviderSpecific = dirsync;
arSearchPrefs[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
arSearchPrefs[2].vValue.dwType = ADSTYPE_INTEGER;
arSearchPrefs[2].vValue.Integer = 100;
hr = pSearch->SetSearchPreference(arSearchPrefs, 3); // this is where error is happening
if (FAILED(hr))
{
wprintf(L"failed to set search prefs: 0x%x\n", hr);
goto cleanup;
}
// Search for the objects indicated by the search filter.
hr = pSearch->ExecuteSearch(pszSearchFilter,
pAttributeNames, dwAttributes, &hSearch );
if (FAILED(hr))
{
wprintf(L"failed to set execute search: 0x%x\n", hr);
goto cleanup;
}
// Loop through the rows of the search result
// Each row is an object that has changed since the previous call.
hr = pSearch->GetNextRow(hSearch);
while ( SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS )
{
hr = pSearch->GetColumn( hSearch, L"distinguishedName", &col );
if ( SUCCEEDED(hr) )
{
wprintf(L"Distinguished Name: %s\n",col.pADsValues->CaseIgnoreString);
pSearch->FreeColumn( &col );
}
dwCount++;
hr = pSearch->GetNextRow( hSearch);
}
ADsGetLastError(&dwError, szError, 512, szProvider, 512);
if(ERROR_MORE_DATA == dwError)
{
noMoreData = false;
wprintf(L"Trying to get cookie...\n");
wprintf(L"Page ends here...\n");
hr = pSearch->GetColumn( hSearch, ADS_DIRSYNC_COOKIE, &col );
if ( SUCCEEDED(hr) ) {
if (col.dwADsType == ADSTYPE_PROV_SPECIFIC && col.pADsValues)
{
wprintf(L"Got cookie\n");
*pulCookieLength = col.pADsValues->ProviderSpecific.dwLength;
*ppCookie = (PUCHAR) AllocADsMem (*pulCookieLength);
memcpy(*ppCookie, col.pADsValues->ProviderSpecific.lpValue,
*pulCookieLength);
}
pSearch->FreeColumn( &col );
} else {
wprintf(L"no cookie: 0x%x\n", hr);
wprintf(L"Error!! More data available but could not continue because of error in cookie retrieval...\n");
noMoreData = true;
}
}
else
noMoreData = true;
}
wprintf(L"dwCount: %d\n", dwCount);
// After looping through the results, get the cookie.
if (hr == S_ADS_NOMORE_ROWS )
{
if (ppCookie != NULL)
{
// Free the existing heap allocation
GlobalFree (*ppCookie);
}
wprintf(L"Trying to get cookie...\n");
hr = pSearch->GetColumn( hSearch, ADS_DIRSYNC_COOKIE, &col );
if ( SUCCEEDED(hr) ) {
if (col.dwADsType == ADSTYPE_PROV_SPECIFIC && col.pADsValues)
{
wprintf(L"Got cookie\n");
*pulCookieLength = col.pADsValues->ProviderSpecific.dwLength;
*ppCookie = (PUCHAR) AllocADsMem (*pulCookieLength);
memcpy(*ppCookie, col.pADsValues->ProviderSpecific.lpValue,
*pulCookieLength);
}
pSearch->FreeColumn( &col );
} else
wprintf(L"no cookie: 0x%x\n", hr);
}
cleanup:
if (pRootDSE)
pRootDSE->Release();
if (hSearch)
pSearch->CloseSearchHandle(hSearch);
if (pSearch)
pSearch->Release();
VariantClear(&var);
CoUninitialize();
delete [] szServerPath;
delete [] szDSPath;
return hr;
}
if i give only 2 first preferences, i.e. Scope and DIRSYNC, then the line
hr = pSearch->SetSearchPreference(arSearchPrefs, 2);
does not give any error.
Can anyone please help me why it is happening. I have checked the way the option for paging is to be specified from msdn : http://msdn.microsoft.com/en-us/library/windows/desktop/aa746414(v=vs.85).aspx
Thanks in advance!
From MSDN link:
ADS_SEARCHPREF_DIRSYNC
Specifies a directory synchronization (DirSync)
search, which returns all changes since a specified state. In the
ADSVALUE structure, set the dwType member to ADS_PROV_SPECIFIC. The
ProviderSpecific member is an ADS_PROV_SPECIFIC structure whose
lpValue member specifies a cookie that indicates the state from which
changes are retrieved. The first time you use the DirSync control, set
the dwLength and lpValue members of the ADS_PROV_SPECIFIC structure to
zero and NULL respectively. After reading the results set returned by
the search until IDirectorySearch::GetNextRow returns
S_ADS_NOMORE_ROWS, call IDirectorySearch::GetColumn to retrieve the
ADS_DIRSYNC_COOKIE attribute which contains a cookie to use in the
next DirSync search. For more information, see Polling for Changes
Using the DirSync Control and LDAP_SERVER_DIRSYNC_OID. This flag
cannot be combined with ADS_SEARCHPREF_PAGESIZE. The caller must have
the SE_SYNC_AGENT_NAME privilege.
I recently inherited a code base that uses quicktime, I've noticed when importing moves using the function below it works most of the time, but then it will occasionally crash. It's weird when it crashes because it work 10 times in a row and then after it would crash the application.
I am using winapi in C for my application.
IDE: Visual Studio 8
OS: Windows 7
Quicktime version: Latest
This is from the Quicktime API Docs:
PutMovieIntoTypedHandle
Takes a movie, or a single track from within that movie, and converts it into a handle of a specified type.
OSErr PutMovieIntoTypedHandle (
Movie theMovie,
Track targetTrack,
OSType handleType,
Handle publicMovie,
TimeValue start,
TimeValue dur,
long flags,
ComponentInstance userComp
);
The Code:
Track newTrack;
FSSpec audioFile;
Movie audioFileMovie;
Track audioFileTrack;
Media audioFileMedia;
TimeValue audioTrackDuration;
long trackCount;
short audioRefNum;
TimeScale audioTimeScale;
OSErr err;
SoundDescriptionHandle outDesc, inDesc;
ComponentInstance soundComp =0;
SndListResource * slr;
SoundHeader * sh;
Handle soundData = NewHandle(0);
long i,sampleOffset,actualSamples, sampleCount;
//Create dataRef from audioFilePath
if((err = NativePathNameToFSSpec(audioFilePath, &audioFile, kErrorIfFileNotFound)) != noErr) {
if(err != fnfErr) {
printf("NativePathNameToFSSpec failed. Error: %d\n", err);
return 0;
}
}
if(noErr != OpenMovieFile(&audioFile, &audioRefNum, fsRdPerm)){
fprintf(stderr, "Couldn't open movie file\n");
return 0;
}
NewMovieFromFile(&audioFileMovie, audioRefNum, NULL, (StringPtr)NULL, newMovieActive, NULL);
if (audioRefNum != 0)
CloseMovieFile(audioRefNum);
//see how many tracks there are in the audioFileMovie
trackCount = GetMovieTrackCount(audioFileMovie);
//Obtain audio track from audio movie
audioFileTrack = GetMovieIndTrack(audioFileMovie, 1);
//Obtain audio media from audioFileTrack so that we can figure out how long it is
audioFileMedia = GetTrackMedia(audioFileTrack);
audioTrackDuration = GetMediaDuration(audioFileMedia);
audioTimeScale = GetMediaTimeScale(audioFileMedia);
soundComp = OpenDefaultComponent('spit', 'snd ');
if(!soundComp)
{
return 1;
}
inDesc=(SoundDescriptionHandle)NewHandle(0);
outDesc=nil;
GetMediaSampleDescription(GetTrackMedia(audioFileTrack),1,(SampleDescriptionHandle)inDesc);
outDesc = (SoundDescriptionHandle) NewHandleClear(sizeof(SoundDescription));
if(!outDesc)
{
CloseComponent(soundComp);
return 1;
}
outDesc[0]->descSize = sizeof(SoundDescription);
outDesc[0]->dataFormat = k8BitOffsetBinaryFormat;
outDesc[0]->numChannels = 1;
outDesc[0]->sampleSize = 8;
outDesc[0]->sampleRate = inDesc[0]->sampleRate;
MovieExportSetSampleDescription(soundComp, (SampleDescriptionHandle)outDesc, 'soun');
DisposeHandle((Handle)inDesc);
DisposeHandle((Handle)outDesc);
if( audioFileMovie && soundData && soundComp )
{
MessageBox(NULL, "Entering \"PutMovieIntoTypedHandle\".\n", "Checkpoint" ,MB_OK);
OSErr result = PutMovieIntoTypedHandle(audioFileMovie,0, 'snd ', soundData, 0, // frameTime,
GetMovieDuration(audioFileMovie), 0, soundComp);
MessageBox(NULL, "Made it passed \"PutMovieIntoTypedHandle\".\n", "Checkpoint" ,MB_OK);
}
else
{
return 1;
}