1 /*
2 	MIT License
3 
4 	Copyright (c) 2016 Błażej Szczygieł
5 
6 	Permission is hereby granted, free of charge, to any person obtaining a copy
7 	of this software and associated documentation files (the "Software"), to deal
8 	in the Software without restriction, including without limitation the rights
9 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 	copies of the Software, and to permit persons to whom the Software is
11 	furnished to do so, subject to the following conditions:
12 
13 	The above copyright notice and this permission notice shall be included in all
14 	copies or substantial portions of the Software.
15 
16 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 	SOFTWARE.
23 */
24 
25 #include "WebMDemuxer.hpp"
26 
27 #include "mkvparser/mkvparser.h"
28 
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
WebMFrame()33 WebMFrame::WebMFrame() :
34 	bufferSize(0), bufferCapacity(0),
35 	buffer(NULL),
36 	time(0),
37 	key(false)
38 {}
~WebMFrame()39 WebMFrame::~WebMFrame()
40 {
41 	free(buffer);
42 }
43 
44 /**/
45 
WebMDemuxer(mkvparser::IMkvReader * reader,int videoTrack,int audioTrack)46 WebMDemuxer::WebMDemuxer(mkvparser::IMkvReader *reader, int videoTrack, int audioTrack) :
47 	m_reader(reader),
48 	m_segment(NULL),
49 	m_cluster(NULL), m_block(NULL), m_blockEntry(NULL),
50 	m_blockFrameIndex(0),
51 	m_videoTrack(NULL), m_vCodec(NO_VIDEO),
52 	m_audioTrack(NULL), m_aCodec(NO_AUDIO),
53 	m_isOpen(false),
54 	m_eos(false)
55 {
56 	long long pos = 0;
57 	if (mkvparser::EBMLHeader().Parse(m_reader, pos))
58 		return;
59 
60 	if (mkvparser::Segment::CreateInstance(m_reader, pos, m_segment))
61 		return;
62 
63 	if (m_segment->Load() < 0)
64 		return;
65 
66 	const mkvparser::Tracks *tracks = m_segment->GetTracks();
67 	const unsigned long tracksCount = tracks->GetTracksCount();
68 	int currVideoTrack = -1, currAudioTrack = -1;
69 	for (unsigned long i = 0; i < tracksCount; ++i)
70 	{
71 		const mkvparser::Track *track = tracks->GetTrackByIndex(i);
72 		if (const char *codecId = track->GetCodecId())
73 		{
74 			if ((!m_videoTrack || currVideoTrack != videoTrack) && track->GetType() == mkvparser::Track::kVideo)
75 			{
76 				if (!strcmp(codecId, "V_VP8"))
77 					m_vCodec = VIDEO_VP8;
78 				else if (!strcmp(codecId, "V_VP9"))
79 					m_vCodec = VIDEO_VP9;
80 				if (m_vCodec != NO_VIDEO)
81 					m_videoTrack = static_cast<const mkvparser::VideoTrack *>(track);
82 				++currVideoTrack;
83 			}
84 			if ((!m_audioTrack || currAudioTrack != audioTrack) && track->GetType() == mkvparser::Track::kAudio)
85 			{
86 				if (!strcmp(codecId, "A_VORBIS"))
87 					m_aCodec = AUDIO_VORBIS;
88 				else if (!strcmp(codecId, "A_OPUS"))
89 					m_aCodec = AUDIO_OPUS;
90 				if (m_aCodec != NO_AUDIO)
91 					m_audioTrack = static_cast<const mkvparser::AudioTrack *>(track);
92 				++currAudioTrack;
93 			}
94 		}
95 	}
96 	if (!m_videoTrack && !m_audioTrack)
97 		return;
98 
99 	m_isOpen = true;
100 }
~WebMDemuxer()101 WebMDemuxer::~WebMDemuxer()
102 {
103 	delete m_segment;
104 	delete m_reader;
105 }
106 
getLength() const107 double WebMDemuxer::getLength() const
108 {
109 	return m_segment->GetDuration() / 1e9;
110 }
111 
getVideoCodec() const112 WebMDemuxer::VIDEO_CODEC WebMDemuxer::getVideoCodec() const
113 {
114 	return m_vCodec;
115 }
getWidth() const116 int WebMDemuxer::getWidth() const
117 {
118 	return m_videoTrack->GetWidth();
119 }
getHeight() const120 int WebMDemuxer::getHeight() const
121 {
122 	return m_videoTrack->GetHeight();
123 }
124 
getAudioCodec() const125 WebMDemuxer::AUDIO_CODEC WebMDemuxer::getAudioCodec() const
126 {
127 	return m_aCodec;
128 }
getAudioExtradata(size_t & size) const129 const unsigned char *WebMDemuxer::getAudioExtradata(size_t &size) const
130 {
131 	return m_audioTrack->GetCodecPrivate(size);
132 }
getSampleRate() const133 double WebMDemuxer::getSampleRate() const
134 {
135 	return m_audioTrack->GetSamplingRate();
136 }
getChannels() const137 int WebMDemuxer::getChannels() const
138 {
139 	return m_audioTrack->GetChannels();
140 }
getAudioDepth() const141 int WebMDemuxer::getAudioDepth() const
142 {
143 	return m_audioTrack->GetBitDepth();
144 }
145 
readFrame(WebMFrame * videoFrame,WebMFrame * audioFrame)146 bool WebMDemuxer::readFrame(WebMFrame *videoFrame, WebMFrame *audioFrame)
147 {
148 	const long videoTrackNumber = (videoFrame && m_videoTrack) ? m_videoTrack->GetNumber() : 0;
149 	const long audioTrackNumber = (audioFrame && m_audioTrack) ? m_audioTrack->GetNumber() : 0;
150 	bool blockEntryEOS = false;
151 
152 	if (videoFrame)
153 		videoFrame->bufferSize = 0;
154 	if (audioFrame)
155 		audioFrame->bufferSize = 0;
156 
157 	if (videoTrackNumber == 0 && audioTrackNumber == 0)
158 		return false;
159 
160 	if (m_eos)
161 		return false;
162 
163 	if (!m_cluster)
164 		m_cluster = m_segment->GetFirst();
165 
166 	do
167 	{
168 		bool getNewBlock = false;
169 		long status = 0;
170 		if (!m_blockEntry && !blockEntryEOS)
171 		{
172 			status = m_cluster->GetFirst(m_blockEntry);
173 			getNewBlock = true;
174 		}
175 		else if (blockEntryEOS || m_blockEntry->EOS())
176 		{
177 			m_cluster = m_segment->GetNext(m_cluster);
178 			if (!m_cluster || m_cluster->EOS())
179 			{
180 				m_eos = true;
181 				return false;
182 			}
183 			status = m_cluster->GetFirst(m_blockEntry);
184 			blockEntryEOS = false;
185 			getNewBlock = true;
186 		}
187 		else if (!m_block || m_blockFrameIndex == m_block->GetFrameCount() || notSupportedTrackNumber(videoTrackNumber, audioTrackNumber))
188 		{
189 			status = m_cluster->GetNext(m_blockEntry, m_blockEntry);
190 			if (!m_blockEntry  || m_blockEntry->EOS())
191 			{
192 				blockEntryEOS = true;
193 				continue;
194 			}
195 			getNewBlock = true;
196 		}
197 		if (status || !m_blockEntry)
198 			return false;
199 		if (getNewBlock)
200 		{
201 			m_block = m_blockEntry->GetBlock();
202 			m_blockFrameIndex = 0;
203 		}
204 	} while (blockEntryEOS || notSupportedTrackNumber(videoTrackNumber, audioTrackNumber));
205 
206 	WebMFrame *frame = NULL;
207 
208 	const long trackNumber = m_block->GetTrackNumber();
209 	if (trackNumber == videoTrackNumber)
210 		frame = videoFrame;
211 	else if (trackNumber == audioTrackNumber)
212 		frame = audioFrame;
213 	else
214 	{
215 		//Should not be possible
216 		assert(trackNumber == videoTrackNumber || trackNumber == audioTrackNumber);
217 		return false;
218 	}
219 
220 	const mkvparser::Block::Frame &blockFrame = m_block->GetFrame(m_blockFrameIndex++);
221 	if (blockFrame.len > frame->bufferCapacity)
222 	{
223 		unsigned char *newBuff = (unsigned char *)realloc(frame->buffer, frame->bufferCapacity = blockFrame.len);
224 		if (newBuff)
225 			frame->buffer = newBuff;
226 		else // Out of memory
227 			return false;
228 	}
229 	frame->bufferSize = blockFrame.len;
230 
231 	frame->time = m_block->GetTime(m_cluster) / 1e9;
232 	frame->key  = m_block->IsKey();
233 
234 	return !blockFrame.Read(m_reader, frame->buffer);
235 }
236 
notSupportedTrackNumber(long videoTrackNumber,long audioTrackNumber) const237 inline bool WebMDemuxer::notSupportedTrackNumber(long videoTrackNumber, long audioTrackNumber) const
238 {
239 	const long trackNumber = m_block->GetTrackNumber();
240 	return (trackNumber != videoTrackNumber && trackNumber != audioTrackNumber);
241 }
242