Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
ffmpeg [2025/12/10 15:58] jango [Build] |
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 236: | Zeile 238: | ||
| ====Remux==== | ====Remux==== | ||
| - | < | + | < |
| #include < | #include < | ||
| #include < | #include < | ||
| Zeile 346: | Zeile 348: | ||
| return ret < 0 ? 1 : 0; | return ret < 0 ? 1 : 0; | ||
| } | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ====Transcode==== | ||
| + | |||
| + | ===Video to Video=== | ||
| + | |||
| + | <code c++> | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | extern " | ||
| + | #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(AVCodecContext* enc, | ||
| + | AVFrame* frame, | ||
| + | AVFormatContext* ofmt, | ||
| + | AVStream* out_stream, | ||
| + | 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; | ||
| + | |||
| + | // Packet timestamps to muxer time_base | ||
| + | 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_v = nullptr; | ||
| + | AVCodecContext* enc_v = nullptr; | ||
| + | SwsContext* sws = nullptr; | ||
| + | |||
| + | AVFrame* dec_frame = av_frame_alloc(); | ||
| + | AVFrame* enc_frame = av_frame_alloc(); | ||
| + | AVPacket* in_pkt | ||
| + | AVPacket* out_pkt | ||
| + | |||
| + | if (!dec_frame || !enc_frame || !in_pkt || !out_pkt) { | ||
| + | std::cerr << " | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | int ret = 0; | ||
| + | |||
| + | // --- open input --- | ||
| + | ret = avformat_open_input(& | ||
| + | if (ret < 0) { std::cerr << "open input: " << fferr(ret) << " | ||
| + | |||
| + | ret = avformat_find_stream_info(ifmt, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | // find best video stream | ||
| + | int v_si = av_find_best_stream(ifmt, | ||
| + | if (v_si < 0) { std::cerr << "No video stream found.\n"; | ||
| + | |||
| + | AVStream* in_vst = ifmt-> | ||
| + | |||
| + | // --- open video decoder --- | ||
| + | const AVCodec* vdec = avcodec_find_decoder(in_vst-> | ||
| + | if (!vdec) { std::cerr << "No decoder.\n"; | ||
| + | |||
| + | dec_v = avcodec_alloc_context3(vdec); | ||
| + | if (!dec_v) { std::cerr << "alloc dec ctx failed\n"; | ||
| + | |||
| + | ret = avcodec_parameters_to_context(dec_v, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | ret = avcodec_open2(dec_v, | ||
| + | if (ret < 0) { std::cerr << "open decoder: " << fferr(ret) << " | ||
| + | |||
| + | // --- open output --- | ||
| + | ret = avformat_alloc_output_context2(& | ||
| + | if (ret < 0 || !ofmt) { std::cerr << "alloc output: " << fferr(ret) << " | ||
| + | |||
| + | std:: | ||
| + | |||
| + | // Create output streams for non-video (copy) | ||
| + | for (unsigned i = 0; i < ifmt-> | ||
| + | if ((int)i == v_si) continue; | ||
| + | |||
| + | AVStream* in_st = ifmt-> | ||
| + | if (in_st-> | ||
| + | in_st-> | ||
| + | continue; | ||
| + | } | ||
| + | |||
| + | AVStream* out_st = avformat_new_stream(ofmt, | ||
| + | if (!out_st) { std::cerr << "new stream failed\n"; | ||
| + | |||
| + | ret = avcodec_parameters_copy(out_st-> | ||
| + | if (ret < 0) { std::cerr << "par copy: " << fferr(ret) << " | ||
| + | |||
| + | out_st-> | ||
| + | out_st-> | ||
| + | map[i] = out_st-> | ||
| + | } | ||
| + | |||
| + | // --- create output video stream (H.264 encode) --- | ||
| + | AVStream* out_vst = avformat_new_stream(ofmt, | ||
| + | if (!out_vst) { std::cerr << "new video stream failed\n"; | ||
| + | map[v_si] = out_vst-> | ||
| + | |||
| + | // prefer libx264 if available, fallback to any H.264 encoder | ||
| + | const AVCodec* venc = avcodec_find_encoder_by_name(" | ||
| + | if (!venc) venc = avcodec_find_encoder(AV_CODEC_ID_H264); | ||
| + | if (!venc) { std::cerr << "No H.264 encoder found.\n"; | ||
| + | |||
| + | enc_v = avcodec_alloc_context3(venc); | ||
| + | if (!enc_v) { std::cerr << "alloc enc ctx failed\n"; | ||
| + | |||
| + | enc_v-> | ||
| + | enc_v-> | ||
| + | enc_v-> | ||
| + | |||
| + | // MP4 wants yuv420p typically | ||
| + | enc_v-> | ||
| + | |||
| + | AVRational fps = av_guess_frame_rate(ifmt, | ||
| + | if (fps.num <= 0 || fps.den <= 0) fps = AVRational{25, | ||
| + | enc_v-> | ||
| + | enc_v-> | ||
| + | |||
| + | enc_v-> | ||
| + | |||
| + | if (ofmt-> | ||
| + | enc_v-> | ||
| + | |||
| + | // if libx264: set some nice defaults (ignore errors if unsupported) | ||
| + | av_opt_set(enc_v-> | ||
| + | av_opt_set(enc_v-> | ||
| + | |||
| + | ret = avcodec_open2(enc_v, | ||
| + | if (ret < 0) { std::cerr << "open encoder: " << fferr(ret) << " | ||
| + | |||
| + | ret = avcodec_parameters_from_context(out_vst-> | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | out_vst-> | ||
| + | |||
| + | // --- open output IO + header --- | ||
| + | if (!(ofmt-> | ||
| + | ret = avio_open(& | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | } | ||
| + | |||
| + | ret = avformat_write_header(ofmt, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | // prepare scaling/ | ||
| + | enc_frame-> | ||
| + | enc_frame-> | ||
| + | enc_frame-> | ||
| + | |||
| + | ret = av_frame_get_buffer(enc_frame, | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | int64_t fallback_pts = 0; | ||
| + | |||
| + | // --- main loop --- | ||
| + | while ((ret = av_read_frame(ifmt, | ||
| + | int si = in_pkt-> | ||
| + | |||
| + | if (si != v_si) { | ||
| + | // remux non-video streams (copy) | ||
| + | if (si >= 0 && si < (int)map.size() && map[si] >= 0) { | ||
| + | AVStream* in_st = ifmt-> | ||
| + | AVStream* out_st = ofmt-> | ||
| + | |||
| + | in_pkt-> | ||
| + | av_packet_rescale_ts(in_pkt, | ||
| + | in_pkt-> | ||
| + | |||
| + | int wret = av_interleaved_write_frame(ofmt, | ||
| + | if (wret < 0) std::cerr << "write copy pkt: " << fferr(wret) << " | ||
| + | } | ||
| + | av_packet_unref(in_pkt); | ||
| + | continue; | ||
| + | } | ||
| + | |||
| + | // --- decode video packet --- | ||
| + | ret = avcodec_send_packet(dec_v, | ||
| + | av_packet_unref(in_pkt); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | while (true) { | ||
| + | ret = avcodec_receive_frame(dec_v, | ||
| + | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| + | // pts handling (best-effort) | ||
| + | int64_t in_pts = (dec_frame-> | ||
| + | ? dec_frame-> | ||
| + | : dec_frame-> | ||
| + | if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | ||
| + | |||
| + | int64_t enc_pts = av_rescale_q(in_pts, | ||
| + | |||
| + | // convert to encoder format if needed | ||
| + | if (!sws) { | ||
| + | sws = sws_getContext(dec_v-> | ||
| + | | ||
| + | | ||
| + | if (!sws) { std::cerr << " | ||
| + | } | ||
| + | |||
| + | ret = av_frame_make_writable(enc_frame); | ||
| + | if (ret < 0) { std::cerr << "frame not writable: " << fferr(ret) << " | ||
| + | |||
| + | sws_scale(sws, | ||
| + | dec_frame-> | ||
| + | 0, dec_v-> | ||
| + | enc_frame-> | ||
| + | |||
| + | enc_frame-> | ||
| + | |||
| + | // --- encode + mux --- | ||
| + | ret = encode_and_write(enc_v, | ||
| + | av_frame_unref(dec_frame); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Drain decoder | ||
| + | avcodec_send_packet(dec_v, | ||
| + | while (true) { | ||
| + | ret = avcodec_receive_frame(dec_v, | ||
| + | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | ||
| + | if (ret < 0) break; | ||
| + | |||
| + | int64_t in_pts = (dec_frame-> | ||
| + | ? dec_frame-> | ||
| + | : dec_frame-> | ||
| + | if (in_pts == AV_NOPTS_VALUE) in_pts = fallback_pts++; | ||
| + | |||
| + | int64_t enc_pts = av_rescale_q(in_pts, | ||
| + | |||
| + | av_frame_make_writable(enc_frame); | ||
| + | sws_scale(sws, | ||
| + | dec_frame-> | ||
| + | 0, dec_v-> | ||
| + | enc_frame-> | ||
| + | enc_frame-> | ||
| + | |||
| + | ret = encode_and_write(enc_v, | ||
| + | av_frame_unref(dec_frame); | ||
| + | if (ret < 0) break; | ||
| + | } | ||
| + | |||
| + | // Flush encoder | ||
| + | ret = encode_and_write(enc_v, | ||
| + | if (ret < 0) std::cerr << "flush encoder: " << fferr(ret) << " | ||
| + | |||
| + | av_write_trailer(ofmt); | ||
| + | |||
| + | // cleanup | ||
| + | sws_freeContext(sws); | ||
| + | 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; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | 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; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | g++ -std=c++17 -O2 main.cpp -o main.exe -IC: | ||
| + | |||
| + | # optional: -lbcrypt | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | main.exe test.mp4 test.mp3 | ||
| </ | </ | ||
| =====Links===== | =====Links===== | ||