lsnes/avi/cscd.hpp
2012-01-17 01:01:52 +02:00

361 lines
9 KiB
C++

#ifndef _output_cscd__hpp__included__
#define _output_cscd__hpp__included__
#if defined(__linux__) && !defined(BOOST_THREADS) && !defined(NO_THREADS)
#define STD_THREADS 1
#endif
#ifdef BOOST_THREADS
#include <boost/thread.hpp>
#include <boost/thread/locks.hpp>
#define ACTUALLY_USE_THREADS
typedef boost::thread thread_class;
typedef boost::condition_variable cv_class;
typedef boost::mutex mutex_class;
typedef boost::unique_lock<boost::mutex> umutex_class;
#else
#ifdef STD_THREADS
#include <thread>
#include <condition_variable>
#include <mutex>
#define ACTUALLY_USE_THREADS
typedef std::thread thread_class;
typedef std::condition_variable cv_class;
typedef std::mutex mutex_class;
typedef std::unique_lock<std::mutex> umutex_class;
#else
struct thread_class
{
template<typename T, typename args>
thread_class(T obj, args a) {}
void join() {}
};
typedef struct mutex_class
{
void lock() {}
void unlock() {}
} umutex_class;
struct cv_class
{
void wait(umutex_class& m) {}
void notify_all() {}
};
#endif
#endif
#ifdef __GNUC__
typedef unsigned long long Uint64;
typedef unsigned long long Sint64;
#elif defined(_MSC_VER)
typedef unsigned __int64 Uint64;
typedef signed __int64 Sint64;
#else
#error "Please define 64bit types."
#endif
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <fstream>
#include <list>
struct avi_file_structure;
/**
* Dump AVI using CSCD for video and PCM for audio.
*/
class avi_cscd_dumper
{
public:
/**
* AVI dumper parameters.
*/
struct global_parameters
{
/**
* Sound sampling rate.
*/
unsigned long sampling_rate;
/**
* Sound channel count.
*/
unsigned channel_count;
/**
* 16-bit audio flag.
*/
bool audio_16bit;
};
/**
* Pixel formats
*/
enum pixelformat
{
PIXFMT_RGB15_LE, /* Little-endian 15-bit RGB. */
PIXFMT_RGB15_BE, /* Big-endian 15-bit RGB. */
PIXFMT_RGB15_NE, /* Native-endian 15-bit RGB. */
PIXFMT_RGB24, /* 24-bit RGB, RGB order. */
PIXFMT_BGR24, /* 24-bit RGB, BGR order. */
PIXFMT_RGBX, /* 32-bit RGB, RGBx order. */
PIXFMT_BGRX, /* 32-bit RGB, BGRx order. */
PIXFMT_XRGB, /* 32-bit RGB, BGRx order. */
PIXFMT_XBGR /* 32-bit RGB, xBGR order. */
};
/**
* Sound formats.
*/
enum soundformat
{
SNDFMT_SILENCE, /* No data, silence. */
SNDFMT_SIGNED_8, /* 8-bit signed. */
SNDFMT_UNSIGNED_8, /* 8-bit unsigned. */
SNDFMT_SIGNED_16LE, /* 16-bit signed little-endian. */
SNDFMT_UNSIGNED_16LE, /* 16-bit unsigned little-endian. */
SNDFMT_SIGNED_16BE, /* 16-bit signed big-endian. */
SNDFMT_UNSIGNED_16BE, /* 16-bit unsigned big-endian. */
SNDFMT_SIGNED_16NE, /* 16-bit signed native-endian. */
SNDFMT_UNSIGNED_16NE /* 16-bit unsigned native-endian. */
};
/**
* AVI per-segment parameters
*/
struct segment_parameters
{
/**
* Framerate numerator.
*/
unsigned long fps_n;
/**
* Framerate denominator.
*/
unsigned long fps_d;
/**
* Pixel format.
*/
enum pixelformat dataformat;
/**
* Picture width
*/
unsigned width;
/**
* Picture height
*/
unsigned height;
/**
* If TRUE, always use default stride (bytes-per-pixel * width)
*/
bool default_stride;
/**
* Picture stride in bytes.
*/
size_t stride;
/**
* Keyframe distance (1 => every frame is keyframe).
*/
unsigned keyframe_distance;
/**
* Deflate compression level (0-9)
*/
unsigned deflate_level;
/**
* Maximum number of frames per major segment.
*/
unsigned long max_segment_frames;
};
/**
* Create new dumper.
*
* Parameter prefix: Prefix for dumped files.
* Parameter global: Global dumper parameters.
* Parameter segment: Dumper segment parameters.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Illegal parameters.
*
* Note: Segment parameters have to be sane, but altering those before dumping the first frame does not cause
* extraneous segment.
*/
avi_cscd_dumper(const std::string& prefix, const global_parameters& global, const segment_parameters& segment)
throw(std::bad_alloc, std::runtime_error);
/**
* Try to close the dump.
*/
~avi_cscd_dumper() throw();
/**
* Get current segment parameters.
*
* Returns: The segment parameters
*/
segment_parameters get_segment_parameters() throw();
/**
* Set segment parameters.
*
* Parameter segment: New segment parameters.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Illegal parameters.
*
* Note: If parameters change in incompatible manner, next dumped frame will cause segment change. The following
* changes are incompatible:
* - Changing the framerate.
* - Changing data format between 15 and 24/32 bit formats.
* - Changing with and height.
*/
void set_segment_parameters(const segment_parameters& segment) throw(std::bad_alloc, std::runtime_error);
/**
* Dump a frame.
*
* Parameter framedata: The frame data, in left-to-right, top-to-bottom order. Pixel format is as specified in
* segment parameters. If NULL, a black frame is dumped. Needs to be held stable until fully read in MT mode.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Can't write frame.
*/
void video(const void* framedata) throw(std::bad_alloc, std::runtime_error);
/**
* Is there frame being processed?
*/
bool is_frame_processing() throw();
/**
* Wait until frame has processed.
*/
void wait_frame_processing() throw();
/**
* Dump audio.
*
* Parameter audio: Audio, first to last channel, first to last sample order.
* Parameter samples: Number of samples to add. Note: Number of samples, not number of channels*samples.
* Parameter format: Sound sample format.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Can't write sound data.
*/
void audio(const void* audio, size_t samples, enum soundformat format) throw(std::bad_alloc,
std::runtime_error);
/**
* Dump audio (stereo).
*
* Parameter laudio: Audio for left channel.
* Parameter raudio: Audio for right channel.
* Parameter samples: Number of samples to add.
* Parameter format: Sound sample format.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Can't write sound data or not stereo sound.
*/
void audio(const void* laudio, const void* raudio, size_t samples, enum soundformat format)
throw(std::bad_alloc, std::runtime_error);
/**
* Signal end of dump.
*
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Can't flush the last segment.
*/
void end() throw(std::bad_alloc, std::runtime_error);
int encode_thread();
void set_capture_error(const std::string& err);
private:
//Information about buffered frame.
struct buffered_frame
{
std::vector<unsigned char> data;
bool keyframe;
unsigned compression_level;
unsigned mode; //1 => 15 bit, 2 => 24 bit, 3 => 32 bit.
bool forcebreak;
unsigned long fps_n;
unsigned long fps_d;
unsigned width;
unsigned height;
};
//Global parameters.
std::string dump_prefix;
unsigned long gp_sampling_rate;
unsigned gp_channel_count;
bool gp_audio_16bit;
//Current segment parameters.
unsigned long sp_fps_n;
unsigned long sp_fps_d;
enum pixelformat sp_dataformat;
unsigned sp_width;
unsigned sp_height;
size_t sp_stride;
unsigned sp_keyframe_distance;
unsigned sp_deflate_level;
unsigned long sp_max_segment_frames;
//Next segment parameters (some parameters can switch immediately).
bool switch_segments_on_next_frame;
unsigned spn_fps_n;
unsigned spn_fps_d;
enum pixelformat spn_dataformat;
unsigned spn_width;
unsigned spn_height;
//Current segment.
unsigned current_major_segment;
unsigned next_minor_segment;
unsigned long current_major_segment_frames;
unsigned frames_since_last_keyframe;
unsigned long frame_period_counter;
std::vector<unsigned char> previous_frame;
std::vector<unsigned char> compression_input;
std::vector<unsigned char> compression_output;
avi_file_structure* avifile_structure;
std::ofstream avifile;
//Sound&frame buffer.
std::vector<unsigned short> sound_buffer;
size_t buffered_sound_samples;
std::list<buffered_frame> frame_buffer;
//Fills compression_output with frame/sound packet and returns the total size.
size_t emit_frame(const std::vector<unsigned char>& data, bool keyframe, unsigned level, unsigned mode);
size_t emit_sound(size_t samples);
//Write a frame/sound packet in compression_output into stream.
void emit_frame_stream(size_t size, bool keyframe);
void emit_sound_stream(size_t size, size_t samples);
//Read next frame from queue with associated sound of given length. Also handles splits.
void write_frame_av(size_t samples);
size_t samples_for_next_frame();
bool restart_segment_if_needed(bool force_break);
void flush_buffers(bool forced);
void start_segment(unsigned major_seg, unsigned minor_seg);
void end_segment();
void request_flush_buffers(bool forced);
void _video(const void* framedata);
//Multithreading stuff.
thread_class* frame_thread;
cv_class frame_cond;
mutex_class frame_mutex;
volatile bool quit_requested;
volatile bool flush_requested;
volatile bool flush_requested_forced;
volatile bool frame_processing;
volatile const void* frame_pointer;
volatile bool exception_error_present;
std::string exception_error;
};
#endif