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",后重新编译即可支持。