| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_MEDIA_LIB_RAW_VIDEO_WRITER_RAW_VIDEO_WRITER_H_ |
| #define SRC_MEDIA_LIB_RAW_VIDEO_WRITER_RAW_VIDEO_WRITER_H_ |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <fbl/unique_fd.h> |
| |
| #include "src/lib/fxl/macros.h" |
| |
| namespace media { |
| |
| // This class enables a client to easily create and write raw uncompressed video |
| // frames to a file. The frames can be played back using a tool like this: |
| // mplayer -demuxer rawvideo -rawvideo w=320:h=240:format=nv12 |
| // |
| // The file itself does not have any format metadata associated, so is only |
| // playable with a command such as the above if the format + size is known or |
| // guessed OOB. Limited ability to write uint32_t to the file is provided but |
| // use of these is discouraged if all the frames are the same format. |
| // |
| // The source data is allowed to provide the data in format-specific ways when |
| // it comes to things like stride, offset to UV plane(s), etc. Each format has |
| // a separate Write_.*() method to allow this class to pack the pixel data into |
| // the file without gaps. |
| // |
| // Callers will want to write all the same frame size and same format, but this |
| // is not enforced currently. Playing back a file with a mix of frame sizes and |
| // formats isn't a thing. |
| // |
| // After creating the RawVideoWriter object, Initialize() must be called exactly |
| // once before invoking other methods. If nullptr or "" is passed to Initialize |
| // (instead of a valid file name), a default file path+name of |
| // '/tmp/raw_video_writer_N.wav' is used, where N is an integer corresponding to |
| // the instance of RawVideoWriter running in that process. |
| // |
| // Following Initialize, an appropriate Write method is used to append a video |
| // frame to the file. Once the client has completely written the file, the |
| // client should call Close to close the file. |
| // |
| // An instance of the class is single-use. |
| // |
| // An instance is thread compatible but not thread safe. The client bears all |
| // responsibilities for synchronization. Call from only one thread at a time, |
| // with adequate happens-before between calls. The global "N" allocator is |
| // thread-safe. |
| // |
| // Appending frames is only supported in one format per object instance. |
| template <bool enabled = true> |
| class RawVideoWriter { |
| public: |
| // Default of nullptr uses /tmp/raw_video_writer_N.wav, where N is unique only |
| // per instance in this process. |
| // |
| // A file is opened lazily by the first write call, to avoid creating an empty |
| // file if no writes occur. |
| explicit RawVideoWriter(const char* file_name = nullptr); |
| |
| // Will call Close() |
| ~RawVideoWriter(); |
| |
| // IsOk() |
| // |
| // Is the file still ok after the writes so far? |
| // |
| // Calling this is entirely optional. The failure model here is stateful in |
| // the sense that we don't try to write more to the file after a previous |
| // open/write to the file fails. |
| bool IsOk(); |
| |
| // WriteUint32BigEndian() / WriteUint32LittleEndian() |
| // |
| // Escape hatch for writing ad-hoc file header / frame header info. Currently |
| // this is mostly here to establish a pattern of caring about endian-ness for |
| // any such escape hatches, just to force consideration of which endian-ness |
| // we actually intend to have in the file (not to have an opinion on which |
| // choice is best). |
| // |
| // The return value is 4, always. A return of 4 does not imply that the write |
| // succeeded - for that see IsOk(). |
| // |
| // BigEndian - Write in big-endian to the file regardless of where this code |
| // is running. |
| // |
| // LittleEndian - Write in little-endian to the file regardless of where this |
| // code is running. |
| size_t WriteUint32BigEndian(uint32_t number); |
| size_t WriteUint32LittleEndian(uint32_t number); |
| |
| // Write an NV12 format frame. |
| // |
| // The return value is the number of bytes implied by the input parameters |
| // always, regardless of write success/failure. For write success/failure see |
| // IsOk(). |
| // |
| // The y_base default of nullptr calculates and returns the count of bytes |
| // that would be written to the file without actually writing them. |
| // |
| // The uv_offset is effectively optional - if 0 is specified (via default or |
| // explicitly), the uv_offset used is height_pixels * stride. |
| // |
| // width_pixels - Y width - must be even, at least for now. There are this |
| // many pixels width of Y data, and half this many pixels width of U and |
| // V data. |
| // height_pixels - Y height - must be even, at least for now. There are |
| // this many lines of Y data, and half this many lines of UV data. |
| // stride - NV12 uses the same stride for Y and UV together. This is the |
| // offset from Y start-of-line to next Y start-of-line, and the offset |
| // from UV start-of-U-then-V-line to next start-of-U-then-V-line. |
| // y_base - pointer to first byte of first pixel of Y plane, or nullptr if |
| // the call is just supposed to calculate how many bytes would have been |
| // written. |
| // uv_offset - the bytes offset from y_base to first byte of first pixel of |
| // U data. |
| size_t WriteNv12(uint32_t width_pixels, uint32_t height_pixels, uint32_t stride, |
| const uint8_t* y_base = nullptr, uint32_t uv_offset = 0); |
| |
| // Close() finishes/closes the file. IsOk() will return true after this call |
| // if closing worked. |
| void Close(); |
| |
| // Delete() deletes the file. IsOk() will return true after this iff the |
| // delete worked. |
| void Delete(); |
| |
| private: |
| void EnsureInitialized(); |
| void Initialize(); |
| void Fail(); |
| void WriteData(const uint8_t* to_write, size_t size); |
| |
| // Once this becomes false, it stays false. |
| bool is_ok_ = true; |
| bool is_initialized_ = false; |
| bool is_done_ = false; |
| std::string file_name_; |
| fbl::unique_fd file_; |
| |
| static std::atomic<uint32_t> instance_count_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(RawVideoWriter); |
| }; |
| |
| template <> |
| class RawVideoWriter<false> { |
| public: |
| explicit RawVideoWriter(const char* file_name = nullptr) {} |
| ~RawVideoWriter() {} |
| |
| bool IsOk() { return true; } |
| |
| size_t WriteUint32BigEndian(uint32_t number) { return sizeof(uint32_t); } |
| size_t WriteUint32LittleEndian(uint32_t number) { return sizeof(uint32_t); } |
| |
| size_t WriteNv12(uint32_t width_pixels, uint32_t height_pixels, uint32_t stride, |
| const uint8_t* y_base = nullptr, uint32_t uv_offset = 0) { |
| return height_pixels * width_pixels + height_pixels / 2 * width_pixels; |
| } |
| |
| void Close() {} |
| void Delete() {} |
| |
| private: |
| FXL_DISALLOW_COPY_AND_ASSIGN(RawVideoWriter); |
| }; |
| |
| } // namespace media |
| |
| #endif // SRC_MEDIA_LIB_RAW_VIDEO_WRITER_RAW_VIDEO_WRITER_H_ |