/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2010 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#include
#include "spice-client.h"
#include "spice-common.h"
#include "spice-channel-priv.h"
#include "spice-marshal.h"
#define SPICE_PLAYBACK_CHANNEL_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_PLAYBACK_CHANNEL, spice_playback_channel))
struct spice_playback_channel {
int mode;
CELTMode *celt_mode;
CELTDecoder *celt_decoder;
};
G_DEFINE_TYPE(SpicePlaybackChannel, spice_playback_channel, SPICE_TYPE_CHANNEL)
enum {
SPICE_PLAYBACK_START,
SPICE_PLAYBACK_DATA,
SPICE_PLAYBACK_STOP,
SPICE_PLAYBACK_LAST_SIGNAL,
};
static guint signals[SPICE_PLAYBACK_LAST_SIGNAL];
static void spice_playback_handle_msg(SpiceChannel *channel, spice_msg_in *msg);
/* ------------------------------------------------------------------ */
static void spice_playback_channel_init(SpicePlaybackChannel *channel)
{
spice_playback_channel *c;
c = channel->priv = SPICE_PLAYBACK_CHANNEL_GET_PRIVATE(channel);
memset(c, 0, sizeof(*c));
spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_PLAYBACK_CAP_CELT_0_5_1);
}
static void spice_playback_channel_finalize(GObject *obj)
{
spice_playback_channel *c = SPICE_PLAYBACK_CHANNEL(obj)->priv;
if (c->celt_decoder) {
celt051_decoder_destroy(c->celt_decoder);
c->celt_decoder = NULL;
}
if (c->celt_mode) {
celt051_mode_destroy(c->celt_mode);
c->celt_mode = NULL;
}
if (G_OBJECT_CLASS(spice_playback_channel_parent_class)->finalize)
G_OBJECT_CLASS(spice_playback_channel_parent_class)->finalize(obj);
}
static void spice_playback_channel_class_init(SpicePlaybackChannelClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
gobject_class->finalize = spice_playback_channel_finalize;
channel_class->handle_msg = spice_playback_handle_msg;
signals[SPICE_PLAYBACK_START] =
g_signal_new("playback-start",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(SpicePlaybackChannelClass, playback_start),
NULL, NULL,
g_cclosure_user_marshal_VOID__INT_INT_INT,
G_TYPE_NONE,
3,
G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
signals[SPICE_PLAYBACK_DATA] =
g_signal_new("playback-data",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(SpicePlaybackChannelClass, playback_data),
NULL, NULL,
g_cclosure_user_marshal_VOID__POINTER_INT,
G_TYPE_NONE,
2,
G_TYPE_POINTER, G_TYPE_INT);
signals[SPICE_PLAYBACK_STOP] =
g_signal_new("playback-stop",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(SpicePlaybackChannelClass, playback_stop),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
g_type_class_add_private(klass, sizeof(spice_playback_channel));
}
/* ------------------------------------------------------------------ */
static void playback_handle_data(SpiceChannel *channel, spice_msg_in *in)
{
spice_playback_channel *c = SPICE_PLAYBACK_CHANNEL(channel)->priv;
SpiceMsgPlaybackPacket *packet = spice_msg_in_parsed(in);
SPICE_DEBUG("%s: time %d data %p size %d", __FUNCTION__,
packet->time, packet->data, packet->data_size);
switch (c->mode) {
case SPICE_AUDIO_DATA_MODE_RAW:
g_signal_emit(channel, signals[SPICE_PLAYBACK_DATA], 0,
packet->data, packet->data_size);
break;
case SPICE_AUDIO_DATA_MODE_CELT_0_5_1: {
celt_int16_t pcm[256 * 2];
g_return_if_fail(c->celt_decoder != NULL);
if (celt051_decode(c->celt_decoder, packet->data,
packet->data_size, pcm) != CELT_OK) {
g_warning("celt_decode() error");
return;
}
g_signal_emit(channel, signals[SPICE_PLAYBACK_DATA], 0,
(uint8_t *)pcm, sizeof(pcm));
break;
}
default:
g_warning("%s: unhandled mode", __FUNCTION__);
break;
}
}
static void playback_handle_mode(SpiceChannel *channel, spice_msg_in *in)
{
spice_playback_channel *c = SPICE_PLAYBACK_CHANNEL(channel)->priv;
SpiceMsgPlaybackMode *mode = spice_msg_in_parsed(in);
SPICE_DEBUG("%s: time %d mode %d data %p size %d", __FUNCTION__,
mode->time, mode->mode, mode->data, mode->data_size);
c->mode = mode->mode;
switch (c->mode) {
case SPICE_AUDIO_DATA_MODE_RAW:
case SPICE_AUDIO_DATA_MODE_CELT_0_5_1:
break;
default:
g_warning("%s: unhandled mode", __FUNCTION__);
break;
}
}
static void playback_handle_start(SpiceChannel *channel, spice_msg_in *in)
{
spice_playback_channel *c = SPICE_PLAYBACK_CHANNEL(channel)->priv;
SpiceMsgPlaybackStart *start = spice_msg_in_parsed(in);
int celt_mode_err;
SPICE_DEBUG("%s: fmt %d channels %d freq %d time %d", __FUNCTION__,
start->format, start->channels, start->frequency, start->time);
switch (c->mode) {
case SPICE_AUDIO_DATA_MODE_RAW:
g_signal_emit(channel, signals[SPICE_PLAYBACK_START], 0,
start->format, start->channels, start->frequency);
break;
case SPICE_AUDIO_DATA_MODE_CELT_0_5_1: {
/* TODO: only support one setting now */
int frame_size = 256;
if (!c->celt_mode)
c->celt_mode = celt051_mode_create(start->frequency, start->channels,
frame_size, &celt_mode_err);
if (!c->celt_mode)
g_warning("create celt mode failed %d", celt_mode_err);
if (!c->celt_decoder)
c->celt_decoder = celt051_decoder_create(c->celt_mode);
if (!c->celt_decoder)
g_warning("create celt decoder failed");
g_signal_emit(channel, signals[SPICE_PLAYBACK_START], 0,
start->format, start->channels, start->frequency);
break;
}
default:
g_warning("%s: unhandled mode", __FUNCTION__);
break;
}
}
static void playback_handle_stop(SpiceChannel *channel, spice_msg_in *in)
{
g_signal_emit(channel, signals[SPICE_PLAYBACK_STOP], 0);
}
static spice_msg_handler playback_handlers[] = {
[ SPICE_MSG_SET_ACK ] = spice_channel_handle_set_ack,
[ SPICE_MSG_PING ] = spice_channel_handle_ping,
[ SPICE_MSG_NOTIFY ] = spice_channel_handle_notify,
[ SPICE_MSG_DISCONNECTING ] = spice_channel_handle_disconnect,
[ SPICE_MSG_WAIT_FOR_CHANNELS ] = spice_channel_handle_wait_for_channels,
[ SPICE_MSG_MIGRATE ] = spice_channel_handle_migrate,
[ SPICE_MSG_PLAYBACK_DATA ] = playback_handle_data,
[ SPICE_MSG_PLAYBACK_MODE ] = playback_handle_mode,
[ SPICE_MSG_PLAYBACK_START ] = playback_handle_start,
[ SPICE_MSG_PLAYBACK_STOP ] = playback_handle_stop,
};
static void spice_playback_handle_msg(SpiceChannel *channel, spice_msg_in *msg)
{
int type = spice_msg_in_type(msg);
g_return_if_fail(type < SPICE_N_ELEMENTS(playback_handlers));
g_return_if_fail(playback_handlers[type] != NULL);
playback_handlers[type](channel, msg);
}