void FFMS_VideoSource::SetPP(const char *PP) { #ifdef FFMS_USE_POSTPROC if (PPMode) pp_free_mode(PPMode); PPMode = NULL; if (PP != NULL && strcmp(PP, "")) { // due to a parsing bug in libpostproc it can read beyond the end of a string // adding a ',' prevents the bug from manifesting // libav head 2011-08-26 std::string s = PP; s.append(","); PPMode = pp_get_mode_by_name_and_quality(s.c_str(), PP_QUALITY_MAX); if (!PPMode) { ResetPP(); throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT, "Invalid postprocesing settings"); } } ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); OutputFrame(DecodeFrame); #else throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED, "FFMS2 was not compiled with postprocessing support"); #endif /* FFMS_USE_POSTPROC */ }
FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, int Track) : Delay(0) , MaxCacheBlocks(50) , BytesPerSample(0) , NeedsResample(false) , CurrentSample(-1) , PacketNumber(0) , CurrentFrame(NULL) , TrackNumber(Track) , SeekOffset(0) , Index(Index) { if (Track < 0 || Track >= static_cast<int>(Index.size())) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Out of bounds track index selected"); if (Index[Track].TT != FFMS_TYPE_AUDIO) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Not an audio track"); if (Index[Track].empty()) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Audio track contains no audio frames"); if (!Index.CompareFileSignature(SourceFile)) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, "The index does not match the source file"); Frames = Index[Track]; Index.AddRef(); }
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode) : FFMS_AudioSource(SourceFile, Index, Track) , FormatContext(NULL) , LastValidTS(AV_NOPTS_VALUE) { LAVFOpenFile(SourceFile, FormatContext); CodecContext.reset(FormatContext->streams[TrackNumber]->codec); assert(CodecContext); AVCodec *Codec = avcodec_find_decoder(CodecContext->codec_id); try { if (!Codec) throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Audio codec not found"); if (avcodec_open2(CodecContext, Codec, NULL) < 0) throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Could not open audio codec"); } catch (...) { avformat_close_input(&FormatContext); throw; } if (Frames.back().PTS == Frames.front().PTS) SeekOffset = -1; else SeekOffset = 10; Init(Index, DelayMode); }
bool FFLAVFVideo::SeekTo(int n, int SeekOffset) { if (SeekMode >= 0) { int TargetFrame = n + SeekOffset; if (TargetFrame < 0) throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN, "Frame accurate seeking is not possible in this file"); if (SeekMode < 3) TargetFrame = Frames.FindClosestVideoKeyFrame(TargetFrame); if (SeekMode == 0) { if (n < CurrentFrame) { av_seek_frame(FormatContext, VideoTrack, Frames[0].PTS, AVSEEK_FLAG_BACKWARD); FlushBuffers(CodecContext); CurrentFrame = 0; DelayCounter = 0; InitialDecode = 1; } } else { // 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat if (n < CurrentFrame || TargetFrame > CurrentFrame + 10 || (SeekMode == 3 && n > CurrentFrame + 10)) { av_seek_frame(FormatContext, VideoTrack, Frames[TargetFrame].PTS, AVSEEK_FLAG_BACKWARD); FlushBuffers(CodecContext); DelayCounter = 0; InitialDecode = 1; return true; } } } else if (n < CurrentFrame) { throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT, "Non-linear access attempted"); } return false; }
FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, int DelayMode, FFMS_ErrorInfo *ErrorInfo) { try { switch (Index->Decoder) { case FFMS_SOURCE_LAVF: return new FFLAVFAudio(SourceFile, Track, *Index, DelayMode); case FFMS_SOURCE_MATROSKA: return new FFMatroskaAudio(SourceFile, Track, *Index, DelayMode); #ifdef HAALISOURCE case FFMS_SOURCE_HAALIMPEG: if (HasHaaliMPEG) return new FFHaaliAudio(SourceFile, Track, *Index, FFMS_SOURCE_HAALIMPEG, DelayMode); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable"); case FFMS_SOURCE_HAALIOGG: if (HasHaaliOGG) return new FFHaaliAudio(SourceFile, Track, *Index, FFMS_SOURCE_HAALIOGG, DelayMode); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable"); #endif default: throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format"); } } catch (FFMS_Exception &e) { e.CopyOut(ErrorInfo); return NULL; } }
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; }
FFMS_Indexer *CreateIndexer(const char *Filename, FFMS_Sources Demuxer) { AVFormatContext *FormatContext = NULL; if (avformat_open_input(&FormatContext, Filename, NULL, NULL) != 0) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Can't open '") + Filename + "'"); // Demuxer was not forced, probe for the best one to use if (Demuxer == FFMS_SOURCE_DEFAULT) { // Do matroska indexing instead? if (!strncmp(FormatContext->iformat->name, "matroska", 8)) { avformat_close_input(&FormatContext); return CreateMatroskaIndexer(Filename); } #ifdef HAALISOURCE // Do haali ts indexing instead? if (HasHaaliMPEG && (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts"))) { avformat_close_input(&FormatContext); return CreateHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG); } if (HasHaaliOGG && !strcmp(FormatContext->iformat->name, "ogg")) { avformat_close_input(&FormatContext); return CreateHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG); } #endif return CreateLavfIndexer(Filename, FormatContext); } // someone forced a demuxer, use it if (Demuxer != FFMS_SOURCE_LAVF) avformat_close_input(&FormatContext); #if !defined(HAALISOURCE) if (Demuxer == FFMS_SOURCE_HAALIOGG || Demuxer == FFMS_SOURCE_HAALIMPEG) { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Your binary was not compiled with support for Haali's DirectShow parsers"); } #endif // !defined(HAALISOURCE) switch (Demuxer) { case FFMS_SOURCE_LAVF: return CreateLavfIndexer(Filename, FormatContext); #ifdef HAALISOURCE case FFMS_SOURCE_HAALIOGG: if (!HasHaaliOGG) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali's Ogg parser is not available"); return CreateHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG); case FFMS_SOURCE_HAALIMPEG: if (!HasHaaliMPEG) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali's MPEG PS/TS parser is not available"); return CreateHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG); #endif case FFMS_SOURCE_MATROSKA: return CreateMatroskaIndexer(Filename); default: throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT, "Invalid demuxer requested"); } }
FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index &Index, int Track, int Threads) : Index(Index) , CodecContext(NULL) { if (Track < 0 || Track >= static_cast<int>(Index.size())) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Out of bounds track index selected"); if (Index[Track].TT != FFMS_TYPE_VIDEO) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Not a video track"); if (Index[Track].empty()) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Video track contains no frames"); if (!Index.CompareFileSignature(SourceFile)) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, "The index does not match the source file"); Frames = Index[Track]; VideoTrack = Track; memset(&VP, 0, sizeof(VP)); SWS = NULL; LastFrameNum = 0; CurrentFrame = 1; DelayCounter = 0; InitialDecode = 1; LastFrameHeight = -1; LastFrameWidth = -1; LastFramePixelFormat = PIX_FMT_NONE; TargetHeight = -1; TargetWidth = -1; TargetResizer = 0; OutputFormat = PIX_FMT_NONE; OutputColorSpace = AVCOL_SPC_UNSPECIFIED; OutputColorRange = AVCOL_RANGE_UNSPECIFIED; InputFormatOverridden = false; InputFormat = PIX_FMT_NONE; InputColorSpace = AVCOL_SPC_UNSPECIFIED; InputColorRange = AVCOL_RANGE_UNSPECIFIED; if (Threads < 1) DecodingThreads = GetNumberOfLogicalCPUs(); else DecodingThreads = Threads; DecodeFrame = avcodec_alloc_frame(); LastDecodedFrame = avcodec_alloc_frame(); // Dummy allocations so the unallocated case doesn't have to be handled later avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16); Index.AddRef(); }
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) { if (avformat_open_input(&FormatContext, SourceFile, NULL, NULL) != 0) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Couldn't open '") + SourceFile + "'"); if (avformat_find_stream_info(FormatContext,NULL) < 0) { avformat_close_input(&FormatContext); FormatContext = NULL; throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Couldn't find stream information"); } }
void FFMS_AudioSource::SetOutputFormat(FFMS_ResampleOptions const& opt) { if (opt.SampleRate != AP.SampleRate) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED, "Sample rate changes are currently unsupported."); #ifndef FFMS_RESAMPLING_ENABLED if (opt.SampleFormat != AP.SampleFormat || opt.SampleRate != AP.SampleRate || opt.ChannelLayout != AP.ChannelLayout) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED, "FFMS was not built with resampling enabled. The only supported conversion is interleaving planar audio."); #endif #ifdef WITH_AVRESAMPLE if (opt.SampleFormat != AP.SampleFormat || opt.ChannelLayout != AP.ChannelLayout) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED, "FFMS was not built with FFMPEG resampling enabled."); #endif // Cache stores audio in the output format, so clear it and reopen the file Cache.clear(); PacketNumber = 0; ReopenFile(); FlushBuffers(CodecContext); BytesPerSample = av_get_bytes_per_sample(static_cast<AVSampleFormat>(opt.SampleFormat)) * av_get_channel_layout_nb_channels(opt.ChannelLayout); NeedsResample = opt.SampleFormat != (int)CodecContext->sample_fmt || opt.SampleRate != AP.SampleRate || opt.ChannelLayout != AP.ChannelLayout || opt.ForceResample; #ifdef FFMS_RESAMPLING_ENABLED if (!NeedsResample) return; FFResampleContext newContext; SetOptions(opt, newContext, resample_options); av_opt_set_int(newContext, "in_sample_rate", AP.SampleRate, 0); av_opt_set_int(newContext, "in_sample_fmt", CodecContext->sample_fmt, 0); av_opt_set_int(newContext, "in_channel_layout", AP.ChannelLayout, 0); av_opt_set_int(newContext, "out_sample_rate", opt.SampleRate, 0); #ifdef WITH_SWRESAMPLE av_opt_set_channel_layout(newContext, "out_channel_layout", opt.ChannelLayout, 0); av_opt_set_sample_fmt(newContext, "out_sample_fmt", (AVSampleFormat)opt.SampleFormat, 0); #endif if (ffms_open(newContext)) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNKNOWN, "Could not open avresample context"); newContext.swap(ResampleContext); #endif }
void FFMS_VideoSource::ReAdjustOutputFormat() { if (SWS) { sws_freeContext(SWS); SWS = nullptr; } DetectInputFormat(); OutputFormat = FindBestPixelFormat(TargetPixelFormats, InputFormat); if (OutputFormat == FFMS_PIX_FMT(NONE)) { ResetOutputFormat(); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, "No suitable output format found"); } OutputColorRange = handle_jpeg(&OutputFormat); if (OutputColorRange == AVCOL_RANGE_UNSPECIFIED) OutputColorRange = CodecContext->color_range; if (OutputColorRange == AVCOL_RANGE_UNSPECIFIED) OutputColorRange = InputColorRange; OutputColorSpace = CodecContext->colorspace; if (OutputColorSpace == AVCOL_SPC_UNSPECIFIED) OutputColorSpace = InputColorSpace; if (InputFormat != OutputFormat || TargetWidth != CodecContext->width || TargetHeight != CodecContext->height || InputColorSpace != OutputColorSpace || InputColorRange != OutputColorRange) { SWS = GetSwsContext( CodecContext->width, CodecContext->height, InputFormat, InputColorSpace, InputColorRange, TargetWidth, TargetHeight, OutputFormat, OutputColorSpace, OutputColorRange, TargetResizer); if (!SWS) { ResetOutputFormat(); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, "Failed to allocate SWScale context"); } } av_freep(&SWSFrameData[0]); if (av_image_alloc(SWSFrameData, SWSFrameLinesize, TargetWidth, TargetHeight, OutputFormat, 4) < 0) throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_ALLOCATION_FAILED, "Could not allocate frame with new resolution."); }
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext, int Track) { if (avformat_open_input(&FormatContext, SourceFile, nullptr, nullptr) != 0) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Couldn't open '") + SourceFile + "'"); if (avformat_find_stream_info(FormatContext,nullptr) < 0) { avformat_close_input(&FormatContext); FormatContext = nullptr; throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Couldn't find stream information"); } for (int i = 0; i < (int) FormatContext->nb_streams; i++) if (i != Track) FormatContext->streams[i]->discard = AVDISCARD_ALL; }
int64_t FileHandle::Size() { int64_t size = avio_size(avio); if (size < 0) throw FFMS_Exception(error_source, FFMS_ERROR_FILE_READ, "Failed to get file size for '" + filename +"'"); return size; }
size_t FileHandle::Read(char *buffer, size_t size) { int count = avio_read(avio, (unsigned char *)buffer, size); if (count < 0) throw FFMS_Exception(error_source, FFMS_ERROR_FILE_READ, "Failed to read from '" + filename + "'"); return (size_t)count; }
int64_t FileHandle::Tell() { int64_t ret = avio_tell(avio); if (ret < 0) throw FFMS_Exception(error_source, error_cause, "Failed to read position in '" + filename + "'"); return ret; }
void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track) { // Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. if (!DecodeFrame->nb_samples) return; if (!AudioContext.W64Writer) { FFMS_AudioProperties AP; FillAP(AP, AudioContext.CodecContext, (*Index)[Track]); int FNSize = (*ANC)(SourceFile.c_str(), Track, &AP, NULL, 0, ANCPrivate); if (FNSize <= 0) { DumpMask = DumpMask & ~(1 << Track); return; } int Format = av_get_packed_sample_fmt(AudioContext.CodecContext->sample_fmt); std::vector<char> WName(FNSize + 1); (*ANC)(SourceFile.c_str(), Track, &AP, &WName[0], FNSize, ANCPrivate); WName.back() = 0; try { AudioContext.W64Writer = new Wave64Writer(WName.data(), av_get_bytes_per_sample(AudioContext.CodecContext->sample_fmt), AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (Format == AV_SAMPLE_FMT_FLT) || (Format == AV_SAMPLE_FMT_DBL)); } catch (...) { throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE, "Failed to write wave data"); } } AudioContext.W64Writer->WriteData(*DecodeFrame); }
void FFMS_Indexer::SetErrorHandling(int ErrorHandling) { if (ErrorHandling != FFMS_IEH_ABORT && ErrorHandling != FFMS_IEH_CLEAR_TRACK && ErrorHandling != FFMS_IEH_STOP_TRACK && ErrorHandling != FFMS_IEH_IGNORE) throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT, "Invalid error handling mode specified"); this->ErrorHandling = ErrorHandling; }
size_t FileHandle::Write(const char *buffer, size_t size) { avio_write(avio, (const unsigned char *)buffer, size); avio_flush(avio); if (avio->error < 0) throw FFMS_Exception(error_source, FFMS_ERROR_FILE_WRITE, "Failed to write to '" + filename + "'"); return size; }
// this might look stupid, but we have actually had crashes caused by not checking like this. static void SanityCheckFrameForData(AVFrame *Frame) { for (int i = 0; i < 4; i++) { if (Frame->data[i] != NULL && Frame->linesize[i] > 0) return; } throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Insanity detected: decoder returned an empty frame"); }
void FFMS_VideoSource::ReAdjustOutputFormat() { if (SWS) { sws_freeContext(SWS); SWS = NULL; } DetectInputFormat(); OutputFormat = FindBestPixelFormat(TargetPixelFormats, InputFormat); if (OutputFormat == PIX_FMT_NONE) { ResetOutputFormat(); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, "No suitable output format found"); } OutputColorRange = handle_jpeg(&OutputFormat); if (OutputColorRange == AVCOL_RANGE_UNSPECIFIED) OutputColorRange = CodecContext->color_range; if (OutputColorRange == AVCOL_RANGE_UNSPECIFIED) OutputColorRange = InputColorRange; OutputColorSpace = CodecContext->colorspace; if (OutputColorSpace == AVCOL_SPC_UNSPECIFIED) OutputColorSpace = InputColorSpace; if (InputFormat != OutputFormat || TargetWidth != CodecContext->width || TargetHeight != CodecContext->height || InputColorSpace != OutputColorSpace || InputColorRange != OutputColorRange) { SWS = GetSwsContext( CodecContext->width, CodecContext->height, InputFormat, InputColorSpace, InputColorRange, TargetWidth, TargetHeight, OutputFormat, OutputColorSpace, OutputColorRange, TargetResizer); if (!SWS) { ResetOutputFormat(); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, "Failed to allocate SWScale context"); } } avpicture_free(&SWSFrame); avpicture_alloc(&SWSFrame, OutputFormat, TargetWidth, TargetHeight); }
FFMS_Index::FFMS_Index(const char *IndexFile) : RefCount(1) { ZipFile zf(IndexFile, "rb"); // Read the index file header if (zf.Read<uint32_t>() != INDEXID) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("'") + IndexFile + "' is not a valid index file"); if (zf.Read<uint32_t>() != FFMS_VERSION) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("'") + IndexFile + "' is not the expected index version"); uint32_t Tracks = zf.Read<uint32_t>(); Decoder = zf.Read<uint32_t>(); ErrorHandling = zf.Read<uint32_t>(); if (!(Decoder & FFMS_GetEnabledSources())) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, "The source which this index was created with is not available"); if (zf.Read<uint32_t>() != avutil_version() || zf.Read<uint32_t>() != avformat_version() || zf.Read<uint32_t>() != avcodec_version() || zf.Read<uint32_t>() != swscale_version()) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("A different FFmpeg build was used to create '") + IndexFile + "'"); Filesize = zf.Read<int64_t>(); zf.Read(Digest, sizeof(Digest)); reserve(Tracks); try { for (size_t i = 0; i < Tracks; ++i) push_back(FFMS_Track(zf)); } catch (FFMS_Exception const&) { throw; } catch (...) { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Unknown error while reading index information in '") + IndexFile + "'"); } }
FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) { this->FormatContext = FormatContext; if (avformat_find_stream_info(FormatContext,NULL) < 0) { avformat_close_input(&FormatContext); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Couldn't find stream information"); } }
FileHandle::FileHandle(const char *filename, const char *mode, int error_source, int error_cause) : avio(ffms_fopen(filename, mode)) , filename(filename) , error_source(error_source) , error_cause(error_cause) { if (!avio) throw FFMS_Exception(error_source, FFMS_ERROR_NO_FILE, "Failed to open '" + this->filename + "'"); }
void FFMS_AudioSource::SetOutputFormat(const FFMS_ResampleOptions *opt) { if (!Cache.empty()) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_USER, "Cannot change the output format after audio decoding has begun"); if (opt->SampleRate != AP.SampleRate) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED, "Sample rate changes are currently unsupported."); #ifndef WITH_AVRESAMPLE if (opt->SampleFormat != AP.SampleFormat || opt->SampleRate != AP.SampleRate || opt->ChannelLayout != AP.ChannelLayout) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNSUPPORTED, "FFMS was not built with resampling enabled. The only supported conversion is interleaving planar audio."); #endif BytesPerSample = av_get_bytes_per_sample(static_cast<AVSampleFormat>(opt->SampleFormat)) * av_get_channel_layout_nb_channels(opt->ChannelLayout); NeedsResample = opt->SampleFormat != (int)CodecContext->sample_fmt || opt->SampleRate != AP.SampleRate || opt->ChannelLayout != AP.ChannelLayout || opt->ForceResample; #ifdef WITH_AVRESAMPLE if (!NeedsResample) return; std::auto_ptr<FFMS_ResampleOptions> oldOptions(ReadOptions(ResampleContext, resample_options)); SetOptions(opt, ResampleContext, resample_options); av_opt_set_int(ResampleContext, "in_sample_rate", AP.SampleRate, 0); av_opt_set_int(ResampleContext, "in_sample_fmt", CodecContext->sample_fmt, 0); av_opt_set_int(ResampleContext, "in_channel_layout", AP.ChannelLayout, 0); if (avresample_open(ResampleContext)) { SetOptions(oldOptions.get(), ResampleContext, resample_options); if (avresample_open(ResampleContext) < 0) throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNKNOWN, "Could not re-open old avresample context"); else throw FFMS_Exception(FFMS_ERROR_RESAMPLING, FFMS_ERROR_UNKNOWN, "Could not open avresample context"); } #endif }
FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) { ClearErrorInfo(ErrorInfo); try { if (!Index->CompareFileSignature(SourceFile)) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, "The index does not belong to the file"); } catch (FFMS_Exception &e) { return e.CopyOut(ErrorInfo); } return FFMS_ERROR_SUCCESS; }
FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, int Track) : TrackNumber(Track) { if (Track < 0 || Track >= static_cast<int>(Index.size())) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Out of bounds track index selected"); if (Index[Track].TT != FFMS_TYPE_AUDIO) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Not an audio track"); if (Index[Track].empty()) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Audio track contains no audio frames"); if (!Index.CompareFileSignature(SourceFile)) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, "The index does not match the source file"); Frames = Index[Track]; }
TrackCompressionContext::TrackCompressionContext(MatroskaFile *MF, TrackInfo *TI, unsigned int Track) : CS(NULL) , CompressedPrivateData(NULL) , CompressedPrivateDataSize(0) , CompressionMethod(TI->CompMethod) { if (CompressionMethod == COMP_ZLIB) { char ErrorMessage[512]; CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); if (CS == NULL) { std::ostringstream buf; buf << "Can't create MKV track decompressor: " << ErrorMessage; throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); } } else if (CompressionMethod == COMP_PREPEND) { CompressedPrivateData = TI->CompMethodPrivate; CompressedPrivateDataSize = TI->CompMethodPrivateSize; } else { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Can't create MKV track decompressor: unknown or unsupported compression method"); } }
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) { ClearErrorInfo(ErrorInfo); for (int i = 0; i < static_cast<int>(Index->size()); i++) if ((*Index)[i].TT == TrackType && !(*Index)[i].empty()) return i; try { throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, "No suitable, indexed track found"); } catch (FFMS_Exception &e) { e.CopyOut(ErrorInfo); return -1; } }
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode) : FFMS_AudioSource(SourceFile, Index, Track) , TI(NULL) { if (!(MC.ST.fp = ffms_fopen(SourceFile, "rb"))) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Can't open '") + SourceFile + "': " + strerror(errno)); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); if (!(MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)))) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Can't parse Matroska file: ") + ErrorMessage); TI = mkv_GetTrackInfo(MF, Track); assert(TI); if (TI->CompEnabled) TCC.reset(new TrackCompressionContext(MF, TI, Track)); CodecContext.reset(avcodec_alloc_context3(NULL), DeleteMatroskaCodecContext); assert(CodecContext); AVCodec *Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate, 0, TI->AV.Audio.BitDepth)); if (!Codec) { mkv_Close(MF); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Audio codec not found"); } InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); if (avcodec_open2(CodecContext, Codec, NULL) < 0) { mkv_Close(MF); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Could not open audio codec"); } Init(Index, DelayMode); }
void FlushBuffers(AVCodecContext *CodecContext) { if (CodecContext->codec->flush) avcodec_flush_buffers(CodecContext); else { // If the codec doesn't have flush(), it might not need it... or it // might need it and just not implement it as in the case of VC-1, so // close and reopen the codec const AVCodec *codec = CodecContext->codec; avcodec_close(CodecContext); // Whether or not codec is const varies between versions if (avcodec_open2(CodecContext, const_cast<AVCodec *>(codec), 0) < 0) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_CODEC, "Couldn't re-open codec."); } }