void Decoder::SaveFrame(AVFrame *pFrame, int width, int height, int ct, KString outPath) throw(IOException) { FILE *pFile; char szFilename[32]; outPath.append("/frame%04i.ppm"); //4-zero's padding printf("Saving frame : "); printf(outPath.c_str(), ct); printf("\n"); sprintf(szFilename, outPath.c_str(), ct); pFile=fopen(szFilename, "wb"); if(pFile == NULL) { return; } fprintf(pFile, "P6\n%d %d\n255\n", width, height); for(int idx = 0 ; idx < height ; idx++) fwrite(pFrame->data[0]+idx*pFrame->linesize[0], 1, width*3, pFile); fclose(pFile); } int Decoder::Decode(KString fileName, KString outPath, bool doSave, KString &errMsg) { AVFormatContext *pFormatCtx=NULL; int videoStream; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; AVPacket packet; int gotFrame; int numBytes; uint8_t *buffer; struct SwsContext *img_convert_ctx; int frameCount=0; int streamHeight = 0; int streamWidth = 0; PixelFormat pixelFmt = PIX_FMT_NONE; // Register all formats and codecs av_register_all(); // Open video file if(avformat_open_input(&pFormatCtx, fileName.c_str(), NULL, NULL)!=0) { errMsg = "Could not open file"; return -1; } /* fill the streams in the format context */ if(avformat_find_stream_info(pFormatCtx, NULL) < 0) { errMsg = "Could not stream packet information."; return -1; } if(_debug) av_dump_format(pFormatCtx, 0, _debugOutputFile.c_str(), 0); /* Look at ffprobe.c line.1575 for full way to assign decoder to each input stream. This will only look for the video stream within the encoded file. */ videoStream = -1; for(int i = 0 ; i < pFormatCtx->nb_streams ; i++) { if( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) { videoStream = i; streamHeight = pFormatCtx->streams[i]->codec->height; streamWidth = pFormatCtx->streams[i]->codec->width; pixelFmt = pFormatCtx->streams[i]->codec->pix_fmt; break; } } if(videoStream== -1) { errMsg = "Unable to find video stream."; return -1; } if(streamHeight == 0 || streamWidth == 0) { if(_suggestedWidth == 0 || _suggestedWidth == 0) { errMsg = "No dimensions provided in codec, suggest non-zero width and height."; return -1; } streamHeight = _suggestedHeight; streamWidth = _suggestedWidth; } if(pixelFmt == PIX_FMT_NONE) { if(_suggestedPixFmt == -1) { errMsg = "No pixel format provided in codec, suggest pixel format."; return -1; } pixelFmt = (PixelFormat)_suggestedPixFmt; } pCodecCtx = pFormatCtx->streams[videoStream]->codec; pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec == NULL) { errMsg = "Codec not supported."; return -1; } pCodecCtx = avcodec_alloc_context3(pCodec); pCodecCtx->width = streamWidth; pCodecCtx->height = streamHeight; pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P; if( avcodec_open(pCodecCtx, pCodec) < 0 ) { errMsg = "Was unable to open codec."; return -1; } // Allocate video frame pFrame = avcodec_alloc_frame(); // Allocate an AVFrame structure pFrameRGB = avcodec_alloc_frame(); if( pFrameRGB == NULL ) { errMsg = "Failed to allocate frame."; return -1; } numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); avpicture_fill( (AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height ); int w = pCodecCtx->width; int h = pCodecCtx->height; img_convert_ctx = sws_getContext ( w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB24, SWS_BICUBIC, /* Optional filters */ NULL, NULL, NULL ); int ct=0; while(av_read_frame(pFormatCtx, &packet) >= 0) { // Is this a packet from the video stream? if(packet.stream_index == videoStream) { /* Decode video returns gotFrame(3rd) flag if success */ int len = avcodec_decode_video2(pCodecCtx, pFrame, &gotFrame, &packet); if ( len < 0 ) { errMsg = "Error decoding frame."; return 1; } if(gotFrame) { sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); if(doSave) SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, (frameCount + 1), outPath); frameCount++; } } /* Free the packet that was allocated by av_read_frame */ av_free_packet(&packet); } //End while /* some codecs, such as MPEG, transmit the I and P frame with a latency of one frame. You must do the following to have a chance to get the last frame of the video */ packet.data = NULL; packet.size = 0; gotFrame = -1; int len = avcodec_decode_video2(pCodecCtx, pFrame, &gotFrame, &packet); if (gotFrame) { sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); if(doSave) SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, ++frameCount, outPath); av_free_packet(&packet); } av_free(buffer); av_free(pFrameRGB); av_free(pFrame); avcodec_close(pCodecCtx); av_close_input_file(pFormatCtx); }