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

mux raw h 264 to an mp4 file, some odd errors

我正在做的是 IOS 应用 Xcode 7.3.

我使用UDP从网络摄像头获取了h264数据,数据可以正确解码和显示(使用ffmpeg解码)。现在我想使用 ffmpeg 将原始 H264 数据复用到一个 mp4 文件(一些用户可能想记录他们在他的手机上观看的内容-phone)。当代码为运行时没有任何问题,结果文件可以在我的电脑上用QuickTime正常播放。但是用iphone的默认视频播放器在iphone上播放时,无法播放normally.Here是我的代码。

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

初始化

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 格式。您还必须在编解码器上下文中填充额外数据。 Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream