#ifndef XCODEC_CPP #define XCODEC_CPP #include "XCodec.h" #include #define STREAM_DURATION 5.0 #define STREAM_FRAME_RATE 24 #define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) #define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ #define OUTPUT_CODEC AV_CODEC_ID_H264 int XCodec::s_frameCount = 0; XCodec::XCodec( const char* filename ) : m_filename( filename ), m_encoder( avcodec_find_encoder( OUTPUT_CODEC )) { av_log_set_level(AV_LOG_VERBOSE); int ret(0); // allocate the output media context ret = avformat_alloc_output_context2( &m_formatCtx, m_outputFormat, NULL, m_filename.c_str()); if (!m_formatCtx) return; m_outputFormat = m_formatCtx->oformat; // Add the video stream using H264 codec add_stream(); // Open video codec and allocate the necessary encode buffers if (m_streamOut) openVideoCodec(); // Print detailed information about input and output av_dump_format( m_formatCtx, 0, m_filename.c_str(), 1); // Open the output media file, if needed if (!( m_outputFormat->flags & AVFMT_NOFILE)) { ret = avio_open( &m_formatCtx->pb, m_filename.c_str(), AVIO_FLAG_WRITE); if (ret < 0) { char error[255]; ret = av_strerror( ret, error, 255); fprintf(stderr, "Could not open '%s': %s\n", m_filename.c_str(), error); return ; } } else { return; } // Write media header ret = avformat_write_header( m_formatCtx, NULL ); if (ret < 0) { char error[255]; av_strerror(ret, error, 255); fprintf(stderr, "Error occurred when opening output file: %s\n", error); return; } if ( m_frame ) m_frame->pts = 0; } XCodec::~XCodec() {} /* Add an output stream. */ void XCodec::add_stream() { AVCodecID codecId = OUTPUT_CODEC; if (!( m_encoder )) { fprintf(stderr, "Could not find encoder for '%s'\n", avcodec_get_name(codecId)); return; } // Get the stream for codec m_streamOut = avformat_new_stream(m_formatCtx, m_encoder); if (!m_streamOut) { fprintf(stderr, "Could not allocate stream\n"); return; } m_streamOut->id = m_formatCtx->nb_streams - 1; m_codecCtx = avcodec_alloc_context3( m_encoder); switch (( m_encoder)->type) { case AVMEDIA_TYPE_VIDEO: m_streamOut->codecpar->codec_id = codecId; m_streamOut->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; m_streamOut->codecpar->bit_rate = 400000; m_streamOut->codecpar->width = 800; m_streamOut->codecpar->height = 640; m_streamOut->codecpar->format = STREAM_PIX_FMT; m_streamOut->time_base = { 1, STREAM_FRAME_RATE }; avcodec_parameters_to_context( m_codecCtx, m_streamOut->codecpar); m_codecCtx->gop_size = 12; /* emit one intra frame every twelve frames at most */ m_codecCtx->max_b_frames = 1; m_codecCtx->time_base = { 1, STREAM_FRAME_RATE }; m_codecCtx->framerate = { STREAM_FRAME_RATE, 1 }; m_codecCtx->pix_fmt = STREAM_PIX_FMT; m_codecCtx->profile = FF_PROFILE_H264_HIGH; break; default: break; } if (m_streamOut->codecpar->codec_id == OUTPUT_CODEC) { av_opt_set( m_codecCtx, "preset", "ultrafast", 0 ); } / // /* Some formats want stream headers to be separate. */ if (m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER) m_codecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; int ret = avcodec_parameters_from_context( m_streamOut->codecpar, m_codecCtx ); if (ret < 0) { char error[255]; av_strerror(ret, error, 255); fprintf(stderr, "avcodec_parameters_from_context returned (%d) - %s", ret, error); return; } } void XCodec::openVideoCodec() { int ret; /* open the codec */ ret = avcodec_open2(m_codecCtx, m_encoder, NULL); if (ret < 0) { char error[255]; av_strerror(ret, error, 255); fprintf(stderr, "Could not open video codec: %s\n", error); return; } /* allocate and init a re-usable frame */ // m_frame = av_frame_alloc(); } void XCodec::encodeImage(const Image &image) { // Compute video time from last added video frame m_timeVideo = image.timeStamp(); //(double)m_frame->pts) * av_q2d(m_streamOut->time_base); // Stop media if enough time if (!m_streamOut /*|| m_timeVideo >= STREAM_DURATION*/) return; // Add a video frame write_video_frame( image ); } void XCodec::write_video_frame( const Image& image ) { int ret; qDebug() << "image num " << image.uniqueImageNumber() << " " << s_frameCount; if ( s_frameCount >= STREAM_NB_FRAMES) { /* No more frames to compress. The codec has a latency of a few * frames if using B-frames, so we get the last frames by * passing the same picture again. */ int p( 0 ) ; } else { createFrame( image ); } // Increase frame pts according to time base // m_frame->pts += av_rescale_q(1, m_codecCtx->time_base, m_streamOut->time_base); m_frame->pts = int64_t( image.timeStamp()) ; if (m_formatCtx->oformat->flags & 0x0020 ) { /* Raw video case - directly store the picture in the packet */ AVPacket pkt; av_init_packet(&pkt); pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = m_streamOut->index; pkt.data = m_frame->data[0]; pkt.size = sizeof(AVPicture); // ret = av_interleaved_write_frame(m_formatCtx, &pkt); ret = av_write_frame( m_formatCtx, &pkt ); } else { AVPacket pkt; av_init_packet(&pkt); /* encode the image */ ret = avcodec_send_frame(m_codecCtx, m_frame); if (ret < 0) { char error[255]; av_strerror(ret, error, 255); fprintf(stderr, "Error encoding video frame: %s\n", error); return; } /* If size is zero, it means the image was buffered. */ ret = avcodec_receive_packet(m_codecCtx, &pkt); if( !ret && pkt.size) { qDebug() << "write frame " << m_frame->display_picture_number; pkt.stream_index = m_streamOut->index; /* Write the compressed frame to the media file. */ // ret = av_interleaved_write_frame(m_formatCtx, &pkt); ret = av_write_frame( m_formatCtx, &pkt ); } else { ret = 0; } } if (ret != 0) { char error[255]; av_strerror(ret, error, 255); fprintf(stderr, "Error while writing video frame: %s\n", error); return; } s_frameCount++; } void XCodec::createFrame( const Image& image /*, AVFrame *m_frame, int frame_index, int width, int height*/) { /** * \note allocate frame */ m_frame = av_frame_alloc(); int ret = av_frame_make_writable( m_frame ); m_frame->format = STREAM_PIX_FMT; m_frame->width = image.width(); m_frame->height = image.height(); // m_frame->pict_type = AV_PICTURE_TYPE_I; m_frame->display_picture_number = image.uniqueImageNumber(); ret = av_image_alloc(m_frame->data, m_frame->linesize, m_frame->width, m_frame->height, STREAM_PIX_FMT, 1); if (ret < 0) { return; } struct SwsContext* sws_ctx = sws_getContext((int)image.width(), (int)image.height(), AV_PIX_FMT_RGB24, (int)image.width(), (int)image.height(), STREAM_PIX_FMT, 0, NULL, NULL, NULL); const uint8_t* rgbData[1] = { (uint8_t* )image.getData() }; int rgbLineSize[1] = { 3 * image.width() }; sws_scale(sws_ctx, rgbData, rgbLineSize, 0, image.height(), m_frame->data, m_frame->linesize); //cv::Mat yuv420p( m_frame->height + m_frame->height/2, m_frame->width, CV_8UC1, m_frame->data[0]); //cv::Mat cvmIm; //cv::cvtColor(yuv420p,cvmIm,CV_YUV420p2BGR); //std::ostringstream ss; //ss << "c:\\tmp\\YUVoriginal_" << image.uniqueImageNumber() << ".png"; //cv::imwrite( ss.str().c_str(), cvmIm); } void XCodec::close() { /* reset the framecount */ s_frameCount = 0 ; int ret( 0 ); /* flush the encoder */ while( ret >= 0 ) ret = avcodec_send_frame(m_codecCtx, NULL); // Write media trailer if( m_formatCtx ) ret = av_write_trailer( m_formatCtx ); /* Close each codec. */ if ( m_streamOut ) { if( m_frame ) { av_free( m_frame->data[0]); av_frame_free( &m_frame ); } if( m_packet ) av_packet_free( &m_packet ); } if (!( m_outputFormat->flags & AVFMT_NOFILE)) /* Close the output file. */ ret = avio_close( m_formatCtx->pb); /* free the stream */ avformat_free_context( m_formatCtx ); fflush( stdout ); } #endif // XCodec_CPP