├── .gitignore ├── libwebm ├── AUTHORS.TXT ├── README.libvpx ├── mkvmuxer │ └── mkvmuxertypes.h ├── PATENTS.TXT ├── LICENSE.TXT ├── common │ └── webmids.h └── mkvparser │ └── mkvparser.h ├── simplewebm.pro ├── LICENSE ├── OpusVorbisDecoder.hpp ├── VPXDecoder.hpp ├── WebMDemuxer.hpp ├── example.cpp ├── VPXDecoder.cpp ├── OpusVorbisDecoder.cpp └── WebMDemuxer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | simplewebm 2 | *.pro.user 3 | Makefile 4 | *.o 5 | -------------------------------------------------------------------------------- /libwebm/AUTHORS.TXT: -------------------------------------------------------------------------------- 1 | # Names should be added to this file like so: 2 | # Name or Organization 3 | 4 | Google Inc. 5 | -------------------------------------------------------------------------------- /libwebm/README.libvpx: -------------------------------------------------------------------------------- 1 | URL: https://chromium.googlesource.com/webm/libwebm 2 | Version: 32d5ac49414a8914ec1e1f285f3f927c6e8ec29d 3 | License: BSD 4 | License File: LICENSE.txt 5 | 6 | Description: 7 | libwebm is used to handle WebM container I/O. 8 | 9 | Local Changes: 10 | * Removed: "mkvmuxer", "hdr_util", "file_util", "mkv_reader". 11 | * Make "~IMkvRerader()" public. 12 | -------------------------------------------------------------------------------- /simplewebm.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | CONFIG += console link_pkgconfig 4 | CONFIG -= app_bundle qt 5 | 6 | PKGCONFIG += vpx opus vorbis 7 | 8 | INCLUDEPATH = . libwebm 9 | DEPENDPATH = . libwebm 10 | 11 | SOURCES += example.cpp \ 12 | WebMDemuxer.cpp \ 13 | VPXDecoder.cpp \ 14 | OpusVorbisDecoder.cpp 15 | 16 | HEADERS += WebMDemuxer.hpp \ 17 | VPXDecoder.hpp \ 18 | OpusVorbisDecoder.hpp 19 | 20 | SOURCES += libwebm/mkvparser/mkvparser.cc 21 | HEADERS += libwebm/mkvparser/mkvparser.h \ 22 | libwebm/common/webmids.h 23 | 24 | #QMAKE_CXXFLAGS_DEBUG += -fsanitize=address -std=gnu++98 25 | #QMAKE_LFLAGS += -fsanitize=address 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Błażej Szczygieł 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libwebm/mkvmuxer/mkvmuxertypes.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The WebM project authors. All Rights Reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style license 4 | // that can be found in the LICENSE file in the root of the source 5 | // tree. An additional intellectual property rights grant can be found 6 | // in the file PATENTS. All contributing project authors may 7 | // be found in the AUTHORS file in the root of the source tree. 8 | 9 | #ifndef MKVMUXER_MKVMUXERTYPES_H_ 10 | #define MKVMUXER_MKVMUXERTYPES_H_ 11 | 12 | namespace mkvmuxer { 13 | typedef unsigned char uint8; 14 | typedef short int16; 15 | typedef int int32; 16 | typedef unsigned int uint32; 17 | typedef long long int64; 18 | typedef unsigned long long uint64; 19 | } // namespace mkvmuxer 20 | 21 | // Copied from Chromium basictypes.h 22 | // A macro to disallow the copy constructor and operator= functions 23 | // This should be used in the private: declarations for a class 24 | #define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \ 25 | TypeName(const TypeName&); \ 26 | void operator=(const TypeName&) 27 | 28 | #endif // MKVMUXER_MKVMUXERTYPES_HPP_ 29 | -------------------------------------------------------------------------------- /libwebm/PATENTS.TXT: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | ------------------------------------ 3 | 4 | "These implementations" means the copyrightable works that implement the WebM 5 | codecs distributed by Google as part of the WebM Project. 6 | 7 | Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, 8 | royalty-free, irrevocable (except as stated in this section) patent license to 9 | make, have made, use, offer to sell, sell, import, transfer, and otherwise 10 | run, modify and propagate the contents of these implementations of WebM, where 11 | such license applies only to those patent claims, both currently owned by 12 | Google and acquired in the future, licensable by Google that are necessarily 13 | infringed by these implementations of WebM. This grant does not include claims 14 | that would be infringed only as a consequence of further modification of these 15 | implementations. If you or your agent or exclusive licensee institute or order 16 | or agree to the institution of patent litigation or any other patent 17 | enforcement activity against any entity (including a cross-claim or 18 | counterclaim in a lawsuit) alleging that any of these implementations of WebM 19 | or any code incorporated within any of these implementations of WebM 20 | constitute direct or contributory patent infringement, or inducement of 21 | patent infringement, then any patent rights granted to you under this License 22 | for these implementations of WebM shall terminate as of the date such 23 | litigation is filed. 24 | -------------------------------------------------------------------------------- /libwebm/LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | 15 | * Neither the name of Google nor the names of its contributors may 16 | be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /OpusVorbisDecoder.hpp: -------------------------------------------------------------------------------- 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 | #ifndef OPUSVORBISDECODER_HPP 26 | #define OPUSVORBISDECODER_HPP 27 | 28 | #include "WebMDemuxer.hpp" 29 | 30 | struct VorbisDecoder; 31 | struct OpusDecoder; 32 | 33 | class OpusVorbisDecoder 34 | { 35 | OpusVorbisDecoder(const OpusVorbisDecoder &); 36 | void operator =(const OpusVorbisDecoder &); 37 | public: 38 | OpusVorbisDecoder(const WebMDemuxer &demuxer); 39 | ~OpusVorbisDecoder(); 40 | 41 | bool isOpen() const; 42 | 43 | inline int getBufferSamples() const 44 | { 45 | return m_numSamples; 46 | } 47 | 48 | bool getPCMS16(WebMFrame &frame, short *buffer, int &numOutSamples); 49 | 50 | private: 51 | bool openVorbis(const WebMDemuxer &demuxer); 52 | bool openOpus(const WebMDemuxer &demuxer); 53 | 54 | void close(); 55 | 56 | VorbisDecoder *m_vorbis; 57 | OpusDecoder *m_opus; 58 | int m_numSamples; 59 | int m_channels; 60 | 61 | }; 62 | 63 | #endif // OPUSVORBISDECODER_HPP 64 | -------------------------------------------------------------------------------- /VPXDecoder.hpp: -------------------------------------------------------------------------------- 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 | #ifndef VPXDECODER_HPP 26 | #define VPXDECODER_HPP 27 | 28 | #include "WebMDemuxer.hpp" 29 | 30 | struct vpx_codec_ctx; 31 | 32 | class VPXDecoder 33 | { 34 | VPXDecoder(const VPXDecoder &); 35 | void operator =(const VPXDecoder &); 36 | public: 37 | class Image 38 | { 39 | public: 40 | int getWidth(int plane) const; 41 | int getHeight(int plane) const; 42 | 43 | int w, h; 44 | int cs; 45 | int chromaShiftW, chromaShiftH; 46 | unsigned char *planes[3]; 47 | int linesize[3]; 48 | }; 49 | 50 | enum IMAGE_ERROR 51 | { 52 | UNSUPPORTED_FRAME = -1, 53 | NO_ERROR, 54 | NO_FRAME 55 | }; 56 | 57 | VPXDecoder(const WebMDemuxer &demuxer, unsigned threads = 1); 58 | ~VPXDecoder(); 59 | 60 | inline bool isOpen() const 61 | { 62 | return (bool)m_ctx; 63 | } 64 | 65 | inline int getFramesDelay() const 66 | { 67 | return m_delay; 68 | } 69 | 70 | bool decode(const WebMFrame &frame); 71 | IMAGE_ERROR getImage(Image &image); //The data is NOT copied! Only 3-plane, 8-bit images are supported. 72 | 73 | private: 74 | vpx_codec_ctx *m_ctx; 75 | const void *m_iter; 76 | int m_delay; 77 | int m_last_space; 78 | }; 79 | 80 | #endif // VPXDECODER_HPP 81 | -------------------------------------------------------------------------------- /WebMDemuxer.hpp: -------------------------------------------------------------------------------- 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 | #ifndef WEBMDEMUXER_HPP 26 | #define WEBMDEMUXER_HPP 27 | 28 | #include 29 | 30 | namespace mkvparser { 31 | class IMkvReader; 32 | class Segment; 33 | class Cluster; 34 | class Block; 35 | class BlockEntry; 36 | class VideoTrack; 37 | class AudioTrack; 38 | } 39 | 40 | class WebMFrame 41 | { 42 | WebMFrame(const WebMFrame &); 43 | void operator =(const WebMFrame &); 44 | public: 45 | WebMFrame(); 46 | ~WebMFrame(); 47 | 48 | inline bool isValid() const 49 | { 50 | return bufferSize > 0; 51 | } 52 | 53 | long bufferSize, bufferCapacity; 54 | unsigned char *buffer; 55 | double time; 56 | bool key; 57 | }; 58 | 59 | class WebMDemuxer 60 | { 61 | WebMDemuxer(const WebMDemuxer &); 62 | void operator =(const WebMDemuxer &); 63 | public: 64 | enum VIDEO_CODEC 65 | { 66 | NO_VIDEO, 67 | VIDEO_VP8, 68 | VIDEO_VP9 69 | }; 70 | enum AUDIO_CODEC 71 | { 72 | NO_AUDIO, 73 | AUDIO_VORBIS, 74 | AUDIO_OPUS 75 | }; 76 | 77 | WebMDemuxer(mkvparser::IMkvReader *reader, int videoTrack = 0, int audioTrack = 0); 78 | ~WebMDemuxer(); 79 | 80 | inline bool isOpen() const 81 | { 82 | return m_isOpen; 83 | } 84 | inline bool isEOS() const 85 | { 86 | return m_eos; 87 | } 88 | 89 | double getLength() const; 90 | 91 | VIDEO_CODEC getVideoCodec() const; 92 | int getWidth() const; 93 | int getHeight() const; 94 | 95 | AUDIO_CODEC getAudioCodec() const; 96 | const unsigned char *getAudioExtradata(size_t &size) const; // Needed for Vorbis 97 | double getSampleRate() const; 98 | int getChannels() const; 99 | int getAudioDepth() const; 100 | 101 | bool readFrame(WebMFrame *videoFrame, WebMFrame *audioFrame); 102 | 103 | private: 104 | inline bool notSupportedTrackNumber(long videoTrackNumber, long audioTrackNumber) const; 105 | 106 | mkvparser::IMkvReader *m_reader; 107 | mkvparser::Segment *m_segment; 108 | 109 | const mkvparser::Cluster *m_cluster; 110 | const mkvparser::Block *m_block; 111 | const mkvparser::BlockEntry *m_blockEntry; 112 | 113 | int m_blockFrameIndex; 114 | 115 | const mkvparser::VideoTrack *m_videoTrack; 116 | VIDEO_CODEC m_vCodec; 117 | 118 | const mkvparser::AudioTrack *m_audioTrack; 119 | AUDIO_CODEC m_aCodec; 120 | 121 | bool m_isOpen; 122 | bool m_eos; 123 | }; 124 | 125 | #endif // WEBMDEMUXER_HPP 126 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 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 "OpusVorbisDecoder.hpp" 26 | #include "VPXDecoder.hpp" 27 | 28 | #include 29 | 30 | #include 31 | 32 | class MkvReader : public mkvparser::IMkvReader 33 | { 34 | public: 35 | MkvReader(const char *filePath) : 36 | m_file(fopen(filePath, "rb")) 37 | {} 38 | ~MkvReader() 39 | { 40 | if (m_file) 41 | fclose(m_file); 42 | } 43 | 44 | int Read(long long pos, long len, unsigned char *buf) 45 | { 46 | if (!m_file) 47 | return -1; 48 | fseek(m_file, pos, SEEK_SET); 49 | const size_t size = fread(buf, 1, len, m_file); 50 | if (size < size_t(len)) 51 | return -1; 52 | return 0; 53 | } 54 | int Length(long long *total, long long *available) 55 | { 56 | if (!m_file) 57 | return -1; 58 | const off_t pos = ftell(m_file); 59 | fseek(m_file, 0, SEEK_END); 60 | if (total) 61 | *total = ftell(m_file); 62 | if (available) 63 | *available = ftell(m_file); 64 | fseek(m_file, pos, SEEK_SET); 65 | return 0; 66 | } 67 | 68 | private: 69 | FILE *m_file; 70 | }; 71 | 72 | int main(int argc, char *argv[]) 73 | { 74 | if (argc != 2) 75 | return -1; 76 | 77 | WebMDemuxer demuxer(new MkvReader(argv[1])); 78 | if (demuxer.isOpen()) 79 | { 80 | VPXDecoder videoDec(demuxer, 8); 81 | OpusVorbisDecoder audioDec(demuxer); 82 | 83 | WebMFrame videoFrame, audioFrame; 84 | 85 | VPXDecoder::Image image; 86 | short *pcm = audioDec.isOpen() ? new short[audioDec.getBufferSamples() * demuxer.getChannels()] : NULL; 87 | 88 | fprintf(stderr, "Length: %f\n", demuxer.getLength()); 89 | 90 | while (demuxer.readFrame(&videoFrame, &audioFrame)) 91 | { 92 | if (videoDec.isOpen() && videoFrame.isValid()) 93 | { 94 | if (!videoDec.decode(videoFrame)) 95 | { 96 | fprintf(stderr, "Video decode error\n"); 97 | break; 98 | } 99 | while (videoDec.getImage(image) == VPXDecoder::NO_ERROR) 100 | { 101 | // for (int p = 0; p < 3; ++p) 102 | // { 103 | // const int w = image.getWidth(p); 104 | // const int h = image.getHeight(p); 105 | // int offset = 0; 106 | // for (int y = 0; y < h; ++y) 107 | // { 108 | // fwrite(image.planes[p] + offset, 1, w, stdout); 109 | // offset += image.linesize[p]; 110 | // } 111 | // } 112 | } 113 | } 114 | if (audioDec.isOpen() && audioFrame.isValid()) 115 | { 116 | int numOutSamples; 117 | if (!audioDec.getPCMS16(audioFrame, pcm, numOutSamples)) 118 | { 119 | fprintf(stderr, "Audio decode error\n"); 120 | break; 121 | } 122 | // fwrite(pcm, 1, numOutSamples * demuxer.getChannels() * sizeof(short), stdout); 123 | } 124 | } 125 | 126 | delete[] pcm; 127 | } 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /VPXDecoder.cpp: -------------------------------------------------------------------------------- 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 "VPXDecoder.hpp" 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | VPXDecoder::VPXDecoder(const WebMDemuxer &demuxer, unsigned threads) : 34 | m_ctx(NULL), 35 | m_iter(NULL), 36 | m_delay(0), 37 | m_last_space(VPX_CS_UNKNOWN) 38 | { 39 | if (threads > 8) 40 | threads = 8; 41 | else if (threads < 1) 42 | threads = 1; 43 | 44 | const vpx_codec_dec_cfg_t codecCfg = { 45 | threads, 46 | 0, 47 | 0 48 | }; 49 | vpx_codec_iface_t *codecIface = NULL; 50 | 51 | switch (demuxer.getVideoCodec()) 52 | { 53 | case WebMDemuxer::VIDEO_VP8: 54 | codecIface = vpx_codec_vp8_dx(); 55 | break; 56 | case WebMDemuxer::VIDEO_VP9: 57 | codecIface = vpx_codec_vp9_dx(); 58 | m_delay = threads - 1; 59 | break; 60 | default: 61 | return; 62 | } 63 | 64 | m_ctx = new vpx_codec_ctx_t; 65 | if (vpx_codec_dec_init(m_ctx, codecIface, &codecCfg, m_delay > 0 ? VPX_CODEC_USE_FRAME_THREADING : 0)) 66 | { 67 | delete m_ctx; 68 | m_ctx = NULL; 69 | } 70 | } 71 | VPXDecoder::~VPXDecoder() 72 | { 73 | if (m_ctx) 74 | { 75 | vpx_codec_destroy(m_ctx); 76 | delete m_ctx; 77 | } 78 | } 79 | 80 | bool VPXDecoder::decode(const WebMFrame &frame) 81 | { 82 | m_iter = NULL; 83 | return !vpx_codec_decode(m_ctx, frame.buffer, frame.bufferSize, NULL, 0); 84 | } 85 | VPXDecoder::IMAGE_ERROR VPXDecoder::getImage(Image &image) 86 | { 87 | IMAGE_ERROR err = NO_FRAME; 88 | if (vpx_image_t *img = vpx_codec_get_frame(m_ctx, &m_iter)) 89 | { 90 | // It seems to be a common problem that UNKNOWN comes up a lot, yet FFMPEG is somehow getting accurate colour-space information. 91 | // After checking FFMPEG code, *they're* getting colour-space information, so I'm assuming something like this is going on. 92 | // It appears to work, at least. 93 | if (img->cs != VPX_CS_UNKNOWN) 94 | m_last_space = img->cs; 95 | if ((img->fmt & VPX_IMG_FMT_PLANAR) && !(img->fmt & (VPX_IMG_FMT_HAS_ALPHA | VPX_IMG_FMT_HIGHBITDEPTH))) 96 | { 97 | if (img->stride[0] && img->stride[1] && img->stride[2]) 98 | { 99 | const int uPlane = !!(img->fmt & VPX_IMG_FMT_UV_FLIP) + 1; 100 | const int vPlane = !(img->fmt & VPX_IMG_FMT_UV_FLIP) + 1; 101 | 102 | image.w = img->d_w; 103 | image.h = img->d_h; 104 | image.cs = m_last_space; 105 | image.chromaShiftW = img->x_chroma_shift; 106 | image.chromaShiftH = img->y_chroma_shift; 107 | 108 | image.planes[0] = img->planes[0]; 109 | image.planes[1] = img->planes[uPlane]; 110 | image.planes[2] = img->planes[vPlane]; 111 | 112 | image.linesize[0] = img->stride[0]; 113 | image.linesize[1] = img->stride[uPlane]; 114 | image.linesize[2] = img->stride[vPlane]; 115 | 116 | err = NO_ERROR; 117 | } 118 | } 119 | else 120 | { 121 | err = UNSUPPORTED_FRAME; 122 | } 123 | } 124 | return err; 125 | } 126 | 127 | /**/ 128 | 129 | static inline int ceilRshift(int val, int shift) 130 | { 131 | return (val + (1 << shift) - 1) >> shift; 132 | } 133 | 134 | int VPXDecoder::Image::getWidth(int plane) const 135 | { 136 | if (!plane) 137 | return w; 138 | return ceilRshift(w, chromaShiftW); 139 | } 140 | int VPXDecoder::Image::getHeight(int plane) const 141 | { 142 | if (!plane) 143 | return h; 144 | return ceilRshift(h, chromaShiftH); 145 | } 146 | -------------------------------------------------------------------------------- /libwebm/common/webmids.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The WebM project authors. All Rights Reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style license 4 | // that can be found in the LICENSE file in the root of the source 5 | // tree. An additional intellectual property rights grant can be found 6 | // in the file PATENTS. All contributing project authors may 7 | // be found in the AUTHORS file in the root of the source tree. 8 | 9 | #ifndef COMMON_WEBMIDS_H_ 10 | #define COMMON_WEBMIDS_H_ 11 | 12 | namespace libwebm { 13 | 14 | enum MkvId { 15 | kMkvEBML = 0x1A45DFA3, 16 | kMkvEBMLVersion = 0x4286, 17 | kMkvEBMLReadVersion = 0x42F7, 18 | kMkvEBMLMaxIDLength = 0x42F2, 19 | kMkvEBMLMaxSizeLength = 0x42F3, 20 | kMkvDocType = 0x4282, 21 | kMkvDocTypeVersion = 0x4287, 22 | kMkvDocTypeReadVersion = 0x4285, 23 | kMkvVoid = 0xEC, 24 | kMkvSignatureSlot = 0x1B538667, 25 | kMkvSignatureAlgo = 0x7E8A, 26 | kMkvSignatureHash = 0x7E9A, 27 | kMkvSignaturePublicKey = 0x7EA5, 28 | kMkvSignature = 0x7EB5, 29 | kMkvSignatureElements = 0x7E5B, 30 | kMkvSignatureElementList = 0x7E7B, 31 | kMkvSignedElement = 0x6532, 32 | // segment 33 | kMkvSegment = 0x18538067, 34 | // Meta Seek Information 35 | kMkvSeekHead = 0x114D9B74, 36 | kMkvSeek = 0x4DBB, 37 | kMkvSeekID = 0x53AB, 38 | kMkvSeekPosition = 0x53AC, 39 | // Segment Information 40 | kMkvInfo = 0x1549A966, 41 | kMkvTimecodeScale = 0x2AD7B1, 42 | kMkvDuration = 0x4489, 43 | kMkvDateUTC = 0x4461, 44 | kMkvTitle = 0x7BA9, 45 | kMkvMuxingApp = 0x4D80, 46 | kMkvWritingApp = 0x5741, 47 | // Cluster 48 | kMkvCluster = 0x1F43B675, 49 | kMkvTimecode = 0xE7, 50 | kMkvPrevSize = 0xAB, 51 | kMkvBlockGroup = 0xA0, 52 | kMkvBlock = 0xA1, 53 | kMkvBlockDuration = 0x9B, 54 | kMkvReferenceBlock = 0xFB, 55 | kMkvLaceNumber = 0xCC, 56 | kMkvSimpleBlock = 0xA3, 57 | kMkvBlockAdditions = 0x75A1, 58 | kMkvBlockMore = 0xA6, 59 | kMkvBlockAddID = 0xEE, 60 | kMkvBlockAdditional = 0xA5, 61 | kMkvDiscardPadding = 0x75A2, 62 | // Track 63 | kMkvTracks = 0x1654AE6B, 64 | kMkvTrackEntry = 0xAE, 65 | kMkvTrackNumber = 0xD7, 66 | kMkvTrackUID = 0x73C5, 67 | kMkvTrackType = 0x83, 68 | kMkvFlagEnabled = 0xB9, 69 | kMkvFlagDefault = 0x88, 70 | kMkvFlagForced = 0x55AA, 71 | kMkvFlagLacing = 0x9C, 72 | kMkvDefaultDuration = 0x23E383, 73 | kMkvMaxBlockAdditionID = 0x55EE, 74 | kMkvName = 0x536E, 75 | kMkvLanguage = 0x22B59C, 76 | kMkvCodecID = 0x86, 77 | kMkvCodecPrivate = 0x63A2, 78 | kMkvCodecName = 0x258688, 79 | kMkvCodecDelay = 0x56AA, 80 | kMkvSeekPreRoll = 0x56BB, 81 | // video 82 | kMkvVideo = 0xE0, 83 | kMkvFlagInterlaced = 0x9A, 84 | kMkvStereoMode = 0x53B8, 85 | kMkvAlphaMode = 0x53C0, 86 | kMkvPixelWidth = 0xB0, 87 | kMkvPixelHeight = 0xBA, 88 | kMkvPixelCropBottom = 0x54AA, 89 | kMkvPixelCropTop = 0x54BB, 90 | kMkvPixelCropLeft = 0x54CC, 91 | kMkvPixelCropRight = 0x54DD, 92 | kMkvDisplayWidth = 0x54B0, 93 | kMkvDisplayHeight = 0x54BA, 94 | kMkvDisplayUnit = 0x54B2, 95 | kMkvAspectRatioType = 0x54B3, 96 | kMkvFrameRate = 0x2383E3, 97 | // end video 98 | // colour 99 | kMkvColour = 0x55B0, 100 | kMkvMatrixCoefficients = 0x55B1, 101 | kMkvBitsPerChannel = 0x55B2, 102 | kMkvChromaSubsamplingHorz = 0x55B3, 103 | kMkvChromaSubsamplingVert = 0x55B4, 104 | kMkvCbSubsamplingHorz = 0x55B5, 105 | kMkvCbSubsamplingVert = 0x55B6, 106 | kMkvChromaSitingHorz = 0x55B7, 107 | kMkvChromaSitingVert = 0x55B8, 108 | kMkvRange = 0x55B9, 109 | kMkvTransferCharacteristics = 0x55BA, 110 | kMkvPrimaries = 0x55BB, 111 | kMkvMaxCLL = 0x55BC, 112 | kMkvMaxFALL = 0x55BD, 113 | // mastering metadata 114 | kMkvMasteringMetadata = 0x55D0, 115 | kMkvPrimaryRChromaticityX = 0x55D1, 116 | kMkvPrimaryRChromaticityY = 0x55D2, 117 | kMkvPrimaryGChromaticityX = 0x55D3, 118 | kMkvPrimaryGChromaticityY = 0x55D4, 119 | kMkvPrimaryBChromaticityX = 0x55D5, 120 | kMkvPrimaryBChromaticityY = 0x55D6, 121 | kMkvWhitePointChromaticityX = 0x55D7, 122 | kMkvWhitePointChromaticityY = 0x55D8, 123 | kMkvLuminanceMax = 0x55D9, 124 | kMkvLuminanceMin = 0x55DA, 125 | // end mastering metadata 126 | // end colour 127 | // audio 128 | kMkvAudio = 0xE1, 129 | kMkvSamplingFrequency = 0xB5, 130 | kMkvOutputSamplingFrequency = 0x78B5, 131 | kMkvChannels = 0x9F, 132 | kMkvBitDepth = 0x6264, 133 | // end audio 134 | // ContentEncodings 135 | kMkvContentEncodings = 0x6D80, 136 | kMkvContentEncoding = 0x6240, 137 | kMkvContentEncodingOrder = 0x5031, 138 | kMkvContentEncodingScope = 0x5032, 139 | kMkvContentEncodingType = 0x5033, 140 | kMkvContentCompression = 0x5034, 141 | kMkvContentCompAlgo = 0x4254, 142 | kMkvContentCompSettings = 0x4255, 143 | kMkvContentEncryption = 0x5035, 144 | kMkvContentEncAlgo = 0x47E1, 145 | kMkvContentEncKeyID = 0x47E2, 146 | kMkvContentSignature = 0x47E3, 147 | kMkvContentSigKeyID = 0x47E4, 148 | kMkvContentSigAlgo = 0x47E5, 149 | kMkvContentSigHashAlgo = 0x47E6, 150 | kMkvContentEncAESSettings = 0x47E7, 151 | kMkvAESSettingsCipherMode = 0x47E8, 152 | kMkvAESSettingsCipherInitData = 0x47E9, 153 | // end ContentEncodings 154 | // Cueing Data 155 | kMkvCues = 0x1C53BB6B, 156 | kMkvCuePoint = 0xBB, 157 | kMkvCueTime = 0xB3, 158 | kMkvCueTrackPositions = 0xB7, 159 | kMkvCueTrack = 0xF7, 160 | kMkvCueClusterPosition = 0xF1, 161 | kMkvCueBlockNumber = 0x5378, 162 | // Chapters 163 | kMkvChapters = 0x1043A770, 164 | kMkvEditionEntry = 0x45B9, 165 | kMkvChapterAtom = 0xB6, 166 | kMkvChapterUID = 0x73C4, 167 | kMkvChapterStringUID = 0x5654, 168 | kMkvChapterTimeStart = 0x91, 169 | kMkvChapterTimeEnd = 0x92, 170 | kMkvChapterDisplay = 0x80, 171 | kMkvChapString = 0x85, 172 | kMkvChapLanguage = 0x437C, 173 | kMkvChapCountry = 0x437E, 174 | // Tags 175 | kMkvTags = 0x1254C367, 176 | kMkvTag = 0x7373, 177 | kMkvSimpleTag = 0x67C8, 178 | kMkvTagName = 0x45A3, 179 | kMkvTagString = 0x4487 180 | }; 181 | 182 | } // namespace libwebm 183 | 184 | #endif // COMMON_WEBMIDS_H_ 185 | -------------------------------------------------------------------------------- /OpusVorbisDecoder.cpp: -------------------------------------------------------------------------------- 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 "OpusVorbisDecoder.hpp" 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | struct VorbisDecoder 33 | { 34 | vorbis_info info; 35 | vorbis_dsp_state dspState; 36 | vorbis_block block; 37 | ogg_packet op; 38 | 39 | bool hasDSPState, hasBlock; 40 | }; 41 | 42 | /**/ 43 | 44 | OpusVorbisDecoder::OpusVorbisDecoder(const WebMDemuxer &demuxer) : 45 | m_vorbis(NULL), m_opus(NULL), 46 | m_numSamples(0) 47 | { 48 | switch (demuxer.getAudioCodec()) 49 | { 50 | case WebMDemuxer::AUDIO_VORBIS: 51 | m_channels = demuxer.getChannels(); 52 | if (openVorbis(demuxer)) 53 | return; 54 | break; 55 | case WebMDemuxer::AUDIO_OPUS: 56 | m_channels = demuxer.getChannels(); 57 | if (openOpus(demuxer)) 58 | return; 59 | break; 60 | default: 61 | return; 62 | } 63 | close(); 64 | } 65 | OpusVorbisDecoder::~OpusVorbisDecoder() 66 | { 67 | close(); 68 | } 69 | 70 | bool OpusVorbisDecoder::isOpen() const 71 | { 72 | return (m_vorbis || m_opus); 73 | } 74 | 75 | bool OpusVorbisDecoder::getPCMS16(WebMFrame &frame, short *buffer, int &numOutSamples) 76 | { 77 | if (m_vorbis) 78 | { 79 | m_vorbis->op.packet = frame.buffer; 80 | m_vorbis->op.bytes = frame.bufferSize; 81 | 82 | if (vorbis_synthesis(&m_vorbis->block, &m_vorbis->op)) 83 | return false; 84 | if (vorbis_synthesis_blockin(&m_vorbis->dspState, &m_vorbis->block)) 85 | return false; 86 | 87 | const int maxSamples = getBufferSamples(); 88 | int samplesCount, count = 0; 89 | float **pcm; 90 | while ((samplesCount = vorbis_synthesis_pcmout(&m_vorbis->dspState, &pcm))) 91 | { 92 | const int toConvert = samplesCount <= maxSamples ? samplesCount : maxSamples; 93 | for (int c = 0; c < m_channels; ++c) 94 | { 95 | float *samples = pcm[c]; 96 | for (int i = 0, j = c; i < toConvert; ++i, j += m_channels) 97 | { 98 | int sample = samples[i] * 32767.0f; 99 | if (sample > 32767) 100 | sample = 32767; 101 | else if (sample < -32768) 102 | sample = -32768; 103 | buffer[count + j] = sample; 104 | } 105 | } 106 | vorbis_synthesis_read(&m_vorbis->dspState, toConvert); 107 | count += toConvert; 108 | } 109 | 110 | numOutSamples = count; 111 | return true; 112 | } 113 | else if (m_opus) 114 | { 115 | const int samples = opus_decode(m_opus, frame.buffer, frame.bufferSize, buffer, m_numSamples, 0); 116 | if (samples >= 0) 117 | { 118 | numOutSamples = samples; 119 | return true; 120 | } 121 | } 122 | return false; 123 | } 124 | 125 | bool OpusVorbisDecoder::openVorbis(const WebMDemuxer &demuxer) 126 | { 127 | size_t extradataSize = 0; 128 | const unsigned char *extradata = demuxer.getAudioExtradata(extradataSize); 129 | 130 | if (extradataSize < 3 || !extradata || extradata[0] != 2) 131 | return false; 132 | 133 | size_t headerSize[3] = {0}; 134 | size_t offset = 1; 135 | 136 | /* Calculate three headers sizes */ 137 | for (int i = 0; i < 2; ++i) 138 | { 139 | for (;;) 140 | { 141 | if (offset >= extradataSize) 142 | return false; 143 | headerSize[i] += extradata[offset]; 144 | if (extradata[offset++] < 0xFF) 145 | break; 146 | } 147 | } 148 | headerSize[2] = extradataSize - (headerSize[0] + headerSize[1] + offset); 149 | 150 | if (headerSize[0] + headerSize[1] + headerSize[2] + offset != extradataSize) 151 | return false; 152 | 153 | ogg_packet op[3]; 154 | memset(op, 0, sizeof op); 155 | 156 | op[0].packet = (unsigned char *)extradata + offset; 157 | op[0].bytes = headerSize[0]; 158 | op[0].b_o_s = 1; 159 | 160 | op[1].packet = (unsigned char *)extradata + offset + headerSize[0]; 161 | op[1].bytes = headerSize[1]; 162 | 163 | op[2].packet = (unsigned char *)extradata + offset + headerSize[0] + headerSize[1]; 164 | op[2].bytes = headerSize[2]; 165 | 166 | m_vorbis = new VorbisDecoder; 167 | m_vorbis->hasDSPState = m_vorbis->hasBlock = false; 168 | vorbis_info_init(&m_vorbis->info); 169 | 170 | /* Upload three Vorbis headers into libvorbis */ 171 | vorbis_comment vc; 172 | vorbis_comment_init(&vc); 173 | for (int i = 0; i < 3; ++i) 174 | { 175 | if (vorbis_synthesis_headerin(&m_vorbis->info, &vc, &op[i])) 176 | { 177 | vorbis_comment_clear(&vc); 178 | return false; 179 | } 180 | } 181 | vorbis_comment_clear(&vc); 182 | 183 | if (vorbis_synthesis_init(&m_vorbis->dspState, &m_vorbis->info)) 184 | return false; 185 | m_vorbis->hasDSPState = true; 186 | 187 | if (m_vorbis->info.channels != m_channels || m_vorbis->info.rate != demuxer.getSampleRate()) 188 | return false; 189 | 190 | if (vorbis_block_init(&m_vorbis->dspState, &m_vorbis->block)) 191 | return false; 192 | m_vorbis->hasBlock = true; 193 | 194 | memset(&m_vorbis->op, 0, sizeof m_vorbis->op); 195 | 196 | m_numSamples = 4096 / m_channels; 197 | 198 | return true; 199 | } 200 | bool OpusVorbisDecoder::openOpus(const WebMDemuxer &demuxer) 201 | { 202 | int opusErr = 0; 203 | m_opus = opus_decoder_create(demuxer.getSampleRate(), m_channels, &opusErr); 204 | if (!opusErr) 205 | { 206 | m_numSamples = demuxer.getSampleRate() * 0.06 + 0.5; //Maximum frame size (for 60 ms frame) 207 | return true; 208 | } 209 | return false; 210 | } 211 | 212 | void OpusVorbisDecoder::close() 213 | { 214 | if (m_vorbis) 215 | { 216 | if (m_vorbis->hasBlock) 217 | vorbis_block_clear(&m_vorbis->block); 218 | if (m_vorbis->hasDSPState) 219 | vorbis_dsp_clear(&m_vorbis->dspState); 220 | vorbis_info_clear(&m_vorbis->info); 221 | delete m_vorbis; 222 | } 223 | if (m_opus) 224 | opus_decoder_destroy(m_opus); 225 | } 226 | -------------------------------------------------------------------------------- /WebMDemuxer.cpp: -------------------------------------------------------------------------------- 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 30 | #include 31 | #include 32 | 33 | WebMFrame::WebMFrame() : 34 | bufferSize(0), bufferCapacity(0), 35 | buffer(NULL), 36 | time(0), 37 | key(false) 38 | {} 39 | WebMFrame::~WebMFrame() 40 | { 41 | free(buffer); 42 | } 43 | 44 | /**/ 45 | 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(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(track); 92 | ++currAudioTrack; 93 | } 94 | } 95 | } 96 | if (!m_videoTrack && !m_audioTrack) 97 | return; 98 | 99 | m_isOpen = true; 100 | } 101 | WebMDemuxer::~WebMDemuxer() 102 | { 103 | delete m_segment; 104 | delete m_reader; 105 | } 106 | 107 | double WebMDemuxer::getLength() const 108 | { 109 | return m_segment->GetDuration() / 1e9; 110 | } 111 | 112 | WebMDemuxer::VIDEO_CODEC WebMDemuxer::getVideoCodec() const 113 | { 114 | return m_vCodec; 115 | } 116 | int WebMDemuxer::getWidth() const 117 | { 118 | return m_videoTrack->GetWidth(); 119 | } 120 | int WebMDemuxer::getHeight() const 121 | { 122 | return m_videoTrack->GetHeight(); 123 | } 124 | 125 | WebMDemuxer::AUDIO_CODEC WebMDemuxer::getAudioCodec() const 126 | { 127 | return m_aCodec; 128 | } 129 | const unsigned char *WebMDemuxer::getAudioExtradata(size_t &size) const 130 | { 131 | return m_audioTrack->GetCodecPrivate(size); 132 | } 133 | double WebMDemuxer::getSampleRate() const 134 | { 135 | return m_audioTrack->GetSamplingRate(); 136 | } 137 | int WebMDemuxer::getChannels() const 138 | { 139 | return m_audioTrack->GetChannels(); 140 | } 141 | int WebMDemuxer::getAudioDepth() const 142 | { 143 | return m_audioTrack->GetBitDepth(); 144 | } 145 | 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 | 237 | 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 | -------------------------------------------------------------------------------- /libwebm/mkvparser/mkvparser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The WebM project authors. All Rights Reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style license 4 | // that can be found in the LICENSE file in the root of the source 5 | // tree. An additional intellectual property rights grant can be found 6 | // in the file PATENTS. All contributing project authors may 7 | // be found in the AUTHORS file in the root of the source tree. 8 | #ifndef MKVPARSER_MKVPARSER_H_ 9 | #define MKVPARSER_MKVPARSER_H_ 10 | 11 | #include 12 | 13 | namespace mkvparser { 14 | 15 | const int E_PARSE_FAILED = -1; 16 | const int E_FILE_FORMAT_INVALID = -2; 17 | const int E_BUFFER_NOT_FULL = -3; 18 | 19 | class IMkvReader { 20 | public: 21 | virtual int Read(long long pos, long len, unsigned char* buf) = 0; 22 | virtual int Length(long long* total, long long* available) = 0; 23 | 24 | virtual ~IMkvReader(); 25 | }; 26 | 27 | template 28 | Type* SafeArrayAlloc(unsigned long long num_elements, 29 | unsigned long long element_size); 30 | long long GetUIntLength(IMkvReader*, long long, long&); 31 | long long ReadUInt(IMkvReader*, long long, long&); 32 | long long ReadID(IMkvReader* pReader, long long pos, long& len); 33 | long long UnserializeUInt(IMkvReader*, long long pos, long long size); 34 | 35 | long UnserializeFloat(IMkvReader*, long long pos, long long size, double&); 36 | long UnserializeInt(IMkvReader*, long long pos, long long size, 37 | long long& result); 38 | 39 | long UnserializeString(IMkvReader*, long long pos, long long size, char*& str); 40 | 41 | long ParseElementHeader(IMkvReader* pReader, 42 | long long& pos, // consume id and size fields 43 | long long stop, // if you know size of element's parent 44 | long long& id, long long& size); 45 | 46 | bool Match(IMkvReader*, long long&, unsigned long, long long&); 47 | bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&); 48 | 49 | void GetVersion(int& major, int& minor, int& build, int& revision); 50 | 51 | struct EBMLHeader { 52 | EBMLHeader(); 53 | ~EBMLHeader(); 54 | long long m_version; 55 | long long m_readVersion; 56 | long long m_maxIdLength; 57 | long long m_maxSizeLength; 58 | char* m_docType; 59 | long long m_docTypeVersion; 60 | long long m_docTypeReadVersion; 61 | 62 | long long Parse(IMkvReader*, long long&); 63 | void Init(); 64 | }; 65 | 66 | class Segment; 67 | class Track; 68 | class Cluster; 69 | 70 | class Block { 71 | Block(const Block&); 72 | Block& operator=(const Block&); 73 | 74 | public: 75 | const long long m_start; 76 | const long long m_size; 77 | 78 | Block(long long start, long long size, long long discard_padding); 79 | ~Block(); 80 | 81 | long Parse(const Cluster*); 82 | 83 | long long GetTrackNumber() const; 84 | long long GetTimeCode(const Cluster*) const; // absolute, but not scaled 85 | long long GetTime(const Cluster*) const; // absolute, and scaled (ns) 86 | bool IsKey() const; 87 | void SetKey(bool); 88 | bool IsInvisible() const; 89 | 90 | enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml }; 91 | Lacing GetLacing() const; 92 | 93 | int GetFrameCount() const; // to index frames: [0, count) 94 | 95 | struct Frame { 96 | long long pos; // absolute offset 97 | long len; 98 | 99 | long Read(IMkvReader*, unsigned char*) const; 100 | }; 101 | 102 | const Frame& GetFrame(int frame_index) const; 103 | 104 | long long GetDiscardPadding() const; 105 | 106 | private: 107 | long long m_track; // Track::Number() 108 | short m_timecode; // relative to cluster 109 | unsigned char m_flags; 110 | 111 | Frame* m_frames; 112 | int m_frame_count; 113 | 114 | protected: 115 | const long long m_discard_padding; 116 | }; 117 | 118 | class BlockEntry { 119 | BlockEntry(const BlockEntry&); 120 | BlockEntry& operator=(const BlockEntry&); 121 | 122 | protected: 123 | BlockEntry(Cluster*, long index); 124 | 125 | public: 126 | virtual ~BlockEntry(); 127 | 128 | bool EOS() const { return (GetKind() == kBlockEOS); } 129 | const Cluster* GetCluster() const; 130 | long GetIndex() const; 131 | virtual const Block* GetBlock() const = 0; 132 | 133 | enum Kind { kBlockEOS, kBlockSimple, kBlockGroup }; 134 | virtual Kind GetKind() const = 0; 135 | 136 | protected: 137 | Cluster* const m_pCluster; 138 | const long m_index; 139 | }; 140 | 141 | class SimpleBlock : public BlockEntry { 142 | SimpleBlock(const SimpleBlock&); 143 | SimpleBlock& operator=(const SimpleBlock&); 144 | 145 | public: 146 | SimpleBlock(Cluster*, long index, long long start, long long size); 147 | long Parse(); 148 | 149 | Kind GetKind() const; 150 | const Block* GetBlock() const; 151 | 152 | protected: 153 | Block m_block; 154 | }; 155 | 156 | class BlockGroup : public BlockEntry { 157 | BlockGroup(const BlockGroup&); 158 | BlockGroup& operator=(const BlockGroup&); 159 | 160 | public: 161 | BlockGroup(Cluster*, long index, 162 | long long block_start, // absolute pos of block's payload 163 | long long block_size, // size of block's payload 164 | long long prev, long long next, long long duration, 165 | long long discard_padding); 166 | 167 | long Parse(); 168 | 169 | Kind GetKind() const; 170 | const Block* GetBlock() const; 171 | 172 | long long GetPrevTimeCode() const; // relative to block's time 173 | long long GetNextTimeCode() const; // as above 174 | long long GetDurationTimeCode() const; 175 | 176 | private: 177 | Block m_block; 178 | const long long m_prev; 179 | const long long m_next; 180 | const long long m_duration; 181 | }; 182 | 183 | /////////////////////////////////////////////////////////////// 184 | // ContentEncoding element 185 | // Elements used to describe if the track data has been encrypted or 186 | // compressed with zlib or header stripping. 187 | class ContentEncoding { 188 | public: 189 | enum { kCTR = 1 }; 190 | 191 | ContentEncoding(); 192 | ~ContentEncoding(); 193 | 194 | // ContentCompression element names 195 | struct ContentCompression { 196 | ContentCompression(); 197 | ~ContentCompression(); 198 | 199 | unsigned long long algo; 200 | unsigned char* settings; 201 | long long settings_len; 202 | }; 203 | 204 | // ContentEncAESSettings element names 205 | struct ContentEncAESSettings { 206 | ContentEncAESSettings() : cipher_mode(kCTR) {} 207 | ~ContentEncAESSettings() {} 208 | 209 | unsigned long long cipher_mode; 210 | }; 211 | 212 | // ContentEncryption element names 213 | struct ContentEncryption { 214 | ContentEncryption(); 215 | ~ContentEncryption(); 216 | 217 | unsigned long long algo; 218 | unsigned char* key_id; 219 | long long key_id_len; 220 | unsigned char* signature; 221 | long long signature_len; 222 | unsigned char* sig_key_id; 223 | long long sig_key_id_len; 224 | unsigned long long sig_algo; 225 | unsigned long long sig_hash_algo; 226 | 227 | ContentEncAESSettings aes_settings; 228 | }; 229 | 230 | // Returns ContentCompression represented by |idx|. Returns NULL if |idx| 231 | // is out of bounds. 232 | const ContentCompression* GetCompressionByIndex(unsigned long idx) const; 233 | 234 | // Returns number of ContentCompression elements in this ContentEncoding 235 | // element. 236 | unsigned long GetCompressionCount() const; 237 | 238 | // Parses the ContentCompression element from |pReader|. |start| is the 239 | // starting offset of the ContentCompression payload. |size| is the size in 240 | // bytes of the ContentCompression payload. |compression| is where the parsed 241 | // values will be stored. 242 | long ParseCompressionEntry(long long start, long long size, 243 | IMkvReader* pReader, 244 | ContentCompression* compression); 245 | 246 | // Returns ContentEncryption represented by |idx|. Returns NULL if |idx| 247 | // is out of bounds. 248 | const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const; 249 | 250 | // Returns number of ContentEncryption elements in this ContentEncoding 251 | // element. 252 | unsigned long GetEncryptionCount() const; 253 | 254 | // Parses the ContentEncAESSettings element from |pReader|. |start| is the 255 | // starting offset of the ContentEncAESSettings payload. |size| is the 256 | // size in bytes of the ContentEncAESSettings payload. |encryption| is 257 | // where the parsed values will be stored. 258 | long ParseContentEncAESSettingsEntry(long long start, long long size, 259 | IMkvReader* pReader, 260 | ContentEncAESSettings* aes); 261 | 262 | // Parses the ContentEncoding element from |pReader|. |start| is the 263 | // starting offset of the ContentEncoding payload. |size| is the size in 264 | // bytes of the ContentEncoding payload. Returns true on success. 265 | long ParseContentEncodingEntry(long long start, long long size, 266 | IMkvReader* pReader); 267 | 268 | // Parses the ContentEncryption element from |pReader|. |start| is the 269 | // starting offset of the ContentEncryption payload. |size| is the size in 270 | // bytes of the ContentEncryption payload. |encryption| is where the parsed 271 | // values will be stored. 272 | long ParseEncryptionEntry(long long start, long long size, 273 | IMkvReader* pReader, ContentEncryption* encryption); 274 | 275 | unsigned long long encoding_order() const { return encoding_order_; } 276 | unsigned long long encoding_scope() const { return encoding_scope_; } 277 | unsigned long long encoding_type() const { return encoding_type_; } 278 | 279 | private: 280 | // Member variables for list of ContentCompression elements. 281 | ContentCompression** compression_entries_; 282 | ContentCompression** compression_entries_end_; 283 | 284 | // Member variables for list of ContentEncryption elements. 285 | ContentEncryption** encryption_entries_; 286 | ContentEncryption** encryption_entries_end_; 287 | 288 | // ContentEncoding element names 289 | unsigned long long encoding_order_; 290 | unsigned long long encoding_scope_; 291 | unsigned long long encoding_type_; 292 | 293 | // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); 294 | ContentEncoding(const ContentEncoding&); 295 | ContentEncoding& operator=(const ContentEncoding&); 296 | }; 297 | 298 | class Track { 299 | Track(const Track&); 300 | Track& operator=(const Track&); 301 | 302 | public: 303 | class Info; 304 | static long Create(Segment*, const Info&, long long element_start, 305 | long long element_size, Track*&); 306 | 307 | enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 }; 308 | 309 | Segment* const m_pSegment; 310 | const long long m_element_start; 311 | const long long m_element_size; 312 | virtual ~Track(); 313 | 314 | long GetType() const; 315 | long GetNumber() const; 316 | unsigned long long GetUid() const; 317 | const char* GetNameAsUTF8() const; 318 | const char* GetLanguage() const; 319 | const char* GetCodecNameAsUTF8() const; 320 | const char* GetCodecId() const; 321 | const unsigned char* GetCodecPrivate(size_t&) const; 322 | bool GetLacing() const; 323 | unsigned long long GetDefaultDuration() const; 324 | unsigned long long GetCodecDelay() const; 325 | unsigned long long GetSeekPreRoll() const; 326 | 327 | const BlockEntry* GetEOS() const; 328 | 329 | struct Settings { 330 | long long start; 331 | long long size; 332 | }; 333 | 334 | class Info { 335 | public: 336 | Info(); 337 | ~Info(); 338 | int Copy(Info&) const; 339 | void Clear(); 340 | long type; 341 | long number; 342 | unsigned long long uid; 343 | unsigned long long defaultDuration; 344 | unsigned long long codecDelay; 345 | unsigned long long seekPreRoll; 346 | char* nameAsUTF8; 347 | char* language; 348 | char* codecId; 349 | char* codecNameAsUTF8; 350 | unsigned char* codecPrivate; 351 | size_t codecPrivateSize; 352 | bool lacing; 353 | Settings settings; 354 | 355 | private: 356 | Info(const Info&); 357 | Info& operator=(const Info&); 358 | int CopyStr(char* Info::*str, Info&) const; 359 | }; 360 | 361 | long GetFirst(const BlockEntry*&) const; 362 | long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; 363 | virtual bool VetEntry(const BlockEntry*) const; 364 | virtual long Seek(long long time_ns, const BlockEntry*&) const; 365 | 366 | const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; 367 | unsigned long GetContentEncodingCount() const; 368 | 369 | long ParseContentEncodingsEntry(long long start, long long size); 370 | 371 | protected: 372 | Track(Segment*, long long element_start, long long element_size); 373 | 374 | Info m_info; 375 | 376 | class EOSBlock : public BlockEntry { 377 | public: 378 | EOSBlock(); 379 | 380 | Kind GetKind() const; 381 | const Block* GetBlock() const; 382 | }; 383 | 384 | EOSBlock m_eos; 385 | 386 | private: 387 | ContentEncoding** content_encoding_entries_; 388 | ContentEncoding** content_encoding_entries_end_; 389 | }; 390 | 391 | struct PrimaryChromaticity { 392 | PrimaryChromaticity() : x(0), y(0) {} 393 | ~PrimaryChromaticity() {} 394 | static bool Parse(IMkvReader* reader, long long read_pos, 395 | long long value_size, bool is_x, 396 | PrimaryChromaticity** chromaticity); 397 | float x; 398 | float y; 399 | }; 400 | 401 | struct MasteringMetadata { 402 | static const float kValueNotPresent; 403 | 404 | MasteringMetadata() 405 | : r(NULL), 406 | g(NULL), 407 | b(NULL), 408 | white_point(NULL), 409 | luminance_max(kValueNotPresent), 410 | luminance_min(kValueNotPresent) {} 411 | ~MasteringMetadata() { 412 | delete r; 413 | delete g; 414 | delete b; 415 | delete white_point; 416 | } 417 | 418 | static bool Parse(IMkvReader* reader, long long element_start, 419 | long long element_size, 420 | MasteringMetadata** mastering_metadata); 421 | 422 | PrimaryChromaticity* r; 423 | PrimaryChromaticity* g; 424 | PrimaryChromaticity* b; 425 | PrimaryChromaticity* white_point; 426 | float luminance_max; 427 | float luminance_min; 428 | }; 429 | 430 | struct Colour { 431 | static const long long kValueNotPresent; 432 | 433 | // Unless otherwise noted all values assigned upon construction are the 434 | // equivalent of unspecified/default. 435 | Colour() 436 | : matrix_coefficients(kValueNotPresent), 437 | bits_per_channel(kValueNotPresent), 438 | chroma_subsampling_horz(kValueNotPresent), 439 | chroma_subsampling_vert(kValueNotPresent), 440 | cb_subsampling_horz(kValueNotPresent), 441 | cb_subsampling_vert(kValueNotPresent), 442 | chroma_siting_horz(kValueNotPresent), 443 | chroma_siting_vert(kValueNotPresent), 444 | range(kValueNotPresent), 445 | transfer_characteristics(kValueNotPresent), 446 | primaries(kValueNotPresent), 447 | max_cll(kValueNotPresent), 448 | max_fall(kValueNotPresent), 449 | mastering_metadata(NULL) {} 450 | ~Colour() { 451 | delete mastering_metadata; 452 | mastering_metadata = NULL; 453 | } 454 | 455 | static bool Parse(IMkvReader* reader, long long element_start, 456 | long long element_size, Colour** colour); 457 | 458 | long long matrix_coefficients; 459 | long long bits_per_channel; 460 | long long chroma_subsampling_horz; 461 | long long chroma_subsampling_vert; 462 | long long cb_subsampling_horz; 463 | long long cb_subsampling_vert; 464 | long long chroma_siting_horz; 465 | long long chroma_siting_vert; 466 | long long range; 467 | long long transfer_characteristics; 468 | long long primaries; 469 | long long max_cll; 470 | long long max_fall; 471 | 472 | MasteringMetadata* mastering_metadata; 473 | }; 474 | 475 | class VideoTrack : public Track { 476 | VideoTrack(const VideoTrack&); 477 | VideoTrack& operator=(const VideoTrack&); 478 | 479 | VideoTrack(Segment*, long long element_start, long long element_size); 480 | 481 | public: 482 | virtual ~VideoTrack(); 483 | static long Parse(Segment*, const Info&, long long element_start, 484 | long long element_size, VideoTrack*&); 485 | 486 | long long GetWidth() const; 487 | long long GetHeight() const; 488 | long long GetDisplayWidth() const; 489 | long long GetDisplayHeight() const; 490 | long long GetDisplayUnit() const; 491 | long long GetStereoMode() const; 492 | double GetFrameRate() const; 493 | 494 | bool VetEntry(const BlockEntry*) const; 495 | long Seek(long long time_ns, const BlockEntry*&) const; 496 | 497 | Colour* GetColour() const; 498 | 499 | private: 500 | long long m_width; 501 | long long m_height; 502 | long long m_display_width; 503 | long long m_display_height; 504 | long long m_display_unit; 505 | long long m_stereo_mode; 506 | 507 | double m_rate; 508 | 509 | Colour* m_colour; 510 | }; 511 | 512 | class AudioTrack : public Track { 513 | AudioTrack(const AudioTrack&); 514 | AudioTrack& operator=(const AudioTrack&); 515 | 516 | AudioTrack(Segment*, long long element_start, long long element_size); 517 | 518 | public: 519 | static long Parse(Segment*, const Info&, long long element_start, 520 | long long element_size, AudioTrack*&); 521 | 522 | double GetSamplingRate() const; 523 | long long GetChannels() const; 524 | long long GetBitDepth() const; 525 | 526 | private: 527 | double m_rate; 528 | long long m_channels; 529 | long long m_bitDepth; 530 | }; 531 | 532 | class Tracks { 533 | Tracks(const Tracks&); 534 | Tracks& operator=(const Tracks&); 535 | 536 | public: 537 | Segment* const m_pSegment; 538 | const long long m_start; 539 | const long long m_size; 540 | const long long m_element_start; 541 | const long long m_element_size; 542 | 543 | Tracks(Segment*, long long start, long long size, long long element_start, 544 | long long element_size); 545 | 546 | ~Tracks(); 547 | 548 | long Parse(); 549 | 550 | unsigned long GetTracksCount() const; 551 | 552 | const Track* GetTrackByNumber(long tn) const; 553 | const Track* GetTrackByIndex(unsigned long idx) const; 554 | 555 | private: 556 | Track** m_trackEntries; 557 | Track** m_trackEntriesEnd; 558 | 559 | long ParseTrackEntry(long long payload_start, long long payload_size, 560 | long long element_start, long long element_size, 561 | Track*&) const; 562 | }; 563 | 564 | class Chapters { 565 | Chapters(const Chapters&); 566 | Chapters& operator=(const Chapters&); 567 | 568 | public: 569 | Segment* const m_pSegment; 570 | const long long m_start; 571 | const long long m_size; 572 | const long long m_element_start; 573 | const long long m_element_size; 574 | 575 | Chapters(Segment*, long long payload_start, long long payload_size, 576 | long long element_start, long long element_size); 577 | 578 | ~Chapters(); 579 | 580 | long Parse(); 581 | 582 | class Atom; 583 | class Edition; 584 | 585 | class Display { 586 | friend class Atom; 587 | Display(); 588 | Display(const Display&); 589 | ~Display(); 590 | Display& operator=(const Display&); 591 | 592 | public: 593 | const char* GetString() const; 594 | const char* GetLanguage() const; 595 | const char* GetCountry() const; 596 | 597 | private: 598 | void Init(); 599 | void ShallowCopy(Display&) const; 600 | void Clear(); 601 | long Parse(IMkvReader*, long long pos, long long size); 602 | 603 | char* m_string; 604 | char* m_language; 605 | char* m_country; 606 | }; 607 | 608 | class Atom { 609 | friend class Edition; 610 | Atom(); 611 | Atom(const Atom&); 612 | ~Atom(); 613 | Atom& operator=(const Atom&); 614 | 615 | public: 616 | unsigned long long GetUID() const; 617 | const char* GetStringUID() const; 618 | 619 | long long GetStartTimecode() const; 620 | long long GetStopTimecode() const; 621 | 622 | long long GetStartTime(const Chapters*) const; 623 | long long GetStopTime(const Chapters*) const; 624 | 625 | int GetDisplayCount() const; 626 | const Display* GetDisplay(int index) const; 627 | 628 | private: 629 | void Init(); 630 | void ShallowCopy(Atom&) const; 631 | void Clear(); 632 | long Parse(IMkvReader*, long long pos, long long size); 633 | static long long GetTime(const Chapters*, long long timecode); 634 | 635 | long ParseDisplay(IMkvReader*, long long pos, long long size); 636 | bool ExpandDisplaysArray(); 637 | 638 | char* m_string_uid; 639 | unsigned long long m_uid; 640 | long long m_start_timecode; 641 | long long m_stop_timecode; 642 | 643 | Display* m_displays; 644 | int m_displays_size; 645 | int m_displays_count; 646 | }; 647 | 648 | class Edition { 649 | friend class Chapters; 650 | Edition(); 651 | Edition(const Edition&); 652 | ~Edition(); 653 | Edition& operator=(const Edition&); 654 | 655 | public: 656 | int GetAtomCount() const; 657 | const Atom* GetAtom(int index) const; 658 | 659 | private: 660 | void Init(); 661 | void ShallowCopy(Edition&) const; 662 | void Clear(); 663 | long Parse(IMkvReader*, long long pos, long long size); 664 | 665 | long ParseAtom(IMkvReader*, long long pos, long long size); 666 | bool ExpandAtomsArray(); 667 | 668 | Atom* m_atoms; 669 | int m_atoms_size; 670 | int m_atoms_count; 671 | }; 672 | 673 | int GetEditionCount() const; 674 | const Edition* GetEdition(int index) const; 675 | 676 | private: 677 | long ParseEdition(long long pos, long long size); 678 | bool ExpandEditionsArray(); 679 | 680 | Edition* m_editions; 681 | int m_editions_size; 682 | int m_editions_count; 683 | }; 684 | 685 | class Tags { 686 | Tags(const Tags&); 687 | Tags& operator=(const Tags&); 688 | 689 | public: 690 | Segment* const m_pSegment; 691 | const long long m_start; 692 | const long long m_size; 693 | const long long m_element_start; 694 | const long long m_element_size; 695 | 696 | Tags(Segment*, long long payload_start, long long payload_size, 697 | long long element_start, long long element_size); 698 | 699 | ~Tags(); 700 | 701 | long Parse(); 702 | 703 | class Tag; 704 | class SimpleTag; 705 | 706 | class SimpleTag { 707 | friend class Tag; 708 | SimpleTag(); 709 | SimpleTag(const SimpleTag&); 710 | ~SimpleTag(); 711 | SimpleTag& operator=(const SimpleTag&); 712 | 713 | public: 714 | const char* GetTagName() const; 715 | const char* GetTagString() const; 716 | 717 | private: 718 | void Init(); 719 | void ShallowCopy(SimpleTag&) const; 720 | void Clear(); 721 | long Parse(IMkvReader*, long long pos, long long size); 722 | 723 | char* m_tag_name; 724 | char* m_tag_string; 725 | }; 726 | 727 | class Tag { 728 | friend class Tags; 729 | Tag(); 730 | Tag(const Tag&); 731 | ~Tag(); 732 | Tag& operator=(const Tag&); 733 | 734 | public: 735 | int GetSimpleTagCount() const; 736 | const SimpleTag* GetSimpleTag(int index) const; 737 | 738 | private: 739 | void Init(); 740 | void ShallowCopy(Tag&) const; 741 | void Clear(); 742 | long Parse(IMkvReader*, long long pos, long long size); 743 | 744 | long ParseSimpleTag(IMkvReader*, long long pos, long long size); 745 | bool ExpandSimpleTagsArray(); 746 | 747 | SimpleTag* m_simple_tags; 748 | int m_simple_tags_size; 749 | int m_simple_tags_count; 750 | }; 751 | 752 | int GetTagCount() const; 753 | const Tag* GetTag(int index) const; 754 | 755 | private: 756 | long ParseTag(long long pos, long long size); 757 | bool ExpandTagsArray(); 758 | 759 | Tag* m_tags; 760 | int m_tags_size; 761 | int m_tags_count; 762 | }; 763 | 764 | class SegmentInfo { 765 | SegmentInfo(const SegmentInfo&); 766 | SegmentInfo& operator=(const SegmentInfo&); 767 | 768 | public: 769 | Segment* const m_pSegment; 770 | const long long m_start; 771 | const long long m_size; 772 | const long long m_element_start; 773 | const long long m_element_size; 774 | 775 | SegmentInfo(Segment*, long long start, long long size, 776 | long long element_start, long long element_size); 777 | 778 | ~SegmentInfo(); 779 | 780 | long Parse(); 781 | 782 | long long GetTimeCodeScale() const; 783 | long long GetDuration() const; // scaled 784 | const char* GetMuxingAppAsUTF8() const; 785 | const char* GetWritingAppAsUTF8() const; 786 | const char* GetTitleAsUTF8() const; 787 | 788 | private: 789 | long long m_timecodeScale; 790 | double m_duration; 791 | char* m_pMuxingAppAsUTF8; 792 | char* m_pWritingAppAsUTF8; 793 | char* m_pTitleAsUTF8; 794 | }; 795 | 796 | class SeekHead { 797 | SeekHead(const SeekHead&); 798 | SeekHead& operator=(const SeekHead&); 799 | 800 | public: 801 | Segment* const m_pSegment; 802 | const long long m_start; 803 | const long long m_size; 804 | const long long m_element_start; 805 | const long long m_element_size; 806 | 807 | SeekHead(Segment*, long long start, long long size, long long element_start, 808 | long long element_size); 809 | 810 | ~SeekHead(); 811 | 812 | long Parse(); 813 | 814 | struct Entry { 815 | // the SeekHead entry payload 816 | long long id; 817 | long long pos; 818 | 819 | // absolute pos of SeekEntry ID 820 | long long element_start; 821 | 822 | // SeekEntry ID size + size size + payload 823 | long long element_size; 824 | }; 825 | 826 | int GetCount() const; 827 | const Entry* GetEntry(int idx) const; 828 | 829 | struct VoidElement { 830 | // absolute pos of Void ID 831 | long long element_start; 832 | 833 | // ID size + size size + payload size 834 | long long element_size; 835 | }; 836 | 837 | int GetVoidElementCount() const; 838 | const VoidElement* GetVoidElement(int idx) const; 839 | 840 | private: 841 | Entry* m_entries; 842 | int m_entry_count; 843 | 844 | VoidElement* m_void_elements; 845 | int m_void_element_count; 846 | 847 | static bool ParseEntry(IMkvReader*, 848 | long long pos, // payload 849 | long long size, Entry*); 850 | }; 851 | 852 | class Cues; 853 | class CuePoint { 854 | friend class Cues; 855 | 856 | CuePoint(long, long long); 857 | ~CuePoint(); 858 | 859 | CuePoint(const CuePoint&); 860 | CuePoint& operator=(const CuePoint&); 861 | 862 | public: 863 | long long m_element_start; 864 | long long m_element_size; 865 | 866 | bool Load(IMkvReader*); 867 | 868 | long long GetTimeCode() const; // absolute but unscaled 869 | long long GetTime(const Segment*) const; // absolute and scaled (ns units) 870 | 871 | struct TrackPosition { 872 | long long m_track; 873 | long long m_pos; // of cluster 874 | long long m_block; 875 | // codec_state //defaults to 0 876 | // reference = clusters containing req'd referenced blocks 877 | // reftime = timecode of the referenced block 878 | 879 | bool Parse(IMkvReader*, long long, long long); 880 | }; 881 | 882 | const TrackPosition* Find(const Track*) const; 883 | 884 | private: 885 | const long m_index; 886 | long long m_timecode; 887 | TrackPosition* m_track_positions; 888 | size_t m_track_positions_count; 889 | }; 890 | 891 | class Cues { 892 | friend class Segment; 893 | 894 | Cues(Segment*, long long start, long long size, long long element_start, 895 | long long element_size); 896 | ~Cues(); 897 | 898 | Cues(const Cues&); 899 | Cues& operator=(const Cues&); 900 | 901 | public: 902 | Segment* const m_pSegment; 903 | const long long m_start; 904 | const long long m_size; 905 | const long long m_element_start; 906 | const long long m_element_size; 907 | 908 | bool Find( // lower bound of time_ns 909 | long long time_ns, const Track*, const CuePoint*&, 910 | const CuePoint::TrackPosition*&) const; 911 | 912 | const CuePoint* GetFirst() const; 913 | const CuePoint* GetLast() const; 914 | const CuePoint* GetNext(const CuePoint*) const; 915 | 916 | const BlockEntry* GetBlock(const CuePoint*, 917 | const CuePoint::TrackPosition*) const; 918 | 919 | bool LoadCuePoint() const; 920 | long GetCount() const; // loaded only 921 | // long GetTotal() const; //loaded + preloaded 922 | bool DoneParsing() const; 923 | 924 | private: 925 | bool Init() const; 926 | bool PreloadCuePoint(long&, long long) const; 927 | 928 | mutable CuePoint** m_cue_points; 929 | mutable long m_count; 930 | mutable long m_preload_count; 931 | mutable long long m_pos; 932 | }; 933 | 934 | class Cluster { 935 | friend class Segment; 936 | 937 | Cluster(const Cluster&); 938 | Cluster& operator=(const Cluster&); 939 | 940 | public: 941 | Segment* const m_pSegment; 942 | 943 | public: 944 | static Cluster* Create(Segment*, 945 | long index, // index in segment 946 | long long off); // offset relative to segment 947 | // long long element_size); 948 | 949 | Cluster(); // EndOfStream 950 | ~Cluster(); 951 | 952 | bool EOS() const; 953 | 954 | long long GetTimeCode() const; // absolute, but not scaled 955 | long long GetTime() const; // absolute, and scaled (nanosecond units) 956 | long long GetFirstTime() const; // time (ns) of first (earliest) block 957 | long long GetLastTime() const; // time (ns) of last (latest) block 958 | 959 | long GetFirst(const BlockEntry*&) const; 960 | long GetLast(const BlockEntry*&) const; 961 | long GetNext(const BlockEntry* curr, const BlockEntry*& next) const; 962 | 963 | const BlockEntry* GetEntry(const Track*, long long ns = -1) const; 964 | const BlockEntry* GetEntry(const CuePoint&, 965 | const CuePoint::TrackPosition&) const; 966 | // const BlockEntry* GetMaxKey(const VideoTrack*) const; 967 | 968 | // static bool HasBlockEntries(const Segment*, long long); 969 | 970 | static long HasBlockEntries(const Segment*, long long idoff, long long& pos, 971 | long& size); 972 | 973 | long GetEntryCount() const; 974 | 975 | long Load(long long& pos, long& size) const; 976 | 977 | long Parse(long long& pos, long& size) const; 978 | long GetEntry(long index, const mkvparser::BlockEntry*&) const; 979 | 980 | protected: 981 | Cluster(Segment*, long index, long long element_start); 982 | // long long element_size); 983 | 984 | public: 985 | const long long m_element_start; 986 | long long GetPosition() const; // offset relative to segment 987 | 988 | long GetIndex() const; 989 | long long GetElementSize() const; 990 | // long long GetPayloadSize() const; 991 | 992 | // long long Unparsed() const; 993 | 994 | private: 995 | long m_index; 996 | mutable long long m_pos; 997 | // mutable long long m_size; 998 | mutable long long m_element_size; 999 | mutable long long m_timecode; 1000 | mutable BlockEntry** m_entries; 1001 | mutable long m_entries_size; 1002 | mutable long m_entries_count; 1003 | 1004 | long ParseSimpleBlock(long long, long long&, long&); 1005 | long ParseBlockGroup(long long, long long&, long&); 1006 | 1007 | long CreateBlock(long long id, long long pos, long long size, 1008 | long long discard_padding); 1009 | long CreateBlockGroup(long long start_offset, long long size, 1010 | long long discard_padding); 1011 | long CreateSimpleBlock(long long, long long); 1012 | }; 1013 | 1014 | class Segment { 1015 | friend class Cues; 1016 | friend class Track; 1017 | friend class VideoTrack; 1018 | 1019 | Segment(const Segment&); 1020 | Segment& operator=(const Segment&); 1021 | 1022 | private: 1023 | Segment(IMkvReader*, long long elem_start, 1024 | // long long elem_size, 1025 | long long pos, long long size); 1026 | 1027 | public: 1028 | IMkvReader* const m_pReader; 1029 | const long long m_element_start; 1030 | // const long long m_element_size; 1031 | const long long m_start; // posn of segment payload 1032 | const long long m_size; // size of segment payload 1033 | Cluster m_eos; // TODO: make private? 1034 | 1035 | static long long CreateInstance(IMkvReader*, long long, Segment*&); 1036 | ~Segment(); 1037 | 1038 | long Load(); // loads headers and all clusters 1039 | 1040 | // for incremental loading 1041 | // long long Unparsed() const; 1042 | bool DoneParsing() const; 1043 | long long ParseHeaders(); // stops when first cluster is found 1044 | // long FindNextCluster(long long& pos, long& size) const; 1045 | long LoadCluster(long long& pos, long& size); // load one cluster 1046 | long LoadCluster(); 1047 | 1048 | long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos, 1049 | long& size); 1050 | 1051 | const SeekHead* GetSeekHead() const; 1052 | const Tracks* GetTracks() const; 1053 | const SegmentInfo* GetInfo() const; 1054 | const Cues* GetCues() const; 1055 | const Chapters* GetChapters() const; 1056 | const Tags* GetTags() const; 1057 | 1058 | long long GetDuration() const; 1059 | 1060 | unsigned long GetCount() const; 1061 | const Cluster* GetFirst() const; 1062 | const Cluster* GetLast() const; 1063 | const Cluster* GetNext(const Cluster*); 1064 | 1065 | const Cluster* FindCluster(long long time_nanoseconds) const; 1066 | // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const; 1067 | 1068 | const Cluster* FindOrPreloadCluster(long long pos); 1069 | 1070 | long ParseCues(long long cues_off, // offset relative to start of segment 1071 | long long& parse_pos, long& parse_len); 1072 | 1073 | private: 1074 | long long m_pos; // absolute file posn; what has been consumed so far 1075 | Cluster* m_pUnknownSize; 1076 | 1077 | SeekHead* m_pSeekHead; 1078 | SegmentInfo* m_pInfo; 1079 | Tracks* m_pTracks; 1080 | Cues* m_pCues; 1081 | Chapters* m_pChapters; 1082 | Tags* m_pTags; 1083 | Cluster** m_clusters; 1084 | long m_clusterCount; // number of entries for which m_index >= 0 1085 | long m_clusterPreloadCount; // number of entries for which m_index < 0 1086 | long m_clusterSize; // array size 1087 | 1088 | long DoLoadCluster(long long&, long&); 1089 | long DoLoadClusterUnknownSize(long long&, long&); 1090 | long DoParseNext(const Cluster*&, long long&, long&); 1091 | 1092 | bool AppendCluster(Cluster*); 1093 | bool PreloadCluster(Cluster*, ptrdiff_t); 1094 | 1095 | // void ParseSeekHead(long long pos, long long size); 1096 | // void ParseSeekEntry(long long pos, long long size); 1097 | // void ParseCues(long long); 1098 | 1099 | const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&); 1100 | }; 1101 | 1102 | } // namespace mkvparser 1103 | 1104 | inline long mkvparser::Segment::LoadCluster() { 1105 | long long pos; 1106 | long size; 1107 | 1108 | return LoadCluster(pos, size); 1109 | } 1110 | 1111 | #endif // MKVPARSER_MKVPARSER_H_ 1112 | --------------------------------------------------------------------------------