shout: added shout_buffer
The Ogg encoder is slightly less optimal under this configuration. It used to send shout data directly out of its ogg_page structures. Now, in the interest of encapsulation, it copies the data from its ogg_page structures into a buffer provided by the shout audio output plugin (see audioOutput_shout_ogg.c, line 77.) I suspect the performance impact is negligible. [mk: this patch and its description was separated from Eric's patch "Refactor and cleanup of shout Ogg and MP3 audio outputs"]
This commit is contained in:

committed by
Max Kellermann

parent
fcac05a207
commit
f482f83eb8
@@ -25,8 +25,16 @@
|
|||||||
#define CONN_ATTEMPT_INTERVAL 60
|
#define CONN_ATTEMPT_INTERVAL 60
|
||||||
#define DEFAULT_CONN_TIMEOUT 2
|
#define DEFAULT_CONN_TIMEOUT 2
|
||||||
|
|
||||||
|
#define SHOUT_BUF_SIZE 8192
|
||||||
|
|
||||||
static int shout_init_count;
|
static int shout_init_count;
|
||||||
|
|
||||||
|
static void clear_shout_buffer(struct shout_data * sd)
|
||||||
|
{
|
||||||
|
sd->buf.len = 0;
|
||||||
|
sd->buf.data[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
static struct shout_data *new_shout_data(void)
|
static struct shout_data *new_shout_data(void)
|
||||||
{
|
{
|
||||||
struct shout_data *ret = xmalloc(sizeof(*ret));
|
struct shout_data *ret = xmalloc(sizeof(*ret));
|
||||||
@@ -41,6 +49,10 @@ static struct shout_data *new_shout_data(void)
|
|||||||
ret->conn_attempts = 0;
|
ret->conn_attempts = 0;
|
||||||
ret->last_attempt = 0;
|
ret->last_attempt = 0;
|
||||||
ret->timer = NULL;
|
ret->timer = NULL;
|
||||||
|
ret->buf.data = xmalloc(sizeof(unsigned char) *
|
||||||
|
SHOUT_BUF_SIZE);
|
||||||
|
ret->buf.max_len = SHOUT_BUF_SIZE;
|
||||||
|
clear_shout_buffer(ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -51,6 +63,8 @@ static void free_shout_data(struct shout_data *sd)
|
|||||||
shout_free(sd->shout_conn);
|
shout_free(sd->shout_conn);
|
||||||
if (sd->tag)
|
if (sd->tag)
|
||||||
tag_free(sd->tag);
|
tag_free(sd->tag);
|
||||||
|
if (sd->buf.data)
|
||||||
|
free(sd->buf.data);
|
||||||
if (sd->timer)
|
if (sd->timer)
|
||||||
timer_free(sd->timer);
|
timer_free(sd->timer);
|
||||||
|
|
||||||
@@ -248,25 +262,25 @@ static int handle_shout_error(struct shout_data *sd, int err)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_page(struct shout_data *sd)
|
static int write_page(struct shout_data *sd)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
shout_sync(sd->shout_conn);
|
shout_sync(sd->shout_conn);
|
||||||
err = shout_send(sd->shout_conn, sd->og.header, sd->og.header_len);
|
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
|
||||||
if (handle_shout_error(sd, err) < 0)
|
|
||||||
return -1;
|
|
||||||
err = shout_send(sd->shout_conn, sd->og.body, sd->og.body_len);
|
|
||||||
if (handle_shout_error(sd, err) < 0)
|
if (handle_shout_error(sd, err) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
clear_shout_buffer(sd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_shout_conn(struct shout_data *sd)
|
static void close_shout_conn(struct shout_data *sd)
|
||||||
{
|
{
|
||||||
if (sd->opened)
|
if (sd->opened) {
|
||||||
shout_ogg_encoder_clear_encoder(sd);
|
if (shout_ogg_encoder_clear_encoder(sd))
|
||||||
|
write_page(sd);
|
||||||
|
}
|
||||||
|
|
||||||
if (shout_get_connected(sd->shout_conn) != SHOUTERR_UNCONNECTED &&
|
if (shout_get_connected(sd->shout_conn) != SHOUTERR_UNCONNECTED &&
|
||||||
shout_close(sd->shout_conn) != SHOUTERR_SUCCESS) {
|
shout_close(sd->shout_conn) != SHOUTERR_SUCCESS) {
|
||||||
@@ -383,22 +397,12 @@ static int open_shout_conn(struct audio_output *audio_output)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sd->buf.len)
|
||||||
|
write_page(sd);
|
||||||
|
|
||||||
sd->shout_error = 0;
|
sd->shout_error = 0;
|
||||||
|
|
||||||
copy_tag_to_vorbis_comment(sd);
|
|
||||||
|
|
||||||
send_ogg_vorbis_header(sd);
|
|
||||||
|
|
||||||
sd->opened = 1;
|
sd->opened = 1;
|
||||||
sd->tag_to_send = 0;
|
sd->tag_to_send = 1;
|
||||||
|
|
||||||
while (ogg_stream_flush(&(sd->os), &(sd->og))) {
|
|
||||||
if (write_page(sd) < 0) {
|
|
||||||
close_shout_conn(sd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sd->conn_attempts = 0;
|
sd->conn_attempts = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -461,12 +465,10 @@ static int my_shout_play(struct audio_output *audio_output,
|
|||||||
|
|
||||||
shout_ogg_encoder_encode(sd, chunk, size);
|
shout_ogg_encoder_encode(sd, chunk, size);
|
||||||
|
|
||||||
while (ogg_stream_pageout(&(sd->os), &(sd->og)) != 0) {
|
|
||||||
if (write_page(sd) < 0) {
|
if (write_page(sd) < 0) {
|
||||||
my_shout_close_device(audio_output);
|
my_shout_close_device(audio_output);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,12 @@
|
|||||||
#include <shout/shout.h>
|
#include <shout/shout.h>
|
||||||
#include <vorbis/vorbisenc.h>
|
#include <vorbis/vorbisenc.h>
|
||||||
|
|
||||||
|
typedef struct _shout_buffer {
|
||||||
|
unsigned char *data;
|
||||||
|
size_t len;
|
||||||
|
size_t max_len;
|
||||||
|
} shout_buffer;
|
||||||
|
|
||||||
struct shout_data {
|
struct shout_data {
|
||||||
shout_t *shout_conn;
|
shout_t *shout_conn;
|
||||||
int shout_error;
|
int shout_error;
|
||||||
@@ -61,15 +67,15 @@ struct shout_data {
|
|||||||
|
|
||||||
/* the configured audio format */
|
/* the configured audio format */
|
||||||
struct audio_format audio_format;
|
struct audio_format audio_format;
|
||||||
};
|
|
||||||
|
|
||||||
int write_page(struct shout_data *sd);
|
shout_buffer buf;
|
||||||
|
};
|
||||||
|
|
||||||
void copy_tag_to_vorbis_comment(struct shout_data *sd);
|
void copy_tag_to_vorbis_comment(struct shout_data *sd);
|
||||||
|
|
||||||
void send_ogg_vorbis_header(struct shout_data *sd);
|
int send_ogg_vorbis_header(struct shout_data *sd);
|
||||||
|
|
||||||
void shout_ogg_encoder_clear_encoder(struct shout_data *sd);
|
int shout_ogg_encoder_clear_encoder(struct shout_data *sd);
|
||||||
|
|
||||||
int init_encoder(struct shout_data *sd);
|
int init_encoder(struct shout_data *sd);
|
||||||
|
|
||||||
|
@@ -54,7 +54,42 @@ void copy_tag_to_vorbis_comment(struct shout_data *sd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_ogg_vorbis_header(struct shout_data *sd)
|
static int copy_ogg_buffer_to_shout_buffer(ogg_page *og,
|
||||||
|
shout_buffer *buf)
|
||||||
|
{
|
||||||
|
if (buf->max_len - buf->len >= (size_t)og->header_len) {
|
||||||
|
memcpy(buf->data + buf->len,
|
||||||
|
og->header, og->header_len);
|
||||||
|
buf->len += og->header_len;
|
||||||
|
} else {
|
||||||
|
ERROR("%s: not enough buffer space!\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf->max_len - buf->len >= (size_t)og->body_len) {
|
||||||
|
memcpy(buf->data + buf->len,
|
||||||
|
og->body, og->body_len);
|
||||||
|
buf->len += og->body_len;
|
||||||
|
} else {
|
||||||
|
ERROR("%s: not enough buffer space!\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flush_ogg_buffer(struct shout_data *sd)
|
||||||
|
{
|
||||||
|
shout_buffer *buf = &sd->buf;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (ogg_stream_flush(&sd->os, &sd->og))
|
||||||
|
ret = copy_ogg_buffer_to_shout_buffer(&sd->og, buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_ogg_vorbis_header(struct shout_data *sd)
|
||||||
{
|
{
|
||||||
vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
|
vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
|
||||||
&(sd->header_comments),
|
&(sd->header_comments),
|
||||||
@@ -63,6 +98,8 @@ void send_ogg_vorbis_header(struct shout_data *sd)
|
|||||||
ogg_stream_packetin(&(sd->os), &(sd->header_main));
|
ogg_stream_packetin(&(sd->os), &(sd->header_main));
|
||||||
ogg_stream_packetin(&(sd->os), &(sd->header_comments));
|
ogg_stream_packetin(&(sd->os), &(sd->header_comments));
|
||||||
ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
|
ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
|
||||||
|
|
||||||
|
return flush_ogg_buffer(sd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_encoder(struct shout_data *sd)
|
static void finish_encoder(struct shout_data *sd)
|
||||||
@@ -78,27 +115,24 @@ static void finish_encoder(struct shout_data *sd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flush_encoder(struct shout_data *sd)
|
int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
|
||||||
{
|
{
|
||||||
return (ogg_stream_pageout(&sd->os, &sd->og) > 0);
|
int ret;
|
||||||
}
|
|
||||||
|
|
||||||
void shout_ogg_encoder_clear_encoder(struct shout_data *sd)
|
|
||||||
{
|
|
||||||
finish_encoder(sd);
|
finish_encoder(sd);
|
||||||
while (1 == flush_encoder(sd)) {
|
if ((ret = ogg_stream_pageout(&sd->os, &sd->og)))
|
||||||
if (!sd->shout_error)
|
copy_ogg_buffer_to_shout_buffer(&sd->og, &sd->buf);
|
||||||
write_page(sd);
|
|
||||||
}
|
|
||||||
|
|
||||||
vorbis_comment_clear(&sd->vc);
|
vorbis_comment_clear(&sd->vc);
|
||||||
ogg_stream_clear(&sd->os);
|
ogg_stream_clear(&sd->os);
|
||||||
vorbis_block_clear(&sd->vb);
|
vorbis_block_clear(&sd->vb);
|
||||||
vorbis_dsp_clear(&sd->vd);
|
vorbis_dsp_clear(&sd->vd);
|
||||||
vorbis_info_clear(&sd->vi);
|
vorbis_info_clear(&sd->vi);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int init_encoder(struct shout_data *sd)
|
static int reinit_encoder(struct shout_data *sd)
|
||||||
{
|
{
|
||||||
vorbis_info_init(&(sd->vi));
|
vorbis_info_init(&(sd->vi));
|
||||||
|
|
||||||
@@ -130,10 +164,23 @@ int init_encoder(struct shout_data *sd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int init_encoder(struct shout_data *sd)
|
||||||
|
{
|
||||||
|
if (reinit_encoder(sd))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (send_ogg_vorbis_header(sd)) {
|
||||||
|
ERROR("error sending ogg vorbis header for shout\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int shout_ogg_encoder_send_metadata(struct shout_data * sd)
|
int shout_ogg_encoder_send_metadata(struct shout_data * sd)
|
||||||
{
|
{
|
||||||
shout_ogg_encoder_clear_encoder(sd);
|
shout_ogg_encoder_clear_encoder(sd);
|
||||||
if (init_encoder(sd) < 0)
|
if (reinit_encoder(sd))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
copy_tag_to_vorbis_comment(sd);
|
copy_tag_to_vorbis_comment(sd);
|
||||||
@@ -146,10 +193,7 @@ int shout_ogg_encoder_send_metadata(struct shout_data * sd)
|
|||||||
ogg_stream_packetin(&(sd->os), &(sd->header_comments));
|
ogg_stream_packetin(&(sd->os), &(sd->header_comments));
|
||||||
ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
|
ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
|
||||||
|
|
||||||
while (ogg_stream_flush(&sd->os, &sd->og)) {
|
flush_ogg_buffer(sd);
|
||||||
if (write_page(sd) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -157,6 +201,7 @@ int shout_ogg_encoder_send_metadata(struct shout_data * sd)
|
|||||||
void shout_ogg_encoder_encode(struct shout_data *sd,
|
void shout_ogg_encoder_encode(struct shout_data *sd,
|
||||||
const char *chunk, size_t size)
|
const char *chunk, size_t size)
|
||||||
{
|
{
|
||||||
|
shout_buffer *buf = &sd->buf;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int j;
|
int j;
|
||||||
float **vorbbuf;
|
float **vorbbuf;
|
||||||
@@ -185,6 +230,9 @@ void shout_ogg_encoder_encode(struct shout_data *sd,
|
|||||||
ogg_stream_packetin(&(sd->os), &(sd->op));
|
ogg_stream_packetin(&(sd->os), &(sd->op));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ogg_stream_pageout(&sd->os, &sd->og))
|
||||||
|
copy_ogg_buffer_to_shout_buffer(&sd->og, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user