static int decode_to_timestamp(AVFormatContext* fc, int video_stream_index, double wanted_timestamp, double fps, AVPacket* pkt, AVFrame* frame, double* timestamp) { double frame_duration = 1.0 / fps; double current_timestamp = wanted_timestamp - 2*frame_duration; int counter = 0; AVStream* video_stream = fc->streams[video_stream_index]; double time_base = av_q2d(video_stream->time_base); while (fabs(current_timestamp - wanted_timestamp) >= 0.98*frame_duration) { int ret = next_video_frame(pkt, fc, video_stream_index); if (ret < 0) return -1; int got_frame = 0; ret = decode(fc, video_stream_index, frame, pkt, &got_frame); if (got_frame) current_timestamp = ((double) pkt->pts) * time_base; av_free_packet(pkt); if (ret < 0) return -1; if (pkt->pts == AV_NOPTS_VALUE) { std::cerr << "Invalid pts in packet\n"; return -1; } ++counter; } #if defined FFMPEGDRIVER_PRINT_DEBUG std::cout << "decoded " << counter << " frames\n"; #endif *timestamp = current_timestamp; return 1; }
void *display_thread(void *arg) { PlayerCtx *ctx = arg; int i; OLRenderParams params; memset(¶ms, 0, sizeof params); params.rate = 48000; params.on_speed = 2.0/100.0; params.off_speed = 2.0/15.0; params.start_wait = 8; params.end_wait = 3; params.snap = 1/120.0; params.render_flags = RENDER_GRAYSCALE; params.min_length = 20; params.start_dwell = 2; params.end_dwell = 2; params.max_framelen = 48000/20.0; if(olInit(OL_FRAMES_BUF, 300000) < 0) { printf("OpenLase init failed\n"); return NULL; } float aspect = ctx->width / (float)ctx->height; float sample_aspect = av_q2d(ctx->v_stream->sample_aspect_ratio); if (sample_aspect != 0) aspect *= sample_aspect; printf("Aspect: %f\n", aspect); float iaspect = 1/aspect; int maxd = ctx->width > ctx->height ? ctx->width : ctx->height; int mind = ctx->width < ctx->height ? ctx->width : ctx->height; g_ctx = ctx; olSetAudioCallback(get_audio); olSetRenderParams(¶ms); OLTraceCtx *trace_ctx; OLTraceParams tparams; OLTraceResult result; memset(&result, 0, sizeof(result)); ctx->settings_changed = 1; tparams.sigma = ctx->settings.blur / 100.0; if (ctx->settings.canny) tparams.mode = OL_TRACE_CANNY; else tparams.mode = OL_TRACE_THRESHOLD; tparams.width = ctx->width; tparams.height = ctx->height; printf("Resolution: %dx%d\n", ctx->width, ctx->height); olTraceInit(&trace_ctx, &tparams); VideoFrame *last = NULL; pthread_mutex_lock(&ctx->display_mode_mutex); DisplayMode display_mode = ctx->display_mode; pthread_mutex_unlock(&ctx->display_mode_mutex); int inf = 0; int bg_white = -1; float time = 0; int frames = 0; while (display_mode != STOP) { pthread_mutex_lock(&ctx->settings_mutex); PlayerSettings settings = ctx->settings; int settings_changed = ctx->settings_changed; ctx->settings_changed = 0; pthread_mutex_unlock(&ctx->settings_mutex); if (ctx->audio_idx == -1) { drop_all_video(ctx); next_video_frame(ctx); } params.min_length = settings.minsize; params.end_dwell = params.start_dwell = settings.dwell; params.off_speed = settings.offspeed * 0.002; params.snap = (settings.snap*2.0)/(float)maxd; params.start_wait = settings.startwait; params.end_wait = settings.endwait; if (settings.minrate == 0) params.max_framelen = 0; else params.max_framelen = params.rate / settings.minrate; olSetRenderParams(¶ms); olLoadIdentity(); if (aspect > 1) { olSetScissor(-1, -iaspect, 1, iaspect); olScale(1, iaspect); } else { olSetScissor(-aspect, -1, aspect, 1); olScale(aspect, 1); } olScale(1 + settings.overscan/100.0, 1 + settings.overscan/100.0); olTranslate(-1.0f, 1.0f); olScale(2.0f/ctx->width, -2.0f/ctx->height); if (!ctx->cur_frame || ctx->cur_frame->seekid < 0) { printf("Dummy frame\n"); float ftime = olRenderFrame(80); pthread_mutex_lock(&ctx->display_mode_mutex); display_mode = ctx->display_mode; pthread_mutex_unlock(&ctx->display_mode_mutex); if (ctx->cur_frame && ctx->cur_frame->seekid < 0) deliver_event(ctx, time, ftime, frames, 1); else deliver_event(ctx, time, ftime, frames, 0); continue; } if (last != ctx->cur_frame || settings_changed) { tparams.sigma = settings.blur / 100.0; if (settings.canny) { tparams.mode = OL_TRACE_CANNY; tparams.threshold = settings.threshold; tparams.threshold2 = settings.threshold2; bg_white = -1; } else { tparams.mode = OL_TRACE_THRESHOLD; if (settings.splitthreshold) { int edge_off = mind * settings.offset / 100; int bsum = 0; int cnt = 0; int c; for (c = edge_off; c < (ctx->width-edge_off); c++) { bsum += ctx->cur_frame->data[c+edge_off*ctx->cur_frame->stride]; bsum += ctx->cur_frame->data[c+(ctx->height-edge_off-1)*ctx->cur_frame->stride]; cnt += 2; } for (c = edge_off; c < (ctx->height-edge_off); c++) { bsum += ctx->cur_frame->data[edge_off+ctx->cur_frame->stride]; bsum += ctx->cur_frame->data[(c+1)*ctx->cur_frame->stride-1-edge_off]; cnt += 2; } bsum /= cnt; if (bg_white == -1) bg_white = bsum > ((settings.darkval + settings.lightval)/2); if (bg_white && bsum < settings.darkval) bg_white = 0; if (!bg_white && bsum > settings.lightval) bg_white = 1; if (bg_white) tparams.threshold = settings.threshold2; else tparams.threshold = settings.threshold; } else { tparams.threshold = settings.threshold; } } olTraceReInit(trace_ctx, &tparams); olTraceFree(&result); printf("Trace\n"); olTrace(trace_ctx, ctx->cur_frame->data, ctx->cur_frame->stride, &result); printf("Trace done\n"); inf++; last = ctx->cur_frame; } int i, j; for (i = 0; i < result.count; i++) { OLTraceObject *o = &result.objects[i]; olBegin(OL_POINTS); OLTracePoint *p = o->points; for (j = 0; j < o->count; j++) { if (j % settings.decimation == 0) olVertex(p->x, p->y, C_WHITE); p++; } olEnd(); } float ftime = olRenderFrame(80); OLFrameInfo info; olGetFrameInfo(&info); frames++; time += ftime; printf("Frame time: %.04f, Cur FPS:%6.02f, Avg FPS:%6.02f, Drift: %7.4f, " "In %4d, Out %4d Thr %3d/%3d Bg %3d Pts %4d", ftime, 1/ftime, frames/time, 0.0, inf, frames, tparams.threshold, tparams.threshold2, 0, info.points); if (info.resampled_points) printf(" Rp %4d Bp %4d", info.resampled_points, info.resampled_blacks); if (info.padding_points) printf(" Pad %4d", info.padding_points); printf("\n"); deliver_event(ctx, time, ftime, frames, 0); pthread_mutex_lock(&ctx->display_mode_mutex); display_mode = ctx->display_mode; pthread_mutex_unlock(&ctx->display_mode_mutex); } olTraceDeinit(trace_ctx); for(i = 0; i < OL_FRAMES_BUF; i++) olRenderFrame(80); olShutdown(); return NULL; }
void get_audio(float *lb, float *rb, int samples) { PlayerCtx *ctx = g_ctx; if (ctx->audio_idx == -1) { memset(lb, 0, samples * sizeof(*lb)); memset(rb, 0, samples * sizeof(*rb)); return; } pthread_mutex_lock(&ctx->display_mode_mutex); DisplayMode display_mode = ctx->display_mode; pthread_mutex_unlock(&ctx->display_mode_mutex); if (display_mode != PLAY) { if (display_mode == PAUSE) { printf("get_audio: paused\n"); if (!ctx->cur_frame) { next_video_frame(ctx); } while (ctx->cur_frame->seekid != ctx->cur_seekid && ctx->cur_frame->seekid != -ctx->cur_seekid) { printf("Drop audio due to seek\n"); drop_audio(ctx, 1); next_video_frame(ctx); drop_audio(ctx, 1); } if (ctx->skip_frame) { ctx->skip_frame = 0; drop_audio(ctx, 1); next_video_frame(ctx); drop_audio(ctx, 1); } printf("get_audio: pause complete\n"); } memset(lb, 0, samples * sizeof(*lb)); memset(rb, 0, samples * sizeof(*rb)); return; } pthread_mutex_lock(&ctx->settings_mutex); double volume = ctx->settings.volume / 100.0; pthread_mutex_unlock(&ctx->settings_mutex); while (samples) { double pts = -1; pthread_mutex_lock(&ctx->a_buf_mutex); int have_samples = ctx->a_buf_put - ctx->a_buf_get; if (!have_samples) { printf("Wait for audio\n"); pthread_cond_wait(&ctx->a_buf_not_empty, &ctx->a_buf_mutex); pthread_mutex_unlock(&ctx->a_buf_mutex); continue; } if (have_samples < 0) have_samples += ctx->a_buf_len; pthread_mutex_unlock(&ctx->a_buf_mutex); int get = ctx->a_buf_get; int played = 0; while (samples && have_samples--) { if (ctx->a_buf[get].seekid == -ctx->cur_seekid) { memset(lb, 0, sizeof(*lb) * samples); memset(rb, 0, sizeof(*rb) * samples); samples = 0; break; } if (ctx->a_buf[get].seekid == ctx->cur_seekid) { pts = ctx->a_buf[get].pts; *lb++ = ctx->a_buf[get].l / 32768.0 * volume; *rb++ = ctx->a_buf[get].r / 32768.0 * volume; samples--; played++; } if (++get >= ctx->a_buf_len) get = 0; } pthread_mutex_lock(&ctx->a_buf_mutex); ctx->a_buf_get = get; pthread_cond_signal(&ctx->a_buf_not_full); pthread_mutex_unlock(&ctx->a_buf_mutex); printf("Played %d samples, next pts %f\n", played, pts); while (1) { if (!ctx->cur_frame) { next_video_frame(ctx); continue; } if (ctx->cur_frame->seekid == -ctx->cur_seekid) break; double next_pts = ctx->cur_frame->pts; if (ctx->last_frame_pts != -1) next_pts = 2 * ctx->cur_frame->pts - ctx->last_frame_pts; if (pts > next_pts || ctx->cur_frame->seekid != ctx->cur_seekid) { if (next_video_frame(ctx)) continue; } break; } } }