/** * Video decoder thread */ static void * vd_thread(void *aux) { video_decoder_t *vd = aux; media_pipe_t *mp = vd->vd_mp; media_queue_t *mq = &mp->mp_video; media_buf_t *mb; media_codec_t *mc; int run = 1; int reqsize = -1; int reinit = 0; int size; vd->vd_frame = avcodec_alloc_frame(); hts_mutex_lock(&mp->mp_mutex); while(run) { if((mb = TAILQ_FIRST(&mq->mq_q)) == NULL) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } if(mb->mb_data_type == MB_VIDEO && vd->vd_hold && vd->vd_skip == 0 && mb->mb_skip == 0) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q, mb, mb_link); mq->mq_freeze_tail = 1; mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); hts_mutex_unlock(&mp->mp_mutex); mc = mb->mb_cw; switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: vd->vd_hold = 1; break; case MB_CTRL_PLAY: vd->vd_hold = 0; break; case MB_FLUSH: vd_init_timings(vd); vd->vd_do_flush = 1; vd->vd_interlaced = 0; video_overlay_flush(vd, 1); break; case MB_VIDEO: if(reinit) { reinit = 0; if(mc->reinit != NULL) mc->reinit(mc); } if(mb->mb_skip == 2) vd->vd_skip = 1; size = mb->mb_size; if(mc->decode) mc->decode(mc, vd, mq, mb, reqsize); else vd_decode_video(vd, mq, mb); update_vbitrate(mp, mq, size, vd); reqsize = -1; break; case MB_REQ_OUTPUT_SIZE: reqsize = mb->mb_data32; break; case MB_REINITIALIZE: reinit = 1; break; #ifdef CONFIG_DVD case MB_DVD_HILITE: case MB_DVD_RESET_SPU: case MB_DVD_CLUT: case MB_DVD_PCI: case MB_DVD_SPU: dvdspu_decoder_dispatch(vd, mb, mp); break; #endif case MB_SUBTITLE: if(vd->vd_ext_subtitles == NULL && mb->mb_stream == mq->mq_stream2) video_overlay_decode(vd, mb); break; case MB_END: break; case MB_BLACKOUT: if(vd->vd_accelerator_blackout) vd->vd_accelerator_blackout(vd->vd_accelerator_opaque); else vd->vd_frame_deliver(NULL, NULL, NULL, vd->vd_opaque); break; case MB_FLUSH_SUBTITLES: video_overlay_flush(vd, 1); break; case MB_EXT_SUBTITLE: if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); // Steal subtitle from the media_buf vd->vd_ext_subtitles = mb->mb_data; mb->mb_data = NULL; video_overlay_flush(vd, 1); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); mq->mq_freeze_tail = 0; media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); // Stop any video accelerator helper threads video_decoder_set_accelerator(vd, NULL, NULL, NULL); if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); /* Free ffmpeg frame */ av_free(vd->vd_frame); return NULL; }
static void * dummy_audio_thread(void *aux) { audio_decoder_t *ad = aux; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int hold = 0; int run = 1; int64_t rt = 0; int64_t base = AV_NOPTS_VALUE; hts_mutex_lock(&mp->mp_mutex); while(run) { if((mb = TAILQ_FIRST(&mq->mq_q)) == NULL) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } if(mb->mb_data_type == MB_AUDIO && hold && mb->mb_skip == 0) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q, mb, mb_link); mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: hold = 1; break; case MB_CTRL_PLAY: hold = 0; base = AV_NOPTS_VALUE; break; case MB_FLUSH: base = AV_NOPTS_VALUE; break; case MB_AUDIO: if(mb->mb_skip || mb->mb_stream != mq->mq_stream) break; if(mb->mb_pts != AV_NOPTS_VALUE) { audio_set_clock(mp, mb->mb_pts, 0, mb->mb_epoch); if(base == AV_NOPTS_VALUE) { base = mb->mb_pts; rt = showtime_get_ts(); } else { int64_t d = mb->mb_pts - base; if(d > 0) { int sleeptime = rt + d - showtime_get_ts(); if(sleeptime > 0) usleep(sleeptime); } } } break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); return NULL; }
static void htsp_channelAddUpdate(htsp_connection_t *hc, htsmsg_t *m, int create) { uint32_t id, next; int chnum; prop_t *p; char txt[200]; const char *title, *icon; htsp_channel_t *ch, *n; if(htsmsg_get_u32(m, "channelId", &id)) return; title = htsmsg_get_str(m, "channelName"); icon = htsmsg_get_str(m, "channelIcon"); chnum = htsmsg_get_s32_or_default(m, "channelNumber", -1); if(chnum == 0) chnum = INT32_MAX; snprintf(txt, sizeof(txt), "%d", id); hts_mutex_lock(&hc->hc_meta_mutex); if(create) { ch = calloc(1, sizeof(htsp_channel_t)); p = ch->ch_root = prop_create_root(txt); ch->ch_metadata = prop_create(p, "metadata"); ch->ch_id = id; snprintf(txt, sizeof(txt), "htsp://%s:%d/channel/%d", hc->hc_hostname, hc->hc_port, id); prop_set_string(prop_create(p, "url"), txt); prop_set_string(prop_create(p, "type"), "tvchannel"); ch->ch_channel_num = chnum; mystrset(&ch->ch_title, title); TAILQ_INSERT_SORTED(&hc->hc_channels, ch, ch_link, channel_compar); n = TAILQ_NEXT(ch, ch_link); if(prop_set_parent_ex(p, hc->hc_channels_nodes, n ? n->ch_root : NULL, NULL)) abort(); } else { int move = 0; ch = htsp_channel_get(hc, id); if(ch == NULL) { TRACE(TRACE_ERROR, "HTSP", "Got update for unknown channel %d", id); hts_mutex_unlock(&hc->hc_meta_mutex); return; } p = ch->ch_root; if(title != NULL) { move = 1; mystrset(&ch->ch_title, title); } if(chnum != -1) { move = 1; ch->ch_channel_num = chnum; } if(move) { TAILQ_REMOVE(&hc->hc_channels, ch, ch_link); TAILQ_INSERT_SORTED(&hc->hc_channels, ch, ch_link, channel_compar); n = TAILQ_NEXT(ch, ch_link); prop_move(p, n ? n->ch_root : NULL); } } hts_mutex_unlock(&hc->hc_meta_mutex); if(icon != NULL) prop_set_string(prop_create(ch->ch_metadata, "icon"), icon); if(title != NULL) prop_set_string(prop_create(ch->ch_metadata, "title"), title); if(chnum != -1) prop_set_int(prop_create(ch->ch_metadata, "channelNumber"), chnum); if(htsmsg_get_u32(m, "eventId", &id)) id = 0; if(htsmsg_get_u32(m, "nextEventId", &next)) next = 0; update_events(hc, ch->ch_metadata, id, next); }
media_pipe_t * mp_create(const char *name, int flags) { media_pipe_t *mp; prop_t *p; mp = calloc(1, sizeof(media_pipe_t)); mp->mp_cancellable = cancellable_create(); mp->mp_vol_ui = 1.0f; mp->mp_satisfied = -1; mp->mp_epoch = 1; mp->mp_mb_pool = pool_create("packet headers", sizeof(media_buf_t), POOL_ZERO_MEM); mp->mp_flags = flags; hts_mutex_lock(&media_mutex); LIST_INSERT_HEAD(&media_pipelines, mp, mp_global_link); num_media_pipelines++; hts_mutex_unlock(&media_mutex); TAILQ_INIT(&mp->mp_eq); atomic_set(&mp->mp_refcount, 1); mp->mp_buffer_limit = 1 * 1024 * 1024; mp->mp_name = name; hts_mutex_init(&mp->mp_mutex); hts_mutex_init(&mp->mp_clock_mutex); hts_mutex_init(&mp->mp_overlay_mutex); TAILQ_INIT(&mp->mp_overlay_queue); TAILQ_INIT(&mp->mp_spu_queue); hts_cond_init(&mp->mp_backpressure, &mp->mp_mutex); mp->mp_prop_root = prop_create(media_prop_sources, NULL); mp->mp_prop_metadata = prop_create(mp->mp_prop_root, "metadata"); mp->mp_prop_primary = prop_create(mp->mp_prop_root, "primary"); mp->mp_prop_io = prop_create(mp->mp_prop_root, "io"); mp->mp_prop_notifications = prop_create(mp->mp_prop_root, "notifications"); mp->mp_prop_url = prop_create(mp->mp_prop_root, "url"); mp->mp_setting_root = prop_create(mp->mp_prop_root, "settings"); //-------------------------------------------------- // Video mp->mp_prop_video = prop_create(mp->mp_prop_root, "video"); mp->mp_setting_video_root = prop_create(mp->mp_prop_video, "settings"); mq_init(&mp->mp_video, mp->mp_prop_video, &mp->mp_mutex, mp); //-------------------------------------------------- // Audio mp->mp_prop_audio = prop_create(mp->mp_prop_root, "audio"); mp->mp_setting_audio_root = prop_create(mp->mp_prop_audio, "settings"); mq_init(&mp->mp_audio, mp->mp_prop_audio, &mp->mp_mutex, mp); mp->mp_prop_audio_track_current = prop_create(mp->mp_prop_audio, "current"); mp->mp_prop_audio_track_current_manual = prop_create(mp->mp_prop_audio, "manual"); mp->mp_prop_audio_tracks = prop_create(mp->mp_prop_metadata, "audiostreams"); prop_linkselected_create(mp->mp_prop_audio_tracks, mp->mp_prop_audio, "active", NULL); prop_set_string(mp->mp_prop_audio_track_current, "audio:off"); mp_track_mgr_init(mp, &mp->mp_audio_track_mgr, mp->mp_prop_audio_tracks, MEDIA_TRACK_MANAGER_AUDIO, mp->mp_prop_audio_track_current, prop_create(mp->mp_prop_audio, "sorted")); //-------------------------------------------------- // Subtitles p = prop_create(mp->mp_prop_root, "subtitle"); mp->mp_setting_subtitle_root = prop_create(p, "settings"); mp->mp_prop_subtitle_track_current = prop_create(p, "current"); mp->mp_prop_subtitle_track_current_manual = prop_create(p, "manual"); mp->mp_prop_subtitle_tracks = prop_create(mp->mp_prop_metadata, "subtitlestreams"); prop_linkselected_create(mp->mp_prop_subtitle_tracks, p, "active", NULL); prop_set_string(mp->mp_prop_subtitle_track_current, "sub:off"); mp_add_track_off(mp->mp_prop_subtitle_tracks, "sub:off"); mp_track_mgr_init(mp, &mp->mp_subtitle_track_mgr, mp->mp_prop_subtitle_tracks, MEDIA_TRACK_MANAGER_SUBTITLES, mp->mp_prop_subtitle_track_current, prop_create(p, "sorted")); //-------------------------------------------------- // Buffer p = prop_create(mp->mp_prop_root, "buffer"); mp->mp_prop_buffer_current = prop_create(p, "current"); prop_set_int(mp->mp_prop_buffer_current, 0); mp->mp_prop_buffer_limit = prop_create(p, "limit"); prop_set_int(mp->mp_prop_buffer_limit, mp->mp_buffer_limit); mp->mp_prop_buffer_delay = prop_create(p, "delay"); // mp->mp_prop_playstatus = prop_create(mp->mp_prop_root, "playstatus"); mp->mp_prop_pausereason = prop_create(mp->mp_prop_root, "pausereason"); mp->mp_prop_currenttime = prop_create(mp->mp_prop_root, "currenttime"); mp->mp_prop_fps = prop_create(mp->mp_prop_root, "fps"); prop_set_float_clipping_range(mp->mp_prop_currenttime, 0, 10e6); mp->mp_prop_avdelta = prop_create(mp->mp_prop_root, "avdelta"); prop_set_float(mp->mp_prop_avdelta, 0); mp->mp_prop_svdelta = prop_create(mp->mp_prop_root, "svdelta"); prop_set_float(mp->mp_prop_svdelta, 0); mp->mp_prop_shuffle = prop_create(mp->mp_prop_root, "shuffle"); prop_set_int(mp->mp_prop_shuffle, 0); mp->mp_prop_repeat = prop_create(mp->mp_prop_root, "repeat"); prop_set_int(mp->mp_prop_repeat, 0); mp->mp_prop_avdiff = prop_create(mp->mp_prop_root, "avdiff"); mp->mp_prop_avdiff_error= prop_create(mp->mp_prop_root, "avdiffError"); mp->mp_prop_canSkipBackward = prop_create(mp->mp_prop_root, "canSkipBackward"); mp->mp_prop_canSkipForward = prop_create(mp->mp_prop_root, "canSkipForward"); mp->mp_prop_canSeek = prop_create(mp->mp_prop_root, "canSeek"); mp->mp_prop_canPause = prop_create(mp->mp_prop_root, "canPause"); mp->mp_prop_canEject = prop_create(mp->mp_prop_root, "canEject"); mp->mp_prop_canShuffle = prop_create(mp->mp_prop_root, "canShuffle"); mp->mp_prop_canRepeat = prop_create(mp->mp_prop_root, "canRepeat"); prop_set_int(prop_create(mp->mp_prop_root, "canStop"), 1); mp->mp_prop_ctrl = prop_create(mp->mp_prop_root, "ctrl"); mp->mp_prop_model = prop_create(mp->mp_prop_root, "model"); mp->mp_sub_currenttime = prop_subscribe(PROP_SUB_NO_INITIAL_UPDATE, PROP_TAG_CALLBACK, mp_seek_by_propchange, mp, PROP_TAG_LOCKMGR, mp_lockmgr, PROP_TAG_MUTEX, mp, PROP_TAG_ROOT, mp->mp_prop_currenttime, NULL); mp->mp_sub_eventsink = prop_subscribe(0, PROP_TAG_NAME("media", "eventSink"), PROP_TAG_CALLBACK_EVENT, media_eventsink, mp, PROP_TAG_LOCKMGR, mp_lockmgr, PROP_TAG_MUTEX, mp, PROP_TAG_NAMED_ROOT, mp->mp_prop_root, "media", NULL); if(media_pipe_init_extra != NULL) media_pipe_init_extra(mp); return mp; }
static void audio_process_audio(audio_decoder_t *ad, media_buf_t *mb) { const audio_class_t *ac = ad->ad_ac; AVFrame *frame = ad->ad_frame; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; int r; int got_frame; if(mb->mb_skip || mb->mb_stream != mq->mq_stream) return; while(mb->mb_size) { if(mb->mb_cw == NULL) { frame->sample_rate = mb->mb_rate; frame->format = AV_SAMPLE_FMT_S16; switch(mb->mb_channels) { case 1: frame->channel_layout = AV_CH_LAYOUT_MONO; frame->nb_samples = mb->mb_size / 2; break; case 2: frame->channel_layout = AV_CH_LAYOUT_STEREO; frame->nb_samples = mb->mb_size / 4; break; default: abort(); } frame->data[0] = mb->mb_data; frame->linesize[0] = 0; r = mb->mb_size; got_frame = 1; } else { media_codec_t *mc = mb->mb_cw; AVCodecContext *ctx = mc->ctx; if(mc->codec_id != ad->ad_in_codec_id) { AVCodec *codec = avcodec_find_decoder(mc->codec_id); TRACE(TRACE_DEBUG, "audio", "Codec changed to %s (0x%x)", codec ? codec->name : "???", mc->codec_id); ad->ad_in_codec_id = mc->codec_id; ad->ad_in_sample_rate = 0; audio_cleanup_spdif_muxer(ad); ad->ad_mode = ac->ac_get_mode != NULL ? ac->ac_get_mode(ad, mc->codec_id, ctx ? ctx->extradata : NULL, ctx ? ctx->extradata_size : 0) : AUDIO_MODE_PCM; if(ad->ad_mode == AUDIO_MODE_SPDIF) { audio_setup_spdif_muxer(ad, codec, mq); } else if(ad->ad_mode == AUDIO_MODE_CODED) { hts_mutex_lock(&mp->mp_mutex); ac->ac_deliver_coded_locked(ad, mb->mb_data, mb->mb_size, mb->mb_pts, mb->mb_epoch); hts_mutex_unlock(&mp->mp_mutex); return; } } if(ad->ad_spdif_muxer != NULL) { mb->mb_pkt.stream_index = 0; ad->ad_pts = mb->mb_pts; ad->ad_epoch = mb->mb_epoch; mb->mb_pts = AV_NOPTS_VALUE; mb->mb_dts = AV_NOPTS_VALUE; av_write_frame(ad->ad_spdif_muxer, &mb->mb_pkt); avio_flush(ad->ad_spdif_muxer->pb); return; } if(ad->ad_mode == AUDIO_MODE_CODED) { ad->ad_pts = mb->mb_pts; ad->ad_epoch = mb->mb_epoch; } if(ctx == NULL) { AVCodec *codec = avcodec_find_decoder(mc->codec_id); assert(codec != NULL); // Checked in libav.c ctx = mc->ctx = avcodec_alloc_context3(codec); if(ad->ad_stereo_downmix) ctx->request_channel_layout = AV_CH_LAYOUT_STEREO; if(avcodec_open2(mc->ctx, codec, NULL) < 0) { av_freep(&mc->ctx); return; } } r = avcodec_decode_audio4(ctx, frame, &got_frame, &mb->mb_pkt); if(r < 0) return; if(frame->sample_rate == 0) { frame->sample_rate = ctx->sample_rate; if(frame->sample_rate == 0 && mb->mb_cw->fmt_ctx) frame->sample_rate = mb->mb_cw->fmt_ctx->sample_rate; if(frame->sample_rate == 0) { if(!ad->ad_sample_rate_fail) { ad->ad_sample_rate_fail = 1; TRACE(TRACE_ERROR, "Audio", "Unable to determine sample rate"); } return; } } if(frame->channel_layout == 0) { frame->channel_layout = av_get_default_channel_layout(ctx->channels); if(frame->channel_layout == 0) { if(!ad->ad_channel_layout_fail) { ad->ad_channel_layout_fail = 1; TRACE(TRACE_ERROR, "Audio", "Unable to map %d channels to channel layout"); } return; } } if(mp->mp_stats) mp_set_mq_meta(mq, ctx->codec, ctx); } if(mb->mb_pts != PTS_UNSET) { int od = 0, id = 0; if(ad->ad_avr != NULL) { od = avresample_available(ad->ad_avr) * 1000000LL / ad->ad_out_sample_rate; id = avresample_get_delay(ad->ad_avr) * 1000000LL / frame->sample_rate; } ad->ad_pts = mb->mb_pts - od - id; ad->ad_epoch = mb->mb_epoch; if(mb->mb_drive_clock) mp_set_current_time(mp, mb->mb_pts - ad->ad_delay, mb->mb_epoch, mb->mb_delta); mb->mb_pts = PTS_UNSET; // No longer valid } mb->mb_data += r; mb->mb_size -= r; if(got_frame) { if(frame->sample_rate != ad->ad_in_sample_rate || frame->format != ad->ad_in_sample_format || frame->channel_layout != ad->ad_in_channel_layout || ad->ad_want_reconfig) { ad->ad_want_reconfig = 0; ad->ad_in_sample_rate = frame->sample_rate; ad->ad_in_sample_format = frame->format; ad->ad_in_channel_layout = frame->channel_layout; ac->ac_reconfig(ad); if(ad->ad_avr == NULL) ad->ad_avr = avresample_alloc_context(); else avresample_close(ad->ad_avr); av_opt_set_int(ad->ad_avr, "in_sample_fmt", ad->ad_in_sample_format, 0); av_opt_set_int(ad->ad_avr, "in_sample_rate", ad->ad_in_sample_rate, 0); av_opt_set_int(ad->ad_avr, "in_channel_layout", ad->ad_in_channel_layout, 0); av_opt_set_int(ad->ad_avr, "out_sample_fmt", ad->ad_out_sample_format, 0); av_opt_set_int(ad->ad_avr, "out_sample_rate", ad->ad_out_sample_rate, 0); av_opt_set_int(ad->ad_avr, "out_channel_layout", ad->ad_out_channel_layout, 0); char buf1[128]; char buf2[128]; av_get_channel_layout_string(buf1, sizeof(buf1), -1, ad->ad_in_channel_layout); av_get_channel_layout_string(buf2, sizeof(buf2), -1, ad->ad_out_channel_layout); TRACE(TRACE_DEBUG, "Audio", "Converting from [%s %dHz %s] to [%s %dHz %s]", buf1, ad->ad_in_sample_rate, av_get_sample_fmt_name(ad->ad_in_sample_format), buf2, ad->ad_out_sample_rate, av_get_sample_fmt_name(ad->ad_out_sample_format)); if(avresample_open(ad->ad_avr)) { TRACE(TRACE_ERROR, "Audio", "Unable to open resampler"); avresample_free(&ad->ad_avr); } if(ac->ac_set_volume != NULL) { prop_set(mp->mp_prop_ctrl, "canAdjustVolume", PROP_SET_INT, 1); ac->ac_set_volume(ad, ad->ad_vol_scale); } } if(ad->ad_avr != NULL) { avresample_convert(ad->ad_avr, NULL, 0, 0, frame->data, frame->linesize[0], frame->nb_samples); } else { int delay = 1000000LL * frame->nb_samples / frame->sample_rate; usleep(delay); } } } }
int keyring_lookup(const char *id, char **username, char **password, char **domain, int *remember_me, const char *source, const char *reason, int flags) { htsmsg_t *m; rstr_t *r; int remember = !!(flags & KEYRING_REMEMBER_ME_SET); hts_mutex_lock(&keyring_mutex); if(flags & KEYRING_QUERY_USER) { htsmsg_t *parent; prop_t *p = prop_create_root(NULL); prop_set_string(prop_create(p, "type"), "auth"); prop_set_string(prop_create(p, "id"), id); prop_set_string(prop_create(p, "source"), source); prop_set_string(prop_create(p, "reason"), reason); prop_set_int(prop_create(p, "disableUsername"), username == NULL); prop_set_int(prop_create(p, "canRemember"), !!(flags & KEYRING_SHOW_REMEMBER_ME)); prop_t *rememberMe = prop_create(p, "rememberMe"); prop_set_int(rememberMe, remember); prop_sub_t *remember_sub = prop_subscribe(0, PROP_TAG_CALLBACK_INT, set_remember, &remember, PROP_TAG_ROOT, rememberMe, NULL); prop_t *user = prop_create(p, "username"); prop_t *pass = prop_create(p, "password"); TRACE(TRACE_INFO, "keyring", "Requesting credentials for %s : %s : %s", id, source, reason); event_t *e = popup_display(p); prop_unsubscribe(remember_sub); if(flags & KEYRING_ONE_SHOT) parent = NULL; else if(remember) parent = persistent_keyring; else parent = temporary_keyring; if(parent != NULL) htsmsg_delete_field(parent, id); if(event_is_action(e, ACTION_OK)) { /* OK */ m = htsmsg_create_map(); if(username != NULL) { r = prop_get_string(user); htsmsg_add_str(m, "username", r ? rstr_get(r) : ""); *username = strdup(r ? rstr_get(r) : ""); rstr_release(r); } r = prop_get_string(pass); htsmsg_add_str(m, "password", r ? rstr_get(r) : ""); *password = strdup(r ? rstr_get(r) : ""); rstr_release(r); if(parent != NULL) { htsmsg_add_msg(parent, id, m); if(parent == persistent_keyring) keyring_store(); } } else { /* CANCEL */ if(parent == persistent_keyring) keyring_store(); } if(remember_me != NULL) *remember_me = remember; prop_destroy(p); if(event_is_action(e, ACTION_CANCEL)) { /* return CANCEL to caller */ hts_mutex_unlock(&keyring_mutex); event_release(e); return -1; } event_release(e); } else { if((m = htsmsg_get_map(temporary_keyring, id)) == NULL && (m = htsmsg_get_map(persistent_keyring, id)) == NULL) { hts_mutex_unlock(&keyring_mutex); return 1; } setstr(username, m, "username"); setstr(password, m, "password"); setstr(domain, m, "domain"); } hts_mutex_unlock(&keyring_mutex); return 0; }
void load_site_news(void) { #if ENABLE_WEBPOPUP struct http_header_list response_headers; buf_t *b; char errbuf[512]; b = fa_load("https://movian.tv/projects/movian/news.json", FA_LOAD_FLAGS(FA_DISABLE_AUTH | FA_COMPRESSION), FA_LOAD_RESPONSE_HEADERS(&response_headers), FA_LOAD_ERRBUF(errbuf, sizeof(errbuf)), NULL); if(b == NULL) { TRACE(TRACE_DEBUG, "News", "Unable to load news -- %s", errbuf); return; } const char *dateheader = http_header_get(&response_headers, "date"); if(dateheader == NULL) { buf_release(b); http_headers_free(&response_headers); return; } dateheader = mystrdupa(dateheader); http_headers_free(&response_headers); htsmsg_t *newsinfo = htsmsg_store_load("sitenews"); time_t no_news_before; if(newsinfo == NULL) newsinfo = htsmsg_create_map(); no_news_before = htsmsg_get_u32_or_default(newsinfo, "nothingbefore", 0); if(no_news_before == 0) { if(http_ctime(&no_news_before, dateheader)) { buf_release(b); htsmsg_release(newsinfo); return; } htsmsg_add_u32(newsinfo, "nothingbefore", no_news_before); htsmsg_store_save(newsinfo, "sitenews"); htsmsg_release(newsinfo); } htsmsg_t *doc = htsmsg_json_deserialize(buf_cstr(b)); buf_release(b); if(doc == NULL) { return; } hts_mutex_lock(&news_mutex); htsmsg_t *news = htsmsg_get_list(doc, "news"); if(news != NULL) { htsmsg_field_t *f; HTSMSG_FOREACH(f, news) { htsmsg_t *entry; if((entry = htsmsg_get_map_by_field(f)) == NULL) continue; const char *title = htsmsg_get_str(entry, "title"); const char *created_on = htsmsg_get_str(entry, "created_on"); int id = htsmsg_get_u32_or_default(entry, "id", 0); if(created_on == NULL || title == NULL || id == 0) continue; time_t t; if(parse_created_on_time(&t, created_on)) continue; if(t < no_news_before) continue; char idstr[64]; snprintf(idstr, sizeof(idstr), "sitenews:%d", id); prop_t *p = add_news_locked(idstr, title, NULL, "Read more", idstr); if(p != NULL) { prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, open_news, p, PROP_TAG_ROOT, prop_create(p, "eventSink"), PROP_TAG_MUTEX, &news_mutex, NULL); } }
static int wii_audio_start(audio_mode_t *am, audio_fifo_t *af) { audio_buf_t *ab; int tbuf = 0, i; uint32_t level; int64_t pts; media_pipe_t *mp; prop_sub_t *s_vol; s_vol = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_FLOAT, set_mastervol, NULL, PROP_TAG_ROOT, prop_mastervol, NULL); LWP_InitQueue(&audio_queue); AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_RegisterDMACallback(switch_buffers); AUDIO_StartDMA(); while(1) { level = IRQ_Disable(); while(tbuf == cur_buffer) LWP_ThreadSleep(audio_queue); tbuf = cur_buffer; IRQ_Restore(level); ab = af_deq(af, 0); if(am != audio_mode_current) { /* We're not the selected audio output anymore, return. */ if(ab != NULL) ab_free(ab); break; } if(ab != NULL) { const int16_t *src = (const int16_t *)ab->ab_data; int16_t *dst = (int16_t *)buffer[!tbuf]; for(i = 0; i < ADMA_BUFFER_SIZE / 2; i++) *dst++ = (*src++ * audio_vol) >> 16; /* PTS is the time of the first frame of this audio packet */ if((pts = ab->ab_pts) != AV_NOPTS_VALUE && ab->ab_mp != NULL) { #if 0 /* Convert the frame delay into micro seconds */ d = (fr * 1000 / aam->aam_sample_rate) * 1000; /* Subtract it from our timestamp, this will yield the PTS for the sample currently played */ pts -= d; /* Offset with user configure delay */ #endif pts += am->am_audio_delay * 1000; mp = ab->ab_mp; hts_mutex_lock(&mp->mp_clock_mutex); mp->mp_audio_clock = pts; mp->mp_audio_clock_realtime = showtime_get_ts(); mp->mp_audio_clock_epoch = ab->ab_epoch; hts_mutex_unlock(&mp->mp_clock_mutex); } ab_free(ab); } else { memset(buffer[!tbuf], 0, ADMA_BUFFER_SIZE); } DCFlushRange(buffer[!tbuf], ADMA_BUFFER_SIZE); }
int keyring_lookup(const char *id, char **username, char **password, char **domain, int query, const char *source, const char *reason, int force_temporary) { htsmsg_t *m; rstr_t *r; hts_mutex_lock(&keyring_mutex); if(query) { htsmsg_t *parent; prop_t *p = prop_create_root(NULL); prop_set_string(prop_create(p, "type"), "auth"); prop_set_string(prop_create(p, "id"), id); prop_set_string(prop_create(p, "source"), source); prop_set_string(prop_create(p, "reason"), reason); int remember = !force_temporary; prop_set_int(prop_create(p, "canRemember"), remember); prop_t *rememberMe = prop_create(p, "rememberMe"); prop_set_int(rememberMe, remember); prop_sub_t *remember_sub = prop_subscribe(0, PROP_TAG_CALLBACK_INT, set_remember, &remember, PROP_TAG_ROOT, rememberMe, NULL); prop_t *user = prop_create(p, "username"); prop_t *pass = prop_create(p, "password"); TRACE(TRACE_INFO, "keyring", "Requesting credentials for %s : %s : %s", id, source, reason); event_t *e = popup_display(p); prop_unsubscribe(remember_sub); if(remember) parent = persistent_keyring; else parent = temporary_keyring; htsmsg_delete_field(parent, id); if(event_is_action(e, ACTION_OK)) { /* OK */ m = htsmsg_create_map(); r = prop_get_string(user); htsmsg_add_str(m, "username", r ? rstr_get(r) : ""); rstr_release(r); r = prop_get_string(pass); htsmsg_add_str(m, "password", r ? rstr_get(r) : ""); rstr_release(r); htsmsg_add_msg(parent, id, m); if(parent == persistent_keyring) keyring_store(); } else { /* CANCEL, store without adding anything */ keyring_store(); } prop_destroy(p); if(event_is_action(e, ACTION_CANCEL)) { /* return CANCEL to caller */ hts_mutex_unlock(&keyring_mutex); event_release(e); return -1; } event_release(e); } if((m = htsmsg_get_map(temporary_keyring, id)) == NULL && (m = htsmsg_get_map(persistent_keyring, id)) == NULL) { hts_mutex_unlock(&keyring_mutex); return 1; } setstr(username, m, "username"); setstr(password, m, "password"); setstr(domain, m, "domain"); hts_mutex_unlock(&keyring_mutex); return 0; }
static void gdk_obtain(void) { hts_mutex_lock(&gdk_mutex); }
int ecmascript_openuri(prop_t *page, const char *url, int sync) { hts_regmatch_t matches[8]; hts_mutex_lock(&route_mutex); es_route_t *er; LIST_FOREACH(er, &routes, er_link) if(!hts_regexec(&er->er_regex, url, 8, matches, 0)) break; if(er == NULL) { hts_mutex_unlock(&route_mutex); return 1; } es_resource_retain(&er->super); es_context_t *ec = er->super.er_ctx; hts_mutex_unlock(&route_mutex); es_context_begin(ec); duk_context *ctx = ec->ec_duk; es_push_root(ctx, er); es_stprop_push(ctx, page); duk_push_boolean(ctx, sync); int array_idx = duk_push_array(ctx); for(int i = 1; i < 8; i++) { if(matches[i].rm_so == -1) break; duk_push_lstring(ctx, url + matches[i].rm_so, matches[i].rm_eo - matches[i].rm_so); duk_put_prop_index(ctx, array_idx, i-1); } int rc = duk_pcall(ctx, 3); if(rc) { if(duk_is_string(ctx, -1)) { nav_open_error(page, duk_to_string(ctx, -1)); } else { duk_get_prop_string(ctx, -1, "message"); nav_open_error(page, duk_to_string(ctx, -1)); duk_pop(ctx); } es_dump_err(ctx); } duk_pop(ctx); es_context_end(ec); es_resource_release(&er->super); return 0; }
static int rpi_codec_create(media_codec_t *mc, const media_codec_params_t *mcp, media_pipe_t *mp) { int fmt; switch(mc->codec_id) { case CODEC_ID_H264: fmt = OMX_VIDEO_CodingAVC; break; case CODEC_ID_MPEG2VIDEO: if(!omx_enable_mpg2) return 1; fmt = OMX_VIDEO_CodingMPEG2; break; #if 0 case CODEC_ID_VC1: case CODEC_ID_WMV3: if(mcp->extradata_size == 0) return 1; mc->decode = vc1_pt_decode; return 0; #endif default: return 1; } rpi_video_codec_t *rvc = calloc(1, sizeof(rpi_video_codec_t)); hts_cond_init(&rvc->rvc_avail_cond, &mp->mp_mutex); omx_component_t *d = omx_component_create("OMX.broadcom.video_decode", &mp->mp_mutex, &rvc->rvc_avail_cond); if(d == NULL) { hts_cond_destroy(&rvc->rvc_avail_cond); free(rvc); return 1; } rvc->rvc_decoder = d; omx_set_state(d, OMX_StateIdle); OMX_VIDEO_PARAM_PORTFORMATTYPE format; OMX_INIT_STRUCTURE(format); format.nPortIndex = 130; format.eCompressionFormat = fmt; omxchk(OMX_SetParameter(d->oc_handle, OMX_IndexParamVideoPortFormat, &format)); OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ec; OMX_INIT_STRUCTURE(ec); ec.bStartWithValidFrame = OMX_FALSE; omxchk(OMX_SetParameter(d->oc_handle, OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ec)); OMX_CONFIG_BOOLEANTYPE bt; OMX_INIT_STRUCTURE(bt); bt.bEnabled = 1; omxchk(OMX_SetConfig(d->oc_handle, OMX_IndexParamBrcmInterpolateMissingTimestamps, &bt)); omx_alloc_buffers(d, 130); omx_set_state(d, OMX_StateExecuting); if(mcp->extradata_size) { hts_mutex_lock(&mp->mp_mutex); OMX_BUFFERHEADERTYPE *buf = omx_get_buffer_locked(rvc->rvc_decoder); hts_mutex_unlock(&mp->mp_mutex); buf->nOffset = 0; buf->nFilledLen = mcp->extradata_size; memcpy(buf->pBuffer, mcp->extradata, buf->nFilledLen); buf->nFlags = OMX_BUFFERFLAG_CODECCONFIG; omxchk(OMX_EmptyThisBuffer(rvc->rvc_decoder->oc_handle, buf)); } mc->opaque = rvc; mc->close = rpi_codec_close; mc->decode = rpi_codec_decode; mc->flush = rpi_codec_flush; return 0; }
static pixmap_t * rpi_pixmap_decode(pixmap_t *pm, const image_meta_t *im, char *errbuf, size_t errlen) { if(pm->pm_type != PIXMAP_JPEG) return NULL; #ifdef TIMING int64_t ts = showtime_get_ts(), ts2; #endif rpi_pixmap_decoder_t *rpd = pixmap_decoder_create(OMX_IMAGE_CodingJPEG); if(rpd == NULL) return NULL; rpd->rpd_im = im; #ifdef NOCOPY #error check rpd->rpd_decoder->oc_stream_corrupt OMX_PARAM_PORTDEFINITIONTYPE portdef; memset(&portdef, 0, sizeof(portdef)); portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = rpd->rpd_decoder->oc_inport; omxchk(OMX_GetParameter(rpd->rpd_decoder->oc_handle, OMX_IndexParamPortDefinition, &portdef)); omx_send_command(rpd->rpd_decoder, OMX_CommandPortEnable, rpd->rpd_decoder->oc_inport, NULL, 0); OMX_BUFFERHEADERTYPE *buf; for(int i = 0; i < portdef.nBufferCountActual; i++) { omxchk(OMX_UseBuffer(rpd->rpd_decoder->oc_handle, &buf, rpd->rpd_decoder->oc_inport, NULL, pm->pm_size, pm->pm_data)); } // Waits for the OMX_CommandPortEnable command omx_wait_command(rpd->rpd_decoder); omx_set_state(rpd->rpd_decoder, OMX_StateExecuting); CHECKPOINT("Initialized"); buf->nOffset = 0; buf->nFilledLen = pm->pm_size; buf->nFlags |= OMX_BUFFERFLAG_EOS; rpd->rpd_decoder->oc_inflight_buffers++; omxchk(OMX_EmptyThisBuffer(rpd->rpd_decoder->oc_handle, buf)); hts_mutex_lock(&rpd->rpd_mtx); while(rpd->rpd_change == 0) hts_cond_wait(&rpd->rpd_cond, &rpd->rpd_mtx); hts_mutex_unlock(&rpd->rpd_mtx); CHECKPOINT("Setup tunnel"); setup_tunnel(rpd); #else const void *data = pm->pm_data; size_t len = pm->pm_size; hts_mutex_lock(&rpd->rpd_mtx); while(len > 0) { OMX_BUFFERHEADERTYPE *buf; if(rpd->rpd_decoder->oc_stream_corrupt) break; if(rpd->rpd_change == 1) { rpd->rpd_change = 2; hts_mutex_unlock(&rpd->rpd_mtx); setup_tunnel(rpd); hts_mutex_lock(&rpd->rpd_mtx); continue; } if(rpd->rpd_decoder->oc_avail == NULL) { hts_cond_wait(&rpd->rpd_cond, &rpd->rpd_mtx); continue; } buf = rpd->rpd_decoder->oc_avail; rpd->rpd_decoder->oc_avail = buf->pAppPrivate; rpd->rpd_decoder->oc_inflight_buffers++; hts_mutex_unlock(&rpd->rpd_mtx); buf->nOffset = 0; buf->nFilledLen = MIN(len, buf->nAllocLen); memcpy(buf->pBuffer, data, buf->nFilledLen); buf->nFlags = 0; if(len <= buf->nAllocLen) buf->nFlags |= OMX_BUFFERFLAG_EOS; data += buf->nFilledLen; len -= buf->nFilledLen; omxchk(OMX_EmptyThisBuffer(rpd->rpd_decoder->oc_handle, buf)); hts_mutex_lock(&rpd->rpd_mtx); } if(rpd->rpd_decoder->oc_stream_corrupt) { hts_mutex_unlock(&rpd->rpd_mtx); goto err; } if(rpd->rpd_change != 2) { while(rpd->rpd_change == 0) hts_cond_wait(&rpd->rpd_cond, &rpd->rpd_mtx); hts_mutex_unlock(&rpd->rpd_mtx); setup_tunnel(rpd); } else { hts_mutex_unlock(&rpd->rpd_mtx); } #endif omx_wait_fill_buffer(rpd->rpd_resizer, rpd->rpd_buf); CHECKPOINT("Got buffer"); err: omx_flush_port(rpd->rpd_decoder, rpd->rpd_decoder->oc_inport); omx_flush_port(rpd->rpd_decoder, rpd->rpd_decoder->oc_outport); omx_flush_port(rpd->rpd_resizer, rpd->rpd_resizer->oc_inport); omx_flush_port(rpd->rpd_resizer, rpd->rpd_resizer->oc_outport); if(rpd->rpd_tunnel != NULL) { omx_tunnel_destroy(rpd->rpd_tunnel); rpd->rpd_tunnel = NULL; } omx_set_state(rpd->rpd_decoder, OMX_StateIdle); omx_set_state(rpd->rpd_resizer, OMX_StateIdle); if(rpd->rpd_buf != NULL) { omxchk(OMX_FreeBuffer(rpd->rpd_resizer->oc_handle, rpd->rpd_resizer->oc_outport, rpd->rpd_buf)); } omx_release_buffers(rpd->rpd_decoder, rpd->rpd_decoder->oc_inport); omx_set_state(rpd->rpd_resizer, OMX_StateLoaded); omx_set_state(rpd->rpd_decoder, OMX_StateLoaded); omx_component_destroy(rpd->rpd_resizer); omx_component_destroy(rpd->rpd_decoder); hts_cond_destroy(&rpd->rpd_cond); hts_mutex_destroy(&rpd->rpd_mtx); pixmap_t *out = rpd->rpd_pm; if(out) { pixmap_release(pm); } else { snprintf(errbuf, errlen, "Load error"); } free(rpd); CHECKPOINT("All done"); return out; }
/** * Video decoder thread */ static void * vd_thread(void *aux) { video_decoder_t *vd = aux; media_pipe_t *mp = vd->vd_mp; media_queue_t *mq = &mp->mp_video; media_buf_t *mb; media_buf_t *cur = NULL; media_codec_t *mc, *mc_current = NULL; int run = 1; int reqsize = -1; int size; int reinit = 0; const media_buf_meta_t *mbm = NULL; vd->vd_frame = av_frame_alloc(); hts_mutex_lock(&mp->mp_mutex); while(run) { if(mbm != vd->vd_reorder_current) { mbm = vd->vd_reorder_current; hts_mutex_unlock(&mp->mp_mutex); vd->vd_estimated_duration = mbm->mbm_duration; video_decoder_set_current_time(vd, mbm->mbm_pts, mbm->mbm_epoch, mbm->mbm_delta); hts_mutex_lock(&mp->mp_mutex); continue; } media_buf_t *ctrl = TAILQ_FIRST(&mq->mq_q_ctrl); media_buf_t *data = TAILQ_FIRST(&mq->mq_q_data); media_buf_t *aux = TAILQ_FIRST(&mq->mq_q_aux); if(ctrl != NULL) { TAILQ_REMOVE(&mq->mq_q_ctrl, ctrl, mb_link); mb = ctrl; } else if(aux != NULL && aux->mb_pts < vd->vd_subpts + 1000000LL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q_aux, aux, mb_link); mb = aux; } else if(cur != NULL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mb = cur; goto retry_current; } else if(data != NULL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q_data, data, mb_link); mp_check_underrun(mp); mb = data; } else { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); retry_current: mc = mb->mb_cw; if(mb->mb_data_type == MB_VIDEO && mc->decode_locked != NULL) { if(mc != mc_current) { if(mc_current != NULL) media_codec_deref(mc_current); mc_current = media_codec_ref(mc); prop_set_int(mq->mq_prop_too_slow, 0); } size = mb->mb_size; mq->mq_no_data_interest = 1; if(mc->decode_locked(mc, vd, mq, mb)) { cur = mb; hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mq->mq_no_data_interest = 0; cur = NULL; update_vbitrate(mp, mq, size, vd); media_buf_free_locked(mp, mb); continue; } hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: vd->vd_hold = 1; break; case MB_CTRL_PLAY: vd->vd_hold = 0; break; case MB_CTRL_FLUSH: if(cur != NULL) { media_buf_free_unlocked(mp, cur); mq->mq_no_data_interest = 0; cur = NULL; } vd_init_timings(vd); vd->vd_interlaced = 0; hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); dvdspu_flush_locked(mp); hts_mutex_unlock(&mp->mp_overlay_mutex); mp->mp_video_frame_deliver(NULL, mp->mp_video_frame_opaque); if(mc_current != NULL) { mc_current->flush(mc_current, vd); media_codec_deref(mc_current); mc_current = NULL; } mp->mp_video_frame_deliver(NULL, mp->mp_video_frame_opaque); if(mp->mp_seek_video_done != NULL) mp->mp_seek_video_done(mp); break; case MB_VIDEO: if(mc != mc_current) { if(mc_current != NULL) media_codec_deref(mc_current); mc_current = media_codec_ref(mc); prop_set_int(mq->mq_prop_too_slow, 0); } if(reinit) { if(mc->reinit != NULL) mc->reinit(mc); reinit = 0; } size = mb->mb_size; mc->decode(mc, vd, mq, mb, reqsize); update_vbitrate(mp, mq, size, vd); reqsize = -1; break; case MB_CTRL_REQ_OUTPUT_SIZE: reqsize = mb->mb_data32; break; case MB_CTRL_REINITIALIZE: reinit = 1; break; case MB_CTRL_RECONFIGURE: mb->mb_cw->reconfigure(mc, mb->mb_frame_info); break; #if ENABLE_DVD case MB_DVD_RESET_SPU: hts_mutex_lock(&mp->mp_overlay_mutex); vd->vd_spu_curbut = 1; dvdspu_flush_locked(mp); hts_mutex_unlock(&mp->mp_overlay_mutex); break; case MB_CTRL_DVD_HILITE: vd->vd_spu_curbut = mb->mb_data32; vd->vd_spu_repaint = 1; break; case MB_DVD_PCI: memcpy(&vd->vd_pci, mb->mb_data, sizeof(pci_t)); vd->vd_spu_repaint = 1; event_payload_t *ep = event_create(EVENT_DVD_PCI, sizeof(event_t) + sizeof(pci_t)); memcpy(ep->payload, mb->mb_data, sizeof(pci_t)); mp_enqueue_event(mp, &ep->h); event_release(&ep->h); break; case MB_DVD_CLUT: dvdspu_decode_clut(vd->vd_dvd_clut, (void *)mb->mb_data); break; case MB_DVD_SPU: dvdspu_enqueue(mp, mb->mb_data, mb->mb_size, vd->vd_dvd_clut, 0, 0, mb->mb_pts); break; #endif case MB_CTRL_DVD_SPU2: dvdspu_enqueue(mp, mb->mb_data+72, mb->mb_size-72, (void *)mb->mb_data, ((const uint32_t *)mb->mb_data)[16], ((const uint32_t *)mb->mb_data)[17], mb->mb_pts); break; case MB_SUBTITLE: if(vd->vd_ext_subtitles == NULL && mb->mb_stream == mq->mq_stream2) video_overlay_decode(mp, mb); break; case MB_CTRL_FLUSH_SUBTITLES: hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); hts_mutex_unlock(&mp->mp_overlay_mutex); break; case MB_CTRL_EXT_SUBTITLE: if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); // Steal subtitle from the media_buf vd->vd_ext_subtitles = (void *)mb->mb_data; mb->mb_data = NULL; hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); hts_mutex_unlock(&mp->mp_overlay_mutex); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } if(cur != NULL) media_buf_free_locked(mp, cur); mq->mq_no_data_interest = 0; hts_mutex_unlock(&mp->mp_mutex); if(mc_current != NULL) media_codec_deref(mc_current); if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); av_frame_free(&vd->vd_frame); return NULL; }
static int pa_audio_start(audio_mode_t *am, audio_fifo_t *af) { pa_audio_mode_t *pam = (pa_audio_mode_t *)am; audio_buf_t *ab = NULL; size_t l, length; int64_t pts; media_pipe_t *mp; int r = 0; pa_threaded_mainloop_lock(mainloop); #if PA_API_VERSION >= 12 pa_proplist *pl = pa_proplist_new(); pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "com.lonelycoder.hts.showtime"); pa_proplist_sets(pl, PA_PROP_APPLICATION_NAME, "Showtime"); /* Create a new connection context */ pam->context = pa_context_new_with_proplist(api, "Showtime", pl); pa_proplist_free(pl); #else pam->context = pa_context_new(api, "Showtime"); #endif if(pam->context == NULL) { pa_threaded_mainloop_unlock(mainloop); return -1; } pa_context_set_state_callback(pam->context, context_state_callback, pam); /* Connect the context */ if(pa_context_connect(pam->context, NULL, 0, NULL) < 0) { TRACE(TRACE_ERROR, "PA", "pa_context_connect() failed: %s", pa_strerror(pa_context_errno(pam->context))); pa_threaded_mainloop_unlock(mainloop); return -1; } /* Need at least one packet of audio */ /* Subscribe to updates of master volume */ pam->sub_mvol = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_FLOAT, set_mastervol, pam, PROP_TAG_ROOT, prop_mastervol, PROP_TAG_EXTERNAL_LOCK, mainloop, prop_pa_lockmgr, NULL); /* Subscribe to updates of master volume mute */ pam->sub_mute = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_INT, set_mastermute, pam, PROP_TAG_ROOT, prop_mastermute, PROP_TAG_EXTERNAL_LOCK, mainloop, prop_pa_lockmgr, NULL); while(1) { if(ab == NULL) { pa_threaded_mainloop_unlock(mainloop); ab = af_deq2(af, 1, am); pa_threaded_mainloop_lock(mainloop); if(ab == AF_EXIT) { ab = NULL; break; } } if(pa_context_get_state(pam->context) == PA_CONTEXT_TERMINATED || pa_context_get_state(pam->context) == PA_CONTEXT_FAILED) { r = -1; break; } if(pam->stream != NULL && (pam->cur_format != ab->ab_format || pam->cur_rate != ab->ab_samplerate || pam->cur_isfloat != ab->ab_isfloat)) { stream_destroy(pam); } if(pam->stream == NULL && pa_context_get_state(pam->context) == PA_CONTEXT_READY) { /* Context is ready, but we don't have a stream yet, set it up */ stream_setup(pam, ab); } if(pam->stream == NULL) { pa_threaded_mainloop_wait(mainloop); continue; } switch(pa_stream_get_state(pam->stream)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: pa_threaded_mainloop_wait(mainloop); continue; case PA_STREAM_READY: break; case PA_STREAM_TERMINATED: case PA_STREAM_FAILED: pa_stream_unref(pam->stream); pam->stream = NULL; char msg[100]; snprintf(msg, sizeof(msg), "Audio stream disconnected from " "PulseAudio server -- %s.", pa_strerror(pam->stream_error)); mp_flush(ab->ab_mp, 0); mp_enqueue_event(ab->ab_mp, event_create_str(EVENT_INTERNAL_PAUSE, msg)); audio_fifo_purge(af, NULL, NULL); if(ab != NULL) { ab_free(ab); ab = NULL; } continue; } if(ab->ab_flush) { pa_operation *o; o = pa_stream_flush(pam->stream, NULL, NULL); if(o != NULL) pa_operation_unref(o); ab->ab_flush = 0; } l = pa_stream_writable_size(pam->stream); if(l == 0) { pa_threaded_mainloop_wait(mainloop); continue; } length = ab->ab_frames * pa_frame_size(&pam->ss) - ab->ab_tmp; if(l > length) l = length; if((pts = ab->ab_pts) != AV_NOPTS_VALUE && ab->ab_mp != NULL) { int64_t pts; pa_usec_t delay; pts = ab->ab_pts; ab->ab_pts = AV_NOPTS_VALUE; if(!pa_stream_get_latency(pam->stream, &delay, NULL)) { mp = ab->ab_mp; hts_mutex_lock(&mp->mp_clock_mutex); mp->mp_audio_clock = pts - delay; mp->mp_audio_clock_realtime = showtime_get_ts(); mp->mp_audio_clock_epoch = ab->ab_epoch; hts_mutex_unlock(&mp->mp_clock_mutex); } } pa_stream_write(pam->stream, ab->ab_data + ab->ab_tmp, l, NULL, 0LL, PA_SEEK_RELATIVE); ab->ab_tmp += l; assert(ab->ab_tmp <= ab->ab_frames * pa_frame_size(&pam->ss)); if(ab->ab_frames * pa_frame_size(&pam->ss) == ab->ab_tmp) { ab_free(ab); ab = NULL; } } prop_unsubscribe(pam->sub_mvol); prop_unsubscribe(pam->sub_mute); if(pam->stream != NULL) stream_destroy(pam); pa_threaded_mainloop_unlock(mainloop); pa_context_unref(pam->context); if(ab != NULL) { ab_free(ab); ab = NULL; } return r; }
/** * Decode subtitles from LAVC */ static void video_subtitles_decode_lavc(video_decoder_t *vd, media_buf_t *mb, AVCodecContext *ctx) { AVSubtitle sub; int size = 0, i, x, y; subtitle_t *s; AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = mb->mb_data; avpkt.size = mb->mb_size; if(avcodec_decode_subtitle2(ctx, &sub, &size, &avpkt) < 1 || size < 1) return; s = malloc(sizeof(subtitle_t) + sizeof(subtitle_rect_t) * sub.num_rects); s->s_active = 0; s->s_text = NULL; s->s_start = mb->mb_pts + sub.start_display_time * 1000; s->s_stop = mb->mb_pts + sub.end_display_time * 1000; s->s_num_rects = sub.num_rects; for(i = 0; i < sub.num_rects; i++) { AVSubtitleRect *r = sub.rects[i]; subtitle_rect_t *sr = &s->s_rects[i]; switch(r->type) { case SUBTITLE_BITMAP: sr->x = r->x; sr->y = r->y; sr->w = r->w; sr->h = r->h; const uint8_t *src = r->pict.data[0]; const uint32_t *clut = (uint32_t *)r->pict.data[1]; sr->bitmap = malloc(4 * r->w * r->h); uint32_t *dst = (uint32_t *)sr->bitmap; for(y = 0; y < r->h; y++) { for(x = 0; x < r->w; x++) { *dst++ = clut[src[x]]; } src += r->pict.linesize[0]; } break; default: sr->x = 0; sr->y = 0; sr->w = 0; sr->h = 0; sr->bitmap = NULL; break; } } hts_mutex_lock(&vd->vd_sub_mutex); TAILQ_INSERT_TAIL(&vd->vd_sub_queue, s, s_link); hts_mutex_unlock(&vd->vd_sub_mutex); }
static void gu_enter(void) { hts_mutex_lock(&gu_mutex); }
static int pulseaudio_audio_deliver(audio_decoder_t *ad, int samples, int64_t pts, int epoch) { decoder_t *d = (decoder_t *)ad; size_t bytes; if(ad->ad_spdif_muxer != NULL) { bytes = ad->ad_spdif_frame_size; } else { bytes = samples * d->framesize; } void *buf; assert(d->s != NULL); pa_threaded_mainloop_lock(mainloop); int writable = pa_stream_writable_size(d->s); if(writable == 0) { d->blocked = 1; pa_threaded_mainloop_unlock(mainloop); return 1; } pa_stream_begin_write(d->s, &buf, &bytes); assert(bytes > 0); if(ad->ad_spdif_muxer != NULL) { memcpy(buf, ad->ad_spdif_frame, ad->ad_spdif_frame_size); } else { int rsamples = bytes / d->framesize; uint8_t *data[8] = {0}; data[0] = (uint8_t *)buf; assert(rsamples <= samples); avresample_read(ad->ad_avr, data, rsamples); } if(pts != AV_NOPTS_VALUE) { media_pipe_t *mp = ad->ad_mp; hts_mutex_lock(&mp->mp_clock_mutex); const pa_timing_info *ti = pa_stream_get_timing_info(d->s); if(ti != NULL) { mp->mp_audio_clock_avtime = ti->timestamp.tv_sec * 1000000LL + ti->timestamp.tv_usec; int64_t busec = pa_bytes_to_usec(ti->write_index - ti->read_index, &d->ss); int64_t delay = ti->sink_usec + busec + ti->transport_usec; mp->mp_audio_clock = pts - delay; mp->mp_audio_clock_epoch = epoch; } hts_mutex_unlock(&mp->mp_clock_mutex); } pa_stream_write(d->s, buf, bytes, NULL, 0LL, PA_SEEK_RELATIVE); ad->ad_spdif_frame_size = 0; pa_threaded_mainloop_unlock(mainloop); return 0; }
static void picture_out(vdec_decoder_t *vdd) { int r; uint32_t addr; vdec_picture_format picfmt; vdec_pic_t *vp; char metainfo[64]; pktmeta_t *pm; picfmt.alpha = 0; picfmt.format_type = VDEC_PICFMT_YUV420P; picfmt.color_matrix = VDEC_COLOR_MATRIX_BT709; r = vdec_get_pic_item(vdd->handle, &addr); if(r != 0) return; vdec_picture *pi = (void *)(intptr_t)addr; pm = &vdd->pktmeta[pi->userdata[0]]; vdd->pending_flush |= pm->flush; if(/* pi->status != 0 ||*/ pi->attr != 0 || pm->skip) { vdec_get_picture(vdd->handle, &picfmt, NULL); reset_active_pictures(vdd, pm->skip ? "Skip" : "Error", 0); return; } if(vdd->pending_flush) { reset_active_pictures(vdd, "stream flush", 0); vdd->pending_flush = 0; vdd->max_order = -1; } int64_t pts = pm->nopts ? AV_NOPTS_VALUE : pi->pts[0].low + ((uint64_t)pi->pts[0].hi << 32); int64_t dts = pm->nodts ? AV_NOPTS_VALUE : pi->dts[0].low + ((uint64_t)pi->dts[0].hi << 32); int64_t order; if(pi->codec_type == VDEC_CODEC_TYPE_MPEG2) { vdec_mpeg2_info *mpeg2 = (void *)(intptr_t)pi->codec_specific_addr; vp = alloc_picture(vdd, mpeg2->width, mpeg2->height); vp->fi.fi_width = mpeg2->width; vp->fi.fi_height = mpeg2->height; vp->fi.fi_duration = mpeg2->frame_rate <= 8 ? mpeg_durations[mpeg2->frame_rate] : 40000; if(pm->disable_deinterlacer) { vp->fi.fi_interlaced = 0; } else { vp->fi.fi_interlaced = !mpeg2->progressive_frame; vp->fi.fi_tff = mpeg2->top_field_first; } if(mpeg2->color_description) vp->fi.fi_color_space = mpeg2->matrix_coefficients; switch(pm->aspect_override) { default: switch(mpeg2->aspect_ratio) { case VDEC_MPEG2_ARI_SAR_1_1: vp->fi.fi_dar_num = mpeg2->width; vp->fi.fi_dar_den = mpeg2->height; break; case VDEC_MPEG2_ARI_DAR_4_3: vp->fi.fi_dar_num = 4; vp->fi.fi_dar_den = 3; break; case VDEC_MPEG2_ARI_DAR_16_9: vp->fi.fi_dar_num = 16; vp->fi.fi_dar_den = 9; break; case VDEC_MPEG2_ARI_DAR_2P21_1: vp->fi.fi_dar_num = 221; vp->fi.fi_dar_den = 100; break; } break; case 1: vp->fi.fi_dar_num = 4; vp->fi.fi_dar_den = 3; break; case 2: vp->fi.fi_dar_num = 16; vp->fi.fi_dar_den = 9; break; } snprintf(metainfo, sizeof(metainfo), "MPEG2 %dx%d%c (Cell)", mpeg2->width, mpeg2->height, vp->fi.fi_interlaced ? 'i' : 'p'); if(pts == AV_NOPTS_VALUE && dts != AV_NOPTS_VALUE && mpeg2->picture_coding_type[0] == 3) pts = dts; #if VDEC_DETAILED_DEBUG TRACE(TRACE_DEBUG, "VDEC DEC", "%ld %d", pts, mpeg2->picture_coding_type[0]); #endif order = vdd->order_base; vdd->order_base++; } else { vdec_h264_info *h264 = (void *)(intptr_t)pi->codec_specific_addr; vp = alloc_picture(vdd, h264->width, h264->height); vp->fi.fi_width = h264->width - vdd->crop_right; vp->fi.fi_height = h264->height - vdd->crop_bottom; vp->fi.fi_duration = h264->frame_rate <= 7 ? mpeg_durations[h264->frame_rate + 1] : 40000; switch(h264->pic_struct) { default: case 0: vp->fi.fi_interlaced = 0; vp->fi.fi_tff = 0; break; case 3: // top field + bottom field vp->fi.fi_interlaced = 1; vp->fi.fi_tff = 1; break; case 4: // bottom field + top field vp->fi.fi_interlaced = 1; vp->fi.fi_tff = 0; break; } if(h264->color_description_present_flag) vp->fi.fi_color_space = h264->matrix_coefficients; vp->fi.fi_dar_num = h264->width; vp->fi.fi_dar_den = h264->height; if(h264->aspect_ratio_idc == 0xff) { vp->fi.fi_dar_num *= h264->sar_width; vp->fi.fi_dar_den *= h264->sar_height; } else { const uint8_t *p; p = h264_sar[h264->aspect_ratio_idc <= 16 ? h264->aspect_ratio_idc : 0]; vp->fi.fi_dar_num *= p[0]; vp->fi.fi_dar_den *= p[1]; } if(h264->idr_picture_flag) { vdd->order_base += 0x100000000LL; vdd->poc_ext = 0; } uint32_t om = h264->pic_order_count[0] & 0x7fff; int p = om >> 13; if(p == ((vdd->poc_ext + 1) & 3)) { vdd->poc_ext = p; if(p == 0) vdd->order_base += 0x100000000LL; } if(p == 3 && vdd->poc_ext == 0) { order = vdd->order_base + om - 0x100000000LL; } else { order = vdd->order_base + om; } if(pts == AV_NOPTS_VALUE && dts != AV_NOPTS_VALUE) { if(h264->picture_type[0] == 2) { vdd->seen_b_frames = 100; pts = dts; } if(vdd->seen_b_frames) vdd->seen_b_frames--; if(!vdd->seen_b_frames) pts = dts; } #if VDEC_DETAILED_DEBUG TRACE(TRACE_DEBUG, "VDEC DEC", "POC=%3d:%-3d IDR=%d PS=%d LD=%d %x 0x%llx %ld %d %d", (uint16_t)h264->pic_order_count[0], (uint16_t)h264->pic_order_count[1], h264->idr_picture_flag, h264->pic_struct, h264->low_delay_hrd_flag, h264->nalUnitPresentFlags, order, pts, h264->picture_type[0], h264->frame_mbs_only_flag); #endif if(vdd->level_major) snprintf(metainfo, sizeof(metainfo), "h264 (Level %d.%d) %dx%d%c (Cell)", vdd->level_major, vdd->level_minor, h264->width, h264->height, vp->fi.fi_interlaced ? 'i' : 'p'); else snprintf(metainfo, sizeof(metainfo), "h264 %dx%d%c (Cell)", h264->width, h264->height, vp->fi.fi_interlaced ? 'i' : 'p'); } prop_set_string(vdd->metainfo, metainfo); vp->fi.fi_pix_fmt = AV_PIX_FMT_YUV420P; vp->fi.fi_pts = pts; vp->fi.fi_epoch = pm->epoch; vp->fi.fi_prescaled = 0; vp->fi.fi_color_space = COLOR_SPACE_UNSET; vp->fi.fi_user_time = pm->user_time; vp->fi.fi_drive_clock = pm->drive_clock; vdec_get_picture(vdd->handle, &picfmt, rsx_to_ppu(vp->vp_offset[0])); vp->order = order; hts_mutex_lock(&vdd->mtx); LIST_INSERT_SORTED(&vdd->pictures, vp, link, vp_cmp, vdec_pic_t); vdd->num_pictures++; if(vdd->max_order != -1) { if(vp->order > vdd->max_order) { vdd->flush_to = vdd->max_order; vdd->max_order = vp->order; } } else { vdd->max_order = vp->order; } hts_mutex_unlock(&vdd->mtx); }
static image_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; image_t *img = NULL; char cacheid[512]; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); const char *siz; *tim++ = 0; int secs; if(!strcmp(tim, "cover")) secs = -1; else secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex[0]); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex[0]); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex[0]); if(im->im_req_width < 100 && im->im_req_height < 100) { siz = "min"; } else if(im->im_req_width < 200 && im->im_req_height < 200) { siz = "mid"; } else { siz = "max"; } snprintf(cacheid, sizeof(cacheid), "%s-%s", url0, siz); buf_t *b = blobcache_get(cacheid, "videothumb", 0, 0, NULL, &mtime); if(b != NULL && mtime == stattime) { img = image_coded_create_from_buf(b, IMAGE_JPEG); buf_release(b); return img; } buf_release(b); if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex[1]); img = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, c); hts_mutex_unlock(&image_from_video_mutex[1]); if(img != NULL) img->im_flags |= IMAGE_ADAPTED; return img; }
static void blobcache_prune(void) { DIR *d1, *d2; struct dirent *de1, *de2; char path[PATH_MAX]; char path2[PATH_MAX]; char path3[PATH_MAX]; struct stat st; uint64_t tsize = 0, msize; int files = 0; struct cachfile_list list; snprintf(path, sizeof(path), "%s/blobcache", showtime_cache_path); if((d1 = opendir(path)) == NULL) return; LIST_INIT(&list); while((de1 = readdir(d1)) != NULL) { if(de1->d_name[0] != '.') { snprintf(path2, sizeof(path2), "%s/blobcache/%s", showtime_cache_path, de1->d_name); if((d2 = opendir(path2)) != NULL) { while((de2 = readdir(d2)) != NULL) { if(de2->d_name[0] != '.') { snprintf(path3, sizeof(path3), "%s/blobcache/%s/%s", showtime_cache_path, de1->d_name, de2->d_name); if(!stat(path3, &st)) { addfile(&list, de1->d_name, de2->d_name, &st); files++; tsize += st.st_size; } } } closedir(d2); } } } closedir(d1); msize = blobcache_compute_size(tsize); if(files > 0) { struct cachefile **v, *c, *next; int i; time_t now; time(&now); for(c = LIST_FIRST(&list); c != NULL; c = next) { next = LIST_NEXT(c, link); digest_to_path(c->d, path, sizeof(path)); int fd = open(path, O_RDONLY, 0); if(fd != -1) { uint8_t buf[4]; if(read(fd, buf, 4) == 4) { time_t exp = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; if(exp < now) { tsize -= c->size; files--; unlink(path); LIST_REMOVE(c, link); free(c); } } close(fd); } } v = malloc(sizeof(struct cachfile_list *) * files); i = 0; LIST_FOREACH(c, &list, link) v[i++] = c; assert(i == files); qsort(v, files, sizeof(struct cachfile_list *), cfcmp); for(i = 0; i < files; i++) { c = v[i]; if(tsize > msize) { digest_to_path(c->d, path, sizeof(path)); if(!unlink(path)) tsize -= c->size; } free(c); } free(v); } hts_mutex_lock(&blobcache_mutex); blobcache_size_max = msize; blobcache_size_current = tsize; TRACE(TRACE_DEBUG, "blobcache", "Using %lld MB out of %lld MB", blobcache_size_current / 1000000LL, blobcache_size_max / 1000000LL); hts_mutex_unlock(&blobcache_mutex); }
/** * Video decoder thread */ static void * vd_thread(void *aux) { video_decoder_t *vd = aux; media_pipe_t *mp = vd->vd_mp; media_queue_t *mq = &mp->mp_video; media_buf_t *mb; media_codec_t *mc; int run = 1; int reqsize = -1; int reinit = 0; int size; vd->vd_frame = avcodec_alloc_frame(); hts_mutex_lock(&mp->mp_mutex); while(run) { if((mb = TAILQ_FIRST(&mq->mq_q)) == NULL) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } if(mb->mb_data_type == MB_VIDEO && vd->vd_hold && vd->vd_skip == 0 && mb->mb_skip == 0) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q, mb, mb_link); mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); hts_mutex_unlock(&mp->mp_mutex); mc = mb->mb_cw; switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: vd->vd_hold = 1; break; case MB_CTRL_PLAY: vd->vd_hold = 0; break; case MB_FLUSH: vd_init_timings(vd); vd->vd_do_flush = 1; vd->vd_interlaced = 0; video_overlay_flush(vd, 1); dvdspu_flush(vd); break; case MB_VIDEO: if(reinit) { reinit = 0; if(mc->reinit != NULL) mc->reinit(mc); } if(mb->mb_skip == 2) vd->vd_skip = 1; size = mb->mb_size; if(mc->decode) mc->decode(mc, vd, mq, mb, reqsize); else vd_decode_video(vd, mq, mb); update_vbitrate(mp, mq, size, vd); reqsize = -1; break; case MB_REQ_OUTPUT_SIZE: reqsize = mb->mb_data32; break; case MB_REINITIALIZE: reinit = 1; break; #if ENABLE_DVD case MB_DVD_RESET_SPU: vd->vd_spu_curbut = 1; dvdspu_flush(vd); break; case MB_DVD_HILITE: vd->vd_spu_curbut = mb->mb_data32; vd->vd_spu_repaint = 1; break; case MB_DVD_PCI: memcpy(&vd->vd_pci, mb->mb_data, sizeof(pci_t)); vd->vd_spu_repaint = 1; event_t *e = event_create(EVENT_DVD_PCI, sizeof(event_t) + sizeof(pci_t)); memcpy(e->e_payload, mb->mb_data, sizeof(pci_t)); mp_enqueue_event(mp, e); event_release(e); break; case MB_DVD_CLUT: dvdspu_decode_clut(vd->vd_dvd_clut, mb->mb_data); break; case MB_DVD_SPU: dvdspu_enqueue(vd, mb->mb_data, mb->mb_size, vd->vd_dvd_clut, 0, 0, mb->mb_pts); break; #endif case MB_DVD_SPU2: dvdspu_enqueue(vd, mb->mb_data+72, mb->mb_size-72, mb->mb_data, ((const uint32_t *)mb->mb_data)[16], ((const uint32_t *)mb->mb_data)[17], mb->mb_pts); break; case MB_SUBTITLE: if(vd->vd_ext_subtitles == NULL && mb->mb_stream == mq->mq_stream2) video_overlay_decode(vd, mb); break; case MB_END: break; case MB_BLACKOUT: vd->vd_frame_deliver(FRAME_BUFFER_TYPE_BLACKOUT, NULL, NULL, vd->vd_opaque); break; case MB_FLUSH_SUBTITLES: video_overlay_flush(vd, 1); break; case MB_EXT_SUBTITLE: if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); // Steal subtitle from the media_buf vd->vd_ext_subtitles = mb->mb_data; mb->mb_data = NULL; video_overlay_flush(vd, 1); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); /* Free ffmpeg frame */ av_free(vd->vd_frame); return NULL; }
static void * ad_thread(void *aux) { audio_decoder_t *ad = aux; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int hold = 0; int run = 1; hts_mutex_lock(&mp->mp_mutex); while(run) { if((mb = TAILQ_FIRST(&mq->mq_q)) == NULL) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } if(mb->mb_data_type == MB_AUDIO && hold && mb->mb_skip == 0) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q, mb, mb_link); mq->mq_freeze_tail = 1; mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: /* Copy back any pending audio in the output fifo */ audio_fifo_purge(thefifo, ad, &ad->ad_hold_queue); hold = 1; break; case MB_CTRL_PLAY: hold = 0; break; case MB_FLUSH: ad->ad_do_flush = 1; /* Flush any pending audio in the output fifo */ audio_fifo_purge(thefifo, ad, NULL); audio_decoder_flush(ad); break; case MB_AUDIO: if(mb->mb_skip != 0) break; if(mb->mb_stream != mq->mq_stream) break; ad_decode_buf(ad, mp, mq, mb); break; case MB_END: mp_set_current_time(mp, AV_NOPTS_VALUE); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); mq->mq_freeze_tail = 0; media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); audio_fifo_purge(thefifo, ad, NULL); return NULL; }
static void sunxi_set_bg(const char *path) { char errbuf[128]; image_meta_t im = {0}; unsigned long args[4] = {0}; pixmap_t *pm; int width = 1280, height = 720; int r; return; // hum im.im_req_width = width; im.im_req_height = height; rstr_t *rpath = rstr_alloc(path); pm = backend_imageloader(rpath, &im, NULL, errbuf, sizeof(errbuf), NULL, NULL, NULL); rstr_release(rpath); if(pm == NULL) { TRACE(TRACE_ERROR, "BG", "Unable to load %s -- %s", path, errbuf); return; } int bpp; switch(pm->pm_type) { case PIXMAP_RGB24: bpp = 3; break; case PIXMAP_BGR32: bpp = 4; break; default: abort(); } size_t tsize = pm->pm_height * pm->pm_linesize; hts_mutex_lock(&sunxi.gfxmem_mutex); uint8_t *dst = tlsf_memalign(sunxi.gfxmem, 1024, tsize); hts_mutex_unlock(&sunxi.gfxmem_mutex); memcpy(dst, pm->pm_pixels, tsize); pixmap_release(pm); __disp_video_fb_t frmbuf; memset(&frmbuf, 0, sizeof(__disp_video_fb_t)); frmbuf.addr[0] = va_to_pa(dst); frmbuf.addr[1] = va_to_pa(dst); frmbuf.addr[2] = va_to_pa(dst); args[1] = DISP_LAYER_WORK_MODE_NORMAL; int hlay = ioctl(sunxi.dispfd, DISP_CMD_LAYER_REQUEST, args); if(hlay == -1) exit(3); __disp_layer_info_t l; memset(&l, 0, sizeof(l)); l.mode = DISP_LAYER_WORK_MODE_NORMAL; l.pipe = 1; l.fb.size.width = pm->pm_linesize / bpp; l.fb.size.height = pm->pm_height; l.fb.addr[0] = frmbuf.addr[0]; l.fb.addr[1] = frmbuf.addr[1]; l.fb.addr[2] = frmbuf.addr[2]; switch(pm->pm_type) { case PIXMAP_RGB24: l.fb.format = DISP_FORMAT_RGB888; l.fb.br_swap = 1; l.fb.mode = DISP_MOD_INTERLEAVED; break; case PIXMAP_BGR32: l.fb.format = DISP_FORMAT_ARGB8888; l.fb.br_swap = 1; l.fb.mode = DISP_MOD_INTERLEAVED; break; default: abort(); } /// l.fb.seq = 0; // l.fb.mode = DISP_MOD_NON_MB_PLANAR; // l.fb.format = DISP_FORMAT_YUV420; l.ck_enable = 0; l.alpha_en = 1; l.alpha_val = 0; l.src_win.x = 0; l.src_win.y = 0; l.src_win.width = width; l.src_win.height = height; l.scn_win.x = 0; l.scn_win.y = 0; l.scn_win.width = width; l.scn_win.height = height; args[1] = hlay; args[2] = (__u32)&l; args[3] = 0; r = ioctl(sunxi.dispfd,DISP_CMD_LAYER_SET_PARA,(void*)args); if(r) perror("ioctl(disphd,DISP_CMD_LAYER_SET_PARA)"); args[1] = hlay; args[2] = 0; r = ioctl(sunxi.dispfd,DISP_CMD_LAYER_OPEN,(void*)args); if(r) perror("ioctl(disphd,DISP_CMD_LAYER_OPEN)"); bg_open = 1; args[1] = hlay; if(ioctl(sunxi.dispfd, DISP_CMD_LAYER_BOTTOM, args)) perror("ioctl(disphd,DISP_CMD_LAYER_BOTTOM)"); bg_layer = hlay; }
void * audio_decode_thread(void *aux) { audio_decoder_t *ad = aux; const audio_class_t *ac = ad->ad_ac; int run = 1; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int blocked = 0; if(ac->ac_init != NULL) ac->ac_init(ad); ad->ad_discontinuity = 1; hts_mutex_lock(&mp->mp_mutex); while(run) { int avail; if(ad->ad_spdif_muxer != NULL) { avail = ad->ad_spdif_frame_size; } else { avail = ad->ad_avr != NULL ? avresample_available(ad->ad_avr) : 0; } media_buf_t *data = TAILQ_FIRST(&mq->mq_q_data); media_buf_t *ctrl = TAILQ_FIRST(&mq->mq_q_ctrl); if(avail >= ad->ad_tile_size && blocked == 0 && !ad->ad_paused && !ctrl) { assert(avail != 0); int samples = MIN(ad->ad_tile_size, avail); int r; if(ac->ac_deliver_locked != NULL) { r = ac->ac_deliver_locked(ad, samples, ad->ad_pts, ad->ad_epoch); if(r) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } } else { hts_mutex_unlock(&mp->mp_mutex); r = ac->ac_deliver_unlocked(ad, samples, ad->ad_pts, ad->ad_epoch); hts_mutex_lock(&mp->mp_mutex); } if(r) { blocked = 1; } else { ad->ad_pts = AV_NOPTS_VALUE; } continue; } if(ctrl != NULL) { TAILQ_REMOVE(&mq->mq_q_ctrl, ctrl, mb_link); mb = ctrl; } else if(data != NULL && avail < ad->ad_tile_size) { TAILQ_REMOVE(&mq->mq_q_data, data, mb_link); mb = data; } else { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); if(mb->mb_data_type == MB_CTRL_UNBLOCK) { assert(blocked); blocked = 0; } else if(ad->ad_mode == AUDIO_MODE_CODED && ac->ac_deliver_coded_locked != NULL && mb->mb_data_type == MB_AUDIO) { ac->ac_deliver_coded_locked(ad, mb->mb_data, mb->mb_size, mb->mb_pts, mb->mb_epoch); } else { hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_AUDIO: audio_process_audio(ad, mb); break; case MB_SET_PROP_STRING: prop_set_string(mb->mb_prop, (void *)mb->mb_data); break; case MB_CTRL_SET_VOLUME_MULTIPLIER: ad->ad_vol_scale = mb->mb_float; if(ac->ac_set_volume != NULL) ac->ac_set_volume(ad, ad->ad_vol_scale); break; case MB_CTRL_PAUSE: ad->ad_paused = 1; if(ac->ac_pause) ac->ac_pause(ad); break; case MB_CTRL_PLAY: ad->ad_paused = 0; if(ac->ac_play) ac->ac_play(ad); break; case MB_CTRL_FLUSH: // Reset some error reporting filters ad->ad_channel_layout_fail = 0; ad->ad_sample_rate_fail = 0; if(ac->ac_flush) ac->ac_flush(ad); ad->ad_pts = AV_NOPTS_VALUE; if(mp->mp_seek_audio_done != NULL) mp->mp_seek_audio_done(mp); ad->ad_discontinuity = 1; if(ad->ad_avr != NULL) { avresample_read(ad->ad_avr, NULL, avresample_available(ad->ad_avr)); assert(avresample_available(ad->ad_avr) == 0); } break; case MB_CTRL_EXIT: run = 0; break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); } media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); if(ac->ac_fini != NULL) ac->ac_fini(ad); return NULL; }
void glw_lock(glw_root_t *gr) { hts_mutex_lock(&gr->gr_mutex); }
static void vda_decode(struct media_codec *mc, struct video_decoder *vd, struct media_queue *mq, struct media_buf *mb, int reqsize) { vda_decoder_t *vdad = mc->opaque; CFDictionaryRef user_info; CFDataRef coded_frame; const int num_kvs = 6; CFStringRef keys[num_kvs]; CFNumberRef values[num_kvs]; const int keyframe = mb->mb_keyframe; const int drive_clock = mb->mb_drive_clock; vda_frame_t *vf; int i; uint8_t skip = mb->mb_skip; vdad->vdad_vd = vd; coded_frame = CFDataCreate(kCFAllocatorDefault, mb->mb_data, mb->mb_size); keys[0] = CFSTR("pts"); keys[1] = CFSTR("duration"); keys[2] = CFSTR("keyframe"); keys[3] = CFSTR("epoch"); keys[4] = CFSTR("drive_clock"); keys[5] = CFSTR("skip"); values[0] = CFNumberCreate(NULL, kCFNumberSInt64Type, &mb->mb_pts); values[1] = CFNumberCreate(NULL, kCFNumberSInt32Type, &mb->mb_duration); values[2] = CFNumberCreate(NULL, kCFNumberSInt32Type, &keyframe); values[3] = CFNumberCreate(NULL, kCFNumberSInt8Type, &mb->mb_epoch); values[4] = CFNumberCreate(NULL, kCFNumberSInt8Type, &drive_clock); values[5] = CFNumberCreate(NULL, kCFNumberSInt8Type, &skip); user_info = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, num_kvs, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); for(i = 0; i < num_kvs; i++) CFRelease(values[i]); uint32_t flags = 0; VDADecoderDecode(vdad->vdad_decoder, flags, coded_frame, user_info); CFRelease(user_info); CFRelease(coded_frame); hts_mutex_lock(&vdad->vdad_mutex); if(vdad->vdad_flush_to != PTS_UNSET) { while((vf = LIST_FIRST(&vdad->vdad_frames)) != NULL) { if(vdad->vdad_flush_to < vf->vf_pts) break; LIST_REMOVE(vf, vf_link); hts_mutex_unlock(&vdad->vdad_mutex); emit_frame(vdad, vf, mq); hts_mutex_lock(&vdad->vdad_mutex); CFRelease(vf->vf_buf); free(vf); } } hts_mutex_unlock(&vdad->vdad_mutex); }
static htsmsg_t * htsp_reqreply(htsp_connection_t *hc, htsmsg_t *m) { void *buf; size_t len; uint32_t seq; int r; tcpcon_t *tc = hc->hc_tc; uint32_t noaccess; htsmsg_t *reply; htsp_msg_t *hm = NULL; int retry = 0; char id[100]; char *username; char *password; struct AVSHA1 *shactx = alloca(av_sha1_size); uint8_t d[20]; if(tc == NULL) return NULL; /* Generate a sequence number for our message */ seq = atomic_add(&hc->hc_seq_generator, 1); htsmsg_add_u32(m, "seq", seq); again: snprintf(id, sizeof(id), "htsp://%s:%d", hc->hc_hostname, hc->hc_port); r = keyring_lookup(id, &username, &password, NULL, !!retry, "TV client", "Access denied"); if(r == -1) { /* User rejected */ return NULL; } if(r == 0) { /* Got auth credentials */ htsmsg_delete_field(m, "username"); htsmsg_delete_field(m, "digest"); if(username != NULL) htsmsg_add_str(m, "username", username); if(password != NULL) { av_sha1_init(shactx); av_sha1_update(shactx, (const uint8_t *)password, strlen(password)); av_sha1_update(shactx, hc->hc_challenge, 32); av_sha1_final(shactx, d); htsmsg_add_bin(m, "digest", d, 20); } free(username); free(password); } if(htsmsg_binary_serialize(m, &buf, &len, -1) < 0) { htsmsg_destroy(m); return NULL; } if(hc->hc_is_async) { /* Async, set up a struct that will be signalled when we get a reply */ hm = malloc(sizeof(htsp_msg_t)); hm->hm_msg = NULL; hm->hm_seq = seq; hm->hm_error = 0; hts_mutex_lock(&hc->hc_rpc_mutex); TAILQ_INSERT_TAIL(&hc->hc_rpc_queue, hm, hm_link); hts_mutex_unlock(&hc->hc_rpc_mutex); } if(tc->write(tc, buf, len)) { free(buf); htsmsg_destroy(m); if(hm != NULL) { hts_mutex_lock(&hc->hc_rpc_mutex); TAILQ_REMOVE(&hc->hc_rpc_queue, hm, hm_link); hts_mutex_unlock(&hc->hc_rpc_mutex); free(hm); } return NULL; } free(buf); if(hm != NULL) { hts_mutex_lock(&hc->hc_rpc_mutex); while(1) { if(hm->hm_error != 0) { r = hm->hm_error; TAILQ_REMOVE(&hc->hc_rpc_queue, hm, hm_link); hts_mutex_unlock(&hc->hc_rpc_mutex); free(hm); htsmsg_destroy(m); return NULL; } if(hm->hm_msg != NULL) break; hts_cond_wait(&hc->hc_rpc_cond, &hc->hc_rpc_mutex); } TAILQ_REMOVE(&hc->hc_rpc_queue, hm, hm_link); hts_mutex_unlock(&hc->hc_rpc_mutex); reply = hm->hm_msg; free(hm); } else { if((reply = htsp_recv(hc)) == NULL) { htsmsg_destroy(m); return NULL; } } if(!htsmsg_get_u32(reply, "noaccess", &noaccess) && noaccess) { retry++; goto again; } htsmsg_destroy(m); /* Destroy original message */ return reply; }