blob: a5f833ce5a392f625ab02f08f3470defd814033b [file] [log] [blame]
// 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.
#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 {
// 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()
// 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();
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_;
template <>
class RawVideoWriter<false> {
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() {}
} // namespace media