/* rfc 3711 section 4.3.1 and 4.3.3 * key: 128 bits * x: 112 bits * n <= 256 * out->len := n / 8 */ static void prf_n(str *out, const char *key, const char *x) { char iv[16]; char o[32]; char in[32]; str in_s; assert(sizeof(o) >= out->len); ZERO(iv); memcpy(iv, x, 14); /* iv[14] = iv[15] = 0; := x << 16 */ ZERO(in); /* outputs the key stream */ str_init_len(&in_s, in, out->len > 16 ? 32 : 16); aes_ctr_128_no_ctx(o, &in_s, key, iv); memcpy(out->s, o, out->len); }
static int parse_media(char *start, char *end, struct sdp_media *output) { char *ep; str s, *sp; EXTRACT_TOKEN(media_type); EXTRACT_TOKEN(port); EXTRACT_TOKEN(transport); str_init_len(&output->formats, start, end - start); output->port_num = strtol(output->port.s, &ep, 10); if (ep == output->port.s) return -1; if (output->port_num < 0 || output->port_num > 0xffff) return -1; if (*ep == '/') { output->port_count = atoi(ep + 1); if (output->port_count <= 0) return -1; if (output->port_count > 10) /* unsupported */ return -1; } else output->port_count = 1; /* to split the "formats" list into tokens, we abuse some vars */ start = output->formats.s; end = start + output->formats.len; while (!extract_token(&start, end, &s)) { sp = g_slice_alloc(sizeof(*sp)); *sp = s; g_queue_push_tail(&output->format_list, sp); } return 0; }
void ice_foundation(struct interface_address *ifa) { random_ice_string(ifa->foundation_buf, sizeof(ifa->foundation_buf)); str_init_len(&ifa->ice_foundation, ifa->foundation_buf, sizeof(ifa->foundation_buf)); }
void ice_foundation(str *s) { str_init_len(s, malloc(ICE_FOUNDATION_LENGTH), ICE_FOUNDATION_LENGTH); random_ice_string(s->s, ICE_FOUNDATION_LENGTH); }
// call->master_lock held in W int media_player_play_db(struct media_player *mp, long long id) { const char *err; AUTO_CLEANUP_BUF(query); err = "missing configuration"; if (!rtpe_config.mysql_host || !rtpe_config.mysql_query) goto err; int len = asprintf(&query, rtpe_config.mysql_query, (unsigned long long) id); err = "query print error"; if (len <= 0) goto err; for (int retries = 0; retries < 5; retries++) { if (!mysql_conn || retries != 0) { err = "failed to connect to database"; if (__connect_db()) goto err; } int ret = mysql_real_query(mysql_conn, query, len); if (ret == 0) goto success; ret = mysql_errno(mysql_conn); if (ret == CR_SERVER_GONE_ERROR || ret == CR_SERVER_LOST) continue; ilog(LOG_ERR, "Failed to query from database: %s", mysql_error(mysql_conn)); } err = "exceeded max number of database retries"; goto err; success:; MYSQL_RES *res = mysql_store_result(mysql_conn); err = "failed to get result from database"; if (!res) goto err; MYSQL_ROW row = mysql_fetch_row(res); unsigned long *lengths = mysql_fetch_lengths(res); err = "empty result from database"; if (!row || !lengths || !row[0] || !lengths[0]) { mysql_free_result(res); goto err; } str blob; str_init_len(&blob, row[0], lengths[0]); int ret = media_player_play_blob(mp, &blob); mysql_free_result(res); return ret; err: if (query) ilog(LOG_ERR, "Failed to start media playback from database (used query '%s'): %s", query, err); else ilog(LOG_ERR, "Failed to start media playback from database: %s", err); return -1; }
static int __ensure_codec_handler(struct media_player *mp, AVStream *avs) { if (mp->handler) return 0; // synthesise rtp payload type struct rtp_payload_type src_pt = { .payload_type = -1 }; // src_pt.codec_def = codec_find_by_av(avs->codec->codec_id); `codec` is deprecated src_pt.codec_def = codec_find_by_av(avs->CODECPAR->codec_id); if (!src_pt.codec_def) { ilog(LOG_ERR, "Attempting to play media from an unsupported file format/codec"); return -1; } src_pt.encoding = src_pt.codec_def->rtpname_str; src_pt.channels = avs->CODECPAR->channels; src_pt.clock_rate = avs->CODECPAR->sample_rate; codec_init_payload_type(&src_pt, mp->media); // find suitable output payload type struct rtp_payload_type *dst_pt; for (GList *l = mp->media->codecs_prefs_send.head; l; l = l->next) { dst_pt = l->data; if (dst_pt->codec_def && !dst_pt->codec_def->supplemental) goto found; } dst_pt = NULL; found: if (!dst_pt) { ilog(LOG_ERR, "No supported output codec found in SDP"); return -1; } ilog(LOG_DEBUG, "Output codec for media playback is " STR_FORMAT, STR_FMT(&dst_pt->encoding_with_params)); // if we played anything before, scale our sync TS according to the time // that has passed if (mp->sync_ts_tv.tv_sec) { long long ts_diff_us = timeval_diff(&rtpe_now, &mp->sync_ts_tv); mp->sync_ts += ts_diff_us * dst_pt->clock_rate / 1000000 / dst_pt->codec_def->clockrate_mult; } mp->handler = codec_handler_make_playback(&src_pt, dst_pt, mp->sync_ts); if (!mp->handler) return -1; mp->duration = avs->duration * 1000 * avs->time_base.num / avs->time_base.den; return 0; } // appropriate lock must be held static void media_player_read_packet(struct media_player *mp) { if (!mp->fmtctx) return; int ret = av_read_frame(mp->fmtctx, &mp->pkt); if (ret < 0) { if (ret == AVERROR_EOF) { ilog(LOG_DEBUG, "EOF reading from media stream"); return; } ilog(LOG_ERR, "Error while reading from media stream"); return; } if (!mp->fmtctx->streams) { ilog(LOG_ERR, "No AVStream present in format context"); goto out; } AVStream *avs = mp->fmtctx->streams[0]; if (!avs) { ilog(LOG_ERR, "No AVStream present in format context"); goto out; } if (__ensure_codec_handler(mp, avs)) goto out; // scale pts and duration according to sample rate long long duration_scaled = mp->pkt.duration * avs->CODECPAR->sample_rate * avs->time_base.num / avs->time_base.den; unsigned long long pts_scaled = mp->pkt.pts * avs->CODECPAR->sample_rate * avs->time_base.num / avs->time_base.den; long long us_dur = mp->pkt.duration * 1000000LL * avs->time_base.num / avs->time_base.den; ilog(LOG_DEBUG, "read media packet: pts %llu duration %lli (scaled %llu/%lli, %lli us), " "sample rate %i, time_base %i/%i", (unsigned long long) mp->pkt.pts, (long long) mp->pkt.duration, pts_scaled, duration_scaled, us_dur, avs->CODECPAR->sample_rate, avs->time_base.num, avs->time_base.den); // synthesise fake RTP header and media_packet context struct rtp_header rtp = { .timestamp = pts_scaled, // taken verbatim by handler_func_playback w/o byte swap .seq_num = htons(mp->seq), }; struct media_packet packet = { .tv = rtpe_now, .call = mp->call, .media = mp->media, .rtp = &rtp, .ssrc_out = mp->ssrc_out, }; str_init_len(&packet.raw, (char *) mp->pkt.data, mp->pkt.size); packet.payload = packet.raw; mp->handler->func(mp->handler, &packet); // as this is timing sensitive and we may have spent some time decoding, // update our global "now" timestamp gettimeofday(&rtpe_now, NULL); // keep track of RTP timestamps and real clock. look at the last packet we received // and update our sync TS. if (packet.packets_out.head) { struct codec_packet *p = packet.packets_out.head->data; if (p->rtp) { mp->sync_ts = ntohl(p->rtp->timestamp); mp->sync_ts_tv = p->to_send; } } media_packet_encrypt(mp->crypt_handler->out->rtp_crypt, mp->sink, &packet); mutex_lock(&mp->sink->out_lock); if (media_socket_dequeue(&packet, mp->sink)) ilog(LOG_ERR, "Error sending playback media to RTP sink"); mutex_unlock(&mp->sink->out_lock); timeval_add_usec(&mp->next_run, us_dur); timerthread_obj_schedule_abs(&mp->tt_obj, &mp->next_run); out: av_packet_unref(&mp->pkt); } // call->master_lock held in W static int media_player_play_init(struct media_player *mp) { media_player_shutdown(mp); // find call media suitable for playback struct call_media *media; for (GList *l = mp->ml->medias.head; l; l = l->next) { media = l->data; if (media->type_id != MT_AUDIO) continue; if (!MEDIA_ISSET(media, SEND)) continue; if (media->streams.length == 0) continue; goto found; } media = NULL; found: if (!media) { ilog(LOG_ERR, "No suitable SDP section for media playback"); return -1; } mp->media = media; mp->sink = media->streams.head->data; mp->crypt_handler = determine_handler(&transport_protocols[PROTO_RTP_AVP], media, 1); return 0; } // call->master_lock held in W static void media_player_play_start(struct media_player *mp) { // needed to have usable duration for some formats. ignore errors. avformat_find_stream_info(mp->fmtctx, NULL); mp->next_run = rtpe_now; // give ourselves a bit of a head start with decoding timeval_add_usec(&mp->next_run, -50000); media_player_read_packet(mp); } #endif // call->master_lock held in W int media_player_play_file(struct media_player *mp, const str *file) { #ifdef WITH_TRANSCODING if (media_player_play_init(mp)) return -1; char file_s[PATH_MAX]; snprintf(file_s, sizeof(file_s), STR_FORMAT, STR_FMT(file)); int ret = avformat_open_input(&mp->fmtctx, file_s, NULL, NULL); if (ret < 0) { ilog(LOG_ERR, "Failed to open media file for playback: %s", av_error(ret)); return -1; } media_player_play_start(mp); return 0; #else return -1; #endif } #ifdef WITH_TRANSCODING static int __mp_avio_read_wrap(void *opaque, uint8_t *buf, int buf_size) { struct media_player *mp = opaque; if (buf_size < 0) return AVERROR(EINVAL); if (buf_size == 0) return 0; if (!mp->read_pos.len) return AVERROR_EOF; int len = buf_size; if (len > mp->read_pos.len) len = mp->read_pos.len; memcpy(buf, mp->read_pos.s, len); str_shift(&mp->read_pos, len); return len; } static int __mp_avio_read(void *opaque, uint8_t *buf, int buf_size) { ilog(LOG_DEBUG, "__mp_avio_read(%i)", buf_size); int ret = __mp_avio_read_wrap(opaque, buf, buf_size); ilog(LOG_DEBUG, "__mp_avio_read(%i) = %i", buf_size, ret); return ret; }