Guten tag.FFMpeg: h264 stream in mp4 container ohne änderungen schreiben
Der Kürze wegen enthält der Code Fehlerbehandlung und Speicherverwaltung.
Ich möchte h264 Videostream erfassen und es in MP4-Container ohne Änderungen packen. Da ich die Quelle des Streams nicht kontrolliere, kann ich keine Annahmen über die Stream-Struktur treffen. Auf diese Weise muss ich den Eingangsstrom prüfen.
AVProbeData probeData;
probeData.buf_size = s->BodySize();
probeData.buf = s->GetBody();
probeData.filename = "";
AVInputFormat* inFormat = av_probe_input_format(&probeData, 1);
Dieser Code definiert korrekt h264-Stream.
Als nächstes mache ich Eingabeformat Zusammenhang
unsigned char* avio_input_buffer = reinterpret_cast<unsigned char*> (av_malloc(AVIO_BUFFER_SIZE));
AVIOContext* avio_input_ctx = avio_alloc_context(avio_input_buffer, AVIO_BUFFER_SIZE,
0, this, &read_packet, NULL, NULL);
AVFormatContext* ifmt_ctx = avformat_alloc_context();
ifmt_ctx->pb = avio_input_ctx;
int ret = avformat_open_input(&ifmt_ctx, NULL, inFormat, NULL);
Set Bildgröße,
ifmt_ctx->streams[0]->codec->width = ifmt_ctx->streams[0]->codec->coded_width = width;
ifmt_ctx->streams[0]->codec->height = ifmt_ctx->streams[0]->codec->coded_height = height;
Ausgabeformat Kontext erstellen,
unsigned char* avio_output_buffer = reinterpret_cast<unsigned char*>(av_malloc(AVIO_BUFFER_SIZE));
AVIOContext* avio_output_ctx = avio_alloc_context(avio_output_buffer, AVIO_BUFFER_SIZE,
1, this, NULL, &write_packet, NULL);
AVFormatContext* ofmt_ctx = nullptr;
avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", NULL);
ofmt_ctx->pb = avio_output_ctx;
AVDictionary* dict = nullptr;
av_dict_set(&dict, "movflags", "faststart", 0);
av_dict_set(&dict, "movflags", "frag_keyframe+empty_moov", 0);
AVStream* outVideoStream = avformat_new_stream(ofmt_ctx, nullptr);
avcodec_copy_context(outVideoStream->codec, ifmt_ctx->streams[0]->codec);
ret = avformat_write_header(ofmt_ctx, &dict);
Initialisierung erfolgt. Weiter gibt es eine Verschiebung von Paketen vom h264-Stream zum mp4-Container. Ich berechne nicht pts und dts, weil Quellpaket AV_NOPTS_VALUE in ihnen hat.
AVPacket pkt;
while (...)
{
ret = av_read_frame(ifmt_ctx, &pkt);
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
av_free_packet(&pkt);
}
Weiter schreibe ich Trailer und freien Speicherplatz zugewiesen. Das ist alles. Code funktioniert und ich habe eine spielbare mp4-Datei.
Jetzt das Problem: Die Stream-Eigenschaften der resultierenden Datei stimmt nicht vollständig mit den Eigenschaften des Quell-Stream überein. Insbesondere sind fps und Bitrate höher als es sein sollte.
Als Probe unten ausgegeben ffplay.exe für Quellenstrom
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/source.mp4':0/0
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2014-04-14T13:03:54.000000Z
Duration: 00:00:58.08, start: 0.000000, bitrate: 12130 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1/0x31637661),yuv420p, 1920x1080, 12129 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Switch subtitle stream from #-1 to #-1 vq= 1428KB sq= 0B f=0/0
Seek to 49% (0:00:28) of total duration (0:00:58) B f=0/0
30.32 M-V: -0.030 fd= 87 aq= 0KB vq= 1360KB sq= 0B f=0/0
und für resultierende Strom (enthält einen Teil der Quellenstrom)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/target.mp4':f=0/0
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1iso6mp41
encoder : Lavf57.56.101
Duration: 00:00:11.64, start: 0.000000, bitrate: 18686 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1/0x31637661), yuv420p, 1920x1080, 18683 kb/s, 38.57 fps, 40 tbr, 90k tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Switch subtitle stream from #-1 to #-1 vq= 2309KB sq= 0B f=0/0
5.70 M-V: 0.040 fd= 127 aq= 0KB vq= 2562KB sq= 0B f=0/0
es ist also eine Frage, was ich verpasst beim Kopieren von Stream? Ich werde für jede Hilfe dankbar sein.
Mit freundlichen Grüßen
Danke für die Antwort, szatmary. Aber ich bin Neuling bei ffmpeg und ich verstehe nicht vollständig, was ich tun soll. Nur nützliche Felder, die ich habe, sind time_base-Felder in der AVStream-Struktur für den Ein- und Ausgabestream. Mit welchen Methoden werden Punkte berechnet? – Bumblebee
Zeitbasis ist ein Verhältnis, sagen wir 1/30, das bedeutet, Zeitstempel bewegen sich um 1/30 Sekunde nach vorne. Wenn Sie also 30 Bilder pro Sekunde haben, werden die Punkte für jedes Bild um eins erhöht. Wenn die Zeitbasis 1/3000 ist, wird sie um 100 erhöht. Schauen Sie sich die Codec-Kontext-Zeitbasis an und schreiben Sie den Frame-Zeitstempel in diese Basis. – szatmary
Das Problem ist, dass ich normalerweise fps des Eingabestroms nicht kenne. Die Felder pts und dts enthalten AV_NOPTS_VALUE, daher kann ich av_rescale_ nicht aufrufen, um korrekte pts \ dts für den Ausgabestream zu erhalten. – Bumblebee