Android(安卓)FFmpeg系列——7 实现快进/快退功能
16lz
2021-01-26
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 实现快进/快退功能
实现快进、快退功能,其实就是设置视频的播放进度!
基本思路如下:
- 暂停 生产(读取帧)和 消费(解码播放);
- 清空队列,包括视频队列和音频队列;
- 调用 av_seek_frame 设置进度;
- 唤醒,继续 生产(读取帧)和 消费(解码播放);
思路很简单,我们一步一步实现!
暂停 生产(读取帧)和 消费(解码播放)
这里,我结合线程锁和条件变量来实现,暂时生产和消费:
/** * 生产函数 * 循环读取帧 解码 丢到对应的队列中 * @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
更多相关文章
- OpenGL播放yuv数据流(着色器SHADER)-android(一)
- okhttp源码分析
- Android之MediaPlayer 音频播放
- Android使用SoundPool播放音效
- Android(安卓)Volley 源码解析
- Android(安卓)TTS
- Java集合 && Android提供的集合
- Android(安卓)AsyncTask两种线程池分析和总结
- Unity播放视频(pc,android,ios)