很久以前,我就想在android做一个复读机程序,用来播放我喜欢的我从网上下的《老友记》,但编一个mp3播放机当然很容易,但其它格式只能借助其它解码工具了,于是在网上找了一堆关于ffmpeg的资料。其它能完成音频播放的工能,欲发几篇文章,一来记录下这几天成果,二是然望能同修们有帮助。
首先的的开发环境是ubuntu,用的是ffmpeg1.0,现在网上很多资料都在很以前比较老的版本,
网上已有很多关于ffmpeg的下载和在lunix底下编译的文章,这里不不多作介绍了,之于利用ndk编译我以后有时间,会说下自己观点和看法。
提取音频程序与解码可能参考 fmpeg-1.0/doc/examples/filtering_audio.c和ffmpeg-1.0/doc/examples/decoding_encoding.c;ffmpeg.c和ffplay太过复杂,对于我这个急于求成的外行来说不太适宜。网上有几篇文章很好比如,
http://ushertechblog.sinaapp.com/post-24.html
#include <stdio.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL.h> #define SDL_AUDIO_BUFFER_SIZE 1024 typedef struct PacketQueue { AVPacketList * first_pkt, *last_pkt; int nb_packets; int size; SDL_mutex *mutex; //mutex主要是用来实现资源的互拆的,跟java里在synchronize关键作用有相似之处 //,虽然本和序中没有用多线程,但sdl在播放另启了一个线程。 SDL_cond * cond; } PacketQueue; PacketQueue audioq; void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if (av_dup_packet(pkt) < 0) return -1; pkt1 = av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; SDL_LockMutex(q->mutex); if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } int quit = 0; static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); for (;;) { if (quit) { ret = -1; break; } pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); //这招我很赞赏,他在取去一个packet后,将上一个paket置空,而在下面的程序就不用再调用av_free操作了 ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } //int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) { int audio_decode_frame(AVCodecContext *aCodecCtx, AVFrame *frame, uint8_t *audio_buf) { static AVPacket pkt_temp; int len1, data_size, got_frame; int new_packet; for (;;) { while (pkt_temp.size > 0 || (!pkt_temp.data && new_packet)) { if (!frame) { if (!(frame = avcodec_alloc_frame())) return AVERROR(ENOMEM); } else { avcodec_get_frame_defaults(frame); } new_packet = 0; len1 = avcodec_decode_audio4(aCodecCtx, frame, &got_frame, &pkt_temp); if (len1 < 0) { /* if error, skip frame */ pkt_temp.size = 0; break; } pkt_temp.data += len1; pkt_temp.size -= len1; if (got_frame <= 0) /* No data yet, get more frames */ continue; data_size = av_samples_get_buffer_size(NULL, aCodecCtx->channels, frame->nb_samples, aCodecCtx->sample_fmt, 1); memcpy(audio_buf, frame->data[0], frame->linesize[0]); /* We have data, return it and come back for more later */ return data_size; } if (quit) return -1; if ((new_packet = packet_queue_get(&audioq, &pkt_temp, 1)) < 0) return -1; } } void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *aCodecCtx = (AVCodecContext *) userdata; //以后变量全定义成static ,确保下次循环,变量不会被初始化 static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; static unsigned int audio_buf_remain_size=0; //记录下audio_buffer剩余数据量 static unsigned int audio_buf_total_size=0; //记录下audio_buffer总数据量 static unsigned int audio_buf_index = 0; int read_size; //第次送入*stream中数据的真正长度,理论值是len,但在最后一次操作实际值可能会小于len; AVFrame *frame = NULL; int flag=0; while(len){ if(audio_buf_index>=audio_buf_total_size){ audio_buf_remain_size = audio_decode_frame(aCodecCtx, frame, audio_buf); audio_buf_total_size=audio_buf_remain_size; audio_buf_index=0; if(audio_buf_total_size<0){ audio_buf_remain_size=audio_buf_total_size = 1024; memset(audio_buf, 0, audio_buf_total_size); continue; } } read_size=(audio_buf_remain_size > len)? len : audio_buf_remain_size; memcpy(stream, (uint8_t *) audio_buf + audio_buf_index, read_size); audio_buf_index += read_size; audio_buf_remain_size -= read_size; stream += read_size; len -= read_size; } } int main(int argc, char *argv[]) { AVFormatContext *pFormatCtx; int i, videoStream, audioStream; AVCodecContext *pCodecCtx, *aCodecCtx; AVCodec *pCodec, *aCodec; AVFrame *pFrame; AVFrame *pFrameRGB; AVPacket packet; SDL_Event event; int frameFinished; int numBytes; uint8_t *buffer; if (argc < 2) { printf("Please provide a movie file\n"); return -1; } if (SDL_Init(SDL_INIT_AUDIO)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); exit(1); } av_register_all(); pFormatCtx = avformat_alloc_context(); // Open video file //if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0) if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL ) != 0) return -1; // Couldn't open file // Retrieve stream information if (avformat_find_stream_info(pFormatCtx, NULL ) < 0) return -1; // Couldn't find stream information // Dump information about file onto standard error av_dump_format(pFormatCtx, 0, argv[1], 0); // Find the first video stream videoStream = -1; audioStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0) videoStream = i; if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0) audioStream = i; } if (videoStream == -1) return -1; // Didn't find a video stream if (audioStream == -1) return -1; // Get a pointer to the codec context for the video&audio stream pCodecCtx = pFormatCtx->streams[videoStream]->codec; aCodecCtx = pFormatCtx->streams[audioStream]->codec; SDL_AudioSpec wanted_spec, spec; wanted_spec.freq = aCodecCtx->sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = aCodecCtx->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = audio_callback; wanted_spec.userdata = aCodecCtx; if (SDL_OpenAudio(&wanted_spec, &spec) < 0) { fprintf(stderr, "SDL_OpenAudio:%s\n", SDL_GetError()); return -1; } // Find the decoder for the video stream pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL ) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Find the decoder for the video stream aCodec = avcodec_find_decoder(aCodecCtx->codec_id); if (aCodec == NULL ) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if (avcodec_open2(aCodecCtx, aCodec, NULL ) < 0) return -1; // Could not open codec // Open codec if (avcodec_open2(pCodecCtx, pCodec, NULL ) < 0) return -1; // Could not open codec packet_queue_init(&audioq); SDL_PauseAudio(0); //Set up a screen while (av_read_frame(pFormatCtx, &packet) >= 0) { // Is this a packet from the video stream? if (packet.stream_index == audioStream) { packet_queue_put(&audioq, &packet); } else { av_free_packet(&packet); } SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: quit = 1; SDL_Quit(); exit(0); break; default: break; } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); // Free the RGB image for (;;) { getchar(); //这个是用来阻塞主线程,也可以用来接收键盘命令,比如暂停播放之类的 break; } // Close the codec avcodec_close(pCodecCtx); avcodec_close(aCodecCtx); // Close the video file avformat_close_input(&pFormatCtx); return 0; }
使用poi来生成xls文件:引用的包poi-3.2.final.jar;
要了解的类HSSFWorkbook,HSSFSheet,HSSFRow,HSSFCell
HSSFWorkbook workbook=new HSSFWorkbook();得到一个Excel文件的引用
HSSFSheet sheet=workbook.createSheet();得到一个sheet,还有HSSFSheet sheet=workbook.createSheet("sheet1");方法可以设置Sheet的名称。
然后我们可以得到一行HSSFRow row=sheet.createRow(rowNum);
最后我们可以得到的是HSSFCell cell=row.create(cellNum);在Cell中还有一个类型的问题,一定要注意
下面是一个从数据库里面读取数据生成文件的方法
public void testQuery() {
Transaction tx = null;
tx = session.beginTransaction();
Query query = session.createQuery("from Tid");
List<Tid> list = query.list();
tx.commit();
int k = 0;
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("sheet1");
HSSFRow row = sheet.createRow(k);
k++;
//得到一个HSSFCell对象
HSSFCell cell1 = row.createCell(0);
//首先要设置HSSFCell的类型,一共有五种CELL_TYPE_STRING,CELL_TYPE_NUMBERIC,CELL_TYPE_FORMULA,
//CELL_TYPE_BLANK,CELL_TYPE_BOOLEAN,CELL_TYPE_ERROR,共6种
//具体可以查看文档http://poi.apache.org/apidocs/org/apache/poi/hssf/usermodel/HSSFCell.html#getCellType()
cell1.setCellType(HSSFCell.CELL_TYPE_STRING);
cell1.setCellValue((new HSSFRichTextString("MER_NUM")));
HSSFCell cell2 = row.createCell(1);
cell1.setCellType(HSSFCell.CELL_TYPE_STRING);
cell2.setCellValue(new HSSFRichTextString("TML_NUM"));
HSSFCell cell3 = row.createCell(2);
cell1.setCellType(HSSFCell.CELL_TYPE_STRING);
cell3.setCellValue(new HSSFRichTextString("COUNTER_NUM"));
for (Iterator<Tid> it = list.iterator(); it.hasNext();) {
Tid tid = (Tid) it.next();
HSSFRow row_ = sheet.createRow((short) k);
HSSFCell c1 = row_.createCell(0);
c1.setCellType(HSSFCell.CELL_TYPE_STRING);
c1.setCellValue((new HSSFRichTextString(tid.getMer_num())));
HSSFCell c2 = row_.createCell(1);
c2.setCellType(HSSFCell.CELL_TYPE_STRING);
c2.setCellValue(new HSSFRichTextString(tid.getTml_num()));
HSSFCell c3 = row_.createCell(2);
c3.setCellType(HSSFCell.CELL_TYPE_STRING);
c3.setCellValue(new HSSFRichTextString(tid.getCounter_num()));
k++;
}
//新建输出流对象FileOutputStream
FileOutputStream fos = null;
try {
fos = new FileOutputStream("D://T99_TID_RELA.xls");
//然后写入HSSFWorkbook中
workbook.write(fos);
fos.flush();
} catch (Exception e) { // TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
System.out.println("文件生成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
HSSFCell类型判断与取值的方法
public String getCellValue(HSSFCell cell)throws Exception{
int cellType=cell.getCellType();
String cellValue=null;
switch(cellType){
case HSSFCell.CELL_TYPE_NUMERIC:
cellValue = String.valueOf(cell.getNumericCellValue());
/*
*DecimalFormat df=new DecimalFormat("#");
*cellValue = df.format(cell.getNumericCellValue());
*DecimalFormat表示的数据格式化,有很多中用法
*/
break;
case HSSFCell.CELL_TYPE_FORMULA:
cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case HSSFCell.CELL_TYPE_BLANK:
cellValue=" ";
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
break;
case HSSFCell.CELL_TYPE_ERROR:
break;
default:
throw new IllegalArgumentException("数据格式不对");
}
return cellValue;
}
解析一个Excel文件
public void testRead() {
HSSFWorkbook workbook = null;
DecimalFormat df = new DecimalFormat("#");
try {
workbook = new HSSFWorkbook(new FileInputStream(new File(
"D://Book1.xls")));
//POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(filePath));
// 获得Sheet数
System.out
.println("===SheetsNum===" + workbook.getNumberOfSheets());
// 开始读取
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
if (null != workbook.getSheetAt(i)) {
// 获得一个Sheet
HSSFSheet sheet = workbook.getSheetAt(i);
System.out.println("共有" + sheet.getLastRowNum() + "行");
for (int rowNumOfSheet = 10; rowNumOfSheet <= sheet
.getLastRowNum(); rowNumOfSheet++) {
if (null != sheet.getRow(rowNumOfSheet)) {
// 获得一个row
HSSFRow row = sheet.getRow(rowNumOfSheet);
System.out.print("第" + rowNumOfSheet + "行 ");
// System.out.print("一行有"+row.getLastCellNum()+"单元格");
for (short cellNumOfRow = 0; cellNumOfRow < row
.getLastCellNum(); cellNumOfRow++) {
HSSFCell cell = row.getCell(cellNumOfRow);
int cellType = cell.getCellType();
switch (cellType) {
case 0:// Numberic
String strCell = df.format(cell.getNumericCellValue());
System.out.print(strCell + " ");
break;
case 1:
strCell = cell.getRichStringCellValue().getString();
System.out.print(strCell + " ");
break;
default:
System.out.println("数据格式不对!");
}
}
System.out.print("/n");
}
}
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
工具集HSSFCellUtil http://poi.apache.org/apidocs/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.html
设置Sheet名称和单元格内容为中文
workbook.setSheetName(n,"库存",HSSFCell.ENCODING_UTF_16);
cell.setEncoding((short)1);cell.setCellValue("单位");
设置列宽和行高
sheet.setColumnWidth((short)column,(short)width);
row.setHeight((short)height);
添加区域,合并单元格
Region region=new Region((short)rowForm,(short)columnForm,(short)rowTo,(short)columnTo);
sheet.addMergedRegion(region);
// 得到该区域
sheet.getNumMergedRegion();
设置字体和内容位置
HSSFFont font=workbook.createFont();
font.setFontHeightInPoints((short) 11);//字号
font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);//加粗
style.setFont(font);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//左右居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//上下居中
style.setRotation((short)rotation);//单元格内容的旋转的角度
HSSFDataFormat df = wb.createDataFormat();
style1.setDataFormat(df.getFormat("0.00%"));//设置单元格数据格式
cell.setCellFormula(string);//给单元格设公式
style.setRotation((short)rotation);//单元格内容的旋转的角度
cell.setCellStyle(style)
常用的单元格边框格式
public static HSSFCellStyle getCellStyle(short type) {
HSSFWorkbook workbook =new HSSFWorkbook();
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(type);//下边框
style.setBorderLeft(type);//左边框
style.setBorderRight(type);//右边框
style.setBorderTop(type);//上边框
return style;
}
虚线HSSFCellStyle.BORDER_DOTTED
实线HSSFCellStyle.BORDER_THIN
最后是插入图片
/先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
BufferedImage bufferImg = ImageIO.read(new File("ok.jpg"));
ImageIO.write(bufferImg,"jpg",byteArrayOut);
//读进一个excel模版
FileInputStream fos = new FileInputStream(filePathName+"/stencil.xlt");
fs = new POIFSFileSystem(fos);
//创建一个工作薄
HSSFWorkbook wb = new HSSFWorkbook(fs);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor(0,0,1023,255,(short) 0,0,(short)10,10);
patriarch.createPicture(anchor , wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));
1.什么是类别?
为现有的类添加新的方法。"category"
声明类别:
@interface NSString (NumberConvenience) - (NSNumber *) lengthAsNumber; @end // NumberConvenience
实现类别:
@implementation NSString (NumberConvenience) -(NSNumber *) lengthAsNumber { unsigned int length = [self length]; return ([Number numberWithUnsignedInt: length]); }//lengthAsNumber @end //NumberConvenience
调用方法:
[dic setObject:[@"hello" lengthAsNumber] forKey:@"hello"];
2.类别的局限性:
第一:无法向类中添加新的实例变量。(可以使用全局字典存储对象与你想要关联的额外变量之间的映射,但也要考虑是否s是完成当前任务的最佳选择)
第二:名称冲突,即类别中的方法与现有的方法重名。当发生名称冲突时,类别具有更高的优先级。你的类别方法将完全取代初始方法,从而无法再使用初始方法,增加前缀是个不错的选择。
3.类表的优点(作用)
第一:类的实现不能分散到多个不同的.m文件中。而类别的实现则可以分散到多个不同的.m文件或多个不同的框架中。
eg:
@interface CategoryThing : NSObject{
int thing1; int thing2; int thing3; } @end //CategoryThing //3个类别声明 @interface CategoryThing (Thing1) - (void) setThing1 :(int) thing1; - (int) thing1; @end // CategoryThing (Thing1) @interface CategoryThing (Thing2) - (void) setThing2 :(int) thing2; - (int) thing2; @end // CategoryThing (Thing2) @interface CategoryThing (Thing3) - (void) setThing3 :(int) thing3; - (int) thing3; @end // CategoryThing (Thing3)
实现:
CategoryThing的实现:
#import "CategoryThing.h"
@implementation CategoryThing -(NSString *) description { NSString *desc; desc = [NSString stringWithFormat: @"%d %d %d", thing1,thing2,ting3]; return (desc); }//description @end //CategoryThing
thing1 的实现:
# import "CategoryThing.h"
@implementation CategoryThing (Thing1) -(void) setThing1:(int) t1 { thing1 = t1; }//setTing1 -(int) thing1 { return (thing1); }// thing1 @end //CategoryThing(Thing1)
thing2,thing3 的实现类似ting1.....
第二:创建对私有方法的前向引用。
Cocoa中没有真正的私有方法,则实现私有方法类似功能的方法为:
先在类别中声明方法;然后到现有类的实现中实现该方法。
这样这个类中的其他方法可以使用该方法,而其他外部的类就不会知道该方法的存在了
第三:向对象添加非正式协议。
非正式协议是NSObject 的一个类别。非正式协议用于实现委托。
什么又是委托呢?
委托(delegate),是一种对象,另一个类的对象会要求委托对象执行它的某些操作。如:NSWindow类的对象询问它自己的委托对象是否应该允许关闭某个窗口。
NSNetServiceBrowser *browser; browser = [[NSNetServiceBrowser alloc] init]; ITunesFinder *finder; finder = [[ITunesFinder alloc] init]; //告诉浏览器使用finder 对象为委托对象 [browser setDelegate:finder]; [browser do someing...]
[browser release];
[finder release];//好习惯从现在开始
委托对象ITunesFinder 又是长什么样呢?
ITunesFinder 接口:
@interface ITunesFinder : NSObject
@end // so easy?
并不需要在@interface 中声明方法。要成为一个委托对象,我们只需要实现已经打算调用的方法。
@implementation ITunesFinder
-(void) test1
{
.......
}
-(void) test2
{
.....
}
test1和test2 为委托方法。
委托和类别的关系:
委托强调类表的另一种应用:被发送给委托对象的方法可以声明为一个NSObject 的类别。
响应选择器:
@selector(test1:)
如果该委托对象能够响应给定的消息,则向该对象发送此消息,否则,将暂时忽略该委托对象,继续正常运行。
选择器可以被传递,可以作为方法的参数使用,可以作为实例变量存储。