Author: stippi Date: 2010-08-27 15:29:52 +0200 (Fri, 27 Aug 2010) New Revision: 38401 Changeset: http://dev.haiku-os.org/changeset/38401 Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp Log: * Optionally separate tracing output for FindKeyFrame(). * Output more information in AVFormatReader::StreamCookie::Open(), AVInputFormat flags for example. * Added CODEC_ID_AAC handling when codecTag is 0. Adds support for AAC in Matroska containers when the ffmpeg plugin is enabled to handle those. * Added some rounding to frame<->time conversions. * AVFormatReader::StreamCookie::Seek() forgot to pass the seek flags to av_seek_frame(). * The most important fix is this, though: There are formats which build the keyframe index on the fly, while parsing the stream!! These means we can only seek to real keyframes for parts of the stream that has already been decoded. Handle this situation by assuming we can seek to the requested frame/time. This change fixes the use of the AVFormatReader as MP3 reader. * Anothe important fix is to ignore the nb_frames member of the stream for the total frame count. This makes MP4 movies also work perfectly now when the AVFormatReader is used for them. Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp =================================================================== --- haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp 2010-08-27 13:21:03 UTC (rev 38400) +++ haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp 2010-08-27 13:29:52 UTC (rev 38401) @@ -32,11 +32,13 @@ # define TRACE printf # define TRACE_IO(a...) # define TRACE_SEEK(a...) +# define TRACE_FIND(a...) # define TRACE_PACKET(a...) #else # define TRACE(a...) # define TRACE_IO(a...) # define TRACE_SEEK(a...) +# define TRACE_FIND(a...) # define TRACE_PACKET(a...) #endif @@ -160,6 +162,8 @@ bool fReusePacket; media_format fFormat; + + bool fStreamBuildsIndexWhileReading; }; @@ -175,7 +179,9 @@ fStream(NULL), fVirtualIndex(-1), - fReusePacket(false) + fReusePacket(false), + + fStreamBuildsIndexWhileReading(false) { memset(&fIOBuffer, 0, sizeof(fIOBuffer)); memset(&fFormat, 0, sizeof(media_format)); @@ -222,7 +228,17 @@ TRACE("AVFormatReader::StreamCookie::Open() - " "av_probe_input_format(): %s\n", inputFormat->name); + TRACE(" flags:%s%s%s%s%s\n", + (inputFormat->flags & AVFMT_GLOBALHEADER) ? " AVFMT_GLOBALHEADER" : "", + (inputFormat->flags & AVFMT_NOTIMESTAMPS) ? " AVFMT_NOTIMESTAMPS" : "", + (inputFormat->flags & AVFMT_GENERIC_INDEX) ? " AVFMT_GENERIC_INDEX" : "", + (inputFormat->flags & AVFMT_TS_DISCONT) ? " AVFMT_TS_DISCONT" : "", + (inputFormat->flags & AVFMT_VARIABLE_FPS) ? " AVFMT_VARIABLE_FPS" : "" + ); + fStreamBuildsIndexWhileReading + = (inputFormat->flags & AVFMT_GENERIC_INDEX) != 0; + const DemuxerFormat* demuxerFormat = demuxer_format_for(inputFormat); if (demuxerFormat == NULL) { // We could support this format, but we don't want to. Bail out. @@ -257,7 +273,8 @@ } TRACE("AVFormatReader::StreamCookie::Open() - " - "av_find_stream_info() success!\n"); + "av_find_stream_info() success! Seek should be by bytes: %d\n", + (inputFormat->flags & AVFMT_TS_DISCONT) != 0); return B_OK; } @@ -426,6 +443,10 @@ description.family = B_QUICKTIME_FORMAT_FAMILY; codecTag = B_BENDIAN_TO_HOST_INT32('.mp3'); break; + case CODEC_ID_AAC: + description.family = B_MISC_FORMAT_FAMILY; + codecTag = 'mp4a'; + break; default: fprintf(stderr, "ffmpeg codecTag is null, codec_id " "unknown 0x%x\n", codecContext->codec_id); @@ -460,7 +481,7 @@ else if (codecContext->codec_id == CODEC_ID_MP2) description.u.mpeg.id = B_MPEG_2_AUDIO_LAYER_2; else if (codecContext->codec_id == CODEC_ID_MP3) - description.u.mpeg.id = B_MPEG_1_AUDIO_LAYER_3; + description.u.mpeg.id = B_MPEG_2_AUDIO_LAYER_3; // TODO: Add some more... else description.u.mpeg.id = B_MPEG_ANY; @@ -717,6 +738,13 @@ double frameRate = FrameRate(); TRACE(" frameRate: %.4f\n", frameRate); + if (fStream->start_time != kNoPTSValue) { + bigtime_t startTime = _ConvertFromStreamTimeBase(fStream->start_time); + TRACE(" start_time: %lld or %.5fs\n", startTime, + startTime / 1000000.0); + // TODO: Handle start time in FindKeyFrame()?! + } + // TODO: This is obviously not working correctly for all stream types... // It seems that the calculations here are correct, because they work // for a couple of streams and are in line with the documentation, but @@ -736,15 +764,43 @@ TRACE(" stream duration: N/A\n"); } - TRACE(" duration: %lld or %.2fs\n", *duration, *duration / 1000000.0); + TRACE(" duration: %lld or %.5fs\n", *duration, *duration / 1000000.0); + #if 0 + if (fStream->nb_index_entries > 0) { + TRACE(" dump of index entries:\n"); + int count = 5; + int firstEntriesCount = min_c(fStream->nb_index_entries, count); + int i = 0; + for (; i < firstEntriesCount; i++) { + AVIndexEntry& entry = fStream->index_entries[i]; + bigtime_t timeGlobal = entry.timestamp; + bigtime_t timeNative = _ConvertFromStreamTimeBase(timeGlobal); + TRACE(" [%d] native: %.5fs global: %.5fs\n", i, + timeNative / 1000000.0f, timeGlobal / 1000000.0f); + } + if (fStream->nb_index_entries - count > i) { + i = fStream->nb_index_entries - count; + TRACE(" ...\n"); + for (; i < fStream->nb_index_entries; i++) { + AVIndexEntry& entry = fStream->index_entries[i]; + bigtime_t timeGlobal = entry.timestamp; + bigtime_t timeNative = _ConvertFromStreamTimeBase(timeGlobal); + TRACE(" [%d] native: %.5fs global: %.5fs\n", i, + timeNative / 1000000.0f, timeGlobal / 1000000.0f); + } + } + } + #endif + *frameCount = fStream->nb_frames; - if (*frameCount == 0) { +// if (*frameCount == 0) { // Calculate from duration and frame rate *frameCount = (int64)(*duration * frameRate / 1000000LL); - TRACE(" frameCount (calculated): %lld\n", *frameCount); - } else - TRACE(" frameCount: %lld\n", *frameCount); + TRACE(" frameCount calculated: %lld, from context: %lld\n", + *frameCount, fStream->nb_frames); +// } else +// TRACE(" frameCount: %lld\n", *frameCount); *format = fFormat; @@ -762,18 +818,18 @@ if (fContext == NULL || fStream == NULL) return B_NO_INIT; - TRACE_SEEK("AVFormatReader::StreamCookie::Seek(%ld, %s %s %s %s, %lld, " + TRACE_SEEK("AVFormatReader::StreamCookie::Seek(%ld,%s%s%s%s, %lld, " "%lld)\n", VirtualIndex(), - (flags & B_MEDIA_SEEK_TO_FRAME) ? "B_MEDIA_SEEK_TO_FRAME" : "", - (flags & B_MEDIA_SEEK_TO_TIME) ? "B_MEDIA_SEEK_TO_TIME" : "", - (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? "B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", - (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? "B_MEDIA_SEEK_CLOSEST_FORWARD" : "", + (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", + (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", + (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", + (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", *frame, *time); // Seeking is always based on time, initialize it when client seeks // based on frame. if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) - *time = (bigtime_t)(*frame * 1000000LL / FrameRate()); + *time = (bigtime_t)(*frame * 1000000LL / FrameRate() + 0.5); #if 0 // This happens in ffplay.c: @@ -799,10 +855,25 @@ *time / 1000000.0, timeStamp, fStream->cur_dts, fStream->time_base.num, fStream->time_base.den); - if (av_seek_frame(fContext, Index(), timeStamp, 0) < 0) { + int searchFlags = AVSEEK_FLAG_BACKWARD; + if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0) + searchFlags = 0; + +#if 0 + bigtime_t oneSecond = 1000000; + int64_t minTimeStamp = timeStamp - _ConvertToStreamTimeBase(oneSecond); + int64_t maxTimeStamp = timeStamp + _ConvertToStreamTimeBase(oneSecond); + if (avformat_seek_file(fContext, Index(), minTimeStamp, timeStamp, + maxTimeStamp, searchFlags) < 0) { + TRACE_SEEK(" avformat_seek_file() failed.\n"); + return B_ERROR; + } +#else + if (av_seek_frame(fContext, Index(), timeStamp, searchFlags) < 0) { TRACE_SEEK(" av_seek_frame() failed.\n"); return B_ERROR; } +#endif // Our last packet is toast in any case. av_free_packet(&fPacket); @@ -819,21 +890,21 @@ if (fContext == NULL || fStream == NULL) return B_NO_INIT; - TRACE_SEEK("AVFormatReader::StreamCookie::FindKeyFrame(%ld, %s %s %s %s, " + TRACE_FIND("AVFormatReader::StreamCookie::FindKeyFrame(%ld,%s%s%s%s, " "%lld, %lld)\n", VirtualIndex(), - (flags & B_MEDIA_SEEK_TO_FRAME) ? "B_MEDIA_SEEK_TO_FRAME" : "", - (flags & B_MEDIA_SEEK_TO_TIME) ? "B_MEDIA_SEEK_TO_TIME" : "", - (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? "B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", - (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? "B_MEDIA_SEEK_CLOSEST_FORWARD" : "", + (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", + (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", + (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", + (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", *frame, *time); double frameRate = FrameRate(); if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) - *time = (bigtime_t)(*frame * 1000000LL / frameRate); + *time = (bigtime_t)(*frame * 1000000LL / frameRate + 0.5); int64_t timeStamp = _ConvertToStreamTimeBase(*time); - TRACE_SEEK(" time: %.2fs -> %lld (time_base: %d/%d)\n", *time / 1000000.0, + TRACE_FIND(" time: %.2fs -> %lld (time_base: %d/%d)\n", *time / 1000000.0, timeStamp, fStream->time_base.num, fStream->time_base.den); int searchFlags = AVSEEK_FLAG_BACKWARD; @@ -842,22 +913,32 @@ int index = av_index_search_timestamp(fStream, timeStamp, searchFlags); if (index < 0) { - TRACE_SEEK(" av_index_search_timestamp() failed.\n"); - // Just seek to the beginning of the stream and assume it is a - // keyframe... - *frame = 0; - *time = 0; - return B_OK; + TRACE_FIND(" av_index_search_timestamp() failed.\n"); + // Best is to assume we can somehow seek to the time/frame + // and leave them as they are. + } else { + const AVIndexEntry& entry = fStream->index_entries[index]; + timeStamp = entry.timestamp; + bigtime_t foundTime = _ConvertFromStreamTimeBase(timeStamp); + bigtime_t timeDiff = foundTime > *time + ? foundTime - *time : *time - foundTime; + + if (fStreamBuildsIndexWhileReading && timeDiff > 1000000) { + // If the stream is building the index on the fly while parsing + // it, we only have entries in the index for positions already + // decoded, i.e. we cannot seek into the future. In that case, + // just assume that we can seek where we want and leave time/frame + // unmodified. + TRACE_FIND(" Not trusting generic index entry. " + "(Current count: %d)\n", fStream->nb_index_entries); + } else + *time = foundTime; } - - const AVIndexEntry& entry = fStream->index_entries[index]; - timeStamp = entry.timestamp; - *time = _ConvertFromStreamTimeBase(timeStamp); - - TRACE_SEEK(" seeked time: %.2fs (%lld)\n", *time / 1000000.0, timeStamp); + + TRACE_FIND(" found time: %.2fs (%lld)\n", *time / 1000000.0, timeStamp); if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) { - *frame = *time * frameRate / 1000000LL; - TRACE_SEEK(" seeked frame: %lld\n", *frame); + *frame = *time * frameRate / 1000000LL + 0.5; + TRACE_FIND(" found frame: %lld\n", *frame); } return B_OK;