Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
jami-daemon
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
savoirfairelinux
jami-daemon
Commits
03780c7b
Commit
03780c7b
authored
6 years ago
by
Philippe Gorley
Committed by
Adrien Béraud
6 years ago
Browse files
Options
Downloads
Patches
Plain Diff
recorder: add doc and reorganize code
Change-Id: I68c9b2b2ea3863653f86fcabd17ad4128f0ebcfd
parent
dd6199ac
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/media/media_recorder.cpp
+52
-77
52 additions, 77 deletions
src/media/media_recorder.cpp
src/media/media_recorder.h
+49
-24
49 additions, 24 deletions
src/media/media_recorder.h
with
101 additions
and
101 deletions
src/media/media_recorder.cpp
+
52
−
77
View file @
03780c7b
...
...
@@ -39,6 +39,7 @@
namespace
ring
{
// Replaces every occurrence of @from with @to in @str
static
std
::
string
replaceAll
(
const
std
::
string
&
str
,
const
std
::
string
&
from
,
const
std
::
string
&
to
)
{
...
...
@@ -61,10 +62,16 @@ MediaRecorder::MediaRecorder()
MediaRecorder
::~
MediaRecorder
()
{
if
(
isRecording_
)
flush
();
if
(
loop_
.
isRunning
())
loop_
.
join
();
if
(
isRecording_
)
flush
();
}
bool
MediaRecorder
::
isRecording
()
const
{
return
isRecording_
;
}
std
::
string
...
...
@@ -82,24 +89,17 @@ MediaRecorder::audioOnly(bool audioOnly)
audioOnly_
=
audioOnly
;
}
void
MediaRecorder
::
setMetadata
(
const
std
::
string
&
title
,
const
std
::
string
&
desc
)
{
title_
=
title
;
description_
=
desc
;
}
void
MediaRecorder
::
setPath
(
const
std
::
string
&
path
)
{
if
(
!
path
.
empty
())
path_
=
path
;
}
bool
MediaRecorder
::
isRecording
()
const
void
MediaRecorder
::
setMetadata
(
const
std
::
string
&
title
,
const
std
::
string
&
desc
)
{
return
isRecording_
;
title_
=
title
;
description_
=
desc
;
}
int
...
...
@@ -136,28 +136,13 @@ MediaRecorder::stopRecording()
flush
();
emitSignal
<
DRing
::
CallSignal
::
RecordPlaybackStopped
>
(
getPath
());
}
resetToDefaults
();
}
void
MediaRecorder
::
update
(
Observable
<
std
::
shared_ptr
<
AudioFrame
>>*
ob
,
const
std
::
shared_ptr
<
AudioFrame
>&
a
)
{
std
::
string
name
;
if
(
dynamic_cast
<
AudioReceiveThread
*>
(
ob
))
name
=
"a:remote"
;
else
// ob is of type AudioInput*
name
=
"a:local"
;
recordData
(
a
->
pointer
(),
streams_
[
name
]);
}
void
MediaRecorder
::
update
(
Observable
<
std
::
shared_ptr
<
VideoFrame
>>*
ob
,
const
std
::
shared_ptr
<
VideoFrame
>&
v
)
{
std
::
string
name
;
if
(
dynamic_cast
<
video
::
VideoReceiveThread
*>
(
ob
))
name
=
"v:remote"
;
else
// ob is of type VideoInput*
name
=
"v:local"
;
recordData
(
v
->
pointer
(),
streams_
[
name
]);
streams_
.
clear
();
videoIdx_
=
audioIdx_
=
-
1
;
isRecording_
=
false
;
audioOnly_
=
false
;
videoFilter_
.
reset
();
audioFilter_
.
reset
();
encoder_
.
reset
();
}
int
...
...
@@ -181,18 +166,32 @@ MediaRecorder::addStream(const MediaStream& ms)
}
}
void
MediaRecorder
::
update
(
Observable
<
std
::
shared_ptr
<
AudioFrame
>>*
ob
,
const
std
::
shared_ptr
<
AudioFrame
>&
a
)
{
std
::
string
name
;
if
(
dynamic_cast
<
AudioReceiveThread
*>
(
ob
))
name
=
"a:remote"
;
else
// ob is of type AudioInput*
name
=
"a:local"
;
recordData
(
a
->
pointer
(),
streams_
[
name
]);
}
void
MediaRecorder
::
update
(
Observable
<
std
::
shared_ptr
<
VideoFrame
>>*
ob
,
const
std
::
shared_ptr
<
VideoFrame
>&
v
)
{
std
::
string
name
;
if
(
dynamic_cast
<
video
::
VideoReceiveThread
*>
(
ob
))
name
=
"v:remote"
;
else
// ob is of type VideoInput*
name
=
"v:local"
;
recordData
(
v
->
pointer
(),
streams_
[
name
]);
}
int
MediaRecorder
::
recordData
(
AVFrame
*
frame
,
const
MediaStream
&
ms
)
{
// recorder may be recording, but not ready for the first frames
if
(
!
isRecording_
)
return
0
;
if
(
!
isReady_
&&
streams_
.
find
(
ms
.
name
)
==
streams_
.
end
())
if
(
addStream
(
ms
)
<
0
)
return
-
1
;
if
(
!
isReady_
||
!
loop_
.
isRunning
())
// check again in case initRecord was called
if
(
!
isRecording_
||
!
loop_
.
isRunning
())
return
0
;
const
auto
&
params
=
streams_
.
at
(
ms
.
name
);
...
...
@@ -288,29 +287,18 @@ MediaRecorder::initRecord()
}
}
// ready to start recording if audio stream index and video stream index are valid
bool
audioIsReady
=
hasAudio_
&&
audioIdx_
>=
0
;
bool
videoIsReady
=
!
audioOnly_
&&
hasVideo_
&&
videoIdx_
>=
0
;
isReady_
=
audioIsReady
&&
videoIsReady
;
if
(
isReady_
)
{
if
(
!
loop_
.
isRunning
())
loop_
.
start
();
std
::
unique_ptr
<
MediaIOHandle
>
ioHandle
;
try
{
std
::
unique_ptr
<
MediaIOHandle
>
ioHandle
;
encoder_
->
setIOContext
(
ioHandle
);
encoder_
->
startIO
();
}
catch
(
const
MediaEncoderException
&
e
)
{
RING_ERR
()
<<
"Could not start recorder: "
<<
e
.
what
();
return
-
1
;
}
RING_DBG
()
<<
"Recording initialized"
;
loop_
.
start
();
return
0
;
}
else
{
RING_ERR
()
<<
"Failed to initialize recorder"
;
return
-
1
;
}
}
MediaStream
...
...
@@ -508,19 +496,6 @@ MediaRecorder::flush()
return
0
;
}
void
MediaRecorder
::
resetToDefaults
()
{
streams_
.
clear
();
videoIdx_
=
audioIdx_
=
-
1
;
isRecording_
=
false
;
isReady_
=
false
;
audioOnly_
=
false
;
videoFilter_
.
reset
();
audioFilter_
.
reset
();
encoder_
.
reset
();
}
void
MediaRecorder
::
process
()
{
...
...
This diff is collapsed.
Click to expand it.
src/media/media_recorder.h
+
49
−
24
View file @
03780c7b
...
...
@@ -53,69 +53,92 @@ public:
MediaRecorder
();
~
MediaRecorder
();
std
::
string
getPath
()
const
;
/**
* Gets whether or not the recorder is active.
*/
bool
isRecording
()
const
;
void
setPath
(
const
std
::
string
&
path
);
/**
* Get file path of file to be recorded. Same path as sent to @setPath, but with
* file extension appended.
*
* NOTE @audioOnly must be called to have the right extension.
*/
std
::
string
getPath
()
const
;
/**
* Sets whether or not output file will have audio. Determines the extension of the
* output file (.ogg or .webm).
*/
void
audioOnly
(
bool
audioOnly
);
// replaces %TIMESTAMP with time at start of recording
// default title: "Conversation at %Y-%m-%d %H:%M:%S"
// default description: "Recorded with Jami https://jami.net"
/**
* Sets output file path.
*
* NOTE An empty path will put the output file in the working directory.
*/
void
setPath
(
const
std
::
string
&
path
);
/**
* Sets title and description metadata for the file. Uses default if either is empty.
* Default title is "Conversation at %Y-%m-%d %H:%M:%S".
* Default description is "Recorded with Jami https://jami.net".
*
* NOTE replaces %TIMESTAMP with time at start of recording
*/
void
setMetadata
(
const
std
::
string
&
title
,
const
std
::
string
&
desc
);
bool
isRecording
()
const
;
/**
*/
int
addStream
(
const
MediaStream
&
ms
);
/**
* Starts the record. Streams must have been added using Observable::attach and
* @addStream.
*/
int
startRecording
();
/**
* Stops the record. Streams must be removed using Observable::detach afterwards.
*/
void
stopRecording
();
int
addStream
(
const
MediaStream
&
ms
);
/* Observer methods
*/
/**
* Updates the recorder with an audio or video frame.
*/
void
update
(
Observable
<
std
::
shared_ptr
<
AudioFrame
>>*
ob
,
const
std
::
shared_ptr
<
AudioFrame
>&
a
)
override
;
void
update
(
Observable
<
std
::
shared_ptr
<
VideoFrame
>>*
ob
,
const
std
::
shared_ptr
<
VideoFrame
>&
v
)
override
;
private:
NON_COPYABLE
(
MediaRecorder
);
int
recordData
(
AVFrame
*
frame
,
const
MediaStream
&
ms
);
int
flush
();
int
initRecord
();
MediaStream
setupVideoOutput
();
std
::
string
buildVideoFilter
(
const
std
::
vector
<
MediaStream
>&
peers
,
const
MediaStream
&
local
)
const
;
MediaStream
setupAudioOutput
();
std
::
string
buildAudioFilter
(
const
std
::
vector
<
MediaStream
>&
peers
,
const
MediaStream
&
local
)
const
;
void
emptyFilterGraph
();
int
sendToEncoder
(
AVFrame
*
frame
,
int
streamIdx
);
int
flush
();
void
resetToDefaults
();
// clear saved data for next recording
std
::
unique_ptr
<
MediaEncoder
>
encoder_
;
std
::
unique_ptr
<
MediaFilter
>
videoFilter_
;
std
::
unique_ptr
<
MediaFilter
>
audioFilter_
;
std
::
mutex
mutex_
;
// protect against concurrent file writes
std
::
map
<
std
::
string
,
const
MediaStream
>
streams_
;
std
::
string
path_
;
std
::
tm
startTime_
;
std
::
string
title_
;
std
::
string
description_
;
std
::
string
path_
;
// NOTE do not use dir_ or filename_, use path_ instead
std
::
string
dir_
;
std
::
string
filename_
;
std
::
unique_ptr
<
MediaEncoder
>
encoder_
;
std
::
unique_ptr
<
MediaFilter
>
videoFilter_
;
std
::
unique_ptr
<
MediaFilter
>
audioFilter_
;
bool
hasAudio_
{
false
};
bool
hasVideo_
{
false
};
int
videoIdx_
=
-
1
;
int
audioIdx_
=
-
1
;
bool
isRecording_
=
false
;
bool
isReady_
=
false
;
bool
audioOnly_
=
false
;
struct
RecordFrame
{
...
...
@@ -131,6 +154,8 @@ private:
};
InterruptedThreadLoop
loop_
;
void
process
();
void
emptyFilterGraph
();
int
sendToEncoder
(
AVFrame
*
frame
,
int
streamIdx
);
std
::
mutex
qLock_
;
std
::
deque
<
RecordFrame
>
frames_
;
};
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment