The duration of aAC file obtained by IJK is not allowed. After a look, it is not allowed. In AE or mediaPlayer, the duration is 8.4 seconds. Duration = 8448000 us), ijk gets 9.3 seconds, stream ends at 8 seconds, put into compiled FFmPEG, 9.3 seconds.

1. Analyze the problem

Let’s start analyzing the problem. If you look at the file from the command line, ffmPEG does get 9.3 seconds

If you look closely at the red arrow, it means that the duration obtained is calculated according to the bitrate and may not be accurate. We can usually start with the avformat_find_stream_info function.

Here, starting directly from log, waring appears under utils.c/libavformat

static void estimate_timings_from_bit_rate(AVFormatContext *ic)
{
    int64_t filesize, duration;
    int i, show_warning = 0;
    AVStream *st;
	
	av_log(ic, AV_LOG_WARNING,
				   "hxk-->ic->bit_rate:%lld\n",ic->bit_rate); / / from herelogAs can be seen, bitrate is also not obtained, bitrate = 0 /*if bit_rate is already set, we believe it */
    if (ic->bit_rate <= 0) {
        int64_t bit_rate = 0;
        for (i = 0; i < ic->nb_streams; i++) {
            st = ic->streams[i];
			
            if (st->codecpar->bit_rate <= 0 && st->internal->avctx->bit_rate > 0)
                st->codecpar->bit_rate = st->internal->avctx->bit_rate;
            if (st->codecpar->bit_rate > 0) {
                if (INT64_MAX - st->codecpar->bit_rate < bit_rate) {
                    bit_rate = 0;
                    break;
                }
                bit_rate += st->codecpar->bit_rate;
            } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->codec_info_nb_frames > 1) {
                // If we have a videostream with packets but without a bitrate
                // then consider the sum not known
                bit_rate = 0;
                break; Bit_rate = bit_rate; av_log(ic, AV_LOG_WARNING,"hxk-->ic->bit_rate:%lld\n",ic->bit_rate); } / / fromlogDuration = 0 /*if duration is already set, we believe it */
	av_log(ic, AV_LOG_WARNING,
               "hxk-->ic->duration:%lld\n",ic->duration);
    if(ic->duration == AV_NOPTS_VALUE && ic->bit_rate ! = 0) { filesize = ic->pb ? avio_size(ic->pb) : 0; av_log(ic, AV_LOG_WARNING,"hxk-->ic->filesize:%lld\n",filesize);
        if (filesize > ic->internal->data_offset) {
            filesize -= ic->internal->data_offset;
            for (i = 0; i < ic->nb_streams; i++) {
                st      = ic->streams[i];
                if(st->time_base.num <= INT64_MAX/IC ->bit_rate && st->duration == AV_NOPTS_VALUE) So the CBR is calculated this way and you can calculate, Duration = av_rescale(8 * filesize, st->time_base.den, IC ->bit_rate * (int64_t) st->time_base.num);  St ->duration = duration; show_warning = 1; }}}}if (show_warning)
        av_log(ic, AV_LOG_WARNING,
               "Estimating duration from bitrate, this may be inaccurate\n");
}
Copy the code

The above function is called from utils.c/libavofrmat:

static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
{
    int64_t file_size;

    /* get the file size, if possible */
    if (ic->iformat->flags & AVFMT_NOFILE) {
        file_size = 0;
    } else {
        file_size = avio_size(ic->pb);
        file_size = FFMAX(0, file_size);
    }
	av_log(ic, AV_LOG_WARNING, "hxk->ic->iformat->name:%s\n", ic->iformat->name);
	av_log(ic, AV_LOG_WARNING, "hxk->file_size:%lld\n", file_size);
	av_log(ic, AV_LOG_WARNING, "hxk->ic->pb->seekable:%d\n", ic->pb->seekable);

    if((! strcmp(ic->iformat->name,"mpeg") | |! strcmp(ic->iformat->name,"mpegts")) &&
        file_size && (ic->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
        /* get accurate estimate from the PTSes */
        estimate_timings_from_pts(ic, old_offset);
        ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
    } else if(has_duration(IC)) {/* At least one component has timings - we use themfor all
         * the components */
        fill_all_stream_timings(ic);
        ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;
    } else/* less precise: use bitrate info */ estimate_timingS_from_bit_rate (IC); /* less precise: use bitrate info */ ESTIMate_timingS_from_bit_rate (IC); ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE; } update_stream_timings(ic); { int i; AVStream av_unused *st;for (i = 0; i < ic->nb_streams; i++) {
            st = ic->streams[i];
            av_log(ic, AV_LOG_TRACE, Stream %d: start_time: %0.3f duration: %0.3f\n", i,
                   (double) st->start_time * av_q2d(st->time_base),
                   (double) st->duration   * av_q2d(st->time_base));
        }
        av_log(ic, AV_LOG_TRACE,
                "format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n", (double) ic->start_time / AV_TIME_BASE, (double) ic->duration / AV_TIME_BASE, (int64_t)ic->bit_rate / 1000); }}Copy the code

The above method is called from the avformat_find_stream_info/utils.c/libavformat function.

2. Solve problems

Now that the cause is known, what can be done to solve the problem? How can I get the duration of aAC? Let’s take a look at the implementation of Aacextractore in the Libstagefright framework in Android

AACExtractor.cpp/libstagefrgiht

AACExtractor::AACExtractor(
        const sp<DataSource> &source, const sp<AMessage> &_meta)
    : mDataSource(source),
      mInitCheck(NO_INIT),
      mFrameDurationUs(0) {
    sp<AMessage> meta = _meta;

    if (meta == NULL) {
        String8 mimeType;
        float confidence;
        sp<AMessage> _meta;

        if(! SniffAAC(mDataSource, &mimeType, &confidence, &meta)) {return;
        }
    }

    int64_t offset;
    CHECK(meta->findInt64("offset", &offset));

    uint8_t profile, sf_index, channel, header[2];
    if (mDataSource->readAt(offset + 2, &header, 2) < 2) {
        return; Profile = (header[0] >> 6) &0x3; Sf_index = (header[0] >> 2) &0xf; Uint32_t sr = get_sample_rate(sf_index);if (sr == 0) {
        return; } / / channel channel = (header [0] & 0 x1) < < 2 | (header [1] > > 6); mMeta = MakeAACCodecSpecificData(profile, sf_index, channel); off64_t streamSize, numFrames = 0; size_t frameSize = 0; int64_t duration = 0; // Get the file sizeif (mDataSource->getSize(&streamSize) == OK) {
         while(offset < streamSize) {// Get the size of each adTS frameif ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) {
                return; } mOffsetVector.push(offset); offset += frameSize; // add numFrames ++; // count frames} //*************** ************* // Round up and get the duration mFrameDurationUs = (1024 * 1000000ll + (SR-1)) / sr; duration = numFrames * mFrameDurationUs; < span style = "max-width: 100%; clear: both;setInt64(kKeyDuration, duration);
    }

    mInitCheck = OK;
}
Copy the code

We’ll look at getAdtsFrameLength/AACExtractor CPP/libstagefrgiht function, this function is calculated according to the head of adts every framesize size

static size_t getAdtsFrameLength(const sp<DataSource> &source, off64_t offset, size_t* headerSize) { //CRC const size_t kAdtsHeaderLengthNoCrc = 7; const size_t kAdtsHeaderLengthWithCrc = 9; size_t frameSize = 0; // uint8_t syncword[2];if (source->readAt(offset, &syncword, 2) ! = 2) {return 0;
    }
    if((syncword[0] ! = 0xff) || ((syncword[1] & 0xf6) ! = 0xf0)) {return0; // Uint8_t protectionAbsent;if (source->readAt(offset + 1, &protectionAbsent, 1) < 1) {
        return 0;
    }
    protectionAbsent &= 0x1;

    uint8_t header[3];
    if (source->readAt(offset + 3, &header, 3) < 3) {
        return0; } / / get the size of the framesize framesize = (header [0] & 0 x3) < < 11 | header [1] < < 3 | header [2] > > 5; // protectionAbsent is 0if there is CRC
    size_t headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc;
    if (headSize > frameSize) {
        return 0;
    }
    if(headerSize ! = NULL) { *headerSize = headSize; }return frameSize;
}
Copy the code

The above implementation principle is based on an AAC raw frame containing 1024 samples and related data over a period of time. The playback time of an AAC audio frame = the number of samples of an AAC frame/the sampling rate. So the total time of aAC audio file t= total number of frames x the playback time of an AAC audio frame

Aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer: aac demuxer

AAC formats:

Let’s take a brief look at the format of AAC:

For detailed AAC format, please refer to this article.

AAC file format and audio file duration calculation

To solve the problem

Demuxer (raw ADTS AAC) and libavformat (aacdec.c/libavformat)

Modify aacdec.c file, add new function

Static int getAdtsFrameLength(AVFormatContext *s,int64_t offset,int* headerSize) {static int getAdtsFrameLength(AVFormatContext *s,int64_t offset,int* headerSize) { int64_t filesize, position = avio_tell(s->pb); filesize = avio_size(s->pb); //av_log(NULL, AV_LOG_WARNING,"hxk->getAdtsFrameLength.filesize:%d\n",filesize); const int kAdtsHeaderLengthNoCrc = 7; const int kAdtsHeaderLengthWithCrc = 9; int frameSize = 0; uint8_t syncword[2]; avio_seek(s->pb, offset, SEEK_SET); // Read the synchronization wordif(avio_read(s->pb,&syncword, 2)! = 2) {return 0;
	}
    if((syncword[0] ! = 0xff) || ((syncword[1] & 0xf6) ! = 0xf0)) {return0; } uint8_t protectionAbsent; avio_seek(s->pb, offset+1, SEEK_SET); / / read protectionAbsentif (avio_read(s->pb, &protectionAbsent, 1) < 1) {
        return0; } protectionAbsent &= 0x1; uint8_t header[3]; // read header avio_seek(s->pb, offset+3, SEEK_SET);if (avio_read(s->pb, &header, 3) < 3) {
        return0; } / / get framesize framesize = (header [0] & 0 x3) < < 11 | header [1] < < 3 | header [2] > > 5; // protectionAbsent is 0if there is CRC
    int headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc;
    if (headSize > frameSize) {
        return 0;
    }
    if(headerSize ! = NULL) { *headerSize = headSize; }returnframeSize; } static uint32_t get_sample_rate(const uint8_t sf_index) {static const uint32_t sample_rates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) {
        return sample_rates[sf_index];
    }

    return 0;
}

//add end
Copy the code

Modify the adts_aac_read_header function

static int adts_aac_read_header(AVFormatContext *s)
{
	av_log(NULL, AV_LOG_WARNING, "hxk->adts_aac_read_header! \n");

    AVStream *st;
    uint16_t state;

    st = avformat_new_stream(s, NULL);
    if(! st)return AVERROR(ENOMEM);

    st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    st->codecpar->codec_id   = s->iformat->raw_codec_id;
    st->need_parsing         = AVSTREAM_PARSE_FULL_RAW;
    ff_id3v1_read(s);
    if((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && ! av_dict_get(s->metadata,"", NULL, AV_DICT_IGNORE_SUFFIX)) {
        int64_t cur = avio_tell(s->pb);
        ff_ape_parse_tag(s);
        avio_seek(s->pb, cur, SEEK_SET);
    }

    // skip data until the first ADTS frame is found
    state = avio_r8(s->pb);
    while(! avio_feof(s->pb) && avio_tell(s->pb) < s->probesize) { state = (state << 8) | avio_r8(s->pb);if((state >> 4) ! = 0xFFF)continue;
        avio_seek(s->pb, -2, SEEK_CUR);
        break;
    }
    if((state >> 4) ! = 0xFFF)return AVERROR_INVALIDDATA;

    // LCM of all possible ADTS sample rates
 //   avpriv_set_pts_info(st, 64, 1, 28224000);
//add by hxk
#if 1Avio_seek (s->pb, 0, SEEK_SET); uint8_t profile, sf_index, channel, header[2]; // The file pointer moves to the first 2 bytes of the file starting point avio_seek(s->pb, 2, SEEK_SET);if (avio_read(s->pb,&header, 2) < 2) {
			av_log(NULL, AV_LOG_ERROR, "avio_read header error! \n");
			return0; } int64_t offset = 0; Profile = (header[0] >> 6) &0x3; st->codecpar->profile = profile; //av_log(NULL, AV_LOG_WARNING,"hxk->profile:%d! \n",profile); sf_index = (header[0] >> 2) & 0xf; Uint32_t sr = get_sample_rate(sf_index); //av_log(NULL, AV_LOG_WARNING,"hxk->samplerate:%d! \n",sr);
		if (sr == 0) {
			av_log(NULL, AV_LOG_ERROR, "avio_read read sampletare error! \n");
			return0; } // assign the codec parameter st-> codecPAR ->sample_rate = sr; / / access channel channel = (header [0] & 0 x1) < < 2 | (header [1] > > 6);if (channel == 0) {
			av_log(NULL, AV_LOG_ERROR, "adts_aac_read_header read channel error! \n");
			return0; } // Assign the codec parameter st->codecpar-> Channels = channel; //av_log(NULL, AV_LOG_WARNING,"hxk->channel:%d! \n",channel); sf_index = (header[0] >> 2) & 0xf; int frameSize = 0; int64_t mFrameDurationUs = 0; int64_t duration = 0; // Set the sample_rate to codec st-> codecPAR ->sample_rate = sr; int64_t streamSize, numFrames = 0; avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); StreamSize = avio_size(s->pb); // av_log(NULL, AV_LOG_WARNING,"hxk->streamSize:%d! \n",streamSize);
		if (streamSize > 0) {
			while (offset < streamSize) {
				if ((frameSize = getAdtsFrameLength(s, offset, NULL)) == 0) {
					  return0; } offset += frameSize; // add numFrames ++; // av_log(NULL, AV_LOG_WARNING,"hxk->frameSize:%d! \n",frameSize);
			}
		//	av_log(NULL, AV_LOG_WARNING, "hxk->numFrames:%lld! \n",numFrames); MFrameDurationUs = (1024 * 1000000ll + (sr-1))/sr; duration = numFrames * mFrameDurationUs; Duration = av_rescale_q(duration,AV_TIME_BASE_Q, st->time_base); if (duration,AV_TIME_BASE_Q, st->time_base)  st->duration = duration; // av_log(NULL, AV_LOG_WARNING,"hxk->duration:%d! \n",duration);
		}
		
#endif
//add end

    return 0;
}

Copy the code

In this way, duration can be obtained in demuxer, and has_duration can be directly evaluated in the estimate_timings function above, and the obtained duration is relatively accurate.

The duration obtained by MtkAACExtractor is the same as the duration obtained by MtkAACExtractor in Android.

subsequent

I am glad to solve the problem, and transplant the changed code to iJK. I found that it can not play, no error, file info read is correct, seek, when reporting such a line error

IJKMEDIA: /storage/emulated/0/3ee807175fc2488d8264ac014ccf55ff.aac: error while seeking
Copy the code

I forgot to put the handle back.

static int adts_aac_read_header(AVFormatContext *s)
{
    AVStream *st;
    uint16_t state;

    st = avformat_new_stream(s, NULL);
    if(! st)return AVERROR(ENOMEM);

    st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    st->codecpar->codec_id   = s->iformat->raw_codec_id;
    st->need_parsing         = AVSTREAM_PARSE_FULL_RAW;

    ff_id3v1_read(s);
    if((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && ! av_dict_get(s->metadata,"", NULL, AV_DICT_IGNORE_SUFFIX)) {
        int64_t cur = avio_tell(s->pb);
        ff_ape_parse_tag(s);
        avio_seek(s->pb, cur, SEEK_SET);
    }

    // skip data until the first ADTS frame is found
    state = avio_r8(s->pb);
    while(! avio_feof(s->pb) && avio_tell(s->pb) < s->probesize) { state = (state << 8) | avio_r8(s->pb);if((state >> 4) ! = 0xFFF)continue;
        avio_seek(s->pb, -2, SEEK_CUR);
        break;
    }
    if((state >> 4) ! = 0xFFF)return AVERROR_INVALIDDATA;

    // LCM of all possible ADTS sample rates
   // avpriv_set_pts_info(st, 64, 1, 28224000);
	//add by hxk
#if 1
			
			avio_seek(s->pb, 0, SEEK_SET);
			uint8_t profile, sf_index, channel, header[2];
			avio_seek(s->pb, 2, SEEK_SET);
			if (avio_read(s->pb,&header, 2) < 2) {
				av_log(NULL, AV_LOG_ERROR, "avio_read header error! \n");
				return 0;
			}
			int64_t offset = 0;
			profile = (header[0] >> 6) & 0x3;
			st->codecpar->profile = profile;
			sf_index = (header[0] >> 2) & 0xf;
			uint32_t sr = get_sample_rate(sf_index);
			if (sr == 0) {
				av_log(NULL, AV_LOG_ERROR, "adts_aac_read_header read sampletare error! \n");
				return 0;
			}
			st->codecpar->sample_rate = sr;
			channel = (header[0] & 0x1) << 2 | (header[1] >> 6);
			if(channel == 0) {
				av_log(NULL, AV_LOG_ERROR, "adts_aac_read_header read channel error! \n");
				return 0;
			}
			st->codecpar->channels = channel;
			sf_index = (header[0] >> 2) & 0xf;
			int frameSize = 0;
			int64_t mFrameDurationUs = 0;
			int64_t duration = 0;
			st->codecpar->sample_rate = sr;
			int64_t streamSize, numFrames = 0;
			avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
			streamSize =  avio_size(s->pb);
			//av_log(NULL, AV_LOG_WARNING, "hxk->streamSize:%d! \n",streamSize);
			if (streamSize > 0) {
				while (offset < streamSize) {
					if ((frameSize = getAdtsFrameLength(s, offset, NULL)) == 0) {
						  return 0;
					}
					offset += frameSize;
					numFrames ++;
				//av_log(NULL, AV_LOG_WARNING, "hxk->frameSize:%d! \n",frameSize);
				}
				// av_log(NULL, AV_LOG_WARNING, "hxk->numFrames:%lld! \n",numFrames); // Round up and get the duration mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr; duration = numFrames * mFrameDurationUs; //us duration = av_rescale_q(duration,AV_TIME_BASE_Q, st->time_base); st->duration = duration; //av_log(NULL, AV_LOG_WARNING,"hxk->duration:%d! \n",duration); } // return the handle avio_seek(s->pb, 0, SEEK_SET);#endif
	//add end

    return 0;
}
Copy the code

Well, you can get the correct time to play normally.