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 JNICALLJava_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 JNICALLJava_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 JNICALLJava_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 JNICALLJava_com_johan_player_Player_seekTo(JNIEnv *env, jobject instance, jint progress) {    ...    pthread_cond_broadcast(&seek_condition);    ...}

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

设置进度完整代码

/** * 快进/快退 */extern "C"JNIEXPORT void JNICALLJava_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

更多相关文章

  1. OpenGL播放yuv数据流(着色器SHADER)-android(一)
  2. okhttp源码分析
  3. Android之MediaPlayer 音频播放
  4. Android使用SoundPool播放音效
  5. Android(安卓)Volley 源码解析
  6. Android(安卓)TTS
  7. Java集合 && Android提供的集合
  8. Android(安卓)AsyncTask两种线程池分析和总结
  9. Unity播放视频(pc,android,ios)

随机推荐

  1. Android通过XML来定义Menu
  2. Android 基于Zxing扫码实现(三)、从相册选
  3. 使用jni接口完成android本地程序的运行
  4. TextureView+SurfaceTexture+OpenGL ES来
  5. 服务器日志现 Android 4.2 传将添多项新
  6. Android进程保活方法 --转自腾讯Bugly公
  7. Android(安卓)给应用定制皮肤-------值得
  8. Android(安卓)培训计划:如何从功能机时代
  9. [置顶] Android异步更新UI-线程池-Future
  10. 代码中进行RelativeLayout布局的修改添加