由于FFmpeg的不断更新,为了深入理解FFmpeg,我参照雷神的代码结构图,重新画了一张图。
主要参考的FFmpeg官方代码:FFmpeg: fftools/ffplay.c File Reference
FFmpeg 版本:3.4.9 released on 2021-10-1
源代码结构图
注:
- 在event_loop函数中判断用户输入(键盘、鼠标等),用户输入有很多种,此结构图没有全部画出来。
- 此结构图忽略了字幕的处理(打开、读入、解码、显示、关闭)
C++音视频开发学习地址:【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂
代码逻辑
播放模式分析
在音视频播放时有不同的模式:
enum ShowMode { SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, // 播放视频画面 SHOW_MODE_WAVES, // 不显示视频画面,只显示波点 SHOW_MODE_RDFT, // 不显示视频画面,只黑屏 SHOW_MODE_NB } show_mode;
C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
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; } 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 since the 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; 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 * 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 since the 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; 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; 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 = 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 = s->sample_array * (1.0 - w * w); i += channels; if (i >= SAMPLE_ARRAY_SIZE) i -= SAMPLE_ARRAY_SIZE; } av_rdft_calc(s->rdft, data); } /* 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[2 * y + 0] * data[2 * y + 0] + data[2 * y + 1] * data[2 * y + 1])); int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[2 * y + 0], data[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; } }
本文【源代码 播放】由作者: 前端后端 提供,本站不拥有所有权,只提供储存服务,如有侵权,联系删除!
本文链接:https://www.cuoshuo.com/blog/4477.html