|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "ISOMediaWriter.h" |
|
7 #include "ISOControl.h" |
|
8 #include "ISOMediaBoxes.h" |
|
9 #include "ISOTrackMetadata.h" |
|
10 #include "nsThreadUtils.h" |
|
11 #include "MediaEncoder.h" |
|
12 #include "VideoUtils.h" |
|
13 |
|
14 #undef LOG |
|
15 #ifdef MOZ_WIDGET_GONK |
|
16 #include <android/log.h> |
|
17 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args); |
|
18 #else |
|
19 #define LOG(args, ...) |
|
20 #endif |
|
21 |
|
22 namespace mozilla { |
|
23 |
|
24 const static uint32_t FRAG_DURATION = 2 * USECS_PER_S; // microsecond per unit |
|
25 |
|
26 ISOMediaWriter::ISOMediaWriter(uint32_t aType, uint32_t aHint) |
|
27 : ContainerWriter() |
|
28 , mState(MUXING_HEAD) |
|
29 , mBlobReady(false) |
|
30 , mType(0) |
|
31 { |
|
32 if (aType & CREATE_AUDIO_TRACK) { |
|
33 mType |= Audio_Track; |
|
34 } |
|
35 if (aType & CREATE_VIDEO_TRACK) { |
|
36 mType |= Video_Track; |
|
37 } |
|
38 mControl = new ISOControl(aHint); |
|
39 MOZ_COUNT_CTOR(ISOMediaWriter); |
|
40 } |
|
41 |
|
42 ISOMediaWriter::~ISOMediaWriter() |
|
43 { |
|
44 MOZ_COUNT_DTOR(ISOMediaWriter); |
|
45 } |
|
46 |
|
47 nsresult |
|
48 ISOMediaWriter::RunState() |
|
49 { |
|
50 nsresult rv; |
|
51 switch (mState) { |
|
52 case MUXING_HEAD: |
|
53 { |
|
54 rv = mControl->GenerateFtyp(); |
|
55 NS_ENSURE_SUCCESS(rv, rv); |
|
56 rv = mControl->GenerateMoov(); |
|
57 NS_ENSURE_SUCCESS(rv, rv); |
|
58 mState = MUXING_FRAG; |
|
59 break; |
|
60 } |
|
61 case MUXING_FRAG: |
|
62 { |
|
63 rv = mControl->GenerateMoof(mType); |
|
64 NS_ENSURE_SUCCESS(rv, rv); |
|
65 |
|
66 bool EOS; |
|
67 if (ReadyToRunState(EOS) && EOS) { |
|
68 mState = MUXING_DONE; |
|
69 } |
|
70 break; |
|
71 } |
|
72 case MUXING_DONE: |
|
73 { |
|
74 break; |
|
75 } |
|
76 } |
|
77 mBlobReady = true; |
|
78 return NS_OK; |
|
79 } |
|
80 |
|
81 nsresult |
|
82 ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData, |
|
83 uint32_t aFlags) |
|
84 { |
|
85 // Muxing complete, it doesn't allowed to reentry again. |
|
86 if (mState == MUXING_DONE) { |
|
87 MOZ_ASSERT(false); |
|
88 return NS_ERROR_FAILURE; |
|
89 } |
|
90 |
|
91 FragmentBuffer* frag = nullptr; |
|
92 uint32_t len = aData.GetEncodedFrames().Length(); |
|
93 |
|
94 if (!len) { |
|
95 // no frame? why bother to WriteEncodedTrack |
|
96 return NS_OK; |
|
97 } |
|
98 for (uint32_t i = 0; i < len; i++) { |
|
99 nsRefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]); |
|
100 EncodedFrame::FrameType type = frame->GetFrameType(); |
|
101 if (type == EncodedFrame::AAC_AUDIO_FRAME || |
|
102 type == EncodedFrame::AAC_CSD || |
|
103 type == EncodedFrame::AMR_AUDIO_FRAME || |
|
104 type == EncodedFrame::AMR_AUDIO_CSD) { |
|
105 frag = mAudioFragmentBuffer; |
|
106 } else if (type == EncodedFrame::AVC_I_FRAME || |
|
107 type == EncodedFrame::AVC_P_FRAME || |
|
108 type == EncodedFrame::AVC_B_FRAME || |
|
109 type == EncodedFrame::AVC_CSD) { |
|
110 frag = mVideoFragmentBuffer; |
|
111 } else { |
|
112 MOZ_ASSERT(0); |
|
113 return NS_ERROR_FAILURE; |
|
114 } |
|
115 |
|
116 frag->AddFrame(frame); |
|
117 } |
|
118 |
|
119 // Encoder should send CSD (codec specific data) frame before sending the |
|
120 // audio/video frames. When CSD data is ready, it is sufficient to generate a |
|
121 // moov data. If encoder doesn't send CSD yet, muxer needs to wait before |
|
122 // generating anything. |
|
123 if (mType & Audio_Track && (!mAudioFragmentBuffer || |
|
124 !mAudioFragmentBuffer->HasCSD())) { |
|
125 return NS_OK; |
|
126 } |
|
127 if (mType & Video_Track && (!mVideoFragmentBuffer || |
|
128 !mVideoFragmentBuffer->HasCSD())) { |
|
129 return NS_OK; |
|
130 } |
|
131 |
|
132 // Only one FrameType in EncodedFrameContainer so it doesn't need to be |
|
133 // inside the for-loop. |
|
134 if (frag && (aFlags & END_OF_STREAM)) { |
|
135 frag->SetEndOfStream(); |
|
136 } |
|
137 |
|
138 nsresult rv; |
|
139 bool EOS; |
|
140 if (ReadyToRunState(EOS)) { |
|
141 // TODO: |
|
142 // The MediaEncoder doesn't use nsRunnable, so thread will be |
|
143 // stocked on that part and the new added nsRunnable won't get to run |
|
144 // before MediaEncoder completing. Before MediaEncoder change, it needs |
|
145 // to call RunState directly. |
|
146 // https://bugzilla.mozilla.org/show_bug.cgi?id=950429 |
|
147 rv = RunState(); |
|
148 NS_ENSURE_SUCCESS(rv, rv); |
|
149 } |
|
150 |
|
151 return NS_OK; |
|
152 } |
|
153 |
|
154 bool |
|
155 ISOMediaWriter::ReadyToRunState(bool& aEOS) |
|
156 { |
|
157 aEOS = false; |
|
158 bool bReadyToMux = true; |
|
159 if ((mType & Audio_Track) && (mType & Video_Track)) { |
|
160 if (!mAudioFragmentBuffer->HasEnoughData()) { |
|
161 bReadyToMux = false; |
|
162 } |
|
163 if (!mVideoFragmentBuffer->HasEnoughData()) { |
|
164 bReadyToMux = false; |
|
165 } |
|
166 |
|
167 if (mAudioFragmentBuffer->EOS() && mVideoFragmentBuffer->EOS()) { |
|
168 aEOS = true; |
|
169 bReadyToMux = true; |
|
170 } |
|
171 } else if (mType == Audio_Track) { |
|
172 if (!mAudioFragmentBuffer->HasEnoughData()) { |
|
173 bReadyToMux = false; |
|
174 } |
|
175 if (mAudioFragmentBuffer->EOS()) { |
|
176 aEOS = true; |
|
177 bReadyToMux = true; |
|
178 } |
|
179 } else if (mType == Video_Track) { |
|
180 if (!mVideoFragmentBuffer->HasEnoughData()) { |
|
181 bReadyToMux = false; |
|
182 } |
|
183 if (mVideoFragmentBuffer->EOS()) { |
|
184 aEOS = true; |
|
185 bReadyToMux = true; |
|
186 } |
|
187 } |
|
188 |
|
189 return bReadyToMux; |
|
190 } |
|
191 |
|
192 nsresult |
|
193 ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs, |
|
194 uint32_t aFlags) |
|
195 { |
|
196 if (mBlobReady) { |
|
197 if (mState == MUXING_DONE) { |
|
198 mIsWritingComplete = true; |
|
199 } |
|
200 mBlobReady = false; |
|
201 return mControl->GetBufs(aOutputBufs); |
|
202 } |
|
203 return NS_OK; |
|
204 } |
|
205 |
|
206 nsresult |
|
207 ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata) |
|
208 { |
|
209 if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC || |
|
210 aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) { |
|
211 mControl->SetMetadata(aMetadata); |
|
212 mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION); |
|
213 mControl->SetFragment(mAudioFragmentBuffer); |
|
214 return NS_OK; |
|
215 } |
|
216 if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) { |
|
217 mControl->SetMetadata(aMetadata); |
|
218 mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION); |
|
219 mControl->SetFragment(mVideoFragmentBuffer); |
|
220 return NS_OK; |
|
221 } |
|
222 |
|
223 return NS_ERROR_FAILURE; |
|
224 } |
|
225 |
|
226 } // namespace mozilla |