uint8_t num_header_slices;
};
+enum fec_client_state {
+ FEC_STATE_NONE = 0, /**< not initialized and not enabled */
+ FEC_STATE_DISABLED, /**< temporarily disabled */
+ FEC_STATE_READY_TO_RUN /**< initialized and enabled */
+};
+
/**
* Describes one connected FEC client.
*/
struct fec_client {
- /** If negative, this client is temporarily disabled. */
- int error;
- /** UDP or DCCP. */
- struct sender *sender;
+ /** Current state of the client */
+ enum fec_client_state state;
+ /** The connected sender client (transport layer). */
+ struct sender_client *sc;
/** Parameters requested by the client. */
struct fec_client_parms *fcp;
/** Used by the core FEC code. */
int num_extra_slices;
/** Contains the FEC-encoded data. */
unsigned char *enc_buf;
- /** Pointer obtained from sender when the client is added. */
- void *private_data;
};
/**
static int compute_next_fec_slice(struct fec_client *fc, struct vss_task *vsst)
{
- assert(fc->error >= 0);
if (fc->first_stream_chunk < 0 || fc->current_slice_num
== fc->fcp->slices_per_group + fc->num_extra_slices) {
int ret = setup_next_fec_group(fc, vsst);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
PARA_ERROR_LOG("FEC client temporarily disabled\n");
- fc->error = ret;
- return fc->error;
+ fc->state = FEC_STATE_DISABLED;
+ return ret;
}
}
write_fec_header(fc, vsst);
/**
* Add one entry to the list of active fec clients.
*
- * \param fcp Describes the fec parameters to be used for this client.
- * \param result An opaque pointer that must be used by remove the client later.
+ * \param sc Generic sender_client data of the transport layer.
+ * \param fcp FEC parameters as supplied by the transport layer.
*
- * \return Standard.
+ * \return Newly allocated fec_client struct.
*/
-int vss_add_fec_client(struct sender *sender, void *private_data,
- struct fec_client **result)
+struct fec_client *vss_add_fec_client(struct sender_client *sc,
+ struct fec_client_parms *fcp)
{
struct fec_client *fc = para_calloc(sizeof(*fc));
- fc->private_data = private_data;
- fc->sender = sender;
+ fc->sc = sc;
+ fc->fcp = fcp;
para_list_add(&fc->node, &fec_client_list);
- *result = fc;
- return 1;
+ return fc;
}
/**
{
struct fec_client *fc;
- assert(vss_playing());
list_for_each_entry(fc, &fec_client_list, node) {
struct timeval diff;
- if (fc->error < 0)
+ if (fc->state != FEC_STATE_READY_TO_RUN)
continue;
if (next_slice_is_due(fc, &diff)) {
timeout->tv_sec = 0;
list_for_each_entry(fc, &fec_client_list, node) {
struct timeval group_duration;
- if (fc->error < 0)
+ if (fc->state != FEC_STATE_READY_TO_RUN)
continue;
tv_scale(fc->group.num_chunks, chunk_tv, &group_duration);
if (tv_diff(&timeout, &group_duration, NULL) < 0)
mmd->events++;
}
-/**
- * Get the list of all supported audio formats.
- *
- * \return Aa space separated list of all supported audio formats
- * It is not allocated at runtime, i.e. there is no need to free
- * the returned string in the caller.
- */
-const char *supported_audio_formats(void)
-{
- return SUPPORTED_AUDIO_FORMATS;
-}
-
static int need_to_request_new_audio_file(struct vss_task *vsst)
{
struct timeval diff;
senders[i].shutdown_clients();
list_for_each_entry_safe(fc, tmp, &fec_client_list, node) {
fc->first_stream_chunk = -1;
- fc->error = 0;
- fc->fcp = NULL;
+ fc->state = FEC_STATE_NONE;
}
mmd->stream_start.tv_sec = 0;
mmd->stream_start.tv_usec = 0;
mmd->new_vss_status_flags = VSS_NEXT;
}
-static int open_fec_client(struct fec_client *fc)
+static int initialize_fec_client(struct fec_client *fc)
{
int ret;
- struct fec_client_parms *fcp;
+ struct fec_client_parms *fcp = fc->fcp;
- ret = fc->sender->open(fc->private_data, &fc->fcp);
- if (ret < 0) {
- fc->fcp = NULL;
- return ret;
+ if (fcp->init_fec) {
+ /*
+ * Set the maximum slice size to the Maximum Packet Size if the
+ * transport protocol allows to determine this value. The user
+ * can specify a slice size up to this value.
+ */
+ ret = fcp->init_fec(fc->sc);
+ if (ret < 0)
+ return ret;
+ if (!fcp->max_slice_bytes || fcp->max_slice_bytes > ret)
+ fcp->max_slice_bytes = ret;
}
- fcp = fc->fcp;
if (fcp->max_slice_bytes < FEC_HEADER_SIZE + fcp->data_slices_per_group)
return -ERRNO_TO_PARA_ERROR(EINVAL);
ret = fec_new(fcp->data_slices_per_group, fcp->slices_per_group,
fc->num_extra_slices = 0;
fc->extra_src_buf = para_calloc(fc->fcp->max_slice_bytes);
fc->next_header_time.tv_sec = 0;
+ fc->state = FEC_STATE_READY_TO_RUN;
return 1;
err:
fec_free(fc->parms);
&due, 1) < 0)
return;
list_for_each_entry_safe(fc, tmp_fc, &fec_client_list, node) {
- if (fc->error < 0)
+ switch (fc->state) {
+ case FEC_STATE_DISABLED:
continue;
- if (!fc->fcp) {
- ret = open_fec_client(fc);
+ case FEC_STATE_NONE:
+ ret = initialize_fec_client(fc);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
continue;
}
+ /* fall through */
+ case FEC_STATE_READY_TO_RUN:
+ break;
}
if (!next_slice_is_due(fc, NULL)) {
fec_active = 1;
continue;
PARA_DEBUG_LOG("sending %d:%d (%u bytes)\n", fc->group.num,
fc->current_slice_num, fc->fcp->max_slice_bytes);
- fc->sender->send_fec((char *)fc->enc_buf,
- fc->fcp->max_slice_bytes,
- fc->private_data);
+ fc->fcp->send_fec(fc->sc, (char *)fc->enc_buf,
+ fc->fcp->max_slice_bytes);
fc->current_slice_num++;
fec_active = 1;
}
int num = mmd->sender_cmd_data.cmd_num,
sender_num = mmd->sender_cmd_data.sender_num;
- if (senders[sender_num].client_cmds[num])
- senders[sender_num].client_cmds[num](&mmd->sender_cmd_data);
+ if (senders[sender_num].client_cmds[num]) {
+ ret = senders[sender_num].client_cmds[num]
+ (&mmd->sender_cmd_data);
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ }
mmd->sender_cmd_data.cmd_num = -1;
}
if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)