ffplay.c学习-8-暂停、逐帧、⾳量

本文详细讲解了ffplay中暂停视频、音频播放,逐帧操作,以及音量调节的方法,包括如何切换暂停/继续状态、暂停状态下视频与音频处理,以及通过s键进行逐帧播放的实现。此外,还介绍了控制音量的原理和关键函数,如toggle_mute和update_volume。
摘要由CSDN通过智能技术生成

ffplay.c学习-8-暂停、逐帧、⾳量


目录

  1. 播放、暂停
    1. 暂停/继续状态切换
    2. 暂停状态下的视频播放
    3. 暂停状态下的⾳频播放
  2. 逐帧、调⾳量、静⾳
    1. 逐帧
    2. 调⾳量
    3. 静⾳

  1. 常⽤的播放器操作:
    1. 播放:程序启动即播放,处于暂停时通过p或空格键
    2. 静⾳:m键
    3. ⾳量+:0键
    4. ⾳量-:9键
    5. 暂停:p或空格键
    6. 快退/快进:左箭头/右箭头
    7. 逐帧:s键
    8. 退出:q或Esc键
    9. 全屏:f或者⿏标左键双击
  2. event_loop做按键响应

1. 播放、暂停

  1. 画⾯要停⽌
    1. 画⾯停留在最后⼀帧
  2. 声⾳要停⽌
    1. ⾳频回调接⼝请求数据帧时直接填0
  3. 读取数据是否要停⽌?
    1. ⾳视频包缓存队列满时进⼊休眠。
  4. 暂停->继续 :时钟的恢复
  5. 暂停:toggle_pause()
1. 暂停/继续状态切换
  1. 函数调⽤关系如下
main() -->
event_loop() -->
toggle_pause() -->
stream_toggle_pause()
  1. stream_toggle_pause()实现状态翻转:
/* pause or resume the video */
static void stream_toggle_pause(VideoState *is)
{
    // 如果当前是暂停 -> 恢复播放
    // 正常播放 -> 暂停
    if (is->paused) {// 当前是暂停,那这个时候进来这个函数就是要恢复播放
        /* 恢复暂停状态时也需要恢复时钟,需要更新vidclk */
        // 加上 暂停->恢复 经过的时间
        is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
        if (is->read_pause_return != AVERROR(ENOSYS)) {
            is->vidclk.paused = 0;
        }
        // 设置时钟的意义,暂停状态下读取的是单纯pts
        // 重新矫正video时钟
        set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
    }
    set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
    // 切换 pause/resume 两种状态
    is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = !is->paused;
    printf("is->step = %d; stream_toggle_pause\n", is->step);
}
2. 暂停状态下的视频播放
  1. 在video_refresh()函数中有如下代码:
            /* called to display each frame */
            static void video_refresh(void *opaque, double *remaining_time)
            {
                ......
                //视频播放
                if (is->video_st) {
                    ......
                    // 暂停处理:不停播放上⼀帧图像
                    if (is->paused)
                        goto display;

                    ......
                }

                ......
            }
  1. 在暂停状态下,实际就是不停播放上⼀帧(最后⼀帧)图像。画⾯不更新.
3. 暂停状态下的⾳频播放
  1. sdl_audio_callback -> audio_decode_frame
            static int audio_decode_frame(VideoState *is)
            {
                int data_size, resampled_data_size;
                int64_t dec_channel_layout;
                av_unused double audio_clock0;
                int wanted_nb_samples;
                Frame *af;

                (is->paused)
                return -1; // 暂停返回-1, 但这⾥返回-1并不会导致程序结束。
                ....
            }

2. 逐帧、调⾳量、静⾳

1. 逐帧
  1. 逐帧播放的本质是,播放⼀帧图像,然后暂停。
  2. 涉及到的函数和变量:
    1. step_to_next_frame()
    2. is->step = 1时单步播放⼀帧,然后paused
  3. 逐帧播放流程
    1. 按s键,如果当前处于暂停则启动播放;
    2. 播放⼀帧数据然后进⼊暂停状态
  4. 逐帧播放是⽤户每按⼀次s键,播放器播放⼀帧画面。
  5. 逐帧播放实现的⽅法是:每次按了s键,就将状态切换为播放,播放⼀帧画⾯后,将状态切换为暂停。
  6. 函数调⽤关系如下:
main() -->
event_loop() -->
step_to_next_frame() -->
stream_toggle_pause()
  1. 实现代码⽐较简单,如下:
        static void step_to_next_frame(VideoState *is)
        {
            /* if the stream is paused unpause it, then step */
            if (is->paused)
                stream_toggle_pause(is); // 确保切换到播放状态,播放⼀帧画⾯
            is->step = 1;
        }
        /* called to display each frame */
        static void video_refresh(void *opaque, double *remaining_time) {
            ......
            // 视频播放
            if (is->video_st) {
                ......
                if (is->step && !is->paused)
                    stream_toggle_pause(is); // 逐帧播放模式下,播放⼀帧画⾯后暂停
                        ......
            }
            ......
        }
2. 调⾳量
  1. ⾳量控制的本质:控制采样点的幅值
    1. 静⾳,将采样点数值置为0
    2. ⾳量+,提升采样点的幅值
    3. ⾳量-,降低采样点的幅值
1. ffplay控制⾳量的⽅式
  1. 最⼤⾳量:输出解码后的原始数据
  2. 静⾳:即是输出数值为0的数据
    1. toggle_mute()
  3. 改变⾳量:通过SDL_MixAudioFormat改变解码后数据的幅值
    1. update_volume()
  4. ⽐如下所示(sdl_audio_callback函数内):
  5. 以下是ffplay的⽅式供参考
        //根据audio_volume决定如何输出audio_buf
        /* 判断是否为静音,以及当前音量的大小,如果音量为最大则直接拷贝数据 */
        // 最大音量则直接拷贝解码后的原始数据
        if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
            memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);
        else {
        	// 先将数据清零,如果不再拷贝新数据给stream,则输出静音
            memset(stream, 0, len1);
            // 3.调整音量
            // 不为静音则通过SDL_MixAudioFormat来调整解码后数据的幅值来实现音量的变化
            /* 如果处于mute状态则直接使用stream填0数据, 暂停时is->audio_buf = NULL */
            if (!is->muted && is->audio_buf)
                SDL_MixAudioFormat(stream, (uint8_t *) is->audio_buf + is->audio_buf_index,
                                   AUDIO_S16SYS, len1, is->audio_volume);
        }
3. 静⾳
 static void toggle_mute(VideoState *is)
 {
 	is->muted = !is->muted;
 }
```go
        //根据audio_volume决定如何输出audio_buf
        /* 判断是否为静音,以及当前音量的大小,如果音量为最大则直接拷贝数据 */
        // 最大音量则直接拷贝解码后的原始数据
        if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
            memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);
        else {
        	// 先将数据清零,如果不再拷贝新数据给stream,则输出静音
            memset(stream, 0, len1);
            // 3.调整音量
            // 不为静音则通过SDL_MixAudioFormat来调整解码后数据的幅值来实现音量的变化
            /* 如果处于mute状态则直接使用stream填0数据, 暂停时is->audio_buf = NULL */
            if (!is->muted && is->audio_buf)
                SDL_MixAudioFormat(stream, (uint8_t *) is->audio_buf + is->audio_buf_index,
                                   AUDIO_S16SYS, len1, is->audio_volume);
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值