MP4点播精准SEEK功能
借助ZLMediaKit配合;功能流程如下
涉及到的代码功能:
1、ZLM是支持更新时钟读取MP4数据的;修改成多文件连续读取即可
uint32_t getCurrentStamp() { return (uint32_t)(_seek_to【seek的那个P帧的时间戳】 + !_paused * _speed * _seek_ticker.elapsedTime()); } ----------------------------- void MP4Reader::startReadMP4(uint64_t sample_ms, bool ref_self, bool file_repeat) { GET_CONFIG(uint32_t, sampleMS, Record::kSampleMS); auto strong_self = shared_from_this(); if (_muxer) { //一直读到所有track就绪为止 while (!_muxer->isAllTrackReady() && readNextSample()); //注册后再切换OwnerPoller _muxer->setMediaListener(strong_self); } auto timer_sec = (sample_ms ? sample_ms : sampleMS) / 1000.0f; //启动定时器 if (ref_self) { _timer = std::make_shared<Timer>(timer_sec, [strong_self]() { lock_guard<recursive_mutex> lck(strong_self->_mtx); return strong_self->readSample();//需要做一点修改,能连续都多个文件,而不是读完一个文件就结束 }, _poller); } else { weak_ptr<MP4Reader> weak_self = strong_self; _timer = std::make_shared<Timer>(timer_sec, [weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { return false; } lock_guard<recursive_mutex> lck(strong_self->_mtx); return strong_self->readSample(); }, _poller); } _file_repeat = file_repeat; }
2、借助老陈的media -server。修改成前向查找GOP
3、播放端要注意:
3.1:SEEk前,暂停播放器解析,解码,渲染线程;清空解析,解码,渲染缓冲,避免seek后播放一段缓冲部分数据在播放SEEk;设置显示标记,和显示起始位置
不同播放器,内部线程不一样,但是一般解码和渲染肯定是生产者消费者,解析和解码可能共用一条线程,音视频解码有各自不同的线程(避免解码阻塞)
3.2:恢复解析解码渲染线程;根据设置的显示标记,flag=精准SEEk,等解码输出到设置的SEEk位置(时间)才输出给渲染线程,其他的直接解码后丢掉;开始播放;
static int mov_stss_seek(struct mov_track_t* track, int64_t *timestamp) { int64_t clock; size_t start, end, mid; size_t idx, prev, next; struct mov_sample_t* sample; idx = mid = start = 0; end = track->stbl.stss_count; assert(track->stbl.stss_count > 0); clock = *timestamp * track->mdhd.timescale / 1000; // mvhd timescale while (start < end) { mid = (start + end) / 2; idx = track->stbl.stss[mid]; if (idx < 1 || idx > track->sample_count) { // start from 1 assert(0); return -1; } idx -= 1; sample = &track->samples[idx]; if (sample->dts > clock) end = mid; else if (sample->dts < clock) start = mid + 1; else break; } prev = track->stbl.stss[mid > 0 ? mid - 1 : mid] - 1; next = track->stbl.stss[mid + 1 < track->stbl.stss_count ? mid + 1 : mid] - 1; /*if (DIFF(track->samples[prev].dts, clock) < DIFF(track->samples[idx].dts, clock)) idx = prev; if (DIFF(track->samples[next].dts, clock) < DIFF(track->samples[idx].dts, clock)) idx = next;*/ if (DIFF(track->samples[prev].dts, clock) < DIFF(track->samples[next].dts, clock)) { idx = prev; } *timestamp = track->samples[idx].dts * 1000 / track->mdhd.timescale; track->sample_offset = idx; return 0; }