Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 7455f77ad38dbaa6007f27caccefa92193e40157
  • master par défaut
  • 1-baka-export
  • meson
  • assdraw
  • old-master
  • v3.2.2
  • v3.2.1
  • v3.2.0
  • v3.1.3
  • v3.1.2
  • v3.1.1
  • v3.1.0
  • v3.0.4
  • v3.0.3
  • v3.0.2
  • v3.0.1
  • v3.0.0
  • v2.1.3
  • v2.1.4
  • v2.1.5
  • v2.1.6
  • v2.1.0
  • v2.1.1
  • v2.1.2
  • v2.1.7
26 résultats

ffaudiosource.cpp

Blame
  • ffaudiosource.cpp 20,40 Kio
    //  Copyright (c) 2007-2009 Fredrik Mellbin
    //
    //  Permission is hereby granted, free of charge, to any person obtaining a copy
    //  of this software and associated documentation files (the "Software"), to deal
    //  in the Software without restriction, including without limitation the rights
    //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    //  copies of the Software, and to permit persons to whom the Software is
    //  furnished to do so, subject to the following conditions:
    //
    //  The above copyright notice and this permission notice shall be included in
    //  all copies or substantial portions of the Software.
    //
    //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    //  THE SOFTWARE.
    
    #include "ffaudiosource.h"
    #include <errno.h>
    
    #ifdef __UNIX__
    #define _snprintf snprintf
    #endif
    
    TAudioBlock::TAudioBlock(int64_t Start, int64_t Samples, uint8_t *SrcData, size_t SrcBytes) {
    	this->Start = Start;
    	this->Samples = Samples;
    	Data = new uint8_t[SrcBytes];
    	memcpy(Data, SrcData, SrcBytes);
    }
    
    TAudioBlock::~TAudioBlock() {
    	delete[] Data;
    }
    
    TAudioCache::TAudioCache() {
    	MaxCacheBlocks = 0;
    	BytesPerSample = 0;
    }
    
    TAudioCache::~TAudioCache() {
    	for (TAudioCache::iterator it=begin(); it != end(); it++)
    		delete *it;
    }
    
    void TAudioCache::Initialize(int BytesPerSample, int MaxCacheBlocks) {
    	this->BytesPerSample = BytesPerSample;
    	this->MaxCacheBlocks = MaxCacheBlocks;
    }
    
    void TAudioCache::CacheBlock(int64_t Start, int64_t Samples, uint8_t *SrcData) {
    	if (BytesPerSample > 0) {
    		for (TAudioCache::iterator it=begin(); it != end(); it++) {
    			if ((*it)->Start == Start) {
    				delete *it;
    				erase(it);
    				break;
    			}
    		}
    
    		push_front(new TAudioBlock(Start, Samples, SrcData, Samples * BytesPerSample));
    		if (static_cast<int>(size()) >= MaxCacheBlocks) {
    			delete back();
    			pop_back();
    		}
    	}
    }
    
    bool TAudioCache::AudioBlockComp(TAudioBlock *A, TAudioBlock *B) {
    	return A->Start < B->Start;
    }
    
    int64_t TAudioCache::FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst) {
    	// May be better to move used blocks to the front
    	std::list<TAudioBlock *> UsedBlocks;
    	for (TAudioCache::iterator it=begin(); it != end(); it++) {
    		int64_t SrcOffset = FFMAX(0, Start - (*it)->Start);
    		int64_t DstOffset = FFMAX(0, (*it)->Start - Start);
    		int64_t CopySamples = FFMIN((*it)->Samples - SrcOffset, Samples - DstOffset);
    		if (CopySamples > 0) {
    			memcpy(Dst + DstOffset * BytesPerSample, (*it)->Data + SrcOffset * BytesPerSample, CopySamples * BytesPerSample);
    			UsedBlocks.push_back(*it);
    		}
    	}
    	UsedBlocks.sort(AudioBlockComp);
    	int64_t Ret = Start;
    	for (std::list<TAudioBlock *>::iterator it = UsedBlocks.begin(); it != UsedBlocks.end(); it++) {
    		if (it == UsedBlocks.begin() || Ret == (*it)->Start)
    			Ret = (*it)->Start + (*it)->Samples;
    		else
    			break;
    	}
    	return FFMIN(Ret, Start + Samples);
    }
    
    FFAudio::FFAudio() {
    	CurrentSample = 0;
    	DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE * 10];
    }
    
    FFAudio::~FFAudio() {
    	delete[] DecodingBuffer;
    }
    
    void FFLAVFAudio::Free(bool CloseCodec) {
    	if (CloseCodec)
    		avcodec_close(CodecContext);
    	if (FormatContext)
    		av_close_input_file(FormatContext);
    }
    
    FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
    	FormatContext = NULL;
    	AVCodec *Codec = NULL;
    	AudioTrack = Track;
    	Frames = (*Index)[AudioTrack];
    
    	if (Frames.size() == 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
    		throw ErrorMsg;
    	}
    
    	if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
    		_snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
    		throw ErrorMsg;
    	}
    	
    	if (av_find_stream_info(FormatContext) < 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
    		throw ErrorMsg;
    	}
    
    	CodecContext = FormatContext->streams[AudioTrack]->codec;
    
    	Codec = avcodec_find_decoder(CodecContext->codec_id);
    	if (Codec == NULL) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
    		throw ErrorMsg;
    	}
    
    	if (avcodec_open(CodecContext, Codec) < 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
    		throw ErrorMsg;
    	}
    
    	// Always try to decode a frame to make sure all required parameters are known
    	int64_t Dummy;
    	if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) {
    		Free(true);
    		throw ErrorMsg;
    	}
    	if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
    		av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
    	avcodec_flush_buffers(CodecContext);
    
    	FillAP(AP, CodecContext, Frames);
    
    	if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
    		Free(true);
    		_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
    		throw ErrorMsg;
    	}
    
    	AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
    }
    
    int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize) {
    	const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	int Ret = -1;
    	*Count = 0;
    	uint8_t *Buf = DecodingBuffer;
    	AVPacket Packet, TempPacket;
    	InitNullPacket(&Packet);
    	InitNullPacket(&TempPacket);
    
    	while (av_read_frame(FormatContext, &Packet) >= 0) {
            if (Packet.stream_index == AudioTrack) {
    			TempPacket.data = Packet.data;
    			TempPacket.size = Packet.size;
    
    			while (TempPacket.size > 0) {
    				int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * 10;
    				Ret = avcodec_decode_audio3(CodecContext, (int16_t *)Buf, &TempOutputBufSize, &TempPacket);
    
    				if (Ret < 0) {// throw error or something?
    					av_free_packet(&Packet);
    					goto Done;
    				}
    
    				if (Ret > 0) {
    					TempPacket.size -= Ret;
    					TempPacket.data += Ret;
    					Buf += TempOutputBufSize;
    					if (SizeConst)
    						*Count += TempOutputBufSize / SizeConst;
    				}
    			}
    
    			av_free_packet(&Packet);
    			goto Done;
            }
    
    		av_free_packet(&Packet);
    	}
    
    Done:
    	return Ret;
    }
    
    int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
    	const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	memset(Buf, 0, SizeConst * Count);
    
    	int PreDecBlocks = 0;
    	uint8_t *DstBuf = static_cast<uint8_t *>(Buf);
    
    	// Fill with everything in the cache
    	int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
    	// Was everything in the cache?
    	if (CacheEnd == Start + Count)
    		return 0;
    
    	size_t CurrentAudioBlock;
    	// Is seeking required to decode the requested samples?
    //	if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
    	if (CurrentSample != CacheEnd) {
    		PreDecBlocks = 15;
    		CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks - 20, (int64_t)0);
    
    		// Did the seeking fail?
    		if (av_seek_frame(FormatContext, AudioTrack, Frames[CurrentAudioBlock].DTS, AVSEEK_FLAG_BACKWARD) < 0)
    			av_seek_frame(FormatContext, AudioTrack, Frames[CurrentAudioBlock].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
    
    		avcodec_flush_buffers(CodecContext);
    
    		AVPacket Packet;
    		InitNullPacket(&Packet);
    
    		// Establish where we actually are
    		// Trigger on packet dts difference since groups can otherwise be indistinguishable
    		int64_t LastDTS = - 1;
    		while (av_read_frame(FormatContext, &Packet) >= 0) {
    			if (Packet.stream_index == AudioTrack) {
    				if (LastDTS < 0) {
    					LastDTS = Packet.dts;
    				} else if (LastDTS != Packet.dts) {
    					for (size_t i = 0; i < Frames.size(); i++)
    						if (Frames[i].DTS == Packet.dts) {
    							// The current match was consumed
    							CurrentAudioBlock = i + 1;
    							break;
    						}
    
    					av_free_packet(&Packet);
    					break;
    				}
    			}
    
    			av_free_packet(&Packet);
    		}
    	} else {
    		CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
    	}
    
    	int64_t DecodeCount;
    
    	do {
    		int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize);
    		if (Ret < 0) {
    			// FIXME
    			//Env->ThrowError("Bleh, bad audio decoding");
    		}
    
    		// Cache the block if enough blocks before it have been decoded to avoid garbage
    		if (PreDecBlocks == 0) {
    			AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, DecodingBuffer);
    			CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
    		} else {
    			PreDecBlocks--;
    		}
    
    		CurrentAudioBlock++;
    		if (CurrentAudioBlock < Frames.size())
    			CurrentSample = Frames[CurrentAudioBlock].SampleStart;
    	} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
    
    	return 0;
    }
    
    FFLAVFAudio::~FFLAVFAudio() {
    	Free(true);
    }
    
    void FFMatroskaAudio::Free(bool CloseCodec) {
    	if (CS)
    		cs_Destroy(CS);
    	if (MC.ST.fp) {
    		mkv_Close(MF);
    		fclose(MC.ST.fp);
    	}
    	if (CloseCodec)
    		avcodec_close(CodecContext);
    	av_free(CodecContext);
    }
    	
    FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
    	CodecContext = NULL;
    	AVCodec *Codec = NULL;
    	TrackInfo *TI = NULL;
    	CS = NULL;
    	Frames = (*Index)[Track];
    
    	if (Frames.size() == 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
    		throw ErrorMsg;
    	}
    
    	MC.ST.fp = fopen(SourceFile, "rb");
    	if (MC.ST.fp == NULL) {
    		_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
    		throw ErrorMsg;
    	}
    
    	setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
    
    	MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
    	if (MF == NULL) {
    		fclose(MC.ST.fp);
    		_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
    		throw ErrorMsg;
    	}
    
    	mkv_SetTrackMask(MF, ~(1 << Track));
    	TI = mkv_GetTrackInfo(MF, Track);
    
    	if (TI->CompEnabled) {
    		CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
    		if (CS == NULL) {
    			Free(false);
    			_snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
    			throw ErrorMsg;
    		}
    	}
    
    	CodecContext = avcodec_alloc_context();
    	CodecContext->extradata = (uint8_t *)TI->CodecPrivate;
    	CodecContext->extradata_size = TI->CodecPrivateSize;
    
    	Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
    	if (Codec == NULL) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Video codec not found");
    		throw ErrorMsg;
    	}
    
    	if (avcodec_open(CodecContext, Codec) < 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Could not open video codec");
    		throw ErrorMsg;
    	}
    
    	// Always try to decode a frame to make sure all required parameters are known
    	int64_t Dummy;
    	if (DecodeNextAudioBlock(&Dummy, 0, ErrorMsg, MsgSize) < 0) {
    		Free(true);
    		throw ErrorMsg;
    	}
    	avcodec_flush_buffers(CodecContext);
    
    	FillAP(AP, CodecContext, Frames);
    
    	if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
    		Free(true);
    		_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
    		throw ErrorMsg;
    	}
    
    	AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
    }
    
    FFMatroskaAudio::~FFMatroskaAudio() {
    	Free(true);
    }
    
    int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
    	const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	memset(Buf, 0, SizeConst * Count);
    
    	int PreDecBlocks = 0;
    	uint8_t *DstBuf = static_cast<uint8_t *>(Buf);
    
    	// Fill with everything in the cache
    	int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
    	// Was everything in the cache?
    	if (CacheEnd == Start + Count)
    		return 0;
    
    	int CurrentAudioBlock;
    	// Is seeking required to decode the requested samples?
    //	if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
    	if (CurrentSample != CacheEnd) {
    		PreDecBlocks = 15;
    		CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);
    		avcodec_flush_buffers(CodecContext);
    	} else {
    		CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
    	}
    
    	int64_t DecodeCount;
    
    	do {
    		int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize);
    		if (Ret < 0) {
    			// FIXME
    			//Env->ThrowError("Bleh, bad audio decoding");
    		}
    
    		// Cache the block if enough blocks before it have been decoded to avoid garbage
    		if (PreDecBlocks == 0) {
    			AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, DecodingBuffer);
    			CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
    		} else {
    			PreDecBlocks--;
    		}
    
    		CurrentAudioBlock++;
    		if (CurrentAudioBlock < static_cast<int>(Frames.size()))
    			CurrentSample = Frames[CurrentAudioBlock].SampleStart;
    	} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
    
    	return 0;
    }
    
    int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize) {
    	const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	int Ret = -1;
    	*Count = 0;
    	uint8_t *Buf = DecodingBuffer;
    	AVPacket TempPacket;
    	InitNullPacket(&TempPacket);
    
    	unsigned int FrameSize = Frames[AudioBlock].FrameSize;
    	if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
    		return 1;
    	TempPacket.data = MC.Buffer;
    	TempPacket.size = FrameSize;
    	if (Frames[AudioBlock].KeyFrame)
    		TempPacket.flags = AV_PKT_FLAG_KEY;
    	else
    		TempPacket.flags = 0;
    
    	while (TempPacket.size > 0) {
    		int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
    		Ret = avcodec_decode_audio3(CodecContext, (int16_t *)Buf, &TempOutputBufSize, &TempPacket);
    
    		if (Ret < 0) // throw error or something?
    			goto Done;
    
    		if (Ret > 0) {
    			TempPacket.size -= Ret;
    			TempPacket.data += Ret;
    			Buf += TempOutputBufSize;
    			if (SizeConst)
    				*Count += TempOutputBufSize / SizeConst;
    		}
    	}
    
    Done:
    	return 0;
    }
    
    #ifdef HAALISOURCE
    
    void FFHaaliAudio::Free(bool CloseCodec) {
    	if (CloseCodec)
    		avcodec_close(CodecContext);
    	av_free(CodecContext);
    	delete[] CodecPrivate;
    }
    
    int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize) {
    	const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	int Ret = -1;
    	*AFirstStartTime = -1;
    	*Count = 0;
    	uint8_t *Buf = DecodingBuffer;
    	AVPacket Packet;
    	InitNullPacket(&Packet);
    
    	for (;;) {
    		CComPtr<IMMFrame> pMMF;
    		if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
    			break;
    
    		REFERENCE_TIME  Ts, Te;
    		if (*AFirstStartTime < 0 && SUCCEEDED(pMMF->GetTime(&Ts, &Te)))
    			*AFirstStartTime = Ts;
    
    		if (pMMF->GetTrack() == AudioTrack) {
    			BYTE *Data = NULL;
    			if (FAILED(pMMF->GetPointer(&Data)))
    				goto Done;
    
    			Packet.data = Data;
    			Packet.size = pMMF->GetActualDataLength();
    			if (pMMF->IsSyncPoint() == S_OK)
    				Packet.flags = AV_PKT_FLAG_KEY;
    			else
    				Packet.flags = 0;
    
    			while (Packet.size > 0) {
    				int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * 10;
    				Ret = avcodec_decode_audio3(CodecContext, (int16_t *)Buf, &TempOutputBufSize, &Packet);
    
    				if (Ret < 0) {// throw error or something?
    					goto Done;
    				}
    
    				if (Ret > 0) {
    					Packet.size -= Ret;
    					Packet.data += Ret;
    					Buf += TempOutputBufSize;
    					if (SizeConst)
    						*Count += TempOutputBufSize / SizeConst;
    				}
    			}
    
    			goto Done;
            }
    	}
    
    Done:
    	return Ret;
    }
    
    FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
    	CodecPrivate = NULL;
    	AVCodec *Codec = NULL;
    	CodecContext = NULL;
    	AudioTrack = Track;
    	Frames = (*Index)[AudioTrack];
    
    	if (Frames.size() == 0) {
    		_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
    		throw ErrorMsg;
    	}
    
    	CLSID clsid = HAALI_TS_Parser;
    	if (SourceMode == 1)
    		clsid = HAALI_OGM_Parser;
    
    	if (FAILED(pMMC.CoCreateInstance(clsid))) {
    		_snprintf(ErrorMsg, MsgSize, "Can't create parser");
    		throw ErrorMsg;
    	}
    
    	CComPtr<IMemAlloc> pMA;
    	if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
    		_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
    		throw ErrorMsg;
    	}
    
    	CComPtr<IMMStream> pMS;
    	if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
    		_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
    		throw ErrorMsg;
    	}
    
    	WCHAR WSourceFile[2048];
    	mbstowcs(WSourceFile, SourceFile, 2000);
    	CComQIPtr<IMMStreamOpen> pMSO(pMS);
    	if (FAILED(pMSO->Open(WSourceFile))) {
    		_snprintf(ErrorMsg, MsgSize, "Can't open file");
    		throw ErrorMsg;
    	}
    
    	if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
    		_snprintf(ErrorMsg, MsgSize, "Can't parse file");
    		throw ErrorMsg;
    	}
    
    	int CodecPrivateSize = 0;
    	int CurrentTrack = 0;
    	CComPtr<IEnumUnknown> pEU;
    	CComQIPtr<IPropertyBag> pBag;
    	if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
    		CComPtr<IUnknown> pU;
    		while (pEU->Next(1, &pU, NULL) == S_OK) {
    			if (CurrentTrack++ == Track) {
    				pBag = pU;
    
    				if (pBag) {
    					CComVariant pV;
    
    					pV.Clear();
    					if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) {
    						CodecPrivateSize = vtSize(pV);
    						CodecPrivate = new uint8_t[CodecPrivateSize];
    						vtCopy(pV, CodecPrivate);
    					}
    
    					pV.Clear();
    					if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
    						char ACodecID[2048];
    						wcstombs(ACodecID, pV.bstrVal, 2000);
    						Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, CodecPrivate));
    					}
    				}
    			}
    			pU = NULL;
    		}
    	}
    
    	CodecContext = avcodec_alloc_context();
    	CodecContext->extradata = CodecPrivate;
    	CodecContext->extradata_size = CodecPrivateSize;
    
    	if (Codec == NULL) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
    		throw ErrorMsg;
    	}
    
    	if (avcodec_open(CodecContext, Codec) < 0) {
    		Free(false);
    		_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
    		throw ErrorMsg;
    	}
    
    	// Always try to decode a frame to make sure all required parameters are known
    	int64_t Dummy1, Dummy2;
    	if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) {
    		Free(true);
    		throw ErrorMsg;
    	}
    	pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
    	avcodec_flush_buffers(CodecContext);
    
    	FillAP(AP, CodecContext, Frames);
    
    	if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
    		Free(true);
    		_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
    		throw ErrorMsg;
    	}
    
    	AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
    }
    
    FFHaaliAudio::~FFHaaliAudio() {
    	Free(true);
    }
    
    int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
    	const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
    	memset(Buf, 0, SizeConst * Count);
    	bool HasSeeked = false;
    
    	int PreDecBlocks = 0;
    	uint8_t *DstBuf = static_cast<uint8_t *>(Buf);
    
    	// Fill with everything in the cache
    	int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
    	// Was everything in the cache?
    	if (CacheEnd == Start + Count)
    		return 0;
    
    	int CurrentAudioBlock;
    	// Is seeking required to decode the requested samples?
    //	if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
    	if (CurrentSample != CacheEnd) {
    		PreDecBlocks = 15;
    		CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks - 20, (int64_t)0);
    		pMMC->Seek(Frames[CurrentAudioBlock].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
    		avcodec_flush_buffers(CodecContext);
    		HasSeeked = true;
    	} else {
    		CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
    	}
    
    	int64_t FirstTime, DecodeCount;
    
    	do {
    		int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize);
    		if (Ret < 0) {
    			// FIXME
    			//Env->ThrowError("Bleh, bad audio decoding");
    		}
    
    		if (HasSeeked) {
    			CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime);	
    			HasSeeked = false;
    		}
    
    		// Cache the block if enough blocks before it have been decoded to avoid garbage
    		if (PreDecBlocks == 0) {
    			AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, DecodingBuffer);
    			CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
    		} else {
    			PreDecBlocks--;
    		}
    
    		CurrentAudioBlock++;
    		if (CurrentAudioBlock < static_cast<int>(Frames.size()))
    			CurrentSample = Frames[CurrentAudioBlock].SampleStart;
    	} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
    
    	return 0;
    }
    
    #endif // HAALISOURCE