[7]ffmpeg+SDL2实现的视频播放器「退出、暂停、播放」

时间:2020-12-10 12:06:05

日期:2016.10.10
作者:isshe
github:github.com/isshe
邮箱:i.sshe@outlook.com
平台:ubuntu16.04 64bit + ffmepg3 + SDL2


前言

这篇记录退出、暂停、播放。
快进快退想了下,没啥思路。囧。

关于退出

一直没怎么管退出,但是始终是个问题,无论是中途退出还是播放完毕退出。

  • 想要做到的:
    1. 中途退出(点窗口的x)能立即退出
    2. 播放完窗口停住,等待x掉。

需要解决的问题

  • 1.如何判断一个流结束?

    关于播完退出,想过的方法:

    1. 判断packet队列是否为空,空的话延时一段时间再判断一次(或多次),如果再为空就退出。
      问题:如果延时,延时多长呢? 判断几次好呢?
    2. 获取时长,大于等于的时候退出。(好像靠谱)
      • 获取总时长:AFStream的duration * AFStream的time_base[当然要用av_p2d转换了]
      • 获取当前时长:AFPacket的pts * AFStream 的time_base[同样要转换]。
        这种方法感觉是可以的,但是在这份代码中用这个方法,最后的packet的pts总是小于总的时长,就算再加一个两帧间的delay也还是小。所以最后还是用了第一种方法
  • 如何处理退出? 如果有两个流,一个长一个短,如何才能不互相影响?
    如何处理退出和播放音视频的代码有关

    本程序中:

    1. 音频是主线程打开(SDL_OpenAudio())设备后,SDL_OpenAudio()开线程调用callback()函数取声音数据来解码发出声音。
    2. 视频是主线程配置好相关信息,然后开一个线程运行刷新函数(refresh_func)循环发出刷新事件,主线程接收事件再调用显示函数(代码中是decode_and_show())。
      • 这个实现中退出视频播放比较简单,只要跳出刷新函数(refresh_func)中的循环即可。
      • 退出音频,思路是:让callback()不再运行。SDL_PauseAudio()实现这个功能。如有必要再SDL_CloseAudio()
        (更新的函数是SDL_OpenAudioDevice()和SDL_CloseAudioDevice())
        「需要注意:哪个线程调用打开,哪个线程调用关闭,类似的, 哪个线程调用创建窗口(SDL_CreateWindow())之类的函数,就用哪个线程调用显示函数,否则容易出现core dump和显示不出来」

暂停/播放

实现的是用键盘的space空格键控制播放、暂停。第一感觉就是用SDL的Event,也确实可行。

如何使用键盘Event?

  • 按键事件的触发方式:
    方法一:
case SDL_KEYDOWN:
{
const Uint8 *state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_RETURN]) {
printf("<RETURN> is pressed.\n");
}
if (state[SDL_SCANCODE_RIGHT] && state[SDL_SCANCODE_UP]) {
printf("Right and Up Keys Pressed.\n");
}
break;
}
case SDL_KEYDOWN:
{
switch(event.key.keysym.sym)
{
case SDLK_LEFT:
break;
case SDLK_RIGHT:
break;
case SDLK_UP:
break;
case SDLK_DOWN:
break;
}
}

如何实现暂停/播放?

这个实现中:用一个变量(player_state)表示视频的状态。
-1: 退出;
0: 播放;
1: 暂停:

  • 视频:
    • 暂停:键盘事件触发以后把视频的状态设置为1, 刷新函数(refresh_func)检测到player_state == 1, 就进入一个循环,类似:
    • 播放:就是refresh_func()退出这个循环,继续Push Event个主线程了。「中间隔了代码后缩进不对了,放上来…」
                while(ps->player_state == 1)
{
SDL_Delay(20); //delay的时间没有多尝试
}


  • 音频:
    • 暂停: SDL_PauseAudio(1), 停止callback()。
    • 播放:SDL_PauseAudio(0), 继续callback()。

在callback里面操作这两个函数也是可以的。
但是不要放到callback里面,因为,SDL_PauseAudio(1)就是停止调用callback()的,停止以后,就调用不了SDL_PauseAudio(0)了,所以,这两个函数哪个线程调用(SDL_OpenAudio())开设备,哪个就调用这两个比较好。(目前实践情况,不一定对)。

参考资料

SDL: http://wiki.libsdl.org/SDL_PauseAudio

代码下载

github: https://github.com/isshe/Practice-project/tree/master/Player/blog/chapter6/player_v1.6
csdn: http://download.csdn.net/detail/i_scream_/9649398