Diferente entre archivos mp4 fragmentados generados por ffmpeg y por código
Actualmente tengo un problema al generar un archivo MP4 fragmentado a partir de código usando libavformat. Mi archivo se puede reproducir usando VLC, pero no se puede transmitir (a través de WebSocket) y reproducir (a través de MediaSource) en el navegador (Chrome). (Solíaesta para probar la transmisión de archivos MP4 fragmentados al navegador a través de WebSocket).
Nota: Los archivos a continuación están codificados por el perfil de línea de base, nivel 4. Por lo tanto, debe cambiar el tipo MIME (en index.html) a const mimeCodec = 'video / mp4; códecs = "avc1.42C028" '; para poder jugarlos.
Verifiqué y descubrí que mi archivo Mp4 generado es un poco diferente con el archivo generado al usarffmpeg
herramienta.
Esto es lo que he hecho:
Tengo un .h264archivo
El primer enfoque, uso ffmpeg para generar elarchivo MP4 fragmentado.
ffmpeg -i temp.h264 -vcodec copy -f mp4 -movflags empty_moov+default_base_moof+frag_keyframe ffmpeg.mp4
El archivo generado puede ser reproducido tanto por el reproductor Quicktime como por el reproductor VLC
El segundo enfoque, genero programáticamentearchivo Mp4 fragmentado utilizando libavformat
Primero inicializo el contexto, elcodec
en código es elAVCodecContext*
de la secuencia de entrada
av_register_all();
avcodec_register_all();
int ret;
AVOutputFormat* fmt = av_guess_format("mp4", 0, 0);
if(!fmt) {
return;
}
AVFormatContext* ctx = avformat_alloc_context();
// Create AVIO context to capture generated Mp4 contain
uint8_t *avio_ctx_buffer = NULL;
size_t avio_ctx_buffer_size = 50000;
IOOutput buffer = {};
const size_t bd_buf_size = 50000;
avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
buffer.buff = (uint8_t*)av_malloc(bd_buf_size);
buffer.size = bd_buf_size;
AVIOContext* ioCtx = avio_alloc_context(avio_ctx_buffer, (int)avio_ctx_buffer_size, 1, &buffer, NULL, MP4Formatter::onWritePacket, NULL);
ctx->oformat = fmt;
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
ctx->pb = ioCtx;
av_opt_set(ctx, "movflags", "frag_keyframe+empty_moov+default_base_moof", 0);
AVStream* st = avformat_new_stream(ctx, codec->codec);
if (!st) {
return;
}
st->id = (ctx->nb_streams - 1);
avcodec_parameters_from_context(st->codecpar, codec);
st->time_base = codec->time_base;
ioCtx->seekable = false;
Segundo, implemento la devolución de llamada onWritePacket
int MP4Formatter::onWritePacket(void *opaque, uint8_t* buf, int buf_size) {
file.write((char*)buf, buf_size);
}
Tercero, en cada paquete del archivo h264, lo escribo usandoav_interleaved_write_frame
if (firstFrame) {
AVDictionary *opts = NULL;
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+default_base_moof", 0);
if(!parseSPSPPS(data, length)) {
return;
}
cout << "spslen " << spslen << " ppslen " << ppslen << endl;
auto c = st->codecpar;
// Extradata contains PPS & SPS for AVCC format
int extradata_len = 8 + spslen + 1 + 2 + ppslen;
c->extradata = (uint8_t*)av_mallocz(extradata_len);
c->extradata_size = extradata_len;
c->extradata[0] = 0x01;
c->extradata[1] = sps[1];
c->extradata[2] = sps[2];
c->extradata[3] = sps[3];
c->extradata[4] = 0xFC | 3;
c->extradata[5] = 0xE0 | 1;
int tmp = spslen;
c->extradata[6] = (tmp >> 8) & 0x00ff;
c->extradata[7] = tmp & 0x00ff;
int i = 0;
for (i=0; i<tmp; i++) {
c->extradata[8 + i] = sps[i];
}
c->extradata[8 + tmp] = 0x01;
int tmp2 = ppslen;
c->extradata[8 + tmp + 1] = (tmp2 >> 8) & 0x00ff;
c->extradata[8 + tmp + 2] = tmp2 & 0x00ff;
for (i=0; i<tmp2; i++) {
c->extradata[8 + tmp + 3 + i] = pps[i];
}
int ret = avformat_write_header(ctx, &opts);
if(ret < 0) {
return;
}
firstFrame = false;
}
AVPacket pkt;
av_init_packet(&pkt);
pkt.buf = av_buffer_alloc(length);
memcpy(pkt.buf->data, data, length);
pkt.buf->size = length;
pkt.data = pkt.buf->data;
pkt.size = pkt.buf->size;
pkt.pts = ts;
pkt.dts = ts;
if (keyFrame) {
pkt.flags |= AV_PKT_FLAG_KEY;
}
else {
pkt.flags = 0;
}
pkt.stream_index = st->id;
av_interleaved_write_frame(ctx, &pkt);
av_buffer_unref(&pkt.buf);
av_packet_unref(&pkt);
¿Pueden echar un vistazo a mi archivo para ver qué pasa?