How to access the meta data of QMediaPlayer? - c++

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());
}
});

Related

uwp: How download files when app is in suspended mode

There is queue with links of files to download. I'm trying find the way to continue downloading when application goes to suspend mode.
According to official microsoft documentation suitable class for this is BackgroundDownloader, but it's handles only one current downloading process. It looks wrong to call in loop CreateDownload() method for every link without waiting for the completion of previous links, isn't right?
More logical in my opinion is using in-process background task. I see it this way:
Implement Run(IBackgroundTaskInstance) method of interface IBackgroundTask (it should stay alive even when app is suspended, right?)
Using custom event transmit the queue to the implemented method
Inside Run(IBackgroundTaskInstance) method use BackgroundDownloader (by implementing the execution of one instance at a time)
But I'm stuck even with simple implementation for one file downloading. Bellow my Run(IBackgroundTaskInstance) method implementation:
void Task::DownloaderTask::Run(IBackgroundTaskInstance ^ taskInstance)
{
TaskDeferral = taskInstance->GetDeferral();
std::wstring filename = L"Pleiades_large.jpg";
Uri^ uri = ref new Uri(ref new Platform::String(L"https://upload.wikimedia.org/wikipedia/commons/4/4e/Pleiades_large.jpg"));
Concurrency::create_task(KnownFolders::GetFolderForUserAsync(nullptr, KnownFolderId::PicturesLibrary))
.then([this, filename, uri](StorageFolder^ picturesLibrary)
{
return picturesLibrary->CreateFileAsync(ref new Platform::String(filename.c_str()), CreationCollisionOption::GenerateUniqueName);
}).then([this, filename, uri](StorageFile^ destinationFile) {
BackgroundDownloader^ downloader = ref new BackgroundDownloader();
DownloadOperation^ download = downloader->CreateDownload(uri, destinationFile);
download->StartAsync();
}).then([this](Concurrency::task<void> previousTask)
{
try
{
previousTask.get();
TaskDeferral->Complete();
}
catch (Platform::Exception^ ex)
{
wchar_t buffer[1024];
swprintf_s(buffer, L"Exception: %s", ex->Message);
OutputDebugString(buffer);
}
});
}
The code above only creates empty file, but using the same code without BackgroundTask it works correctly. I didn't find any restrictions for BackgroundDownloader inside BackgroundTask.
So, my questions are:
Is it right way of usage BackgroundTask?
Is there another approach to solving the problem?
Is this problem solvable at all?
I've found the cause of the unexpected behavior:
The line of code TaskDeferral->Complete(); was at the end of the method at first while it should be at the end of async call.
Therefore, initial implementation (published in question) is correct.
All that had to be done was to Rebuild project.

Apply MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE to IMFSinkWriter

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

How to delete files created with ExtAudioFileCreateWithURL in c++?

I am doing low level audio processing on iOS. As a result parts of my application are written in c++.
There I create and write to audio files. Files are created using ExtAudioFileCreateWithURL and written to using ExtAudioFileWriteAsync. Files are closed using ExtAudioFileDispose.
Sometimes files need to be deleted.
How do I delete files created with ExtAudioFileCreateWithURL in c++?
Since the files are opened with a CFURLRef the function CFURLDestroyResource seems to be the way to do this, but this function is deprecated and it is unclear to me what would replace this function.
I eventually managed to tackle this problem by writing a helper function in a Objective-C file and calling that function from the C++ code.
bool removeItemAtURL(CFURLRef url) {
NSFileManager *fileManager = [NSFileManager defaultManager];
return [fileManager removeItemAtURL: (__bridge NSURL *)url error: NULL];
}
Although this works I am unsure if it is save to use the NSFilemanger object in a time critical audio rendering thread. If so then it would be best to let deletion of files be handled in a different thread.

Storing an INI file in memory

I am writing an application that uses an ini file to store all status codes (errors, success codes, etc.) A very simple version is this:
[success]
000=Status code not found.
[error]
000=Error code not found.
001=Username/Password not found.
And my CF Component to work with that uses the following code:
component hint="Set of tools to interact with status codes."{
public function init(string codefile) any{
this.codefile = Arguments.codefile;
}
public function getCodeString(string type, string code) string{
var code = getProfileString(Variables.codefile, Arguments.type, Arguments.code);
return code;
}
}
What I assume happens when I call the getProfileString is that Railo opens the file, searches for the key and returns the value. So as my application grows and I have a lot more codes, I expect that this process will slow down. So is there a way that I can open the file in my init method and read it all into the variables scope, and call the getProfileString from there?
You can even parse your ini file in onApplicationStart and push the data into application scope like recommended for a XML file by #Sergii, if you want to stick with the .ini approach.
Do something like that:
var sections = getProfileSections(variables.codeFile);
var sectionEntries = [];
var indx = 0;
for (key in sections){
sectionEntries = listToArray(sections[key]);
application[key] = {};
for (indx=1; indx <= arraylen(sectionEntries); indx++){
application[key][sectionEntries[indx]] = getProfileString(variables.cfgFile,key,sectionEntries[indx]);
}
}
haven't tested this on Railo, but it should work on ColdFusion 9 at least
Because you are using Railo there's possibly easiest solution: put the file into the RAM filesystem.
So full path to the file will look like ram:///some/path/to/config.ini.
Obviously, you need to write the file into the RAM first, possibly on first request.
So slightly modified version of the component may look this way:
component hint="Set of tools to interact with status codes."{
public function init(string codefile, string.ramfile) any{
variables.codefile = arguments.codefile;
variables.ramfile = arguments.ramfile;
}
public function getCodeString(string type, string code) string{
if (NOT fileExists(variables.ramfile)) {
fileCopy(variables.codefile, variables.ramfile);
}
return getProfileString(variables.ramfile, arguments.type, arguments.code);
}
}
Please note that I've changed this.codefile to the variables.codefile in the init.
Any way, I'm also not sure ini file is the most handy and maintainable solution. You need to parse it each time any way, right? If you need a file config, use XML. Just parse it in onApplicationStart and push the data into the application scope.

Monitoring a folder for new files in Windows

What is the best way to go about monitoring a folder to see when an image file has been added to it? Files are added approximately once a minute and the naming goes like this... image0001.jpg, image0002.jpg, image0003.jpg etc. I need to know when a file has been written to the folder so that my app can access and use it.
Look into directory change notifications.
As per previously mentioned, the directory change notifications is what you want.
I have looked into them as well, and the caveat I have seen is that windows will fire off the notification when the file starts to be written to the folder. If the file is large enough then you will receive the notification before the file has finished being written.
Check out this google search for various solutions for waiting until the file is completely written
Edit: I just saw that the question was tagged with c++, and I linked to a .Net search. Although what I provided may not be the correct language, I would think that you will still have the same issues on Windows no matter what system you are coding with.
FileSystemWatcher should be able to do that for you.
Change notifactions may cause some overhead, if you've NTFS, consider NTFS change journals.
You can use a polling method to monitor the folder. The loop will execute every 5 seconds, for example.
This method returns a list of new files:
List<string> files = new List<string>();
string path = #"C:\test\"; // whatever the path is
public List<string> GetNewFiles(string path)
{
// store all the filenames (only .jpg files) in a list
List<string> currentFiles = System.IO.Directory.GetFiles(path, "*.jpg");
if ( currentFiles.Count() > files.Count() )
{
count = newFiles.Length - files.Length;
List<string> newFiles = new List<string>();
foreach ( string file in currentFiles )
{
if ( !files.Contains(file) )
{
newFiles.Add(file);
}
}
}
files = currentFiles;
return newFiles;
}
This is the method that will poll every 5 seconds and call the previous method.
public void MonitorFolder()
{
while (true)
{
List<string> newFiles = GetNewFiles(path);
System.Threading.Thread.Sleep(5000); // 5000 milliseconds
}
}
Synch.variant FindFirstChangeNotification
Asynch.variant ReadDirectoryChangesW
This was the top google result for my search so I'll add this as an answer.
If you're using Qt, there's QFileSystemWatcher. I didn't know this existed and we happened to be using Qt, so I wasted more than a few hours using FindFirstChangeNotification to rewrite what was readily available to me until a colleague showed me the light.
Good learning experience though.
inotify might be your thing