Commit ba56966d authored by Simon Morlat's avatar Simon Morlat
Browse files

recording video only works, but crashes in append mode

parent 84672db3
......@@ -1109,11 +1109,9 @@ MS2_PUBLIC void video_stream_close_remote_play(VideoStream *stream);
* Open a recorder to record the video coming from remote end into a mkv file.
* This must be done before the stream is started.
**/
MS2_PUBLIC int video_stream_remote_record_open(VideoStream *stream, const char *filename);
MS2_PUBLIC MSFilter * video_stream_open_remote_record(VideoStream *stream, const char *filename);
MS2_PUBLIC int video_stream_remote_record_start(VideoStream *stream);
MS2_PUBLIC int video_stream_remote_record_stop(VideoStream *stream);
MS2_PUBLIC void video_stream_close_remote_record(VideoStream *stream);
/**
* Small API to display a local preview window.
......
......@@ -203,7 +203,7 @@ typedef enum _MSRecorderState MSRecorderState;
/** Interface definitions for video decoders */
/** Event definitions for video decoders */
#define MS_VIDEO_DECODER_DECODING_ERRORS \
MS_FILTER_EVENT_NO_ARG(MSFilterVideoDecoderInterface,0)
#define MS_VIDEO_DECODER_FIRST_IMAGE_DECODED \
......@@ -214,6 +214,8 @@ typedef enum _MSRecorderState MSRecorderState;
MS_FILTER_EVENT(MSFilterVideoDecoderInterface, 3, MSVideoCodecSLI)
#define MS_VIDEO_DECODER_SEND_RPSI \
MS_FILTER_EVENT(MSFilterVideoDecoderInterface, 4, MSVideoCodecRPSI)
/** Method definitions for video decoders */
#define MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION \
MS_FILTER_METHOD_NO_ARG(MSFilterVideoDecoderInterface, 5)
#define MS_VIDEO_DECODER_ENABLE_AVPF \
......@@ -224,6 +226,8 @@ typedef enum _MSRecorderState MSRecorderState;
MS_FILTER_METHOD(MSFilterVideoDecoderInterface, 8, bool_t)
#define MS_VIDEO_DECODER_RECOVERED_FROM_ERRORS \
MS_FILTER_EVENT_NO_ARG(MSFilterVideoDecoderInterface, 9)
/** Interface definitions for video capture */
#define MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION \
......
......@@ -34,6 +34,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define bool_t ambigous use ms_bool_t or matroska_bool_t
static int recorder_close(MSFilter *f, void *arg);
/*********************************************************************************************
* Module interface *
*********************************************************************************************/
......@@ -1576,10 +1578,12 @@ typedef struct {
MSRecorderState state;
Muxer muxer;
TimeCorrector timeCorrector;
ms_bool_t needKeyFrame;
uint64_t lastFirTime;
const MSFmtDescriptor **inputDescsList;
Module **modulesList;
TimeLoopCanceler **timeLoopCancelers;
TimeLoopCanceler **timeLoopCancelers;
ms_bool_t needKeyFrame;
ms_bool_t tracksInitialized;
} MKVRecorder;
static void recorder_init(MSFilter *f) {
......@@ -1596,7 +1600,7 @@ static void recorder_init(MSFilter *f) {
obj->inputDescsList = (const MSFmtDescriptor **)ms_new0(const MSFmtDescriptor *, f->desc->ninputs);
obj->modulesList = (Module **)ms_new0(Module *, f->desc->ninputs);
obj->timeLoopCancelers = (TimeLoopCanceler **)ms_new0(TimeLoopCanceler *, f->desc->ninputs);
obj->lastFirTime = (uint64_t) -1;
time_corrector_init(&obj->timeCorrector, f->desc->ninputs);
f->data=obj;
......@@ -1606,10 +1610,10 @@ static void recorder_uninit(MSFilter *f){
MKVRecorder *obj = (MKVRecorder *)f->data;
int i;
muxer_uninit(&obj->muxer);
if(obj->state != MSRecorderClosed) {
matroska_close_file(&obj->file);
recorder_close(f, NULL);
}
muxer_uninit(&obj->muxer);
matroska_uninit(&obj->file);
for(i=0; i < f->desc->ninputs; i++) {
if(obj->modulesList[i] != NULL) module_free(obj->modulesList[i]);
......@@ -1674,7 +1678,6 @@ static inline void changeClockRate(mblk_t *buffer, unsigned int oldClockRate, un
static int recorder_open_file(MSFilter *f, void *arg) {
MKVRecorder *obj = (MKVRecorder *)f->data;
const char *filename = (const char *)arg;
int i;
ms_filter_lock(f);
if(obj->state != MSRecorderClosed) {
......@@ -1687,45 +1690,13 @@ static int recorder_open_file(MSFilter *f, void *arg) {
obj->openMode = MKV_OPEN_CREATE;
}
ms_message("MKVRecoreder: opening file %s in %s mode", filename, obj->openMode == MKV_OPEN_APPEND ? "append" : "create");
ms_message("MKVRecorder: opening file %s in %s mode", filename, obj->openMode == MKV_OPEN_APPEND ? "append" : "create");
if(matroska_open_file(&obj->file, filename, obj->openMode) != 0) {
ms_error("MKVRecorder: fail to open %s", filename);
goto fail;
}
if(obj->openMode == MKV_OPEN_CREATE) {
matroska_set_doctype_version(&obj->file, MKV_DOCTYPE_VERSION, MKV_DOCTYPE_READ_VERSION);
matroska_write_ebml_header(&obj->file);
matroska_start_segment(&obj->file);
matroska_write_zeros(&obj->file, 1024);
matroska_mark_segment_info_position(&obj->file);
matroska_write_zeros(&obj->file, 1024);
for(i=0; i < f->desc->ninputs; i++) {
if(obj->inputDescsList[i] != NULL) {
obj->modulesList[i] = module_new(obj->inputDescsList[i]->encoding);
module_set(obj->modulesList[i], obj->inputDescsList[i]);
matroska_add_track(&obj->file, i+1, module_get_codec_id(obj->modulesList[i]));
obj->timeLoopCancelers[i] = time_loop_canceler_new();
}
}
obj->duration = 0;
} else {
for(i=0; i < f->desc->ninputs; i++) {
if(obj->inputDescsList[i] != NULL) {
const uint8_t *data = NULL;
size_t length;
obj->modulesList[i] = module_new(obj->inputDescsList[i]->encoding);
module_set(obj->modulesList[i], obj->inputDescsList[i]);
if(matroska_get_codec_private(&obj->file, i+1, &data, &length) == 0) {
module_load_private_data(obj->modulesList[i], data, length);
}
obj->timeLoopCancelers[i] = time_loop_canceler_new();
}
}
obj->duration = matroska_get_duration(&obj->file);
}
time_corrector_set_origin(&obj->timeCorrector, obj->duration);
obj->state = MSRecorderPaused;
ms_filter_unlock(f);
return 0;
......@@ -1737,11 +1708,49 @@ static int recorder_open_file(MSFilter *f, void *arg) {
static int recorder_start(MSFilter *f, void *arg) {
MKVRecorder *obj = (MKVRecorder *)f->data;
int i;
ms_filter_lock(f);
if(obj->state == MSRecorderClosed) {
ms_error("MKVRecorder: fail to start recording. The file has not been opened");
goto fail;
}
if (!obj->tracksInitialized){
if(obj->openMode == MKV_OPEN_CREATE) {
matroska_set_doctype_version(&obj->file, MKV_DOCTYPE_VERSION, MKV_DOCTYPE_READ_VERSION);
matroska_write_ebml_header(&obj->file);
matroska_start_segment(&obj->file);
matroska_write_zeros(&obj->file, 1024);
matroska_mark_segment_info_position(&obj->file);
matroska_write_zeros(&obj->file, 1024);
for(i=0; i < f->desc->ninputs; i++) {
if(obj->inputDescsList[i] != NULL) {
obj->modulesList[i] = module_new(obj->inputDescsList[i]->encoding);
module_set(obj->modulesList[i], obj->inputDescsList[i]);
matroska_add_track(&obj->file, i+1, module_get_codec_id(obj->modulesList[i]));
obj->timeLoopCancelers[i] = time_loop_canceler_new();
}
}
obj->duration = 0;
} else {
for(i=0; i < f->desc->ninputs; i++) {
if(obj->inputDescsList[i] != NULL) {
const uint8_t *data = NULL;
size_t length;
obj->modulesList[i] = module_new(obj->inputDescsList[i]->encoding);
module_set(obj->modulesList[i], obj->inputDescsList[i]);
if(matroska_get_codec_private(&obj->file, i+1, &data, &length) == 0) {
module_load_private_data(obj->modulesList[i], data, length);
}
obj->timeLoopCancelers[i] = time_loop_canceler_new();
}
}
obj->duration = matroska_get_duration(&obj->file);
}
time_corrector_set_origin(&obj->timeCorrector, obj->duration);
obj->tracksInitialized = TRUE;
}
obj->state = MSRecorderRunning;
obj->needKeyFrame = TRUE;
ms_message("MKVRecorder: recording successfully started");
......@@ -1777,6 +1786,13 @@ static int recorder_stop(MSFilter *f, void *arg) {
return -1;
}
static void recorder_request_fir(MSFilter *f, MKVRecorder *obj){
if (obj->lastFirTime == -1 || obj->lastFirTime +2000 < f->ticker->time){
obj->lastFirTime = f->ticker->time;
ms_filter_notify_no_arg(f, MS_RECORDER_NEEDS_FIR);
}
}
static void recorder_process(MSFilter *f) {
MKVRecorder *obj = f->data;
int i;
......@@ -1808,13 +1824,14 @@ static void recorder_process(MSFilter *f) {
if(obj->inputDescsList[i]->type == MSVideo && obj->needKeyFrame) {
while((buffer = ms_queue_get(&frames_ms)) != NULL) {
if(module_is_key_frame(obj->modulesList[i], buffer)) {
if (module_is_key_frame(obj->modulesList[i], buffer)) {
break;
} else {
recorder_request_fir(f, obj);
freemsg(buffer);
}
}
if(buffer != NULL) {
if (buffer != NULL) {
do {
time_corrector_proceed(&obj->timeCorrector, buffer, i);
muxer_put_buffer(&obj->muxer, buffer, i);
......@@ -1903,6 +1920,7 @@ static int recorder_close(MSFilter *f, void *arg) {
} else {
ms_warning("MKVRecorder: no file has been opened");
}
obj->tracksInitialized = FALSE;
ms_filter_unlock(f);
return 0;
......@@ -1919,23 +1937,26 @@ static int recorder_set_input_fmt(MSFilter *f, void *arg) {
}
if(data->state != MSRecorderClosed) {
if(pinFmt->fmt == NULL) {
ms_error("MKVRecorder: could not disable pin #%d. The file is opened", pinFmt->pin);
goto fail;
}
if(data->inputDescsList[pinFmt->pin] == NULL) {
ms_error("MKVRecorder: could not set pin #%d video size. That pin is not enabled", pinFmt->pin);
goto fail;
}
if(pinFmt->fmt->type != MSVideo ||
if (data->tracksInitialized){
if(pinFmt->fmt == NULL) {
ms_error("MKVRecorder: could not disable pin #%d. The file is opened", pinFmt->pin);
goto fail;
}
if(data->inputDescsList[pinFmt->pin] == NULL) {
ms_error("MKVRecorder: could not set pin #%d video size. That pin is not enabled", pinFmt->pin);
goto fail;
}
if(pinFmt->fmt->type != MSVideo ||
strcmp(pinFmt->fmt->encoding, data->inputDescsList[pinFmt->pin]->encoding) != 0 ||
pinFmt->fmt->rate != data->inputDescsList[pinFmt->pin]->rate) {
ms_error("MKVRecorder: could not set pin #%d video size. The specified format is not compatible with the current format. current={%s}, new={%s}",
ms_error("MKVRecorder: could not set pin #%d video size. The specified format is not compatible with the current format. current={%s}, new={%s}",
pinFmt->pin,
ms_fmt_descriptor_to_string(data->inputDescsList[pinFmt->pin]),
ms_fmt_descriptor_to_string(pinFmt->fmt));
goto fail;
goto fail;
}
}
data->inputDescsList[pinFmt->pin] = pinFmt->fmt;
ms_message("MKVRecorder: pin #%d video size set on %dx%d", pinFmt->pin, pinFmt->fmt->vsize.width, pinFmt->fmt->vsize.height);
} else {
......
......@@ -648,7 +648,7 @@ static void video_stream_payload_type_changed(RtpSession *session, void *data){
int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam){
MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
if (cam==NULL){
if (cam == NULL){
cam = ms_web_cam_manager_get_default_cam( ms_web_cam_manager_get() );
}
io.input.type = MSResourceCamera;
......@@ -659,6 +659,16 @@ int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *re
return video_stream_start_from_io(stream, profile, rem_rtp_ip, rem_rtp_port, rem_rtcp_ip, rem_rtcp_port, payload, &io);
}
static void recorder_handle_event(void *userdata, MSFilter *recorder, unsigned int event, void *event_arg){
VideoStream *stream = (VideoStream*) userdata;
switch (event){
case MS_RECORDER_NEEDS_FIR:
ms_message("Request sending of FIR on videostream [%p]", stream);
video_stream_send_fir(stream);
break;
}
}
int video_stream_start_from_io(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
const char *rem_rtcp_ip, int rem_rtcp_port, int payload, const MSMediaStreamIO *io) {
MSWebCam *cam = NULL;
......@@ -715,6 +725,8 @@ int video_stream_start_from_io(VideoStream *stream, RtpProfile *profile, const c
ms_filter_destroy(stream->recorder_output);
}
stream->recorder_output = recorder;
ms_filter_add_notify_callback(recorder, recorder_handle_event, stream, TRUE);
if (io->output.file) video_stream_open_remote_record(stream, io->output.file);
break;
default:
/*will just display in all other cases*/
......@@ -1063,6 +1075,15 @@ static int video_stream_start_with_source_and_output(VideoStream *stream, RtpPro
stream->ms.voidsink = ms_filter_new(MS_VOID_SINK_ID);
ms_filter_link(stream->ms.rtprecv, 0, stream->ms.voidsink, 0);
}
/*start the video recorder if it was opened previously*/
if (stream->recorder_output && ms_filter_implements_interface(stream->recorder_output, MSFilterRecorderInterface)){
MSRecorderState state = MSRecorderClosed;
ms_filter_call_method(stream->recorder_output, MS_RECORDER_GET_STATE, &state);
if (state == MSRecorderPaused){
ms_filter_call_method_noarg(stream->recorder_output, MS_RECORDER_START);
}
}
/* create the ticker */
if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
......@@ -1357,6 +1378,15 @@ static MSFilter* _video_stream_stop(VideoStream * stream, bool_t keep_source)
rtp_session_set_rtcp_xr_media_callbacks(stream->ms.sessions.rtp_session, NULL);
rtp_session_signal_disconnect_by_callback(stream->ms.sessions.rtp_session,"payload_type_changed",(RtpCallback)video_stream_payload_type_changed);
/*Automatically the video recorder if it was opened previously*/
if (stream->recorder_output && ms_filter_implements_interface(stream->recorder_output, MSFilterRecorderInterface)){
MSRecorderState state = MSRecorderClosed;
ms_filter_call_method(stream->recorder_output, MS_RECORDER_GET_STATE, &state);
if (state != MSRecorderClosed){
ms_filter_call_method_noarg(stream->recorder_output, MS_RECORDER_CLOSE);
}
}
if( keep_source ){
source = stream->source;
stream->source = NULL; // will prevent video_stream_free() from destroying the source
......@@ -1628,17 +1658,29 @@ void video_stream_close_remote_play(VideoStream *stream){
}
}
int video_stream_remote_record_open(VideoStream *stream, const char *filename){
/*stub, to be implemented.*/
return -1;
}
int video_stream_remote_record_start(VideoStream *stream){
/*stub, to be implemented.*/
return -1;
MSFilter *video_stream_open_remote_record(VideoStream *stream, const char *filename){
MSFilter *recorder = stream->recorder_output;
if (!recorder || !ms_filter_implements_interface(recorder, MSFilterRecorderInterface)){
ms_error("video_stream_open_remote_play(): the stream is not using a recorder.");
return NULL;
}
if (ms_filter_call_method(recorder, MS_RECORDER_OPEN, (void*)filename)!=0){
return NULL;
}
return recorder;
}
int video_stream_remote_record_stop(VideoStream *stream){
/*stub, to be implemented.*/
return -1;
void video_stream_close_remote_record(VideoStream *stream){
MSFilter *recorder = stream->recorder_output;
MSRecorderState state = MSRecorderClosed;
if (!recorder || !ms_filter_implements_interface(recorder, MSFilterRecorderInterface)){
ms_error("video_stream_close_remote_record(): the stream is not using a recorder.");
return ;
}
ms_filter_call_method(recorder, MS_RECORDER_GET_STATE, &state);
if (state != MSRecorderClosed){
ms_filter_call_method_noarg(recorder, MS_RECORDER_CLOSE);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment