1

What I'm doing is on an IOS app with Xcode 7.3.

I got h264 data from an ip camera using UDP,the data can be decoded and displayed rightly (decoded by ffmpeg). Now I want to mux the raw H264 data to an mp4 file(some users may want to record what they are watching on his cell-phone), using ffmpeg. Nothing wrong happened when the code is running, and the result file can be played normally with QuickTime on my computer. But when played on iphone with iphone's default video player, it can't be played normally.Here is my code.

Wish someone could tell me what should I do, thanks!

init

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;
}

add video stream and open

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;
}

write video frame

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;
}

set the extradata in the codec context

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);
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
C.sandy
  • 13
  • 4

1 Answers1

0

Your bitstream format is annex b. You must convert to the MP4 format by replacing start codes with nal length values. You must also populate the extradata in the codeccontext. Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream

Community
  • 1
  • 1
szatmary
  • 29,969
  • 8
  • 44
  • 57
  • Thanks a lot! According to your answer, I just populate the extradata in the codec context. And now it works fine! I will post what is "populate the extradata". – C.sandy Aug 19 '16 at 06:00
  • Codec context has a variable calld extradata (and extradata size ) just set that to point to the structure described in that post. – szatmary Aug 19 '16 at 06:03