]> git.sesse.net Git - nageru/blobdiff - ffmpeg_capture.h
Support audio-only FFmpeg inputs. Somewhat wonky, though.
[nageru] / ffmpeg_capture.h
index afca641e951e20031bf37bf2f9ed2d0cad9413a6..8a513df971fa20f2cba71e25ac603242b9be3be3 100644 (file)
@@ -16,7 +16,7 @@
 // but it would require some more plumbing, and it would also fail if the file
 // changes parameters midway, which is allowed in some formats.
 //
-// There is currently no audio support.
+// You can get out the audio either as decoded or in raw form (Kaeru uses this).
 
 #include <assert.h>
 #include <stdint.h>
 #include <movit/ycbcr.h>
 
 extern "C" {
+#include <libavresample/avresample.h>
 #include <libavutil/pixfmt.h>
 #include <libavutil/rational.h>
+#include <libavutil/samplefmt.h>
 }
 
 #include "bmusb/bmusb.h"
 #include "ffmpeg_raii.h"
+#include "ref_counted_frame.h"
 #include "quittable_sleeper.h"
 
 struct AVFormatContext;
@@ -74,6 +77,25 @@ public:
                producer_thread_should_quit.wakeup();
        }
 
+       std::string get_filename() const
+       {
+               std::lock_guard<std::mutex> lock(filename_mu);
+               return filename;
+       }
+
+       void change_filename(const std::string &new_filename)
+       {
+               std::lock_guard<std::mutex> lock(filename_mu);
+               filename = new_filename;
+               should_interrupt = true;
+       }
+
+       // Will stop the stream even if it's hung on blocking I/O.
+       void disconnect()
+       {
+               should_interrupt = true;
+       }
+
        // CaptureInterface.
        void set_video_frame_allocator(bmusb::FrameAllocator *allocator) override
        {
@@ -104,7 +126,8 @@ public:
 
        // FFmpegCapture-specific overload of set_frame_callback that also gives
        // the raw original pts from the video. Negative pts means a dummy frame.
-       typedef std::function<void(int64_t pts, AVRational timebase, uint16_t timecode,
+       typedef std::function<void(int64_t video_pts, AVRational video_timebase, int64_t audio_pts, AVRational audio_timebase,
+                                  uint16_t timecode,
                                   bmusb::FrameAllocator::Frame video_frame, size_t video_offset, bmusb::VideoFormat video_format,
                                   bmusb::FrameAllocator::Frame audio_frame, size_t audio_offset, bmusb::AudioFormat audio_format)>
                frame_callback_t;
@@ -117,13 +140,13 @@ public:
        {
                frame_callback = std::bind(
                        callback,
-                       std::placeholders::_3,
-                       std::placeholders::_4,
                        std::placeholders::_5,
                        std::placeholders::_6,
                        std::placeholders::_7,
                        std::placeholders::_8,
-                       std::placeholders::_9);
+                       std::placeholders::_9,
+                       std::placeholders::_10,
+                       std::placeholders::_11);
        }
 
        // FFmpegCapture-specific callback that gives the raw audio.
@@ -194,11 +217,18 @@ private:
        bool process_queued_commands(AVFormatContext *format_ctx, const std::string &pathname, timespec last_modified, bool *rewound);
 
        // Returns nullptr if no frame was decoded (e.g. EOF).
-       AVFrameWithDeleter decode_frame(AVFormatContext *format_ctx, AVCodecContext *codec_ctx, const std::string &pathname, int video_stream_index, int audio_stream_index, bool *error);
+       AVFrameWithDeleter decode_frame(AVFormatContext *format_ctx, AVCodecContext *video_codec_ctx, AVCodecContext *audio_codec_ctx,
+                                       const std::string &pathname, int video_stream_index, int audio_stream_index,
+                                       bmusb::FrameAllocator::Frame *audio_frame, bmusb::AudioFormat *audio_format, int64_t *audio_pts, bool *error);
+       void convert_audio(const AVFrame *audio_avframe, bmusb::FrameAllocator::Frame *audio_frame, bmusb::AudioFormat *audio_format);
 
        bmusb::VideoFormat construct_video_format(const AVFrame *frame, AVRational video_timebase);
-       bmusb::FrameAllocator::Frame make_video_frame(const AVFrame *frame, const std::string &pathname, bool *error);
+       UniqueFrame make_video_frame(const AVFrame *frame, const std::string &pathname, bool *error);
 
+       static int interrupt_cb_thunk(void *unique);
+       int interrupt_cb();
+
+       mutable std::mutex filename_mu;
        std::string description, filename;
        uint16_t timecode = 0;
        unsigned width, height;
@@ -207,6 +237,8 @@ private:
        bool running = false;
        int card_index = -1;
        double rate = 1.0;
+       std::atomic<bool> should_interrupt{false};
+       bool last_frame_was_connected = true;
 
        bool has_dequeue_callbacks = false;
        std::function<void()> dequeue_init_callback = nullptr;
@@ -222,13 +254,13 @@ private:
        SwsContextWithDeleter sws_ctx;
        int sws_last_width = -1, sws_last_height = -1, sws_last_src_format = -1;
        AVPixelFormat sws_dst_format = AVPixelFormat(-1);  // In practice, always initialized.
-       AVRational video_timebase;
+       AVRational video_timebase, audio_timebase;
 
        QuittableSleeper producer_thread_should_quit;
        std::thread producer_thread;
 
        int64_t pts_origin, last_pts;
-       std::chrono::steady_clock::time_point start, next_frame_start;
+       std::chrono::steady_clock::time_point start, next_frame_start, last_frame;
 
        std::mutex queue_mu;
        struct QueuedCommand {
@@ -236,6 +268,13 @@ private:
                double new_rate;  // For CHANGE_RATE.
        };
        std::vector<QueuedCommand> command_queue;  // Protected by <queue_mu>.
+
+       // Audio resampler.
+       AVAudioResampleContext *resampler = nullptr;
+       AVSampleFormat last_src_format, last_dst_format;
+       int64_t last_channel_layout;
+       int last_sample_rate;
+
 };
 
 #endif  // !defined(_FFMPEG_CAPTURE_H)