FFmpegSource 1.5

Originally committed to SVN as r1459.
This commit is contained in:
Fredrik Mellbin 2007-07-29 20:49:50 +00:00
parent 8d69e930f6
commit c62c674975
4 changed files with 45 additions and 16 deletions

View file

@ -23,7 +23,7 @@ int FFBase::FrameFromDTS(int64_t ADTS) {
int FFBase::ClosestFrameFromDTS(int64_t ADTS) { int FFBase::ClosestFrameFromDTS(int64_t ADTS) {
int Frame = 0; int Frame = 0;
int64_t BestDiff = 0xFFFFFFFFFFFFFF; int64_t BestDiff = 0xFFFFFFFFFFFFFFLL;
for (int i = 0; i < (int)FrameToDTS.size(); i++) { for (int i = 0; i < (int)FrameToDTS.size(); i++) {
int64_t CurrentDiff = FFABS(FrameToDTS[i].DTS - ADTS); int64_t CurrentDiff = FFABS(FrameToDTS[i].DTS - ADTS);
if (CurrentDiff < BestDiff) { if (CurrentDiff < BestDiff) {
@ -51,7 +51,8 @@ bool FFBase::LoadFrameInfoFromFile(const char *AVideoCacheFile, const char *ASou
if (!CacheFile) if (!CacheFile)
return false; return false;
if (fscanf(CacheFile, "%d\r\n", &VI.num_frames) <= 0) { if (fscanf(CacheFile, "%d\r\n", &VI.num_frames) <= 0 || VI.num_frames <= 0) {
VI.num_frames = 0;
fclose(CacheFile); fclose(CacheFile);
return false; return false;
} }
@ -106,11 +107,14 @@ bool FFBase::PrepareAudioCache(const char *AAudioCacheFile, const char *ASource,
if (!strcmp(AAudioCacheFile, "")) if (!strcmp(AAudioCacheFile, ""))
AAudioCacheFile = DefaultCacheFilename; AAudioCacheFile = DefaultCacheFilename;
bool IsWritable = false;
AudioCache = fopen(AAudioCacheFile, "rb"); AudioCache = fopen(AAudioCacheFile, "rb");
if (!AudioCache) { if (!AudioCache) {
AudioCache = fopen(AAudioCacheFile, "wb+"); AudioCache = fopen(AAudioCacheFile, "wb+");
if (!AudioCache) if (!AudioCache)
Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing"); Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing");
IsWritable = true;
return false; return false;
} }
@ -121,9 +125,11 @@ bool FFBase::PrepareAudioCache(const char *AAudioCacheFile, const char *ASource,
return true; return true;
} }
AudioCache = freopen(AAudioCacheFile, "wb", AudioCache); if (!IsWritable) {
AudioCache = freopen(AAudioCacheFile, "wb+", AudioCache);
if (!AudioCache) if (!AudioCache)
Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing"); Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing");
}
return false; return false;
} }
@ -149,7 +155,9 @@ void FFBase::InitPP(int AWidth, int AHeight, const char *APPString, int AQuality
default: default:
Env->ThrowError("FFmpegSource: Input format is not supported for postprocessing"); Env->ThrowError("FFmpegSource: Input format is not supported for postprocessing");
} }
PPContext = pp_get_context(VI.width, VI.height, Flags); PPContext = pp_get_context(VI.width, VI.height, Flags);
if (avpicture_alloc(&PPPicture, APixelFormat, AWidth, AHeight) < 0) if (avpicture_alloc(&PPPicture, APixelFormat, AWidth, AHeight) < 0)
Env->ThrowError("FFmpegSource: Failed to allocate picture"); Env->ThrowError("FFmpegSource: Failed to allocate picture");
} }

View file

@ -110,6 +110,13 @@ public:
if (avcodec_open(VideoCodecContext, VideoCodec) < 0) if (avcodec_open(VideoCodecContext, VideoCodec) < 0)
Env->ThrowError("FFmpegSource: Could not open video codec"); Env->ThrowError("FFmpegSource: Could not open video codec");
// Fix for mpeg2 and other formats where decoding a frame is necessary to get information about the stream
if (VideoCodecContext->pix_fmt == PIX_FMT_NONE) {
int64_t Dummy;
DecodeNextFrame(DecodeFrame, &Dummy, Env);
mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME);
}
VI.image_type = VideoInfo::IT_TFF; VI.image_type = VideoInfo::IT_TFF;
VI.width = VideoTI->AV.Video.PixelWidth; VI.width = VideoTI->AV.Video.PixelWidth;
VI.height = VideoTI->AV.Video.PixelHeight; VI.height = VideoTI->AV.Video.PixelHeight;
@ -204,7 +211,7 @@ public:
if (VideoTrack >= 0) { if (VideoTrack >= 0) {
mkv_SetTrackMask(MF, ~(1 << VideoTrack)); mkv_SetTrackMask(MF, ~(1 << VideoTrack));
mkv_Seek(MF, FrameToDTS[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME); mkv_Seek(MF, FrameToDTS.front().DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
if (FrameToDTS.size() >= 2) { if (FrameToDTS.size() >= 2) {
double DTSDiff = (double)(FrameToDTS.back().DTS - FrameToDTS.front().DTS); double DTSDiff = (double)(FrameToDTS.back().DTS - FrameToDTS.front().DTS);
@ -259,7 +266,7 @@ int FFMatroskaSource::ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, Comp
DecompressedFrameSize += ReadBytes; DecompressedFrameSize += ReadBytes;
} }
} else { } else {
if (fseek(ST.fp, AFilePos, SEEK_SET)) if (_fseeki64(ST.fp, AFilePos, SEEK_SET))
Env->ThrowError("FFmpegSource: fseek(): %s", strerror(errno)); Env->ThrowError("FFmpegSource: fseek(): %s", strerror(errno));
if (BufferSize < AFrameSize) { if (BufferSize < AFrameSize) {
@ -414,7 +421,6 @@ public:
VI.height = VideoCodecContext->height; VI.height = VideoCodecContext->height;
VI.fps_denominator = FormatContext->streams[VideoTrack]->time_base.num; VI.fps_denominator = FormatContext->streams[VideoTrack]->time_base.num;
VI.fps_numerator = FormatContext->streams[VideoTrack]->time_base.den; VI.fps_numerator = FormatContext->streams[VideoTrack]->time_base.den;
VI.num_frames = (int)FormatContext->streams[VideoTrack]->duration;
// sanity check framerate // sanity check framerate
if (VI.fps_denominator > VI.fps_numerator || VI.fps_denominator <= 0 || VI.fps_numerator <= 0) { if (VI.fps_denominator > VI.fps_numerator || VI.fps_denominator <= 0 || VI.fps_numerator <= 0) {
@ -457,10 +463,9 @@ public:
// Needs to be indexed? // Needs to be indexed?
if (!ACacheIsValid || !VCacheIsValid) { if (!ACacheIsValid || !VCacheIsValid) {
AVPacket Packet; AVPacket Packet;
VI.num_frames = 0;
while (av_read_frame(FormatContext, &Packet) >= 0) { while (av_read_frame(FormatContext, &Packet) >= 0) {
if (Packet.stream_index == VideoTrack && !VCacheIsValid) { if (Packet.stream_index == VideoTrack && !VCacheIsValid) {
FrameToDTS.push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) != 0)); FrameToDTS.push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0));
VI.num_frames++; VI.num_frames++;
} else if (Packet.stream_index == AudioTrack && !ACacheIsValid) { } else if (Packet.stream_index == AudioTrack && !ACacheIsValid) {
int Size = Packet.size; int Size = Packet.size;
@ -492,10 +497,15 @@ public:
avcodec_close(AudioCodecContext); avcodec_close(AudioCodecContext);
if (VideoTrack >= 0) { if (VideoTrack >= 0) {
av_seek_frame(FormatContext, VideoTrack, FrameToDTS[0].DTS, AVSEEK_FLAG_BACKWARD); av_seek_frame(FormatContext, VideoTrack, FrameToDTS.front().DTS, AVSEEK_FLAG_BACKWARD);
if (!SaveTimecodesToFile(ATimecodes, FormatContext->streams[VideoTrack]->time_base.num * 1000, FormatContext->streams[VideoTrack]->time_base.den)) if (!SaveTimecodesToFile(ATimecodes, FormatContext->streams[VideoTrack]->time_base.num * 1000, FormatContext->streams[VideoTrack]->time_base.den))
Env->ThrowError("FFmpegSource: Failed to write timecodes"); Env->ThrowError("FFmpegSource: Failed to write timecodes");
if (FrameToDTS.size() >= 2) {
int64_t DTSDiff = (double)(FrameToDTS[1].DTS - FrameToDTS[0].DTS);
VI.fps_denominator *= DTSDiff;
}
} }
} }
@ -542,7 +552,7 @@ PVideoFrame __stdcall FFmpegSource::GetFrame(int n, IScriptEnvironment* Env) {
if (SeekMode == 0) { if (SeekMode == 0) {
if (n < CurrentFrame) { if (n < CurrentFrame) {
av_seek_frame(FormatContext, VideoTrack, FrameToDTS[0].DTS, AVSEEK_FLAG_BACKWARD); av_seek_frame(FormatContext, VideoTrack, FrameToDTS.front().DTS, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(VideoCodecContext); avcodec_flush_buffers(VideoCodecContext);
CurrentFrame = 0; CurrentFrame = 0;
} }

View file

@ -9,8 +9,19 @@ FFmpegSource Documentation
<h2>Changes</h2> <h2>Changes</h2>
<ul> <ul>
<li>1.5<ul>
<li>Fixed a bug that made avformat opened files only return audio if only the audio cache needed to be created</li>
<li>Rejects more corrupt cache files</li>
<li>Fixed crash when a 0 byte audio cache file is present</li>
<li>Improved framerate guessing for avformat which now takes takes the duration of the first frame into account</li>
<li>Fixed a bug introduced in 1.4 that would make the number of reported frames too high for files opened with avformat</li>
<li>Fixed mpeg2 and probably some other formats stored in mkv</li>
<li>Fixed issues with large mkv files and large audio cache files</li>
<li>FFmpeg is now compiled with liba52 and faad2</li>
</ul></li>
<li>1.4<ul> <li>1.4<ul>
<li>Uses the average framerate for mkv files</li>
<li>Naming scheme of cache files changed to prevent confusion with the default names in files with multiple tracks of the same type</li> <li>Naming scheme of cache files changed to prevent confusion with the default names in files with multiple tracks of the same type</li>
<li>Use mmx optimizations in swscaler when possible</li> <li>Use mmx optimizations in swscaler when possible</li>
<li>Now uses normal windows linebreaks in all files</li> <li>Now uses normal windows linebreaks in all files</li>

View file

@ -17,7 +17,7 @@ typedef struct StdIoStream StdIoStream;
*/ */
int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count) { int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count) {
size_t rd; size_t rd;
if (fseek(st->fp, pos, SEEK_SET)) { if (_fseeki64(st->fp, pos, SEEK_SET)) {
st->error = errno; st->error = errno;
return -1; return -1;
} }
@ -39,13 +39,13 @@ longlong StdIoScan(StdIoStream *st, ulonglong start, unsigned signature) {
unsigned cmp = 0; unsigned cmp = 0;
FILE *fp = st->fp; FILE *fp = st->fp;
if (fseek(fp, start, SEEK_SET)) if (_fseeki64(fp, start, SEEK_SET))
return -1; return -1;
while ((c = getc(fp)) != EOF) { while ((c = getc(fp)) != EOF) {
cmp = ((cmp << 8) | c) & 0xffffffff; cmp = ((cmp << 8) | c) & 0xffffffff;
if (cmp == signature) if (cmp == signature)
return ftell(fp) - 4; return _ftelli64(fp) - 4;
} }
return -1; return -1;