需要帮助,最近,我使用ffmpeg libavcodec解码视频文件然后编码到H264并写入mp4媒体容器,最后,媒体文件的持续时间为零,以下是我的代码的工作流程:
AVFormatContext* input_format_context = NULL;
AVFormatContext* output_format_context = NULL;
AVIOContext* output_io_context = NULL;
AVCodecContext* input_codec_context = NULL;
AVCodecContext* output_codec_context = NULL;
AVCodec* codec = NULL;
AVStream* input_stream = NULL;
AVStream* output_stream = NULL;
AVFrame* frame = NULL;
int convert_init(const char* input_filename, const char* output_filename)
{
/** Allocate a new encode context */
avformat_open_input(&input_format_context,
input_filename, NULL, NULL);
/** Get information on the input file (number of streams etc.). */
avformat_find_stream_info(input_format_context, NULL);
/** Open the output file to write to it. */
avio_open(&output_io_context, output_filename,
AVIO_FLAG_WRITE);
/** Create a new format context for the output container format. */
output_format_context = avformat_alloc_context();
/** Associate the output file (pointer) with the container format context. */
output_format_context->pb = output_io_context;
/** Guess the desired container format based on the file extension. */
output_format_context->oformat = av_guess_format(NULL,
output_filename, NULL);
av_strlcpy((output_format_context)->filename, output_filename,
sizeof(output_format_context->filename));
/** stream0 is the video stream */
AVStream* input_stream = input_format_context->streams[0];
/**
* Init the input_codec_context
*/
/** Find a decoder for the audio stream. */
codec = avcodec_find_decoder(input_stream->codecpar->codec_id);
/** Allocate a new decode context */
input_codec_context = avcodec_alloc_context3(codec);
/** Initialize the stream parameters with demuxer information */
avcodec_parameters_to_context(input_codec_context,
input_stream->codecpar);
/** Open the decoder for the stream. */
avcodec_open2(input_codec_context, codec, NULL);
/**
* Create an output stream for writing encoded data
*
* AM I MISSING SOMETHING ?
*
*/
output_stream = avformat_new_stream(output_format_context, NULL);
/**
* Init the output_codec_context
*/
/** Find a encoder for the output video stream, using H264. */
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
/** Allocate an encode context. */
output_codec_context = avcodec_alloc_context3(codec);
/**
* Setup encode context parameters.
*
* AM I MISSING SOMETHING ?
*
* */
output_codec_context->bit_rate = input_codec_context->bit_rate;
output_codec_context->width = input_codec_context->width;
output_codec_context->height = input_codec_context->height;
output_codec_context->time_base = (AVRational){1, 25};
output_codec_context->framerate = (AVRational){25, 1};
output_codec_context->gop_size = 10;
output_codec_context->max_b_frames = 1;
output_codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
output_codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
/** Setup output_stream codecpar. */
avcodec_parameters_from_context(output_stream->codecpar,
codec_context);
/** Alloc an av frame */
frame = av_frame_alloc();
}
void convert_it()
{
AVPacket input_packet;
AVPacket output_packet;
/** Write the media file container header */
avformat_write_header(output_format_context, NULL);
/**
* decode frames and encode to H264
* */
while (1) {
av_init_packet(&input_packet);
input_packet.data = NULL;
input_packet.size = 0;
av_init_packet(&output_packet);
output_packet.data = NULL;
output_packet.size = 0;
/** Read a frame to decode */
av_read_frame(input_format_context, &input_packet);
if (av_read_frame is end of file) {
break;
}
...
...
/** Decoding... */
avcodec_send_packet(input_codec_context, &input_packet);
...
...
/** Get a decoded frame */
avcodec_receive_frame(input_codec_context, frame);
...
...
/** Make the frame writable, is it necessary ?? */
av_frame_make_writable(frame);
/** Encode to H264 */
avcodec_send_frame(output_codec_context, frame);
...
...
/** Get a encoded packet */
avcodec_receive_packet(output_codec_context, &output_packet);
/**
* Write the packet to output.
* Here is the point! should I configure the parameters
* in packet such as 'pts', 'dts', 'duration', etc, if so,
* hwo? or I just directly write the packet into output?
*/
av_interleaved_write_frame(output_format_context, &packet);
}
/** Write the media file container trailer */
av_write_trailer(output_format_context);
}
int main() {
convert_init("./sample.avi", "./output.mp4");
convert_it();
}
使用VLC或QuickTime播放output.mp4文件,它失败了,导致文件持续时间为零,当拖动时间进度条时,我可以清楚地看到相框,看来编码包缓冲区数据是否正确,但时间戳是错误的,我在配置output_stream或数据包时遗漏了什么?以下是来自ffprobe的消息。
ffprobe output.mp4
ffprobe version 3.3.3 Copyright (c) 2007-2017 the FFmpeg developers
built with Apple LLVM version 8.1.0 (clang-802.0.42)
configuration: --enable-shared --enable-libmp3lame
libavutil 55. 58.100 / 55. 58.100
libavcodec 57. 89.100 / 57. 89.100
libavformat 57. 71.100 / 57. 71.100
libavdevice 57. 6.100 / 57. 6.100
libavfilter 6. 82.100 / 6. 82.100
libswscale 4. 6.100 / 4. 6.100
libswresample 2. 7.100 / 2. 7.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'output.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.71.100
Duration: 00:00:00.06, start: 0.000000, bitrate: 6902181 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x408 [SAR 1:1 DAR 30:17], 13929056 kb/s, 90k fps, 90k tbr, 90k tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
答案 0 :(得分:0)
您的问题可能是在混合期间帧的时基有误。 根据格式the muxer can change the stream timebase。
应将流时基设置为调用方希望用于此流的时基(请注意,复用器实际使用的时基可以不同,如下所述)。 [...]
请注意,有关发送到复用器的数据包的时序信息必须位于相应的AVStream的时基中。该时基由多路复用器设置(在avformat_write_header()步骤中),并且可能与调用方请求的时基不同。
因此,在编写框架之前,必须将其时基从创建FormatContext时设置的理论转换为流实际使用的理论。为此,您可以使用av_packet_rescale_ts函数。
例如:
av_packet_rescale_ts(
packet,
codec_contex->time_base, // your theoric timebase
format_context->streams[packet->stream_index]->time_base); // the actual timebase