Android万能音频播放器09-添加Seek功能和完成播放回调

1、Seek功能

播放本地音视频才有用
对AVFormatContext加锁,因为在seek的时候,不能继续播放,也就是不能继续读取frame了;

pthread_mutex_lock(&seek_mutex);
int64_t rel = secs * AV_TIME_BASE;
avformat_seek_file(pFormatCtx, -1, INT64_MIN, rel, INT64_MAX, 0);
pthread_mutex_unlock(&seek_mutex);

pthread_mutex_lock(&seek_mutex);
ret = av_read_frame(pFormatCtx, packet);
pthread_mutex_unlock(&seek_mutex);

实现Seek方法;

void JfFFmpeg::seek(int64_t sec) {
    if (duration < 0){
        return;
    }
    if (sec >= 0 && sec <= duration){
        if (audio != NULL){
            playStatus->seeking = true;
            audio->queue->clearAVPacket();//可能队列中还有一两秒的缓存,所以要清空
            audio->clock = 0;//时间置零,需要重新计算
            audio->last_time = 0;

            pthread_mutex_lock(&seek_mutex);

            int64_t rel = sec * AV_TIME_BASE;
            avformat_seek_file(pAFmtCtx,-1,INT64_MIN,rel,INT64_MAX,0);

            pthread_mutex_unlock(&seek_mutex);
            playStatus->seeking = false;
        }
    }
}

seeking时不再av_read_frame();

void JfFFmpeg::start() {
    if (audio == NULL) {
        if (LOG_DEBUG){
            LOGE("AUDIO == NULL");
        }
    }

    audio->play();

    int count;
    while (playStatus != NULL && !playStatus->exit) {

        if (playStatus->seeking){
            continue;//如果seeking中,不再往下执行
        }

        if (audio->queue->getQueueSize() > 40){//设置队列只保存40 frames,
            continue;
        }

        AVPacket *avPacket = av_packet_alloc();

        pthread_mutex_lock(&seek_mutex);
        int ret = av_read_frame(pAFmtCtx,avPacket);
        pthread_mutex_unlock(&seek_mutex);

        if (ret == 0) {
            if (avPacket->stream_index == audio->streamIndex){
                count++;
                /*if (LOG_DEBUG) {
                    LOGD("解码第%d帧",count);
                }*/
                audio->queue->putAVPacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
            }
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            //队列中的avPacket还没有解码完
            while (playStatus != NULL && !playStatus->exit){
                if (audio->queue->getQueueSize() > 0){//把缓存中的avPacket也要释放出来
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
        }
    }
    exit = true;
    }
}

Java层调用

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myplayer_player_JfPlayer_n_1seek(JNIEnv *env, jobject instance, jint sec) {

    // TODO
    if (ffmpeg != NULL){
        ffmpeg->seek(sec);
    }
}

2、完成播放回调

在最后调用完成接口即可。
创建完成播放接口,供C++层调用:

public interface JfOnCompleteListener {
    void onComplete();
}

private JfOnCompleteListener jfOnCompleteListener;
public void setJfOnCompleteListener(JfOnCompleteListener jfOnCompleteListener) {
    this.jfOnCompleteListener = jfOnCompleteListener;
}

public void onCallComplete(){
    stop();
    if (jfOnCompleteListener != null) {
        jfOnCompleteListener.onComplete();
    }
}

然后就是C++调用Java方法的流程,在

void JfFFmpeg::start() {
    if (audio == NULL) {
        if (LOG_DEBUG){
            LOGE("AUDIO == NULL");
        }
    }

    audio->play();

    int count;
    while (playStatus != NULL && !playStatus->exit) {

        if (playStatus->seeking){
            continue;//如果seeking中,不再往下执行
        }

        if (audio->queue->getQueueSize() > 40){//设置队列只保存40 frames,
            continue;
        }

        AVPacket *avPacket = av_packet_alloc();

        pthread_mutex_lock(&seek_mutex);
        int ret = av_read_frame(pAFmtCtx,avPacket);
        pthread_mutex_unlock(&seek_mutex);

        if (ret == 0) {
            if (avPacket->stream_index == audio->streamIndex){
                count++;
                /*if (LOG_DEBUG) {
                    LOGD("解码第%d帧",count);
                }*/
                audio->queue->putAVPacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
            }
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            //队列中的avPacket还没有解码完
            while (playStatus != NULL && !playStatus->exit){
                if (audio->queue->getQueueSize() > 0){//把缓存中的avPacket也要释放出来
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
        }
    }
    
    if (callJava != NULL){
    callJava->onCallComplete(CHILD_THREAD);
    }

    exit = true;
    }
}

并为start方法创建一个子线程:

void *startCallback(void *data){
    JfFFmpeg *ffmpeg = (JfFFmpeg *)data;
    ffmpeg->start();
    pthread_exit(&thread_start);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myplayer_player_JfPlayer_n_1start(JNIEnv *env, jobject instance) {

    // TODO
    if (ffmpeg != NULL){
        //ffmpeg->start();
        pthread_create(&thread_start,NULL,startCallback,ffmpeg);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_34099526/article/details/87563842