From 141e8bdd4ce1542cb927ed88e05bae6dd4d9acfc Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sat, 27 Aug 2016 22:59:42 +0300 Subject: [PATCH] Video transcoding kind of works (at least if input video frames has correct pts/dts) --- main.cpp | 77 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/main.cpp b/main.cpp index 8bc1195..0830a8e 100644 --- a/main.cpp +++ b/main.cpp @@ -8,7 +8,7 @@ void checkResult(int res, const char* msg) { if(res < 0) { char errStr[260]; av_strerror(res, errStr, 260); - std::cout << msg << ": " << errStr << std::endl; + std::cerr << msg << ": " << errStr << std::endl; } } @@ -49,21 +49,6 @@ int main(int argc, char** argv) { AVCodec* dstVCodec = avcodec_find_encoder(AV_CODEC_ID_H264); if(!dstVCodec) std::cout << "cannot find encoder" << std::endl; - AVCodecContext* dstVCodecCtx = avcodec_alloc_context3(dstVCodec); - if(!dstVCodecCtx) std::cout << "cannot allocate context" << std::endl; - - dstVCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; - dstVCodecCtx->width = codecCtx->width; - dstVCodecCtx->height = codecCtx->height; - dstVCodecCtx->time_base = codecCtx->time_base; - outFmtCtx->duration = inFmtCtx->duration; - - res = avcodec_open2(dstVCodecCtx, dstVCodec, nullptr); - checkResult(res, "cannot open codec for encoding"); - - if(outFmtCtx->oformat->flags & AVFMT_GLOBALHEADER) - dstVCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; - AVStream* outVideoStream = avformat_new_stream(outFmtCtx, dstVCodec); if(!outVideoStream) std::cout << "cannot create output video stream" << std::endl; @@ -74,15 +59,29 @@ int main(int argc, char** argv) { av_dump_format(outFmtCtx, 0, argv[2], 1); + AVCodecContext* dstVCodecCtx = outVideoStream->codec; + dstVCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; + dstVCodecCtx->width = codecCtx->width; + dstVCodecCtx->height = codecCtx->height; + //dstVCodecCtx->time_base = codecCtx->time_base; + outVideoStream->time_base = codecCtx->time_base; + outFmtCtx->duration = inFmtCtx->duration; + + if(outFmtCtx->oformat->flags & AVFMT_GLOBALHEADER) + dstVCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + res = avcodec_open2(dstVCodecCtx, dstVCodec, nullptr); + checkResult(res, "cannot open codec for encoding"); + res = avformat_write_header(outFmtCtx, nullptr); checkResult(res, "error writing header to output file"); - AVPacket packet; - packet.data = nullptr; - packet.size = 0; AVFrame* frame = nullptr; int gotPicture = 0, gotPacket = 0, decodedFrames = 1; while(true) { + AVPacket packet; + packet.data = nullptr; + packet.size = 0; res = av_read_frame(inFmtCtx, &packet); if(res < 0) break; @@ -99,7 +98,8 @@ int main(int argc, char** argv) { if(gotPicture) { frame->pts = av_frame_get_best_effort_timestamp(frame); - std::cout << "decoded frame: " << decodedFrames++ << std::endl; + frame->pict_type = AV_PICTURE_TYPE_NONE; + //std::cout << "decoded frame: " << decodedFrames++ << " pts: " << frame->pts << ", dts: " << frame->pkt_dts << std::endl; AVPacket encPacket; encPacket.data = nullptr; @@ -107,19 +107,12 @@ int main(int argc, char** argv) { av_init_packet(&encPacket); encPacket.stream_index = 0; - encPacket.pts = decodedFrames; - encPacket.dts = decodedFrames; - res = avcodec_encode_video2(dstVCodecCtx, &encPacket, frame, &gotPacket); av_frame_free(&frame); if(res < 0) break; if(!gotPacket) continue; - AVStream *st = outFmtCtx->streams[encPacket.stream_index]; - auto x = st->codec->has_b_frames; - auto y = st->codec->max_b_frames; - - //av_packet_rescale_ts(&encPacket, st->codec->time_base, st->time_base); + av_packet_rescale_ts(&encPacket, videoStream->time_base, outVideoStream->time_base); res = av_interleaved_write_frame(outFmtCtx, &encPacket); checkResult(res, "cannot write frame to output file"); @@ -130,8 +123,34 @@ int main(int argc, char** argv) { av_free_packet(&packet); } - av_write_trailer(outFmtCtx); + // flush encoder + if(dstVCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY) { + std::cout << "flushing encoder" << std::endl; + int gotFrame = 0; + while (1) { + AVPacket encPacket; + encPacket.data = nullptr; + encPacket.size = 0; + av_init_packet(&encPacket); + encPacket.stream_index = 0; + res = avcodec_encode_video2(dstVCodecCtx, &encPacket, nullptr, &gotFrame); + if (res < 0) { + std::cout << "avcodec_encode_video2 failed" << std::endl; + break; + } + if (gotFrame) { + //std::cout << "extra frame" << std::endl; + av_packet_rescale_ts(&encPacket, videoStream->time_base, outVideoStream->time_base); + res = av_interleaved_write_frame(outFmtCtx, &encPacket); + checkResult(res, "[flush encoder] cannot write frame to output file"); + } else { + break; + } + } + } + res = av_write_trailer(outFmtCtx); + checkResult(res, "error writing trailer to output file"); if(codecCtx) avcodec_close(codecCtx);