移植HM到Android实现播放器 - Fla
移植HM到Android实现播放器
我的CSDN博客中详细介绍了其相关内容:
http://blog.csdn.net/luofl1992/article/details/8736149
详细的Android.mk如何编写,之前的文章中有所介绍,这里不再重复介绍了。
本文为相关的代码。
移植到Android不可能完全用java写一个解码器,毕竟java本身效率就不高。
再来笔者的经历有限,不可能在一个月之内重写出HM解码器的代码。
所以考虑其他方式,好在Google公司提供了Android NDK工具,可以帮我们把C/C++程序编译成Android上可用的动态库。
在我的CSDN博客中详细介绍了其相关内容:
http://blog.csdn.net/luofl1992/article/details/8736149
详细的Android.mk如何编写,之前的文章中有所介绍,这里不再重复介绍了。
这里不再详细叙述,此处列出关键的接口代码,这是我的毕业设计的工作的一部分,希望大家慎重使用:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_NcHevc_NcHevcDecoder */ #ifndef _Included_com_NcHevc_NcHevcDecoder #define _Included_com_NcHevc_NcHevcDecoder #include "TLibCommon/CommonDef.h" typedef unsigned char byte; // typedefine type as unsigned char // 实现 Yuv 到 RGB 转换的函数 extern void NcDecoderYUV2RGB(Pel *y, Pel *u, Pel *v, Int stride, Int cstride, UInt *lpDiBits, int width, int height); #ifdef __cplusplus extern "C" { #endif /* * Class: com_NcHevc_NcHevcDecoder * Method: Open * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_Open (JNIEnv *, jobject, jstring); /* * Class: com_NcHevc_NcHevcDecoder * Method: GetPixelsBuffer * Signature: ([I)V */ JNIEXPORT void JNICALL Java_com_NcHevc_NcHevcDecoder_GetPixelsBuffer (JNIEnv *, jobject, jintArray); /* * Class: com_NcHevc_NcHevcDecoder * Method: DecodeFrame * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_DecodeFrame (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
然后是源文件的实现。
#include "NcHevcDecoder.h" #include <list> #include <vector> #include <stdio.h> #include <fcntl.h> #include <assert.h> #include <locale.h> #include <string> using namespace std; #include "TAppDecTop.h" #include "TLibDecoder/AnnexBread.h" #include "TLibDecoder/NALread.h" Bool g_md5_mismatch = false; // top level flag to signal when there is a decode problem class CNcMHevcDecoder : public TAppDecTop { protected: ifstream bitstreamFile; InputByteStream bytestream; Int poc; TComList<TComPic *> *pcListPic; Int width, height; UInt *lpDiBits; Bool recon_opened; // reconstruction file not yet opened. (must be performed after SPS is seen) int last_tId; // mark as the last tId which DecodeFrames() returned, initialed as -2. // static UINT ThreadDecode( LPVOID lpVoid ); int DecodeFrames( void ); // decode a frame if last_tId isn't equal to -2 public: CNcMHevcDecoder() : bytestream(bitstreamFile) , poc(0), recon_opened(false), last_tId(-2), width(416), height(240) { // setlocale(LC_ALL, ".936"); pcListPic = NULL; lpDiBits = NULL; LOGI("0. trying create file: aaaaa1.txt "); FILE *fp = fopen("/mnt/sdcard/aaaaa1.txt","rw"); if ( NULL == fp ) { LOGI(" 0.1. trying create file failed: aaaaa1.txt "); return ; } else { LOGI(" 0.2. create file success: aaaaa1.txt "); fprintf( fp, "123456789\n" ); fclose( fp ); LOGI(" 0.3. write file success: aaaaa1.txt "); } LOGW("HM software: Decoder version [%s] %s", NV_VERSION, NVM_ONOS); LOGW(NVM_BITS); LOGW(NVM_COMPILEDBY); } ~CNcMHevcDecoder() { // delete buffers m_cTDecTop.deletePicBuffer(); // destroy internal classes xDestroyDecLib(); LOGI(" 0.4. CNcMHevcDecoder destroyed! "); TAppDecTop::destroy(); } // 返回下一帧的内容 bool GetNextFrame( void ); bool OpenFile( const char * lpFilePath); bool GetWidthHeight(int &w, int &h) { DecodeFrames(); if ( pcListPic != NULL && pcListPic->size() ) { TComPic* pcPic = *(pcListPic->begin()); width = pcPic->getPicYuvRec()->getWidth(); height = pcPic->getPicYuvRec()->getHeight(); LOGI("Success: Frame width = %d, height = %d", width, height); w = width; h = height; return true; } else { LOGI("Error: Frame width = %d, height = %d", width, height); return false; } } void SetPixels(int *pixels) { lpDiBits = (UInt *)pixels; } }; // 定义一个全局变量 CNcMHevcDecoder decoder; /* * Class: com_NcHevc_NcHevcDecoder * Method: Open * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_Open (JNIEnv *env, jobject obj, jstring str) { /* JNIString str(env, string); JNIString s2(str); s2 += ".txt"; FILE *fp = fopen( s2, "rw" ); if ( NULL != fp ) { fprintf( fp, "12345" ); fclose(fp); } return decoder.OpenFile(str.GetChars()); */ /* LOGI("0. JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_Open"); string s("/mnt/sdcard/aaaa2.txt"); LOGI("1. path: = %s", s.c_str()); FILE *fp = fopen( s.c_str(), "rw" ); if ( NULL != fp ) { LOGI("2. file open succ %s", s.c_str()); fprintf( fp, "12345" ); fclose(fp); } else { LOGW("2. file open failed. %s", s.c_str()); } */ return decoder.OpenFile(env->GetStringUTFChars(str, NULL)); } /* * Class: com_MNcHevc_NcHevcDecoder * Method: GetPixelsBuffer * Signature: ([I)V */ JNIEXPORT void JNICALL Java_com_NcHevc_NcHevcDecoder_GetPixelsBuffer (JNIEnv *env, jobject obj, jintArray pixels) { int width = 412, height = 240; decoder.GetWidthHeight( width, height ); // pixels = env->NewIntArray(width*height); int *arr = env->GetIntArrayElements(pixels, NULL); memset( arr, 0xffffffff, sizeof(int) * env->GetArrayLength(pixels)); // 2013-3-24: 0->0xffffffff LOGI( "pixels buffer = 0x%08x , size = %d", arr, env->GetArrayLength(pixels)); arr[0] = width; arr[1] = height; decoder.SetPixels( arr ); } /* * Class: com_NcHevc_NcHevcDecoder * Method: GetPixelsBuffer * Signature: ([I)V */ JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_DecodeFrame (JNIEnv *env, jobject obj) { return decoder.GetNextFrame(); } // 一些函数的实现 bool CNcMHevcDecoder::OpenFile( const char *lpFilePath ) { TAppDecTop::create(); // LOGI("1. CNcMHevcDecoder::OpenFile : %s", lpFilePath); if( !TAppDecTop::parseCfg( lpFilePath ) ) { LOGI("2. CNcMHevcDecoder::OpenFile :parse Configure error : %s", lpFilePath); TAppDecTop::destroy(); return false; } bitstreamFile.open( (char const *)m_pchBitstreamFile, ifstream::in | ifstream::binary ); if( !bitstreamFile ) { LOGI("3. CNcMHevcDecoder::OpenFile :reading file error : %s", lpFilePath); return false; } //LOGI("4. CNcMHevcDecoder::OpenFile : success "); // create & initialize internal classes xCreateDecLib(); //LOGI("5. CNcMHevcDecoder::OpenFile : xCreateDecLib succ "); xInitDecLib(); //LOGI("6. CNcMHevcDecoder::OpenFile : xInitDecLib succ "); m_iPOCLastDisplay += m_iSkipFrame; // set the last displayed POC correctly for skip forward. //LOGI("7. CNcMHevcDecoder::OpenFile : Finished"); return true; } int CNcMHevcDecoder::DecodeFrames( void ) { // main decode loop // LOGI("----0. CNcMHevcDecoder::DecodeFrames()"); if ( last_tId != -2 ) return last_tId; // last decoded frame not been handled by GetNextFrame(). while ( !!bitstreamFile ) { /* location serves to work around a design fault in the decoder, whereby * the process of reading a new slice that is the first slice of a new frame * requires the TDecTop::decode() method to be called again with the same * nal unit. */ // LOGI(" 1. CNcMHevcDecoder::DecodeFrames()"); streampos location = bitstreamFile.tellg(); AnnexBStats stats = AnnexBStats(); Bool bPreviousPictureDecoded = false; vector<uint8_t> nalUnit; InputNALUnit nalu; byteStreamNALUnit( bytestream, nalUnit, stats ); // call actual decoding function Bool bNewPicture = false; // LOGI("----2. CNcMHevcDecoder::DecodeFrames()"); if( nalUnit.empty() ) { /* this can happen if the following occur: * - empty input file * - two back-to-back start_code_prefixes * - start_code_prefix immediately followed by EOF */ // LOGW( "----Warning: Attempt to decode an empty NAL unit\n" ); } else { // LOGI("----3. CNcMHevcDecoder::DecodeFrames()"); read( nalu, nalUnit ); if( ( m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer ) || !isNaluWithinTargetDecLayerIdSet( &nalu ) ) { // LOGI("----4. CNcMHevcDecoder::DecodeFrames()"); if( bPreviousPictureDecoded ) { bNewPicture = true; bPreviousPictureDecoded = false; } else { bNewPicture = false; } } else { // LOGI("----5. CNcMHevcDecoder::DecodeFrames()"); bNewPicture = m_cTDecTop.decode( nalu, m_iSkipFrame, m_iPOCLastDisplay ); if( bNewPicture ) { bitstreamFile.clear(); /* location points to the current nalunit payload[1] due to the * need for the annexB parser to read three extra bytes. * [1] except for the first NAL unit in the file * (but bNewPicture doesn't happen then) */ bitstreamFile.seekg( location - streamoff( 3 ) ); bytestream.reset(); } bPreviousPictureDecoded = true; } } if( bNewPicture || !bitstreamFile ) { // 执行环路滤波 // LOGI("----6. CNcMHevcDecoder::DecodeFrames() 执行环路滤波"); m_cTDecTop.executeLoopFilters( poc, pcListPic); } if( pcListPic ) { // LOGI("----7. CNcMHevcDecoder::DecodeFrames() pcListPic Not Null"); if( m_pchReconFile && !recon_opened ) { if( !m_outputBitDepthY ) { m_outputBitDepthY = g_bitDepthY; } if( !m_outputBitDepthC ) { m_outputBitDepthC = g_bitDepthC; } m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode recon_opened = true; } // write reconstruction to file if( bNewPicture ) { /* if( nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ){ LOGI("----8. CNcMHevcDecoder::DecodeFrames() new picture"); // xFlushOutput( pcListPic ); // 写解码结果到文件 } */ // 这里修改成传递出去一个新的图像帧用于界面显示 // xWriteOutput( pcListPic, nalu.m_temporalId ); // LOGI("----9. CNcMHevcDecoder::DecodeFrames() new picture"); return last_tId = nalu.m_temporalId; } } // LOGI(" 10. CNcMHevcDecoder::DecodeFrames() pcList NUll"); } // LOGI("----11. CNcMHevcDecoder::DecodeFrames() decode error"); return -1; } /* * 把YUV转换成RGB */ inline byte clip(const Pel x) { return (x > 255) ? 255 : (x < 0 ? 0 : x); } // 是否采用快速转换 #if 1 //#define FAST_TABLE_SEARCH_YUV2RGB #ifdef FAST_TABLE_SEARCH_YUV2RGB static const Pel dFAC_B[256] = { -226, -224, -222, -220, -219, -217, -215, -213, -212, -210, -208, -206, -205, -203, -201, -199, -197, -196, -194, -192, -190, -189, -187, -185, -183, -182, -180, -178, -176, -174, -173, -171, -169, -167, -166, -164, -162, -160, -158, -157, -155, -153, -151, -150, -148, -146, -144, -143, -141, -139, -137, -135, -134, -132, -130, -128, -127, -125, -123, -121, -119, -118, -116, -114, -112, -111, -109, -107, -105, -104, -102, -100, -98, -96, -95, -93, -91, -89, -88, -86, -84, -82, -81, -79, -77, -75, -73, -72, -70, -68, -66, -65, -63, -61, -59, -57, -56, -54, -52, -50, -49, -47, -45, -43, -42, -40, -38, -36, -34, -33, -31, -29, -27, -26, -24, -22, -20, -18, -17, -15, -13, -11, -10, -8, -6, -4, -3, -1, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23, 25, 27, 28, 30, 32, 34, 35, 37, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 64, 66, 67, 69, 71, 73, 74, 76, 78, 80, 82, 83, 85, 87, 89, 90, 92, 94, 96, 97, 99, 101, 103, 105, 106, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 126, 128, 129, 131, 133, 135, 136, 138, 140, 142, 144, 145, 147, 149, 151, 152, 154, 156, 158, 159, 161, 163, 165, 167, 168, 170, 172, 174, 175, 177, 179, 181, 183, 184, 186, 188, 190, 191, 193, 195, 197, 198, 200, 202, 204, 206, 207, 209, 211, 213, 214, 216, 218, 220, 221, 223, 225 }; static const Pel dFAC_R[256] = { -178, -177, -176, -174, -173, -171, -170, -169, -167, -166, -164, -163, -162, -160, -159, -157, -156, -155, -153, -152, -150, -149, -148, -146, -145, -143, -142, -141, -139, -138, -136, -135, -134, -132, -131, -129, -128, -127, -125, -124, -122, -121, -120, -118, -117, -115, -114, -113, -111, -110, -108, -107, -106, -104, -103, -101, -100, -99, -97, -96, -94, -93, -92, -90, -89, -87, -86, -85, -83, -82, -80, -79, -78, -76, -75, -73, -72, -71, -69, -68, -66, -65, -63, -62, -61, -59, -58, -56, -55, -54, -52, -51, -49, -48, -47, -45, -44, -42, -41, -40, -38, -37, -35, -34, -33, -31, -30, -28, -27, -26, -24, -23, -21, -20, -19, -17, -16, -14, -13, -12, -10, -9, -7, -6, -5, -3, -2, 0, 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14, 15, 17, 18, 20, 21, 22, 24, 25, 27, 28, 29, 31, 32, 34, 35, 36, 38, 39, 41, 42, 43, 45, 46, 48, 49, 50, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66, 67, 69, 70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 86, 87, 88, 90, 91, 93, 94, 95, 97, 98, 100, 101, 102, 104, 105, 107, 108, 109, 111, 112, 114, 115, 116, 118, 119, 121, 122, 123, 125, 126, 128, 129, 130, 132, 133, 135, 136, 137, 139, 140, 142, 143, 144, 146, 147, 149, 150, 151, 153, 154, 156, 157, 158, 160, 161, 163, 164, 165, 167, 168, 170, 171, 172, 174, 175, 177, 178 }; static const Pel dFAC_GU[256] = { 44, 44, 43, 43, 43, 42, 42, 42, 41, 41, 41, 40, 40, 40, 39, 39, 39, 38, 38, 38, 37, 37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, 26, 26, 26, 25, 25, 25, 24, 24, 24, 23, 23, 23, 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -2, -2, -2, -3, -3, -3, -4, -4, -5, -5, -5, -6, -6, -6, -7, -7, -7, -8, -8, -8, -9, -9, -9, -10, -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14, -14, -14, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -24, -24, -24, -25, -25, -25, -26, -26, -27, -27, -27, -28, -28, -28, -29, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -39, -39, -39, -40, -40, -40, -41, -41, -41, -42, -42, -42, -43 }; static const Pel dFAC_GV[256] = { 91, 91, 90, 89, 89, 88, 87, 86, 86, 85, 84, 84, 83, 82, 81, 81, 80, 79, 79, 78, 77, 76, 76, 75, 74, 74, 73, 72, 71, 71, 70, 69, 69, 68, 67, 66, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57, 56, 56, 55, 54, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 46, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 39, 38, 37, 36, 36, 35, 34, 34, 33, 32, 31, 31, 30, 29, 29, 28, 27, 26, 26, 25, 24, 24, 23, 22, 21, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 14, 13, 12, 11, 11, 10, 9, 9, 8, 7, 6, 6, 5, 4, 4, 3, 2, 1, 1, 0, 0, 0, -1, -2, -3, -3, -4, -5, -5, -6, -7, -8, -8, -9, -10, -10, -11, -12, -13, -13, -14, -15, -15, -16, -17, -18, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -33, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -43, -43, -44, -45, -45, -46, -47, -48, -48, -49, -50, -50, -51, -52, -53, -53, -54, -55, -55, -56, -57, -58, -58, -59, -60, -60, -61, -62, -63, -63, -64, -65, -65, -66, -67, -68, -68, -69, -70, -70, -71, -72, -73, -73, -74, -75, -75, -76, -77, -78, -78, -79, -80, -80, -81, -82, -83, -83, -84, -85, -85, -86, -87, -88, -88, -89, -90 }; #endif // TComPicYuv *pcPicYuv := 要转换YUV图像 // pRGB := bmp 的颜色开始 // void NcDecoderYUV2RGB(TComPicYuv *pcPicYuv, byte *pRGB, Int width, Int height) { if ( NULL == pRGB ) return; Pel *y = pcPicYuv->getLumaAddr(), // 重建图像的 Y 颜色开始地址 *u = pcPicYuv->getCbAddr(), // 重建图像的 Cb 颜色开始地址 *v = pcPicYuv->getCrAddr(); // 重建图像的 Cr 颜色开始地址 // byte *pRGB = (byte *)lpDiBits; const Int dY = pcPicYuv->getStride(); // YUV 一行的一个y值对应下一行的同样位置的位置增量 const Int dRGB = width << 2; // bmp 一行的某个像素的下一行同样位置的像素值偏移 const Int dY2 = dY + 1, dRGB2 = dRGB + 4; const Int d2Y = (dY << 1) - width; // YUV Y 像素偏移值的修正 const Int dUV = pcPicYuv->getCStride() - width / 2; // YUV UV像素偏移值的修正 Pel dB, dG, dR, Cb, Cr, y0, y1, y2, y3; Int i = 0, j = 0; height >>= 1 ; width >>= 1 ; // 行数和列数均减半 for ( j = 0; j < height; j++ ) { for ( i = 0; i < width; i++ ) { // ARGB_8888: 四个像素值依次为 BGRA (已经验证,同时A值应为255表示不透明) /* dR = 1.13983 * ( *v - 128 ); dG = -0.39465 * ( *u - 128 ) - 0.58060 * ( *v - 128 ); dB = 2.03211 * ( *u - 128 ); */ // or // a fast mode using shifting. y0 = *y; y1 = *(y+1); y2 = *(y+dY); y3 = *(y+dY2); #ifdef FAST_TABLE_SEARCH_YUV2RGB // 查表法速度最快,但和下面的运算法相比并不能加快很多 约提升2% Cb = *u; Cr = *v; dB = dFAC_B[Cb]; dR = dFAC_R[Cr]; dG = dFAC_GU[Cb] + dFAC_GV[Cr]; #else Cb = *u - 128; Cr = *v - 128; #if 1 // 测试表明,以下三种: // 这种效率最优,即采用临时变量,可以节约运算操作次数 dB = Cb + (Cb>>1) + (Cb>>2) + (Cb>>6); dR = Cr + (Cr>>2) + (Cr>>3); dG = - ((Cb>>2) + (Cb>>4) + (Cb>>5) ) - ((dR>>1) + (Cr>>5)); // - ((Cr>>1) + (Cr>>3) + (Cr>>4) + (Cr>>5)) dR += (Cr>>5); // Cr + (Cr>>2) + (Cr>>3) + (Cr>>5); #elif 1 // 这种结果次优 dB = Cb + (Cb>>1) + (Cb>>2) + (Cb>>6); dG = - ((Cb>>2) + (Cb>>4) + (Cb>>5) ) - ((Cr>>1) + (Cr>>3) + (Cr>>4) + (Cr>>5)); dR = Cr + (Cr>>2) + (Cr>>3) + (Cr>>5); #else // 这种直接乘法的效率最低 dR = 1.402* Cr; dG = -0.344* Cb - 0.714*Cr; dB = 1.772 * Cb; #endif #endif *( pRGB) = clip(y0 + dB); // B *(pRGB+ 4 ) = clip(y1 + dB); *(pRGB+dRGB) = clip(y2 + dB); *(pRGB+dRGB2) = clip(y3 + dB); *(++pRGB) = clip(y0 + dG); // G *(pRGB+ 4 ) = clip(y1 + dG); *(pRGB+dRGB) = clip(y2 + dG); *(pRGB+dRGB2) = clip(y3 + dG); *(++pRGB) = clip(y0 + dR); // R *(pRGB+ 4 ) = clip(y1 + dR); *(pRGB+dRGB) = clip(y2 + dR); *(pRGB+dRGB2) = clip(y3 + dR); pRGB += 6; // 总计两个RGBA像素,指针需要移动 8 个字节,补齐 // 初始化中已经对 A 赋值,故此处不再处理 2013-3-24 y+=2; u++; v++; // 横向两个点才变化一次,因为其表示了 2 * 2个点的像素值 } // for loop (i < width) pRGB += dRGB; // 跳过一行,因为上面是两行两行的处理,每次处理了 4 个点 y += d2Y; // y 像素值的偏移位置修正(两行) u += dUV; // u 像素值偏移的修正 v += dUV; // v 像素值偏移的修正 } // for loop (j < height) } #else void NcDecoderYUV2RGB(TComPicYuv *pcPicYuv, byte *pRGB, int width, int height) { if ( NULL == pRGB ) return; Pel *y = pcPicYuv->getLumaAddr(), // 重建图像的 Y 颜色开始地址 *u = pcPicYuv->getCbAddr(), // 重建图像的 Cb 颜色开始地址 *v = pcPicYuv->getCrAddr(); // 重建图像的 Cr 颜色开始地址 // UInt *p = lpDiBits; UInt dy = pcPicYuv->getStride(); // 一行的一个y值对应下一行的同样位置的位置增量 UInt dUV = (pcPicYuv->getCStride() - width / 2); // 像素值偏移的修正 Int dR = 0, dG = 0, dB = 0; for ( int j = 0; j < height; j++ ) { for ( int i = 0; i < width; i+=2 ) { // ARGB_8888: 四个像素值依次为 BGRA (已经验证,同时A值应为255表示不透明) #if 1 dR = 1.402*(*v-128); dG = -0.344*(*u-128) - 0.714*(*v-128); dB = 1.772 * ( *u - 128 ); #else dR = 1.13983 * ( *v - 128 ); dG = -0.39465 * ( *u - 128 ) - 0.58060 * ( *v - 128 ); dB = 2.03211 * ( *u - 128 ); #endif *pRGB++ = clip(*y + dB); // B *pRGB++ = clip(*y + dG); // G *pRGB++ = clip(*y + dR); // R *pRGB++; // = 255; // A y++; *pRGB++ = clip(*y + dB); // B *pRGB++ = clip(*y + dG); // G *pRGB++ = clip(*y + dR); // R *pRGB++; // = 255; // A // 以下:用于测试各个位表示什么颜色 2013-3-14 已经验证按指针从低到高访问时依次为 BGRA /* *pRGB++ = 255; *pRGB++ = 0; *pRGB++ = 0; *pRGB++ = 128; y++; *pRGB++ = 255; *pRGB++ = 0; *pRGB++ = 0; *pRGB++ = 128; // 用32位来处理,效率反而降低了 *p++ = ((UInt( *y + 1.772 * *u - 0xE2) & 0xff) << 0) // B | ((UInt( *y - 0.34413 * *u - 0.71414 * *v + 0x87) & 0xff) << 8) // G | ((UInt( *y + 1.402 * *v - 0xAB) & 0xff) << 16 ) // R | 0xff000000; y++; *p++ = ((UInt( *y + 1.772 * *u - 0xE2) & 0xff) << 0) // B | ((UInt( *y - 0.34413 * *u - 0.71414 * *v + 0x87) & 0xff) << 8) // G | ((UInt( *y + 1.402 * *v - 0xAB) & 0xff) << 16 ) // R | 0xff000000; */ // *p++ = 0xff0000ff; y++; *p++ = 0xff0000ff; y++; u++; v++; // 横向两个点才变化一次,因为其表示了 2 * 2个点的像素值 } y += ( pcPicYuv->getStride() - width); // 像素值的偏移位置修正 if ( j % 2 == 0 ) { u -= width >> 1; v -= width >> 1; // 偶数行不变 } else { // 奇数行(从0开始编号)才变化,也就是到下一个偶数像素点时改变了 u += dUV; // 像素值偏移的修正 v += dUV; // 像素值偏移的修正 } } // for loop (j < height2) } #endif bool CNcMHevcDecoder::GetNextFrame(void) { // LOGE(">>>>0. CNcMHevcDecoder::GetNextFrame()"); static long tm = 0; int tId = DecodeFrames(); last_tId = -2; // mark it as -2 to make DecodeFrames() be called the last time. if ( NULL == pcListPic ) return false; TComList<TComPic*>::iterator iterPic = pcListPic->begin(); Int not_displayed = 0; assert( lpDiBits != NULL ); // 检测是否为空指针 while (iterPic != pcListPic->end()) { TComPic* pcPic = *(iterPic); if(pcPic->getOutputMark() && pcPic->getPOC() > m_iPOCLastDisplay) not_displayed++; iterPic++; } if ( 0 == not_displayed && -1 == tId ) { LOGE(">>>>>>>> HM software <<<<<<<<"); LOGW("Decoder version [%s] %s", NV_VERSION, NVM_ONOS); LOGW(NVM_BITS); LOGW(NVM_COMPILEDBY); return false; } while (1) { iterPic = pcListPic->begin(); while (iterPic != pcListPic->end()) { TComPic* pcPic = *(iterPic); // LOGI(">>>>1. CNcMHevcDecoder::GetNextFrame() not_displayed = %d, %d, %d, %d", // not_displayed, pcPic->getNumReorderPics(tId), pcPic->getPOC(), m_iPOCLastDisplay ); // if ( pcPic->getOutputMark() && (not_displayed > pcPic->getNumReorderPics(tId) && pcPic->getPOC() > m_iPOCLastDisplay)) if ( pcPic->getOutputMark() && not_displayed > pcPic->getNumReorderPics(tId) && pcPic->getPOC() > m_iPOCLastDisplay ) { // write to file not_displayed--; // LOGI(">>>>1.0 CNcMHevcDecoder::GetNextFrame() YUV to RGB"); clock_t t = clock(); #if 0 NcDecoderYUV2RGB( pcPic->getPicYuvRec(), (byte *)lpDiBits, width, height); #else NcDecoderYUV2RGB( pcPic->getPicYuvRec()->getLumaAddr(), // y pcPic->getPicYuvRec()->getCbAddr(), // u pcPic->getPicYuvRec()->getCrAddr(), // v pcPic->getStride(), pcPic->getCStride(), // strides lpDiBits, width, height); // bmp #endif tm += clock() - t; LOG("tm(YUV2RGB) = %5d.%03d", tm / 1000, tm % 1000); poc++; // LOGI(">>>>1.1. poc %03d decoded. not_displayed = %d", poc, not_displayed); /* if ( m_pchReconFile ) { CroppingWindow &crop = pcPic->getCroppingWindow(g); m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(), crop.getPicCropLeftOffset(), crop.getPicCropRightOffset(), crop.getPicCropTopOffset(), crop.getPicCropBottomOffset() ); } // update POC of display order */ m_iPOCLastDisplay = pcPic->getPOC(); // erase non-referenced picture in the reference picture list after display pcPic->setOutputMark(false); return true; } // if not displayed > ... // 释放已经输出且无依赖关系的帧 if ( !pcPic->getSlice(0)->isReferenced() && pcPic->getOutputMark() == false ) { // LOGI(">>>>1.2. poc %03d destroyed", poc); pcPic->destroy(); pcListPic->erase( iterPic ); iterPic = pcListPic->begin(); } else { iterPic++; // next decoded frame. } } // while (iterPic != pcListPic->end()) // 再解码一帧图像,以得到应输出显示的下一帧图像 // 可以保证一定能够在某次循环中退出这个while(1)循环 tId = DecodeFrames(); last_tId = -2; } // while ( 1 ) // LOGI(">>>>2. CNcMHevcDecoder::GetNextFrame() Finished"); // LOGE( ">>>> %d", tId != -1 ); return false; }