Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "TheoraDecoder.h"
8 : #include "TimeUnits.h"
9 : #include "XiphExtradata.h"
10 : #include "gfx2DGlue.h"
11 : #include "mozilla/PodOperations.h"
12 : #include "nsError.h"
13 :
14 : #include <algorithm>
15 :
16 : #undef LOG
17 : #define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("TheoraDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
18 :
19 : namespace mozilla {
20 :
21 : using namespace gfx;
22 : using namespace layers;
23 :
24 : extern LazyLogModule gMediaDecoderLog;
25 :
26 0 : ogg_packet InitTheoraPacket(const unsigned char* aData, size_t aLength,
27 : bool aBOS, bool aEOS,
28 : int64_t aGranulepos, int64_t aPacketNo)
29 : {
30 : ogg_packet packet;
31 0 : packet.packet = const_cast<unsigned char*>(aData);
32 0 : packet.bytes = aLength;
33 0 : packet.b_o_s = aBOS;
34 0 : packet.e_o_s = aEOS;
35 0 : packet.granulepos = aGranulepos;
36 0 : packet.packetno = aPacketNo;
37 0 : return packet;
38 : }
39 :
40 0 : TheoraDecoder::TheoraDecoder(const CreateDecoderParams& aParams)
41 : : mImageAllocator(aParams.mKnowsCompositor)
42 0 : , mImageContainer(aParams.mImageContainer)
43 0 : , mTaskQueue(aParams.mTaskQueue)
44 : , mTheoraSetupInfo(nullptr)
45 : , mTheoraDecoderContext(nullptr)
46 : , mPacketCount(0)
47 0 : , mInfo(aParams.VideoConfig())
48 : {
49 0 : MOZ_COUNT_CTOR(TheoraDecoder);
50 0 : }
51 :
52 0 : TheoraDecoder::~TheoraDecoder()
53 : {
54 0 : MOZ_COUNT_DTOR(TheoraDecoder);
55 0 : th_setup_free(mTheoraSetupInfo);
56 0 : th_comment_clear(&mTheoraComment);
57 0 : th_info_clear(&mTheoraInfo);
58 0 : }
59 :
60 : RefPtr<ShutdownPromise>
61 0 : TheoraDecoder::Shutdown()
62 : {
63 0 : RefPtr<TheoraDecoder> self = this;
64 0 : return InvokeAsync(mTaskQueue, __func__, [self, this]() {
65 0 : if (mTheoraDecoderContext) {
66 0 : th_decode_free(mTheoraDecoderContext);
67 0 : mTheoraDecoderContext = nullptr;
68 : }
69 0 : return ShutdownPromise::CreateAndResolve(true, __func__);
70 0 : });
71 : }
72 :
73 : RefPtr<MediaDataDecoder::InitPromise>
74 0 : TheoraDecoder::Init()
75 : {
76 0 : th_comment_init(&mTheoraComment);
77 0 : th_info_init(&mTheoraInfo);
78 :
79 0 : nsTArray<unsigned char*> headers;
80 0 : nsTArray<size_t> headerLens;
81 0 : if (!XiphExtradataToHeaders(headers, headerLens,
82 0 : mInfo.mCodecSpecificConfig->Elements(),
83 0 : mInfo.mCodecSpecificConfig->Length())) {
84 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
85 : }
86 0 : for (size_t i = 0; i < headers.Length(); i++) {
87 0 : if (NS_FAILED(DoDecodeHeader(headers[i], headerLens[i]))) {
88 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
89 0 : __func__);
90 : }
91 : }
92 0 : if (mPacketCount != 3) {
93 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
94 : }
95 :
96 0 : mTheoraDecoderContext = th_decode_alloc(&mTheoraInfo, mTheoraSetupInfo);
97 0 : if (mTheoraDecoderContext) {
98 0 : return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
99 : } else {
100 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
101 : }
102 :
103 : }
104 :
105 : RefPtr<MediaDataDecoder::FlushPromise>
106 0 : TheoraDecoder::Flush()
107 : {
108 0 : return InvokeAsync(mTaskQueue, __func__, []() {
109 : return FlushPromise::CreateAndResolve(true, __func__);
110 0 : });
111 : }
112 :
113 : nsresult
114 0 : TheoraDecoder::DoDecodeHeader(const unsigned char* aData, size_t aLength)
115 : {
116 0 : bool bos = mPacketCount == 0;
117 : ogg_packet pkt =
118 0 : InitTheoraPacket(aData, aLength, bos, false, 0, mPacketCount++);
119 :
120 0 : int r = th_decode_headerin(&mTheoraInfo,
121 : &mTheoraComment,
122 : &mTheoraSetupInfo,
123 0 : &pkt);
124 0 : return r > 0 ? NS_OK : NS_ERROR_FAILURE;
125 : }
126 :
127 : RefPtr<MediaDataDecoder::DecodePromise>
128 0 : TheoraDecoder::ProcessDecode(MediaRawData* aSample)
129 : {
130 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
131 :
132 0 : const unsigned char* aData = aSample->Data();
133 0 : size_t aLength = aSample->Size();
134 :
135 0 : bool bos = mPacketCount == 0;
136 : ogg_packet pkt = InitTheoraPacket(
137 : aData, aLength, bos, false,
138 0 : aSample->mTimecode.ToMicroseconds(), mPacketCount++);
139 :
140 0 : int ret = th_decode_packetin(mTheoraDecoderContext, &pkt, nullptr);
141 0 : if (ret == 0 || ret == TH_DUPFRAME) {
142 : th_ycbcr_buffer ycbcr;
143 0 : th_decode_ycbcr_out(mTheoraDecoderContext, ycbcr);
144 :
145 0 : int hdec = !(mTheoraInfo.pixel_fmt & 1);
146 0 : int vdec = !(mTheoraInfo.pixel_fmt & 2);
147 :
148 0 : VideoData::YCbCrBuffer b;
149 0 : b.mPlanes[0].mData = ycbcr[0].data;
150 0 : b.mPlanes[0].mStride = ycbcr[0].stride;
151 0 : b.mPlanes[0].mHeight = mTheoraInfo.frame_height;
152 0 : b.mPlanes[0].mWidth = mTheoraInfo.frame_width;
153 0 : b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
154 :
155 0 : b.mPlanes[1].mData = ycbcr[1].data;
156 0 : b.mPlanes[1].mStride = ycbcr[1].stride;
157 0 : b.mPlanes[1].mHeight = mTheoraInfo.frame_height >> vdec;
158 0 : b.mPlanes[1].mWidth = mTheoraInfo.frame_width >> hdec;
159 0 : b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
160 :
161 0 : b.mPlanes[2].mData = ycbcr[2].data;
162 0 : b.mPlanes[2].mStride = ycbcr[2].stride;
163 0 : b.mPlanes[2].mHeight = mTheoraInfo.frame_height >> vdec;
164 0 : b.mPlanes[2].mWidth = mTheoraInfo.frame_width >> hdec;
165 0 : b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
166 :
167 : IntRect pictureArea(mTheoraInfo.pic_x, mTheoraInfo.pic_y,
168 0 : mTheoraInfo.pic_width, mTheoraInfo.pic_height);
169 :
170 0 : VideoInfo info;
171 0 : info.mDisplay = mInfo.mDisplay;
172 : RefPtr<VideoData> v =
173 0 : VideoData::CreateAndCopyData(info,
174 : mImageContainer,
175 : aSample->mOffset,
176 : aSample->mTime,
177 : aSample->mDuration,
178 : b,
179 0 : aSample->mKeyframe,
180 : aSample->mTimecode,
181 0 : mInfo.ScaledImageRect(mTheoraInfo.frame_width,
182 0 : mTheoraInfo.frame_height),
183 0 : mImageAllocator);
184 0 : if (!v) {
185 0 : LOG(
186 : "Image allocation error source %ux%u display %ux%u picture %ux%u",
187 : mTheoraInfo.frame_width,
188 : mTheoraInfo.frame_height,
189 : mInfo.mDisplay.width,
190 : mInfo.mDisplay.height,
191 : mInfo.mImage.width,
192 : mInfo.mImage.height);
193 : return DecodePromise::CreateAndReject(
194 0 : MediaResult(NS_ERROR_OUT_OF_MEMORY,
195 0 : RESULT_DETAIL("Insufficient memory")),
196 0 : __func__);
197 : }
198 0 : return DecodePromise::CreateAndResolve(DecodedData{v}, __func__);
199 : }
200 0 : LOG("Theora Decode error: %d", ret);
201 : return DecodePromise::CreateAndReject(
202 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
203 0 : RESULT_DETAIL("Theora decode error:%d", ret)),
204 0 : __func__);
205 : }
206 :
207 : RefPtr<MediaDataDecoder::DecodePromise>
208 0 : TheoraDecoder::Decode(MediaRawData* aSample)
209 : {
210 : return InvokeAsync<MediaRawData*>(mTaskQueue, this, __func__,
211 0 : &TheoraDecoder::ProcessDecode, aSample);
212 : }
213 :
214 : RefPtr<MediaDataDecoder::DecodePromise>
215 0 : TheoraDecoder::Drain()
216 : {
217 0 : return InvokeAsync(mTaskQueue, __func__, [] {
218 0 : return DecodePromise::CreateAndResolve(DecodedData(), __func__);
219 0 : });
220 : }
221 :
222 : /* static */
223 : bool
224 0 : TheoraDecoder::IsTheora(const nsACString& aMimeType)
225 : {
226 0 : return aMimeType.EqualsLiteral("video/theora");
227 : }
228 :
229 : } // namespace mozilla
230 : #undef LOG
|