138 lines
4.4 KiB
C++
138 lines
4.4 KiB
C++
#pragma once
|
|
#include <windows.h>
|
|
#include <bfc/platform/types.h>
|
|
|
|
// nsmkv stuff
|
|
#include "../nsmkv/Cluster.h"
|
|
#include "../nsmkv/header.h"
|
|
#include "../nsmkv/SeekTable.h"
|
|
#include "../nsmkv/SegmentInfo.h"
|
|
#include "../nsmkv/Tracks.h"
|
|
#include "../nsmkv/Cues.h"
|
|
#include "../nsmkv/Attachments.h"
|
|
#include "../nsmkv/mkv_reader.h"
|
|
|
|
#include "ifc_mkvvideodecoder.h"
|
|
#include "ifc_mkvaudiodecoder.h"
|
|
|
|
#include "../nu/AutoLock.h"
|
|
#include "../nu/AudioOutput.h"
|
|
|
|
|
|
class MKVPlayer
|
|
{
|
|
public:
|
|
MKVPlayer(const wchar_t *_filename);
|
|
~MKVPlayer();
|
|
DWORD CALLBACK ThreadFunction();
|
|
DWORD CALLBACK VideoThreadFunction();
|
|
|
|
void Kill();
|
|
void Seek(int seek_pos);
|
|
int GetOutputTime() const;
|
|
|
|
private:
|
|
// subfunctions to make the code cleaner
|
|
// they all have "side effects", which is a coding style I don't like
|
|
// but it makes it easier to read & modify
|
|
// TODO: move these to step, and have a state variable to know where we are
|
|
bool ParseHeader(); // on completion, header will be filled, file pointer will be at next level 0 node
|
|
bool FindSegment(); // on completion, segment_position will be calculated, file pointer will be at first level 1 node under segment
|
|
|
|
// file position is restored at end of function
|
|
bool FindCues();
|
|
|
|
int ParseCluster(nsmkv::MKVReader *stream, uint64_t size, uint64_t *track_numbers, size_t track_numbers_len);
|
|
|
|
enum
|
|
{
|
|
// values that you can return from OnXXXX()
|
|
MKV_CONTINUE = 0, // continue processing
|
|
MKV_ABORT = 1, // abort parsing gracefully (maybe 'stop' was pressed)
|
|
MKV_STOP = 2, // stop parsing completely - usually returned when mkv version is too new or codecs not supported
|
|
|
|
// values returned from errors within the Step() function itself
|
|
MKV_EOF = 3, // end of file
|
|
MKV_ERROR = 4, // parsing error
|
|
};
|
|
int OnHeader(const nsmkv::Header &header);
|
|
void OnSegmentInfo(const nsmkv::SegmentInfo &segment_info);
|
|
int OnTracks(const nsmkv::Tracks &tracks);
|
|
int OnBlock(const nsmkv::Cluster &cluster, const nsmkv::Block &block);
|
|
int OnFirstCluster(uint64_t position);
|
|
int OnAudio(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary);
|
|
int OnVideo(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary);
|
|
|
|
int OutputPictures(uint64_t default_timestamp);
|
|
/* start calling with cluster_number = 0 and block_number = 0 (or whatever appropriate based on CuePoints when seeking
|
|
will return 0 on success, 1 on EOF and -1 on failure
|
|
*/
|
|
int GetBlock(nsmkv::MKVReader *stream, uint64_t track_number, nsmkv::BlockBinary &binary, const nsmkv::Cluster **cluster, size_t &cluster_number, size_t &block_number);
|
|
static void CALLBACK SeekAPC(ULONG_PTR data);
|
|
|
|
int Step(nsmkv::MKVReader *stream, uint64_t *track_numbers, size_t track_numbers_len); // only gives you block data for the passed track number
|
|
|
|
private:
|
|
/* nsmkv internal implementation */
|
|
nsmkv::Header header;
|
|
uint64_t segment_position; // position of the start of the first level 1 element in the segment(for SeekHead relative positions)
|
|
uint64_t segment_size; // size of that segment
|
|
nsmkv::SeekTable seek_table;
|
|
nsmkv::SegmentInfo segment_info;
|
|
nsmkv::Tracks tracks;
|
|
nsmkv::Clusters clusters;
|
|
nsmkv::Cues cues;
|
|
nsmkv::Attachments attachments;
|
|
bool cues_searched;
|
|
bool first_cluster_found;
|
|
|
|
/* player implementation */
|
|
nsmkv::MKVReader *main_reader; // also gets used as audio_stream
|
|
Nullsoft::Utility::LockGuard cluster_guard;
|
|
HANDLE killswitch, seek_event;
|
|
wchar_t *filename;
|
|
volatile int m_needseek;
|
|
|
|
/* Audio */
|
|
ifc_mkvaudiodecoder *audio_decoder;
|
|
bool audio_opened;
|
|
uint64_t audio_track_num;
|
|
uint8_t audio_buffer[65536]; // TODO: dynamically allocate from OutputFrameSize
|
|
size_t audio_output_len;
|
|
size_t audio_buffered;
|
|
int audio_first_timestamp;
|
|
enum FlushState
|
|
{
|
|
FLUSH_NONE=0,
|
|
FLUSH_START=1,
|
|
FLUSH_SEEK=2,
|
|
};
|
|
FlushState audio_flushing;
|
|
unsigned int audio_bitrate;
|
|
|
|
/* Video */
|
|
ifc_mkvvideodecoder *video_decoder;
|
|
const nsmkv::TrackEntry *video_track_entry;
|
|
bool video_opened;
|
|
HANDLE video_thread;
|
|
double video_timecode_scale;
|
|
uint64_t video_track_num;
|
|
uint64_t video_cluster_position;
|
|
nsmkv::MKVReader *video_stream;
|
|
HANDLE video_break, video_flush, video_flush_done, video_resume, video_ready;
|
|
unsigned int video_bitrate;
|
|
int consecutive_early_frames;
|
|
|
|
/* AudioOutput implementation */
|
|
class MKVWait
|
|
{
|
|
public:
|
|
void Wait_SetEvents(HANDLE killswitch, HANDLE seek_event);
|
|
protected:
|
|
int WaitOrAbort(int time_in_ms);
|
|
private:
|
|
HANDLE handles[2];
|
|
};
|
|
nu::AudioOutput<MKVWait> audio_output;
|
|
|
|
}; |