GNOME Bugzilla – Bug 736924
iOS 8 issue mp4 recording not playable
Last modified: 2015-08-13 16:02:54 UTC
We are using the following code to create mp4's on IOS using Gstreamer, this works fine on iOS 7.x and below but on IOS we cannot play partially recorded files, or files that haven't finished recording. this is a severe error. We can confirm these files will not play outside IOS using vlc either. i // pipeline def NSString *source = [NSString stringWithFormat:@"udpsrc address=%@ port=%@ name=zeVideoRecordSource", self.host, port]; source = [source stringByAppendingString:@" ! application/x-rtp, media=video, clock-rate=90000, encoding-name=H264, payload=96"]; //source = [source stringByAppendingString:@" ! rtpjitterbuffer"]; // this may or may not be needed source = [source stringByAppendingString:@" ! rtph264depay ! queue"]; //source = [source stringByAppendingString:@" ! rtph264depay"]; source = [source stringByAppendingString:@" ! h264parse"]; source = [source stringByAppendingString:@" ! video/x-h264, framerate=24/1"]; source = [source stringByAppendingString:@" ! queue name=zeQueue"]; and the processing bin code -(NSString *) buildNewProcessingBin { // TODO: element cleanup in fail state? NSString *errMsg = nil; // create initial elements NSString *processingBinName = [NSString stringWithFormat:@"processing_bin_%u", _processingBinCount]; GstElement *processingBin = gst_bin_new([processingBinName cStringUsingEncoding:NSUTF8StringEncoding]); GstElement *muxer = gst_element_factory_make("mp4mux", NULL); GstElement *writer = gst_element_factory_make("filesink", NULL); if (processingBin == NULL || muxer == NULL || writer == NULL) { errMsg = @"Could not create at least one element in processing bin"; LOG_ERROR(GSTR,errMsg, nil); } // set up the file writer, creating base directory if necessary if (errMsg == nil) { // formatter for storage of video file NSString * streamName = [[DateManager sharedInstance] recordingTimeStampAsString]; streamName = [streamName stringByAppendingFormat:@"_%@", [self getStreamName]]; NSString *file = [streamName stringByAppendingFormat:@"_%u", _processingBinCount]; NSString *ext = @"mp4"; NSString *fileWithExt = [file stringByAppendingPathExtension:ext]; NSString *path = [self getStreamStorageLocation]; NSString *pathWithFile = [path stringByAppendingPathComponent:fileWithExt]; LOG_DEBUG(GSTR,@"file storage location = %@", pathWithFile); // remove old recordings so we don't exceed our storage limit NSUInteger maxFiles = 110; [self removeStaleRecordingsInStreamStorageLocation:maxFiles doRemove:YES]; // set filesink properties NSError *dirCreateError = nil; BOOL didCreateDirectory = NO; BOOL isDirectory = NO; if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) { [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:&dirCreateError]; didCreateDirectory = YES; } if (dirCreateError != nil) { errMsg = [NSString stringWithFormat:@"Unable to create directory at path %@", path]; LOG_ERROR(GSTR,errMsg, nil); LOG_ERROR(GSTR,@"Directory create error: %@", dirCreateError.description); } else if (!didCreateDirectory && !isDirectory) { errMsg = [NSString stringWithFormat:@"File already exists at directory path %@", path]; LOG_ERROR(GSTR,errMsg, nil); } else { g_object_set(writer, "location", [pathWithFile cStringUsingEncoding:NSUTF8StringEncoding], NULL); } } // add and link elements in processing bin if (errMsg == nil) { gst_bin_add_many(GST_BIN(processingBin), muxer, writer, NULL); if (!gst_element_link_many(muxer, writer, NULL)) { errMsg = @"Could not link muxing and writing elements together in processing bin"; LOG_ERROR(GSTR,errMsg, nil); } } // add a ghost pad, but first we need a template to request the muxer sink pad GstPadTemplate *muxSinkPadTemplate = NULL; if (errMsg == nil) { muxSinkPadTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(muxer), "video_%u"); if (muxSinkPadTemplate == NULL) { errMsg = @"Could not get muxer sink pad template in processing bin"; LOG_ERROR(GSTR,errMsg, nil); } } // now request the muxer sink pad GstPad *lastSinkPad = NULL; if (errMsg == nil) { GstPad *sinkPad = gst_element_request_pad(muxer, muxSinkPadTemplate, NULL, NULL); GstPad *ghostPad = gst_ghost_pad_new("sink", sinkPad); if (sinkPad == NULL || ghostPad == NULL) { errMsg = @"Could not get muxer sink pad or create ghost pad in processing bin"; LOG_ERROR(GSTR,errMsg, nil); } else { gst_element_add_pad(processingBin, ghostPad); lastSinkPad = gst_element_get_static_pad(writer, "sink"); } gst_object_unref(muxSinkPadTemplate); } // save a reference to our new processing bin, and last sink pad in the processing bin if (errMsg == nil) { if (lastSinkPad == NULL) { errMsg = @"Could not get last sink pad in processing bin"; LOG_ERROR(GSTR,errMsg, nil); } else { _processingElement = processingBin; _processingElementLastSinkPad = lastSinkPad; _processingBinCount++; } } return errMsg; }
How do you shut down your pipeline? You need to 1) Send an EOS event to the pipeline 2) Wait for the EOS message to arrive on the bus 3) Set the pipeline to NULL state If you only do 3) the file will be unplayable.
well we use to be able to play the partially recordable files while they were being recorded, also we do 1 , 2 and 3 but if the app crashes of course that doesn't occur. We confirmed that the IOS 8 behavior is different than the IOS 7.x behavior.
files created with avassetwriter have the correct behavior. I can confirm that expected moov bytes do not exist in the file
Created attachment 286530 [details] vlc log vlc log
this seems to at least partially fix the problem //fragment durations in ms (produce a fragmented file if > 0). GstElement *muxer = gst_element_factory_make("mp4mux", NULL); g_object_set(G_OBJECT (muxer), "fragment-duration", 10, NULL);
Can you attach a complete and minimal testcase for reproducing your problem? And does it also happen on other platforms, if so a simple commandline application would be preferred.
Closing this bug report as no further information has been provided. Please feel free to reopen this bug report if you can provide the information that was asked for in a previous comment. Thanks!