uint32_t KeyframeSequencer::AddBytes( const uint8_t *bytes, const uint32_t byte_count, const int64_t stream_offset) /* throw() */ { const uint8_t *local_bytes = bytes; const uint8_t *local_bytes_end = bytes + byte_count; state_changed = false; while (local_bytes < local_bytes_end) { local_bytes = ff_find_start_code(local_bytes, local_bytes_end, &sync_accumulator); if ((sync_accumulator & 0xffffff00) == 0x00000100) { uint8_t k = *(local_bytes-1); sync_stream_offset = stream_offset; keyframe = false; KeyframePredicate(k); first_NAL_byte = k; return local_bytes - bytes; } } return local_bytes - bytes; }
/** \fn DTVRecorder::FindMPEG2Keyframes(const TSPacket* tspacket) * \brief Locates the keyframes and saves them to the position map. * * This searches for three magic integers in the stream. * The picture start code 0x00000100, the GOP code 0x000001B8, * and the sequence start code 0x000001B3. The GOP code is * prefered, but is only required of MPEG1 streams, the * sequence start code is a decent fallback for MPEG2 * streams, and if all else fails we just look for the picture * start codes and call every 16th frame a keyframe. * * NOTE: This does not only find keyframes but also tracks the * total frames as well. At least a couple times seeking * has been broken by short-circuiting the search once * a keyframe stream id has been found. This may work on * some channels, but will break on others so algorithmic * optimizations should be done with great care. * * \code * PES header format * byte 0 byte 1 byte 2 byte 3 [byte 4 byte 5] * 0x00 0x00 0x01 PESStreamID PES packet length * \endcode * * \return Returns true if packet[s] should be output. */ bool DTVRecorder::FindMPEG2Keyframes(const TSPacket* tspacket) { bool haveBufferedData = !_payload_buffer.empty(); if (!tspacket->HasPayload()) // no payload to scan return !haveBufferedData; if (!ringBuffer) return !haveBufferedData; // if packet contains start of PES packet, start // looking for first byte of MPEG start code (3 bytes 0 0 1) // otherwise, pick up search where we left off. const bool payloadStart = tspacket->PayloadStart(); _start_code = (payloadStart) ? 0xffffffff : _start_code; // Just make these local for efficiency reasons (gcc not so smart..) const uint maxKFD = kMaxKeyFrameDistance; bool hasFrame = false; bool hasKeyFrame = false; // Scan for PES header codes; specifically picture_start // sequence_start (SEQ) and group_start (GOP). // 00 00 01 00: picture_start_code // 00 00 01 B8: group_start_code // 00 00 01 B3: seq_start_code // (there are others that we don't care about) const uint8_t *bufptr = tspacket->data() + tspacket->AFCOffset(); const uint8_t *bufend = tspacket->data() + TSPacket::SIZE; while (bufptr < bufend) { bufptr = ff_find_start_code(bufptr, bufend, &_start_code); if ((_start_code & 0xffffff00) == 0x00000100) { // At this point we have seen the start code 0 0 1 // the next byte will be the PES packet stream id. const int stream_id = _start_code & 0x000000ff; if (PESStreamID::PictureStartCode == stream_id) hasFrame = true; else if (PESStreamID::GOPStartCode == stream_id) { _last_gop_seen = _frames_seen_count; hasKeyFrame |= true; } else if (PESStreamID::SequenceStartCode == stream_id) { _last_seq_seen = _frames_seen_count; hasKeyFrame |= (_last_gop_seen + maxKFD)<_frames_seen_count; } } } if (hasFrame && !hasKeyFrame) { // If we have seen kMaxKeyFrameDistance frames since the // last GOP or SEQ stream_id, then pretend this picture // is a keyframe. We may get artifacts but at least // we will be able to skip frames. hasKeyFrame = !(_frames_seen_count & 0xf); hasKeyFrame &= (_last_gop_seen + maxKFD) < _frames_seen_count; hasKeyFrame &= (_last_seq_seen + maxKFD) < _frames_seen_count; } if (hasKeyFrame) { _last_keyframe_seen = _frames_seen_count; HandleKeyframe(); } if (hasFrame) { _frames_seen_count++; if (!_wait_for_keyframe_option || _first_keyframe>=0) _frames_written_count++; } return hasKeyFrame || (_payload_buffer.size() >= (188*50)); }