0031501: Foundation Classes, Message_Printer - remove theToPutEndl argument -- use...
[occt.git] / src / Media / Media_CodecContext.cxx
CommitLineData
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>
31extern "C"
32{
33 #include <libavformat/avformat.h>
34};
35#include <Standard_WarningsRestore.hxx>
36#endif
37
38IMPLEMENT_STANDARD_RTTIEXT(Media_CodecContext, Standard_Transient)
39
40// =======================================================================
41// function : Media_CodecContext
42// purpose :
43// =======================================================================
44Media_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// =======================================================================
62Media_CodecContext::~Media_CodecContext()
63{
64 Close();
65}
66
67// =======================================================================
68// function : Init
69// purpose :
70// =======================================================================
71bool 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// =======================================================================
86bool 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// =======================================================================
169void 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// =======================================================================
183void 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// =======================================================================
197int 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// =======================================================================
210int 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// =======================================================================
223bool 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// =======================================================================
233bool 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// =======================================================================
256bool 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}