After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 736924 - iOS 8 issue mp4 recording not playable
iOS 8 issue mp4 recording not playable
Status: RESOLVED INCOMPLETE
Product: GStreamer
Classification: Platform
Component: dont know
2.x
Other other
: Normal blocker
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2014-09-18 18:14 UTC by michelle
Modified: 2015-08-13 16:02 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
vlc log (33.57 KB, text/plain)
2014-09-18 19:07 UTC, michelle
Details

Description michelle 2014-09-18 18:14: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;
}
Comment 1 Sebastian Dröge (slomo) 2014-09-18 18:39:02 UTC
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.
Comment 2 michelle 2014-09-18 18:50:05 UTC
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.
Comment 3 michelle 2014-09-18 18:54:02 UTC
files created with avassetwriter have the correct behavior.

I can confirm that expected moov bytes do not exist in the file
Comment 4 michelle 2014-09-18 19:07:25 UTC
Created attachment 286530 [details]
vlc log

vlc log
Comment 5 michelle 2014-09-18 23:00:35 UTC
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);
Comment 6 Sebastian Dröge (slomo) 2014-09-24 07:32:06 UTC
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.
Comment 7 Sebastian Dröge (slomo) 2015-08-13 16:02:54 UTC
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!