当前位置:网站首页>Résoudre le problème de la durée inexacte du fichier audio AAC obtenu par ffmpeg
Résoudre le problème de la durée inexacte du fichier audio AAC obtenu par ffmpeg
2022-06-11 06:23:00 【Tinghua M】
Les tests récents ont proposé unbug,ijkAcquisaacDocumentdurationNon.,Envoyez - le - moi,Pas du tout.,InAEOu le systèmemediaplayerTout ce que j'ai3m48s(L'heure exacte estMMParserExtractor: ADTS: duration = 228010580us,Comme le montre la figure ci - dessous:),ijkCe qu'on a, c'est2m54s,Pendant la lecture,In2m54sLe flux est terminé quand,Mis dans la compilationffmpegMoyenne, Duration:C'est vraiment00:03:13.07,MaisVLC- Oui.3m53s,Ce fichier est aussi un travail fantastique!Les autres joueurs ne discutent pas pour le moment,J'espère juste queMMParserExtractorAvecIJKPlayerLa durée obtenue est toujours! 
1、Analyse des problèmes
Commençons par analyser ce problème,La ligne de commande regarde ce fichier,ffmpegCe que j'ai trouvé dans3m13s

Regardez attentivement la flèche rouge,Ça veut dire obtenirdurationEst calculé sur la base du débit binaire,Peut - être inexact.. Cette acquisition audio et vidéo info En cas de problème, nous pouvons généralement avformat_find_stream_infoLa fonction commence l'analyse.
Ça vient directement delogRegarde.,waring Apparaît dans utils.c/libavformatEn bas.
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, "-->ic->bit_rate:%lld\n",ic->bit_rate);
//Par ici.logJe vois.,bitrate Je n'ai pas ,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;
}
}
// Voici un bitrate
ic->bit_rate = bit_rate;
av_log(ic, AV_LOG_WARNING, "-->ic->bit_rate:%lld\n",ic->bit_rate);
}
//DelogComme vous pouvez le voir,Ici.durationC'est aussi0
/* if duration is already set, we believe it */
av_log(ic, AV_LOG_WARNING,"-->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,"-->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) {
// Ceci est basé sur les octets de fichier *8 / Débit binaire pour calculer duration,Ici.cbr Ce calcul permet de calculer ,Mais sivbr( Dynamique du débit binaire ) Il y a un problème
duration = av_rescale(8 * filesize, st->time_base.den,
ic->bit_rate *
(int64_t) st->time_base.num);
//AcquisdurationCe n'est pas exact
st->duration = duration;
show_warning = 1;
}
}
}
}
if (show_warning)
av_log(ic, AV_LOG_WARNING,
"Estimating duration from bitrate, this may be inaccurate\n");
}La fonction ci - dessus est appelée 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, "->ic->iformat->name:%s\n", ic->iformat->name);
av_log(ic, AV_LOG_WARNING, "->file_size:%lld\n", file_size);
av_log(ic, AV_LOG_WARNING, "->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)) {
//Si dansdemuxerDansdurationC'est
/* 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 {
// Ce fichier n'est pas disponible duration, Alors c'est ici que je vais
/* 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);
}
}Appelez la méthode ci - dessus avformat_find_stream_info/utils.c/libavformatEn fonction.
2、 Recherche de solutions
La raison est déjà connue , Comment résoudre ce problème? ?
aacDeduration Comment obtenir ?
Voyons voir.androidDans le systèmelibstagefrightDans le cadreaacextractoreRéalisation
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;
}
//Accèsprofile
profile = (header[0] >> 6) & 0x3;
// Obtenir l'index d'échantillonnage
sf_index = (header[0] >> 2) & 0xf;
// Obtenir le taux d'échantillonnage
uint32_t sr = get_sample_rate(sf_index);
if (sr == 0) {
return;
}
//Accès
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;
//Obtenir la taille du fichier
if (mDataSource->getSize(&streamSize) == OK) {
while (offset < streamSize) {
//Accèsadts Taille de chaque image
if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) {
return;
}
mOffsetVector.push(offset);
offset += frameSize;// Offset plus
numFrames ++;// Calculer le nombre de cadres
}
//*************** L'accent est mis ici , Voici une analyse de aac Le format du document sera expliqué plus en détail *************
// Round up and get the duration
mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
duration = numFrames * mFrameDurationUs;//Nombre total de cadresxUnAAC Temps de lecture du cadre audio
mMeta->setInt64(kKeyDuration, duration);
}
mInitCheck = OK;
}On verra.getAdtsFrameLength/AACExtractor.cpp/libstagefrgihtFonctions, Cette fonction est en fait basée sur adts Pour calculer chaque framesizeDe la taille de
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;
//Mot de synchronisation
uint8_t syncword[2];
if (source->readAt(offset, &syncword, 2) != 2) {
return 0;
}
if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
return 0;
}
//0Non.crc,1Oui.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;
}
//AccèsframesizeTaille
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;
}Le principe de mise en œuvre ci - dessus est basé sur AAC Le cadre original contient une période de temps 1024 Échantillonnage et données connexes .UnAAC Temps de lecture du cadre audio =UnAAC Nombre d'échantillons échantillonnés correspondant au cadre /Taux d'échantillonnage.Alors...aac Durée totale du fichier audio t=Nombre total de cadresxUnAAC Temps de lecture du cadre audio .
Regardez en basaacDedemuxer,Inaacdec.c/libavformatEn bas., J'ai trouvé une paire à l'intérieur aidf Il n'y a pas de traitement de la tête , Ça n'a pas d'importance .
AACPrésentation du format
La première chose à savoir estAACLes formats de fichiers sont:ADIFEtADTSDeux.,Parmi euxADIF(Audio Data Interchange Format Format d'échange de données audio) Le décodage doit être effectué au début clairement défini , Impossible de commencer au milieu du flux de données ;EtADTS(Audio Data Transport Stream Flux de données audio)C'est le contraire., Ce format est caractérisé par des mots synchrones ,Le décodage peut commencer n'importe où dans ce flux,Comme son nom,C'est une sorte deTS Stream dans un format similaire .
ADTS Chaque image du format a un en - tête , Avec des caractéristiques de flux , Convient à la transmission et au traitement du réseau ,EtADIFUne seule tête unifiée, Et les deux formats header Le format est également différent . Actuellement, le courant dominant est ADTSFormat.
ADTS AACLe format du fichier est le suivant:
ADTS_header | AAC ES | ADTS_header | AAC ES | … | ADTS_header | AAC ES |
DetailedAAC Voir cet article pour le format
AAC Format de fichier et calcul de la durée du fichier audio
Obtenir la durée par image :ffmpeg Capable de lire correctement chaque image nb_samples Et en général sample_rate, Donc la Division est la durée de chaque image .
AAC:Taille du cadre1024- Oui.sample,Le taux d'échantillonnage est de44100Hz , Durée de lecture du cadre :acc dur=1024/44100 = 0.02322s=23.22ms
Comment obtenir une durée précise? ?Ça devrait passer paradts frame header Prenez le nombre total de cadres * La valeur de la durée par cadre est duration.
3、Résoudre le problème
Maintenant, regardonsffmpeg Dans ce format demuxer, Ce format d'encapsulation de fichier raw ADTS AAC,Maintenant, regardonsaacdec.c/libavformat
//Accèsadts frame Longueur du cadre pour
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);
// Lire le mot de synchronisation
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);
//LireprotectionAbsent
if (avio_read(s->pb, &protectionAbsent, 1) < 1) {
return 0;
}
protectionAbsent &= 0x1;
uint8_t header[3];
//Lireheader
avio_seek(s->pb, offset+3, SEEK_SET);
if (avio_read(s->pb, &header, 3) < 3) {
return 0;
}
//Accèsframesize
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;
}
// Obtenir le taux d'échantillonnage à partir de l'indice de taux d'échantillonnage
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 endModifieradts_aac_read_headerFonctions
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 M
#if 1
// La poignée renvoie au point de départ
avio_seek(s->pb, 0, SEEK_SET);
uint8_t profile, sf_index, channel, header[2];
// Le pointeur de fichier se déplace avant le début du fichier 2Octets
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;
//Accèsprofile
profile = (header[0] >> 6) & 0x3;
st->codecpar->profile = profile;
sf_index = (header[0] >> 2) & 0xf;
// Obtenir le taux d'échantillonnage
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;
}
//Assigner une valeur àcodec Paramètres
st->codecpar->channels = channel;
sf_index = (header[0] >> 2) & 0xf;
int frameSize = 0;
int64_t mFrameDurationUs = 0;
int64_t duration = 0;
// Taux d'échantillonnage attribué à codec
st->codecpar->sample_rate = sr;
int64_t streamSize, numFrames = 0;
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
//Obtenir la taille du fichier
streamSize = avio_size(s->pb);
if (streamSize > 0) {
while (offset < streamSize) {
if ((frameSize = getAdtsFrameLength(s, offset, NULL)) == 0) {
goto end;
}
offset += frameSize;
// Nombre de cadres plus ,Obtenir le nombre total de cadres
numFrames ++;
}
end:
av_log(NULL, AV_LOG_WARNING, "---streamSize:%lld,numFrames:%lld!---\n",streamSize, numFrames);
// Round up and get the duration, Calculer le temps de chaque image
mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
av_log(NULL, AV_LOG_WARNING, "---mFrameDurationUs:%lld!---\n",mFrameDurationUs);
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, "-------duration:%d------!\n",duration);
}
// Remettre la poignée
avio_seek(s->pb, 0, SEEK_SET);
#endif
//add end
return 0;
}À l'origine, il faisait référence au blog d'une fleur de pêche qui écrase Begonia ,Ici.return 0De,Testé,SectionaacLe fichier ne peut pas être lu, A été remplacé par le texte ci - dessus. gotoC'est
if ((frameSize = getAdtsFrameLength(s, offset, NULL)) == 0) {
return 0;
}Aucun problème avec le test actuel ,C'est normal.seek Avec la lecture !
Liens de référence:ffmpegSérie-RésolutionffmpegAccèsaacFichiers audiodurationNon._ Un blog de fleurs de pêche sur Begonia -CSDNBlogs_ffmpeg Audioduration
边栏推荐
- Chapter 6 of machine learning [series] random forest model
- Box model
- Continuous update of ansible learning
- Analyze the capacity expansion mechanism of ArrayList
- jenkins-用户权限管理
- call和apply和bind的区别
- CCS method of installing compiler
- On cursor in MySQL
- ThymeleafEngine模板引擎
- FPGA interview notes (III) -- implementation of handshake signal synchronization in cross clock domain, arbitrary frequency division, binary conversion, RAM memory, original code inversion and complem
猜你喜欢

ThymeleafEngine模板引擎

FPGA面试题目笔记(二)——同步异步D触发器、静动态时序分析、分频设计、Retiming

解决ffmpeg获取AAC音频文件duration不准

ERROR 1215 (HY000): Cannot add foreign key constraint

Shandong University machine learning experiment 7 pca+ SVM face recognition

Which company is better in JIRA organizational structure management?

Jenkins user rights management
![[must see for game development] 3-step configuration p4ignore + wonderful Q & A analysis (reprinted from user articles)](/img/4c/42933ac0fde18798ed74a23279c826.jpg)
[must see for game development] 3-step configuration p4ignore + wonderful Q & A analysis (reprinted from user articles)

Examinelistactivity of Shandong University project training

FPGA interview notes (II) -- synchronous asynchronous D flip-flop, static and dynamic timing analysis, frequency division design, retiming
随机推荐
JIRA software annual summary: release of 12 important functions
Human gene editing technology and ethical issues behind it [personal view, for reference only]
Principle of copyonwritearraylist copy on write
Basic use of BufferedReader and bufferedwriter
Notes sur les questions d'entrevue de la FPGA (IV) - - détecteur de séquence, Code gris dans le domaine de l'horloge croisée, opération de ping - pong, réduction de la perte statique et dynamique, err
Warning: Each child in a list should have a unique “key“ prop.
jenkins-用户权限管理
FPGA interview notes (IV) -- sequence detector, gray code in cross clock domain, ping-pong operation, static and dynamic loss reduction, fixed-point lossless error, recovery time and removal time
Verilog realizes binocular camera image data acquisition and Modelsim simulation, and finally matlab performs image display
Wechat applet (authorized login) (not recommended, click the home page to view the updated authorized login)
Growth Diary 01
A multi classification model suitable for discrete value classification -- softmax regression
Stock K-line drawing
C语言大战“扫雷”
Using idea to add, delete, modify and query database
Learn C language well from keywords
Ethical discussion on reptile Technology
verilog实现双目摄像头图像数据采集并modelsim仿真,最终matlab进行图像显示
Training and testing of super score model in mmediting
修复鼠标右键没有vscode快捷入口的问题