Media Foundation get encoded bitrate - c++

I am trying to get the encoded bitrate of an audio file (mp4, m4a, aac) using Media Foundation.
What I did is:
PROPVARIANT prop;
IMFSourceReader* reader;
MFCreateSourceReaderFromURL(filePath, NULL, &reader);
reader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_AUDIO_ENCODING_BITRATE,
&prop);
The second line ends with an error and with empty PROPVARIAT.
However, when I do:
reader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &prop);
It works fine.
Does anyone know what is the issue and/or are there any other approaches to get the encoded bitrate of an audio track?

Audio bitrate is a property of a track, not of a media file. Hence, you'd normally want to choose a specific track (yes, typically it's the first audio track even if the file is audio-only single track file) and query its attributes.
Presentation description would get you attributes like this (I list just a few relevant):
Key MF_MT_MAJOR_TYPE, vValue MFMediaType_Audio
Key MF_MT_SUBTYPE, vValue MFAudioFormat_AAC
Key MF_MT_AVG_BITRATE, vValue 125601
Key MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, vValue 0
Key MF_MT_AAC_PAYLOAD_TYPE, vValue 0
If you only need an informational value, such as presented by Windows shell:
and you don't need Media Foundation otherwise (that is, just to access the value), you can use shell property handler to do this job for you. You would just request PKEY_Audio_EncodingBitrate property and the handler would leverage Media Foundation to retrieve that for you.

Related

How can i properly configure ASF media sink in Media Foundation

Here is how i'm trying to configure the ASF media sink:
// Create media type
ComPtr<IMFMediaType> videoOutputType;
Try(MFCreateMediaType(&videoOutputType));
Try(MFSetAttributeSize(videoOutputType.Get(), MF_MT_FRAME_SIZE, 400, 300));
Try(videoOutputType->SetUINT32(MF_MT_AVG_BITRATE, 626000));
Try(videoOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
Try(videoOutputType->SetUINT32(MF_MT_VIDEO_ROTATION, 0));
Try(MFSetAttributeRatio(videoOutputType.Get(), MF_MT_FRAME_RATE, 30000, 1001));
Try(MFSetAttributeRatio(videoOutputType.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
Try(videoOutputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
Try(videoOutputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3));
// Create profile
ComPtr<IMFASFProfile> asfProfile;
Try(MFCreateASFProfile(&asfProfile));
ComPtr<IMFASFStreamConfig> streamConfig;
Try(asfProfile->CreateStream(videoOutputType.Get(), &streamConfig));
Try(streamConfig->SetStreamNumber(0));
Try(asfProfile->SetStream(streamConfig.Get()));
// Create media sink
ComPtr<IMFMediaSink> asfMediaSink;
ComPtr<IMFByteStream> outputByteStream(new NetworkOutputByteStream(stream));
Try(MFCreateASFStreamingMediaSink(outputByteStream.Get(), &asfMediaSink));
// Set content info
ComPtr<IMFASFContentInfo> asfContentInfo;
Try(asfMediaSink.As(&asfContentInfo));
Try(asfContentInfo->SetProfile(asfProfile.Get()));
// Create sink writer
Try(MFCreateSinkWriterFromMediaSink(asfMediaSink.Get(), NULL, &this->sinkWriter));
But the method SetProfile is returning the following error: E_INVALIDARG One or more arguments are invalid. So I assume that i am configuring it in a bad way. How can I do it right? I'm not sure how to use ASF media sink because i can't find any good samples about it.
I can say that in your code there are at least two big mistakes:
1. you index stream from 0:
streamConfig->SetStreamNumber(0)
It is a mistake - in Tutorial: 1-Pass Windows Media Encoding it is written that:
if (wStreamNumber < 1 || wStreamNumber > 127 )
{
return MF_E_INVALIDSTREAMNUMBER;
}
In ASF there are max 128 streams, but stream with index 0 is reserved for format needs. You must use index more that 0.
You try create media type by filling attributes - it does not good idea - firstly, you do not know all attributes, which are needed by MediaSink; secondly, you try create MediaType for Windows Video encoder - originally it is a DMO encoder which is changed for Media Foundation - it needs add special codec private data for MediaType via MF_MT_USER_DATA, Configuring a WMV Encoder - it means that MediaSink will try find such type data for Windows Media codec, but will not find it.
These are two mistakes, which are markable for me - I think that you should research tutorials on MSDN.
Regards.

IMediaSample returned by Sample Grabber has unexpected buffer size

I was working on a audio/video capture library for Windows using Media Foundation. However, I faced the issue described in this post for some webcams on Windows 8.1. I have thus decided to have another implementation using Directshow to support in my app the webcams for which the drivers haven't been updated yet.
The library works quite well, but I have noticed a problem with some webcams for which the sample (IMediaSample) returned has not the expected size according to the format that was set before starting the camera.
For example, I have the case where the format set has a subtype of MEDIASUBTYPE_RGB24 (3 bytes per pixels) and the frame size is 640x480. The biSizeImage (from BITMAPINFOHEADER) is well 640*480*3 = 921600 when applying the format.
The IAMStreamConfig::SetFormat() method succeed to apply the format.
hr = pStreamConfig->SetFormat(pmt);
I also set the format to the Sample Grabber Interface as follow:
hr = pSampleGrabberInterface->SetMediaType(pmt);
I applied the format before starting the graph.
However, in the callback (ISampleGrabberCB::SampleCB), I'm receiving a sample of size 230400 (which might be a buffer for a frame of size 320x240 (320*240*3=230400)).
HRESULT MyClass::SampleCB(double sampleTime, IMediaSample *pSample)
{
unsigned char* pBuffer= 0;
HRESULT hr = pSample->GetPointer((unsigned char**) &pBuffer);
if(SUCCEEDED(hr) {
long bufSize = pSample->GetSize();
//bufSize = 230400
}
}
I tried to investigate the media type returned using the IMediaSample::GetMediaType() method, but the media type is NULL, which means, according to the documentation of the GetMediaType method that the media type has not changed (so I guess, it's still the media type I've applied successfully using the IAMStreamConfig::SetFormat() function).
HRESULT hr = pSample->GetMediaType(&pType);
if(SUCCEEDED(hr)) {
if(pType==NULL) {
//it enters here => the media type has not changed!
}
}
Why the sample buffer size returned is not the expected size in this case? How can I solve this issue?
Thanks in advance!
Sample Grabber callback will always return "correct" size in terms that it matches actual data size and formats used in streaming pipeline.
If you are seeing the mismatch, it means that your filter graph topology differs from what you expect it to be. You need to review the graph (esp. using remote connection by GraphEdit), inspect media types and check why it was built incorrectly. For example, you could be applying format of your interest after connecting pins, which is too late.
See also:
How can I reverse engineer a DirectShow graph?

Windows Audio Endpoint API. Getting the names of my Audio Devices

My main goal at the moment is to get detailed information about all of the local machine's Audio Endpoint Devices. That is the objects representing the audio peripherals. I want to be able to choose which device to record from based on some logic (or eventually allow the user to manually do so).
Here's what I've got so far. I'm pretty new to c++ so dealing with all of these abstract classes is getting a bit tricky so feel free to comment on code quality as well.
//Create vector of IMMDevices
UINT endpointCount = NULL;
(*pCollection).GetCount(&endpointCount);
std::vector<IMMDevice**> IMMDevicePP; //IMMDevice seems to contain all endpoint devices, so why have a collection here?
for (UINT i = 0; i < (endpointCount); i++)
{
IMMDevice* pp = NULL;
(*pCollection).Item(i, &pp);
IMMDevicePP.assign(1, &pp);
}
My more technical goal at present is to get objects that implement this interface: http://msdn.microsoft.com/en-us/library/windows/desktop/dd371414(v=vs.85).aspx
This is a type that is supposed to represent a single Audio Endpoint device whereas the IMMDevice seems to contain a collection of devices. However IMMEndpoint only contains a method called GetDataFlow so I'm unsure if that will help me. Again the goal is to easily select which endpoint device to record and stream audio from.
Any suggestions? Am I using the wrong API? This API definitely has good commands for the actual streaming and sampling of the audio but I'm a bit lost as to how to make sure I'm using the desired device.
WASAPI will allow you to do what you need so you're using the right API. You're mistaken about IMMDevice representing a collection of audio devices though, that is IMMDeviceCollection. IMMDevice represents a single audio device. By "device", WASAPI does't mean audio card as you might expect, rather it means a single input/output on such card. For example an audio card with analog in/out + digital out will show up as 3 IMMDevices each with it's own IMMEndpoint. I'm not sure what detailed info you're after but it seems to me IMMDevice will provide you with everything you need. Basically, you'll want to do something like this:
Create an IMMDeviceEnumerator
Call EnumAudioEndpoints specifying render, capture or both, to enumerate into an IMMDeviceCollection
Obtain individual IMMDevice instances from IMMDeviceCollection
Device name and description can be queried from IMMDevice using OpenPropertyStore (http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812%28v=vs.85%29.aspx). Additional supported device details can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370794%28v=vs.85%29.aspx.
IMMDevice instances obtained from IMMDeviceCollection will also be instances of IMMEndpoint, use QueryInterface to switch between the two. However, as you noted, this will only tell you if you've got your hands on a render or capture device. Much easier to only ask for what you want directly on EnumAudioEndpoints.
About code quality: use x->f() instead if (*x).f(), although it's technically the same thing the -> operator is the common way to call a function through an object pointer
Don't use vector::assign, apparently that replaces the contents of the entire vector on each call so you'll end up with a collection of size 1 regardless of the number of available devices. Use push_back instead.
After enumerating your IMMDevices as Sjoerd stated it is a must to retrieve the IPropertyStore
information for the device. From there you have to extract the PROPVARIANT object as such:
PROPERTYKEY key;
HRESULT keyResult = (*IMMDeviceProperties[i]).GetAt(p, &key);
then
PROPVARIANT propVari;
HRESULT propVariResult = (*IMMDeviceProperties[i]).GetValue(key, &propVari);
according to these documents:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb761471(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa380072(v=vs.85).aspx
And finally to navigate the large PROPVARIANT structure in order to get the friendly name of the audio endpoint device simply access the pwszVal member of the PROPVARIANT structure as illustrated here:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd316594(v=vs.85).aspx
All about finding the right documentation!

Get encoder name from SinkWriter or ICodecAPI or IMFTransform

I'm using the SinkWriter in order to encode video using media foundation.
After I initialize the SinkWriter, I would like to get the underlying encoder it uses, and print out its name, so I can see what encoder it uses. (In my case, the encoder is most probably the H.264 Video Encoder included in MF).
I can get references to the encoder's ICodecAPI and IMFTransform interface (using pSinkWriter->GetServiceForStream), but I don't know how to get the encoder's friendly name using those interfaces.
Does anyone know how to get the encoder's friendly name from the sinkwriter? Or from its ICodecAPI or IMFTransform interface?
This is by far an effective solution and i am not 100% sure it works, but what could be done is:
1) At start-up enumerate all the codecs that could be used (as i understand in this case H264 encoders) and subscribe to setting change event
MFT_REGISTER_TYPE_INFO TransformationOutput = { MFMediaType_Video, MFVideoFormat_H264 };
DWORD nFlags = MFT_ENUM_FLAG_ALL;
UINT32 nCount = 0;
CLSID* pClsids;
MFTEnum( MFT_CATEGORY_VIDEO_ENCODER, nFlags, NULL, &TransformationOutput, NULL, &pClsids, &nCount);
// Ok here we assume nCount is 1 and we got the MS encoder
ICodecAPI *pMsEncoder;
hr = CoCreateInstance(pClsids[0], NULL, CLSCTX_INPROC_SERVER, __uuidof(ICodecAPI), (void**)&pMsEncoder);
// nCodecIds is supposed to be an array of identifiers to distinguish the sender
hr = pMsEncoder->RegisterForEvent(CODECAPI_AVEncVideoOutputFrameRate, (LONG_PTR)&nCodecIds[0]);
2) Not 100% sure if the frame rate setting is also set when the input media type for the stream is set, but anyhow you can try to set the same property on the ICodecAPI you retrieved from the SinkWriter. Then after getting the event you should be able to identify the codec by comparing lParam1 to the value passed. But still this is very poor since it relies on the fact that all the encoders support the event notification and requires unneeded parameter changing if my hypothesis about the event being generated on stream construction is wrong.
Having IMFTransform you don't have a friendly name of the encoder.
One of the options you have is to check transform output type and compare to well known GUIDs to identify the encoder, in particular you are going to have a subtype of MFVideoFormat_H264 with H264 Encoder MFT.
Another option is to reach CLSID of the encoder (IMFTransform does not get you it, but you might have it otherwise such as via IMFActivate or querying MFT_TRANSFORM_CLSID_Attribute attribute, or via IPersist* interfaces). Then you could look registry up for a friendly name or enumerate transforms and look your one in that list by comparing CLSID.

How to set bitrate of IVP8Encoder filter in a DirectShow application

How to set bitrate of vp8encoder filter in directshow application (c++ code). my graph looks like this.
Webcam --->Webm VP8 encoder -->AVI mux --->file writer(.avi)
I'm able to set bitrate in graphedit by right clicking vp8encoder->properties. But i want to set bitrate using c++ code in directshow application. I'm new to directshow please provide sample code . Thanks in advance
The subject suggests that you already have IVP8Encoder interface on hands (which also goes in line with the fact that you do have IDL files and their derivatives).
IVP8Encoder::SetTargetBitrate is the method that does the thing.
//Target data rate
//
//Target bandwidth to use for this stream, in kilobits per second.
//The value 0 means "use the codec default".
HRESULT SetTargetBitrate([in] int Bitrate);
HRESULT GetTargetBitrate([out] int* pBitrate);