Android FFmpeg系列——7 实现快进/快退功能

Android FFmpeg系列——0 编译.so库
Android FFmpeg系列——1 播放视频
Android FFmpeg系列——2 播放音频
Android FFmpeg系列——3 C多线程使用
Android FFmpeg系列——4 子线程播放音视频
Android FFmpeg系列——5 音视频同步播放
Android FFmpeg系列——6 Java 获取播放进度
Android FFmpeg系列——7 实现快进/快退功能

实现快进、快退功能,其实就是设置视频的播放进度!

基本思路如下:

  1. 暂停 生产(读取帧)和 消费(解码播放);
  2. 清空队列,包括视频队列和音频队列;
  3. 调用 av_seek_frame 设置进度;
  4. 唤醒,继续 生产(读取帧)和 消费(解码播放);

思路很简单,我们一步一步实现!

暂停 生产(读取帧)和 消费(解码播放)

这里,我结合线程锁和条件变量来实现,暂时生产和消费:

/**
 * 生产函数
 * 循环读取帧 解码 丢到对应的队列中
 * @param arg
 * @return
 */
void* produce(void* arg) {
    
    for (;;) {
        pthread_mutex_lock(&seek_mutex);
        while (is_seek) {
            LOGE("Player Log : produce waiting seek");
            pthread_cond_wait(&seek_condition, &seek_mutex);
            LOGE("Player Log : produce wake up seek");
        }
        pthread_mutex_unlock(&seek_mutex);
        ...
    }
    ...
    return NULL;
}
/**
 * 消费函数
 * 从队列获取解码数据 同步播放
 * @param arg
 * @return
 */
void* consume(void* arg) {
    ...
    for (;;) {
    	...
        pthread_mutex_lock(&seek_mutex);
        while (is_seek) {
            pthread_cond_wait(&seek_condition, &seek_mutex);
        }
        pthread_mutex_unlock(&seek_mutex);
        ...
    }
    return NULL;
}
/**
 * 快进/快退
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {
    is_seek = true;
    pthread_mutex_lock(&seek_mutex);
    ...
    is_seek = false;
    pthread_cond_broadcast(&seek_condition);
    pthread_mutex_unlock(&seek_mutex);
}

清空队列,包括视频队列和音频队列

/**
 * 清空队列
 * @param queue
 */
void queue_clear(Queue* queue) {
    pthread_mutex_lock(queue->mutex_id);
    Node* node = queue->head;
    while (node != NULL) {
        queue->head = queue->head->next;
        free(node);
        node = queue->head;
    }
    queue->head = NULL;
    queue->tail = NULL;
    queue->size = 0;
    queue->is_block = true;
    pthread_cond_signal(queue->not_full_condition);
    pthread_mutex_unlock(queue->mutex_id);
}
/**
 * 快进/快退
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {
    ...
    queue_clear(cplayer->video_queue);
    queue_clear(cplayer->audio_queue);
    ...
}

记得唤醒生产函数!!!我就是没有唤醒,导致假死锁!

调用 av_seek_frame 设置进度

/**
 * 快进/快退
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {
    ...
    int result = av_seek_frame(cplayer->format_context, cplayer->video_stream_index, (int64_t) (progress / av_q2d(cplayer->format_context->streams[cplayer->video_stream_index]->time_base)), AVSEEK_FLAG_BACKWARD);
    if (result < 0) {
        LOGE("Player Error : Can not seek video to %d", progress);
        return;
    }
    result = av_seek_frame(cplayer->format_context, cplayer->audio_stream_index, (int64_t) (progress / av_q2d(cplayer->format_context->streams[cplayer->audio_stream_index]->time_base)), AVSEEK_FLAG_BACKWARD);
    if (result < 0) {
        LOGE("Player Error : Can not seek audio to %d", progress);
        return;
    }
    ...
}

我传入的 progress 参数的单位是秒,而 av_seek_frame 的第3个参数单位是 time_base 时间基,所以做这样的运算:

progress / av_q2d(stream->time_base)

唤醒,继续 生产(读取帧)和 消费(解码播放)

/**
 * 快进/快退
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {
    ...
    pthread_cond_broadcast(&seek_condition);
    ...
}

用 pthread_cond_broadcast 唤醒,让生产和消费可以继续!

设置进度完整代码

/**
 * 快进/快退
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {
    is_seek = true;
    pthread_mutex_lock(&seek_mutex);
    queue_clear(cplayer->video_queue);
    queue_clear(cplayer->audio_queue);
    int result = av_seek_frame(cplayer->format_context, cplayer->video_stream_index, (int64_t) (progress / av_q2d(cplayer->format_context->streams[cplayer->video_stream_index]->time_base)), AVSEEK_FLAG_BACKWARD);
    if (result < 0) {
        LOGE("Player Error : Can not seek video to %d", progress);
        return;
    }
    result = av_seek_frame(cplayer->format_context, cplayer->audio_stream_index, (int64_t) (progress / av_q2d(cplayer->format_context->streams[cplayer->audio_stream_index]->time_base)), AVSEEK_FLAG_BACKWARD);
    if (result < 0) {
        LOGE("Player Error : Can not seek audio to %d", progress);
        return;
    }
    is_seek = false;
    pthread_cond_broadcast(&seek_condition);
    pthread_mutex_unlock(&seek_mutex);
}

代码地址:https://github.com/JohanMan/Player

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值