ffmpeg系列-解决ffmpeg获取aac音频文件duration不准

2023-05-16

这个问题是这样产生的,一同事反应会随机出现ijk获取到的aac文件的duration不准,发来一看,确实不准,在AE或者系统mediaplayer中得到的都是8.4秒(准确时间是MtkAACExtractor: ADTS: duration = 8448000 us),ijk得到的是9.3秒,在播放的时候,在8秒的时候流就结束了,放到编译的ffmpeg中,一看也是9.3秒。

1.分析问题

下面开始分析这个问题,命令行看下这个文件,ffmpeg中获取到的确实是9.3秒
image

仔细看下红色箭头所指,这个意思是获取到的duration是根据比特率计算的,可能不准确。这种获取音视频info有问题的我们一般可以从avformat_find_stream_info函数开始分析。

这里直接从log开始看,waring出现出现在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);
	//这里从log可以看到,bitrate也没获取到,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;
            }
        }
        //这里算出来一个bitrate
        ic->bit_rate = bit_rate;
		av_log(ic, AV_LOG_WARNING,
				   "hxk-->ic->bit_rate:%lld\n",ic->bit_rate);
    }
    //从log中可以看到,这里的duration也是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) {
                    //这里根据文件字节*8 /比特率来计算duration,这里cbr这样计算可以计算,但是如果vbr(码率动态)的话就有问题了
                    duration = av_rescale(8 * filesize, st->time_base.den,
                                          ic->bit_rate *
                                          (int64_t) st->time_base.num);
                //获取到的duration就不准确了
                    st->duration = duration;
                    show_warning = 1;
                }
            }
        }
    }
    if (show_warning)
        av_log(ic, AV_LOG_WARNING,
               "Estimating duration from bitrate, this may be inaccurate\n");
}

调用上面这个函数的地方是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)) {
    //如果在demuxer中获取到duration了
        /* at least one component has timings - we use them for all
         * the components */
        fill_all_stream_timings(ic);
        ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;
    } else {
    //这个文件没有获取到duration,所以走的是这里
        /* 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);
    }
}

调用上面这个方法是在avformat_find_stream_info/utils.c/libavformat函数中。

2.解决问题

原因已经知道了,那么可以如何解决这个问题呢?
aac的duration可以如何获取呢?
我们看下android系统中libstagefright框架中aacextractore的实现

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
    profile = (header[0] >> 6) & 0x3;
//获取采样索引
    sf_index = (header[0] >> 2) & 0xf;
//获取采样率
    uint32_t sr = get_sample_rate(sf_index);
    if (sr == 0) {
        return;
    }
//通道
    channel = (header[0] & 0x1) << 2 | (header[1] >> 6);

    mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);

    off64_t streamSize, numFrames = 0;
    size_t frameSize = 0;
    int64_t duration = 0;
//获取文件大小
    if (mDataSource->getSize(&streamSize) == OK) {
         while (offset < streamSize) {
         //获取adts每一帧大小
            if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) {
                return;
            }

            mOffsetVector.push(offset);

            offset += frameSize;//偏移加加
            numFrames ++;//计算帧数目
        }
//***************重点看下这里,这里在下面分析aac文件格式的时候会讲解细致一点*************
        // Round up and get the duration
        mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
        duration = numFrames * mFrameDurationUs;//总帧数x一个AAC音频帧的播放时间
        mMeta->setInt64(kKeyDuration, duration);
    }

    mInitCheck = OK;
}

我们再看下getAdtsFrameLength/AACExtractor.cpp/libstagefrgiht函数,这个函数其实就是根据adts头来计算出每一个framesize的大小的

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)) {
        return 0;
    }
//0没有crc,1有crc
    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) {
        return 0;
    }
//获取framesize的大小
    frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;

    // protectionAbsent is 0 if there is CRC
    size_t headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc;
    if (headSize > frameSize) {
        return 0;
    }
    if (headerSize != NULL) {
        *headerSize = headSize;
    }

    return frameSize;
}

上面的实现原理就是根据一个AAC原始帧包含一段时间内1024个采样及相关数据。一个AAC音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样率。所以aac音频文件总时间t=总帧数x一个AAC音频帧的播放时间

下面看一下aac的demuxer,在aacdec.c/libavformat下,发现里面连对aidf头的处理都没有,这个先不管了。

AAC格式:

下面我们先简单看下aac的格式:

详细的AAC格式参考下这篇文章吧,实在懒得写。

AAC文件格式与音频文件时长计算

解决问题

下面我们看下ffmpeg中这个格式的demuxer,这个文件封装格式raw ADTS AAC,下面我们看下aacdec.c/libavformat

修改aacdec.c文件,新加函数

//add by hxk
//获取adts frame的帧长
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);
	//读取同步字
    if(avio_read(s->pb,&syncword, 2)!= 2){
		return 0;
	}
    if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
        return 0;
    }
	uint8_t protectionAbsent;
	avio_seek(s->pb, offset+1, SEEK_SET);
	//读取protectionAbsent
    if (avio_read(s->pb, &protectionAbsent, 1) < 1) {
        return 0;
    }
    protectionAbsent &= 0x1;
    uint8_t header[3];
//读取header
	avio_seek(s->pb, offset+3, SEEK_SET);
    if (avio_read(s->pb, &header, 3) < 3) {
        return 0;
    }
    
    //获取framesize
    frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
    // protectionAbsent is 0 if there is CRC
    int headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc;
    if (headSize > frameSize) {
        return 0;
    }
    if (headerSize != NULL) {
        *headerSize = headSize;
    }
    return frameSize;
}
//根据采样率下标获取采样率
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

修改adts_aac_read_header函数

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  1
        //句柄指回起点
		avio_seek(s->pb, 0, SEEK_SET);
		uint8_t profile, sf_index, channel, header[2];
		//文件指针移动到文件起点前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
		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");
			return 0;
		}
		//赋值给codec参数
		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;
		}
	    //赋值给codec 参数
		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;
		//采样率赋值给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) {
					  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
			//时间基转换avstream的,us单位(AV_TIME_BASE_Q)转avstream的时间基
			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);
		}
		
#endif
//add end

    return 0;
}

这样在demuxer中就获得了duration,在上面的estimate_timings函数中就直接走has_duration这个判断了,得到的duration也是比较准确的了。

运行一下修改后的代码,从下图可以看到时间已经改变了,和android中MtkAACExtractor获取的duration是一样的了。

image

后续

正满心欢喜解决了问题后,把改动的代码移植到ijk上的时候,发现不能播放,没报任何错误,文件info读取都是正确的,seek一下的时候报了这么一行错误

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

原来忘记把句柄置回去了
修改如下:

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);
			}
			//置回句柄
			avio_seek(s->pb, 0, SEEK_SET);
			
#endif
	//add end

    return 0;
}

嗯,可以获取正确的时间来正常播放了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ffmpeg系列-解决ffmpeg获取aac音频文件duration不准 的相关文章

  • 在 MacOS 终端上运行 ffmpeg [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我对 MacOS 相当陌生 我发现使用终端来获取信息并不容易ffmpeg和我在 Window 上一样正常运行 我有 ffmpeg 二进制文件ffmpe
  • 在 macOS 上为 MoviePy 安装 ffmpeg 失败并出现 SSL 错误

    我正在尝试编写一个 Python 程序 在 Mac OS 10 11 16 上使用 MoviePy 将 MP4 文件转换为 GIF 我用 import moviepy editor as mp 我收到一条错误消息 说我需要打电话imagei
  • Windows 上的 ffmpeg-android ndk

    我正在尝试编译 bash 文件 带有 android ndk 的 ffmpeg 我收到如下错误 arm linux androideabi gcc 无法创建可执行文件 C 编译器测试失败 Makefile 2 config mak 没有这样
  • ffmpeg concat:“不安全的文件名”

    尝试将一堆 mts 文件转换为一个大 mp4 文件 stephan rechenmonster mnt backupsystem archive2 Videos 20151222 PRIVATE AVCHD BDMV bin ffmpeg
  • 从 ffmpeg 获取实时输出以在进度条中使用(PyQt4,stdout)

    我已经查看了很多问题 但仍然无法完全弄清楚 我正在使用 PyQt 并且希望能够运行ffmpeg i file mp4 file avi并获取流式输出 以便我可以创建进度条 我看过这些问题 ffmpeg可以显示进度条吗 https stack
  • 使用FFMpeg确定视频类型,然后进行转换?

    我正在尝试以编程方式确定文件的真实类型 看来我必须使用 FFMPeg 来实现这一点 我想确定上传的文件实际上是否是 MP4 或 FLV 对于 Flash 视频 或 WebM 对于 HTML5 我知道 FFMPeg 中的 i 运算符 但我不知
  • Bash 脚本:自动为 mpeg-dash 进行 ffmpeg 编码

    我正在编写一个 bash 文件来创建视频编码和串联 以供 dash 实时流媒体使用 基本上 它读取输入视频文件夹 将所有视频编码为三种分辨率格式 然后将它们连接起来创建三个适应集 DIAGRAM 该脚本检查 fps 一致性 如果输入不是 1
  • 使用 Coldfusion 的 CFFILE 标签监控 FFMpeg 的进度日志

    我想学习如何使用 ColdFusion 中的 CFFILE 标签来读取文本文件的内容 就我而言 该文本文件是 FFMpeg 在对媒体文件进行转码时生成的进度日志 我想编写一个 ColdFusion 脚本 该脚本将定期轮询进度日志 直到日志表
  • Node.js - 将数据缓冲到 Ffmpeg

    我使用 Node js 和 Ffmpeg 来创建动画 因为我试图避免第三方 avi mp4 解析器 所以我决定将动画输出为原始 rgb24 数据文件 然后使用一些程序将其转换为 mp4 文件 我发现 Ffmpeg 是免费且开源的 它完全可以
  • C++ OpenCV 3.4 / FFMPEG 3.4.1 VideoWriter 和 MP4 输出文件格式

    我正在运行 Linux 内核 4 9 35 ti r44 的 ARM BeagleBone X 15 Debian 机器 在我的 C Qt 5 应用程序中 我想将 cv Mat 帧保存为 MP4 格式视频 我安装了 libx264 并从头开
  • 如何在 RTMP 流中嵌入 pic_timing SEI 挂钟时间码?

    我需要将我的桌面流式传输到 AWS MediaLive 服务 并且根据要求 我必须在流中包含挂钟时间码 AWS 支持人员善意地通知我 对于 h 264 编码流 我需要提供时间码作为 pic timing SEI 消息 我在 Windows
  • swscaler 警告:使用了已弃用的像素格式

    我想先对视频帧执行颜色空间转换 然后使用以下代码将其转换为 opengl 纹理 struct SwsContext pSwsCtx sws getCachedContext NULL width height codec gt pix fm
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • PowerShell 脚本 ffmpeg

    作为一名优秀的 Windows 系统管理员 我终于开始学习 PowerShell 话虽这么说 我不知道我在做什么 惊讶 惊讶 我认为远离生产环境 在家里使用 PowerShell 对我来说将是一次很好的学习经历 最近 我开始使用 FFMPE
  • 使用 ffmpeg 将 mp4 转换为 gif 时帧率较低

    我正在使用 ffmpeg 将高质量视频转换为 gif 大多数视频都是 60fps 及以上720p 但是当我使用下面的代码将视频转换为 gif 时 我得到的 gif 输出的 fps 非常低 usr bin env palette tmp pa
  • 有没有更有效的方法通过ffmpeg批量添加水印和加入视频?

    我有这个批处理文件 使用 ffmpeg 在我的视频中添加徽标 然后添加简介 但需要 10 小时到一天的时间 具体取决于我需要添加水印的数量 是否有更有效的方法来实现此目的 视频有时具有不同的分辨率 因此我无法删除到 1280 720 尺寸的
  • 使用 ffmpeg 库以可变帧率模式将一系列图像(cv::Mat)保存到 mp4 文件,如何设置 pts?

    在C 代码中 我可以正确保存一系列图像 opencv的cv Mat 到 mp4 文件 使用ffmpeg图书馆 请参阅此处的问题和答案 当我尝试将多个 RGB 数据保存到 output mp4 文件时 avformat write heade
  • 如何创建媒体文件的稳定校验和?

    如何仅创建媒体数据的校验和而不包含元数据以获得媒体文件的稳定标识 最好是使用支持多种格式的库的跨平台方法 例如vlc ffmpeg 或 mplayer 媒体文件应该是常见格式的音频和视频 图像也很好 好吧 可能已经晚了 11 年才得到答案
  • 用于 Windows Phone 开发的 FFmpeg

    我在 ASP Net 基于 Web 的应用程序中使用了 FFmpeg 现在我想用它来进行Windows Phone开发 可以使用吗 如果是 那么如何 Windows Phone 7 根本不支持 FFmpeg 而且据我在网上找到的信息 Win
  • 在 Mac 操作系统上使用 ffmpeg 录制视频

    我想在 mac OS 上使用 ffmpeg 以任何格式录制实时网络摄像头视频 我尝试了很多 但无法找到用于重新编码视频的命令 所以请任何人都可以告诉我 ffmpeg 命令用于使用 Mac 操作系统的网络摄像头捕获视频 提前致谢 对于 Mac

随机推荐

  • Android N之hasSystemFeature

    当我们判断某一功能打开与否时 xff0c 一般会有个确认本功能是否支持的过程 xff0c 以便与为相关的功能初始化其他的环境 xff0c 例如 xff1a 蓝牙 NFC 例如 NFC HCE 两个的声明如下 xff1a Feature fo
  • Android Go项目预置应用Google GTS测试testPreloadedAppsTargetSdkVersion失败

    Android GO项目中预置的一个Weather应用 xff0c GTS测试通不过 据log提示 xff0c 是兼容的SDK目标版本过低导致 xff0c GO版本要求必须为API 26 43 含26 xff09 LOG如下 xff1a 0
  • 多维缩放算法(MDS)

    算法思想 MDS算法思想很简单 xff0c 一句话就是保持样本在原空间和低维空间的距离不变 因为距离是样本之间一个很好的分离属性 xff0c 对于大多数聚类算法来说 xff0c 距离是将样本分类的重要属性 xff0c 因此当我们降维后 xf
  • Gerrit 安装lfs插件

    一 下载lfs插件 https gerrit ci gerritforge com job plugin lfs bazel stable 2 16 这个是直接编译好的 二 安装插件 将下载的插件放在 GERRIT SITE plugins
  • 反编译so库破解so

    所需工具 1 IDA Pro v6 8 and Hex Rays Decompiler 2 WinHex 3 ARM ASM 背景 xff1a I2C通讯时报log CameraHal Marvin HAL MOCKUP HalReadI2
  • 长虹官方刷机包和刷机教程

    为了解决部分朋友因应用引起的电视死机 无法开机 系统被破坏等情形 xff0c 长虹电视团队特开此帖为朋友们提供刷机方法 xff0c 但刷机有风险 xff0c 如完全不懂刷机技巧的朋友需要谨慎操作哦 xff0c 如有疑问可以微信留言给我们 下
  • Android终端通过adb 配置静态IP和DNS

    有时我们需要使用命令行来配置eth0的IP信息 xff0c 这在linux系统是非常简单的 xff0c 网上也有很多资料 但是在Android系统 xff0c 就非常困难 xff0c 因为Android精简掉了很多linux命令 xff0c
  • 【官方】下载最新adb及安装驱动的方法

    Only adb 驱动 xff1a https adbdriver com downloads adb工具 xff1a https adbshell com upload adb zip https adbshell com downloa
  • 中芯微随身WIFI破解实体SIM卡槽(不拆机,无需切卡密码)

    目前网上卖的一些随身WIFI是中芯微的方案 MF782 部分产品限制用户使用实体SIM卡 只能使用内置eSIM 下面谈谈解决方案 1 中沃的没有限制 实体SIM卡优先 检测到插的有实体SIM卡 就使用实体SIM卡网络 2 另外一部分网上提供
  • 高通Android随身WIFI屏蔽商家远程控制断网

    nbsp nbsp nbsp nbsp 部分随身WIFI商家后台会监测用户是否使用的是自家的eSIM 若使用了外置卡槽或eSIM的ICCID改变就会断网 主要表现是先联网后突然变成飞行模式 或联网后开热点变飞行模式 这就是商家后台做了监测
  • Linux kernel make clean时忽略部分文件(不被删除)

    有时我们在运行make clean 时 xff0c 需要保留某些 o 文件 xff0c 这就需要我们修改 Makefile 文件 xff0c 下面以 linux 2 6 18 的 Makefile 为例 xff1a Files to ign
  • Audio参数讲解

    一 音频基础参数 frame bits 一帧数据的位数比如 xff1a 16bits 2ch frame bits 61 16 2 sample bits 采样位数 比如16bit 24bit 32bit period size 指一个周期
  • linux ALSA 驱动架构

    一 kernel Audio驱动架构主流有两大类 xff0c 一类是SOC Machine架构 xff0c 另一类是simple card架构 MTK QCom主要采用machine架构 xff0c rockchip采用simple car
  • adaboost提升算法

    引言 俗话说得好 xff0c 三个臭皮匠赛过诸葛亮 更主要的是三个臭皮匠好找 xff0c 一个诸葛亮太难找了 在机器学习里面也是一样的 我们可以设计出各种分类器 xff0c 然而分类器的效果确实不一而同的 xff0c 相对而言 xff0c
  • 长虹电视刷机固件包汇总

    为了解决部分朋友因应用引起的电视死机 无法开机 系统被破坏等情形 xff0c 快客服务特开此帖为朋友们提供刷机方法 xff0c 但刷机有风险 xff0c 如完全不懂刷机技巧的朋友需要谨慎操作 xff0c 用户自行刷机所产生问题自行负责 xf
  • WebRTC-集成qsv硬解码实现

    1 Window下QSV硬解码配置 在libavcodec codec list c下添加 amp ff h264 qsv decoder 在ffmpeg generate gni下加入 34 libavcodec h264idct c 3
  • ijkplayer-添加播放截图功能

    应用播放的时候需要截图 xff0c 可以在上层使用TexturView来使用截图 xff0c 不过太具有局限性呢 xff0c 还是在底层处理比较好 那么先分析下可以在哪里加截图呢 xff1f 看到网上很多做的都不能支持硬解截图 xff0c
  • avformat_seek_file及其flag含义

    我们从ijk中seek的处理流程来看ffmpeg的这个问题 int ffp seek to l FFPlayer ffp long msec assert ffp VideoState is 61 ffp gt is int64 t sta
  • 单例模式

    单例模式 xff1a include lt iostream gt using namespace std class Singleton public Singleton cout lt lt 34 Singleton虚构函数 34 lt
  • ffmpeg系列-解决ffmpeg获取aac音频文件duration不准

    这个问题是这样产生的 xff0c 一同事反应会随机出现ijk获取到的aac文件的duration不准 xff0c 发来一看 xff0c 确实不准 xff0c 在AE或者系统mediaplayer中得到的都是8 4秒 xff08 准确时间是M