mux原始h 264到mp4文件,一些奇怪的错误

问题描述:

我在做什么是在IOS应用程序与Xcode 7.3。mux原始h 264到mp4文件,一些奇怪的错误

我使用UDP从ip摄像头获得h264数据,数据可以正确解码并正确显示(由ffmpeg解码)。现在我想使用ffmpeg将原始H264数据复合到mp4文件(某些用户可能想记录他们在手机上观看的内容)。代码运行时没有发生任何错误,并且可以在我的计算机上使用QuickTime正常播放结果文件。但是当用iPhone的默认视频播放器在iPhone上播放时,它不能正常播放。以下是我的代码。

希望有人能告诉我该怎么办,谢谢!

初始化

AVFormatContext *formatContext; 
AVOutputFormat *outputFormat; 
AVStream *video_st; 
int STREAM_FRAME_RATE = 15; 
unsigned long video_PTS; 
int initRecorder(char *fileName, int width, int height) { 
    video_st = NULL; 
    video_PTS = 0; 

    av_register_all(); 

    outputFormat = av_guess_format(NULL, fileName, NULL); 
    if (!outputFormat) { 
     zj_printf("av_guess_format -> fail\n"); 
     return -1; 
    } 
    outputFormat->video_codec = AV_CODEC_ID_H264; 

    avformat_alloc_output_context2(&formatContext, NULL, NULL, fileName); 
    if (!formatContext) { 
     zj_printf("avformat_alloc_context -> fail\n"); 
     return -2; 
    } 
    formatContext->oformat = outputFormat; 
    strcpy(formatContext->filename, fileName); 

    video_st = add_video_stream(formatContext, outputFormat, width, height); 
    if (!video_st || open_video(formatContext, video_st)) { 
     zj_printf("Could not open video codec\n"); 
     return -3; 
    } 

    av_dump_format(formatContext, 0, fileName, 1); 
    if (!(outputFormat->flags & AVFMT_NOFILE)) { 
     if (avio_open(&formatContext->pb, fileName, AVIO_FLAG_READ_WRITE) < 0) { 
      zj_printf("could not open file: %s\n", fileName); 
      return -7; 
     } 
    } 

    /* write the stream header, if any */ 
    if (avformat_write_header(formatContext, NULL)) { 
     zj_printf("avformat_write_header -> fail\n"); 
    } 

    return 0; 
} 

添加视频流和开放

static AVStream * add_video_stream(AVFormatContext *pFormatContext, AVOutputFormat *pOutputFormat, int wight, int height) { 

    AVStream *stream = avformat_new_stream(pFormatContext, NULL); 
    if (!stream) { 
     zj_fprintf(stderr, "Could not alloc stream\n"); 
     return NULL; 
    } 
    stream->id = 0; 

    AVCodecContext *codecContext = stream->codec; 
    codecContext->codec_id = pOutputFormat->video_codec; 
    codecContext->codec_type = AVMEDIA_TYPE_VIDEO; 

    /* resolution must be a multiple of two */ 
    codecContext->width = wight; 
    codecContext->height = height; 
    /* time base: this is the fundamental unit of time (in seconds) in terms 
    of which frame timestamps are represented. for fixed-fps content, 
    timebase should be 1/framerate and timestamp increments should be 
    identically 1. */ 
    if (wight==1280 && height == 720) { 
     codecContext->bit_rate = 512000; 
     STREAM_FRAME_RATE = 15; 
    } else { 
     codecContext->bit_rate = 384000; 
     STREAM_FRAME_RATE = 20; 
    } 
    codecContext->time_base = (AVRational){1,STREAM_FRAME_RATE}; 
    stream->time_base = (AVRational){1,STREAM_FRAME_RATE}; 
    codecContext->max_b_frames = 0; 
    codecContext->pix_fmt = AV_PIX_FMT_YUV420P; 
    // these are the encoding params, here we do not need them 
    // codecContext->gop_size = 12; //10 
    // codecContext->me_range = 16; 
    // codecContext->max_qdiff = 4; 
    // codecContext->qmin = 10; 
    // codecContext->qmax = 31; 

    if (pFormatContext->oformat->flags & AVFMT_GLOBALHEADER) 
     codecContext->flags |= CODEC_FLAG_GLOBAL_HEADER; 

    return stream; 
} 

static int open_video(AVFormatContext *pFormatContext, AVStream *pStream) { 
    /* find the video encoder */ 
    AVCodec *codec = avcodec_find_encoder(pStream->codec->codec_id); 
    if (!codec) { 
     return -1; 
    } 

    /* open the codec */ 
    if (avcodec_open2(pStream->codec, codec, NULL)) { 
     return -2; 
    } 

    return 0; 
} 

写入视频帧

static int write_video_frame(char *buffer, int size) { 
    int ret = 0; 

    if (size > 0) { 
     AVPacket mAVPacket; 
     av_init_packet(&mAVPacket); 
     mAVPacket.flags = isIFrame(buffer, size); 
     mAVPacket.stream_index = video_st->index; 

     mAVPacket.data = buffer; 
     mAVPacket.size = size; 
     mAVPacket.pts = video_PTS; 
     mAVPacket.dts = video_PTS; 
     video_PTS += 1; 

     mAVPacket.pts = av_rescale_q_rnd(mAVPacket.pts, video_st->codec->time_base, video_st->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); 
     mAVPacket.dts = av_rescale_q_rnd(mAVPacket.dts, video_st->codec->time_base, video_st->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); 
     mAVPacket.duration = 0; 
     mAVPacket.pos = -1; 

     ret = av_interleaved_write_frame(formatContext, &mAVPacket); 
     } 

     av_packet_unref(&mAVPacket); 


    } else { 
     ret = -2; 
    } 

    if (ret != 0) { 
     zj_printf("av_write_frame error:%d\n", ret); 
    } 

    return ret; 
} 

设置在编解码器的上下文而额外

unsigned char sps_pps[23] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x29, 0xac, 0x1b, 0x1a, 0xc1, 0xe0, 0x51, 0x90, 0x00, 0x00, 0x00, 0x01, 0x68, 0xea, 0x43, 0xcb}; 
codecContext->extradata_size = 23; 
codecContext->extradata = av_malloc(23 + AV_INPUT_BUFFER_PADDING_SIZE); 
if (codecContext->extradata == NULL) { 
    printf("could not av_malloc the video params extradata!\n"); 
    return -1; 
} 
memcpy(codecContext->extradata, sps_pps, 23); 

您的比特流格式是附录b。您必须通过替换具有最终长度值的开始代码来转换为MP4格式。您还必须在codeccontext中填充extradata。 Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream

+0

非常感谢!根据你的回答,我只是在编解码器上下文中填充extradata。现在它工作正常!我将发布什么是“填充extradata”。 –

+0

编解码器上下文具有一个变量集合的额外数据(和额外数据大小),只是将其设置为指向该帖子中描述的结构。 – szatmary