/* * cmyth_recorder_cancel_next_recording() * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' cancels its next scheduled recording. * This is used as response to ASK_RECORDING when the user does not want * to allow the recorder to be taken for a pending recording. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_cancel_next_recording(cmyth_recorder_t rec, int cancel) { int err; int ret = -1; char msg[256]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]CANCEL_NEXT_RECORDING[]:[]%"PRIu32 ,rec->rec_id, cancel == 1); if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err = cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
static int cmyth_conn_set_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting, const char* value) { char msg[1024]; int err = 0; if(conn->conn_version < 17) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support SET_SETTING\n", __FUNCTION__); return -1; } if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -2; } snprintf(msg, sizeof(msg), "SET_SETTING %s %s %s", hostname, setting, value); if ((err = cmyth_send_message(conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); return -3; } if (cmyth_rcv_okay(conn) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); return -4; } return 1; }
/* * cmyth_recorder_change_channel() * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' change channels in one of the * followin * * CHANNEL_DIRECTION_UP - Go up one channel in the listing * * CHANNEL_DIRECTION_DOWN - Go down one channel in the listing * * CHANNEL_DIRECTION_FAVORITE - Go to the next favorite channel * * CHANNEL_DIRECTION_SAME - Stay on the same (current) channel * * Note that the recorder must not be actively recording when this * request is made or bad things may happen to the server (i.e. it may * segfault). * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_change_channel(cmyth_recorder_t rec, cmyth_channeldir_t direction) { int err; int ret = -1; char msg[256]; cmyth_livetv_chain_t newchain = NULL; cmyth_livetv_chain_t oldchain = NULL; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -EINVAL; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]CHANGE_CHANNEL[]:[]%d", rec->rec_id, direction); if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } if ((err = cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto out; } if (rec->rec_ring) rec->rec_ring->file_pos = 0; else { oldchain = rec->rec_livetv_chain; newchain = cmyth_livetv_chain_create(oldchain->chainid); newchain->livetv_buflen = oldchain->livetv_buflen; newchain->livetv_tcp_rcvbuf = oldchain->livetv_tcp_rcvbuf; newchain->prog_update_callback = oldchain->prog_update_callback; newchain->chain_switch_on_create = 1; rec->rec_livetv_chain = newchain; } ret = 0; out: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); if (oldchain) ref_release(oldchain); return ret; }
/* * cmyth_recorder_change_channel() * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' change channels in one of the * followin * * CHANNEL_DIRECTION_UP - Go up one channel in the listing * * CHANNEL_DIRECTION_DOWN - Go down one channel in the listing * * CHANNEL_DIRECTION_FAVORITE - Go to the next favorite channel * * CHANNEL_DIRECTION_SAME - Stay on the same (current) channel * * Note that the recorder must not be actively recording when this * request is made or bad things may happen to the server (i.e. it may * segfault). * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_change_channel(cmyth_recorder_t rec, cmyth_channeldir_t direction) { int err; int ret = -1; char msg[256]; cmyth_livetv_chain_t newchain = 0; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]CHANGE_CHANNEL[]:[]%d", rec->rec_id, direction); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } if(rec->rec_ring) rec->rec_ring->file_pos = 0; else { newchain = cmyth_livetv_chain_create(rec->rec_livetv_chain->chainid); newchain->progs = rec->rec_livetv_chain->progs; ref_release(rec->rec_livetv_file); rec->rec_livetv_file = NULL; ref_release(rec->rec_livetv_chain); rec->rec_livetv_chain = newchain; } ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_file_destroy() * * Scope: PRIVATE (static) * * Description * * Tear down and release storage associated with a file connection. * This should only be called by ref_release(). All others * should call ref_release() to release a file connection. * * Return Value: * * None. */ static void cmyth_file_destroy(cmyth_file_t file) { int err; char msg[256]; cmyth_dbg(CMYTH_DBG_DEBUG, "%s {\n", __FUNCTION__); if (!file) { cmyth_dbg(CMYTH_DBG_DEBUG, "%s }!\n", __FUNCTION__); return; } if (file->file_control) { pthread_mutex_lock(&mutex); /* * Try to shut down the file transfer. Can't do much * if it fails other than log it. */ snprintf(msg, sizeof(msg), "QUERY_FILETRANSFER %"PRIu32"[]:[]DONE", file->file_id); if ((err = cmyth_send_message(file->file_control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(file->file_control)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } fail: pthread_mutex_unlock(&mutex); ref_release(file->file_control); } if (file->closed_callback) { (file->closed_callback)(file); } if (file->file_data) { ref_release(file->file_data); } cmyth_dbg(CMYTH_DBG_DEBUG, "%s }\n", __FUNCTION__); }
int cmyth_recorder_done_ringbuf(cmyth_recorder_t rec) { int err; int ret = -1; char msg[256]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } if (!rec->rec_connected) { return -EINVAL; } if(rec->rec_conn->conn_version >= 26) return 0; pthread_mutex_lock(&rec->rec_conn->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]DONE_RINGBUF", rec->rec_id); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } ret = 0; fail: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); return ret; }
/* * cmyth_recorder_change_channel(cmyth_recorder_t rec, * cmyth_channeldir_t direction) * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' change channels in one of the * followin * * CHANNEL_DIRECTION_UP - Go up one channel in the listing * * CHANNEL_DIRECTION_DOWN - Go down one channel in the listing * * CHANNEL_DIRECTION_FAVORITE - Go to the next favorite channel * * CHANNEL_DIRECTION_SAME - Stay on the same (current) channel * * Note that the recorder must not be actively recording when this * request is made or bad things may happen to the server (i.e. it may * segfault). * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_change_channel(cmyth_recorder_t rec, cmyth_channeldir_t direction) { int err; int ret = -1; char msg[256]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]CHANGE_CHANNEL[]:[]%d", rec->rec_id, direction); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn, "ok")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } if(rec->rec_ring) rec->rec_ring->file_pos = 0; else rec->rec_livetv_file->file_pos = 0; ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
int cmyth_recorder_set_live_recording(cmyth_recorder_t rec, uint8_t recording) { int err; int ret = -1; char msg[256]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); if (rec->rec_conn->conn_version >= 26) { snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SET_LIVE_RECORDING[]:[]%u", rec->rec_id, recording); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } ret = recording; } else { ret = -EPERM; } fail: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); return ret; }
/* * cmyth_recorder_check_channel(cmyth_recorder_t rec, char *channame) * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' check the validity of the channel * specified by 'channame'. * * Return Value: * * Check the validity of a channel name. * Success: 1 - valid channel, 0 - invalid channel * * Failure: -(ERRNO) */ int cmyth_recorder_check_channel(cmyth_recorder_t rec, char *channame) { int err; int ret = -1; char msg[256]; if (!rec || !channame) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: invalid args " "rec = %p, channame = %p\n", __FUNCTION__, rec, channame); return -EINVAL; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]CHECK_CHANNEL[]:[]%s", rec->rec_id, channame); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn, "ok")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_recorder_pause(cmyth_recorder_t rec) * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' pause in the middle of the current * recording. This will prevent the recorder from transmitting any * data until the recorder is unpaused. At this moment, it is not * clear to me what will cause an unpause. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_pause(cmyth_recorder_t rec) { int ret = -EINVAL; char Buffer[255]; if (rec == NULL) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: Invalid args rec = %p\n", __FUNCTION__, rec); return -EINVAL; } if (!rec->rec_connected) { return -EINVAL; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); sprintf(Buffer, "QUERY_RECORDER %ld[]:[]PAUSE", (long) rec->rec_id); if ((ret=cmyth_send_message(rec->rec_conn, Buffer)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, Buffer); goto err; } if ((ret=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto err; } ret = 0; err: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); return ret; }
int cmyth_recorder_stop_livetv(cmyth_recorder_t rec) { int err; int ret = -1; char msg[256]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]STOP_LIVETV", rec->rec_id); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, int64_t bookmark) { char *buf; unsigned int len = CMYTH_TIMESTAMP_LEN + CMYTH_INT64_LEN * 2 + 18 + 2; int ret; char start_ts_dt[CMYTH_TIMESTAMP_LEN + 1]; cmyth_datetime_to_string(start_ts_dt, prog->proginfo_rec_start_ts); buf = alloca(len); if (!buf) { return -ENOMEM; } if (conn->conn_version >= 66) { /* * Since protocol 66 mythbackend expects a single 64 bit integer rather than two 32 bit * hi and lo integers. Nevertheless the backend (at least up to 0.25) checks * the existence of the 4th parameter, so adding a 0. */ sprintf(buf, "SET_BOOKMARK %"PRIu32" %s %"PRId64" 0", prog->proginfo_chanId, start_ts_dt, bookmark); } else { sprintf(buf, "SET_BOOKMARK %"PRIu32" %s %"PRId32" %"PRId32, prog->proginfo_chanId, start_ts_dt, (int32_t)(bookmark >> 32), (int32_t)(bookmark & 0xffffffff)); } pthread_mutex_lock(&conn->conn_mutex);; if ((ret = cmyth_send_message(conn,buf)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, ret); goto out; } if ((ret = cmyth_rcv_okay(conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); } out: pthread_mutex_unlock(&conn->conn_mutex); return ret; }
/* * cmyth_recorder_pause(cmyth_recorder_t rec) * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' pause in the middle of the current * recording. This will prevent the recorder from transmitting any * data until the recorder is unpaused. At this moment, it is not * clear to me what will cause an unpause. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_pause(cmyth_recorder_t rec) { int ret = -EINVAL; char Buffer[255]; if (rec == NULL) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: Invalid args rec = %p\n", __FUNCTION__, rec); return -EINVAL; } pthread_mutex_lock(&mutex); PRINTF("** SSDEBUG: trying to pause recorder:%p:%p\n",rec,rec->rec_conn); sprintf(Buffer, "QUERY_RECORDER %ld[]:[]PAUSE", (long) rec->rec_id); if ((ret=cmyth_send_message(rec->rec_conn, Buffer)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, Buffer); goto err; } if ((ret=cmyth_rcv_okay(rec->rec_conn, "ok")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto err; } ret = 0; err: PRINTF("** SSDEBUG: recorder paused:\n"); pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_file_set_timeout() * * Scope: PUBLIC * * Description * * Set whether reading from a file should have a fast or slow timeout. * Slow timeouts are used for live TV ring buffers, and is three seconds. * Fast timeouts are used for static files, and are 0.12 seconds. * * Return Value: * * Sucess: 0 * * Failure: an int containing -errno */ int cmyth_file_set_timeout(cmyth_file_t file, int32_t fast) { int ret; char msg[256]; if (!file) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } pthread_mutex_lock (&mutex); snprintf(msg, sizeof(msg), "QUERY_FILETRANSFER %"PRIu32"[]:[]SET_TIMEOUT[]:[]%"PRId32, file->file_id, fast); if ((ret = cmyth_send_message(file->file_control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, ret); goto out; } if ((ret = cmyth_rcv_okay(file->file_control)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto out; } ret = 0; out: pthread_mutex_unlock (&mutex); return ret; }
static cmyth_conn_t cmyth_conn_connect(char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf, int event) { cmyth_conn_t conn; char announcement[256]; unsigned long tmp_ver; int attempt = 0; top: conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return NULL; } /* * Find out what the Myth Protocol Version is for this connection. * Loop around until we get agreement from the server. */ if (attempt == 0) tmp_ver = conn->conn_version; conn->conn_version = tmp_ver; if (tmp_ver >= 62) { myth_protomap_t *map = protomap; while (map->version != 0 && map->version != tmp_ver) map++; if (map->version == 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } sprintf(announcement, "MYTH_PROTO_VERSION %ld %04X", conn->conn_version, map->token); } else { sprintf(announcement, "MYTH_PROTO_VERSION %ld", conn->conn_version); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_version(conn, &tmp_ver) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_version() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_ERROR, "%s: asked for version %ld, got version %ld\n", __FUNCTION__, conn->conn_version, tmp_ver); if (conn->conn_version != tmp_ver) { if (attempt == 1) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } attempt = 1; ref_release(conn); goto top; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: agreed on Version %ld protocol\n", __FUNCTION__, conn->conn_version); sprintf(announcement, "ANN Playback %s %d", my_hostname, event); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } return conn; shut: ref_release(conn); return NULL; }
/* Sergio: Added to support the new livetv protocol */ int cmyth_recorder_spawn_chain_livetv(cmyth_recorder_t rec, char* channame) { int err; int ret = -1; char msg[256]; char myhostname[32]; char datestr[32]; time_t t; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); /* Get our own IP address */ gethostname(myhostname, 32); /* Get the current date and time to create a unique id */ t = time(NULL); strftime(datestr, 32, "%Y-%m-%dT%H:%M:%S", localtime(&t)); /* Now build the SPAWN_LIVETV message */ if(rec->rec_conn->conn_version >= 34 && channame) snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]SPAWN_LIVETV[]:[]live-%s-%s[]:[]%d[]:[]%s", rec->rec_id, myhostname, datestr, 0, channame); else snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]SPAWN_LIVETV[]:[]live-%s-%s[]:[]%d", rec->rec_id, myhostname, datestr, 0); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } /* Create an empty livetv chain with the ID used in the spawn command */ snprintf(msg, sizeof(msg), "live-%s-%s", myhostname, datestr); rec->rec_livetv_chain = cmyth_livetv_chain_create(msg); ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_recorder_set_channel(cmyth_recorder_t rec, * char *channame) * * Scope: PUBLIC * * Description * * Request that the recorder 'rec' change channels to the channel * named 'channame'. * * Note that the recorder must not be actively recording when this * request is made or bad things may happen to the server (i.e. it may * segfault). * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_recorder_set_channel(cmyth_recorder_t rec, char *channame) { int err; int ret = -1; char msg[256]; cmyth_file_t file; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } if (!rec->rec_connected) { return -EINVAL; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); cmyth_chain_lock(rec->rec_chain); snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SET_CHANNEL[]:[]%s", rec->rec_id, channame); if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } pthread_mutex_unlock(&rec->rec_conn->conn_mutex); cmyth_chain_add_wait(rec->rec_chain); cmyth_chain_unlock(rec->rec_chain); if ((file=cmyth_livetv_current_file(rec)) == NULL) { goto fail_nolock; } cmyth_file_seek(file, 0, SEEK_SET); ref_release(file); ret = 0; fail: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); fail_nolock: return ret; }
static cmyth_conn_t cmyth_conn_connect(char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf, int event) { cmyth_conn_t conn; char announcement[256]; unsigned long tmp_ver; int attempt = 0; top: conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return NULL; } /* * Find out what the Myth Protocol Version is for this connection. * Loop around until we get agreement from the server. */ if (attempt == 0) tmp_ver = conn->conn_version; conn->conn_version = tmp_ver; /* * Myth 0.23.1 (Myth 0.23 + fixes) introduced an out of sequence protocol version number (23056) * due to the next protocol version number having already been bumped in trunk. * * http://www.mythtv.org/wiki/Myth_Protocol */ if (tmp_ver >= 62 && tmp_ver != 23056) { // Treat protocol version number 23056 the same as protocol 56 myth_protomap_t *map = protomap; while (map->version != 0 && map->version != tmp_ver) map++; if (map->version == 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } sprintf(announcement, "MYTH_PROTO_VERSION %ld %s", conn->conn_version, map->token); } else { sprintf(announcement, "MYTH_PROTO_VERSION %ld", conn->conn_version); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_version(conn, &tmp_ver) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_version() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_ERROR, "%s: asked for version %ld, got version %ld\n", __FUNCTION__, conn->conn_version, tmp_ver); if (conn->conn_version != tmp_ver) { if (attempt == 1) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } attempt = 1; ref_release(conn); goto top; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: agreed on Version %ld protocol\n", __FUNCTION__, conn->conn_version); sprintf(announcement, "ANN Playback %s %d", my_hostname, event); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } /* * All of the downstream code in libcmyth assumes a monotonically increasing version number. * This was not the case for Myth 0.23.1 (0.23 + fixes) where protocol version number 23056 * was used since 57 had already been used in trunk. * * Convert from protocol version number 23056 to version number 56 so subsequent code within * libcmyth uses the same logic for the 23056 protocol as would be used for protocol version 56. */ if (conn->conn_version == 23056) { conn->conn_version = 56; } return conn; shut: ref_release(conn); return NULL; }
/* Sergio: Added to support the new livetv protocol */ int cmyth_recorder_spawn_chain_livetv(cmyth_recorder_t rec) { int err; int ret = -1; char msg[256], id[128]; char myhostname[32]; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } if (!rec->rec_connected) { return -EINVAL; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); /* Get our own IP address */ gethostname(myhostname, 32); /* Create an empty livetv chain with a unique ID */ snprintf(id, sizeof(id), "live-%s-%d-%p", myhostname, getpid(), rec); /* Now build the SPAWN_LIVETV message */ if (rec->rec_conn->conn_version >=50) { snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SPAWN_LIVETV[]:[]%s[]:[]%d[]:[]", rec->rec_id, id, 0); } else { snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SPAWN_LIVETV[]:[]%s[]:[]%d", rec->rec_id, id, 0); } if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } rec->rec_chain = cmyth_chain_create(rec, id); ret = 0; fail: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); return ret; }
/* * cmyth_conn_connect_ring(char *server, unsigned short port, unsigned buflen * cmyth_recorder_t rec) * * Scope: PUBLIC * * Description: * * Create a new ring buffer connection for use transferring live-tv * using the MythTV protocol. Return a pointer to the newly created * ring buffer connection. The ring buffer connection is returned * held, and may be released using ref_release(). * * Return Value: * * Success: Non-NULL cmyth_conn_t (this is a pointer type) * * Failure: NULL cmyth_conn_t */ int cmyth_conn_connect_ring(cmyth_recorder_t rec, unsigned buflen, int tcp_rcvbuf) { cmyth_conn_t conn; char *announcement; int ann_size = sizeof("ANN RingBuffer "); char *server; unsigned short port; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: rec is NULL\n", __FUNCTION__); return -1; } server = rec->rec_server; port = rec->rec_port; cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting ringbuffer\n", __FUNCTION__); conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting ringbuffer, conn = %p\n", __FUNCTION__, conn); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return -1; } ann_size += CMYTH_LONG_LEN + strlen(my_hostname); announcement = malloc(ann_size); if (!announcement) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc(%d) failed for announcement\n", __FUNCTION__, ann_size); goto shut; } sprintf(announcement, "ANN RingBuffer %s %d", my_hostname, rec->rec_id); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); free(announcement); goto shut; } free(announcement); if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } rec->rec_ring->conn_data = conn; return 0; shut: ref_release(conn); return -1; }