播放器
正常的播放器播放一个文件或则URL应该是一个什么样的流程?看下图
- 解协议:一般是针对网络流,如rtmp,hls等,网络传输的时候肯定要对数据做利于网络传输的封包,如rtmp协议,在这里需要先根据协议进行解析一得到flv数据。
- 解封装:最直接的就是我们播放的视频文件一般都是mp4,mkv等这些封装格式。一个媒体文件中可能包含视频,音频,字母等,而且我们必须要同步播放这些文件,这里就需要靠外部的封装格式将这几个类型的数据串联在一起;
- 音视频压缩数据:音视频的源数据非常的大,必须要经过压缩之后才能存储和传输,因此这里收到的就应该是音视频压缩数据;
- 音视频解码:...
- 音视频同步:音频和视频都有各自的播放时间长即(PTS),理论上来说只要按着各自的现实时间戳进行播放,音视频应该是同步的;但是实际情况中由于外部的各种不可控的因素不能让他们顺利的按照各自的时间戳进行播放,所以就有必要加上一个同步的环节,在此有三种同步的方法(1.视频到音频;2.音频到视频;3.音视频到外部时钟)
ffplay
ffplay可以理解为使用ffmpeg加上SDL做的一个播放器,其中解协议,解封装,解码,音视频同步都是使用的ffmpeg的模块,只有在调用设备进行音视频渲染的时候使用的是SDL;
看源码
main函数
main 函数在此主要完成以下工作:
- 注册各种协议,封装格式,编解码
- 解析参数,并打开输入文件
- 初始化SDL库
- 开始播放
/* Called from the main */
int main(int argc, char **argv)
{
int flags;
VideoState *is;
init_dynload();
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
/* 注册各种*/
/* register all codecs, demux and protocols */
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avformat_network_init();
init_opts();
signal(SIGINT , sigterm_handler); /* Interrupt (ANSI). */
signal(SIGTERM, sigterm_handler); /* Termination (ANSI). */
show_banner(argc, argv, options);
/*解析参数,此处会打开输入文件*/
parse_options(NULL, argc, argv, options, opt_input_file);
if (!input_filename) {
show_usage();
av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");
av_log(NULL, AV_LOG_FATAL,
"Use -h to get full help or, even better, run 'man %s'\n", program_name);
exit(1);
}
if (display_disable) {
video_disable = 1;
}
/* 初始化SDL库*/
flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
if (audio_disable)
flags &= ~SDL_INIT_AUDIO;
else {
/* Try to work around an occasional ALSA buffer underflow issue when the
* period size is NPOT due to ALSA resampling by forcing the buffer size. */
if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
}
if (display_disable)
flags &= ~SDL_INIT_VIDEO;
if (SDL_Init (flags)) {
av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
exit(1);
}
SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
av_init_packet(&flush_pkt);
flush_pkt.data = (uint8_t *)&flush_pkt;
if (!display_disable) {
int flags = SDL_WINDOW_HIDDEN;
if (borderless)
flags |= SDL_WINDOW_BORDERLESS;
else
flags |= SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
if (window) {
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
renderer = SDL_CreateRenderer(window, -1, 0);
}
if (renderer) {
if (!SDL_GetRendererInfo(renderer, &renderer_info))
av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);
}
}
if (!window || !renderer || !renderer_info.num_texture_formats) {
av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());
do_exit(NULL);
}
}
/*启动播放*/
is = stream_open(input_filename, file_iformat);
if (!is) {
av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");
do_exit(NULL);
}
event_loop(is);
/* never returns */
return 0;
}
了解SDL
前面一直说播放器使用的是SDL进行渲染的,所以在此我们有必要了解下SDL库的用法。
man-解析参数打开文件
parse_options(NULL, argc, argv, options, opt_input_file);
... 未完待续
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。