我在网上搜索过,但找不到一个很好的例子来说明我想要完成的事情。使用 c++,我正在处理 Kodi(一个开源媒体中心应用程序)的可视化。这种可视化将数据作为 http 客户端发送到 Philips Hue 桥接器(http 服务器),以更改灯光的颜色和其他属性以匹配音乐。我正在使用 cURL 来处理 http 请求。
使用一个线程会导致 cURL 花费时间完成其工作并从 Philips Hue 桥(http 服务器)接收响应的问题。这会阻止屏幕上的可视化,有时还会阻止音频输出。您可以在此处查看延迟:可视化的 YouTube 视频。
Kodi 中附加组件的结构方式意味着没有“主要”功能。所以对于多线程,我想不出一个好的结构,因为网络上的大多数示例在 main 中创建一个线程,然后在 main 中加入它。目前,我正在尝试这样的事情:
- start 函数创建一个工作线程
- 工作线程等待直到堆栈被 audiodata 函数填充
- 一旦堆栈被填充,工作线程读取顶部元素,发送 cURL 请求,并弹出元素
- 销毁函数应该加入线程吗?
我尝试了多种创建线程的技术,但是在 Windows 中进行调试时,它们都会导致如下错误:
First-chance exception at 0x0856335A (visualization.wavforhue.dll) in Kodi.exe: 0xC0000005: Access violation reading location 0xFEEEFEF6.
First-chance exception at 0x76CDC52F in Kodi.exe: Microsoft C++ exception: access_violation at memory location 0x003BEC5C.
First-chance exception at 0x76CDC52F in Kodi.exe: Microsoft C++ exception: access_violation at memory location 0x003BEC5C.
或者
Run-Time Check Failure #2 - Stack around the variable '_Now' was corrupted.
在std::this_thread::sleep_for(std::chrono::seconds(2));
.
或在函数完成abort();
后由 Kodi 调用。start
在下面的代码中启用 curl 调用后,我收到一个不同的错误。它发生在第二次尝试播放歌曲时。第一次尝试成功。
错误是The ordinal 4445 could not be located in the dynamic link library LIBEAY32.dll.
此库与 SSL 相关联,我的代码未使用该库。但是,我一定会以某种方式影响 Kodi 程序中的其他 curl 实例。
有趣的部分是没有 curl 调用,代码似乎可以正常运行。我想如果我能成功解决LIBEAY32.dll问题,这可以标记为已解决。
这是完整的代码(底部有 GPL 通知以提高可读性):
//--------------------------------------------------------------------------------------
#include <xbmc_vis_dll.h>
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#endif
#include <curl/curl.h>
#include <string>
//------------------------------------------------------------------
//------------------------------------------------------------------
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
//----------------------------------------------------------------------
// Thread initialization -------------------------------------------------
std::mutex gMutex;
std::condition_variable gThreadConditionVariable;
std::atomic<bool> gRunThread;
bool gReady;
std::thread gWorkerThread;
std::queue<int> gQueue;
// End thread initialization ---------------------------------------------
void workerThread()
{
bool isEmpty;
std::string json;
// This thread comes alive when Create(), Start(), or AudioData()
// is invoked by the main program.
// It runs until Destroy() or Stop() is invoked.
while (gRunThread)
{
//check that an item is on the stack
{
std::lock_guard<std::mutex> lock(gMutex);
isEmpty = gQueue.empty();
}
if (isEmpty)
{
//Wait until AudioData() sends data.
std::unique_lock<std::mutex> lock(gMutex);
gThreadConditionVariable.wait(lock, []{return gReady; });
}
else
{
std::lock_guard<std::mutex> lock(gMutex);
int value = gQueue.front();
gQueue.pop();
}
if (!isEmpty)
{
/*
CURL *curl = curl_easy_init();
CURLcode res;
json = "{\"hue\":" + std::to_string(rand() % 60000) + "}";
// Now specify we want to PUT data, but not using a file, so it has to be a CUSTOMREQUEST
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3L);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
//curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, noop_cb);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
// Set the URL that is about to receive our POST.
curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.10.6/api/KodiVisWave/lights/3/state");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// always cleanup curl
curl_easy_cleanup(curl);
*/
}
}
}
//-- Create -------------------------------------------------------------------
// Called on load. Addon should fully initalize or return error status
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_Create(void* hdl, void* props)
{
if (!props)
return ADDON_STATUS_UNKNOWN;
gRunThread = true;
gReady = false;
// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}
// Must initialize libcurl before any threads are started.
//curl_global_init(CURL_GLOBAL_ALL);
return ADDON_STATUS_OK;
}
//-- Start --------------------------------------------------------------------
// Called when a new soundtrack is played
//-----------------------------------------------------------------------------
extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName)
{
gRunThread = true;
// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}
}
//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
// Processing audio data
if (rand() % 7 == 3)
{
std::lock_guard<std::mutex> lock(gMutex);
gQueue.push(1);
}
gRunThread = true;
// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}
// Send the curl calls to the worker thread
{
std::lock_guard<std::mutex> lock(gMutex);
gReady = true;
}
gThreadConditionVariable.notify_one();
}
//-- Stop ---------------------------------------------------------------------
// This dll must stop all runtime activities
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Stop()
{
gRunThread = false;
while (gWorkerThread.joinable())
{
gWorkerThread.join();
}
}
//-- Detroy -------------------------------------------------------------------
// Do everything before unload of this add-on
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Destroy()
{
gRunThread = false;
while (gWorkerThread.joinable())
{
gWorkerThread.join();
}
}
//-- Render -------------------------------------------------------------------
// Called once per frame. Do all rendering here.
//-----------------------------------------------------------------------------
extern "C" void Render()
{
}
//-- GetInfo ------------------------------------------------------------------
// Tell XBMC our requirements
//-----------------------------------------------------------------------------
extern "C" void GetInfo(VIS_INFO* pInfo)
{
pInfo->bWantsFreq = false;
pInfo->iSyncDelay = 0;
}
//-- OnAction -----------------------------------------------------------------
// Handle XBMC actions such as next preset, lock preset, album art changed etc
//-----------------------------------------------------------------------------
extern "C" bool OnAction(long flags, const void *param)
{
bool ret = false;
return ret;
}
//-- GetPresets ---------------------------------------------------------------
// Return a list of presets to XBMC for display
//-----------------------------------------------------------------------------
extern "C" unsigned int GetPresets(char ***presets)
{
return 0;
}
//-- GetPreset ----------------------------------------------------------------
// Return the index of the current playing preset
//-----------------------------------------------------------------------------
extern "C" unsigned GetPreset()
{
return 0;
}
//-- IsLocked -----------------------------------------------------------------
// Returns true if this add-on use settings
//-----------------------------------------------------------------------------
extern "C" bool IsLocked()
{
return false;
}
//-- GetSubModules ------------------------------------------------------------
// Return any sub modules supported by this vis
//-----------------------------------------------------------------------------
extern "C" unsigned int GetSubModules(char ***names)
{
return 0; // this vis supports 0 sub modules
}
//-- HasSettings --------------------------------------------------------------
// Returns true if this add-on use settings
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" bool ADDON_HasSettings()
{
return false;
}
//-- GetStatus ---------------------------------------------------------------
// Returns the current Status of this visualization
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_GetStatus()
{
return ADDON_STATUS_OK;
}
//-- GetSettings --------------------------------------------------------------
// Return the settings for XBMC to display
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
{
return 0;
}
//-- FreeSettings --------------------------------------------------------------
// Free the settings struct passed from XBMC
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_FreeSettings()
{
}
//-- SetSetting ---------------------------------------------------------------
// Set a specific Setting value (called from XBMC)
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_SetSetting(const char *strSetting, const void* value)
{
return ADDON_STATUS_OK;
}
//-- Announce -----------------------------------------------------------------
// Receive announcements from XBMC
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data)
{
}
/*
* Copyright (C) 2008-2016 Team Kodi
* http://kodi.tv
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
有一个更好的方法吗?如果不是,我应该在销毁函数中做什么来减轻访问冲突?
这是一个非常简单的源代码版本的链接,没有复杂的声音数据处理、图形渲染和 curl 调用。它仍然失败Run-Time Check Failure #2 - Stack around the variable '_Now' was corrupted.
at std::this_thread::sleep_for(std::chrono::seconds(2));
。
编辑:上面链接的代码现在应该可以工作了。我仍然在 Windows 7 和 8.1 上遇到 DirectX 11 和 cURL 的单独问题,但 OpenELEC、Ubuntu 和 Android 对这种结构感到满意。完整实现在视频中链接或切换到 GitHub 中的 master 分支。