//{{{ Constructor //////////////////////////////////////////////////////////////////////////// /// /// Initialize the class by resetting it. /// /// During a constructor calls to virtual methods resolve to the current class (because /// the vtable is still for the class being constructed). This means we need to call /// ::Reset again because the calls made by the sub-constructors will not have called /// our reset method. /// Collator_PesVideoMjpeg_c::Collator_PesVideoMjpeg_c(void) { COLLATOR_TRACE("%s\n", __FUNCTION__); if (InitializationStatus != CollatorNoError) return; Collator_PesVideoMjpeg_c::Reset(); }
//////////////////////////////////////////////////////////////////////////// /// /// Record a prediction about the location of the next sync word. /// void Collator_PesAudioDvd_c::MakeDvdSyncWordPrediction(int Prediction) { // Every time we make a bad prediction then we decrement the mis-prediction counter. // When it reaches zero we force a wildcard prediction and enter DVD mode. This is a hack // to allow us to play DVD-style streams with broken first access unit pointers where the // pointers don't, in fact, point to the an access unit at all. Such streams have been // observed in the wild - see https://bugzilla.stlinux.com/show_bug.cgi?id=5051 if (0 == RemainingMispredictionsBeforeAutomaticWildcard) { COLLATOR_TRACE("Deploying bad first access unit pointer workaround.\n"); Prediction = WILDCARD_PREDICTION; PassPesPrivateDataToElementaryStreamHandler = false; RemainingMispredictionsBeforeAutomaticWildcard = MaxMispredictionsBeforeAutomaticWildcard; } COLLATOR_DEBUG("Making prediction of %d\n", Prediction); SyncWordPrediction = Prediction; }
//////////////////////////////////////////////////////////////////////////// /// /// Check a previous prediction regarding the location of the sync word. /// /// This method does not report the result of the prediction. Instead it /// updates Collator_PesAudio_c::PassPesPrivateDataToElementaryStreamHandler /// accordingly. /// void Collator_PesAudioDvd_c::VerifyDvdSyncWordPrediction(int Offset) { if (SyncWordPrediction != WILDCARD_PREDICTION) { // If SyncWordPrediction != Offset then we are *not* a DVD-style PES stream // and need to ensure the DVD PES private data *is* processed as elementary // stream. PassPesPrivateDataToElementaryStreamHandler = (SyncWordPrediction != Offset); // If we badly predicted the location of the sync word then record this. This // counter is used as part of a heuristic workaround in other parts of the code. if ((SyncWordPrediction != Offset) && (SyncWordPrediction != INVALID_PREDICTION) && (RemainingMispredictionsBeforeAutomaticWildcard > 0)) RemainingMispredictionsBeforeAutomaticWildcard--; // Having consumed the prediction enter a wildcard mode. This causes // PassPesPrivateDataToElementaryStreamHandler to hold the same // value until we process a PES private data area whilst SeekingFrameHeader. // This is very important for DVD streams which can have sync words located // such that we never process a PES private data area in this mode. This this // case we would never regain lock on the stream. MakeDvdSyncWordPrediction(WILDCARD_PREDICTION); } else { // Apply a wildcard (this is not a 'match all'; it means 'do not change state'). // However we can only apply a wildcard a limited number of times. After this // point we assume the current state (PassPesPrivateDataToElementaryStreamHandler) is // impeeding regain of sync and force a change of state. if (--RemainingWildcardsPermitted < 0) { COLLATOR_TRACE("Having trouble re-locking, %s DVD wildcard mode.\n", (PassPesPrivateDataToElementaryStreamHandler ? "entering" : "leaving")); PassPesPrivateDataToElementaryStreamHandler = !PassPesPrivateDataToElementaryStreamHandler; // having switched modes we can reset our counter. RemainingWildcardsPermitted = MaxWildcardsPermitted; } } }
//}}} //{{{ Reset //////////////////////////////////////////////////////////////////////////// /// /// Resets and configures according to the requirements of this stream content /// /// \return void /// CollatorStatus_t Collator_PesVideoMjpeg_c::Reset(void) { CollatorStatus_t Status; COLLATOR_TRACE("%s\n", __FUNCTION__); Status = Collator_PesVideo_c::Reset(); if (Status != CollatorNoError) return Status; Configuration.GenerateStartCodeList = true; Configuration.MaxStartCodes = 32; Configuration.StreamIdentifierMask = PES_START_CODE_MASK; Configuration.StreamIdentifierCode = PES_START_CODE_VIDEO; Configuration.BlockTerminateMask = 0xff; Configuration.BlockTerminateCode = MJPEG_SOI; Configuration.IgnoreCodesRangeStart = 0x00; Configuration.IgnoreCodesRangeEnd = MJPEG_SOF_0 - 1; Configuration.InsertFrameTerminateCode = false; // Force the mme decode to terminate after a picture Configuration.TerminalCode = 0; Configuration.ExtendedHeaderLength = 0; Configuration.DeferredTerminateFlag = false; Configuration.StreamTerminateFlushesFrame = false; // Use an end of sequence to force a frame flush Configuration.StreamTerminationCode = 0; return CollatorNoError; }
//////////////////////////////////////////////////////////////////////////// /// /// Determine the new state of the collator according to the incoming sub frame /// Also returns this sub frame length /// /// \return Collator status code, CollatorNoError indicates success. /// CollatorStatus_t Collator_PesAudioLpcm_c::DecideCollatorNextStateAndGetLength( unsigned int *FrameLength ) { CollatorStatus_t Status = CollatorNoError; // COLLATOR_DEBUG(">><<\n"); if ( IsFirstPacket ) { //case of very first packet, skip the packet portion before the FirstAccessUnitOffsetPointer CollatorState = SkipSubFrame; IsFirstPacket = false; // for the very first packet of dvd-audio, the stuffing bytes are not part of the PDA // since Configuration.ExtendedHeaderLength is not the same as PrivateHeaderLength, // so skip these bytes in this case *FrameLength = PesPrivateToSkip + NextParsedFrameHeader.FirstAccessUnitPointer + FirstAccessUnitOffset[StreamType] - NextParsedFrameHeader.PrivateHeaderLength; PesPrivateToSkip = 0; COLLATOR_TRACE("First packet: Skipping %d bytes\n", *FrameLength); return (Status); } // accumulate the private data area for the frame parser, // if some major parameter inside have changed... if ( AccumulatePrivateDataArea ) { // save the location of the private data area, to update it later... Status = AccumulateData( Configuration.ExtendedHeaderLength, &NewPesPrivateDataArea[0] ); if ( Status != CollatorNoError ) { return (Status); } AccumulatePrivateDataArea = false; COLLATOR_DEBUG("Accumulate PDA of length %d\n", Configuration.ExtendedHeaderLength); } if ( !IsPesPrivateDataAreaValid ) { //case of wrong packet passed to collator, skip the whole packet CollatorState = SkipSubFrame; *FrameLength = RemainingElementaryLength; } else if ( PesPrivateToSkip > 0 ) { CollatorState = SkipSubFrame; *FrameLength = min(PesPrivateToSkip, RemainingElementaryLength); PesPrivateToSkip -= *FrameLength; COLLATOR_DEBUG("Skipping %d bytes of pda\n", *FrameLength); } else if ( RemainingDataLength > 0 ) { CollatorState = ReadSubFrame; *FrameLength = min(RemainingDataLength, RemainingElementaryLength); RemainingDataLength -= *FrameLength; COLLATOR_DEBUG("Reading %d bytes (rest of frame)\n", *FrameLength); } else if ( (AccumulatedFrameNumber >= NbAudioFramesToGlob[StreamType][NextParsedFrameHeader.SamplingFrequency1]) || IsPesPrivateDataAreaNew ) { // flush the frame if we have already more than x accumulated frames // or if some pda key parameters are new CollatorState = GotCompleteFrame; COLLATOR_DEBUG("Flush after %d audio frames\n", AccumulatedFrameNumber); AccumulatedFrameNumber = 0; // prevent accumulating anything for this frame, the next thing we need to do // is accumlate the private data area for the frame parser but we can't do that // until we've cleared this frame from the accumulation buffer. *FrameLength = 0; // at next call of this function accumulate the pda AccumulatePrivateDataArea = true; // reset IsPesPrivateDataAreaNew = false; PlaybackTime += (GlobbedFramesOfNewPacket * LpcmPresentationTime[StreamType][NextParsedFrameHeader.SamplingFrequency1]); COLLATOR_DEBUG("PlaybackTime: %x\n", PlaybackTime); } else { // normal case: accumulate the audio frames CollatorState = ReadSubFrame; AccumulatedFrameNumber += 1; GlobbedFramesOfNewPacket += 1; COLLATOR_ASSERT(0 == RemainingDataLength); COLLATOR_ASSERT(0 == PesPrivateToSkip); // DVD-audio allows stuffing bytes to form part of the private data area (and these can be different // for each packet). We can't easily handle this in the PES layer since the length can't be predicted // and therefore we cannot set Configuration.ExtendedHeaderLength until it is too late. Instead // Collator_PesAudioLpcm_c::HandlePesPrivateData() records how many bytes we must skip // (PesPrivateToSkip) and leaves it to the state change logic to skip that data. This means that we // must ensure that this method will be called at the right point to skip data. We do this be making // sure we don't ever return a *FrameLength larger than the RemainingElementaryData . if ((RemainingElementaryLength < NextParsedFrameHeader.AudioFrameSize)) { RemainingDataLength = NextParsedFrameHeader.AudioFrameSize - RemainingElementaryLength; *FrameLength = RemainingElementaryLength; } else { // read the whole frame! *FrameLength = NextParsedFrameHeader.AudioFrameSize; } COLLATOR_DEBUG("Read frame of size %d (total frames in this chunk: %d)\n", *FrameLength, AccumulatedFrameNumber); } return Status; }