int64_t VideoEncoder::GetFrameDelay() { int64_t interval = 0; size_t frames = GetQueuedFrameCount(); if(frames > THROTTLE_THRESHOLD_FRAMES) { int64_t n = (frames - THROTTLE_THRESHOLD_FRAMES) * 1000 / THROTTLE_THRESHOLD_FRAMES; interval += n * n; } size_t packets = GetMuxer()->GetQueuedPacketCount(GetStreamIndex()); if(packets > THROTTLE_THRESHOLD_PACKETS) { int64_t n = (packets - THROTTLE_THRESHOLD_PACKETS) * 1000 / THROTTLE_THRESHOLD_PACKETS; interval += n * n; } if(interval > 1000000) interval = 1000000; return interval; }
bool AudioEncoder::EncodeFrame(AVFrame* frame) { if(frame != NULL) { #if SSR_USE_AVFRAME_NB_SAMPLES assert((unsigned int) frame->nb_samples == GetFrameSize()); #endif #if SSR_USE_AVFRAME_CHANNELS assert(frame->channels == GetStream()->codec->channels); #endif #if SSR_USE_AVFRAME_SAMPLE_RATE assert(frame->sample_rate == GetStream()->codec->sample_rate); #endif #if SSR_USE_AVFRAME_FORMAT assert(frame->format == GetStream()->codec->sample_fmt); #endif } #if SSR_USE_AVCODEC_ENCODE_AUDIO2 // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); // encode the frame int got_packet; if(avcodec_encode_audio2(GetStream()->codec, packet->GetPacket(), frame, &got_packet) < 0) { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of audio frame failed!")); throw LibavException(); } // do we have a packet? if(got_packet) { // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); return true; } else { return false; } #else // encode the frame short *data = (frame == NULL)? NULL : (short*) frame->data[0]; int bytes_encoded = avcodec_encode_audio(GetStream()->codec, m_temp_buffer.data(), m_temp_buffer.size(), data); if(bytes_encoded < 0) { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of audio frame failed!")); throw LibavException(); } // do we have a packet? if(bytes_encoded > 0) { // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper(bytes_encoded)); // copy the data memcpy(packet->GetPacket()->data, m_temp_buffer.data(), bytes_encoded); // set the timestamp // note: pts will be rescaled and stream_index will be set by Muxer if(GetStream()->codec->coded_frame != NULL && GetStream()->codec->coded_frame->pts != (int64_t) AV_NOPTS_VALUE) packet->GetPacket()->pts = GetStream()->codec->coded_frame->pts; // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); return true; } else { return false; } #endif }
bool AudioEncoder::EncodeFrame(AVFrameWrapper* frame) { if(frame != NULL) { #if SSR_USE_AVFRAME_NB_SAMPLES assert((unsigned int) frame->GetFrame()->nb_samples == GetFrameSize()); #endif #if SSR_USE_AVFRAME_CHANNELS assert(frame->GetFrame()->channels == GetCodecContext()->channels); #endif #if SSR_USE_AVFRAME_SAMPLE_RATE assert(frame->GetFrame()->sample_rate == GetCodecContext()->sample_rate); #endif #if SSR_USE_AVFRAME_FORMAT assert(frame->GetFrame()->format == GetCodecContext()->sample_fmt); #endif } #if SSR_USE_AVCODEC_SEND_RECEIVE // send a frame AVFrame *avframe = (frame == NULL)? NULL : frame->Release(); try { if(avcodec_send_frame(GetCodecContext(), avframe) < 0) { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Sending of audio frame failed!")); throw LibavException(); } } catch(...) { av_frame_free(&avframe); throw; } av_frame_free(&avframe); // try to receive a packet for( ; ; ) { std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); int res = avcodec_receive_packet(GetCodecContext(), packet->GetPacket()); if(res == 0) { // we have a packet, send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); } else if(res == AVERROR(EAGAIN)) { // we have no packet return true; } else if(res == AVERROR_EOF) { // this is the end of the stream return false; } else { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Receiving of audio packet failed!")); throw LibavException(); } } #elif SSR_USE_AVCODEC_ENCODE_AUDIO2 // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); // encode the frame int got_packet; if(avcodec_encode_audio2(GetCodecContext(), packet->GetPacket(), (frame == NULL)? NULL : frame->GetFrame(), &got_packet) < 0) { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of audio frame failed!")); throw LibavException(); } // do we have a packet? if(got_packet) { // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); return true; } else { return false; } #else // encode the frame short *data = (frame == NULL)? NULL : (short*) frame->GetFrame()->data[0]; int bytes_encoded = avcodec_encode_audio(GetCodecContext(), m_temp_buffer.data(), m_temp_buffer.size(), data); if(bytes_encoded < 0) { Logger::LogError("[AudioEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of audio frame failed!")); throw LibavException(); } // do we have a packet? if(bytes_encoded > 0) { // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper(bytes_encoded)); // copy the data memcpy(packet->GetPacket()->data, m_temp_buffer.data(), bytes_encoded); // set the timestamp // note: pts will be rescaled and stream_index will be set by Muxer if(GetCodecContext()->coded_frame != NULL && GetCodecContext()->coded_frame->pts != (int64_t) AV_NOPTS_VALUE) packet->GetPacket()->pts = GetCodecContext()->coded_frame->pts; // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); return true; } else { return false; } #endif }
VideoEncoder::VideoEncoder(Muxer* muxer, const QString& codec_name, const std::vector<std::pair<QString, QString> >& codec_options, unsigned int bit_rate, unsigned int width, unsigned int height, unsigned int frame_rate) : BaseEncoder(muxer) { try { m_bit_rate = bit_rate; m_width = width; m_height = height; m_frame_rate = frame_rate; m_opt_threads = std::max(1u, std::thread::hardware_concurrency()); m_opt_minrate = (unsigned int) -1; m_opt_maxrate = (unsigned int) -1; m_opt_bufsize = (unsigned int) -1; #if !SSR_USE_AVCODEC_PRIVATE_CRF m_opt_crf = (unsigned int) -1; #endif #if !SSR_USE_AVCODEC_PRIVATE_PRESET m_opt_preset = ""; #endif if(m_width == 0 || m_height == 0) { Logger::LogError("[VideoEncoder::Init] " + QObject::tr("Error: Width or height is zero!")); throw LibavException(); } if(m_width > 10000 || m_height > 10000) { Logger::LogError("[VideoEncoder::Init] " + QObject::tr("Error: Width or height is too large, the maximum width and height is %1!").arg(10000)); throw LibavException(); } if(m_width % 2 != 0 || m_height % 2 != 0) { Logger::LogError("[VideoEncoder::Init] " + QObject::tr("Error: Width or height is not an even number!")); throw LibavException(); } if(m_frame_rate == 0) { Logger::LogError("[VideoEncoder::Init] " + QObject::tr("Error: Frame rate it zero!")); throw LibavException(); } // start the encoder AVDictionary *options = NULL; try { for(unsigned int i = 0; i < codec_options.size(); ++i) { if(codec_options[i].first == "threads") m_opt_threads = codec_options[i].second.toUInt(); else if(codec_options[i].first == "minrate") m_opt_minrate = codec_options[i].second.toUInt() * 1024; // kbps else if(codec_options[i].first == "maxrate") m_opt_maxrate = codec_options[i].second.toUInt() * 1024; // kbps else if(codec_options[i].first == "bufsize") m_opt_bufsize = codec_options[i].second.toUInt() * 1024; // kbit #if !SSR_USE_AVCODEC_PRIVATE_PRESET else if(codec_options[i].first == "crf") m_opt_crf = codec_options[i].second.toUInt(); #endif #if !SSR_USE_AVCODEC_PRIVATE_PRESET else if(codec_options[i].first == "preset") m_opt_preset = codec_options[i].second; #endif else av_dict_set(&options, codec_options[i].first.toAscii().constData(), codec_options[i].second.toAscii().constData(), 0); } CreateCodec(codec_name, &options); av_dict_free(&options); } catch(...) { av_dict_free(&options); throw; } #if !SSR_USE_AVCODEC_ENCODE_VIDEO2 // allocate a temporary buffer // Apparently libav/ffmpeg completely ignores the size of the buffer, and if it's too small it just crashes. // Originally it was 256k, which is large enough for about 99.9% of the packets, but it still occasionally crashes. // So now I'm using a buffer that's always at least large enough to hold a 256k header and *two* completely uncompressed frames. // (one YUV frame takes w * h * 1.5 bytes) // Newer versions of libav/ffmpeg have deprecated avcodec_encode_video and added a new function which does the allocation // automatically, just like avcodec_encode_audio2, but that function isn't available in Ubuntu 12.04/12.10 yet. m_temp_buffer.resize(std::max<unsigned int>(FF_MIN_BUFFER_SIZE, 256 * 1024 + m_width * m_height * 3)); #endif GetMuxer()->RegisterEncoder(GetStreamIndex(), this); } catch(...) { Destruct(); throw; } }
bool VideoEncoder::EncodeFrame(AVFrame* frame) { #if SSR_USE_AVFRAME_FORMAT if(frame != NULL) { Q_ASSERT(frame->format == GetCodecContext()->pix_fmt); } #endif #if SSR_USE_AVCODEC_ENCODE_VIDEO2 // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); // encode the frame int got_packet; if(avcodec_encode_video2(GetCodecContext(), packet->GetPacket(), frame, &got_packet) < 0) { Logger::LogError("[VideoEncoder::EncodeFrame] " + QObject::tr("Error: Encoding of video frame failed!")); throw LibavException(); } // do we have a packet? if(got_packet) { if(GetCodecContext()->coded_frame->key_frame) packet->GetPacket()->flags |= AV_PKT_FLAG_KEY; // send the packet to the muxer GetMuxer()->AddPacket(GetStreamIndex(), std::move(packet)); return true; } else { return false; } #else // encode the frame int bytes_encoded = avcodec_encode_video(GetCodecContext(), m_temp_buffer.data(), m_temp_buffer.size(), frame); if(bytes_encoded < 0) { Logger::LogError("[VideoEncoder::EncodeFrame] " + QObject::tr("Error: Encoding of video frame failed!")); throw LibavException(); } // do we have a packet? if(bytes_encoded > 0) { // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper(bytes_encoded)); // copy the data memcpy(packet->GetPacket()->data, m_temp_buffer.data(), bytes_encoded); // set the timestamp // note: pts will be rescaled and stream_index will be set by Muxer if(GetCodecContext()->coded_frame != NULL && GetCodecContext()->coded_frame->pts != (int64_t) AV_NOPTS_VALUE) packet->GetPacket()->pts = GetCodecContext()->coded_frame->pts; // set the keyframe flag if(GetCodecContext()->coded_frame->key_frame) packet->GetPacket()->flags |= AV_PKT_FLAG_KEY; // send the packet to the muxer GetMuxer()->AddPacket(GetStreamIndex(), std::move(packet)); return true; } else { return false; } #endif }
unsigned int BaseEncoder::GetQueuedPacketCount() { return GetMuxer()->GetQueuedPacketCount(GetStream()->index); }
bool VideoEncoder::EncodeFrame(AVFrameWrapper* frame) { if(frame != NULL) { #if SSR_USE_AVFRAME_WIDTH_HEIGHT assert(frame->GetFrame()->width == GetCodecContext()->width); assert(frame->GetFrame()->height == GetCodecContext()->height); #endif #if SSR_USE_AVFRAME_FORMAT assert(frame->GetFrame()->format == GetCodecContext()->pix_fmt); #endif #if SSR_USE_AVFRAME_SAR assert(frame->GetFrame()->sample_aspect_ratio.num == GetCodecContext()->sample_aspect_ratio.num); assert(frame->GetFrame()->sample_aspect_ratio.den == GetCodecContext()->sample_aspect_ratio.den); #endif } #if SSR_USE_AVCODEC_SEND_RECEIVE // send a frame AVFrame *avframe = (frame == NULL)? NULL : frame->Release(); try { if(avcodec_send_frame(GetCodecContext(), avframe) < 0) { Logger::LogError("[VideoEncoder::EncodeFrame] " + Logger::tr("Error: Sending of video frame failed!")); throw LibavException(); } } catch(...) { av_frame_free(&avframe); throw; } av_frame_free(&avframe); // try to receive a packet for( ; ; ) { std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); int res = avcodec_receive_packet(GetCodecContext(), packet->GetPacket()); if(res == 0) { // we have a packet, send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); } else if(res == AVERROR(EAGAIN)) { // we have no packet return true; } else if(res == AVERROR_EOF) { // this is the end of the stream return false; } else { Logger::LogError("[VideoEncoder::EncodeFrame] " + Logger::tr("Error: Receiving of video packet failed!")); throw LibavException(); } } #elif SSR_USE_AVCODEC_ENCODE_VIDEO2 // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper()); // encode the frame int got_packet; if(avcodec_encode_video2(GetCodecContext(), packet->GetPacket(), (frame == NULL)? NULL : frame->GetFrame(), &got_packet) < 0) { Logger::LogError("[VideoEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of video frame failed!")); throw LibavException(); } // do we have a packet? if(got_packet) { // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); return true; } else { return false; } #else // encode the frame int bytes_encoded = avcodec_encode_video(GetCodecContext(), m_temp_buffer.data(), m_temp_buffer.size(), (frame == NULL)? NULL : frame->GetFrame()); if(bytes_encoded < 0) { Logger::LogError("[VideoEncoder::EncodeFrame] " + Logger::tr("Error: Encoding of video frame failed!")); throw LibavException(); } // do we have a packet? if(bytes_encoded > 0) { // allocate a packet std::unique_ptr<AVPacketWrapper> packet(new AVPacketWrapper(bytes_encoded)); // copy the data memcpy(packet->GetPacket()->data, m_temp_buffer.data(), bytes_encoded); // set the timestamp // note: pts will be rescaled and stream_index will be set by Muxer if(GetCodecContext()->coded_frame != NULL && GetCodecContext()->coded_frame->pts != (int64_t) AV_NOPTS_VALUE) packet->GetPacket()->pts = GetCodecContext()->coded_frame->pts; // set the keyframe flag if(GetCodecContext()->coded_frame->key_frame) packet->GetPacket()->flags |= AV_PKT_FLAG_KEY; // send the packet to the muxer GetMuxer()->AddPacket(GetStream()->index, std::move(packet)); IncrementPacketCounter(); return true; } else { return false; } #endif }