讓linux支援qq手機音樂播放
Gmediarender 是一個模擬upnp render的linux下的軟體,他可以接收qq音樂發來的資料並在電腦上播放出來。下面介紹一下方法:
網站http://gmrender.nongnu.org/下載gmediarender
編譯,編譯過程回出現
gcc -Wall -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -L/usr/lib -o gmediarender main.o upnp.o upnp_control.o upnp_connmgr.o upnp_transport.o upnp_device.o upnp_renderer.o webserver.o output_gstreamer.o xmlescape.o -pthread -lgstreamer-0.10 -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lxml2 -lglib-2.0 -lupnp
/usr/bin/ld: upnp.o: undefined reference to symbol 'ixmlDocument_createElementNS'
//usr/lib/x86_64-linux-gnu/libixml.so.2: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
這樣的錯誤,grep上面的函式發現在下面檔案裡面:
/usr/lib/x86_64-linux-gnu/libixml.a
/usr/lib/x86_64-linux-gnu/libixml.so.2.0.7
解決方法,直接連結靜態庫:
gcc -Wall -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -L/usr/lib -o gmediarender main.o upnp.o upnp_control.o upnp_connmgr.o upnp_transport.o upnp_device.o upnp_renderer.o webserver.o output_gstreamer.o xmlescape.o -pthread -lgstreamer-0.10 -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lxml2 -lglib-2.0 -lupnp /usr/lib/x86_64-linux-gnu/libixml.a
安裝完成後即可用手機qq音樂連線測試。
首先要保證電腦和手機連線到同一個網段(同一個wifi ap),在pc端執行gmediarender,然後開啟qq音樂後開始播放音樂,在播放介面出現一個圈圈帶箭頭圖示,點選該圖示顯示出新的播放裝置gmediarender,點選後音樂就傳送到了pc上,pc電腦就可以出聲音了。
Gmediarender 僅支援gst,我對其做了mpd的簡單擴充套件,讓聲音可以從mpd裡面播放出來,下面是擴充套件mpd支援的程式碼(非常初級,僅僅可以讓mpd播放聲音而已):
output_mpd.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <mpd/client.h>
#include <mpd/status.h>
#include <mpd/song.h>
#include <mpd/entity.h>
#include <mpd/search.h>
#include <mpd/tag.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <glib.h>
//#define ENABLE_TRACING
#include "logging.h"
#include "upnp_connmgr.h"
#include "output_mpd.h"
static char *gsuri = NULL;
static struct mpd_connection *conn = NULL;
#define SET_CONN() conn = mpd_connection_new(NULL, 0, 30000)
#define FREE_CONN() mpd_connection_free(conn); conn=NULL
struct mpd_status *
getStatus(struct mpd_connection *conn)
{
struct mpd_status *ret = mpd_run_status(conn);
return ret;
}
static unsigned
query_queue_length(struct mpd_connection *conn)
{
struct mpd_status *status = getStatus(conn);
const unsigned length = mpd_status_get_queue_length(status);
mpd_status_free(status);
return length;
}
void output_set_uri(const char *uri)
{
ENTER();
printf("%s: setting uri to '%s'\n", __FUNCTION__, uri);
if (gsuri != NULL)
{
free(gsuri);
}
gsuri = strdup(uri);
LEAVE();
}
int output_play(void)
{
int result = -1;
int try_num=10;
enum mpd_server_error serror_error;
ENTER();
if(strlen(gsuri)==0)
{printf("usri is empty:%s\n");return -1;};
SET_CONN();
//next schedule it to the next play
printf("conn=%x\n",(unsigned int )conn);
struct mpd_status *status = getStatus(conn);
if(status==NULL)
{
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
printf("connection error code:%d,free connection\n", mpd_connection_get_error(conn));
serror_error=mpd_connection_get_server_error(conn);
printf("serror=%d\n",serror_error);
mpd_connection_free(conn);
}
}
const int playlist_len=mpd_status_get_queue_length(status);
printf("---playlist_len=%d,add song:%s\n",playlist_len,gsuri);
mpd_command_list_begin(conn, false);
if(playlist_len)
{
const int next_song_pos=mpd_status_get_next_song_pos(status);
printf("next_song_pos=%d\n",next_song_pos);
if(next_song_pos==-1)
{
mpd_send_add(conn,gsuri);
}
else
mpd_send_add_id_to(conn, gsuri,next_song_pos);
}
else{
mpd_send_add(conn,gsuri);
}
mpd_command_list_end(conn);
mpd_response_finish(conn);
mpd_run_play(conn);
if(playlist_len)
mpd_run_next(conn);
mpd_status_free(status);
FREE_CONN();
result = 0;
LEAVE();
return result;
}
int output_stop(void)
{
SET_CONN();
mpd_run_stop(conn);
FREE_CONN();
return 0;
}
int output_pause(void)
{
SET_CONN();
mpd_run_pause(conn,true);
FREE_CONN();
return 0;
}
int output_loop()
{
GMainLoop *loop;
/* Create a main loop that runs the default GLib main context */
loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
return 0;
}
int output_mpt_init(void)
{
ENTER();
SET_CONN();
if (conn == NULL) {
printf("%s", "Out of memory");
return -1;
}
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
printf("%s", mpd_connection_get_error_message(conn));
mpd_connection_free(conn);
conn = NULL;
return -1;
}
FREE_CONN();
LEAVE();
return 0;
}
output_mpd.h
#ifndef _OUTPUT_MPD_H
#define _OUTPUT_GSTREAMER_H
int output_mpd_init(void);
void output_set_uri(const char *uri);
int output_play(void);
int output_stop(void);
int output_pause(void);
int output_loop(void);
#endif /* _OUTPUT_MPD_H */
需要注意的是qq網路音樂是把mov的字尾改動為字尾為mqcc的形式(qq本地傳入的音樂),而mpd又是以後綴作為解碼的依據,這個可以修改檔案src/decoder/plugins/FfmpegDecoderPlugin.cxx,找到:ffmpeg_suffixes,增加:"wve", "mqcc",後重新編譯即可支援。