@@ -17,8 +17,7 @@ namespace ffmpeg
17
17
}
18
18
19
19
Writer::Writer ()
20
- : _state(WriterStateNone),
21
- _av_format (nullptr )
20
+ : _state(WriterStateNone)
22
21
{
23
22
}
24
23
@@ -75,8 +74,6 @@ namespace ffmpeg
75
74
76
75
bool Writer::SetUrl (const ov::String url, const ov::String format)
77
76
{
78
- std::unique_lock<std::mutex> mlock (_lock);
79
-
80
77
if (!url || url.IsEmpty () == true )
81
78
{
82
79
logte (" Destination url is empty" );
@@ -87,14 +84,17 @@ namespace ffmpeg
87
84
_format = format;
88
85
89
86
// Create output context
90
- int error = avformat_alloc_output_context2 (&_av_format, nullptr , (_format != nullptr ) ? _format.CStr () : nullptr , _url.CStr ());
87
+ AVFormatContext *av_format = nullptr ;
88
+ int error = avformat_alloc_output_context2 (&av_format, nullptr , (_format != nullptr ) ? _format.CStr () : nullptr , _url.CStr ());
91
89
if (error < 0 )
92
90
{
93
91
logte (" Could not create output context. error(%s), url(%s)" , ffmpeg::Conv::AVErrorToString (error).CStr (), _url.CStr ());
94
92
95
93
return false ;
96
94
}
97
95
96
+ SetAVFormatContext (av_format);
97
+
98
98
return true ;
99
99
}
100
100
@@ -113,18 +113,23 @@ namespace ffmpeg
113
113
return _timestamp_mode;
114
114
}
115
115
116
- bool Writer::AddTrack (std::shared_ptr<MediaTrack> media_track)
116
+ bool Writer::AddTrack (const std::shared_ptr<MediaTrack> & media_track)
117
117
{
118
- std::unique_lock<std::mutex> mlock (_lock);
119
-
120
118
if (media_track->GetCodecId () == cmn::MediaCodecId::Opus &&
121
119
media_track->GetDecoderConfigurationRecord () == nullptr )
122
120
{
123
121
auto opus_config { std::make_shared<OpusSpecificConfig>(media_track->GetChannel ().GetCounts (), media_track->GetSampleRate ()) };
124
122
media_track->SetDecoderConfigurationRecord (opus_config);
125
123
}
126
124
127
- AVStream *av_stream = avformat_new_stream (_av_format, nullptr );
125
+ auto av_format = GetAVFormatContext ();
126
+ if (av_format == nullptr )
127
+ {
128
+ logte (" AVFormatContext is null" );
129
+ return false ;
130
+ }
131
+
132
+ AVStream *av_stream = avformat_new_stream (av_format.get (), nullptr );
128
133
if (!av_stream)
129
134
{
130
135
logte (" Could not allocate stream" );
@@ -140,36 +145,46 @@ namespace ffmpeg
140
145
return false ;
141
146
}
142
147
143
- // MediaTrackID -> AVStream, MediaTrack
144
- _track_map[media_track->GetId ()] = std::make_pair (av_stream, media_track);
148
+ {
149
+ std::lock_guard<std::shared_mutex> mlock (_track_map_lock);
150
+ // MediaTrackID -> AVStream, MediaTrack
151
+ // AVStream doesn't need to be released. It will be released when AVFormatContext is released.
152
+ std::shared_ptr<AVStream> av_stream_ptr (av_stream);
153
+ _track_map[media_track->GetId ()] = std::make_pair (av_stream_ptr, media_track);
154
+ }
145
155
146
156
return true ;
147
157
}
148
158
149
159
bool Writer::Start ()
150
160
{
151
- std::unique_lock<std::mutex> mlock (_lock);
152
-
153
161
SetState (WriterStateConnecting);
154
162
155
163
_start_time = -1LL ;
156
164
_need_to_flush = false ;
157
165
_need_to_close = false ;
158
166
159
- _av_format->flush_packets = 1 ;
167
+ auto av_format = GetAVFormatContext ();
168
+ if (av_format == nullptr )
169
+ {
170
+ logte (" AVFormatContext is null" );
171
+ return false ;
172
+ }
173
+
174
+ av_format->flush_packets = 1 ;
160
175
161
176
// Set Interrupt Callback
162
177
_interrupt_cb = {InterruptCallback, this };
163
178
164
- if (!(_av_format ->oformat ->flags & AVFMT_NOFILE))
179
+ if (!(av_format ->oformat ->flags & AVFMT_NOFILE))
165
180
{
166
181
_last_packet_sent_time = std::chrono::high_resolution_clock::now ();
167
- int error = avio_open2 (&_av_format ->pb , _av_format ->url , AVIO_FLAG_WRITE, &_interrupt_cb, nullptr );
182
+ int error = avio_open2 (&av_format ->pb , av_format ->url , AVIO_FLAG_WRITE, &_interrupt_cb, nullptr );
168
183
if (error < 0 )
169
184
{
170
185
SetState (WriterStateError);
171
186
172
- logte (" Error opening file. error(%s), url(%s)" , ffmpeg::Conv::AVErrorToString (error).CStr (), _av_format ->url );
187
+ logte (" Error opening file. error(%s), url(%s)" , ffmpeg::Conv::AVErrorToString (error).CStr (), av_format ->url );
173
188
174
189
return false ;
175
190
}
@@ -183,7 +198,7 @@ namespace ffmpeg
183
198
AVDictionary *format_options = nullptr ;
184
199
av_dict_set_int (&format_options, " use_editlist" , 0 , 0 );
185
200
186
- if (avformat_write_header (_av_format , &format_options) < 0 )
201
+ if (avformat_write_header (av_format. get () , &format_options) < 0 )
187
202
{
188
203
SetState (WriterStateError);
189
204
@@ -193,7 +208,7 @@ namespace ffmpeg
193
208
}
194
209
195
210
// Set output format name
196
- _output_format_name = _av_format ->oformat ->name ;
211
+ _output_format_name = av_format ->oformat ->name ;
197
212
198
213
_need_to_flush = true ;
199
214
@@ -204,48 +219,47 @@ namespace ffmpeg
204
219
205
220
bool Writer::Stop ()
206
221
{
207
- std::unique_lock<std::mutex> mlock (_lock);
208
-
209
- if (_av_format)
222
+ auto av_format = GetAVFormatContext ();
223
+ if (av_format)
210
224
{
225
+ auto av_format_ptr = av_format.get ();
211
226
// Write trailer
212
227
if (_need_to_flush)
213
228
{
214
- av_write_trailer (_av_format );
229
+ av_write_trailer (av_format_ptr );
215
230
}
216
231
217
232
// Close file
218
233
if (_need_to_close)
219
234
{
220
- avformat_close_input (&_av_format );
235
+ avformat_close_input (&av_format_ptr );
221
236
}
222
237
223
238
// Free context
224
- avformat_free_context (_av_format);
225
- _av_format = nullptr ;
239
+ avformat_free_context (av_format_ptr);
240
+
241
+ ReleaseAVFormatContext ();
226
242
}
227
243
228
244
SetState (WriterStateClosed);
229
245
230
246
return true ;
231
247
}
232
248
233
- bool Writer::SendPacket (std::shared_ptr<MediaPacket> packet)
249
+ bool Writer::SendPacket (const std::shared_ptr<MediaPacket> & packet)
234
250
{
235
251
if (!packet)
236
252
{
237
253
return false ;
238
254
}
239
255
240
256
// Find MediaTrack and AVSTream from MediaTrackID
241
- auto it = _track_map. find (packet->GetTrackId ());
242
- if (it == _track_map. end () )
257
+ auto [av_stream, media_track] = GetTrack (packet->GetTrackId ());
258
+ if (av_stream == nullptr || media_track == nullptr )
243
259
{
244
- // If there is no track in the map, the packet is dropped. this is not an error.
245
- return true ;
260
+ logtw ( " Could not find track. track_id:%d " , packet-> GetTrackId ());
261
+ return false ;
246
262
}
247
- auto av_stream = it->second .first ;
248
- auto media_track = it->second .second ;
249
263
250
264
// Start Timestamp
251
265
if (_start_time == -1LL )
@@ -368,8 +382,8 @@ namespace ffmpeg
368
382
// Passthrough
369
383
}
370
384
371
- std::unique_lock<std::mutex> mlock (_lock );
372
- if (!_av_format )
385
+ auto av_format = GetAVFormatContext ( );
386
+ if (!av_format )
373
387
{
374
388
av_packet_unref (&av_packet);
375
389
SetState (WriterStateError);
@@ -378,7 +392,7 @@ namespace ffmpeg
378
392
379
393
_last_packet_sent_time = std::chrono::high_resolution_clock::now ();
380
394
381
- int error = av_interleaved_write_frame (_av_format , &av_packet);
395
+ int error = av_interleaved_write_frame (av_format. get () , &av_packet);
382
396
if (error != 0 )
383
397
{
384
398
SetState (WriterStateError);
@@ -395,4 +409,38 @@ namespace ffmpeg
395
409
{
396
410
return _last_packet_sent_time;
397
411
}
412
+
413
+ std::shared_ptr<AVFormatContext> Writer::GetAVFormatContext () const
414
+ {
415
+ std::shared_lock<std::shared_mutex> mlock (_av_format_lock);
416
+ return _av_format_ptr;
417
+ }
418
+
419
+ void Writer::SetAVFormatContext (AVFormatContext *av_format)
420
+ {
421
+ std::lock_guard<std::shared_mutex> mlock (_av_format_lock);
422
+ _av_format_ptr.reset (av_format);
423
+ }
424
+
425
+ void Writer::ReleaseAVFormatContext ()
426
+ {
427
+ std::lock_guard<std::shared_mutex> mlock (_av_format_lock);
428
+ if (_av_format_ptr)
429
+ {
430
+ avformat_free_context (_av_format_ptr.get ());
431
+ _av_format_ptr.reset ();
432
+ ;
433
+ }
434
+ }
435
+
436
+ std::pair<std::shared_ptr<AVStream>, std::shared_ptr<MediaTrack>> Writer::GetTrack (int32_t track_id) const
437
+ {
438
+ std::shared_lock<std::shared_mutex> mlock (_track_map_lock);
439
+ auto it = _track_map.find (track_id);
440
+ if (it == _track_map.end ())
441
+ {
442
+ return std::make_pair (nullptr , nullptr );
443
+ }
444
+ return it->second ;
445
+ }
398
446
} // namespace ffmpeg
0 commit comments