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);
}
}
网友评论