tecnicas por multiplexacion frecuencia ejemplos codigo c++ audio video ffmpeg libavformat

c++ - por - FFMPEG: multiplexación de flujos con diferente duración



multiplexacion por division de frecuencia ejemplos (1)

Estoy multiplexando secuencias de video y audio. El flujo de video proviene de los datos de imagen generados. El flujo de audio proviene de un archivo aac. Algunos archivos de audio son más largos que el tiempo total de video que configuré, por lo que mi estrategia para detener la transmisión de audio cuando su tiempo sea mayor que el tiempo total de video (el último que controlo por número de cuadros de video codificados).

No pondré aquí todo el código de configuración, pero es similar al ejemplo muxing.c del último repositorio de FFMPEG. La única diferencia es que uso el flujo de audio del archivo, como dije, no del cuadro codificado generado sintéticamente. Estoy bastante seguro de que el problema está en mi sincronización incorrecta durante el bucle de muxer. Esto es lo que hago:

void AudioSetup(const char* audioInFileName) { AVOutputFormat* outputF = mOutputFormatContext->oformat; auto audioCodecId = outputF->audio_codec; if (audioCodecId == AV_CODEC_ID_NONE) { return false; } audio_codec = avcodec_find_encoder(audioCodecId); avformat_open_input(&mInputAudioFormatContext, audioInFileName, 0, 0); avformat_find_stream_info(mInputAudioFormatContext, 0); av_dump_format(mInputAudioFormatContext, 0, audioInFileName, 0); for (size_t i = 0; i < mInputAudioFormatContext->nb_streams; i++) { if (mInputAudioFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { inAudioStream = mInputAudioFormatContext->streams[i]; AVCodecParameters *in_codecpar = inAudioStream->codecpar; mAudioOutStream.st = avformat_new_stream(mOutputFormatContext, NULL); mAudioOutStream.st->id = mOutputFormatContext->nb_streams - 1; AVCodecContext* c = avcodec_alloc_context3(audio_codec); mAudioOutStream.enc = c; c->sample_fmt = audio_codec->sample_fmts[0]; avcodec_parameters_to_context(c, inAudioStream->codecpar); //copyparams from input to autput audio stream: avcodec_parameters_copy(mAudioOutStream.st->codecpar, inAudioStream->codecpar); mAudioOutStream.st->time_base.num = 1; mAudioOutStream.st->time_base.den = c->sample_rate; c->time_base = mAudioOutStream.st->time_base; if (mOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER) { c->flags |= CODEC_FLAG_GLOBAL_HEADER; } break; } } } void Encode() { int cc = av_compare_ts(mVideoOutStream.next_pts, mVideoOutStream.enc->time_base, mAudioOutStream.next_pts, mAudioOutStream.enc->time_base); if (mAudioOutStream.st == NULL || cc <= 0) { uint8_t* data = GetYUVFrame();//returns ready video YUV frame to work with int ret = 0; AVPacket pkt = { 0 }; av_init_packet(&pkt); pkt.size = packet->dataSize; pkt.data = data; const int64_t duration = av_rescale_q(1, mVideoOutStream.enc->time_base, mVideoOutStream.st->time_base); pkt.duration = duration; pkt.pts = mVideoOutStream.next_pts; pkt.dts = mVideoOutStream.next_pts; mVideoOutStream.next_pts += duration; pkt.stream_index = mVideoOutStream.st->index; ret = av_interleaved_write_frame(mOutputFormatContext, &pkt); } else if(audio_time < video_time) { //5 - duration of video in seconds AVRational r = { 60, 1 }; auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r); if (cmp >= 0) { mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max(); return true; //don''t mux audio anymore } AVPacket a_pkt = { 0 }; av_init_packet(&a_pkt); int ret = 0; ret = av_read_frame(mInputAudioFormatContext, &a_pkt); //if audio file is shorter than stop muxing when at the end of the file if (ret == AVERROR_EOF) { mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max(); return true; } a_pkt.stream_index = mAudioOutStream.st->index; av_packet_rescale_ts(&a_pkt, inAudioStream->time_base, mAudioOutStream.st->time_base); mAudioOutStream.next_pts += a_pkt.pts; ret = av_interleaved_write_frame(mOutputFormatContext, &a_pkt); } }

Ahora, la parte de video es impecable. Pero si la pista de audio es más larga que la duración del video, estoy obteniendo una duración total del video de aproximadamente 5% a 20%, y está claro que el audio está contribuyendo a que los cuadros de video se terminen exactamente donde se supone que deben estar.

El ''truco'' más cercano con el que vine es esta parte:

AVRational r = { 60 ,1 }; auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r); if (cmp >= 0) { mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max(); return true; }

Aquí estaba tratando de comparar los siguientes next_pts del flujo de audio con el tiempo total establecido para el archivo de video, que es de 5 segundos. Al establecer r = {60,1} estoy convirtiendo esos segundos en la base de tiempo de la transmisión de audio. Al menos eso es lo que creo que estoy haciendo. Con este truco, obtengo una desviación muy pequeña de la duración correcta de la película al usar archivos AAC estándar, que es una frecuencia de muestreo de 44100, estéreo. Pero si pruebo con muestras más problemáticas, como AAC 16000, mono, entonces el archivo de video agrega casi un segundo a su tamaño. Apreciaré si alguien puede señalar lo que estoy haciendo mal aquí.

Nota importante: no configuro la duración en ninguno de los contextos. Controlo la finalización de la sesión muxing, que se basa en el recuento de fotogramas de video. El flujo de entrada de audio tiene una duración, por supuesto, pero no me ayuda ya que la duración del video es lo que define la duración de la película.

ACTUALIZAR:

Este es el segundo intento de recompensa.

ACTUALIZACIÓN 2:

En realidad, mi marca de tiempo de audio de {den, num} estaba equivocada, mientras que {1,1} es el camino a seguir, como lo explica la respuesta. Lo que impedía que funcionara era un error en esta línea (mi error):

mAudioOutStream.next_pts += a_pkt.pts;

Que debe ser:

mAudioOutStream.next_pts = a_pkt.pts;

El error provocó un incremento exponencial de pts, lo que provocó un alcance muy temprano hasta el final de la secuencia (en términos de pts) y, por lo tanto, provocó que la transmisión de audio terminara mucho antes de lo que se suponía.


El problema es que le dices que compare el tiempo de audio dado con 5 tics a 60 seconds per tick . Estoy realmente sorprendido de que funcione en algunos casos, pero creo que realmente depende de la time_base de time_base específica de la time_base de audio dada.

Supongamos que el audio tiene una time_base de time_base de 1/25 y la transmisión es de 6 segundos, que es más de lo que desea, por lo que desea que av_compare_ts devuelva 0 o 1 . Dadas estas condiciones, tendrás los siguientes valores:

mAudioOutStream.next_pts = 150 mAudioOutStream.enc->time_base = 1/25

Así llamas a av_compare_ts con los siguientes parámetros:

ts_a = 150 tb_a = 1/25 ts_b = 5 tb_b = 60/1

Ahora veamos la implementación de av_compare_ts :

int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b) { int64_t a = tb_a.num * (int64_t)tb_b.den; int64_t b = tb_b.num * (int64_t)tb_a.den; if ((FFABS(ts_a)|a|FFABS(ts_b)|b) <= INT_MAX) return (ts_a*a > ts_b*b) - (ts_a*a < ts_b*b); if (av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b) return -1; if (av_rescale_rnd(ts_b, b, a, AV_ROUND_DOWN) < ts_a) return 1; return 0; }

Teniendo en cuenta los valores anteriores, obtienes:

a = 1 * 1 = 1 b = 60 * 25 = 1500

Entonces av_rescale_rnd se llama con estos parámetros:

a = 150 b = 1 c = 1500 rnd = AV_ROUND_DOWN

Teniendo en cuenta nuestros parámetros, podemos en realidad av_rescale_rnd toda la función av_rescale_rnd en la siguiente línea. (No av_rescale_rnd todo el cuerpo de la función para av_rescale_rnd ya que es bastante largo, pero puedes verlo here ).

return (a * b) / c;

Esto devolverá (150 * 1) / 1500 , que es 0 .

Por av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b tanto, av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b se resolverá como true , porque 0 es más pequeño que ts_b ( 5 ), y así av_compare_ts devolverá -1 , que no es exactamente lo que desea.

Si cambias tu r a 1/1 debería funcionar, porque ahora tus 5 serán tratados como 5 seconds :

ts_a = 150 tb_a = 1/25 ts_b = 5 tb_b = 1/1

En av_compare_ts ahora obtenemos:

a = 1 * 1 = 1 b = 1 * 25 = 25

Entonces av_rescale_rnd se llama con estos parámetros:

a = 150 b = 1 c = 25 rnd = AV_ROUND_DOWN

Esto devolverá (150 * 1) / 25 , que es 6 .

6 es mayor que 5 , la condición falla y av_rescale_rnd se llama nuevamente, esta vez con:

a = 5 b = 25 c = 1 rnd = AV_ROUND_DOWN

que devolverá (5 * 25) / 1 , que es 125 . Eso es más pequeño que 150 , por lo tanto, se devuelve 1 y se soluciona su problema.

En caso de que step_size sea mayor que 1

Si el step_size de su flujo de audio no es 1 , necesita modificar su r para step_size = 1024 en cuenta, por ejemplo, step_size = 1024 :

r = { 1, 1024 };

Repasemos rápidamente lo que sucede ahora:

En ~ 6 segundos:

mAudioOutStream.next_pts = 282 mAudioOutStream.enc->time_base = 1/48000

av_compare_ts obtiene los siguientes parámetros:

ts_a = 282 tb_a = 1/48000 ts_b = 5 tb_b = 1/1024

Así:

a = 1 * 1024 = 1024 b = 1 * 48000 = 48000

Y en av_rescale_rnd :

a = 282 b = 1024 c = 48000 rnd = AV_ROUND_DOWN

(a * b) / c dará (282 * 1024) / 48000 = 288768 / 48000 que es 6 .

Con r={1,1} habrías obtenido 0 nuevo, porque habría calculado (281 * 1) / 48000 .