Tramas H264 raw en contenedor mpegts usando libavcodec
Realmente agradecería un poco de ayuda con el siguiente tema:
Tengo un gadget con una cámara, produciendo fotogramas de video comprimidos H264, estos fotogramas se envían a mi aplicación. Estos fotogramas no están en un contenedor, solo datos sin procesar.
Quiero usar las funciones ffmpeg y libav para crear un archivo de video, que se puede usar más tarde.
Si decodifico los fotogramas, luego los codifico, todo funciona bien, obtengo un archivo de video válido. (los pasos de decodificación/codificación son los habituales comandos libav, nada de fantasía aquí, los tomé de la Internet todopoderoso, son sólidos como una roca)... Sin embargo, pierdo mucho tiempo decodificando y codificando, así que me gustaría omitir este paso y poner directamente los fotogramas en el flujo de salida. Ahora, los problemas vienen.
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 los datos de fotograma decodificados, que provenían de la cámara, se decodificó en un paso anterior y videoOutBuf
es un búfer uint8_t simple para contener los datos
He modificado la aplicación para no decodificar los frames, sino simplemente pasar a través de 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);
Donde
frameData
es el marco crudo H264
y currentFrameSize
es el tamaño del marco H264 crudo, es decir. el número 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 estableciendo un correcto PTS para el paquete. Lo que hice fue lo siguiente (estoy desesperado, se puede ver desde este enfoque :))
packet.pts = timestamps[timestamp_counter ++];
Donde timestamps
es en realidad una lista de PTS producidos por el código de trabajo anterior, y escrito en un archivo (sí, lo 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 pista de qué hacer, así que aquí está la pregunta:
Me gustaría crear un "mpegts" transmisión de vídeo con las funciones libav, inserte en la transmisión fotogramas de vídeo ya codificados y cree un archivo de vídeo con él. ¿Cómo lo hago?
Gracias, f.
2 answers
Creo que si configura lo siguiente, verá la reproducción de video.
packet.flags |= AV_PKT_FLAG_KEY;
packet.pts = packet.dts = 0;
Realmente debería configurar paquete.banderas según las cabeceras de paquetes h264. Puedes probar la sugerencia de este compañero de stack overflowian 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 está funcionando para mí de mi aplicación de prueba. Por alguna razón, los valores dts / pts de cero funcionan para mí, pero los valores que no sean 0 o AV_NOPTS_VALUE no lo hacen. 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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:14
Puede crear un proceso para llamar a ffmpeg desde la consola.
Ejemplo de línea de comandos para procesar archivos como 000001.jpg, 000002.jpg, 000003.jpg, ...
Ffmpeg-i c:\frames\%06d.jpg -r 16-vcodec mpeg4-an-y c:\video\some_video.avi
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-22 16:42:44