Recently, I am doing video file 264 decoding. I feel very difficult because I am not very familiar with this field. However, after unremitting efforts, we have made some progress and feel very lucky. At the beginning, I searched for information on the internet because I was not familiar with it. Although there were a lot of information on the Internet, it was very messy. So I took a lot of detours at the beginning, now I want to write a little bit of my experience. Later, you may be able to take less detours. Of course, I am still a cainiao in video processing. Don't laugh when you see what I call "Experiences, if you see something wrong, please criticize and correct it.
At the beginning, I first searched for information on the Internet. I think there was an article that was very useful, because what I needed most at that time was the various codecs in the world, what are the features of each type, such as whether the decoding speed can meet the real-time playback needs. 264 Standard support level and so on. This article is "H. 264 evaluation of open-source decoder, this article details the evaluation of several popular types of H. 264 decoder, including JM decoder, t264 decoder, x264 decoder, FFMPEG libavcodec, and Intel's IPP library. After evaluation, the author found that the fastest speed is Intel IPP, but intel
IPP is a commercial software, and all other decoders belong to open-source projects. Therefore, the most suitable option is the FFMPEG with the second decoding speed, and its speed can fully meet the real-time playback requirements;
After the decoder is selected, the first step is complete, and the second step is to study the usage of FFMPEG. After exploration, my choice is: to go to the Chinese video network in ffmpeg sdk 2.0, this is probably the most suitable for VC ++6. The FFMPEG-based SDK is used, which is easy to use.
The third step is to write the player shell. The shell code is written in VC ++ 6. I will give all the code of the shell at the end of the text sheet. Note: The lpdata obtained by the surgical code is a Windows memory bitmap, with the DWORD alignment feature, in addition, the decoded image is inverted, So I specifically wrote a function to reverse the image, and the operation speed is still quite fast, without interfering with real-time playback;
The work in the previous stage is satisfactory. The work in the next stage is the RTP payload of H.264.Protocol.
Appendix:
H.264 playback shell code example -------------------------------------------------------------------------------------------------
// Decode264.cpp: defines the initialization routines for the DLL.
# Include "stdafx. H"
# Include "decode264.h"
// Add the following code ////////////////////////////
# Include <stdlib. h>
# Include <time. h>
# Include "avformat. H"
# Include "avcodec. H"
# Include <windows. h>
// Define the target format
# Define dest_format pix_fmt_bgr24
// Pix_fmt_yuv420p
// Define global variables
Avformatcontext * pformatctx ;//
Int I, videostream;
Avcodeccontext * pcodecctx;
Avcodec * pcodec; // Codec
Avframe * pframe; // Frame
Avframe * pframeyuv; // YUV Frame
Clock_t T;
Double FPS;
Int y_size, I _frame = 0;
Int numbytes;
Uint8_t * buffer;
//////////////////////////////////////// ////////
# Ifdef _ debug
# Define new debug_new
# UNDEF this_file
Static char this_file [] = _ file __;
# Endif
//
// Note!
//
// If this DLL is dynamically linked against the MFC
// DLLs, any functions exported from this dll which
// Call into MFC must have the afx_manage_state macro
// Added at the very beginning of the function.
//
// For example:
//
// Extern "C" bool Pascal export exportedfunction ()
//{
// Afx_manage_state (afxgetstaticmodulestate ());
/// Normal function body here
//}
//
// It is very important that this macro appear in each
// Function, prior to any callinto MFC. This means that
// It must appear as the first statement within
// Function, even before any object variable declarations
// As their constructors may generate callinto the MFC
// DLL.
//
// Please see MFC technical notes 33 and 58 for additional
// Details.
//
//////////////////////////////////////// /////////////////////////////////////
// Cdecode=app
Begin_message_map (cdecode=app, cwinapp)
// {Afx_msg_map (cdecode=app)
// Note-The classwizard will add and remove mapping macros here.
// Do not edit what you see in these blocks of generated code!
//} Afx_msg_map
End_message_map ()
//////////////////////////////////////// /////////////////////////////////////
// Cdecode=app Construction
Cdecode=app: cdecode=app ()
{
// Todo: Add construction code here,
// Place all significant initialization in initinstance
}
//////////////////////////////////////// /////////////////////////////////////
// The one and only cdecode=app object
Cdecodeappsapp theapp;
// Add the following code ///////////////////////////////// ////////////////////////
// Put the image upside down;
Long upendbmp (unsigned char * lpdata, long width, long height)
{
Long lbpl; // The number of bytes in each row, because DWORD alignment is required.
Long X, Y, idx_src, idx_dest;
Unsigned char * tmpdata;
If (0 = (width * 3) % 4) // nwidth * 3 is the number of bytes required to store pixels in each row, if it is an integer multiple of 4.
Lbpl = (width * 3); // returns nwidth * 3, which is the number of bytes in each row.
Else // if it is not an integer multiple of 4, you must add a number to an integer multiple of 4, which is the number of bytes in each line.
Lbpl = (width * 3 + (4-(width * 3) % 4 )));
Tmpdata = new unsigned char [lbpl * Height];
X = 0;
For (y = 0; y {
Idx_src = (height-1-y) * lbpl; // idx_src = (height-1-y) * lbpl + x * 3; before optimization
Idx_dest = y * lbpl; // idx_dest = y * lbpl + x * 3; before optimization
Memcpy (& tmpdata [idx_dest], & lpdata [idx_src], lbpl); // copy a row
}
Memcpy (lpdata, tmpdata, lbpl * Height );
Delete [] tmpdata;
Return 0;
}
// Create a BMP file. For debugging
Static int av_create_bmp (char * filename, uint8_t * prgbbuffer,
Int width, int height, int bpp)
{
Bitmapfileheader bmpheader;
Bitmapinfo BMP Info;
File * FP;
Fp = fopen (filename, "WB ");
If (! FP) Return-1;
Bmpheader. bftype = (''m'' <8) | ''B '';
Bmpheader. bfreserved1 = 0;
Bmpheader. bfreserved2 = 0;
Bmpheader. bfoffbits = sizeof (bitmapfileheader) + sizeof (bitmapinfoheader );
Bmpheader. bfsize = bmpheader. bfoffbits + width * height * BPP/8;
BMP info. bmiheader. bisize = sizeof (bitmapinfoheader );
BMP info. bmiheader. biwidth = width;
BMP info. bmiheader. biheight = height;
BMP info. bmiheader. biplanes = 1;
BMP info. bmiheader. bibitcount = BPP;
BMP info. bmiheader. bicompression = bi_rgb;
BMP info. bmiheader. bisizeimage = 0;
BMP info. bmiheader. bixpelspermeter = 100;
Bminfo. bmiheader. biypelspermeter = 100;
BMP info. bmiheader. biclrused = 0;
BMP info. bmiheader. biclrimportant = 0;
Fwrite (& bmpheader, sizeof (bitmapfileheader), 1, FP );
Fwrite (& BMP info. bmiheader, sizeof (bitmapinfoheader), 1, FP );
Fwrite (prgbbuffer, width * height * BPP/8, 1, FP );
Fclose (FP );
Return 0;
}
// Obtain the next frame
Static bool getnextframe (avformatcontext * pformatctx,
Avcodeccontext * pcodecctx,
Int videostream,
Avframe * pframe)
{
Static avpacket packet; // AV package. Static variable.
Static int bytesremaining = 0; // remaining bytes. Static variable.
Static uint8_t * rawdata; // number of original data bytes. Static variable.
Static bool ffirsttime = true; // flag, first time ;. Static variable.
Int bytesdecoded; // The decoded bytes;
Int framefinished; // indicates the completion of frame decoding;
// First time we're called, set packet. Data to null to indicate it
// Doesn' t have to be freed when it is called for the first time, set packet. Data to null to indicate
// It does not need to be released;
If (ffirsttime ){
Ffirsttime = false;
Packet. Data = NULL;
}
// Decodes the packets until a complete frame is decoded;
// Decode packets until we have decoded a complete Frame
While (true)
{
// Work on the current package until we decode all.
// Work on the current packet until we have decoded all of it
While (bytesremaining> 0)
{
// Decode the next chunk of data to decode the next data block
Bytesdecoded = avcodec_decode_video (pcodecctx, pframe,
& Framefinished, rawdata, bytesremaining );
// Was there an error?
If (bytesdecoded <0 ){
Fprintf (stderr, "error while Decoding Frame \ n ");
Return false;
}
Bytesremaining-= bytesdecoded;
Rawdata + = bytesdecoded;
// Did we finish the current frame? Then we can return
If (framefinished) // if we have decoded the current frame, we can return
Return true;
}
// Read the next package and skip all packages that do not belong to this stream;
// Read the next packet, skipping all packets that aren't for this
// Stream
Do {
// Free old packet releases the old package
If (packet. Data! = NULL)
Av_free_packet (& Packet );
// Read new packet to read the new package
If (av_read_frame (pformatctx, & Packet) <0)
Goto loop_exit;
} While (packet. stream_index! = Videostream); // when it is not the video stream to be searched, continue the loop, that is, re-read;
// Wait until the video stream to be found is found and exit the loop;
Bytesremaining = packet. size; // The number of bytes in the record package;
Rawdata = packet. Data ;//
}
Loop_exit:
// Decode the rest of the last frame
Bytesdecoded = avcodec_decode_video (pcodecctx, pframe, & framefinished,
Rawdata, bytesremaining );
// Free last packet
If (packet. Data! = NULL)
Av_free_packet (& Packet );
Return framefinished! = 0;
}
// External API. Open the 264 file and obtain necessary information, such as the number of frames in width and height.
Long _ stdcall open1_file (char * filename, long * out_width,
Long * out_height, long * out_framenum,
Long * out_bufsize)
{
// Register all formats and codecs
Av_regi [FS: Page] ster_all ();
// Open video file // open the video file
If (av_open_input_file (& pformatctx, filename, null, 0, null )! = 0)
Return-1; // couldn't open file if it cannot be opened,-1 is returned.
// Retrieve stream information fetch stream information
If (av_find_stream_info (pformatctx) <0)
Return-1; // couldn't find stream information
// Dump information about file onto standard error
Dump_format (pformatctx, 0, filename, false );
T = clock ();
// Find the first video stream to find the first video stream
Videostream =-1;
For (I = 0; I <pformatctx-> nb_streams; I ++)
If (pformatctx-> streams [I]-> codec-> codec_type = codec_type_video ){
Videostream = I;
Break;
}
If (videostream =-1)
Return-1; // didn't find a video stream
// Obtain the context pointer of a decoder for the video stream;
// Get a pointer to the codec context for the video stream
Pcodecctx = pformatctx-> streams [videostream]-> codec;
// Find the decoder for the video stream to obtain the decoder
Pcodec = avcodec_find_decoder (pcodecctx-> codec_id );
If (pcodec = NULL)
Return-1; // codec not found decoder not found;
// Inform the decoder that we can process the bitstream that has been deleted
// That is to say, the bitstream at the frame boundary can fall into the middle of the package;
// Inform the codec that we can handle truncated bitstreams -- I. e .,
// Bitstreams where frame boundaries can fall in the middle of packets
If (pcodec-> capabilities & codec_cap_truncated)
Pcodecctx-> flags | = codec_flag_truncated;
// Open codec // open the decoder
If (avcodec_open (pcodecctx, pcodec) <0)
Return-1; // cocould not open codec cannot open decoder, return-1;
// Allocate video frame allocation Video Frame
Pframe = avcodec_alloc_frame ();
// Allocate an avframe structure allocates an avframe Structure
Pframeyuv = avcodec_alloc_frame (); // decoded frame
If (pframeyuv = NULL)
Return-1;
// Determine the buffer size required and allocate space;
// Determine required buffer size and allocate Buffer
Numbytes = avpicture_get_size (dest_format, pcodecctx-> width,
Pcodecctx-> height );
Buffer = (uint8_t *) malloc (numbytes );
// Output the width, height, and number of frames to the outside world;
* Out_width = pcodecctx-> width;
* Out_height = pcodecctx-> height;
* Out_framenum = pcodecctx-> frame_number;
* Out_bufsize = numbytes;
// Assign appropriate parts of buffer to image planes in pframergb
// Assign the appropriate part of the buffer to the image panel in pframergb
Avpicture_fill (avpicture *) pframeyuv, buffer, dest_format,
Pcodecctx-> width, pcodecctx-> height );
Return 0;
}
// External API. Close the 264 file and release related resources
Long _ stdcall closesponfile ()
{
// Calculate the decoding Rate
T = clock ()-T;
FPS = (double) (T)/clocks_per_sec;
FPS = I _frame/FPs;
Printf ("\ n ==> decode rate %. 4f FPS! \ N ", FPS );
// Free the YUV image releases the YUV Image
Free (buffer );
Av_free (pframeyuv );
// Free the YUV frame to release the YUV Frame
Av_free (pframe );
// Close the CODEC to disable the decoder
Avcodec_close (pcodecctx );
// Close the video file to close the video file
Av_close_input_file (pformatctx );
Return 0;
}
// External API. Obtains decoded data of a frame.
Long _ stdcall getnextframe (unsigned char * lpdata)
{
// Read Frames
If (getnextframe (pformatctx, pcodecctx, videostream, pframe ))
{
Img_convert (avpicture *) pframeyuv, dest_format, (avpicture *) pframe,
Pcodecctx-> pix_fmt, pcodecctx-> width, pcodecctx-> height );
// For debugging, write a BMP file to drive C;
// Av_create_bmp ("C :\\\ 1.bmp", (unsigned char *) pframeyuv-> data [0], pcodecctx-> width, pcodecctx-> height, 24 );
I _frame ++;
Y_size = pcodecctx-> width * pcodecctx-> height;
// Write a file
/* Fwrite (pframeyuv-> data [0], 1, y_size, FP );
Fwrite (pframeyuv-> data [1], 1, (y_size/4), FP );
Fwrite (pframeyuv-> data [2], 1, (y_size/4), FP );*/
Memcpy (lpdata, pframeyuv-> data [0], y_size * 3 );
Upendbmp (lpdata, pcodecctx-> width, pcodecctx-> height );
Return 0;
}
Else
{Return-1 ;}
}