Что я делаю, это приложение IOS с Xcode 7.3.mux raw h 264 в файл mp4, некоторые нечетные ошибки
Я получил данные h264 с ip-камеры с использованием UDP, данные могут быть декодированы и отображены правильно (декодировано ffmpeg). Теперь я хочу отобразить необработанные данные H264 в файле mp4 (некоторые пользователи могут записывать то, что они смотрят на своем мобильном телефоне), используя ffmpeg. При запуске кода ничего плохого не произошло, и файл результатов можно воспроизводить в обычном режиме с помощью QuickTime на моем компьютере. Но когда играется на iphone с помощью видеопроигрывателя по умолчанию iphone, его нельзя воспроизводить нормально. Вот мой код.
Пожелайте, чтобы кто-нибудь мог сказать мне, что мне делать, спасибо!
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);
Большое спасибо! Согласно вашему ответу, я просто заполняю extradata в контексте кодека. И теперь все отлично! Я опубликую, что «заполняет extradata». –
Контекст кодека имеет переменную calld extradata (и размер extradata), которая просто устанавливает это, чтобы указать на структуру, описанную в этом сообщении. – szatmary