Apply MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE to IMFSinkWriter - c++

Currently I'm creating WMV file with Windows Media Foundation SDK using the method (Transcode API) in following article: https://learn.microsoft.com/en-us/windows/desktop/medfound/tutorial--using-the-sink-writer-to-encode-video
What I noticed is if I don't set MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, output WMV file may be corrupted (show up as terrible artifacts during playback) when coded WMV file over the specified bitrate.
According to following article, the flag needs to set to IMFASFContentInfo:
https://learn.microsoft.com/en-us/windows/desktop/medfound/mfpkey-asfmediasink-autoadjust-bitrate-property
So I tried following way:
IMFSinkWriter* pSinkWriter = NULL;
//initialize sink writer with MFCreateSinkWriterFromURL
...
IMFASFContentInfo* pContentInfo = NULL;
pSinkWriter->GetServiceForStream((DWORD)MF_SINK_WRITER_MEDIASINK, GUID_NULL, __uuidof(IMFASFContentInfo), (LPVOID*)&pContentInfo);
IPropertyStore* pPropStore = NULL;
pContentInfo->GetEncodingConfigurationPropertyStore(0, &pPropStore);
PROPVARIANT var;
PropVariantInit(&var);
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
pPropStore->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var);
PropVariantClear(&var);
//Add Video stream and BeginWriting, then start passing samples
...
But, the settings does not seem to be applied to muxer, and I still see obvious artifacts of corrupted stream.
What I guess is since MFCreateSinkWriterFromURL will create MediaSink and underlying IMFASFMultiplexer internally, however MFASF_MULTIPLEXER_AUTOADJUST_BITRATE needs to be set during creating IMFASFMultiplexer, so settings the flag after SinkWriter is created is too late.
If I don't use Transcode API, and create IMFASFWriter by myself, I think I can set MFASF_MULTIPLEXER_AUTOADJUST_BITRATE during creating IMFASFWriter manually but since I already have a working code besides settings this flag by using Transcode API, if possible I want to keep current way.
If anybody has any clue/solution/workaround, please let me know.

You should be able to query for IPropertyStore of to the ASF File Sink from the Sink Writer directly using GetServiceForStream and specifying MF_SINK_WRITER_MEDIASINK like this:
pSinkWriter->GetServiceForStream(MF_SINK_WRITER_MEDIASINK, GUID_NULL, IID_PPV_ARGS(&pPropertyStore));
where pPropertyStore points to an IPropertyStore.
After that you should set the MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE property as explained here

Related

Correctly setting up MMDevice in a DirectX project

I am currently trying to piece together a shader-based music visualizer. The plan is to read data from the current MMDevice, which I'm trying to follow the documentation for, but I must be doing something wrong because I had to jump through all sorts of hoops to get even just the MMDeviceEnumerator to compile.
In order for the uuids of MMDeviceEnumerator and IMMDeviceEnumerator to be defined, I had to set #define WINAPI_FAMILY WINAPI_FAMILY_GAMES. This was also required for EDataFlow and ERole enumerations to be defined. My first question is if I've missed some configuration somewhere, or if this is the intended method of enabling these things.
Currently, I have the following code in an AudioStream class:
class AudioStream {
public:
AudioStream() {
//SUCCEEDING(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
SUCCEEDING(CoCreateInstance(
__uuidof(IMMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(MMDeviceEnumerator),
(void**)&this->mmDeviceEnumerator));
SUCCEEDING(this->mmDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole,
&this->mmDevice));
}
private:
IAudioClient* audioClient = NULL;
IAudioCaptureClient* captureClient = NULL;
IMMDeviceEnumerator* mmDeviceEnumerator = NULL;
IMMDevice* mmDevice = NULL;
};
If you're familiar with what the DirectX 12 project template looks like, this object is being instantiated in the Sample3DSceneRenderer constructor. The main issue I'm having right now is the following two errors which are immediately raised during startup:
onecore\com\combase\dcomrem\resolver.cxx(2299)\combase.dll!75AA0DFF: (caller: 75B1CF2C) ReturnHr(1) tid(42a8) 80040154 Class not registered
onecore\com\combase\dcomrem\resolver.cxx(2507)\combase.dll!75B1CF4D: (caller: 75AA29E4) ReturnHr(2) tid(42a8) 80040154 Class not registered
This causes the entire app to hang, and the project template visualization to never appear (the succeeding macro exits). Does anyone have any idea why this is failing? It must have to be something with the CoCreateInstance call :(
You are writing a Universal Windows Platform (UWP) app because that's what the "built-in" DirectX 12 App project template creates in Visual Studio. UWPs do not have access to all the same APIs and IMMDevice is not part of the UWP API surface area.
The fact that you defined WINAPI_FAMILY_GAMES means you hacked the API Family Partition macros which will define the API in a UWP context, but it doesn't mean that API actually works from the AppContainer process that all UWPs run in.
You really have two options:
(1) If you want to write a UWP, then you will need to enumerate audio devices via the proper Windows Runtime APIs which are in the Windows::Devices::Enumeration namespace.
Assuming you are using C++/CX language extensions (instead of the more modern C++/WinRT projections), then this code works:
auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status == Windows::Foundation::AsyncStatus::Started)
{
Sleep(100);
}
if (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
throw std::runtime_error("FindAllAsync");
}
DeviceInformationCollection^ devices = operation->GetResults();
for (unsigned i = 0; i < devices->Size; ++i)
{
using Windows::Devices::Enumeration::DeviceInformation;
DeviceInformation^ d = devices->GetAt(i);
// d->Id->Data();
// d->Name->Data();
}
Also, if you want to get access to the audio capture device from a UWP, you must add a capability to your manifest to request it via <DeviceCapability Name="microphone"/>. See Microsoft Docs.
You should take the time to read the Microsoft Docs on UWPs so you have a better idea of what's supported and what's not.
(2) If you want to write a Win32 desktop app, use the directx-vs-templates instead which include DirectX 12 starting templates for Win32 desktop apps (plus alternative DirectX templates for UWP if that's your thing).
Whichever appmodel you use, you may want to take a look at DirectX Tool Kit for Audio.
BTW, WINAPI_FAMILY_GAMES is used by the Microsoft GDK for Xbox which is for writing titles for Xbox One and Xbox Series X|S. It uses Win32 APIs and doesn't use Windows Runtime APIs, so it has the IMMDevice interface in it's API surface. See Microsoft Docs.

How to access the meta data of QMediaPlayer?

I want to access the metadata of the mp3 file and put it in the labels but the program doesn't read it.
I read http://doc.qt.io/qt-5/qmediametadata.html.
I wrote this code but it doesn't work properly (besides QFileInfo).
path = item->text(); //text is a path from QFileDialog::getOpenFileName
/*QMediaPlayer*/ sound.setMedia(QUrl::fromLocalFile(path));
QFileInfo info(path);
ui->label_3->setText(sound.metaData("Title").toString());
if (ui->label_3->text()=="")
ui->label_3->setText(sound.metaData("AlbumTitle").toString());
if (ui->label_3->text()=="")
ui->label_3->setText(info.baseName());
ui->label_5->setText(sound.metaData("Author").toString());
if (ui->label_5->text()=="")
ui->label_5->setText(sound.metaData("AlbumArtist").toString());
if (ui->label_5->text()=="")
ui->label_5->setText(sound.metaData("Composer").toString());
Library and multimedia are added.
Cause
It takes time for the media to be loaded after calling QMediaPlayer::setMedia, hence requesting the meta data right after the media has been set results in:
QVariant(Invalid)
Solution
I would suggest you to wait for the media to be loaded by connecting to the QMediaPlayer::mediaStatusChanged and reading the meta data once the status becomes QMediaPlayer::LoadedMedia.
Note: If you make sound a local variable, it would be destroyed when it goes out of scope. Better use auto *sound = new QMediaPlayer(this);.
Example
Here is an example I have prepared for you of how you could change your code in order to implement to proposed solution:
connect(sound, &QMediaPlayer::mediaStatusChanged, [this, sound, info](QMediaPlayer::MediaStatus status){
if (status == QMediaPlayer::LoadedMedia) {
ui->label_3->setText(sound->metaData("Title").toString());
if (ui->label_3->text()=="")
ui->label_3->setText(sound->metaData("AlbumTitle").toString());
if (ui->label_3->text()=="")
ui->label_3->setText(info.baseName());
ui->label_5->setText(sound->metaData("Author").toString());
if (ui->label_5->text()=="")
ui->label_5->setText(sound->metaData("AlbumArtist").toString());
if (ui->label_5->text()=="")
ui->label_5->setText(sound->metaData("Composer").toString());
}
});

WPD Object Filename Truncated at '.'

In my project, I'm using the Windows Portable Device (WPD) API to enumerate the contents of a mobile device. WPD API Enumeration Guide. I'm able to enumerate over each object and view their properties as shown in the API programming guide. WPD API Properties Guide
However when I try to get an object's name that has a . within the name, the returned value is truncated at that .
HRESULT hr = objectProperties->GetStringValue(WPD_OBJECT_NAME, &strOriginalFileName);
if(FAILED(hr))
return false;
PWSTR wideStr = strOriginalFileName;
char buffer[20];
wcstombs(buffer, wideStr, 20);
qDebug() << buffer;
So for example, an object (folder on the device) with the name of com.example is returned as com. This becomes an obvious issue when I'm trying to locate a specific filepath on the device.
I can't seem to figure out what's wrong. Am I misunderstanding how the filename actually is? Is example another property or something within the com object? I'm very confused.
EDIT:
So I used the WPD API sample software to retrieve all the object properties of the com.example object and you can see that WPD itself cannot get the full folder name.
Thanks for your time!
The WPD Application Programming Reference refers following 3 NAMEs.
WPD_OBJECT_HINT_LOCATION_DISPLAY_NAME: A friendlier name, mostly intended for display
WPD_OBJECT_NAME: The name of the object on device.
WPD_OBJECT_ORIGINAL_FILE_NAME: The original filename of the object on device.
The MS code sample in C++ uses WPD_OBJECT_ORIGINAL_FILE_NAME to get to the actual file name (underneath the object) while transferring files from device to PC.
I modified the MS code sample (to enumerate object properties) and it showed me the actual file name (nothing truncated from the filename com.ef1.first.second)
I used:
Windows Windows 7 Ultimate (without SP1)
Visual Studio 2013
Android 4.4.4 (Moto-E)
Connection type: MTP
Memory type: Internal Memory as well as External (SD Card)
I wouldn't be surprised if it doesn't work on some combination of Windows versions, Windows SDK versions, android versions, Connection types (MTP, PTP, USB Mass Storage).
Here is the part of code that I modified (and that is how it worked).
// Reads properties for the user specified object.
void ReadContentProperties(_In_ IPortableDevice* device)
{
//.... Edited for brevity
tempHr = propertiesToRead->Add(WPD_OBJECT_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
// Here is the added code
tempHr = propertiesToRead->Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
//.... Edited for brevity
}

c++ - ffmpeg yadif deinterlacing

I'm trying deinterlacing video with ffmpeg in my C++ program.
First of all, i used avpicture_deinterlacebut is deprecated.
Looking for more information, I've tried avfilter_get_by_name("yadif")after avfilter_register_all()but always return NULL. I've tried the next code too, but still not working. I've tried different parameters in avfilter_init_strfunction buterris always less than 0, that means there is an error.
int err;
// Register all built-in filters
avfilter_register_all();
// Find the yadif filter
AVFilter *yadif_filter = avfilter_get_by_name("buffer");
AVFilterContext *filter_ctx;
// Create the filter context with yadif filter
avfilter_open(&filter_ctx, yadif_filter, NULL);
// Init the yadif context with "1:-1" option
err = avfilter_init_str(filter_ctx, "\"yadif=1:-1\"");
I know filtering_video.c file is a good start point to understand how to build a filter but I don't want to build one, I only need to use the yadif deinterlacing filter. I have the AVFramebut I don't know how to apply de yadif filter to it.
Any help could be welcome.
In older FFmpeg releases, yadif was only compiled if --enable-gpl configure option was used. You probably need to update to a later release or re-compile the old release with --enable-gpl.

Writing namespace extensions with Windows 7 integration

I'm new to the topic shell extensions and I'm looking for resources about namespace extensions. I would like to write a namespace extension which supports SFTP with all options to browse like FTP in the explorer.
I read the examples 1, 2 of zengxi from codeproject, but they don't compile right and seems to be old. I think that there were also many changes like the folder selection in the address bar.
Can somebody provide me some resources in the right direction or some working examples?
UPDATE:
It is important that the source is free. This is a non-profit project.
At the moment I found a good source on the MSDN called Explorer Data Provider Sample. This is up to date and provides some aliases Explorer Data Provider and Shell Data Source. What is yet missing is drop & drag support and a glue for supporting protocol links.
For future usage it would be great to find a way to associate a file extension with that shell data source like zip files.
Here is the full example of creating a namespace to mapping real files on Flickr.
However, only source codes available. No tutorial.
Hope it helps....
http://www.viksoe.dk/code/flickrdrive.htm
For Drag&Drop, this series of articles are a great point to start. After I understood that the files must have the flag can copy, can move, etc. I had almost the solution. The magic was to add one line in GetAttributesOf:
*rgfInOut |= SFGAO_CANCOPY|SFGAO_CANMOVE;
Also I had to publish the IDataObject in GetUIObjectOf like this:
if(riid == IID_IDataObject) {
PWSTR pszName;
hr = _GetName(apidl[0], &pszName);
hr = SHCreateDataObject(m_pidl, cidl, apidl,
new CFileDataObject(pszName), riid, ppv);
} else if(riid == IID_IDropTarget) {
// TODO publish
return E_NOINTERFACE;
}
That's all.
By the way what is the best practice for allocating CFileDataObject here?
Take a look at the EZNamespaceExtensionsMFC library which makes it very easy to develop namespace extensions. Check out its FileBrowser and RegBrowser samples which you can use a starting point.
DISCLAIMER: I work for LogicNP Software, the developer of EZNamespaceExtensionsMFC