📄 ffplay.c
字号:
/* * FFplay : Simple Media Player based on the ffmpeg libraries * Copyright (c) 2003 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#define HAVE_AV_CONFIG_H#include "avformat.h"#include "cmdutils.h"#include <SDL.h>#include <SDL_thread.h>#ifdef CONFIG_WIN32#undef main /* We don't want SDL to override our main() */#endif#if defined(__linux__)#define HAVE_X11#endif#ifdef HAVE_X11#include <X11/Xlib.h>#endif//#define DEBUG_SYNC#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)/* SDL audio buffer size, in samples. Should be small to have precise A/V sync as SDL does not have hardware buffer fullness info. */#define SDL_AUDIO_BUFFER_SIZE 1024/* no AV sync correction is done if below the AV sync threshold */#define AV_SYNC_THRESHOLD 0.01/* no AV correction is done if too big error */#define AV_NOSYNC_THRESHOLD 10.0/* maximum audio speed change to get correct sync */#define SAMPLE_CORRECTION_PERCENT_MAX 10/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */#define AUDIO_DIFF_AVG_NB 20/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */#define SAMPLE_ARRAY_SIZE (2*65536)typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; int abort_request; SDL_mutex *mutex; SDL_cond *cond;} PacketQueue;#define VIDEO_PICTURE_QUEUE_SIZE 1typedef struct VideoPicture { double pts; /* presentation time stamp for this picture */ SDL_Overlay *bmp; int width, height; /* source height & width */ int allocated;} VideoPicture;enum { AV_SYNC_AUDIO_MASTER, /* default choice */ AV_SYNC_VIDEO_MASTER, AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */};typedef struct VideoState { SDL_Thread *parse_tid; SDL_Thread *video_tid; AVInputFormat *iformat; int no_background; int abort_request; int paused; int last_paused; int seek_req; int64_t seek_pos; AVFormatContext *ic; int dtg_active_format; int audio_stream; int av_sync_type; double external_clock; /* external clock base */ int64_t external_clock_time; double audio_clock; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; int audio_diff_avg_count; AVStream *audio_st; PacketQueue audioq; int audio_hw_buf_size; /* samples output by the codec. we reserve more space for avsync compensation */ uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; int audio_buf_size; /* in bytes */ int audio_buf_index; /* in bytes */ AVPacket audio_pkt; uint8_t *audio_pkt_data; int audio_pkt_size; int show_audio; /* if true, display audio samples */ int16_t sample_array[SAMPLE_ARRAY_SIZE]; int sample_array_index; int last_i_start; double frame_timer; double frame_last_pts; double frame_last_delay; double video_clock; int video_stream; AVStream *video_st; PacketQueue videoq; double video_last_P_pts; /* pts of the last P picture (needed if B frames are present) */ double video_current_pts; /* current displayed pts (different from video_clock if frame fifos are used) */ int64_t video_current_pts_time; /* time at which we updated video_current_pts - used to have running video pts */ VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; int pictq_size, pictq_rindex, pictq_windex; SDL_mutex *pictq_mutex; SDL_cond *pictq_cond; // QETimer *video_timer; char filename[1024]; int width, height, xleft, ytop;} VideoState;void show_help(void);static int audio_write_get_buf_size(VideoState *is);/* options specified by the user */static AVInputFormat *file_iformat;static AVImageFormat *image_format;static const char *input_filename;static int fs_screen_width;static int fs_screen_height;static int screen_width = 640;static int screen_height = 480;static int audio_disable;static int video_disable;static int display_disable;static int show_status;static int av_sync_type = AV_SYNC_AUDIO_MASTER;static int64_t start_time = AV_NOPTS_VALUE;static int debug = 0;static int debug_mv = 0;static int step = 0;static int thread_count = 1;static int workaround_bugs = 1;/* current context */static int is_full_screen;static VideoState *cur_stream;static int64_t audio_callback_time;#define FF_ALLOC_EVENT (SDL_USEREVENT)#define FF_REFRESH_EVENT (SDL_USEREVENT + 1)#define FF_QUIT_EVENT (SDL_USEREVENT + 2)SDL_Surface *screen;/* packet queue handling */static void packet_queue_init(PacketQueue *q){ memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond();}static void packet_queue_flush(PacketQueue *q){ AVPacketList *pkt, *pkt1; for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_free_packet(&pkt->pkt); } q->last_pkt = NULL; q->first_pkt = NULL; q->nb_packets = 0; q->size = 0;}static void packet_queue_end(PacketQueue *q){ packet_queue_flush(q); SDL_DestroyMutex(q->mutex); SDL_DestroyCond(q->cond);}static int packet_queue_put(PacketQueue *q, AVPacket *pkt){ AVPacketList *pkt1; /* duplicate the packet */ if (av_dup_packet(pkt) < 0) return -1; pkt1 = av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; SDL_LockMutex(q->mutex); if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; /* XXX: should duplicate packet data in DV case */ SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0;}static void packet_queue_abort(PacketQueue *q){ SDL_LockMutex(q->mutex); q->abort_request = 1; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex);}/* return < 0 if aborted, 0 if no packet and > 0 if packet. */static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){ AVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); for(;;) { if (q->abort_request) { ret = -1; break; } pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret;}static inline void fill_rectangle(SDL_Surface *screen, int x, int y, int w, int h, int color){ SDL_Rect rect; rect.x = x; rect.y = y; rect.w = w; rect.h = h; SDL_FillRect(screen, &rect, color);}#if 0/* draw only the border of a rectangle */void fill_border(VideoState *s, int x, int y, int w, int h, int color){ int w1, w2, h1, h2; /* fill the background */ w1 = x; if (w1 < 0) w1 = 0; w2 = s->width - (x + w); if (w2 < 0) w2 = 0; h1 = y; if (h1 < 0) h1 = 0; h2 = s->height - (y + h); if (h2 < 0) h2 = 0; fill_rectangle(screen, s->xleft, s->ytop, w1, s->height, color); fill_rectangle(screen, s->xleft + s->width - w2, s->ytop, w2, s->height, color); fill_rectangle(screen, s->xleft + w1, s->ytop, s->width - w1 - w2, h1, color); fill_rectangle(screen, s->xleft + w1, s->ytop + s->height - h2, s->width - w1 - w2, h2, color);}#endifstatic void video_image_display(VideoState *is){ VideoPicture *vp; float aspect_ratio; int width, height, x, y; SDL_Rect rect; vp = &is->pictq[is->pictq_rindex]; if (vp->bmp) { /* XXX: use variable in the frame */ if (is->video_st->codec.sample_aspect_ratio.num == 0) aspect_ratio = 0; else aspect_ratio = av_q2d(is->video_st->codec.sample_aspect_ratio) * is->video_st->codec.width / is->video_st->codec.height;; if (aspect_ratio <= 0.0) aspect_ratio = (float)is->video_st->codec.width / (float)is->video_st->codec.height; /* if an active format is indicated, then it overrides the mpeg format */#if 0 if (is->video_st->codec.dtg_active_format != is->dtg_active_format) { is->dtg_active_format = is->video_st->codec.dtg_active_format; printf("dtg_active_format=%d\n", is->dtg_active_format); }#endif#if 0 switch(is->video_st->codec.dtg_active_format) { case FF_DTG_AFD_SAME: default: /* nothing to do */ break; case FF_DTG_AFD_4_3: aspect_ratio = 4.0 / 3.0; break; case FF_DTG_AFD_16_9: aspect_ratio = 16.0 / 9.0; break; case FF_DTG_AFD_14_9: aspect_ratio = 14.0 / 9.0; break; case FF_DTG_AFD_4_3_SP_14_9: aspect_ratio = 14.0 / 9.0; break; case FF_DTG_AFD_16_9_SP_14_9: aspect_ratio = 14.0 / 9.0; break; case FF_DTG_AFD_SP_4_3: aspect_ratio = 4.0 / 3.0; break; }#endif /* XXX: we suppose the screen has a 1.0 pixel ratio */ height = is->height; width = ((int)rint(height * aspect_ratio)) & -3; if (width > is->width) { width = is->width; height = ((int)rint(width / aspect_ratio)) & -3; } x = (is->width - width) / 2; y = (is->height - height) / 2; if (!is->no_background) { /* fill the background */ // fill_border(is, x, y, width, height, QERGB(0x00, 0x00, 0x00)); } else { is->no_background = 0; } rect.x = is->xleft + x; rect.y = is->xleft + y; rect.w = width; rect.h = height; SDL_DisplayYUVOverlay(vp->bmp, &rect); } else {#if 0 fill_rectangle(screen, is->xleft, is->ytop, is->width, is->height, QERGB(0x00, 0x00, 0x00));#endif }}static inline int compute_mod(int a, int b){ a = a % b; if (a >= 0) return a; else return a + b;}static void video_audio_display(VideoState *s){ int i, i_start, x, y1, y, ys, delay, n, nb_display_channels; int ch, channels, h, h2, bgcolor, fgcolor; int16_t time_diff; /* compute display index : center on currently output samples */ channels = s->audio_st->codec.channels; nb_display_channels = channels; if (!s->paused) { n = 2 * channels; delay = audio_write_get_buf_size(s); delay /= n; /* to be more precise, we take into account the time spent since the last buffer computation */ if (audio_callback_time) { time_diff = av_gettime() - audio_callback_time; delay += (time_diff * s->audio_st->codec.sample_rate) / 1000000; } delay -= s->width / 2; if (delay < s->width) delay = s->width; i_start = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE); s->last_i_start = i_start; } else { i_start = s->last_i_start; } bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); fill_rectangle(screen, s->xleft, s->ytop, s->width, s->height, bgcolor); fgcolor = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff); /* total height for one channel */ h = s->height / nb_display_channels; /* graph height / 2 */ h2 = (h * 9) / 20; for(ch = 0;ch < nb_display_channels; ch++) { i = i_start + ch; y1 = s->ytop + ch * h + (h / 2); /* position of center line */ for(x = 0; x < s->width; x++) { y = (s->sample_array[i] * h2) >> 15; if (y < 0) { y = -y; ys = y1 - y; } else { ys = y1; } fill_rectangle(screen,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -