I'm writing an application in C++ using the Windows Media Foundation API.
I create an IMFMediaSource, create a PresentationDescriptor, select a stream, then call Start on the media source.
My reading of the API docs is that the call to Start should generate a MENewStream event. My code calls GetEvent for the media source, which just blocks forever.
Why don't I see at least the new stream event? I plan to request an audio sample from the media source inside the loop, but to do that, I need the interface provided by the new stream event.
IMFMediaSource *pMediaSource = <initialized from another pointer>
HRESULT hr;
IMFPresentationDescriptor *pPresentationDescriptor;
IMFMediaEventGenerator *pEventGen;
IMFMediaStream *pMediaStream;
IMFMediaEvent *pMediaEvent;
DWORD streamCount;
PROPVARIANT propVar;
hr = pMediaSource->CreatePresentationDescriptor(&pPresentationDescriptor);
hr = pPresentationDescriptor->GetStreamDescriptorCount(&streamCount);
printf("The capture device has %ld streams\n",streamCount);
hr = pPresentationDescriptor->SelectStream(0L);
hr = pMediaSource->QueryInterface(IID_IMFMediaEventGenerator,(LPVOID *)&pEventGen);
PropVariantInit(&propVar);
propVar.scode = VT_EMPTY;
hr = pMediaSource->Start(pPresentationDescriptor,NULL,&propVar);
while (1) {
printf("Waiting for event\n"); fflush(stdout);
hr = pMediaSource->GetEvent(0L,&pMediaEvent);
printf("Got event\n"); fflush(stdout);
}
This loop runs in a worker thread spawned by the main application thread.
For brevity, I've omitted the error checks on the HRESULTs.
Solved: there was a missing call of MFStartup().
Related
I'm trying to query a list of supported modes from a video adapter driver:
// IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES - Retrieve the count of modes on the display adapter
// Input-Buffer: none
// Output-Buffer: VIDEO_NUM_MODES
VIDEO_NUM_MODES videoNumModes{};
// Send the IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES control code directly to the device driver
ULONG bytesReturned{};
if (::DeviceIoControl(
hDevice, // Handle to the display adapter device
IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES, // IOCTL code
nullptr, 0, // No input param struct
&videoNumModes, sizeof videoNumModes, // Address/size of output param struct
&bytesReturned, // Bytes returned in the output param struct
nullptr)) // Optional OVERLAPPED structure
{
// Allocate a buffer to receive the array of supported modes
const auto bufferSizeInBytes = videoNumModes.NumModes * videoNumModes.ModeInformationLength;
pVideoModeInfo = new VIDEO_MODE_INFORMATION[videoNumModes.NumModes];
// IOCTL_VIDEO_QUERY_AVAIL_MODES - Retrieve the array of supported modes
// Input-Buffer: none
// Output-Buffer: <allocated buffer>
// Send the IOCTL_VIDEO_QUERY_AVAIL_MODES control code directly to the device driver
if (::DeviceIoControl(
hDevice,
IOCTL_VIDEO_QUERY_AVAIL_MODES,
nullptr, 0,
pVideoModeInfo, bufferSizeInBytes,
&bytesReturned,
nullptr))
I get FALSE back on the first DeviceIoControl call with LastError set to ERROR_INVALID_FUNCTION (0x1).
I use this same code successfully to call custom IOCTL stuff in my drivers, so I'm confident that the implementation itself is sound. However, when I open a handle to the device, I'm supposed to use a string containing information about both the device and the interface I'm going to use. I defined the GUID for my custom IOCTL interface, and I use something like the following to send custom IOCTL commands:
hDevice = ::CreateFileW(L"\\\\?\\ROOT#DISPLAY#0000#{5f2f2b485bbd-5201-f1f9-4520-30f4bf353599}", ...);
But the documentation for IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES and IOCTL_VIDEO_QUERY_AVAIL_MODES doesn't mention which interface (GUID) they're a part of.
I assumed that I had to open the adapter device with the GUID_DEVINTERFACE_DISPLAY_ADAPTER interface, but I'm getting Incorrect Function on the first DeviceIoControl call. Same result if I open the adapter or one of its monitors with GUID_DEVINTERFACE_MONITOR.
I've searched online for any code examples, but all I find are from the driver side, responding to the query.
The display adapter driver that I'm issuing this against is an IddCx driver, if that helps. Any clues?
I'm trying to support a digital camera with WPD. I have working request/data/response communication (PTP types 1,2,3), but have problems with events (type 4). From the event I need a command code (ex. 0xc102) and up to three integer parameters.
I have registered and am receiving events using the code from Microsoft's Portable Devices COM API Sample, and it catches the occurrence of the event easily - but I have no way to get the parameters. The command code is not provided directly, but it is embedded as part of the (supposedly random) CLSID.
I have tried to use the event object's IPortableDeviceValues as I would in parsing a response, but when trying to call GetIPortableDevicePropVariantCollectionValue the API returns a HRESULT of ERROR_NOT_FOUND (0x80070490). This call is used in getting params from the response object.
I tried (code inserted in CPortableDeviceEventsCallback::OnEvent):
HRESULT hr;
// Try to get all available data from the event:
DWORD pcelt{};
pEventParameters->GetCount(&pcelt);
printf("pEventParameters->GetCount: %d\n", pcelt);
for (unsigned int i = 0; i < pcelt; i++)
{
PROPERTYKEY pk;
PROPVARIANT pv;
pEventParameters->GetAt(i, &pk, &pv);
printf("PARAM %ws %d -> ", (PWSTR)CGuidToString(pk.fmtid), pk.pid);
switch (pv.vt)
{
case VT_UNKNOWN: printf("unknown\n"); break;
case VT_LPWSTR: printf("VT_LPWSTR: %ws\n", pv.pwszVal); break;
case VT_CLSID:
{
// note that OLECHAR is a typedef'd wchar_t
WCHAR szGUID[64] = { 0 };
_GUID guid = *pv.puuid;
(void)StringFromGUID2(guid, szGUID, 64);
printf("VT_CLSID: %ws\n", szGUID);
break;
}
default: printf("not supported vt %d\n", pv.vt); break;
}
}
// PRINTED: pEventParameters->GetCount: 3
// PRINTED: PARAM {15AB1953-F817-4FEF-A921-5676E838F6E0} 3 -> VT_CLSID: {C1020000-5738-4FF2-8445-BE3126691059}
// PRINTED: PARAM {4D545058-EF88-4E4D-95C3-4F327F728A96} 1011 -> VT_IUNKNOWN
// PRINTED: PARAM {15AB1953-F817-4FEF-A921-5676E838F6E0} 2 -> VT_LPWSTR: \\?\usb#vid_04da&pid_2382#0000000000000000000xhr1805180002#{6ac27878-a6fa-4155-ba85-f98f491d4f33}
// Try to get response code as if this was response (PTP type 3) - ERROR_NOT_SUPPORTED
DWORD dwResponseCode{};
hr = pEventParameters->GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_RESPONSE_CODE, &dwResponseCode);
assert(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
// Try to get params as if this was response (PTP type 3) - ERROR_NOT_SUPPORTED
CComPtr<IPortableDevicePropVariantCollection> spRespParams;
hr = pEventParameters->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_MTP_EXT_RESPONSE_PARAMS, &spRespParams);
assert(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
I also tried to get WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID and WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE from it, but they are not set.
There is also WPD_EVENT_ATTRIBUTE_OPTIONS, WPD_EVENT_ATTRIBUTE_NAME and WPD_EVENT_ATTRIBUTE_PARAMETERS - but I have no idea on how to get to them, nor is there anything of interest inside.
Or maybe what I seek is in the IUNKNOWN that I ignore in the switch case? But what type could it be?
The data is there, I can see it in Wireshark+USBPcap and other PTP applications use it.
Found the answer in https://blogs.msdn.microsoft.com/dimeby8/2006/10/06/listening-to-mtp-events/
It was the IUnknown, and it's type was IPortableDevicePropVariantCollection with GUID 4D545058-EF88-4E4D-95C3-4F327F728A96.
I need to play raw PCM data (16 bit signed) using CoreAudio on OS X. I get it from network using UDP socket (on sender side data is captured from microphone).
The problem is that all I hear now is some short cracking noise and then only silence.
I'm trying to play data using AudioQueue. I setup it like this:
// Set up stream format fields
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = 44100;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
streamFormat.mBitsPerChannel = 16;
streamFormat.mChannelsPerFrame = 1;
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mFramesPerPacket = 1;
streamFormat.mReserved = 0;
OSStatus err = noErr;
// create the audio queue
err = AudioQueueNewOutput(&streamFormat, MyAudioQueueOutputCallback, myData, NULL, NULL, 0, &myData->audioQueue);
if (err)
{ PRINTERROR("AudioQueueNewOutput"); myData->failed = true; result = false;}
// allocate audio queue buffers
for (unsigned int i = 0; i < kNumAQBufs; ++i) {
err = AudioQueueAllocateBuffer(myData->audioQueue, kAQBufSize, &myData->audioQueueBuffer[i]);
if (err)
{ PRINTERROR("AudioQueueAllocateBuffer"); myData->failed = true; break; result = false;}
}
// listen for kAudioQueueProperty_IsRunning
err = AudioQueueAddPropertyListener(myData->audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData);
if (err)
{ PRINTERROR("AudioQueueAddPropertyListener"); myData->failed = true; result = false;}
MyAudioQueueOutputCallback is:
void MyAudioQueueOutputCallback(void* inClientData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer)
{
// this is called by the audio queue when it has finished decoding our data.
// The buffer is now free to be reused.
MyData* myData = (MyData*)inClientData;
unsigned int bufIndex = MyFindQueueBuffer(myData, inBuffer);
// signal waiting thread that the buffer is free.
pthread_mutex_lock(&myData->mutex);
myData->inuse[bufIndex] = false;
pthread_cond_signal(&myData->cond);
pthread_mutex_unlock(&myData->mutex);
}
MyAudioQueueIsRunningCallback is:
void MyAudioQueueIsRunningCallback(void* inClientData,
AudioQueueRef inAQ,
AudioQueuePropertyID inID)
{
MyData* myData = (MyData*)inClientData;
UInt32 running;
UInt32 size;
OSStatus err = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &running, &size);
if (err) { PRINTERROR("get kAudioQueueProperty_IsRunning"); return; }
if (!running) {
pthread_mutex_lock(&myData->mutex);
pthread_cond_signal(&myData->done);
pthread_mutex_unlock(&myData->mutex);
}
}
and MyData is:
struct MyData
{
AudioQueueRef audioQueue; // the audio queue
AudioQueueBufferRef audioQueueBuffer[kNumAQBufs]; // audio queue buffers
AudioStreamPacketDescription packetDescs[kAQMaxPacketDescs]; // packet descriptions for enqueuing audio
unsigned int fillBufferIndex; // the index of the audioQueueBuffer that is being filled
size_t bytesFilled; // how many bytes have been filled
size_t packetsFilled; // how many packets have been filled
bool inuse[kNumAQBufs]; // flags to indicate that a buffer is still in use
bool started; // flag to indicate that the queue has been started
bool failed; // flag to indicate an error occurred
bool finished; // flag to inidicate that termination is requested
pthread_mutex_t mutex; // a mutex to protect the inuse flags
pthread_mutex_t mutex2; // a mutex to protect the AudioQueue buffer
pthread_cond_t cond; // a condition varable for handling the inuse flags
pthread_cond_t done; // a condition varable for handling the inuse flags
};
I'm sorry if I posted too much code - hope it helps anyone to understand what exactly I do.
Mostly my code based on this code which is version of AudioFileStreamExample from Mac Developer Library adapted to work with CBR data.
Also I looked at this post and tried AudioStreamBasicDescription desribed there. And tried to change my flags to Little or Big Endian. It didn't work.
I looked at some another posts here and in the other resources while finding similar problem, I checked the order of my PCM data, for example. I just can't post more than two links.
Please anyone help me to understand what I'm doing wrong! Maybe I should abandon this way and use Audio Units right away? I'm just very newbie in CoreAudio and hoped that mid-level of CoreAudio will help me to solve this problem.
P.S. Sorry for my English, I tried as I can.
I hope you've solved this one on your own already, but for the benefit of other people who are having this problem, I'll post up an answer.
The problem is most likely because once an Audio Queue is started, time continues moving forward, even if you stop enqueueing buffers. But when you enqueue a buffer, it is enqueued with a timestamp that is right after the previously enqueued buffer. This means that if you don't stay ahead of the where the audio queue is playing, you will end up enqueuing buffers with a timestamp in the past, therefore the audio queue will go silent and the isRunning property will still be true.
To work around this, you have a couple of options. The simplest in theory would be to never fall behind on submitting buffers. But since you are using UDP, there is no guarantee that you will always have data to submit.
Another option is that you can keep track of what sample you should be playing and submit an empty buffer of silence whenever you need to have a gap. This option works good if your source data has timestamps that you can can use to calculate how much silence you need. But ideally, you wouldn't need to do this.
Instead you should be calculating the timestamp for the buffer using the system time. Instead of AudioQueueEnqueueBuffer, you'll need to use AudioQueueEnqueueBufferWithParameters instead. You just need to make sure the timestamp is ahead of where the queue is currently at. You'll also have to keep track what the system time was when you started the queue, so you can calculate the correct timestamp for each buffer you are submitting. If you have timestamp values on your source data, you should be able to use them to calculate the buffer timestamps as well.
I am porting an app reading data from a BT device to Mac. On the mac-specific code, I have a class with the delegate methods for the BT callbacks, like -(void) rfcommChannelData:(...)
On that callback, I fill a buffer with the received data. I have a function called from the app:
-(int) m_timedRead:(unsigned char*)buffer length:(unsigned long)numBytes time:(unsigned int)timeout
{
double steps=0.01;
double time = (double)timeout/1000;
bool ready = false;
int read,total=0;
unsigned long restBytes = numBytes;
while(!ready){
unsigned char *ptr = buffer+total;
read = [self m_readRFCOMM:(unsigned char*)ptr length:(unsigned long)restBytes];
total+=read;
if(total>=numBytes){
ready=true; continue;
}
restBytes = numBytes-total;
CFRunLoopRunInMode(kCFRunLoopDefaultMode, .4, false);
time -= steps;
if(time<=0){
ready=true; continue;
}
}
My problem is that this RunLoop makes the whole app un extremely slow. If I don't use default mode, and create my on runloop with a runlooptimer, the callback method rfcommChannelData never gets called. I create my one runloop with the following code:
// CFStringRef myCustomMode = CFSTR("MyCustomMode");
// CFRunLoopTimerRef myTimer;
// myTimer = CFRunLoopTimerCreate(NULL,CFAbsoluteTimeGetCurrent()+1.0,1.0,0,0,foo,NULL);
// CFRunLoopAddTimer(CFRunLoopGetCurrent(), myTimer, myCustomMode);
// CFRunLoopTimerInvalidate(myTimer);
// CFRelease(myTimer);
Any idea why the default RunLoop slows down the whole app, or how to make my own run loop allow callbacks from rfcommchannel being triggered?
Many thanks,
Anton Albajes-Eizagirre
If you're working on the main thread of a GUI app, don't run the run loop internally to your own methods. Install run loop sources (or allow asynchronous APIs of the frameworks install sources on your behalf) and just return to the main event loop. That is, let flow of execution return out of your code and back to your caller. The main event loop runs the run loop of the main thread and, when sources are ready, their callbacks will fire which will probably call your methods.
I want to read and write from serial using events/interrupts.
Currently, I have it in a while loop and it continuously reads and writes through the serial. I want it to only read when something comes from the serial port. How do I implement this in C++?
This is my current code:
while(true)
{
//read
if(!ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL)){
//error occurred. Report to user.
}
//write
if(!WriteFile(hSerial, szBuff, n, &dwBytesRead, NULL)){
//error occurred. Report to user.
}
//print what you are reading
printf("%s\n", szBuff);
}
Use a select statement, which will check the read and write buffers without blocking and return their status, so you only need to read when you know the port has data, or write when you know there's room in the output buffer.
The third example at http://www.developerweb.net/forum/showthread.php?t=2933 and the associated comments may be helpful.
Edit: The man page for select has a simpler and more complete example near the end. You can find it at http://linux.die.net/man/2/select if man 2 select doesn't work on your system.
Note: Mastering select() will allow you to work with both serial ports and sockets; it's at the heart of many network clients and servers.
For a Windows environment the more native approach would be to use asynchronous I/O. In this mode you still use calls to ReadFile and WriteFile, but instead of blocking you pass in a callback function that will be invoked when the operation completes.
It is fairly tricky to get all the details right though.
Here is a copy of an article that was published in the c/C++ users journal a few years ago. It goes into detail on the Win32 API.
here a code that read serial incomming data using interruption on windows
you can see the time elapsed during the waiting interruption time
int pollComport(int comport_number, LPBYTE buffer, int size)
{
BYTE Byte;
DWORD dwBytesTransferred;
DWORD dwCommModemStatus;
int n;
double TimeA,TimeB;
// Specify a set of events to be monitored for the port.
SetCommMask (m_comPortHandle[comport_number], EV_RXCHAR );
while (m_comPortHandle[comport_number] != INVALID_HANDLE_VALUE)
{
// Wait for an event to occur for the port.
TimeA = clock();
WaitCommEvent (m_comPortHandle[comport_number], &dwCommModemStatus, 0);
TimeB = clock();
if(TimeB-TimeA>0)
cout <<" ok "<<TimeB-TimeA<<endl;
// Re-specify the set of events to be monitored for the port.
SetCommMask (m_comPortHandle[comport_number], EV_RXCHAR);
if (dwCommModemStatus & EV_RXCHAR)
{
// Loop for waiting for the data.
do
{
ReadFile(m_comPortHandle[comport_number], buffer, size, (LPDWORD)((void *)&n), NULL);
// Display the data read.
if (n>0)
cout << buffer <<endl;
} while (n > 0);
}
return(0);
}
}