tag manager google c h.264 libavcodec libavformat

c - google tag manager



Marcos H264 sin procesar en contenedores mpegts usando libavcodec (2)

Realmente agradecería algo de ayuda con el siguiente problema:

Tengo un gadget con una cámara, produciendo marcos de video comprimidos H264, estos marcos se envían a mi aplicación. Estos marcos no están en un contenedor, solo datos brutos.

Quiero usar las funciones ffmpeg y libav para crear un archivo de video, que se puede usar más adelante.

Si decodifico los cuadros, luego los codifico, todo funciona bien, obtengo un archivo de video válido. (los pasos de decodificación / codificación son los comandos de libav habituales, nada de lujoso aquí, los tomé del todopoderoso Internet, son sólidos como la roca) ... Sin embargo, pierdo mucho tiempo decodificando y codificando, así que me gustaría omita este paso y coloque directamente los cuadros en la secuencia de salida. Ahora, vienen los problemas.

Aquí está el código que se me ocurrió para producir la codificación:

AVFrame* picture; avpicture_fill((AVPicture*) picture, (uint8_t*)frameData, codecContext->pix_fmt, codecContext->width, codecContext->height); int outSize = avcodec_encode_video(codecContext, videoOutBuf, sizeof(videoOutBuf), picture); if (outSize > 0) { AVPacket packet; av_init_packet(&packet); packet.pts = av_rescale_q(codecContext->coded_frame->pts, codecContext->time_base, videoStream->time_base); if (codecContext->coded_frame->key_frame) { packet.flags |= PKT_FLAG_KEY; } packet.stream_index = videoStream->index; packet.data = videoOutBuf; packet.size = outSize; av_interleaved_write_frame(context, &packet); put_flush_packet(context->pb); }

Donde las variables son como:

frameData es el dato de fotograma decodificado, que vino de la cámara, fue decodificado en un paso previo y videoOutBuf es un buffer liso uint8_t para contener los datos

He modificado la aplicación para no decodificar los marcos, simplemente pasar los datos como:

AVPacket packet; av_init_packet(&packet); packet.stream_index = videoStream->index; packet.data = (uint8_t*)frameData; packet.size = currentFrameSize; av_interleaved_write_frame(context, &packet); put_flush_packet(context->pb);

dónde

frameData es el marco sin procesar H264 y currentFrameSize es el tamaño del marco sin procesar H264, es decir. la cantidad de bytes que obtengo del gadget para cada fotograma.

Y, de repente, la aplicación ya no funciona correctamente, el video producido no se puede reproducir. Esto es obvio, ya que no estaba configurando un PTS correcto para el paquete. Lo que hice fue lo siguiente (estoy desesperado, puedes verlo desde este enfoque :))

packet.pts = timestamps[timestamp_counter ++];

donde timestamps es en realidad una lista de PTS producida por el código de trabajo anterior, y escrita en un archivo (sí, la leyó correctamente, registré todos los PTS para una sesión de 10 minutos y quería usarlos).

La aplicación todavía no funciona.

Ahora, aquí estoy sin ninguna idea de qué hacer, así que aquí está la pregunta:

Me gustaría crear un flujo de video "mpegts" usando las funciones de libav, insertar en el flujo marcos de video ya codificados y crear un archivo de video con él. ¿Cómo lo hago?

Gracias, f.


Creo que si configuras lo siguiente, verás la reproducción de video.

packet.flags |= AV_PKT_FLAG_KEY; packet.pts = packet.dts = 0;

Realmente debe establecer packet.flags de acuerdo con los encabezados de paquetes h264. Puedes probar la sugerencia de este compañero de desbordamiento de pila para extraer directamente de la secuencia.

Si también está agregando audio, entonces pts / dts va a ser más importante. Te sugiero que estudies este tutorial

EDITAR

Encontré tiempo para extraer lo que funciona para mí desde mi aplicación de prueba. Por alguna razón, los valores dts / pts de cero me funcionan, pero los valores distintos de 0 o AV_NOPTS_VALUE no. Me pregunto si tenemos diferentes versiones de ffmpeg. Tengo lo último de git: //git.videolan.org/ffmpeg.git .

fftest.cpp

#include <string> #ifndef INT64_C #define INT64_C(c) (c ## LL) #define UINT64_C(c) (c ## ULL) #endif //#define _M #define _M printf( "%s(%d) : MARKER/n", __FILE__, __LINE__ ) extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" }; AVFormatContext *fc = 0; int vi = -1, waitkey = 1; // < 0 = error // 0 = I-Frame // 1 = P-Frame // 2 = B-Frame // 3 = S-Frame int getVopType( const void *p, int len ) { if ( !p || 6 >= len ) return -1; unsigned char *b = (unsigned char*)p; // Verify NAL marker if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] ) { b++; if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] ) return -1; } // end if b += 3; // Verify VOP id if ( 0xb6 == *b ) { b++; return ( *b & 0xc0 ) >> 6; } // end if switch( *b ) { case 0x65 : return 0; case 0x61 : return 1; case 0x01 : return 2; } // end switch return -1; } void write_frame( const void* p, int len ) { if ( 0 > vi ) return; AVStream *pst = fc->streams[ vi ]; // Init packet AVPacket pkt; av_init_packet( &pkt ); pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0; pkt.stream_index = pst->index; pkt.data = (uint8_t*)p; pkt.size = len; // Wait for key frame if ( waitkey ) if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) ) return; else waitkey = 0; pkt.dts = AV_NOPTS_VALUE; pkt.pts = AV_NOPTS_VALUE; // av_write_frame( fc, &pkt ); av_interleaved_write_frame( fc, &pkt ); } void destroy() { waitkey = 1; vi = -1; if ( !fc ) return; _M; av_write_trailer( fc ); if ( fc->oformat && !( fc->oformat->flags & AVFMT_NOFILE ) && fc->pb ) avio_close( fc->pb ); // Free the stream _M; av_free( fc ); fc = 0; _M; } int get_nal_type( void *p, int len ) { if ( !p || 5 >= len ) return -1; unsigned char *b = (unsigned char*)p; // Verify NAL marker if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] ) { b++; if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] ) return -1; } // end if b += 3; return *b; } int create( void *p, int len ) { if ( 0x67 != get_nal_type( p, len ) ) return -1; destroy(); const char *file = "test.avi"; CodecID codec_id = CODEC_ID_H264; // CodecID codec_id = CODEC_ID_MPEG4; int br = 1000000; int w = 480; int h = 354; int fps = 15; // Create container _M; AVOutputFormat *of = av_guess_format( 0, file, 0 ); fc = avformat_alloc_context(); fc->oformat = of; strcpy( fc->filename, file ); // Add video stream _M; AVStream *pst = av_new_stream( fc, 0 ); vi = pst->index; AVCodecContext *pcc = pst->codec; _M; avcodec_get_context_defaults2( pcc, AVMEDIA_TYPE_VIDEO ); pcc->codec_type = AVMEDIA_TYPE_VIDEO; pcc->codec_id = codec_id; pcc->bit_rate = br; pcc->width = w; pcc->height = h; pcc->time_base.num = 1; pcc->time_base.den = fps; // Init container _M; av_set_parameters( fc, 0 ); if ( !( fc->oformat->flags & AVFMT_NOFILE ) ) avio_open( &fc->pb, fc->filename, URL_WRONLY ); _M; av_write_header( fc ); _M; return 1; } int main( int argc, char** argv ) { int f = 0, sz = 0; char fname[ 256 ] = { 0 }; char buf[ 128 * 1024 ]; av_log_set_level( AV_LOG_ERROR ); av_register_all(); do { // Raw frames in v0.raw, v1.raw, v2.raw, ... // sprintf( fname, "rawvideo/v%lu.raw", f++ ); sprintf( fname, "frames/frame%lu.bin", f++ ); printf( "%s/n", fname ); FILE *fd = fopen( fname, "rb" ); if ( !fd ) sz = 0; else { sz = fread( buf, 1, sizeof( buf ) - FF_INPUT_BUFFER_PADDING_SIZE, fd ); if ( 0 < sz ) { memset( &buf[ sz ], 0, FF_INPUT_BUFFER_PADDING_SIZE ); if ( !fc ) create( buf, sz ); if ( fc ) write_frame( buf, sz ); } // end if fclose( fd ); } // end else } while ( 0 < sz ); destroy(); }


Puede crear un proceso para llamar a ffmpeg desde la consola.

Ejemplo de línea de comando para procesar archivos como 000001.jpg, 000002.jpg, 000003.jpg, ...

ffmpeg -ic: / frames /% 06d.jpg -r 16 -vcodec mpeg4 -an -yc: / video / some_video.avi

Otros ejemplos de documentos de ffmpeg