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:33] jango [Transcode] |
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 668: | Zeile 670: | ||
| #include < | #include < | ||
| #include < | #include < | ||
| + | |||
| extern " | extern " | ||
| #include < | #include < | ||
| Zeile 679: | Zeile 681: | ||
| #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 685: | Zeile 687: | ||
| return buf; | return buf; | ||
| } | } | ||
| + | |||
| static int encode_and_write_audio(AVCodecContext* enc, AVFrame* frame, AVFormatContext* ofmt, | static int encode_and_write_audio(AVCodecContext* enc, AVFrame* frame, AVFormatContext* ofmt, | ||
| AVStream* out_st, AVPacket* pkt) | AVStream* out_st, AVPacket* pkt) | ||
| Zeile 691: | Zeile 693: | ||
| 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; | ||
| + | |||
| 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 705: | Zeile 707: | ||
| } | } | ||
| } | } | ||
| + | |||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||
| if (argc < 3) { | if (argc < 3) { | ||
| Zeile 711: | Zeile 713: | ||
| 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 = nullptr; | AVCodecContext* dec = nullptr; | ||
| AVCodecContext* enc = nullptr; | AVCodecContext* enc = nullptr; | ||
| SwrContext* swr = nullptr; | SwrContext* swr = nullptr; | ||
| AVAudioFifo* fifo = nullptr; | AVAudioFifo* fifo = nullptr; | ||
| + | |||
| AVPacket* ipkt = av_packet_alloc(); | AVPacket* ipkt = av_packet_alloc(); | ||
| AVPacket* opkt = av_packet_alloc(); | AVPacket* opkt = av_packet_alloc(); | ||
| AVFrame* | AVFrame* | ||
| AVFrame* | AVFrame* | ||
| + | |||
| if (!ipkt || !opkt || !frame || !out_frame) { | if (!ipkt || !opkt || !frame || !out_frame) { | ||
| std::cerr << " | std::cerr << " | ||
| return 1; | return 1; | ||
| } | } | ||
| + | |||
| int ret = avformat_open_input(& | int 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 << " | ||
| + | |||
| int a_si = av_find_best_stream(ifmt, | int a_si = av_find_best_stream(ifmt, | ||
| if (a_si < 0) { std::cerr << "No audio stream.\n"; | if (a_si < 0) { std::cerr << "No audio stream.\n"; | ||
| + | |||
| AVStream* in_st = ifmt-> | AVStream* in_st = ifmt-> | ||
| + | |||
| // --- decoder --- | // --- decoder --- | ||
| const AVCodec* adec = avcodec_find_decoder(in_st-> | const AVCodec* adec = avcodec_find_decoder(in_st-> | ||
| if (!adec) { std::cerr << "No audio decoder.\n"; | if (!adec) { std::cerr << "No audio decoder.\n"; | ||
| + | |||
| dec = avcodec_alloc_context3(adec); | dec = avcodec_alloc_context3(adec); | ||
| if (!dec) { std::cerr << "alloc dec failed\n"; | if (!dec) { std::cerr << "alloc dec failed\n"; | ||
| + | |||
| ret = avcodec_parameters_to_context(dec, | ret = avcodec_parameters_to_context(dec, | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| ret = avcodec_open2(dec, | ret = avcodec_open2(dec, | ||
| if (ret < 0) { std::cerr << "open dec: " << fferr(ret) << " | if (ret < 0) { std::cerr << "open dec: " << fferr(ret) << " | ||
| + | |||
| // --- output context (.mp3) --- | // --- output context (.mp3) --- | ||
| 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) << " | ||
| + | |||
| 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"; | ||
| + | |||
| // --- encoder (prefer libmp3lame) --- | // --- encoder (prefer libmp3lame) --- | ||
| const AVCodec* aenc = avcodec_find_encoder_by_name(" | const AVCodec* aenc = avcodec_find_encoder_by_name(" | ||
| Zeile 771: | Zeile 773: | ||
| return 1; | return 1; | ||
| } | } | ||
| + | |||
| enc = avcodec_alloc_context3(aenc); | enc = avcodec_alloc_context3(aenc); | ||
| if (!enc) { std::cerr << "alloc enc failed\n"; | if (!enc) { std::cerr << "alloc enc failed\n"; | ||
| + | |||
| enc-> | enc-> | ||
| enc-> | enc-> | ||
| + | |||
| + | /* | ||
| // channel layout (old-style, works on many FFmpeg builds; may be deprecated) | // channel layout (old-style, works on many FFmpeg builds; may be deprecated) | ||
| uint64_t in_ch_layout = dec-> | uint64_t in_ch_layout = dec-> | ||
| if (!in_ch_layout) in_ch_layout = av_get_default_channel_layout(dec-> | if (!in_ch_layout) in_ch_layout = av_get_default_channel_layout(dec-> | ||
| + | |||
| enc-> | enc-> | ||
| 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 | // pick a supported sample format | ||
| enc-> | enc-> | ||
| ? aenc-> | ? aenc-> | ||
| : AV_SAMPLE_FMT_S16P; | : AV_SAMPLE_FMT_S16P; | ||
| + | |||
| enc-> | enc-> | ||
| + | |||
| if (ofmt-> | if (ofmt-> | ||
| enc-> | enc-> | ||
| + | |||
| ret = avcodec_open2(enc, | ret = avcodec_open2(enc, | ||
| if (ret < 0) { std::cerr << "open enc: " << fferr(ret) << " | if (ret < 0) { std::cerr << "open enc: " << fferr(ret) << " | ||
| + | |||
| ret = avcodec_parameters_from_context(out_st-> | ret = avcodec_parameters_from_context(out_st-> | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| out_st-> | out_st-> | ||
| + | |||
| + | /* | ||
| // --- swr (convert input audio to encoder format) --- | // --- swr (convert input audio to encoder format) --- | ||
| swr = swr_alloc_set_opts(nullptr, | swr = swr_alloc_set_opts(nullptr, | ||
| Zeile 808: | Zeile 824: | ||
| 0, nullptr); | 0, nullptr); | ||
| if (!swr) { std::cerr << " | if (!swr) { std::cerr << " | ||
| + | */ | ||
| + | ret = swr_alloc_set_opts2(& | ||
| + | & | ||
| + | & | ||
| + | 0, nullptr); | ||
| + | if (ret < 0) { std::cerr << " | ||
| + | |||
| ret = swr_init(swr); | ret = swr_init(swr); | ||
| if (ret < 0) { std::cerr << " | 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"; | if (!fifo) { std::cerr << "audio fifo alloc failed\n"; | ||
| + | |||
| // --- write header --- | // --- write header --- | ||
| if (!(ofmt-> | if (!(ofmt-> | ||
| Zeile 820: | Zeile 843: | ||
| 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 << " | ||
| + | |||
| int frame_size = (enc-> | int frame_size = (enc-> | ||
| int64_t samples_written = 0; | int64_t samples_written = 0; | ||
| + | |||
| auto push_converted_to_fifo = [& | auto push_converted_to_fifo = [& | ||
| int out_nb = (int)av_rescale_rnd( | int out_nb = (int)av_rescale_rnd( | ||
| swr_get_delay(swr, | swr_get_delay(swr, | ||
| enc-> | enc-> | ||
| + | |||
| uint8_t** conv = nullptr; | uint8_t** conv = nullptr; | ||
| - | int ret2 = av_samples_alloc_array_and_samples(& | + | |
| + | //int ret2 = av_samples_alloc_array_and_samples(& | ||
| + | int ret2 = av_samples_alloc_array_and_samples(& | ||
| + | |||
| if (ret2 < 0) return ret2; | if (ret2 < 0) return ret2; | ||
| + | |||
| int conv_samp = swr_convert(swr, | int conv_samp = swr_convert(swr, | ||
| | | ||
| if (conv_samp < 0) { av_freep(& | if (conv_samp < 0) { av_freep(& | ||
| + | |||
| ret2 = av_audio_fifo_realloc(fifo, | ret2 = av_audio_fifo_realloc(fifo, | ||
| if (ret2 < 0) { av_freep(& | if (ret2 < 0) { av_freep(& | ||
| + | |||
| av_audio_fifo_write(fifo, | av_audio_fifo_write(fifo, | ||
| + | |||
| av_freep(& | av_freep(& | ||
| av_freep(& | av_freep(& | ||
| return 0; | return 0; | ||
| }; | }; | ||
| + | |||
| auto pop_fifo_encode = [&](int nb) -> int { | auto pop_fifo_encode = [&](int nb) -> int { | ||
| av_frame_unref(out_frame); | av_frame_unref(out_frame); | ||
| out_frame-> | out_frame-> | ||
| out_frame-> | out_frame-> | ||
| - | out_frame-> | + | |
| - | out_frame-> | + | //out_frame-> |
| + | | ||
| + | av_channel_layout_copy(& | ||
| + | |||
| + | out_frame-> | ||
| + | |||
| int r2 = av_frame_get_buffer(out_frame, | int r2 = av_frame_get_buffer(out_frame, | ||
| if (r2 < 0) return r2; | if (r2 < 0) return r2; | ||
| + | |||
| r2 = av_frame_make_writable(out_frame); | r2 = av_frame_make_writable(out_frame); | ||
| if (r2 < 0) return r2; | if (r2 < 0) return r2; | ||
| + | |||
| int got = av_audio_fifo_read(fifo, | int got = av_audio_fifo_read(fifo, | ||
| if (got != nb) return AVERROR(EIO); | if (got != nb) return AVERROR(EIO); | ||
| + | |||
| out_frame-> | out_frame-> | ||
| samples_written += nb; | samples_written += nb; | ||
| + | |||
| return encode_and_write_audio(enc, | return encode_and_write_audio(enc, | ||
| }; | }; | ||
| + | |||
| // --- main loop --- | // --- main loop --- | ||
| while ((ret = av_read_frame(ifmt, | while ((ret = av_read_frame(ifmt, | ||
| Zeile 878: | Zeile 908: | ||
| continue; | continue; | ||
| } | } | ||
| + | |||
| ret = avcodec_send_packet(dec, | ret = avcodec_send_packet(dec, | ||
| av_packet_unref(ipkt); | av_packet_unref(ipkt); | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| while (true) { | while (true) { | ||
| ret = avcodec_receive_frame(dec, | ret = avcodec_receive_frame(dec, | ||
| 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 << " | ||
| + | |||
| ret = push_converted_to_fifo(frame); | ret = push_converted_to_fifo(frame); | ||
| av_frame_unref(frame); | av_frame_unref(frame); | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| + | |||
| while (av_audio_fifo_size(fifo) >= frame_size) { | while (av_audio_fifo_size(fifo) >= frame_size) { | ||
| ret = pop_fifo_encode(frame_size); | ret = pop_fifo_encode(frame_size); | ||
| Zeile 898: | Zeile 928: | ||
| } | } | ||
| } | } | ||
| + | |||
| // flush decoder | // flush decoder | ||
| avcodec_send_packet(dec, | avcodec_send_packet(dec, | ||
| Zeile 905: | Zeile 935: | ||
| if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; | ||
| if (ret < 0) break; | if (ret < 0) break; | ||
| + | |||
| ret = push_converted_to_fifo(frame); | ret = push_converted_to_fifo(frame); | ||
| av_frame_unref(frame); | av_frame_unref(frame); | ||
| if (ret < 0) break; | if (ret < 0) break; | ||
| } | } | ||
| + | |||
| // drain fifo (last partial) | // drain fifo (last partial) | ||
| while (av_audio_fifo_size(fifo) > 0) { | while (av_audio_fifo_size(fifo) > 0) { | ||
| Zeile 917: | Zeile 947: | ||
| if (ret < 0) { std::cerr << " | if (ret < 0) { std::cerr << " | ||
| } | } | ||
| + | |||
| // flush encoder | // flush encoder | ||
| ret = encode_and_write_audio(enc, | ret = encode_and_write_audio(enc, | ||
| if (ret < 0) std::cerr << "flush enc: " << fferr(ret) << " | if (ret < 0) std::cerr << "flush enc: " << fferr(ret) << " | ||
| + | |||
| av_write_trailer(ofmt); | av_write_trailer(ofmt); | ||
| + | |||
| // cleanup | // cleanup | ||
| av_audio_fifo_free(fifo); | av_audio_fifo_free(fifo); | ||
| Zeile 929: | Zeile 959: | ||
| 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; | return 0; | ||
| } | } | ||
| Zeile 944: | Zeile 974: | ||
| < | < | ||
| - | g++ -std=c++17 -O2 mp4_to_mp3.cpp -o mp4_to_mp3.exe -IC: | + | g++ -std=c++17 -O2 main.cpp -o main.exe -IC: |
| + | |||
| + | # optional: | ||
| </ | </ | ||