2016-08-18 12 views
1

내가하고있는 일은 Xcode 7.3이 설치된 IOS 응용 프로그램입니다.mux raw h 264를 mp4 파일에, 이상한 오류가 발생했습니다.

UDP를 사용하는 IP 카메라에서 h264 데이터를 얻었 으면 데이터를 디코딩하고 올바르게 표시 할 수 있습니다 (ffmpeg로 해독 됨). 이제 ffmpeg를 사용하여 원시 H264 데이터를 mp4 파일에 다중화하려는 경우 (일부 사용자는 휴대 전화에서보고있는 내용을 기록하려고합니다). 코드가 실행 중일 때 아무 일도 일어나지 않았으며 결과 파일은 내 컴퓨터에서 QuickTime으로 정상적으로 재생할 수 있습니다. 하지만 아이폰의 기본 비디오 플레이어로 아이폰에서 재생할 때 정상적으로 재생할 수 없습니다. 여기 내 코드가 있습니다.

소원 누군가가 내가 뭘해야하는지 말해 줄 수 있었어, 고마워!

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

비디오 스트림을 추가 개방

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

코덱의 맥락에서 extradata 설정

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

답변

0

귀하의 비트 스트림 형식은 부록 b입니다. 시작 코드를 길이 값으로 바꾸면 MP4 형식으로 변환해야합니다. 또한 코덱 컨텍스트에서 외부 데이터를 채워야합니다. Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream

+0

고맙습니다. 귀하의 대답에 따르면, 나는 코덱 컨텍스트에서 extradata를 채 웁니다. 그리고 지금은 잘 작동합니다! 나는 "유인물"을 게시 할 것입니다. –

+0

코덱 컨텍스트에는 해당 게시물에 설명 된 구조를 가리 키도록 설정된 calld extradata (및 extrasata 크기) 변수가 있습니다. – szatmary