コード例 #1
0
ファイル: audiosource.cpp プロジェクト: zzlee/ffms2
void FFMS_AudioSource::Init(const FFMS_Index &Index, int DelayMode) {
	// Decode the first packet to ensure all properties are initialized
	// Don't cache it since it might be in the wrong format
	// Instead, leave it in DecodeFrame and it'll get cached later
	while (DecodeFrame->nb_samples == 0)
		DecodeNextBlock();

	// Read properties of the audio which may not be available until the first
	// frame has been decoded
	FillAP(AP, CodecContext, Frames);

	if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
		throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
			"Codec returned zero size audio");

	std::auto_ptr<FFMS_ResampleOptions> opt(CreateResampleOptions());
	SetOutputFormat(opt.get());

	if (DelayMode < FFMS_DELAY_NO_SHIFT)
		throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
			"Bad audio delay compensation mode");

	if (DelayMode == FFMS_DELAY_NO_SHIFT) return;

	if (DelayMode > (signed)Index.size())
		throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
			"Out of bounds track index selected for audio delay compensation");

	if (DelayMode >= 0 && Index[DelayMode].TT != FFMS_TYPE_VIDEO)
		throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
			"Audio delay compensation must be relative to a video track");

	int64_t Delay = 0;
	if (DelayMode != FFMS_DELAY_TIME_ZERO) {
		if (DelayMode == FFMS_DELAY_FIRST_VIDEO_TRACK) {
			for (size_t i = 0; i < Index.size(); ++i) {
				if (Index[i].TT == FFMS_TYPE_VIDEO && !Index[i].empty()) {
					DelayMode = i;
					break;
				}
			}
		}

		if (DelayMode >= 0) {
			const FFMS_Track &VTrack = Index[DelayMode];
			Delay = -(VTrack[0].PTS * VTrack.TB.Num * AP.SampleRate / (VTrack.TB.Den * 1000));
		}
	}

	if (Frames.HasTS) {
		int i = 0;
		while (Frames[i].PTS == ffms_av_nopts_value) ++i;
		Delay += Frames[i].PTS * Frames.TB.Num * AP.SampleRate / (Frames.TB.Den * 1000);
		for (; i >= 0; --i)
			Delay -= Frames[i].SampleCount;
	}

	AP.NumSamples += Delay;
}
コード例 #2
0
ファイル: lavfaudio.cpp プロジェクト: TheRyuu/ffms2
void FFLAVFAudio::Seek() {
    size_t TargetPacket = GetSeekablePacketNumber(Frames, PacketNumber);
    LastValidTS = AV_NOPTS_VALUE;

    int Flags = Frames.HasTS ? AVSEEK_FLAG_BACKWARD : AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_BYTE;

    if (av_seek_frame(FormatContext, TrackNumber, FrameTS(TargetPacket), Flags) < 0)
        av_seek_frame(FormatContext, TrackNumber, FrameTS(TargetPacket), Flags | AVSEEK_FLAG_ANY);

    if (TargetPacket != PacketNumber) {
        // Decode until the PTS changes so we know where we are
        int64_t LastPTS = FrameTS(PacketNumber);
        while (LastPTS == FrameTS(PacketNumber)) DecodeNextBlock();
    }
}
コード例 #3
0
ファイル: audiosource.cpp プロジェクト: zzlee/ffms2
void FFMS_AudioSource::CacheBeginning() {
	// Nothing to do if the cache is already populated
	if (!Cache.empty()) return;

	// The first frame is already decoded, so add it to the cache
	CacheIterator it = Cache.end();
	CacheBlock(it);

	// The first packet after a seek is often decoded incorrectly, which
	// makes it impossible to ever correctly seek back to the beginning, so
	// store the first block now

	// In addition, anything with the same PTS as the first packet can't be
	// distinguished from the first packet and so can't be seeked to, so
	// store those as well

	// Some of LAVF's splitters don't like to seek to the beginning of the
	// file (ts and?), so cache a few blocks even if PTSes are unique
	// Packet 7 is the last packet I've had be unseekable to, so cache up to
	// 10 for a bit of an extra buffer
	CacheIterator end = Cache.end();
	while (PacketNumber < Frames.size() &&
		((Frames[0].PTS != ffms_av_nopts_value && Frames[PacketNumber].PTS == Frames[0].PTS) ||
		 Cache.size() < 10)) {

		// Vorbis in particular seems to like having 60+ packets at the start
		// of the file with a PTS of 0, so we might need to expand the search
		// range to account for that.
		// Expanding slightly before it's strictly needed to ensure there's a
		// bit of space for an actual cache
		if (Cache.size() >= MaxCacheBlocks - 5) {
			 if (MaxCacheBlocks >= EXCESSIVE_CACHE_SIZE)
				throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_ALLOCATION_FAILED,
					"Exceeded the search range for an initial valid audio PTS");
			MaxCacheBlocks *= 2;
		}

		DecodeNextBlock(&end);
	}
	// Store the iterator to the last element of the cache which is used for
	// correctness rather than speed, so that when looking for one to delete
	// we know how much to skip
	CacheNoDelete = Cache.end();
	--CacheNoDelete;
}
コード例 #4
0
ファイル: audiosource.cpp プロジェクト: slajar/ffms2
void FFMS_AudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count) {
	if (Start < 0 || Start + Count > AP.NumSamples || Count < 0)
		throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
			"Out of bounds audio samples requested");

	CacheBeginning();

	uint8_t *Dst = static_cast<uint8_t*>(Buf);

	// Apply audio delay (if any) and fill any samples before the start time with zero
	Start -= Delay;
	if (Start < 0) {
		size_t Bytes = static_cast<size_t>(BytesPerSample * FFMIN(-Start, Count));
		memset(Dst, 0, Bytes);

		Count += Start;
		// Entire request was before the start of the audio
		if (Count <= 0) return;

		Start = 0;
		Dst += Bytes;
	}

	auto it = Cache.begin();

	while (Count > 0) {
		// Find first useful cache block
		while (it != Cache.end() && it->Start + it->Samples <= Start) ++it;

		// Cache has the next block we want
		if (it != Cache.end() && it->Start <= Start) {
			int64_t SrcOffset = FFMAX(0, Start - it->Start);
			int64_t DstOffset = FFMAX(0, it->Start - Start);
			int64_t CopySamples = FFMIN(it->Samples - SrcOffset, Count - DstOffset);
			size_t Bytes = static_cast<size_t>(CopySamples * BytesPerSample);

			memcpy(Dst + DstOffset * BytesPerSample, &it->Data[SrcOffset * BytesPerSample], Bytes);
			Start += CopySamples;
			Count -= CopySamples;
			Dst += Bytes;
			++it;
		}
		// Decode another block
		else {
			if (Start < CurrentSample && SeekOffset == -1)
				throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_CODEC, "Audio stream is not seekable");

			if (SeekOffset >= 0 && (Start < CurrentSample || Start > CurrentSample + DecodeFrame->nb_samples * 5)) {
				FrameInfo f;
				f.SampleStart = Start;
				size_t NewPacketNumber = std::distance(
					Frames.begin(),
					std::lower_bound(Frames.begin(), Frames.end(), f, SampleStartComp));
				NewPacketNumber = NewPacketNumber > static_cast<size_t>(SeekOffset + 15)
				                ? NewPacketNumber - SeekOffset - 15
				                : 0;
				while (NewPacketNumber > 0 && !Frames[NewPacketNumber].KeyFrame) --NewPacketNumber;

				// Only seek forward if it'll actually result in moving forward
				if (Start < CurrentSample || static_cast<size_t>(NewPacketNumber) > PacketNumber) {
					PacketNumber = NewPacketNumber;
					CurrentSample = -1;
					DecodeFrame.reset();
					avcodec_flush_buffers(CodecContext);
					Seek();
				}
			}

			// Decode until we hit the block we want
			if (PacketNumber >= Frames.size())
				throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_CODEC, "Seeking is severely broken");
			while (CurrentSample + DecodeFrame->nb_samples <= Start && PacketNumber < Frames.size())
				DecodeNextBlock(&it);
			if (CurrentSample > Start)
				throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_CODEC, "Seeking is severely broken");

			// The block we want is now in the cache immediately before it
			--it;
		}
	}
}