ffplay 源代码结构图

写在前面

这篇文章参考了雷神的文章:ffplay.c函数结构简单分析(画图)_雷霄骅(leixiaohua1020)

由于FFmpeg的不断更新,雷神画的结构图与现在版本FFmpeg的代码结构有所不同。为了深入理解FFmpeg,我参照雷神的代码结构图,重新画了一张图。

主要参考的FFmpeg官方代码:FFmpeg: fftools/ffplay.c File Reference

FFmpeg 版本:3.4.9 released on 2021-10-11

对应文档:FFmpeg: Main Page

源代码结构图

注:

  1. 在event_loop函数中判断用户输入(键盘、鼠标等),用户输入有很多种,此结构图没有全部画出来。
  2. 此结构图忽略了字幕的处理(打开、读入、解码、显示、关闭)

代码逻辑

播放模式分析

在音视频播放时有不同的模式:

enum ShowMode {SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0,   // 播放视频画面SHOW_MODE_WAVES,       // 不显示视频画面,只显示波点SHOW_MODE_RDFT,      // 不显示视频画面,只黑屏SHOW_MODE_NB
} show_mode;

SHOW_MODE_VIDEO

播放画面正常

对应播放的代码:

static void video_image_display(VideoState *is)
{Frame *vp;Frame *sp = NULL;SDL_Rect rect;vp = frame_queue_peek_last(&is->pictq);/*...process code for subtitile...*/calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);if (!vp->uploaded) {if (upload_texture(&is->vid_texture, vp->frame, &is->img_convert_ctx) < 0)return;vp->uploaded = 1;vp->flip_v = vp->frame->linesize[0] < 0;}SDL_RenderCopyEx(renderer, is->vid_texture, NULL, &rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0);/*...show subtitile code...*/}

SHOW_MODE_WAVES

播放画面

此画面是由video_audio_display函数生成的。

对应播放的代码:

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;int64_t time_diff;int rdft_bits, nb_freq;for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++);nb_freq = 1 << (rdft_bits - 1);/* compute display index : center on currently output samples */channels = s->audio_tgt.channels;nb_display_channels = channels;if (!s->paused) {int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq);n = 2 * channels;delay = s->audio_write_buf_size;delay /= n;/* to be more precise, we take into account the time spent sincethe last buffer computation */if (audio_callback_time) {time_diff = av_gettime_relative() - audio_callback_time;delay -= (time_diff * s->audio_tgt.freq) / 1000000;}delay += 2 * data_used;if (delay < data_used)delay = data_used;i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);if (s->show_mode == SHOW_MODE_WAVES) {h = INT_MIN;for (i = 0; i < 1000; i += channels) {int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;int a = s->sample_array[idx];int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];int score = a - d;if (h < score && (b ^ c) < 0) {h = score;i_start = idx;}}}s->last_i_start = i_start;} else {i_start = s->last_i_start;}if (s->show_mode == SHOW_MODE_WAVES) {SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);/* 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(s->xleft + x, ys, 1, y);i += channels;if (i >= SAMPLE_ARRAY_SIZE)i -= SAMPLE_ARRAY_SIZE;}}SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);for (ch = 1; ch < nb_display_channels; ch++) {y = s->ytop + ch * h;fill_rectangle(s->xleft, y, s->width, 1);}}
}

SHOW_MODE_RDFT

播放画面

此画面是由video_audio_display函数生成的。

对应播放的代码:

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;int64_t time_diff;int rdft_bits, nb_freq;for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++);nb_freq = 1 << (rdft_bits - 1);/* compute display index : center on currently output samples */channels = s->audio_tgt.channels;nb_display_channels = channels;if (!s->paused) {int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq);n = 2 * channels;delay = s->audio_write_buf_size;delay /= n;/* to be more precise, we take into account the time spent sincethe last buffer computation */if (audio_callback_time) {time_diff = av_gettime_relative() - audio_callback_time;delay -= (time_diff * s->audio_tgt.freq) / 1000000;}delay += 2 * data_used;if (delay < data_used)delay = data_used;i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);if (s->show_mode == SHOW_MODE_WAVES) {h = INT_MIN;for (i = 0; i < 1000; i += channels) {int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;int a = s->sample_array[idx];int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];int score = a - d;if (h < score && (b ^ c) < 0) {h = score;i_start = idx;}}}s->last_i_start = i_start;} else {i_start = s->last_i_start;}if (s->show_mode == SHOW_MODE_WAVES) {/* process for SHOW_MODE_WAVES*/} else {if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)return;nb_display_channels= FFMIN(nb_display_channels, 2);if (rdft_bits != s->rdft_bits) {av_rdft_end(s->rdft);av_free(s->rdft_data);s->rdft = av_rdft_init(rdft_bits, DFT_R2C);s->rdft_bits = rdft_bits;s->rdft_data = av_malloc_array(nb_freq, 4 *sizeof(*s->rdft_data));}if (!s->rdft || !s->rdft_data){av_log(NULL, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");s->show_mode = SHOW_MODE_WAVES;} else {FFTSample *data[2];SDL_Rect rect = {.x = s->xpos, .y = 0, .w = 1, .h = s->height};uint32_t *pixels;int pitch;for (ch = 0; ch < nb_display_channels; ch++) {data[ch] = s->rdft_data + 2 * nb_freq * ch;i = i_start + ch;for (x = 0; x < 2 * nb_freq; x++) {double w = (x-nb_freq) * (1.0 / nb_freq);data[ch][x] = s->sample_array[i] * (1.0 - w * w);i += channels;if (i >= SAMPLE_ARRAY_SIZE)i -= SAMPLE_ARRAY_SIZE;}av_rdft_calc(s->rdft, data[ch]);}/* Least efficient way to do this, we should of course* directly access it but it is more than fast enough. */if (!SDL_LockTexture(s->vis_texture, &rect, (void **)&pixels, &pitch)) {pitch >>= 2;pixels += pitch * s->height;for (y = 0; y < s->height; y++) {double w = 1 / sqrt(nb_freq);int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1])): a;a = FFMIN(a, 255);b = FFMIN(b, 255);pixels -= pitch;*pixels = (a << 16) + (b << 8) + ((a+b) >> 1);}SDL_UnlockTexture(s->vis_texture);}SDL_RenderCopy(renderer, s->vis_texture, NULL, NULL);}if (!s->paused)s->xpos++;if (s->xpos >= s->width)s->xpos= s->xleft;}
}

之后分析ffplay的音视频同步。

【FFmpeg】ffplay 源代码结构图+播放模式分析相关推荐

  1. FFMPEG 3.4.2 - ffplay源代码分析 (三)

    1.数据结构之VideoState VideoState是所有其他数据结构的母体. main 线程启动新线程read_thread,初始化VideoState. AVFormatContext保存与& ...

  2. FFmpeg源代码结构图 - 编码

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  3. ffplay设置解码器播放8k*8k视频;ffmpeg命令补充

    4k视频许多人听过,但是8k可能就不太有人接触了,尤其是纵横都是8k分辨率的视频. 这种视频播放器一般是播不了的,如我经常用的MPC-HC播放器(后来得知Windows10自带的"电影和电视 ...

  4. ffmpeg ffplay ffprobe使用说明

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的"大神",有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然 ...

  5. 如何用FFmpeg编写一个简单播放器详细步骤介绍

    如何用FFmpeg编写一个简单播放器详细步骤介绍(转载) FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具.FFMPEG几乎为你把所有的繁重工作都做了,比如解码.编码.复用和解复用. ...

  6. ffmpeg/ffplay 添加实时的时间水印

    右上角添加时间水印 ffmpeg -i 0.ts -vf drawtext="fontfile=arial.ttf:x=w-tw:fontcolor=white:fontsize=30:te ...

  7. 支持HEVC/H265 RTMP接收的FFMPEG/FFPLAY WINDOWS版本

    本文镜像:https://linkpi.cn/archives/1249 本文链接:https://blog.csdn.net/weixin_45326556/article/details/1111 ...

  8. 最简单的基于FFMPEG+SDL的音频播放器

    ===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...

  9. 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...

最新文章

  1. 剑指offer:反转链表 python实现
  2. GDB多进程调试(转)
  3. JVM学习笔记二:JVM参数
  4. mysql实验三单表和多表查询_数据库实验三(单表查询)
  5. NSURLConnection 下载数据 -- IOS(实例)
  6. jQuery 异步和同步请求
  7. php5.2 json,php5.2 对json格式的支持
  8. 第五天 面向对象软件分析与设计
  9. Exposure X8 ps人像图片调色滤镜插件
  10. 简单三步教你制作一个数据可视化大屏
  11. 优秀的 Verilog/FPGA开源项目介绍(二十九)- 开源网站
  12. strcpy会覆盖原来的吗_你真的会拍合影照吗?原来高大上的合影照这样拍
  13. 关于Autosar中的NM模块的理解
  14. 新的Novidade漏洞利用工具包目标瞄准家用和SOHO路由器
  15. torch.meshgrid()函数解析
  16. Web Atoms Crack,JavaScript 桥接器
  17. 规则引擎在数据治理平台的实践
  18. 01_使用jupyter
  19. 特斯拉降价,国产车嘴上说不怕实际两股战战,后续还有可怕的技术
  20. oracle 48101 block,oracle数据库的一次异常起停处理。

热门文章

  1. 2022-2027年中国移动音乐行业市场调研及未来发展趋势预测报告
  2. 计算机软件专利申请研究
  3. canvas实现简单进度条
  4. php扣费到期自动续期,PHP 处理苹果的自动续签
  5. 适合平板用的Android版本,安卓平板专享 推荐五款Pad版应用浏览器
  6. 深入理解Kotlin协程suspend工作原理(初学者也能看得懂)
  7. [bzoj4372]烁烁的游戏
  8. 锦城学院计算机系考研,考研心得分享
  9. 2021桂林市高考一调成绩查询,2021年广西桂林市高考化学一调试卷.docx
  10. 华为云服务器数据库连接失败