[Linux C]百度音樂API實現線上搜歌
阿新 • • 發佈:2019-01-23
最近在做科大訊飛的語音解析模組,主要用於語音控制播放。採集語音輸入後,送給科大訊飛語音的SDK,雲伺服器返回JSON的資料,再解析拿到URL地址,最後送給播放器去播放。不知是否是尚未上線的product,申請的appid,解析JSON後拿到的URL地址,歌曲播放的時間都很短,一般不到1分鐘。
一番網路搜尋後,據說百度有個未公開的搜歌API,只要拿到歌手和歌曲名,就可以傳給這個URL,然後百度就會回你一個XML檔案,解析這個檔案,就可以拿到你要的MP3播放地址了,那就開始幹活吧。
百度音樂搜尋API實現說明
搜歌API: http://box.zhangmen.baidu.com/x?op=12&count=1&title=
在上面這個地址後面加上要搜尋的歌手和歌曲名,如下圖
至於這裡的歌手和歌曲名,都是由科大訊飛的SDK返回的JSON資料,解析出來的,此處不需要關心。
具體流程圖如下圖:
本文主要講述實線框框內的實現
- Decode 中文 URL
因為URL帶有中文字元,做HTTP請求的時候,需要將中文字元 decode一下,方能識別,具體方法如下
CHAR from_hex(CHAR ch)
{
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
CHAR *url_decode(CHAR *str)
{
CHAR *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
while (*pstr)
{
if (*pstr == '%')
{
if (pstr[1] && pstr[2])
{
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
pstr += 2;
}
}
else if (*pstr == '+')
{
*pbuf++ = ' ';
}
else
{
*pbuf++ = *pstr;
}
pstr++;
}
*pbuf = '\0';
return buf;
}
拿到decode的字元後,將其拼湊成一個完整的URL即可。
- CURL模擬HTTP請求
拿到可以訪問的URL後,接下來就是用CURL模擬HTTP請求,去抓取百度搜歌的內容,此處利用的CURL將資料寫到buffer的方式,並非儲存檔案方式,具體實現如下:
static INT32 curl_http_download_progress_callback(void *p,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
INT32 currentPercent = 0;
if(t != 0)
{
currentPercent = (int)((double)100*(d/t));
}
printf("Curl DownLoad percent : %d\n", currentPercent);
if(100 == currentPercent)
{
sem_post(&semDownLoadFinished);
}
return CURL_RET_OK;
}
static size_t curl_http_write_memory_cb(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if (mem->memory == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
exit(EXIT_FAILURE);
}
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
INT32 curl_http_get_page(const CHAR * url)
{
CURL *curl;
CURLcode res;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0; /* no data at this point */
res = curl_global_init(CURL_GLOBAL_ALL);
if(CURLE_OK != res)
{
return CURL_RET_FAIL;
}
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_http_write_memory_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_http_download_progress_callback);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,curl);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // display debug msg
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return CURL_RET_OK;
}
在確保網頁的內容全部儲存到buffer中,這裡用到了CURL的CURLOPT_PROGRESSFUNCTION引數,通過它,你可以拿到下載的百分比,當下載到100%時,會post一個訊號量,收到這個訊號量,就可以進行下一個環節了
- XML解析
拿到網頁內容後,解析來就是XML欄位。這裡是利用libxml2去做解析,具體實現如下
CHAR * xml_parse_file(CHAR *buf, INT32 len)
{
INT32 str1Len=0, str2Len=0;
CHAR *temp1URL=NULL, *temp2URL=NULL;
CHAR * retURL = NULL;
xmlDocPtr doc;
xmlNodePtr root,node;
xmlChar *value;
//xml_str_replace(buf, "encoding=\"gb2312\"", "encoding=\"utf-8\""); //此處因為在板子上跑,xml解析會出錯,說不支援gb2312,而PC上的LINUX不需要轉換即可解析,PC上加了也可以解析出來
doc = xmlParseMemory(buf,len); //parse xml in memory
if (NULL == doc)
{
printf("Document not parsed successfully\n");
return NULL;
}
root=xmlDocGetRootElement(doc);
for(node=root->children;node;node=node->next)
{
if(xmlStrcasecmp(node->name,BAD_CAST"url")==0)
break;
}
if(node==NULL)
{
TONLY_VOICE_LOG_ERR("no node = content\n");
return NULL;
}
for(node=node->children;node;node=node->next)
{
if(xmlStrcasecmp(node->name,BAD_CAST"encode")==0)
{
value=xmlNodeGetContent(node);
temp1URL = strrchr((CHAR *)value, '/');
str1Len = strlen((CHAR *)value) - strlen((CHAR *)temp1URL);
temp2URL = (CHAR *)malloc(sizeof(CHAR)*str1Len+1);
memset(temp2URL, 0, sizeof(CHAR)*str1Len+1);
memcpy(temp2URL, value, str1Len+1);
temp2URL[str1Len+1] = '\0';
printf("Cut out the decode URL is %s\n",temp2URL);
xmlFree(value);
}
else if(xmlStrcasecmp(node->name,BAD_CAST"decode")==0)
{
value=xmlNodeGetContent(node);
if(temp2URL)
{
str2Len = strlen((CHAR *)value) + strlen((CHAR *)temp2URL);
retURL = (CHAR *)malloc(sizeof(CHAR)*str2Len + 1);
memset(retURL, 0, sizeof(CHAR)*strlen((CHAR *)value) + 1);
strcpy(retURL, temp2URL);
strcat(retURL, (CHAR *)value);
retURL[str2Len+1] = '\0';
free(temp2URL);
printf("retURL is %s\n",retURL);
}
xmlFree(value);
}
}
xmlFreeDoc(doc);
return retURL;
}
最後xml_parse_file返回的字串,就是最終的mp3播放地址,將其送給播放器就可以實現播放了。
注意:
1.libxml2的安裝方法如下:
(1)sudo apt-get install libxml2
(2)sudo apt-get install libxml2-dev