aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_mkv/MKVPlayer.h
blob: 91de512234f31a70f5813fd12e935c6b5eb3ed45 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#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;

};