diff --git a/app/mosh-me.cpp b/app/mosh-me.cpp index d8551fb..45cc3b6 100644 --- a/app/mosh-me.cpp +++ b/app/mosh-me.cpp @@ -1,3 +1,152 @@ +extern "C" { +#include +#include +} + #include -int main() { spdlog::info("Hello!!"); } +static void log_err(int err) { + char buf[256]; + av_strerror(err, buf, sizeof(buf)); + spdlog::error("FFMPEG error: {}", buf); +} + +static void smearStreams(const std::vector &stream_list, + AVFormatContext *input_format, + AVFormatContext *output_format) { + + bool remove_iframes = false; + + AVPacket prev; + for (;;) { + AVPacket packet; + if (auto ret = av_read_frame(input_format, &packet); ret < 0) { + break; + } + + AVStream *istream = input_format->streams[packet.stream_index]; + if (packet.stream_index >= input_format->nb_streams || + stream_list[packet.stream_index] < 0 || + (istream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + packet.flags & AV_PKT_FLAG_KEY && remove_iframes)) { + av_packet_unref(&packet); + continue; + } + + // Map to output stream index + packet.stream_index = stream_list[packet.stream_index]; + AVStream *ostream = output_format->streams[packet.stream_index]; + + if (packet.pts > 0 && packet.pts % 25 < 10 && packet.pts % 2 == 0) { + prev.pts = packet.pts; + prev.dts = packet.dts; + prev.duration = packet.duration; + packet = prev; + } + + // Adjust time data for output timebase + packet.pts = + av_rescale_q_rnd(packet.pts, istream->time_base, ostream->time_base, + AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); + packet.dts = + av_rescale_q_rnd(packet.dts, istream->time_base, ostream->time_base, + AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); + packet.duration = + av_rescale_q(packet.duration, istream->time_base, ostream->time_base); + packet.pos = -1; // unknown position + + if (auto ret = av_interleaved_write_frame(output_format, &packet); + ret < 0) { + spdlog::error("Could not mux packet"); + log_err(ret); + } + + prev = packet; + + av_packet_unref(&packet); + } + av_packet_unref(&prev); +} + +static std::vector +prepareOutputStreams(const AVFormatContext *input_format, + AVFormatContext *output_format) { + + std::vector streams_list(input_format->nb_streams); + off_t stream_ix = 0; + + for (off_t i = 0; i < input_format->nb_streams; i++) { + AVStream *istream = input_format->streams[i]; + AVCodecParameters *icpar = istream->codecpar; + if (icpar->codec_type != AVMEDIA_TYPE_AUDIO && + icpar->codec_type != AVMEDIA_TYPE_VIDEO && + icpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + streams_list[i] = -1; + continue; + } + + streams_list[i] = stream_ix++; + AVStream *ostream = avformat_new_stream(output_format, nullptr); + if (auto ret = avcodec_parameters_copy(ostream->codecpar, icpar); ret < 0) { + spdlog::error("Could not prepare output stream"); + log_err(ret); + } + } + + return streams_list; +} + +int main(int argc, const char *argv[]) { + if (argc < 3) { + spdlog::error("Please invoke with {} ", argv[0]); + return 1; + } + spdlog::info("Opening File {}", argv[1]); + + AVFormatContext *input_format = nullptr; + AVFormatContext *output_format = nullptr; + if (auto ret = avformat_open_input(&input_format, argv[1], nullptr, nullptr); + ret < 0) { + log_err(ret); + return 1; + } + + if (auto ret = avformat_find_stream_info(input_format, nullptr); ret < 0) { + log_err(ret); + } + + spdlog::info("Format: {}, frames: {}, bitrate: {}", + input_format->iformat->name, input_format->duration, + input_format->bit_rate); + + if (auto ret = avformat_alloc_output_context2(&output_format, nullptr, + nullptr, argv[2]); + ret < 0) { + log_err(ret); + } + + auto streams = prepareOutputStreams(input_format, output_format); + + if (!(output_format->oformat->flags & AVFMT_NOFILE)) { + if (auto ret = avio_open(&output_format->pb, argv[2], AVIO_FLAG_WRITE); + ret < 0) { + spdlog::error("Could not open output file {}", argv[2]); + log_err(ret); + } + } + + if (auto ret = avformat_write_header(output_format, nullptr); ret < 0) { + spdlog::error("Could not write header"); + log_err(ret); + } + + smearStreams(streams, input_format, output_format); + + av_write_trailer(output_format); + + avformat_close_input(&input_format); + if (output_format && !(output_format->oformat->flags & AVFMT_NOFILE)) { + avio_closep(&output_format->pb); + } + avformat_free_context(output_format); +}