📄 ffplay.c
字号:
h = screen_height; }else if (is->video_st && is->video_st->codec->width){ w = is->video_st->codec->width; h = is->video_st->codec->height; } else { w = 640; h = 480; }#ifndef __APPLE__ screen = SDL_SetVideoMode(w, h, 0, flags);#else /* setting bits_per_pixel = 0 or 32 causes blank video on OS X */ screen = SDL_SetVideoMode(w, h, 24, flags);#endif if (!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); return -1; } SDL_WM_SetCaption("FFplay", "FFplay"); is->width = screen->w; is->height = screen->h; return 0;}/* display the current picture, if any */static void video_display(VideoState *is){ if(!screen) video_open(cur_stream); if (is->audio_st && is->show_audio) video_audio_display(is); else if (is->video_st) video_image_display(is);}static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque){ SDL_Event event; event.type = FF_REFRESH_EVENT; event.user.data1 = opaque; SDL_PushEvent(&event); return 0; /* 0 means stop timer */}/* schedule a video refresh in 'delay' ms */static void schedule_refresh(VideoState *is, int delay){ SDL_AddTimer(delay, sdl_refresh_timer_cb, is);}/* get the current audio clock value */static double get_audio_clock(VideoState *is){ double pts; int hw_buf_size, bytes_per_sec; pts = is->audio_clock; hw_buf_size = audio_write_get_buf_size(is); bytes_per_sec = 0; if (is->audio_st) { bytes_per_sec = is->audio_st->codec->sample_rate * 2 * is->audio_st->codec->channels; } if (bytes_per_sec) pts -= (double)hw_buf_size / bytes_per_sec; return pts;}/* get the current video clock value */static double get_video_clock(VideoState *is){ double delta; if (is->paused) { delta = 0; } else { delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; } return is->video_current_pts + delta;}/* get the current external clock value */static double get_external_clock(VideoState *is){ int64_t ti; ti = av_gettime(); return is->external_clock + ((ti - is->external_clock_time) * 1e-6);}/* get the current master clock value */static double get_master_clock(VideoState *is){ double val; if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) { if (is->video_st) val = get_video_clock(is); else val = get_audio_clock(is); } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) { if (is->audio_st) val = get_audio_clock(is); else val = get_video_clock(is); } else { val = get_external_clock(is); } return val;}/* seek in the stream */static void stream_seek(VideoState *is, int64_t pos, int rel){ if (!is->seek_req) { is->seek_pos = pos; is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0; if (seek_by_bytes) is->seek_flags |= AVSEEK_FLAG_BYTE; is->seek_req = 1; }}/* pause or resume the video */static void stream_pause(VideoState *is){ is->paused = !is->paused; if (!is->paused) { is->video_current_pts = get_video_clock(is); is->frame_timer += (av_gettime() - is->video_current_pts_time) / 1000000.0; }}/* called to display each frame */static void video_refresh_timer(void *opaque){ VideoState *is = opaque; VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; SubPicture *sp, *sp2; if (is->video_st) { if (is->pictq_size == 0) { /* if no picture, need to wait */ schedule_refresh(is, 1); } else { /* dequeue the picture */ vp = &is->pictq[is->pictq_rindex]; /* update current video pts */ is->video_current_pts = vp->pts; is->video_current_pts_time = av_gettime(); /* compute nominal delay */ delay = vp->pts - is->frame_last_pts; if (delay <= 0 || delay >= 2.0) { /* if incorrect delay, use previous one */ delay = is->frame_last_delay; } is->frame_last_delay = delay; is->frame_last_pts = vp->pts; /* update delay to follow master synchronisation source */ if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) || is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) { /* if video is slave, we try to correct big delays by duplicating or deleting a frame */ ref_clock = get_master_clock(is); diff = vp->pts - ref_clock; /* skip or repeat frame. We take into account the delay to compute the threshold. I still don't know if it is the best guess */ sync_threshold = AV_SYNC_THRESHOLD; if (delay > sync_threshold) sync_threshold = delay; if (fabs(diff) < AV_NOSYNC_THRESHOLD) { if (diff <= -sync_threshold) delay = 0; else if (diff >= sync_threshold) delay = 2 * delay; } } is->frame_timer += delay; /* compute the REAL delay (we need to do that to avoid long term errors */ actual_delay = is->frame_timer - (av_gettime() / 1000000.0); if (actual_delay < 0.010) { /* XXX: should skip picture */ actual_delay = 0.010; } /* launch timer for next picture */ schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));#if defined(DEBUG_SYNC) printf("video: delay=%0.3f actual_delay=%0.3f pts=%0.3f A-V=%f\n", delay, actual_delay, vp->pts, -diff);#endif if(is->subtitle_st) { if (is->subtitle_stream_changed) { SDL_LockMutex(is->subpq_mutex); while (is->subpq_size) { free_subpicture(&is->subpq[is->subpq_rindex]); /* update queue size and signal for next picture */ if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE) is->subpq_rindex = 0; is->subpq_size--; } is->subtitle_stream_changed = 0; SDL_CondSignal(is->subpq_cond); SDL_UnlockMutex(is->subpq_mutex); } else { if (is->subpq_size > 0) { sp = &is->subpq[is->subpq_rindex]; if (is->subpq_size > 1) sp2 = &is->subpq[(is->subpq_rindex + 1) % SUBPICTURE_QUEUE_SIZE]; else sp2 = NULL; if ((is->video_current_pts > (sp->pts + ((float) sp->sub.end_display_time / 1000))) || (sp2 && is->video_current_pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))) { free_subpicture(sp); /* update queue size and signal for next picture */ if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE) is->subpq_rindex = 0; SDL_LockMutex(is->subpq_mutex); is->subpq_size--; SDL_CondSignal(is->subpq_cond); SDL_UnlockMutex(is->subpq_mutex); } } } } /* display picture */ video_display(is); /* update queue size and signal for next picture */ if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_rindex = 0; SDL_LockMutex(is->pictq_mutex); is->pictq_size--; SDL_CondSignal(is->pictq_cond); SDL_UnlockMutex(is->pictq_mutex); } } else if (is->audio_st) { /* draw the next audio frame */ schedule_refresh(is, 40); /* if only audio stream, then display the audio bars (better than nothing, just to test the implementation */ /* display picture */ video_display(is); } else { schedule_refresh(is, 100); } if (show_status) { static int64_t last_time; int64_t cur_time; int aqsize, vqsize, sqsize; double av_diff; cur_time = av_gettime(); if (!last_time || (cur_time - last_time) >= 500 * 1000) { aqsize = 0; vqsize = 0; sqsize = 0; if (is->audio_st) aqsize = is->audioq.size; if (is->video_st) vqsize = is->videoq.size; if (is->subtitle_st) sqsize = is->subtitleq.size; av_diff = 0; if (is->audio_st && is->video_st) av_diff = get_audio_clock(is) - get_video_clock(is); printf("%7.2f A-V:%7.3f aq=%5dKB vq=%5dKB sq=%5dB \r", get_master_clock(is), av_diff, aqsize / 1024, vqsize / 1024, sqsize); fflush(stdout); last_time = cur_time; } }}/* allocate a picture (needs to do that in main thread to avoid potential locking problems */static void alloc_picture(void *opaque){ VideoState *is = opaque; VideoPicture *vp; vp = &is->pictq[is->pictq_windex]; if (vp->bmp) SDL_FreeYUVOverlay(vp->bmp);#if 0 /* XXX: use generic function */ /* XXX: disable overlay if no hardware acceleration or if RGB format */ switch(is->video_st->codec->pix_fmt) { case PIX_FMT_YUV420P: case PIX_FMT_YUV422P: case PIX_FMT_YUV444P: case PIX_FMT_YUYV422: case PIX_FMT_YUV410P: case PIX_FMT_YUV411P: is_yuv = 1; break; default: is_yuv = 0; break; }#endif vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width, is->video_st->codec->height, SDL_YV12_OVERLAY, screen); vp->width = is->video_st->codec->width; vp->height = is->video_st->codec->height; SDL_LockMutex(is->pictq_mutex); vp->allocated = 1; SDL_CondSignal(is->pictq_cond); SDL_UnlockMutex(is->pictq_mutex);}/** * * @param pts the dts of the pkt / pts of the frame and guessed if not known */static int queue_picture(VideoState *is, AVFrame *src_frame, double pts){ VideoPicture *vp; int dst_pix_fmt; AVPicture pict; static struct SwsContext *img_convert_ctx; /* wait until we have space to put a new picture */ SDL_LockMutex(is->pictq_mutex); while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->videoq.abort_request) { SDL_CondWait(is->pictq_cond, is->pictq_mutex); } SDL_UnlockMutex(is->pictq_mutex); if (is->videoq.abort_request) return -1; vp = &is->pictq[is->pictq_windex]; /* alloc or resize hardware picture buffer */ if (!vp->bmp || vp->width != is->video_st->codec->width || vp->height != is->video_st->codec->height) { SDL_Event event; vp->allocated = 0; /* the allocation must be done in the main thread to avoid locking problems */ event.type = FF_ALLOC_EVENT; event.user.data1 = is; SDL_PushEvent(&event); /* wait until the picture is allocated */ SDL_LockMutex(is->pictq_mutex); while (!vp->allocated && !is->videoq.abort_request) { SDL_CondWait(is->pictq_cond, is->pictq_mutex); } SDL_UnlockMutex(is->pictq_mutex); if (is->videoq.abort_request) return -1; } /* if the frame is not skipped, then display it */ if (vp->bmp) { /* get a pointer on the bitmap */ SDL_LockYUVOverlay (vp->bmp); dst_pix_fmt = PIX_FMT_YUV420P; pict.data[0] = vp->bmp->pixels[0]; pict.data[1] = vp->bmp->pixels[2]; pict.data[2] = vp->bmp->pixels[1]; pict.linesize[0] = vp->bmp->pitches[0]; pict.linesize[1] = vp->bmp->pitches[2]; pict.linesize[2] = vp->bmp->pitches[1]; img_convert_ctx = sws_getCachedContext(img_convert_ctx, is->video_st->codec->width, is->video_st->codec->height, is->video_st->codec->pix_fmt, is->video_st->codec->width, is->video_st->codec->height, dst_pix_fmt, sws_flags, NULL, NULL, NULL); if (img_convert_ctx == NULL) { fprintf(stderr, "Cannot initialize the conversion context\n"); exit(1); } sws_scale(img_convert_ctx, src_frame->data, src_frame->linesize, 0, is->video_st->codec->height, pict.data, pict.linesize); /* update the bitmap content */ SDL_UnlockYUVOverlay(vp->bmp); vp->pts = pts; /* now we can update the picture count */ if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_windex = 0; SDL_LockMutex(is->pictq_mutex); is->pictq_size++; SDL_UnlockMutex(is->pictq_mutex); } return 0;}/** * compute the exact PTS for the picture if it is omitted in the stream * @param pts1 the dts of the pkt / pts of the frame */static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -