예제 #1
0
파일: timer.c 프로젝트: ThreeGe/mpv
int main(void) {
    int c = 2000000;
    int64_t j, r, t = 0;
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, NULL);
    pthread_cond_t cnd;
    pthread_cond_init(&cnd, NULL);

    mp_time_init();

    for (int i = 0; i < c; i++) {
        const int delay = rand() / (RAND_MAX / 1e5);
        r = mp_time_us();
#if TEST_SLEEP
        mp_sleep_us(delay);
#else
        struct timespec ts = mp_time_us_to_timespec(r + delay);
        pthread_cond_timedwait(&cnd, &mtx, &ts);
#endif
        j = (mp_time_us() - r) - delay;
        printf("sleep time: t=%"PRId64" sleep=%8i err=%5i\n", r, delay, (int)j);
        t += j;
    }
    fprintf(stderr, "average error:\t%i\n", (int)(t / c));

    return 0;
}
예제 #2
0
파일: dither.c 프로젝트: AbhiDGamer/mpv
int main(void)
{
    mp_time_init();
    struct ctx *k = malloc(sizeof(struct ctx));
    int64_t s = mp_time_us();
    makegauss(k, 6);
    makeuniform(k);
    print(k);
    fsck(k);
    int64_t l = mp_time_us() - s;
    printf("time: %f ms\n", l / 1000.0);
    return 0;
}
예제 #3
0
파일: misc.c 프로젝트: Deadsign/mpv
double get_relative_time(struct MPContext *mpctx)
{
    int64_t new_time = mp_time_us();
    int64_t delta = new_time - mpctx->last_time;
    mpctx->last_time = new_time;
    return delta * 0.000001;
}
예제 #4
0
파일: ao_sdl.c 프로젝트: ArcherSeven/mpv
static void audio_callback(void *userdata, Uint8 *stream, int len)
{
    struct ao *ao = userdata;
    struct priv *priv = ao->priv;

    SDL_LockMutex(priv->buffer_mutex);

#ifdef ESTIMATE_DELAY
    priv->callback_time1 = priv->callback_time0;
    priv->callback_time0 = mp_time_us();
#endif

    while (len > 0 && !priv->paused) {
        int got = av_fifo_size(priv->buffer);
        if (got > len)
            got = len;
        if (got > 0) {
            av_fifo_generic_read(priv->buffer, stream, got, NULL);
            len -= got;
            stream += got;
        }
        if (len > 0)
            SDL_CondWait(priv->underrun_cond, priv->buffer_mutex);
    }

    SDL_UnlockMutex(priv->buffer_mutex);
}
예제 #5
0
int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
{
    pthread_mutex_lock(&ctx->lock);
    ctx->recent_flip = time > 0 ? time : mp_time_us();
    pthread_mutex_unlock(&ctx->lock);

    return 0;
}
예제 #6
0
파일: timer.c 프로젝트: ThreeGe/mpv
int64_t mp_time_relative_us(int64_t *t)
{
    int64_t r = 0;
    int64_t now = mp_time_us();
    if (*t)
        r = now - *t;
    *t = now;
    return r;
}
예제 #7
0
static float get_delay(void) {
  int buffered = av_fifo_size(buffer); // could be less
  float in_jack = jack_latency;
  if (estimate && callback_interval > 0) {
    float elapsed = mp_time_us() / 1000000.0 - callback_time;
    in_jack += callback_interval - elapsed;
    if (in_jack < 0) in_jack = 0;
  }
  return (float)buffered / (float)ao_data.bps + in_jack;
}
예제 #8
0
int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
{
    pthread_mutex_lock(&ctx->lock);
    int64_t next = time > 0 ? time : mp_time_us();
    if (ctx->recent_flip)
        ctx->approx_vsync = next - ctx->recent_flip;
    ctx->recent_flip = next;
    pthread_mutex_unlock(&ctx->lock);

    return 0;
}
예제 #9
0
static float get_delay(struct ao *ao)
{
    struct priv *p = ao->priv;
    int buffered = mp_ring_buffered(p->ring); // could be less
    float in_jack = p->jack_latency;
    if (p->estimate && p->callback_interval > 0) {
        float elapsed = mp_time_us() / 1000000.0 - p->callback_time;
        in_jack += p->callback_interval - elapsed;
        if (in_jack < 0)
            in_jack = 0;
    }
    return (float)buffered / (float)ao->bps + in_jack;
}
예제 #10
0
static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
                              const AudioTimeStamp *ts, UInt32 bus,
                              UInt32 frames, AudioBufferList *buffer_list)
{
    struct ao *ao   = ctx;
    struct priv *p  = ao->priv;
    AudioBuffer buf = buffer_list->mBuffers[0];

    int64_t end = mp_time_us();
    end += p->hw_latency_us + ca_get_latency(ts) + ca_frames_to_us(ao, frames);
    ao_read_data(ao, &buf.mData, frames, end);
    return noErr;
}
예제 #11
0
파일: ao_sdl.c 프로젝트: AddictXQ/mpv
static void audio_callback(void *userdata, Uint8 *stream, int len)
{
    struct ao *ao = userdata;

    void *data[1] = {stream};

    if (len % ao->sstride)
        MP_ERR(ao, "SDL audio callback not sample aligned");

    // Time this buffer will take, plus assume 1 period (1 callback invocation)
    // fixed latency.
    double delay = 2 * len / (double)ao->bps;

    ao_read_data(ao, data, len / ao->sstride, mp_time_us() + 1000000LL * delay);
}
예제 #12
0
파일: ao_wasapi.c 프로젝트: ThreeGe/mpv
static void thread_feed(struct ao *ao)
{
    struct wasapi_state *state = ao->priv;
    HRESULT hr;

    UINT32 frame_count = state->bufferFrameCount;

    if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) {
        UINT32 padding = 0;
        hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
        EXIT_ON_ERROR(hr);

        frame_count -= padding;
        MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
                 frame_count, padding);
    }
    double delay_us;
    hr = get_device_delay(state, &delay_us);
    EXIT_ON_ERROR(hr);
    // add the buffer delay
    delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec;

    BYTE *pData;
    hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
                                      frame_count, &pData);
    EXIT_ON_ERROR(hr);

    BYTE *data[1] = {pData};

    ao_read_data(ao, (void **)data, frame_count,
                 mp_time_us() + (int64_t)llrint(delay_us));

    // note, we can't use ao_read_data return value here since we already
    // commited to frame_count above in the GetBuffer call
    hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
                                          frame_count, 0);
    EXIT_ON_ERROR(hr);

    atomic_fetch_add(&state->sample_count, frame_count);

    return;
exit_label:
    MP_ERR(state, "Error feeding audio: %s\n", mp_HRESULT_to_str(hr));
    MP_VERBOSE(ao, "Requesting ao reload\n");
    ao_request_reload(ao);
    return;
}
예제 #13
0
파일: ao_jack.c 프로젝트: Nikoli/mpv
static int process(jack_nframes_t nframes, void *arg)
{
    struct ao *ao = arg;
    struct priv *p = ao->priv;

    void *buffers[MP_NUM_CHANNELS];

    for (int i = 0; i < p->num_ports; i++)
        buffers[i] = jack_port_get_buffer(p->ports[i], nframes);

    int64_t end_time = mp_time_us();
    end_time += (p->jack_latency + nframes / (double)ao->samplerate) * 1000000.0;

    ao_read_data(ao, buffers, nframes, end_time);

    return 0;
}
예제 #14
0
/**
 * \brief JACK Callback function
 * \param nframes number of frames to fill into buffers
 * \param arg unused
 * \return currently always 0
 *
 * Write silence into buffers if paused or an underrun occured
 */
static int outputaudio(jack_nframes_t nframes, void *arg) {
  float *bufs[MAX_CHANS];
  int i;
  for (i = 0; i < num_ports; i++)
    bufs[i] = jack_port_get_buffer(ports[i], nframes);
  if (paused || underrun)
    silence(bufs, nframes, num_ports);
  else
    if (read_buffer(bufs, nframes, num_ports) < nframes)
      underrun = 1;
  if (estimate) {
    float now = mp_time_us() / 1000000.0;
    float diff = callback_time + callback_interval - now;
    if ((diff > -0.002) && (diff < 0.002))
      callback_time += callback_interval;
    else
      callback_time = now;
    callback_interval = (float)nframes / (float)ao_data.samplerate;
  }
  return 0;
}
예제 #15
0
/**
 * \brief JACK Callback function
 * \param nframes number of frames to fill into buffers
 * \param arg unused
 * \return currently always 0
 *
 * Write silence into buffers if paused or an underrun occured
 */
static int outputaudio(jack_nframes_t nframes, void *arg)
{
    struct ao *ao = arg;
    struct priv *p = ao->priv;
    float *bufs[MAX_CHANS];
    int i;
    for (i = 0; i < p->num_ports; i++)
        bufs[i] = jack_port_get_buffer(p->ports[i], nframes);
    if (p->paused || p->underrun || !p->ring)
        silence(bufs, nframes, p->num_ports);
    else if (read_buffer(p->ring, bufs, nframes, p->num_ports) < nframes)
        p->underrun = 1;
    if (p->estimate) {
        float now = mp_time_us() / 1000000.0;
        float diff = p->callback_time + p->callback_interval - now;
        if ((diff > -0.002) && (diff < 0.002))
            p->callback_time += p->callback_interval;
        else
            p->callback_time = now;
        p->callback_interval = (float)nframes / (float)ao->samplerate;
    }
    return 0;
}
예제 #16
0
파일: ao_sdl.c 프로젝트: ArcherSeven/mpv
static float get_delay(struct ao *ao)
{
    struct priv *priv = ao->priv;
    SDL_LockMutex(priv->buffer_mutex);
    int sz = av_fifo_size(priv->buffer);
#ifdef ESTIMATE_DELAY
    int64_t callback_time0 = priv->callback_time0;
    int64_t callback_time1 = priv->callback_time1;
#endif
    SDL_UnlockMutex(priv->buffer_mutex);

    // delay component: our FIFO's length
    float delay = sz / (float) ao->bps;

#ifdef ESTIMATE_DELAY
    // delay component: outstanding audio living in SDL

    int64_t current_time = mp_time_us();

    // interval between callbacks
    int64_t callback_interval = callback_time0 - callback_time1;
    int64_t elapsed_interval = current_time - callback_time0;
    if (elapsed_interval > callback_interval)
        elapsed_interval = callback_interval;

    // delay subcomponent: remaining audio from the currently played buffer
    int64_t buffer_interval = callback_interval - elapsed_interval;

    // delay subcomponent: remaining audio from the next played buffer, as
    // provided by the callback
    buffer_interval += callback_interval;

    delay += buffer_interval / 1000000.0;
#endif

    return delay;
}
예제 #17
0
파일: ao_wasapi.c 프로젝트: Nealefelaen/mpv
static void thread_feed(struct ao *ao)
{
    struct wasapi_state *state = (struct wasapi_state *)ao->priv;
    HRESULT hr;

    UINT32 frame_count = state->bufferFrameCount;

    if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) {
        UINT32 padding = 0;
        hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
        EXIT_ON_ERROR(hr);

        frame_count -= padding;
    }

    BYTE *pData;
    hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
                                      frame_count, &pData);
    EXIT_ON_ERROR(hr);

    BYTE *data[1] = {pData};
    ao_read_data(ao, (void**)data, frame_count, (int64_t) (
                 mp_time_us() + get_device_delay(state) * 1e6 +
                 frame_count * 1e6 / state->format.Format.nSamplesPerSec));

    hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
                                          frame_count, 0);
    EXIT_ON_ERROR(hr);

    atomic_fetch_add(&state->sample_count, frame_count);

    return;
exit_label:
    MP_ERR(state, "thread_feed fails with %"PRIx32"!\n", (uint32_t)hr);
    return;
}
예제 #18
0
파일: timer.c 프로젝트: ThreeGe/mpv
struct timespec mp_time_us_to_timespec(int64_t time_us)
{
    struct timespec ts;
    get_realtime(&ts);
    // We don't know what time source mp_time_us() uses, but usually it's not
    // CLOCK_REALTIME - so we have to remap the times.
    int64_t unow = mp_time_us();
    int64_t diff_us = time_us - unow;
    int64_t diff_secs = diff_us / (1000L * 1000L);
    long diff_nsecs = (diff_us - diff_secs * (1000L * 1000L)) * 1000L;
    if (diff_nsecs < 0) {
        diff_secs -= 1;
        diff_nsecs += 1000000000L;
    }
    if (diff_nsecs + ts.tv_nsec >= 1000000000UL) {
        diff_secs += 1;
        diff_nsecs -= 1000000000UL;
    }
    // OSX can't deal with large timeouts. Also handles tv_sec/time_t overflows.
    diff_secs = MPMIN(diff_secs, 10000000);
    ts.tv_sec += diff_secs;
    ts.tv_nsec += diff_nsecs;
    return ts;
}
예제 #19
0
파일: vo_drm.c 프로젝트: AppleNuts/mpv
static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
{
    if (buf->map) {
        munmap(buf->map, buf->size);
    }
    if (buf->fb) {
        drmModeRmFB(fd, buf->fb);
    }
    if (buf->handle) {
        struct drm_mode_destroy_dumb dreq = {
            .handle = buf->handle,
        };
        drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
    }
}

static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf)
{
    int ret = 0;

    buf->handle = 0;

    // create dumb buffer
    struct drm_mode_create_dumb creq = {
        .width = buf->width,
        .height = buf->height,
        .bpp = 32,
    };
    ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
    if (ret < 0) {
        MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno));
        ret = -errno;
        goto end;
    }
    buf->stride = creq.pitch;
    buf->size = creq.size;
    buf->handle = creq.handle;

    // create framebuffer object for the dumb-buffer
    ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride,
                       buf->handle, &buf->fb);
    if (ret) {
        MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno));
        ret = -errno;
        goto end;
    }

    // prepare buffer for memory mapping
    struct drm_mode_map_dumb mreq = {
        .handle = buf->handle,
    };
    ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
    if (ret) {
        MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
        ret = -errno;
        goto end;
    }

    // perform actual memory mapping
    buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
                    fd, mreq.offset);
    if (buf->map == MAP_FAILED) {
        MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
        ret = -errno;
        goto end;
    }

    memset(buf->map, 0, buf->size);

end:
    if (ret == 0) {
        return 0;
    }

    modeset_destroy_fb(fd, buf);
    return ret;
}

static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res,
                             drmModeConnector *conn, struct modeset_dev *dev)
{
    for (unsigned int i = 0; i < conn->count_encoders; ++i) {
        drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
        if (!enc) {
            MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n",
                    i, conn->encoders[i], mp_strerror(errno));
            continue;
        }

        // iterate all global CRTCs
        for (unsigned int j = 0; j < res->count_crtcs; ++j) {
            // check whether this CRTC works with the encoder
            if (!(enc->possible_crtcs & (1 << j)))
                continue;

            dev->enc = enc;
            dev->crtc = enc->crtc_id;
            return 0;
        }

        drmModeFreeEncoder(enc);
    }

    MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id);
    return -ENOENT;
}

static bool is_connector_valid(struct vo *vo, int conn_id,
                               drmModeConnector *conn, bool silent)
{
    if (!conn) {
        if (!silent) {
            MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id,
                   mp_strerror(errno));
        }
        return false;
    }

    if (conn->connection != DRM_MODE_CONNECTED) {
        if (!silent) {
            MP_ERR(vo, "Connector %d is disconnected\n", conn_id);
        }
        return false;
    }

    if (conn->count_modes == 0) {
        if (!silent) {
            MP_ERR(vo, "Connector %d has no valid modes\n", conn_id);
        }
        return false;
    }

    return true;
}

static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id,
                               struct modeset_dev **out)
{
    struct modeset_dev *dev = NULL;
    drmModeConnector *conn = NULL;

    int ret = 0;
    *out = NULL;

    drmModeRes *res = drmModeGetResources(fd);
    if (!res) {
        MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno));
        ret = -errno;
        goto end;
    }

    if (conn_id == -1) {
        // get the first connected connector
        for (int i = 0; i < res->count_connectors; i++) {
            conn = drmModeGetConnector(fd, res->connectors[i]);
            if (is_connector_valid(vo, i, conn, true)) {
                conn_id = i;
                break;
            }
            if (conn) {
                drmModeFreeConnector(conn);
                conn = NULL;
            }
        }
        if (conn_id == -1) {
            MP_ERR(vo, "No connected connectors found\n");
            ret = -ENODEV;
            goto end;
        }
    }

    if (conn_id < 0 || conn_id >= res->count_connectors) {
        MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n",
               res->count_connectors);
        ret = -ENODEV;
        goto end;
    }

    conn = drmModeGetConnector(fd, res->connectors[conn_id]);
    if (!is_connector_valid(vo, conn_id, conn, false)) {
        ret = -ENODEV;
        goto end;
    }

    dev = talloc_zero(vo->priv, struct modeset_dev);
    dev->conn = conn->connector_id;
    dev->front_buf = 0;
    dev->mode = conn->modes[0];
    dev->bufs[0].width = conn->modes[0].hdisplay;
    dev->bufs[0].height = conn->modes[0].vdisplay;
    dev->bufs[1].width = conn->modes[0].hdisplay;
    dev->bufs[1].height = conn->modes[0].vdisplay;

    MP_INFO(vo, "Connector using mode %ux%u\n",
            dev->bufs[0].width, dev->bufs[0].height);

    ret = modeset_find_crtc(vo, fd, res, conn, dev);
    if (ret) {
        MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id);
        goto end;
    }

    for (unsigned int i = 0; i < BUF_COUNT; i++) {
        ret = modeset_create_fb(vo, fd, &dev->bufs[i]);
        if (ret) {
            MP_ERR(vo, "Cannot create framebuffer for connector %d\n",
                   conn_id);
            for (unsigned int j = 0; j < i; j++) {
                modeset_destroy_fb(fd, &dev->bufs[j]);
            }
            goto end;
        }
    }

end:
    if (conn) {
        drmModeFreeConnector(conn);
        conn = NULL;
    }
    if (res) {
        drmModeFreeResources(res);
        res = NULL;
    }
    if (ret == 0) {
        *out = dev;
    } else {
        talloc_free(dev);
    }
    return ret;
}

static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec,
                                 unsigned int usec, void *data)
{
    struct priv *p = data;
    p->pflip_happening = false;
}



static int setup_vo_crtc(struct vo *vo)
{
    struct priv *p = vo->priv;
    if (p->active)
        return 0;
    p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
    int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
                          p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
                          0, 0, &p->dev->conn, 1, &p->dev->mode);
    p->active = true;
    return ret;
}

static void release_vo_crtc(struct vo *vo)
{
    struct priv *p = vo->priv;

    if (!p->active)
        return;
    p->active = false;

    // wait for current page flip
    while (p->pflip_happening) {
        int ret = drmHandleEvent(p->fd, &p->ev);
        if (ret) {
            MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
            break;
        }
    }

    if (p->old_crtc) {
        drmModeSetCrtc(p->fd,
                       p->old_crtc->crtc_id,
                       p->old_crtc->buffer_id,
                       p->old_crtc->x,
                       p->old_crtc->y,
                       &p->dev->conn,
                       1,
                       &p->dev->mode);
        drmModeFreeCrtc(p->old_crtc);
        p->old_crtc = NULL;
    }
}

static void release_vt(void *data)
{
    struct vo *vo = data;
    release_vo_crtc(vo);
    if (USE_MASTER) {
        //this function enables support for switching to x, weston etc.
        //however, for whatever reason, it can be called only by root users.
        //until things change, this is commented.
        struct priv *p = vo->priv;
        if (drmDropMaster(p->fd)) {
            MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
        }
    }
}

static void acquire_vt(void *data)
{
    struct vo *vo = data;
    if (USE_MASTER) {
        struct priv *p = vo->priv;
        if (drmSetMaster(p->fd)) {
            MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
        }
    }

    setup_vo_crtc(vo);
}



static int wait_events(struct vo *vo, int64_t until_time_us)
{
    struct priv *p = vo->priv;
    int64_t wait_us = until_time_us - mp_time_us();
    int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
    vt_switcher_poll(&p->vt_switcher, timeout_ms);
    return 0;
}

static void wakeup(struct vo *vo)
{
    struct priv *p = vo->priv;
    vt_switcher_interrupt_poll(&p->vt_switcher);
}

static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
{
    struct priv *p = vo->priv;

    vo->dwidth = p->device_w;
    vo->dheight = p->device_h;
    vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);

    int32_t w = p->dst.x1 - p->dst.x0;
    int32_t h = p->dst.y1 - p->dst.y0;

    // p->osd contains the parameters assuming OSD rendering in window
    // coordinates, but OSD can only be rendered in the intersection
    // between window and video rectangle (i.e. not into panscan borders).
    p->osd.w = w;
    p->osd.h = h;
    p->osd.mt = MPMIN(0, p->osd.mt);
    p->osd.mb = MPMIN(0, p->osd.mb);
    p->osd.mr = MPMIN(0, p->osd.mr);
    p->osd.ml = MPMIN(0, p->osd.ml);

    p->x = (p->device_w - w) >> 1;
    p->y = (p->device_h - h) >> 1;

    mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts);
    p->sws->src = *params;
    p->sws->dst = (struct mp_image_params) {
        .imgfmt = IMGFMT_BGR0,
        .w = w,
        .h = h,
        .d_w = w,
        .d_h = h,
    };

    talloc_free(p->cur_frame);
    p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h);
    mp_image_params_guess_csp(&p->sws->dst);
    mp_image_set_params(p->cur_frame, &p->sws->dst);

    struct modeset_buf *buf = p->dev->bufs;
    memset(buf[0].map, 0, buf[0].size);
    memset(buf[1].map, 0, buf[1].size);

    if (mp_sws_reinit(p->sws) < 0)
        return -1;

    vo->want_redraw = true;
    return 0;
}

static void draw_image(struct vo *vo, mp_image_t *mpi)
{
    struct priv *p = vo->priv;

    if (p->active) {
        struct mp_image src = *mpi;
        struct mp_rect src_rc = p->src;
        src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
        src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
        mp_image_crop_rc(&src, src_rc);
        mp_sws_scale(p->sws, p->cur_frame, &src);
        osd_draw_on_image(vo->osd, p->osd, src.pts, 0, p->cur_frame);

        struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
        int32_t shift = (p->device_w * p->y + p->x) * 4;
        memcpy_pic(front_buf->map + shift,
                   p->cur_frame->planes[0],
                   (p->dst.x1 - p->dst.x0) * 4,
                   p->dst.y1 - p->dst.y0,
                   p->device_w * 4,
                   p->cur_frame->stride[0]);
    }

    if (mpi != p->last_input) {
        talloc_free(p->last_input);
        p->last_input = mpi;
    }
}

static void flip_page(struct vo *vo)
{
    struct priv *p = vo->priv;
    if (!p->active || p->pflip_happening)
        return;

    int ret = drmModePageFlip(p->fd, p->dev->crtc,
                              p->dev->bufs[p->dev->front_buf].fb,
                              DRM_MODE_PAGE_FLIP_EVENT, p);
    if (ret) {
        MP_WARN(vo, "Cannot flip page for connector\n");
    } else {
        p->dev->front_buf++;
        p->dev->front_buf %= BUF_COUNT;
        p->pflip_happening = true;
    }

    // poll page flip finish event
    const int timeout_ms = 3000;
    struct pollfd fds[1] = {
        { .events = POLLIN, .fd = p->fd },
    };
    poll(fds, 1, timeout_ms);
    if (fds[0].revents & POLLIN) {
        ret = drmHandleEvent(p->fd, &p->ev);
        if (ret != 0) {
            MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
            return;
        }
    }
}

static void uninit(struct vo *vo)
{
    struct priv *p = vo->priv;

    if (p->dev) {
        release_vo_crtc(vo);

        modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
        modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
        drmModeFreeEncoder(p->dev->enc);
    }

    vt_switcher_destroy(&p->vt_switcher);
    talloc_free(p->last_input);
    talloc_free(p->cur_frame);
    talloc_free(p->dev);
    close(p->fd);
}

static int preinit(struct vo *vo)
{
    struct priv *p = vo->priv;
    p->sws = mp_sws_alloc(vo);
    p->fd = -1;
    p->ev.version = DRM_EVENT_CONTEXT_VERSION;
    p->ev.page_flip_handler = modeset_page_flipped;

    if (vt_switcher_init(&p->vt_switcher, vo->log))
        goto err;

    vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo);
    vt_switcher_release(&p->vt_switcher, release_vt, vo);

    if (modeset_open(vo, &p->fd, p->device_path))
        goto err;

    if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev))
        goto err;

    assert(p->dev);
    p->device_w = p->dev->bufs[0].width;
    p->device_h = p->dev->bufs[0].height;

    if (setup_vo_crtc(vo)) {
        MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
               mp_strerror(errno));
        goto err;
    }

    return 0;

err:
    uninit(vo);
    return -1;
}

static int query_format(struct vo *vo, int format)
{
    return sws_isSupportedInput(imgfmt2pixfmt(format));
}

static int control(struct vo *vo, uint32_t request, void *data)
{
    struct priv *p = vo->priv;
    switch (request) {
    case VOCTRL_SCREENSHOT_WIN:
        *(struct mp_image**)data = mp_image_new_copy(p->cur_frame);
        return VO_TRUE;
    case VOCTRL_REDRAW_FRAME:
        draw_image(vo, p->last_input);
        return VO_TRUE;
    case VOCTRL_GET_PANSCAN:
        return VO_TRUE;
    case VOCTRL_SET_PANSCAN:
        if (vo->config_ok)
            reconfig(vo, vo->params, 0);
        return VO_TRUE;
    }
    return VO_NOTIMPL;
}

#define OPT_BASE_STRUCT struct priv

const struct vo_driver video_out_drm = {
    .name = "drm",
    .description = "Direct Rendering Manager",
    .preinit = preinit,
    .query_format = query_format,
    .reconfig = reconfig,
    .control = control,
    .draw_image = draw_image,
    .flip_page = flip_page,
    .uninit = uninit,
    .wait_events = wait_events,
    .wakeup = wakeup,
    .priv_size = sizeof(struct priv),
    .options = (const struct m_option[]) {
        OPT_STRING("devpath", device_path, 0),
        OPT_INT("connector", connector_id, 0),
        {0},
    },
    .priv_defaults = &(const struct priv) {
예제 #20
0
파일: ao_sdl.c 프로젝트: ArcherSeven/mpv
static int init(struct ao *ao)
{
    if (SDL_WasInit(SDL_INIT_AUDIO)) {
        mp_msg(MSGT_AO, MSGL_ERR, "[sdl] already initialized\n");
        return -1;
    }

    struct priv *priv = ao->priv;

    if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
        if (!ao->probing)
            mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_Init failed\n");
        uninit(ao, true);
        return -1;
    }

    struct mp_chmap_sel sel = {0};
    mp_chmap_sel_add_waveext_def(&sel);
    if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
        uninit(ao, true);
        return -1;
    }

    SDL_AudioSpec desired, obtained;

    switch (ao->format) {
        case AF_FORMAT_U8: desired.format = AUDIO_U8; break;
        case AF_FORMAT_S8: desired.format = AUDIO_S8; break;
        case AF_FORMAT_U16_LE: desired.format = AUDIO_U16LSB; break;
        case AF_FORMAT_U16_BE: desired.format = AUDIO_U16MSB; break;
        default:
        case AF_FORMAT_S16_LE: desired.format = AUDIO_S16LSB; break;
        case AF_FORMAT_S16_BE: desired.format = AUDIO_S16MSB; break;
#ifdef AUDIO_S32LSB
        case AF_FORMAT_S32_LE: desired.format = AUDIO_S32LSB; break;
#endif
#ifdef AUDIO_S32MSB
        case AF_FORMAT_S32_BE: desired.format = AUDIO_S32MSB; break;
#endif
#ifdef AUDIO_F32LSB
        case AF_FORMAT_FLOAT_LE: desired.format = AUDIO_F32LSB; break;
#endif
#ifdef AUDIO_F32MSB
        case AF_FORMAT_FLOAT_BE: desired.format = AUDIO_F32MSB; break;
#endif
    }
    desired.freq = ao->samplerate;
    desired.channels = ao->channels.num;
    desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate *
                                                     priv->buflen));
    desired.callback = audio_callback;
    desired.userdata = ao;

    mp_msg(MSGT_AO, MSGL_V, "[sdl] requested format: %d Hz, %d channels, %x, "
           "buffer size: %d samples\n",
           (int) desired.freq, (int) desired.channels,
           (int) desired.format, (int) desired.samples);

    obtained = desired;
    if (SDL_OpenAudio(&desired, &obtained)) {
        if (!ao->probing)
            mp_msg(MSGT_AO, MSGL_ERR, "[sdl] could not open audio: %s\n",
                SDL_GetError());
        uninit(ao, true);
        return -1;
    }

    mp_msg(MSGT_AO, MSGL_V, "[sdl] obtained format: %d Hz, %d channels, %x, "
           "buffer size: %d samples\n",
           (int) obtained.freq, (int) obtained.channels,
           (int) obtained.format, (int) obtained.samples);

    switch (obtained.format) {
        case AUDIO_U8: ao->format = AF_FORMAT_U8; break;
        case AUDIO_S8: ao->format = AF_FORMAT_S8; break;
        case AUDIO_S16LSB: ao->format = AF_FORMAT_S16_LE; break;
        case AUDIO_S16MSB: ao->format = AF_FORMAT_S16_BE; break;
        case AUDIO_U16LSB: ao->format = AF_FORMAT_U16_LE; break;
        case AUDIO_U16MSB: ao->format = AF_FORMAT_U16_BE; break;
#ifdef AUDIO_S32LSB
        case AUDIO_S32LSB: ao->format = AF_FORMAT_S32_LE; break;
#endif
#ifdef AUDIO_S32MSB
        case AUDIO_S32MSB: ao->format = AF_FORMAT_S32_BE; break;
#endif
#ifdef AUDIO_F32LSB
        case AUDIO_F32LSB: ao->format = AF_FORMAT_FLOAT_LE; break;
#endif
#ifdef AUDIO_F32MSB
        case AUDIO_F32MSB: ao->format = AF_FORMAT_FLOAT_BE; break;
#endif
        default:
            if (!ao->probing)
                mp_msg(MSGT_AO, MSGL_ERR,
                       "[sdl] could not find matching format\n");
            uninit(ao, true);
            return -1;
    }

    if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
        uninit(ao, true);
        return -1;
    }

    ao->samplerate = obtained.freq;
    priv->buffer = av_fifo_alloc(obtained.size * priv->bufcnt);
    priv->buffer_mutex = SDL_CreateMutex();
    if (!priv->buffer_mutex) {
        mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateMutex failed\n");
        uninit(ao, true);
        return -1;
    }
    priv->underrun_cond = SDL_CreateCond();
    if (!priv->underrun_cond) {
        mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateCond failed\n");
        uninit(ao, true);
        return -1;
    }

    priv->unpause = 1;
    priv->paused = 1;
    priv->callback_time0 = priv->callback_time1 = mp_time_us();

    return 1;
}
예제 #21
0
파일: tv.c 프로젝트: candux/mpv
static void tv_scan(tvi_handle_t *tvh)
{
    unsigned int now;
    struct CHANLIST cl;
    tv_channels_t *tv_channel_tmp=NULL;
    tv_channels_t *tv_channel_add=NULL;
    tv_scan_t* scan;
    int found=0, index=1;

    //Channel scanner without tuner is useless and causes crash due to uninitialized chanlist_s
    if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) != TVI_CONTROL_TRUE)
    {
        MP_WARN(tvh, "Channel scanner is not available without tuner\n");
        tvh->tv_param->scan=0;
        return;
    }

    scan = tvh->scan;
    now=(unsigned int)mp_time_us();
    if (!scan) {
        scan=calloc(1,sizeof(tv_scan_t));
        tvh->scan=scan;
        cl = tvh->chanlist_s[scan->channel_num];
        tv_set_freq_float(tvh, cl.freq);
        scan->scan_timer=now+1e6*tvh->tv_param->scan_period;
    }
    if(scan->scan_timer>now)
        return;

    if (tv_get_signal(tvh)>tvh->tv_param->scan_threshold) {
        cl = tvh->chanlist_s[scan->channel_num];
        tv_channel_tmp=tv_channel_list;
        while (tv_channel_tmp) {
            index++;
            if (cl.freq==tv_channel_tmp->freq){
                found=1;
                break;
            }
            tv_channel_add=tv_channel_tmp;
            tv_channel_tmp=tv_channel_tmp->next;
        }
        if (!found) {
            MP_INFO(tvh, "Found new channel: %s (#%d). \n",cl.name,index);
            scan->new_channels++;
            tv_channel_tmp = malloc(sizeof(tv_channels_t));
            tv_channel_tmp->index=index;
            tv_channel_tmp->next=NULL;
            tv_channel_tmp->prev=tv_channel_add;
            tv_channel_tmp->freq=cl.freq;
            snprintf(tv_channel_tmp->name,sizeof(tv_channel_tmp->name),"ch%d",index);
            strncpy(tv_channel_tmp->number, cl.name, 5);
            tv_channel_tmp->number[4]='\0';
            if (!tv_channel_list)
                tv_channel_list=tv_channel_tmp;
            else {
                tv_channel_add->next=tv_channel_tmp;
                tv_channel_list->prev=tv_channel_tmp;
            }
        }else
            MP_INFO(tvh, "Found existing channel: %s-%s.\n",
                tv_channel_tmp->number,tv_channel_tmp->name);
    }
    scan->channel_num++;
    scan->scan_timer=now+1e6*tvh->tv_param->scan_period;
    if (scan->channel_num>=chanlists[tvh->chanlist].count) {
        tvh->tv_param->scan=0;
        MP_INFO(tvh, "TV scan end. Found %d new channels.\n", scan->new_channels);
        tv_channel_tmp=tv_channel_list;
        if(tv_channel_tmp){
            MP_INFO(tvh, "channels=");
            while(tv_channel_tmp){
                MP_INFO(tvh, "%s-%s",tv_channel_tmp->number,tv_channel_tmp->name);
                if(tv_channel_tmp->next)
                    MP_INFO(tvh, ",");
                tv_channel_tmp=tv_channel_tmp->next;
            }
            MP_INFO(tvh, "\n");
        }
        if (!tv_channel_current) tv_channel_current=tv_channel_list;
        if (tv_channel_current)
            tv_set_freq_float(tvh, tv_channel_current->freq);
        free(tvh->scan);
        tvh->scan=NULL;
    }else{
        cl = tvh->chanlist_s[scan->channel_num];
        tv_set_freq_float(tvh, cl.freq);
        MP_INFO(tvh, "Trying: %s (%.2f). \n",cl.name,1e-3*cl.freq);
    }
}
예제 #22
0
파일: video.c 프로젝트: jeremiejig/mpv
void write_video(struct MPContext *mpctx, double endpts)
{
    struct MPOpts *opts = mpctx->opts;
    struct vo *vo = mpctx->video_out;

    if (!mpctx->d_video)
        return;

    update_fps(mpctx);

    // Whether there's still at least 1 video frame that can be shown.
    // If false, it means we can reconfig the VO if needed (normally, this
    // would disrupt playback, so only do it on !still_playing).
    bool still_playing = vo_has_next_frame(vo, true);
    // For the last frame case (frame is being displayed).
    still_playing |= mpctx->playing_last_frame;
    still_playing |= mpctx->last_frame_duration > 0;

    double frame_time = 0;
    int r = update_video(mpctx, endpts, !still_playing, &frame_time);
    MP_TRACE(mpctx, "update_video: %d (still_playing=%d)\n", r, still_playing);

    if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode.
        return;

    if (r < 0) {
        MP_FATAL(mpctx, "Could not initialize video chain.\n");
        int uninit = INITIALIZED_VCODEC;
        if (!opts->force_vo)
            uninit |= INITIALIZED_VO;
        uninit_player(mpctx, uninit);
        if (!mpctx->current_track[STREAM_AUDIO])
            mpctx->stop_play = PT_NEXT_ENTRY;
        mpctx->error_playing = true;
        handle_force_window(mpctx, true);
        return; // restart loop
    }

    if (r == VD_EOF) {
        if (!mpctx->playing_last_frame && mpctx->last_frame_duration > 0) {
            mpctx->time_frame += mpctx->last_frame_duration;
            mpctx->last_frame_duration = 0;
            mpctx->playing_last_frame = true;
            MP_VERBOSE(mpctx, "showing last frame\n");
        }
    }

    if (r == VD_NEW_FRAME) {
        MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);

        if (mpctx->video_status > STATUS_PLAYING)
            mpctx->video_status = STATUS_PLAYING;

        if (mpctx->video_status >= STATUS_READY) {
            mpctx->time_frame += frame_time / opts->playback_speed;
            adjust_sync(mpctx, frame_time);
        }
    } else if (r == VD_EOF && mpctx->playing_last_frame) {
        // Let video timing code continue displaying.
        mpctx->video_status = STATUS_DRAINING;
        MP_VERBOSE(mpctx, "still showing last frame\n");
    } else if (r <= 0) {
        // EOF or error
        mpctx->delay = 0;
        mpctx->last_av_difference = 0;
        mpctx->video_status = STATUS_EOF;
        MP_VERBOSE(mpctx, "video EOF\n");
        return;
    } else {
        if (mpctx->video_status > STATUS_PLAYING)
            mpctx->video_status = STATUS_PLAYING;

        // Decode more in next iteration.
        mpctx->sleeptime = 0;
        MP_TRACE(mpctx, "filtering more video\n");
    }

    // Actual playback starts when both audio and video are ready.
    if (mpctx->video_status == STATUS_READY)
        return;

    if (mpctx->paused && mpctx->video_status >= STATUS_READY)
        return;

    mpctx->time_frame -= get_relative_time(mpctx);
    double audio_pts = playing_audio_pts(mpctx);
    if (!mpctx->sync_audio_to_video || mpctx->video_status < STATUS_READY) {
        mpctx->time_frame = 0;
    } else if (mpctx->audio_status == STATUS_PLAYING &&
               mpctx->video_status == STATUS_PLAYING)
    {
        double buffered_audio = ao_get_delay(mpctx->ao);
        MP_TRACE(mpctx, "audio delay=%f\n", buffered_audio);

        if (opts->autosync) {
            /* Smooth reported playback position from AO by averaging
             * it with the value expected based on previus value and
             * time elapsed since then. May help smooth video timing
             * with audio output that have inaccurate position reporting.
             * This is badly implemented; the behavior of the smoothing
             * now undesirably depends on how often this code runs
             * (mainly depends on video frame rate). */
            float predicted = (mpctx->delay / opts->playback_speed +
                                mpctx->time_frame);
            float difference = buffered_audio - predicted;
            buffered_audio = predicted + difference / opts->autosync;
        }

        mpctx->time_frame = (buffered_audio -
                                mpctx->delay / opts->playback_speed);
    } else {
        /* If we're more than 200 ms behind the right playback
         * position, don't try to speed up display of following
         * frames to catch up; continue with default speed from
         * the current frame instead.
         * If untimed is set always output frames immediately
         * without sleeping.
         */
        if (mpctx->time_frame < -0.2 || opts->untimed || vo->untimed)
            mpctx->time_frame = 0;
    }

    double vsleep = mpctx->time_frame - vo->flip_queue_offset;
    if (vsleep > 0.050) {
        mpctx->sleeptime = MPMIN(mpctx->sleeptime, vsleep - 0.040);
        return;
    }
    mpctx->sleeptime = 0;
    mpctx->playing_last_frame = false;

    // last frame case
    if (r != VD_NEW_FRAME)
        return;

    //=================== FLIP PAGE (VIDEO BLT): ======================


    mpctx->video_pts = mpctx->video_next_pts;
    mpctx->last_vo_pts = mpctx->video_pts;
    mpctx->playback_pts = mpctx->video_pts;

    update_subtitles(mpctx);
    update_osd_msg(mpctx);

    MP_STATS(mpctx, "vo draw frame");

    vo_new_frame_imminent(vo);

    MP_STATS(mpctx, "vo sleep");

    mpctx->time_frame -= get_relative_time(mpctx);
    mpctx->time_frame -= vo->flip_queue_offset;
    if (mpctx->time_frame > 0.001)
        mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame);
    mpctx->time_frame += vo->flip_queue_offset;

    int64_t t2 = mp_time_us();
    /* Playing with playback speed it's possible to get pathological
     * cases with mpctx->time_frame negative enough to cause an
     * overflow in pts_us calculation, thus the MPMAX. */
    double time_frame = MPMAX(mpctx->time_frame, -1);
    int64_t pts_us = mpctx->last_time + time_frame * 1e6;
    int duration = -1;
    double pts2 = vo_get_next_pts(vo, 0); // this is the next frame PTS
    if (mpctx->video_pts != MP_NOPTS_VALUE && pts2 == MP_NOPTS_VALUE) {
        // Make up a frame duration. Using the frame rate is not a good
        // choice, since the frame rate could be unset/broken/random.
        float fps = mpctx->d_video->fps;
        double frame_duration = fps > 0 ? 1.0 / fps : 0;
        pts2 = mpctx->video_pts + MPCLAMP(frame_duration, 0.0, 5.0);
    }
    if (pts2 != MP_NOPTS_VALUE) {
        // expected A/V sync correction is ignored
        double diff = (pts2 - mpctx->video_pts);
        diff /= opts->playback_speed;
        if (mpctx->time_frame < 0)
            diff += mpctx->time_frame;
        if (diff < 0)
            diff = 0;
        if (diff > 10)
            diff = 10;
        duration = diff * 1e6;
        mpctx->last_frame_duration = diff;
    }
    if (mpctx->video_status != STATUS_PLAYING)
        duration = -1;

    MP_STATS(mpctx, "start flip");
    vo_flip_page(vo, pts_us | 1, duration);
    MP_STATS(mpctx, "end flip");

    if (audio_pts != MP_NOPTS_VALUE)
        MP_STATS(mpctx, "value %f ptsdiff", mpctx->video_pts - audio_pts);

    mpctx->last_vo_flip_duration = (mp_time_us() - t2) * 0.000001;
    if (vo->driver->flip_page_timed) {
        // No need to adjust sync based on flip speed
        mpctx->last_vo_flip_duration = 0;
        // For print_status - VO call finishing early is OK for sync
        mpctx->time_frame -= get_relative_time(mpctx);
    }
    mpctx->shown_vframes++;
    if (mpctx->video_status < STATUS_PLAYING)
        mpctx->video_status = STATUS_READY;
    update_avsync(mpctx);
    screenshot_flip(mpctx);

    mp_notify(mpctx, MPV_EVENT_TICK, NULL);

    if (!mpctx->sync_audio_to_video)
        mpctx->video_status = STATUS_EOF;
}
예제 #23
0
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
{
    assert(ctx->renderer);

    gl_video_set_gl_state(ctx->renderer);

    pthread_mutex_lock(&ctx->lock);

    struct vo *vo = ctx->active;

    ctx->force_update |= ctx->reconfigured;
    ctx->frozen = false;

    if (ctx->vp_w != vp_w || ctx->vp_h != vp_h)
        ctx->force_update = true;

    if (ctx->force_update && vo) {
        ctx->force_update = false;
        ctx->vp_w = vp_w;
        ctx->vp_h = vp_h;

        struct mp_rect src, dst;
        struct mp_osd_res osd;
        mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps,
                             &ctx->img_params, vp_w, abs(vp_h),
                             1.0, &src, &dst, &osd);

        gl_video_resize(ctx->renderer, vp_w, vp_h, &src, &dst, &osd);
    }

    if (ctx->reconfigured) {
        gl_video_set_osd_source(ctx->renderer, vo ? vo->osd : NULL);
        gl_video_config(ctx->renderer, &ctx->img_params);
    }
    if (ctx->update_new_opts) {
        struct vo_priv *p = vo ? vo->priv : NULL;
        struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p;
        if (opts) {
            gl_video_set_options(ctx->renderer, opts->renderer_opts);
            gl_video_configure_queue(ctx->renderer, vo);
            ctx->gl->debug_context = opts->use_gl_debug;
            gl_video_set_debug(ctx->renderer, opts->use_gl_debug);
            frame_queue_shrink(ctx, opts->frame_queue_size);
        }
    }
    ctx->reconfigured = false;
    ctx->update_new_opts = false;

    struct mp_csp_equalizer *eq = gl_video_eq_ptr(ctx->renderer);
    if (ctx->eq_changed) {
        memcpy(eq->values, ctx->eq.values, sizeof(eq->values));
        gl_video_eq_update(ctx->renderer);
    }
    ctx->eq_changed = false;
    ctx->eq = *eq;

    struct vo_frame *frame = frame_queue_pop(ctx);
    if (frame) {
        talloc_free(ctx->cur_frame);
        ctx->cur_frame = vo_frame_ref(frame);
    } else {
        frame = vo_frame_ref(ctx->cur_frame);
    }
    struct vo_frame dummy = {0};
    if (!frame)
        frame = &dummy;

    if (ctx->approx_vsync > 0) {
        frame->prev_vsync = prev_sync(ctx, mp_time_us());
        frame->next_vsync = frame->prev_vsync + ctx->approx_vsync;
    }

    pthread_mutex_unlock(&ctx->lock);

    gl_video_render_frame(ctx->renderer, frame, fbo);

    gl_video_unset_gl_state(ctx->renderer);

    if (frame != &dummy)
        talloc_free(frame);

    pthread_mutex_lock(&ctx->lock);
    const int left = ctx->queued_frames;
    if (vo && left > 0)
        update(vo->priv);
    pthread_mutex_unlock(&ctx->lock);

    return left;
}
예제 #24
0
파일: timer.c 프로젝트: ThreeGe/mpv
double mp_time_sec(void)
{
    return mp_time_us() / (double)(1000 * 1000);
}
예제 #25
0
파일: video.c 프로젝트: bagobor/mpv
void write_video(struct MPContext *mpctx, double endpts)
{
    struct MPOpts *opts = mpctx->opts;
    struct vo *vo = mpctx->video_out;

    if (!mpctx->d_video)
        return;

    // Actual playback starts when both audio and video are ready.
    if (mpctx->video_status == STATUS_READY)
        return;

    if (mpctx->paused && mpctx->video_status >= STATUS_READY)
        return;

    update_fps(mpctx);

    int r = video_output_image(mpctx, endpts);
    MP_TRACE(mpctx, "video_output_image: %d\n", r);

    if (r < 0)
        goto error;

    if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode.
        return;

    if (r == VD_EOF) {
        mpctx->video_status =
            vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
        mpctx->delay = 0;
        mpctx->last_av_difference = 0;
        MP_VERBOSE(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
        return;
    }

    if (mpctx->video_status > STATUS_PLAYING)
        mpctx->video_status = STATUS_PLAYING;

    mpctx->time_frame -= get_relative_time(mpctx);
    update_avsync_before_frame(mpctx);

    if (r != VD_NEW_FRAME) {
        mpctx->sleeptime = 0; // Decode more in next iteration.
        return;
    }

    // Filter output is different from VO input?
    struct mp_image_params p = mpctx->next_frame[0]->params;
    if (!vo->params || !mp_image_params_equal(&p, vo->params)) {
        // Changing config deletes the current frame; wait until it's finished.
        if (vo_still_displaying(vo))
            return;

        const struct vo_driver *info = mpctx->video_out->driver;
        MP_INFO(mpctx, "VO: [%s] %dx%d => %dx%d %s\n",
                info->name, p.w, p.h, p.d_w, p.d_h, vo_format_name(p.imgfmt));
        MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description);

        int vo_r = vo_reconfig(vo, &p, 0);
        if (vo_r < 0)
            goto error;
        init_vo(mpctx);
        mpctx->time_frame = 0; // display immediately
    }

    double time_frame = MPMAX(mpctx->time_frame, -1);
    int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);

    if (!vo_is_ready_for_frame(vo, pts))
        return; // wait until VO wakes us up to get more frames

    int64_t duration = -1;
    double diff = -1;
    double vpts0 = mpctx->next_frame[0] ? mpctx->next_frame[0]->pts : MP_NOPTS_VALUE;
    double vpts1 = mpctx->next_frame[1] ? mpctx->next_frame[1]->pts : MP_NOPTS_VALUE;
    if (vpts0 != MP_NOPTS_VALUE && vpts1 != MP_NOPTS_VALUE)
        diff = vpts1 - vpts0;
    if (diff < 0 && mpctx->d_video->fps > 0)
        diff = 1.0 / mpctx->d_video->fps; // fallback to demuxer-reported fps
    if (diff >= 0) {
        // expected A/V sync correction is ignored
        diff /= opts->playback_speed;
        if (mpctx->time_frame < 0)
            diff += mpctx->time_frame;
        duration = MPCLAMP(diff, 0, 10) * 1e6;
    }

    mpctx->video_pts = mpctx->next_frame[0]->pts;
    mpctx->last_vo_pts = mpctx->video_pts;
    mpctx->playback_pts = mpctx->video_pts;

    mpctx->osd_force_update = true;
    update_osd_msg(mpctx);
    update_subtitles(mpctx);

    vo_queue_frame(vo, mpctx->next_frame[0], pts, duration);
    mpctx->next_frame[0] = NULL;

    mpctx->shown_vframes++;
    if (mpctx->video_status < STATUS_PLAYING) {
        mpctx->video_status = STATUS_READY;
        // After a seek, make sure to wait until the first frame is visible.
        vo_wait_frame(vo);
    }
    update_avsync_after_frame(mpctx);
    screenshot_flip(mpctx);

    mp_notify(mpctx, MPV_EVENT_TICK, NULL);

    if (!mpctx->sync_audio_to_video)
        mpctx->video_status = STATUS_EOF;

    if (mpctx->video_status != STATUS_EOF) {
        if (mpctx->step_frames > 0) {
            mpctx->step_frames--;
            if (!mpctx->step_frames && !opts->pause)
                pause_player(mpctx);
        }
        if (mpctx->max_frames == 0)
            mpctx->stop_play = PT_NEXT_ENTRY;
        if (mpctx->max_frames > 0)
            mpctx->max_frames--;
    }

    mpctx->sleeptime = 0;
    return;

error:
    MP_FATAL(mpctx, "Could not initialize video chain.\n");
    int uninit = INITIALIZED_VCODEC;
    if (!opts->force_vo)
        uninit |= INITIALIZED_VO;
    uninit_player(mpctx, uninit);
    if (!mpctx->current_track[STREAM_AUDIO])
        mpctx->stop_play = PT_NEXT_ENTRY;
    mpctx->error_playing = true;
    handle_force_window(mpctx, true);
    mpctx->sleeptime = 0;
}
예제 #26
0
파일: timer.c 프로젝트: ThreeGe/mpv
struct timespec mp_rel_time_to_timespec(double timeout_sec)
{
    return mp_time_us_to_timespec(mp_add_timeout(mp_time_us(), timeout_sec));
}
예제 #27
0
파일: video.c 프로젝트: Jim-Duke/mpv
void write_video(struct MPContext *mpctx, double endpts)
{
    struct MPOpts *opts = mpctx->opts;
    struct vo *vo = mpctx->video_out;

    if (!mpctx->d_video)
        return;

    // Actual playback starts when both audio and video are ready.
    if (mpctx->video_status == STATUS_READY)
        return;

    if (mpctx->paused && mpctx->video_status >= STATUS_READY)
        return;

    int r = video_output_image(mpctx, endpts);
    MP_TRACE(mpctx, "video_output_image: %d\n", r);

    if (r < 0)
        goto error;

    if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode.
        return;

    if (r == VD_EOF) {
        mpctx->video_status =
            vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
        mpctx->delay = 0;
        mpctx->last_av_difference = 0;
        MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
        return;
    }

    if (mpctx->video_status > STATUS_PLAYING)
        mpctx->video_status = STATUS_PLAYING;

    if (r != VD_NEW_FRAME) {
        mpctx->sleeptime = 0; // Decode more in next iteration.
        return;
    }

    // Filter output is different from VO input?
    struct mp_image_params p = mpctx->next_frames[0]->params;
    if (!vo->params || !mp_image_params_equal(&p, vo->params)) {
        // Changing config deletes the current frame; wait until it's finished.
        if (vo_still_displaying(vo))
            return;

        const struct vo_driver *info = mpctx->video_out->driver;
        char extra[20] = {0};
        if (p.w != p.d_w || p.h != p.d_h)
            snprintf(extra, sizeof(extra), " => %dx%d", p.d_w, p.d_h);
        MP_INFO(mpctx, "VO: [%s] %dx%d%s %s\n",
                info->name, p.w, p.h, extra, vo_format_name(p.imgfmt));
        MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description);

        int vo_r = vo_reconfig(vo, &p, 0);
        if (vo_r < 0) {
            mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED;
            goto error;
        }
        init_vo(mpctx);
    }

    mpctx->time_frame -= get_relative_time(mpctx);
    update_avsync_before_frame(mpctx);

    double time_frame = MPMAX(mpctx->time_frame, -1);
    int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);

    // wait until VO wakes us up to get more frames
    if (!vo_is_ready_for_frame(vo, pts)) {
        if (video_feed_async_filter(mpctx) < 0)
            goto error;
        return;
    }

    assert(mpctx->num_next_frames >= 1);
    struct vo_frame dummy = {
        .pts = pts,
        .duration = -1,
        .num_frames = mpctx->num_next_frames,
    };
    for (int n = 0; n < dummy.num_frames; n++)
        dummy.frames[n] = mpctx->next_frames[n];
    struct vo_frame *frame = vo_frame_ref(&dummy);

    double diff = -1;
    double vpts0 = mpctx->next_frames[0]->pts;
    double vpts1 = MP_NOPTS_VALUE;
    if (mpctx->num_next_frames >= 2)
        vpts1 = mpctx->next_frames[1]->pts;
    if (vpts0 != MP_NOPTS_VALUE && vpts1 != MP_NOPTS_VALUE)
        diff = vpts1 - vpts0;
    if (diff < 0 && mpctx->d_video->fps > 0)
        diff = 1.0 / mpctx->d_video->fps; // fallback to demuxer-reported fps
    if (opts->untimed || vo->driver->untimed)
        diff = -1; // disable frame dropping and aspects of frame timing
    if (diff >= 0) {
        // expected A/V sync correction is ignored
        diff /= opts->playback_speed;
        if (mpctx->time_frame < 0)
            diff += mpctx->time_frame;
        frame->duration = MPCLAMP(diff, 0, 10) * 1e6;
    }

    mpctx->video_pts = mpctx->next_frames[0]->pts;
    mpctx->last_vo_pts = mpctx->video_pts;
    mpctx->playback_pts = mpctx->video_pts;

    update_avsync_after_frame(mpctx);

    mpctx->osd_force_update = true;
    update_osd_msg(mpctx);
    update_subtitles(mpctx);

    vo_queue_frame(vo, frame);

    shift_frames(mpctx);

    // The frames were shifted down; "initialize" the new first entry.
    if (mpctx->num_next_frames >= 1)
        handle_new_frame(mpctx);

    mpctx->shown_vframes++;
    if (mpctx->video_status < STATUS_PLAYING) {
        mpctx->video_status = STATUS_READY;
        // After a seek, make sure to wait until the first frame is visible.
        vo_wait_frame(vo);
        MP_VERBOSE(mpctx, "first video frame after restart shown\n");
    }
    screenshot_flip(mpctx);

    mp_notify(mpctx, MPV_EVENT_TICK, NULL);

    if (!mpctx->sync_audio_to_video)
        mpctx->video_status = STATUS_EOF;

    if (mpctx->video_status != STATUS_EOF) {
        if (mpctx->step_frames > 0) {
            mpctx->step_frames--;
            if (!mpctx->step_frames && !opts->pause)
                pause_player(mpctx);
        }
        if (mpctx->max_frames == 0 && !mpctx->stop_play)
            mpctx->stop_play = AT_END_OF_FILE;
        if (mpctx->max_frames > 0)
            mpctx->max_frames--;
    }

    mpctx->sleeptime = 0;
    return;

error:
    MP_FATAL(mpctx, "Could not initialize video chain.\n");
    uninit_video_chain(mpctx);
    error_on_track(mpctx, mpctx->current_track[STREAM_VIDEO][0]);
    handle_force_window(mpctx, true);
    mpctx->sleeptime = 0;
}
예제 #28
0
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
{
    assert(ctx->renderer);

    gl_video_set_gl_state(ctx->renderer);

    pthread_mutex_lock(&ctx->lock);

    struct vo *vo = ctx->active;

    ctx->force_update |= ctx->reconfigured;

    if (ctx->vp_w != vp_w || ctx->vp_h != vp_h)
        ctx->force_update = true;

    if (ctx->force_update && vo) {
        ctx->force_update = false;
        ctx->vp_w = vp_w;
        ctx->vp_h = vp_h;

        struct mp_rect src, dst;
        struct mp_osd_res osd;
        mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps,
                             &ctx->img_params, vp_w, abs(vp_h),
                             1.0, &src, &dst, &osd);

        gl_video_resize(ctx->renderer, vp_w, vp_h, &src, &dst, &osd);
    }

    if (ctx->reconfigured) {
        gl_video_set_osd_source(ctx->renderer, vo ? vo->osd : NULL);
        gl_video_config(ctx->renderer, &ctx->img_params);
    }
    if (ctx->update_new_opts) {
        struct vo_priv *p = vo ? vo->priv : NULL;
        struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p;
        if (opts) {
            int queue = 0;
            gl_video_set_options(ctx->renderer, opts->renderer_opts, &queue);
            ctx->vsync_timed = opts->renderer_opts->interpolation;
            if (ctx->vsync_timed)
                queue += 0.050 * 1e6; // disable video timing
            vo_set_flip_queue_params(vo, queue, false);
            ctx->gl->debug_context = opts->use_gl_debug;
            gl_video_set_debug(ctx->renderer, opts->use_gl_debug);
            frame_queue_shrink(ctx, opts->frame_queue_size);
        }
    }
    ctx->reconfigured = false;
    ctx->update_new_opts = false;

    struct mp_csp_equalizer *eq = gl_video_eq_ptr(ctx->renderer);
    if (ctx->eq_changed) {
        memcpy(eq->values, ctx->eq.values, sizeof(eq->values));
        gl_video_eq_update(ctx->renderer);
    }
    ctx->eq_changed = false;
    ctx->eq = *eq;

    struct mp_image *mpi = frame_queue_pop(ctx);
    if (mpi) {
        struct frame_timing *t = mpi->priv; // set by draw_image_timed
        if (t)
            ctx->cur_pts = t->pts;
    }

    struct frame_timing timing = {
        .pts = ctx->cur_pts,
    };
    if (ctx->approx_vsync > 0) {
        timing.prev_vsync = prev_sync(ctx, mp_time_us());
        timing.next_vsync = timing.prev_vsync + ctx->approx_vsync;
    }

    pthread_mutex_unlock(&ctx->lock);

    if (mpi)
        gl_video_set_image(ctx->renderer, mpi);

    gl_video_render_frame(ctx->renderer, fbo, timing.pts ? &timing : NULL);

    gl_video_unset_gl_state(ctx->renderer);

    pthread_mutex_lock(&ctx->lock);
    const int left = ctx->queued_frames;
    if (vo && (left > 0 || ctx->vsync_timed))
        update(vo->priv);
    pthread_mutex_unlock(&ctx->lock);

    return left;
}

int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
{
    pthread_mutex_lock(&ctx->lock);
    int64_t next = time > 0 ? time : mp_time_us();
    if (ctx->recent_flip)
        ctx->approx_vsync = next - ctx->recent_flip;
    ctx->recent_flip = next;
    pthread_mutex_unlock(&ctx->lock);

    return 0;
}