Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
ffmpeg [2025/12/10 16:05] jango [Remux] |
ffmpeg [2025/12/10 16:54] (aktuell) jango |
||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | =====Stream===== | + | =====CLI===== |
| + | |||
| + | ====Stream==== | ||
| Mit [[vlc|VLC]] kann man auch direkt streamen. | Mit [[vlc|VLC]] kann man auch direkt streamen. | ||
| Zeile 36: | Zeile 38: | ||
| </ | </ | ||
| - | =====Convert===== | + | ====Convert==== |
| https:// | https:// | ||
| Zeile 66: | Zeile 68: | ||
| </ | </ | ||
| - | =====Modify===== | + | ====Modify==== |
| < | < | ||
| Zeile 350: | Zeile 352: | ||
| ====Transcode==== | ====Transcode==== | ||
| + | |||
| + | ===Video to Video=== | ||
| <code c++> | <code c++> | ||
| Zeile 355: | Zeile 359: | ||
| #include < | #include < | ||
| #include < | #include < | ||
| + | |||
| extern " | extern " | ||
| #include < | #include < | ||
| Zeile 362: | Zeile 366: | ||
| #include < | #include < | ||
| #include < | #include < | ||
| + | #include < | ||
| } | } | ||
| + | |||
| static std::string fferr(int e) { | static std::string fferr(int e) { | ||
| char buf[AV_ERROR_MAX_STRING_SIZE] = {0}; | char buf[AV_ERROR_MAX_STRING_SIZE] = {0}; | ||
| Zeile 369: | Zeile 374: | ||
| return buf; | return buf; | ||
| } | } | ||
| + | |||
| static int encode_and_write(AVCodecContext* enc, | static int encode_and_write(AVCodecContext* enc, | ||
| AVFrame* frame, | AVFrame* frame, | ||
| Zeile 378: | Zeile 383: | ||
| int ret = avcodec_send_frame(enc, | int ret = avcodec_send_frame(enc, | ||
| if (ret < 0) return ret; | if (ret < 0) return ret; | ||
| + | |||
| while (true) { | while (true) { | ||
| ret = avcodec_receive_packet(enc, | ret = avcodec_receive_packet(enc, | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return 0; | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return 0; | ||
| if (ret < 0) return ret; | if (ret < 0) return ret; | ||
| + | |||
| // Packet timestamps to muxer time_base | // Packet timestamps to muxer time_base | ||
| av_packet_rescale_ts(pkt, | av_packet_rescale_ts(pkt, | ||
| pkt-> | pkt-> | ||
| + | |||
| ret = av_interleaved_write_frame(ofmt, | ret = av_interleaved_write_frame(ofmt, | ||
| av_packet_unref(pkt); | av_packet_unref(pkt); | ||
| Zeile 393: | Zeile 398: | ||
| } | } | ||
| } | } | ||
| + | |||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||
| if (argc < 3) { | if (argc < 3) { | ||
| Zeile 399: | Zeile 404: | ||
| return 1; | return 1; | ||
| } | } | ||
| + | |||
| const char* in_filename | const char* in_filename | ||
| const char* out_filename = argv[2]; | const char* out_filename = argv[2]; | ||
| + | |||
| AVFormatContext* ifmt = nullptr; | AVFormatContext* ifmt = nullptr; | ||
| AVFormatContext* ofmt = nullptr; | AVFormatContext* ofmt = nullptr; | ||
| + | |||
| AVCodecContext* dec_v = nullptr; | AVCodecContext* dec_v = nullptr; | ||
| AVCodecContext* enc_v = nullptr; | AVCodecContext* enc_v = nullptr; | ||
| SwsContext* sws = nullptr; | SwsContext* sws = nullptr; | ||
| + | |||
| AVFrame* dec_frame = av_frame_alloc(); | AVFrame* dec_frame = av_frame_alloc(); | ||
| AVFrame* enc_frame = av_frame_alloc(); | AVFrame* enc_frame = av_frame_alloc(); | ||
| AVPacket* in_pkt | AVPacket* in_pkt | ||
| AVPacket* out_pkt | AVPacket* out_pkt | ||
| + | |||
| if (!dec_frame || !enc_frame || !in_pkt || !out_pkt) { | if (!dec_frame || !enc_frame || !in_pkt || !out_pkt) { | ||
| std::cerr << " | std::cerr << " | ||
| return 1; | return 1; | ||
| } | } | ||
| + | |||
| int ret = 0; | int ret = 0; | ||
| + | |||
| // --- open input --- | // --- open input --- | ||
| ret = avformat_open_input(& | ret = avformat_open_input(& | ||
| if (ret < 0) { std::cerr << "open input: " << fferr(ret) << " | if (ret < 0) { std::cerr << "open input: " << fferr(ret) << " | ||
| + | |||
| ret = avformat_find_stream_info(ifmt, | ret = avformat_find_stream_info(ifmt, | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| // find best video stream | // find best video stream | ||
| int v_si = av_find_best_stream(ifmt, | int v_si = av_find_best_stream(ifmt, | ||
| if (v_si < 0) { std::cerr << "No video stream found.\n"; | if (v_si < 0) { std::cerr << "No video stream found.\n"; | ||
| + | |||
| AVStream* in_vst = ifmt-> | AVStream* in_vst = ifmt-> | ||
| + | |||
| // --- open video decoder --- | // --- open video decoder --- | ||
| const AVCodec* vdec = avcodec_find_decoder(in_vst-> | const AVCodec* vdec = avcodec_find_decoder(in_vst-> | ||
| if (!vdec) { std::cerr << "No decoder.\n"; | if (!vdec) { std::cerr << "No decoder.\n"; | ||
| + | |||
| dec_v = avcodec_alloc_context3(vdec); | dec_v = avcodec_alloc_context3(vdec); | ||
| if (!dec_v) { std::cerr << "alloc dec ctx failed\n"; | if (!dec_v) { std::cerr << "alloc dec ctx failed\n"; | ||
| + | |||
| ret = avcodec_parameters_to_context(dec_v, | ret = avcodec_parameters_to_context(dec_v, | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| ret = avcodec_open2(dec_v, | ret = avcodec_open2(dec_v, | ||
| if (ret < 0) { std::cerr << "open decoder: " << fferr(ret) << " | if (ret < 0) { std::cerr << "open decoder: " << fferr(ret) << " | ||
| + | |||
| // --- open output --- | // --- open output --- | ||
| ret = avformat_alloc_output_context2(& | ret = avformat_alloc_output_context2(& | ||
| if (ret < 0 || !ofmt) { std::cerr << "alloc output: " << fferr(ret) << " | if (ret < 0 || !ofmt) { std::cerr << "alloc output: " << fferr(ret) << " | ||
| + | |||
| std:: | std:: | ||
| + | |||
| // Create output streams for non-video (copy) | // Create output streams for non-video (copy) | ||
| for (unsigned i = 0; i < ifmt-> | for (unsigned i = 0; i < ifmt-> | ||
| if ((int)i == v_si) continue; | if ((int)i == v_si) continue; | ||
| + | |||
| AVStream* in_st = ifmt-> | AVStream* in_st = ifmt-> | ||
| if (in_st-> | if (in_st-> | ||
| Zeile 463: | Zeile 468: | ||
| continue; | continue; | ||
| } | } | ||
| + | |||
| AVStream* out_st = avformat_new_stream(ofmt, | AVStream* out_st = avformat_new_stream(ofmt, | ||
| if (!out_st) { std::cerr << "new stream failed\n"; | if (!out_st) { std::cerr << "new stream failed\n"; | ||
| + | |||
| ret = avcodec_parameters_copy(out_st-> | ret = avcodec_parameters_copy(out_st-> | ||
| if (ret < 0) { std::cerr << "par copy: " << fferr(ret) << " | if (ret < 0) { std::cerr << "par copy: " << fferr(ret) << " | ||
| + | |||
| out_st-> | out_st-> | ||
| out_st-> | out_st-> | ||
| map[i] = out_st-> | map[i] = out_st-> | ||
| } | } | ||
| + | |||
| // --- create output video stream (H.264 encode) --- | // --- create output video stream (H.264 encode) --- | ||
| AVStream* out_vst = avformat_new_stream(ofmt, | AVStream* out_vst = avformat_new_stream(ofmt, | ||
| if (!out_vst) { std::cerr << "new video stream failed\n"; | if (!out_vst) { std::cerr << "new video stream failed\n"; | ||
| map[v_si] = out_vst-> | map[v_si] = out_vst-> | ||
| + | |||
| // prefer libx264 if available, fallback to any H.264 encoder | // prefer libx264 if available, fallback to any H.264 encoder | ||
| const AVCodec* venc = avcodec_find_encoder_by_name(" | const AVCodec* venc = avcodec_find_encoder_by_name(" | ||
| if (!venc) venc = avcodec_find_encoder(AV_CODEC_ID_H264); | if (!venc) venc = avcodec_find_encoder(AV_CODEC_ID_H264); | ||
| if (!venc) { std::cerr << "No H.264 encoder found.\n"; | if (!venc) { std::cerr << "No H.264 encoder found.\n"; | ||
| + | |||
| enc_v = avcodec_alloc_context3(venc); | enc_v = avcodec_alloc_context3(venc); | ||
| if (!enc_v) { std::cerr << "alloc enc ctx failed\n"; | if (!enc_v) { std::cerr << "alloc enc ctx failed\n"; | ||
| + | |||
| enc_v-> | enc_v-> | ||
| enc_v-> | enc_v-> | ||
| enc_v-> | enc_v-> | ||
| + | |||
| // MP4 wants yuv420p typically | // MP4 wants yuv420p typically | ||
| enc_v-> | enc_v-> | ||
| + | |||
| AVRational fps = av_guess_frame_rate(ifmt, | AVRational fps = av_guess_frame_rate(ifmt, | ||
| if (fps.num <= 0 || fps.den <= 0) fps = AVRational{25, | if (fps.num <= 0 || fps.den <= 0) fps = AVRational{25, | ||
| enc_v-> | enc_v-> | ||
| enc_v-> | enc_v-> | ||
| + | |||
| enc_v-> | enc_v-> | ||
| + | |||
| if (ofmt-> | if (ofmt-> | ||
| enc_v-> | enc_v-> | ||
| + | |||
| // if libx264: set some nice defaults (ignore errors if unsupported) | // if libx264: set some nice defaults (ignore errors if unsupported) | ||
| av_opt_set(enc_v-> | av_opt_set(enc_v-> | ||
| av_opt_set(enc_v-> | av_opt_set(enc_v-> | ||
| + | |||
| ret = avcodec_open2(enc_v, | ret = avcodec_open2(enc_v, | ||
| if (ret < 0) { std::cerr << "open encoder: " << fferr(ret) << " | if (ret < 0) { std::cerr << "open encoder: " << fferr(ret) << " | ||
| + | |||
| ret = avcodec_parameters_from_context(out_vst-> | ret = avcodec_parameters_from_context(out_vst-> | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| out_vst-> | out_vst-> | ||
| + | |||
| // --- open output IO + header --- | // --- open output IO + header --- | ||
| if (!(ofmt-> | if (!(ofmt-> | ||
| Zeile 521: | Zeile 526: | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| } | } | ||
| + | |||
| ret = avformat_write_header(ofmt, | ret = avformat_write_header(ofmt, | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| // prepare scaling/ | // prepare scaling/ | ||
| enc_frame-> | enc_frame-> | ||
| enc_frame-> | enc_frame-> | ||
| enc_frame-> | enc_frame-> | ||
| + | |||
| ret = av_frame_get_buffer(enc_frame, | ret = av_frame_get_buffer(enc_frame, | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| int64_t fallback_pts = 0; | int64_t fallback_pts = 0; | ||
| + | |||
| // --- main loop --- | // --- main loop --- | ||
| while ((ret = av_read_frame(ifmt, | while ((ret = av_read_frame(ifmt, | ||
| int si = in_pkt-> | int si = in_pkt-> | ||
| + | |||
| if (si != v_si) { | if (si != v_si) { | ||
| // remux non-video streams (copy) | // remux non-video streams (copy) | ||
| Zeile 544: | Zeile 549: | ||
| AVStream* in_st = ifmt-> | AVStream* in_st = ifmt-> | ||
| AVStream* out_st = ofmt-> | AVStream* out_st = ofmt-> | ||
| + | |||
| in_pkt-> | in_pkt-> | ||
| av_packet_rescale_ts(in_pkt, | av_packet_rescale_ts(in_pkt, | ||
| in_pkt-> | in_pkt-> | ||
| + | |||
| int wret = av_interleaved_write_frame(ofmt, | int wret = av_interleaved_write_frame(ofmt, | ||
| if (wret < 0) std::cerr << "write copy pkt: " << fferr(wret) << " | if (wret < 0) std::cerr << "write copy pkt: " << fferr(wret) << " | ||
| Zeile 555: | Zeile 560: | ||
| continue; | continue; | ||
| } | } | ||
| + | |||
| // --- decode video packet --- | // --- decode video packet --- | ||
| ret = avcodec_send_packet(dec_v, | ret = avcodec_send_packet(dec_v, | ||
| av_packet_unref(in_pkt); | av_packet_unref(in_pkt); | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| while (true) { | while (true) { | ||
| ret = avcodec_receive_frame(dec_v, | ret = avcodec_receive_frame(dec_v, | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| // pts handling (best-effort) | // pts handling (best-effort) | ||
| int64_t in_pts = (dec_frame-> | int64_t in_pts = (dec_frame-> | ||
| ? dec_frame-> | ? dec_frame-> | ||
| - | : | + | : dec_frame-> |
| if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | ||
| + | |||
| int64_t enc_pts = av_rescale_q(in_pts, | int64_t enc_pts = av_rescale_q(in_pts, | ||
| + | |||
| // convert to encoder format if needed | // convert to encoder format if needed | ||
| if (!sws) { | if (!sws) { | ||
| Zeile 581: | Zeile 586: | ||
| if (!sws) { std::cerr << " | if (!sws) { std::cerr << " | ||
| } | } | ||
| + | |||
| ret = av_frame_make_writable(enc_frame); | ret = av_frame_make_writable(enc_frame); | ||
| if (ret < 0) { std::cerr << "frame not writable: " << fferr(ret) << " | if (ret < 0) { std::cerr << "frame not writable: " << fferr(ret) << " | ||
| + | |||
| sws_scale(sws, | sws_scale(sws, | ||
| dec_frame-> | dec_frame-> | ||
| 0, dec_v-> | 0, dec_v-> | ||
| enc_frame-> | enc_frame-> | ||
| + | |||
| enc_frame-> | enc_frame-> | ||
| + | |||
| // --- encode + mux --- | // --- encode + mux --- | ||
| ret = encode_and_write(enc_v, | ret = encode_and_write(enc_v, | ||
| Zeile 598: | Zeile 603: | ||
| } | } | ||
| } | } | ||
| + | |||
| // Drain decoder | // Drain decoder | ||
| avcodec_send_packet(dec_v, | avcodec_send_packet(dec_v, | ||
| Zeile 605: | Zeile 610: | ||
| if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | ||
| if (ret < 0) break; | if (ret < 0) break; | ||
| + | |||
| int64_t in_pts = (dec_frame-> | int64_t in_pts = (dec_frame-> | ||
| ? dec_frame-> | ? dec_frame-> | ||
| - | : | + | : dec_frame-> |
| if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | ||
| + | |||
| int64_t enc_pts = av_rescale_q(in_pts, | int64_t enc_pts = av_rescale_q(in_pts, | ||
| + | |||
| av_frame_make_writable(enc_frame); | av_frame_make_writable(enc_frame); | ||
| sws_scale(sws, | sws_scale(sws, | ||
| Zeile 619: | Zeile 624: | ||
| enc_frame-> | enc_frame-> | ||
| enc_frame-> | enc_frame-> | ||
| + | |||
| ret = encode_and_write(enc_v, | ret = encode_and_write(enc_v, | ||
| av_frame_unref(dec_frame); | av_frame_unref(dec_frame); | ||
| if (ret < 0) break; | if (ret < 0) break; | ||
| } | } | ||
| + | |||
| // Flush encoder | // Flush encoder | ||
| ret = encode_and_write(enc_v, | ret = encode_and_write(enc_v, | ||
| if (ret < 0) std::cerr << "flush encoder: " << fferr(ret) << " | if (ret < 0) std::cerr << "flush encoder: " << fferr(ret) << " | ||
| + | |||
| av_write_trailer(ofmt); | av_write_trailer(ofmt); | ||
| + | |||
| // cleanup | // cleanup | ||
| sws_freeContext(sws); | sws_freeContext(sws); | ||
| avcodec_free_context(& | avcodec_free_context(& | ||
| avcodec_free_context(& | avcodec_free_context(& | ||
| + | |||
| av_frame_free(& | av_frame_free(& | ||
| av_frame_free(& | av_frame_free(& | ||
| av_packet_free(& | av_packet_free(& | ||
| av_packet_free(& | av_packet_free(& | ||
| + | |||
| avformat_close_input(& | avformat_close_input(& | ||
| if (ofmt && !(ofmt-> | if (ofmt && !(ofmt-> | ||
| avformat_free_context(ofmt); | avformat_free_context(ofmt); | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | < | ||
| + | g++ -std=c++17 -O2 main.cpp -o main.exe -IC: | ||
| + | |||
| + | # optional: -lbcrypt | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | main.exe test.mp4 test.mkv | ||
| + | </ | ||
| + | |||
| + | ===Video to Audio=== | ||
| + | |||
| + | <code c++> | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | extern " | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | } | ||
| + | |||
| + | static std::string fferr(int e) { | ||
| + | char buf[AV_ERROR_MAX_STRING_SIZE] = {0}; | ||
| + | av_strerror(e, | ||
| + | return buf; | ||
| + | } | ||
| + | |||
| + | static int encode_and_write_audio(AVCodecContext* enc, AVFrame* frame, AVFormatContext* ofmt, | ||
| + | AVStream* out_st, AVPacket* pkt) | ||
| + | { | ||
| + | int ret = avcodec_send_frame(enc, | ||
| + | if (ret < 0) return ret; | ||
| + | |||
| + | while (true) { | ||
| + | ret = avcodec_receive_packet(enc, | ||
| + | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return 0; | ||
| + | if (ret < 0) return ret; | ||
| + | |||
| + | av_packet_rescale_ts(pkt, | ||
| + | pkt-> | ||
| + | |||
| + | ret = av_interleaved_write_frame(ofmt, | ||
| + | av_packet_unref(pkt); | ||
| + | if (ret < 0) return ret; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int main(int argc, char** argv) { | ||
| + | if (argc < 3) { | ||
| + | std::cerr << " | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | const char* in_filename | ||
| + | const char* out_filename = argv[2]; | ||
| + | |||
| + | AVFormatContext* ifmt = nullptr; | ||
| + | AVFormatContext* ofmt = nullptr; | ||
| + | |||
| + | AVCodecContext* dec = nullptr; | ||
| + | AVCodecContext* enc = nullptr; | ||
| + | SwrContext* swr = nullptr; | ||
| + | AVAudioFifo* fifo = nullptr; | ||
| + | |||
| + | AVPacket* ipkt = av_packet_alloc(); | ||
| + | AVPacket* opkt = av_packet_alloc(); | ||
| + | AVFrame* | ||
| + | AVFrame* | ||
| + | |||
| + | if (!ipkt || !opkt || !frame || !out_frame) { | ||
| + | std::cerr << " | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | int ret = avformat_open_input(& | ||
| + | if (ret < 0) { std::cerr << "open input: " << fferr(ret) << " | ||
| + | |||
| + | ret = avformat_find_stream_info(ifmt, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | int a_si = av_find_best_stream(ifmt, | ||
| + | if (a_si < 0) { std::cerr << "No audio stream.\n"; | ||
| + | |||
| + | AVStream* in_st = ifmt-> | ||
| + | |||
| + | // --- decoder --- | ||
| + | const AVCodec* adec = avcodec_find_decoder(in_st-> | ||
| + | if (!adec) { std::cerr << "No audio decoder.\n"; | ||
| + | |||
| + | dec = avcodec_alloc_context3(adec); | ||
| + | if (!dec) { std::cerr << "alloc dec failed\n"; | ||
| + | |||
| + | ret = avcodec_parameters_to_context(dec, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | ret = avcodec_open2(dec, | ||
| + | if (ret < 0) { std::cerr << "open dec: " << fferr(ret) << " | ||
| + | |||
| + | // --- output context (.mp3) --- | ||
| + | ret = avformat_alloc_output_context2(& | ||
| + | if (ret < 0 || !ofmt) { std::cerr << "alloc output: " << fferr(ret) << " | ||
| + | |||
| + | AVStream* out_st = avformat_new_stream(ofmt, | ||
| + | if (!out_st) { std::cerr << "new stream failed\n"; | ||
| + | |||
| + | // --- encoder (prefer libmp3lame) --- | ||
| + | const AVCodec* aenc = avcodec_find_encoder_by_name(" | ||
| + | if (!aenc) aenc = avcodec_find_encoder(AV_CODEC_ID_MP3); | ||
| + | if (!aenc) { | ||
| + | std::cerr << "No MP3 encoder found. Your FFmpeg is missing libmp3lame encoder.\n"; | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | enc = avcodec_alloc_context3(aenc); | ||
| + | if (!enc) { std::cerr << "alloc enc failed\n"; | ||
| + | |||
| + | enc-> | ||
| + | enc-> | ||
| + | |||
| + | /* | ||
| + | // channel layout (old-style, works on many FFmpeg builds; may be deprecated) | ||
| + | uint64_t in_ch_layout = dec-> | ||
| + | if (!in_ch_layout) in_ch_layout = av_get_default_channel_layout(dec-> | ||
| + | |||
| + | enc-> | ||
| + | enc-> | ||
| + | */ | ||
| + | | ||
| + | av_channel_layout_default(& | ||
| + | if (dec-> | ||
| + | av_channel_layout_copy(& | ||
| + | } | ||
| + | |||
| + | // encoder layout = input layout | ||
| + | av_channel_layout_copy(& | ||
| + | |||
| + | // wenn du irgendwo " | ||
| + | int out_channels = enc-> | ||
| + | |||
| + | // pick a supported sample format | ||
| + | enc-> | ||
| + | ? aenc-> | ||
| + | : AV_SAMPLE_FMT_S16P; | ||
| + | |||
| + | enc-> | ||
| + | |||
| + | if (ofmt-> | ||
| + | enc-> | ||
| + | |||
| + | ret = avcodec_open2(enc, | ||
| + | if (ret < 0) { std::cerr << "open enc: " << fferr(ret) << " | ||
| + | |||
| + | ret = avcodec_parameters_from_context(out_st-> | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | out_st-> | ||
| + | |||
| + | /* | ||
| + | // --- swr (convert input audio to encoder format) --- | ||
| + | swr = swr_alloc_set_opts(nullptr, | ||
| + | | ||
| + | | ||
| + | 0, nullptr); | ||
| + | if (!swr) { std::cerr << " | ||
| + | */ | ||
| + | ret = swr_alloc_set_opts2(& | ||
| + | & | ||
| + | & | ||
| + | 0, nullptr); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | ret = swr_init(swr); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | //fifo = av_audio_fifo_alloc(enc-> | ||
| + | fifo = av_audio_fifo_alloc(enc-> | ||
| + | if (!fifo) { std::cerr << "audio fifo alloc failed\n"; | ||
| + | |||
| + | // --- write header --- | ||
| + | if (!(ofmt-> | ||
| + | ret = avio_open(& | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | } | ||
| + | |||
| + | ret = avformat_write_header(ofmt, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | int frame_size = (enc-> | ||
| + | int64_t samples_written = 0; | ||
| + | |||
| + | auto push_converted_to_fifo = [& | ||
| + | int out_nb = (int)av_rescale_rnd( | ||
| + | swr_get_delay(swr, | ||
| + | enc-> | ||
| + | |||
| + | uint8_t** conv = nullptr; | ||
| + | | ||
| + | //int ret2 = av_samples_alloc_array_and_samples(& | ||
| + | int ret2 = av_samples_alloc_array_and_samples(& | ||
| + | |||
| + | if (ret2 < 0) return ret2; | ||
| + | |||
| + | int conv_samp = swr_convert(swr, | ||
| + | | ||
| + | if (conv_samp < 0) { av_freep(& | ||
| + | |||
| + | ret2 = av_audio_fifo_realloc(fifo, | ||
| + | if (ret2 < 0) { av_freep(& | ||
| + | |||
| + | av_audio_fifo_write(fifo, | ||
| + | |||
| + | av_freep(& | ||
| + | av_freep(& | ||
| + | return 0; | ||
| + | }; | ||
| + | |||
| + | auto pop_fifo_encode = [&](int nb) -> int { | ||
| + | av_frame_unref(out_frame); | ||
| + | out_frame-> | ||
| + | out_frame-> | ||
| + | | ||
| + | // | ||
| + | av_channel_layout_uninit(& | ||
| + | av_channel_layout_copy(& | ||
| + | |||
| + | out_frame-> | ||
| + | |||
| + | int r2 = av_frame_get_buffer(out_frame, | ||
| + | if (r2 < 0) return r2; | ||
| + | |||
| + | r2 = av_frame_make_writable(out_frame); | ||
| + | if (r2 < 0) return r2; | ||
| + | |||
| + | int got = av_audio_fifo_read(fifo, | ||
| + | if (got != nb) return AVERROR(EIO); | ||
| + | |||
| + | out_frame-> | ||
| + | samples_written += nb; | ||
| + | |||
| + | return encode_and_write_audio(enc, | ||
| + | }; | ||
| + | |||
| + | // --- main loop --- | ||
| + | while ((ret = av_read_frame(ifmt, | ||
| + | if (ipkt-> | ||
| + | av_packet_unref(ipkt); | ||
| + | continue; | ||
| + | } | ||
| + | |||
| + | ret = avcodec_send_packet(dec, | ||
| + | av_packet_unref(ipkt); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | while (true) { | ||
| + | ret = avcodec_receive_frame(dec, | ||
| + | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | ret = push_converted_to_fifo(frame); | ||
| + | av_frame_unref(frame); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | while (av_audio_fifo_size(fifo) >= frame_size) { | ||
| + | ret = pop_fifo_encode(frame_size); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // flush decoder | ||
| + | avcodec_send_packet(dec, | ||
| + | while (true) { | ||
| + | ret = avcodec_receive_frame(dec, | ||
| + | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | ||
| + | if (ret < 0) break; | ||
| + | |||
| + | ret = push_converted_to_fifo(frame); | ||
| + | av_frame_unref(frame); | ||
| + | if (ret < 0) break; | ||
| + | } | ||
| + | |||
| + | // drain fifo (last partial) | ||
| + | while (av_audio_fifo_size(fifo) > 0) { | ||
| + | int nb = std:: | ||
| + | ret = pop_fifo_encode(nb); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | } | ||
| + | |||
| + | // flush encoder | ||
| + | ret = encode_and_write_audio(enc, | ||
| + | if (ret < 0) std::cerr << "flush enc: " << fferr(ret) << " | ||
| + | |||
| + | av_write_trailer(ofmt); | ||
| + | |||
| + | // cleanup | ||
| + | av_audio_fifo_free(fifo); | ||
| + | swr_free(& | ||
| + | avcodec_free_context(& | ||
| + | avcodec_free_context(& | ||
| + | |||
| + | av_frame_free(& | ||
| + | av_frame_free(& | ||
| + | av_packet_free(& | ||
| + | av_packet_free(& | ||
| + | |||
| + | avformat_close_input(& | ||
| + | if (ofmt && !(ofmt-> | ||
| + | avformat_free_context(ofmt); | ||
| + | |||
| return 0; | return 0; | ||
| } | } | ||
| Zeile 650: | Zeile 974: | ||
| < | < | ||
| - | g++ -std=c++17 -O2 transcode_min.cpp -o transcode_min.exe -IC:\ffmpeg32\include -LC:\ffmpeg32\lib -lavformat -lavcodec -lavutil -lswscale | + | g++ -std=c++17 -O2 main.cpp -o main.exe -IC:\chroot\ffmpeg\include -LC:\chroot\ffmpeg\lib -lavformat -lavcodec -lavutil -lswresample |
| + | |||
| + | # optional: | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | main.exe test.mp4 test.mp3 | ||
| </ | </ | ||
| =====Links===== | =====Links===== | ||