I'm creating a webrtc-based voip app for windows in C++. I'm trying to initialize a peerconnection. I'm stuck at the part to fetch a camera. I'm using the following code to find a camera to start streaming media from (copied from the peerconnection client example):
rtc::scoped_ptr<cricket::DeviceManagerInterface> dev_manager(cricket::DeviceManagerFactory::Create());
if (!dev_manager->Init()) {
LOG(LS_ERROR) << "Can't create device manager";
return NULL;
}
std::vector<cricket::Device> devs;
if (!dev_manager->GetVideoCaptureDevices(&devs)) {
LOG(LS_ERROR) << "Can't enumerate video devices";
return NULL;
}
std::vector<cricket::Device>::iterator dev_it = devs.begin();
cricket::VideoCapturer* capturer = NULL;
for (; dev_it != devs.end(); ++dev_it) {
capturer = dev_manager->CreateVideoCapturer(*dev_it);
if (capturer != NULL)
break;
}
capturer is empty after this procedure. I stepped through the code to see what was wrong. dev_manager is succesfully intialized, devs gets a single entry (my webcam) with a name:
"logitech HD webcam c270"
And an id:
"\\\\?\\usb#vid_046d&pid_0825&mi_00#7&2dbd1a82&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\\{bbefb6c7-2fc4-4139-bb8b-a58bba724083}"
But after the CreateVideoCapturer() call, capturer is still empty. I get a warning in the console saying:
Warning(webrtcvideocapturer.cc:175): Failed to find capturer for id: \\?\usb#vid_046d&pid_0825&mi_00#7&2dbd1a82&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{bbefb6c7-2fc4-4139-bb8b-a58bba724083}
I checked if the id I get in devs and this one match and they do. The entire log for my app can be found in this pastebin. As you can see, right after trying to assign the camera as capturer and failing, the app crashes on an assert(capturer != NULL) call somewhere in videosource.cc.
The camera is not in use, nor is it defect. The peerconnection client example works perfectly and uses the same code. I think I'm missing some step in the initialization of webrtc, but I can't find which step.
edit with additional info
I'm debug stepping through the library now. In webrtcvideocapturer.cc around line 160 is the following code:
int num_cams = info->NumberOfDevices();
char vcm_id[256] = "";
bool found = false;
for (int index = 0; index < num_cams; ++index) {
char vcm_name[256];
int32 i = info->GetDeviceName(index, vcm_name, ARRAY_SIZE(vcm_name), vcm_id, ARRAY_SIZE(vcm_id));
if (i != -1) {
if (device.name == reinterpret_cast<char*>(vcm_name)) {
found = true;
break;
}
}
}
if (!found) {
LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
factory_->DestroyDeviceInfo(info);
return false;
}
There are two problems with this part. First of all, if I step into info->NumberOfDevices() it shows me that that function's only content is the line return 0;. I tried hard-coding 1 there, to at least get into the for loop. Then when I step into the info->GetDeviceName() call it shows me that the content of that function is return -1;.
These two functions are meant to be implemented by a class that inherits from webrtc::VideoCaptureModule::DeviceInfo, so there is clearly something not initialized that does needs to be initialized. What do I still need to do before trying to get a camera?
What libraries did you link?
Because Google WebRTC source codes are changing rapidly, it is difficult to track down issues to the source level.
But I remember the almost same problem occurred when I accidentally linked external capture module library (video_capture_module_impl ??) or (I'm not sure) omitted internal impl (video_capture_module_internal_impl ??).
Related
I've written an app that's based on the SampleGrabberSink example. My application is actually working as I want but one function I needed to perform was to get the video resolution when the .mp4 source changed part way into the file. I eventually worked out how to do it but it seems very long winded and I suspect there must be a simpler way.
In the sample below is there a way to shorten the code block handling the MESessionStreamSinkFormatChanged case? It seems like what's taking nearly 40 lines of code (counting initialisation and cleanup) should take 1 or 2.
HRESULT RunSession(IMFMediaSession *pSession, IMFTopology *pTopology, OnVideoResolutionChangedFunc onVideoResolutionChanged)
{
IMFMediaEvent *pEvent = NULL;
IMFTopologyNode *pNode = nullptr;
IMFStreamSink *pStreamSink = nullptr;
IUnknown *pNodeObject = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = nullptr;
IMFMediaType *pMediaType = nullptr;
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = S_OK;
CHECK_HR(hr = pSession->SetTopology(0, pTopology));
CHECK_HR(hr = pSession->Start(&GUID_NULL, &var));
while(1)
{
HRESULT hrStatus = S_OK;
MediaEventType met;
CHECK_HR(hr = pSession->GetEvent(0, &pEvent));
CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
CHECK_HR(hr = pEvent->GetType(&met));
if(FAILED(hrStatus))
{
printf("Session error: 0x%x (event id: %d)\n", hrStatus, met);
hr = hrStatus;
goto done;
}
else
{
//printf("Session event: event id: %d\n", met);
switch(met)
{
case MESessionStreamSinkFormatChanged:
//std::cout << "MESessionStreamSinkFormatChanged." << std::endl;
{
MF_TOPOLOGY_TYPE nodeType;
UINT64 outputNode{0};
GUID majorMediaType;
UINT64 videoResolution{0};
UINT32 stride{0};
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!
CHECK_HR_ERROR(pEvent->GetUINT64(MF_EVENT_OUTPUT_NODE, &outputNode), "Failed to get ouput node from media changed event.");
CHECK_HR_ERROR(pTopology->GetNodeByID(outputNode, &pNode), "Failed to get topology node for output ID.");
CHECK_HR_ERROR(pNode->GetObject(&pNodeObject), "Failed to get the node's object pointer.");
CHECK_HR_ERROR(pNodeObject->QueryInterface(IID_PPV_ARGS(&pStreamSink)), "Failed to get media stream sink from activation object.");
CHECK_HR_ERROR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler from stream sink.");
CHECK_HR_ERROR(pMediaTypeHandler->GetCurrentMediaType(&pMediaType), "Failed to get current media type.");
CHECK_HR_ERROR(pMediaType->GetMajorType(&majorMediaType), "Failed to get major media type.");
if(majorMediaType == MFMediaType_Video)
{
CHECK_HR_ERROR(pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &videoResolution), "Failed to get new video resolution.");
CHECK_HR_ERROR(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride), "Failed to get the new stride.");
std::cout << "Media session video resolution changed to width " << std::to_string(HI32(videoResolution))
<< " and height " << std::to_string(LO32(videoResolution))
<< " and stride " << stride << "." << std::endl;
if(onVideoResolutionChanged != nullptr) {
onVideoResolutionChanged(HI32(videoResolution), LO32(videoResolution), stride);
}
}
break;
}
default:
break;
}
}
if(met == MESessionEnded)
{
break;
}
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
}
done:
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
return hr;
}
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!
The code looks good. You don't need to do everything on resolution change - you can retrieve media type handler just once and keep the pointer when it's needed.
On the entertaining comment above I would say the following. Just like in the case of DirectShow, Sample Grabber is the way to cut corners hard and do something against the design of the pipeline. Almost everyone out there loved DirectShow Sample Grabber and so the future of Media Foundation Sample Grabber could be if there was enough of people who developed for Media Foundation in first place.
Resolution change is generally the business of primitives, i.e. source-transform, transform-transform, and transform-sink connections. Even in this scenario you are getting the notification on resolution change out of band (it's asynchronous notification for you) and you are lucky Media Foundation and its Sample Grabber are so flexible that you can handle this in first place.
To implement this reliably you would normally need a custom media sink, but Sample Grabber lets you cut a corner even at this time.
With custom sink implementation you are guaranteed that you don't receive media sample with new resolution before you agree on new resolution in first place (and you can reject it, of course). With MESessionStreamSinkFormatChanged however the event is posted for async retrieval and Sample Grabber continues processing, so technially you can have grabber callbacks with frames of new resolution before you get the session event.
If in your real application you create output node using stream sink and not media sink activate as in your example above, you would not need to retrieve media type handle using topology nodes - you would be able to pull it directly.
I am trying to detect the presence of an UPS(Eaton)from my application in RAD Studio C++. I tried using the SYSTEM_POWER_CAPABILITIES (MSDN) but I always get the answer that the "UPS not found" even though the UPS is connected and works fine. Is there any other way to detect if the UPS is present and if it is ON?
int tmain()
{
SYSTEM_POWER_CAPABILITIES SysPowCap = {0};
if(!::GetPwrCapabilities(&SysPowCap))
{
ShowMessage(GetLastError());
return 0;
}
if(SysPowCap.UpsPresent)
ShowMessage("UPS found");
else
ShowMessage("UPS not found");
return 0;
}
I am using xboxdrv in version 0.8.7 and a XBox 360 Wireless controller on raspberrypi 3 (4.9.13-v7+).
It works sometimes a day, sometimes some minutes. But I get always:
[ERROR] USBController::on_read_data(): USB read failure: 32:
LIBUSB_TRANSFER_ERROR
The common suggestions in the forum didn't help me and I think there are still some people struggling with that.
I tried to contact the author but didn't get answer so far.
The software runs under GPL-3.0, so I had a look in the source code.
Project on GitHub:
https://github.com/xboxdrv/xboxdrv
I identified where it crashs (src/usb_controller.cpp, line 277 and below). That's the last else-section in the code below. Starting with log_error("USB read failure:
void
USBController::on_read_data(libusb_transfer* transfer)
{
assert(transfer);
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
// process data
XboxGenericMsg msg;
if (parse(transfer->buffer, transfer->actual_length, &msg))
{
submit_msg(msg);
}
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS) // could also check for LIBUSB_ERROR_NO_DEVICE
{
log_error("failed to resubmit USB transfer: " << usb_strerror(ret));
m_transfers.erase(transfer);
libusb_free_transfer(transfer);
send_disconnect();
}
}
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED)
{
m_transfers.erase(transfer);
libusb_free_transfer(transfer);
}
else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
{
m_transfers.erase(transfer);
libusb_free_transfer(transfer);
send_disconnect();
}
else
{
log_error("USB read failure: " << transfer->length << ": " << usb_transfer_strerror(transfer->status));
m_transfers.erase(transfer);
libusb_free_transfer(transfer);
}
}
My C++ knowledge is bad, further I haven't experience with libusb. Could you make a suggestion how I could fix that for me.
The problem is, as soon as the mentioned error occurs, xboxdrv is stopping and it will handle no more input from the XBOX 360 Wireless Controller.
It should just kind of ignore that wrong package and wait for a new one. But just don't stop working.
Thank you
I am using Qt and libvlc to create a gui for an ip camera stream. After i finished it i started testing it against wrong values NULLs... etc. My problem is that when i give a wrong string as network link i dont get NULL as return values. A portion of the code is this
const char * const vlc_args[] = {
"--preferred-resolution=576",
"--network-caching=250",
"--no-audio"
};
my_vlcInstance = libvlc_new(3, vlc_args);
if (my_vlcInstance == NULL){
emit sendDebugMessage("Couldn't create vlc instance", "Red");
return;
}
my_LiveMedia = libvlc_media_new_location(my_vlcInstance, link.toStdString().c_str());
if (my_LiveMedia == NULL){
emit sendDebugMessage("Error installing media", "Red");
return;
}
my_LiveMediaPlayer = libvlc_media_player_new_from_media(my_LiveMedia);
if (my_LiveMediaPlayer == NULL){
emit sendDebugMessage("Error creating media player", "Red");
return;
}
now link is a QString containing the network link. I know that bad things happen as i can see the errors in the Visual studio debug window. "core input error: open of `dsfgdfgfg' failed" "Your input can't be opened" and stuff like that. So why cant i catch these errors?
Furthermore... when i push my start recording button a file is produced (corrupted obviously as there is no stream) and libvlc function calls dont return the errors as specified in the documentation. For this for example:
if (libvlc_media_player_play(my_LiveMediaPlayer) == 0){
emit sendDebugMessage("Live mode started successfully", "Green");
isLive = true;
}
So how do i catch the bad link?
I solved my problem with the libvlc_media_player_get_state function. I ignore the libvlc_media_player_play return value and i just poll the current state of the media player until i get a different state from IDLE/CLOSE and OPENING. On the bad links i get an ERROR state and on the correct links i get PLAYING state.
Goodday all,
I'm getting the following error:
First-chance exception at 0x67887AB7 (SDL2_mixer.dll) in Racing.exe: 0xC0000005: Access violation reading location 0xCCCCCCD4.
I think the problem could be with the pointer, but I'm not experienced enough in c++ to find it.
I'm hoping someone can tell me what I did wrong, and hopefully I can learn from it :)
Trying to call it from my gamebus.
Music Mus;
Mus.SpeelGeluid("crash");
Music class
bool Music::LoadMusic(){
//Load music
Mix_Music *gMusic = NULL;
gMusic = Mix_LoadMUS("MusicTest.wav");
Mix_PlayMusic(gMusic, -1);
bool success = true;
if (gMusic == NULL)
{
printf("Failed to load Music background song! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
// Load sound effects
Mix_Chunk *gCrash = NULL;
gCrash = Mix_LoadWAV("Crash.wav");
if (gCrash == NULL)
{
printf("Failed to load scratch sound effect! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
return success;
}
void Music::SpeelGeluid(string soundname){
cout << soundname << endl;
if (soundname == "crash")
{
try
{
Mix_PlayChannel(-1, gCrash, 0);
}
catch (int e)
{
cout << "An exception occurred. Exception Nr. " << e << '\n';
}
}
else
{
} }
Thank you for your time
You have two major problems:
First, you're using gMusic before checking whether it's NULL.
This may crash Mix_PlayMusic, and actually means that compilers may optimise out the later NULL check.
Second, and most likely the cause of your problem, you're declaring gCrash and gMusic as local variables in LoadMusic.
From the names, and your use of gCrash in SpeelGeluid, my educated guess is that you also have two global variables, or possibly member variables, with the same names where you intend to store the results of loading the files.
Your local variables are hiding these globals, and you're only modifying the local variables.
Remove the lines
Mix_Music *gMusic = NULL;
and
Mix_Chunk *gCrash = NULL;
to get rid of these local variables.
And do initialise the globals to NULL when you define them.
Check your pointers as soon as you've allocated them and handle them appropriately. You nearly do:
gMusic = Mix_LoadMUS("MusicTest.wav");
Mix_PlayMusic(gMusic, -1); // <- You're using the pointer here before you check it
bool success = true;
if (gMusic == NULL) // <- this needs to be immediately after assignment
That's just from the code you've pointed. It's also possible you've missed a generic initialisation call from the SDL2_mixer.dll.
Do debug it and step through line by line so you know which bit is causing the error.