2017-12-13 4 views
1

libavcodec을 사용하여 스트림을 h264로 인코딩하고 libavformat을 사용하여 mp4에 저장합니다. 결과 컨테이너에는 VLC에서 재생할 수있는 잘못된 헤더가 있지만 다른 플레이어에는 없습니다.libavformat/libavcodec 잘못된 컨테이너 헤더 제공

나는 mp4 컨테이너와 "mpeg4"코덱을 사용하면 유효한 mp4 파일을 생성하지만 libx265 (HEVC) 또는 libx264 코덱을 사용하면 잘못된 mp4가 생성된다는 것을 알았습니다.

나는 ffmpeg -i invalid.mp4 -vcodec copy valid.mp4을 사용할 수 있으며 유효한 크기의 파일이지만 크기는 거의 동일합니다. 이러한 파일의

예를 들면 현재 위치 : Broken file 및 가 Repaied file 나는 두 개의 파일과 무효의 헤더의 차이를 볼 수있는 16 진수 편집기를 사용

[검사 오른쪽 상단에있는 다운로드 링크를 사용] 하나는 유효한 것보다 1 바이트 작습니다.

내가 컨테이너와 코덱을 열 헤더를 작성하는 사용하고 코드는 여기에 있습니다 :

AVOutputFormat *container_format; 
AVFormatContext *container_format_context; 
AVStream *video_stream; 
int ret; 

/* allocate the output media context */ 
avformat_alloc_output_context2(&container_format_context, NULL, NULL, out_file); 
if (!container_format_context) { 
    log(INFO, "Unable to determine container format from filename, exiting\n"); 
    exit(1); 
} 
else { 
    log(INFO, "Using container %s\n", container_format_context->oformat->name); 
} 

if (!container_format_context) { 
    log(ERROR, "Could not build container format context. Encoding failed."); 
    exit(1); 
} 

container_format = container_format_context->oformat; 

/* Pull codec based on name */ 
AVCodec* codec = avcodec_find_encoder_by_name(codec_name); 
if (codec == NULL) { 
    log(ERROR, "Failed to locate codec \"%s\".", 
      codec_name); 
    exit(1); 
} 

/* create stream */ 
video_stream = NULL; 
video_stream = avformat_new_stream(container_format_context, codec); 
if (!video_stream) { 
    log(ERROR, "Could not allocate encoder stream. Cannot continue.\n"); 
    exit(1); 
} 
video_stream->id = container_format_context->nb_streams - 1; 

video_stream->time_base = video_stream->codec->time_base = (AVRational) { 1, 25}; 

av_dump_format(container_format_context, 0, out_file, 1); 



/* Retrieve encoding context */ 
AVCodecContext* avcodec_context = video_stream->codec; 
if (avcodec_context == NULL) { 
    log(ERROR, "Failed to allocate context for " 
      "codec \"%s\".", codec_name); 
    exit(1); 
} 


/* Init context with encoding parameters */ 
avcodec_context->bit_rate = bitrate; 
avcodec_context->width = width; 
avcodec_context->height = height; 
avcodec_context->gop_size = 10; 
avcodec_context->max_b_frames = 1; 
avcodec_context->qmax = 31; 
avcodec_context->qmin = 2; 
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P; 

av_dump_format(container_format_context, 0, out_file, 1); 

/* Open codec for use */ 
if (avcodec_open2(avcodec_context, codec, NULL) < 0) { 
    log(ERROR, "Failed to open codec \"%s\".", codec_name); 
    exit(1); 
} 

/* Allocate corresponding frame */ 
AVFrame* frame = av_frame_alloc(); 
if (frame == NULL) { 
    exit(1); 
} 

/* Copy necessary data for frame from avcodec_context */ 
frame->format = avcodec_context->pix_fmt; 
frame->width = avcodec_context->width; 
frame->height = avcodec_context->height; 

/* Allocate actual backing data for frame */ 
if (av_image_alloc(frame->data, frame->linesize, frame->width, 
      frame->height, frame->format, 32) < 0) { 
    exit(1); 
} 

/* open the output file, if the container needs it */ 
if (!(container_format->flags & AVFMT_NOFILE)) { 
    ret = avio_open(&container_format_context->pb, out_file, AVIO_FLAG_WRITE); 
    if (ret < 0) { 
     log(ERROR, "Error occurred while opening output file: %s\n", 
       av_err2str(ret)); 
     exit(1); 
    } 
} 

/* write the stream header, if needed */ 
ret = avformat_write_header(container_format_context, NULL); 
if (ret < 0) { 
    log(ERROR, "Error occurred while writing output file header: %s\n", 
       av_err2str(ret)); 
} 

프레임을 인코딩하는 코드는 여기에 있습니다 :

/* Init video packet */ 
AVPacket packet; 
av_init_packet(&packet); 

/* Request that encoder allocate data for packet */ 
packet.data = NULL; 
packet.size = 0; 

/* Write frame to video */ 
int got_data; 
if (avcodec_encode_video2(avcontext, &packet, frame, &got_data) < 0) { 
    log(WARNING, "Error encoding frame #%" PRId64, 
      video_struct->next_pts); 
    return -1; 
} 

/* Write corresponding data to file */ 
if (got_data) { 
    if (packet.pts != AV_NOPTS_VALUE) { 
     packet.pts = av_rescale_q(packet.pts, video_struct->output_stream->codec->time_base, video_struct->output_stream->time_base); 
    } 
    if (packet.dts != AV_NOPTS_VALUE) { 
     packet.dts = av_rescale_q(packet.dts, video_struct->output_stream->codec->time_base, video_struct->output_stream->time_base); 
    } 
    write_packet(video_struct, &packet, packet.size); 
    av_packet_unref(&packet); 
} 

그리고 코드 비디오 스트림에 패킷을 쓰려면 :

static int write_packet(video_struct* video, void* data, int size) { 

int ret; 

/* use AVStream is not null, otherwise write to output fd */ 
AVPacket *pkt = (AVPacket*) data; 
pkt->stream_index = video->output_stream->index; 
ret = av_interleaved_write_frame(video->container_format_context, pkt); 
if (ret != 0) { 
    return -1; 
} 

/* Data was written successfully */ 
return ret; 
} 
+0

샘플을 업로드해야합니다. – aergistal

+0

당신은 "mpeg4"와 h264가 같은 것입니까? –

+0

h264와 mpeg4는 똑같은 것이 아닙니다. 게시물에서 삭제해야하는 코드 일 뿐이므로 편집하겠습니다. 명시 적으로 "libx264"를 명령 줄 인수로 전달합니다. – seanr8

답변

1

이 문제가 해결되었습니다. 문제는 컨테이너가 필요로 할 경우 전역 헤더를 컨테이너에 할당하지 않는다는 것이 었습니다. 등등 avcodec_context까지의 높이, 폭, 비트 레이트와 같은 특성을 부여하면서, I는 문제를 해결 한 것으로 보인다

if (container_format_context->oformat->flags & AVFMT_GLOBALHEADER) { 
    avcodec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; 
} 

첨가.