98e6c6d1 |
1 | // Created by: Kirill GAVRILOV |
2 | // Copyright (c) 2019 OPEN CASCADE SAS |
3 | // |
4 | // This file is part of Open CASCADE Technology software library. |
5 | // |
6 | // This library is free software; you can redistribute it and/or modify it under |
7 | // the terms of the GNU Lesser General Public License version 2.1 as published |
8 | // by the Free Software Foundation, with special exception defined in the file |
9 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
10 | // distribution for complete text of the license and disclaimer of any warranty. |
11 | // |
12 | // Alternatively, this file may be used under the terms of Open CASCADE |
13 | // commercial license or contractual agreement. |
14 | |
15 | // activate some C99 macros like UINT64_C in "stdint.h" which used by FFmpeg |
16 | #ifndef __STDC_CONSTANT_MACROS |
17 | #define __STDC_CONSTANT_MACROS |
18 | #endif |
19 | |
20 | #include <Media_CodecContext.hxx> |
21 | |
22 | #include <Media_Frame.hxx> |
23 | #include <Media_FormatContext.hxx> |
24 | |
25 | #include <Message.hxx> |
26 | #include <Message_Messenger.hxx> |
27 | #include <OSD_Parallel.hxx> |
28 | |
29 | #ifdef HAVE_FFMPEG |
30 | #include <Standard_WarningsDisable.hxx> |
31 | extern "C" |
32 | { |
33 | #include <libavformat/avformat.h> |
34 | }; |
35 | #include <Standard_WarningsRestore.hxx> |
36 | #endif |
37 | |
38 | IMPLEMENT_STANDARD_RTTIEXT(Media_CodecContext, Standard_Transient) |
39 | |
40 | // ======================================================================= |
41 | // function : Media_CodecContext |
42 | // purpose : |
43 | // ======================================================================= |
44 | Media_CodecContext::Media_CodecContext() |
45 | : myCodecCtx (NULL), |
46 | myCodec (NULL), |
47 | myPtsStartBase (0.0), |
48 | myPtsStartStream(0.0), |
49 | myTimeBase (1.0), |
50 | myStreamIndex (0), |
51 | myPixelAspectRatio (1.0f) |
52 | { |
53 | #ifdef HAVE_FFMPEG |
54 | myCodecCtx = avcodec_alloc_context3 (NULL); |
55 | #endif |
56 | } |
57 | |
58 | // ======================================================================= |
59 | // function : ~Media_CodecContext |
60 | // purpose : |
61 | // ======================================================================= |
62 | Media_CodecContext::~Media_CodecContext() |
63 | { |
64 | Close(); |
65 | } |
66 | |
67 | // ======================================================================= |
68 | // function : Init |
69 | // purpose : |
70 | // ======================================================================= |
71 | bool Media_CodecContext::Init (const AVStream& theStream, |
72 | double thePtsStartBase, |
73 | int theNbThreads) |
74 | { |
75 | #ifdef HAVE_FFMPEG |
76 | return Init (theStream, thePtsStartBase, theNbThreads, AV_CODEC_ID_NONE); |
77 | #else |
78 | return Init (theStream, thePtsStartBase, theNbThreads, 0); |
79 | #endif |
80 | } |
81 | |
82 | // ======================================================================= |
83 | // function : Init |
84 | // purpose : |
85 | // ======================================================================= |
86 | bool Media_CodecContext::Init (const AVStream& theStream, |
87 | double thePtsStartBase, |
88 | int theNbThreads, |
89 | int theCodecId) |
90 | { |
91 | #ifdef HAVE_FFMPEG |
92 | myStreamIndex = theStream.index; |
93 | if (avcodec_parameters_to_context (myCodecCtx, theStream.codecpar) < 0) |
94 | { |
a87b1b37 |
95 | Message::SendFail ("Internal error: unable to copy codec parameters"); |
98e6c6d1 |
96 | Close(); |
97 | return false; |
98 | } |
99 | |
100 | myTimeBase = av_q2d (theStream.time_base); |
101 | myPtsStartBase = thePtsStartBase; |
102 | myPtsStartStream = Media_FormatContext::StreamUnitsToSeconds (theStream, theStream.start_time); |
103 | |
104 | const AVCodecID aCodecId = theCodecId != AV_CODEC_ID_NONE ? (AVCodecID )theCodecId : theStream.codecpar->codec_id; |
105 | myCodec = avcodec_find_decoder (aCodecId); |
106 | if (myCodec == NULL) |
107 | { |
a87b1b37 |
108 | Message::Send ("FFmpeg: unable to find decoder", Message_Fail); |
98e6c6d1 |
109 | Close(); |
110 | return false; |
111 | } |
112 | |
113 | myCodecCtx->codec_id = aCodecId; |
114 | AVDictionary* anOpts = NULL; |
115 | av_dict_set (&anOpts, "refcounted_frames", "1", 0); |
116 | if (theStream.codecpar->codec_type == AVMEDIA_TYPE_VIDEO) |
117 | { |
118 | myCodecCtx->thread_count = theNbThreads <= -1 ? OSD_Parallel::NbLogicalProcessors() : theNbThreads; |
119 | } |
120 | |
121 | if (avcodec_open2 (myCodecCtx, myCodec, &anOpts) < 0) |
122 | { |
a87b1b37 |
123 | Message::SendFail ("FFmpeg: unable to open decoder"); |
98e6c6d1 |
124 | Close(); |
125 | return false; |
126 | } |
127 | |
128 | myPixelAspectRatio = 1.0f; |
129 | if (theStream.sample_aspect_ratio.num && av_cmp_q(theStream.sample_aspect_ratio, myCodecCtx->sample_aspect_ratio)) |
130 | { |
131 | myPixelAspectRatio = float(theStream.sample_aspect_ratio.num) / float(theStream.sample_aspect_ratio.den); |
132 | } |
133 | else |
134 | { |
135 | if (myCodecCtx->sample_aspect_ratio.num == 0 |
136 | || myCodecCtx->sample_aspect_ratio.den == 0) |
137 | { |
138 | myPixelAspectRatio = 1.0f; |
139 | } |
140 | else |
141 | { |
142 | myPixelAspectRatio = float(myCodecCtx->sample_aspect_ratio.num) / float(myCodecCtx->sample_aspect_ratio.den); |
143 | } |
144 | } |
145 | |
146 | if (theStream.codecpar->codec_type == AVMEDIA_TYPE_VIDEO |
147 | && (myCodecCtx->width <= 0 |
148 | || myCodecCtx->height <= 0)) |
149 | { |
a87b1b37 |
150 | Message::SendFail ("FFmpeg: video stream has invalid dimensions"); |
98e6c6d1 |
151 | Close(); |
152 | return false; |
153 | } |
154 | |
155 | return true; |
156 | #else |
157 | (void )&theStream; |
158 | (void )thePtsStartBase; |
159 | (void )theNbThreads; |
160 | (void )theCodecId; |
161 | return false; |
162 | #endif |
163 | } |
164 | |
165 | // ======================================================================= |
166 | // function : Close |
167 | // purpose : |
168 | // ======================================================================= |
169 | void Media_CodecContext::Close() |
170 | { |
171 | if (myCodecCtx != NULL) |
172 | { |
173 | #ifdef HAVE_FFMPEG |
174 | avcodec_free_context (&myCodecCtx); |
175 | #endif |
176 | } |
177 | } |
178 | |
179 | // ======================================================================= |
180 | // function : Flush |
181 | // purpose : |
182 | // ======================================================================= |
183 | void Media_CodecContext::Flush() |
184 | { |
185 | if (myCodecCtx != NULL) |
186 | { |
187 | #ifdef HAVE_FFMPEG |
188 | avcodec_flush_buffers (myCodecCtx); |
189 | #endif |
190 | } |
191 | } |
192 | |
193 | // ======================================================================= |
194 | // function : SizeX |
195 | // purpose : |
196 | // ======================================================================= |
197 | int Media_CodecContext::SizeX() const |
198 | { |
199 | #ifdef HAVE_FFMPEG |
200 | return (myCodecCtx != NULL) ? myCodecCtx->width : 0; |
201 | #else |
202 | return 0; |
203 | #endif |
204 | } |
205 | |
206 | // ======================================================================= |
207 | // function : SizeY |
208 | // purpose : |
209 | // ======================================================================= |
210 | int Media_CodecContext::SizeY() const |
211 | { |
212 | #ifdef HAVE_FFMPEG |
213 | return (myCodecCtx != NULL) ? myCodecCtx->height : 0; |
214 | #else |
215 | return 0; |
216 | #endif |
217 | } |
218 | |
219 | // ======================================================================= |
220 | // function : CanProcessPacket |
221 | // purpose : |
222 | // ======================================================================= |
223 | bool Media_CodecContext::CanProcessPacket (const Handle(Media_Packet)& thePacket) const |
224 | { |
225 | return !thePacket.IsNull() |
226 | && myStreamIndex == thePacket->StreamIndex(); |
227 | } |
228 | |
229 | // ======================================================================= |
230 | // function : SendPacket |
231 | // purpose : |
232 | // ======================================================================= |
233 | bool Media_CodecContext::SendPacket (const Handle(Media_Packet)& thePacket) |
234 | { |
235 | if (!CanProcessPacket (thePacket)) |
236 | { |
237 | return false; |
238 | } |
239 | |
240 | #ifdef HAVE_FFMPEG |
241 | const int aRes = avcodec_send_packet (myCodecCtx, thePacket->Packet()); |
242 | if (aRes < 0 && aRes != AVERROR_EOF) |
243 | { |
244 | return false; |
245 | } |
246 | return true; |
247 | #else |
248 | return false; |
249 | #endif |
250 | } |
251 | |
252 | // ======================================================================= |
253 | // function : ReceiveFrame |
254 | // purpose : |
255 | // ======================================================================= |
256 | bool Media_CodecContext::ReceiveFrame (const Handle(Media_Frame)& theFrame) |
257 | { |
258 | if (theFrame.IsNull()) |
259 | { |
260 | return false; |
261 | } |
262 | |
263 | #ifdef HAVE_FFMPEG |
264 | const int aRes2 = avcodec_receive_frame (myCodecCtx, theFrame->ChangeFrame()); |
265 | if (aRes2 < 0) |
266 | { |
267 | return false; |
268 | } |
269 | |
270 | const int64_t aPacketPts = theFrame->BestEffortTimestamp() != AV_NOPTS_VALUE ? theFrame->BestEffortTimestamp() : 0; |
271 | const double aFramePts = double(aPacketPts) * myTimeBase - myPtsStartBase; |
272 | theFrame->SetPts (aFramePts); |
273 | return true; |
274 | #else |
275 | return false; |
276 | #endif |
277 | } |