1、
channel-playback.c源文件中
SPICE_MSG_PLAYBACK_DATA的
set_handler处理回调函数(解析数据->解码数据->发送信号)
/* coroutine context */
static void playback_handle_data(SpiceChannel *channel, SpiceMsgIn *in)
{
SpicePlaybackChannelPrivate *c = SPICE_PLAYBACK_CHANNEL(channel)->priv;
SpiceMsgPlaybackPacket *packet = spice_msg_in_parsed(in);
#ifdef DEBUG
CHANNEL_DEBUG(channel, "%s: time %u data %p size %d", __FUNCTION__,
packet->time, packet->data, packet->data_size);
#endif
//判断时间是否过时
if (spice_mmtime_diff(c->last_time, packet->time) > 0)
g_warn_if_reached();
c->last_time = packet->time;
uint8_t *data = packet->data;
int n = packet->data_size;
uint8_t pcm[SND_CODEC_MAX_FRAME_SIZE * 2 * 2];
//是否解码数据
if (c->mode != SPICE_AUDIO_DATA_MODE_RAW) {
n = sizeof(pcm);
data = pcm;
if (snd_codec_decode(c->codec, packet->data, packet->data_size,pcm, &n) != SND_CODEC_OK) {
g_warning("snd_codec_decode() error");
return;
}
}
g_coroutine_signal_emit(channel, signals[SPICE_PLAYBACK_DATA], 0, data, n);
if ((c->frame_count++ % 100) == 0) {
g_coroutine_signal_emit(channel, signals[SPICE_PLAYBACK_GET_DELAY], 0);
}
}
2、
spice-gstaudio.c源
文件中的
connect_channel
函数
连接了信号
static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
{
SpiceGstaudio *gstaudio = SPICE_GSTAUDIO(audio);
SpiceGstaudioPrivate *p = gstaudio->priv;
if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
g_return_val_if_fail(p->pchannel == NULL, FALSE);
p->pchannel = channel;
g_object_weak_ref(G_OBJECT(p->pchannel), channel_weak_notified, audio);
spice_g_signal_connect_object(channel, "playback-start", G_CALLBACK(playback_start), gstaudio, 0);
spice_g_signal_connect_object(channel, "playback-data", G_CALLBACK(playback_data), gstaudio, 0);
spice_g_signal_connect_object(channel, "playback-stop", G_CALLBACK(playback_stop), gstaudio, G_CONNECT_SWAPPED);
spice_g_signal_connect_object(channel, "notify::volume", G_CALLBACK(playback_volume_changed), gstaudio, 0);
spice_g_signal_connect_object(channel, "notify::mute", G_CALLBACK(playback_mute_changed), gstaudio, 0);
return TRUE;
}
...
}
3、s
pice-gstaudio.c源
文件中的
playback_data函数将PCM的音频数据放到pipeline的appsrc中,开始播放
static void playback_data(SpicePlaybackChannel *channel,
gpointer *audio, gint size,
gpointer data)
{
SpiceGstaudio *gstaudio = data;
SpiceGstaudioPrivate *p = gstaudio->priv;
GstBuffer *buf;
g_return_if_fail(p != NULL);
if(p->playback.queue){
uint32_t queue_current_buffers = 0;
g_object_get(p->playback.queue,"current-level-buffers",&queue_current_buffers,NULL);
printf("1---------------queue_current_buffers:%u------\n",queue_current_buffers);
}
audio = g_memdup(audio, size); /* TODO: try to avoid memory copy */
buf = gst_buffer_new_wrapped(audio, size);
gst_app_src_push_buffer(GST_APP_SRC(p->playback.src), buf);
}
4、sp
ice-gstaudio.c源
文件中的
playback_start
函数创建pipeline
static void playback_start(SpicePlaybackChannel *channel, gint format, gint channels,
gint frequency, gpointer data)
{
SpiceGstaudio *gstaudio = data;
SpiceGstaudioPrivate *p = gstaudio->priv;
g_return_if_fail(p != NULL);
g_return_if_fail(format == SPICE_AUDIO_FMT_S16);
if (p->playback.pipe &&
(p->playback.rate != frequency ||
p->playback.channels != channels)) {
playback_stop(gstaudio);
g_clear_pointer(&p->playback.pipe, gst_object_unref);
}
if (!p->playback.pipe) {
GError *error = NULL;
gchar *audio_caps =
g_strdup_printf("audio/x-raw,format=\"S16LE\",channels=%d,rate=%d,"
"layout=interleaved", channels, frequency);
gchar *pipeline = g_strdup (g_getenv("SPICE_GST_AUDIOSINK"));
if (pipeline == NULL){
//pipeline = g_strdup_printf("appsrc is-live=1 do-timestamp=0 format=time caps=\"%s\" name=\"appsrc\" ! queue max-size-buffers=2 name=\"queue\" ! "
//"audioconvert ! audioresample ! autoaudiosink name=\"audiosink\"", audio_caps);
pipeline = g_strdup_printf("appsrc is-live=1 do-timestamp=0 format=time caps=\"%s\" name=\"appsrc\" ! "
"audioconvert ! audioresample ! autoaudiosink name=\"audiosink\"", audio_caps);
}
SPICE_DEBUG("audio pipeline: %s", pipeline);
p->playback.pipe = gst_parse_launch(pipeline, &error);
if (error != NULL) {
g_warning("Failed to create pipeline: %s", error->message);
goto cleanup;
}
p->playback.src = gst_bin_get_by_name(GST_BIN(p->playback.pipe), "appsrc");
p->playback.sink = gst_bin_get_by_name(GST_BIN(p->playback.pipe), "audiosink");
// p->playback.queue = gst_bin_get_by_name(GST_BIN(p->playback.pipe), "queue");
p->playback.queue = NULL;
p->playback.rate = frequency;
p->playback.channels = channels;
cleanup:
if (error != NULL)
g_clear_pointer(&p->playback.pipe, gst_object_unref);
g_clear_error(&error);
g_free(audio_caps);
g_free(pipeline);
}
if (p->playback.pipe)
gst_element_set_state(p->playback.pipe, GST_STATE_PLAYING);
if (!p->playback.fake && p->mmtime_id == 0) {
update_mmtime_timeout_cb(gstaudio);
p->mmtime_id = g_timeout_add_seconds(1, update_mmtime_timeout_cb, gstaudio);
}
}