2017-03-03 2 views
1

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

Antwort

1

I dont calculate pts and dts Das ist Ihr Problem. Bildfrequenz und Bitrate sind beide Verhältnisse, bei denen die Zeit der Nenner ist. Aber wenn du keine PTS/DTS schreibst, hast du ein kürzeres Video als du willst. h.264 timestamp nicht jeden Frame. das ist der Containerjob. Sie müssen Zeitstempel aus der bekannten Bildrate oder einem anderen Wert erstellen.

+0

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

+0

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

+0

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

Verwandte Themen