星期六 十月 10, 2009 15:49

六十岁的中国,二十岁的我(2)

Posted by 邵 明博

这个假期的大部分时间,主要还是宅code。正如上次提到的,我的具体任务是:研制现有资源向移动学习终端系统显示模式的资源转换工具,以及相关的全套技术文档。接单之后,任务很自然的被我分解为:视频、图片和html三个处理模块。(移动学习终端方面,是用的TCL的IOpen来做测试的)。截止至7号凌晨5点16分,整个任务的基本功能全部实现。

图片处理这块,毫无疑问的,我是走了一大圈弯路。 不敢说自己是一个java程序员,但让一个学习了java的人转过头来搞c#,这无疑会带着很复杂的感情。正如Jesse在他的著作里谈的那样:

Java programmers may look at C# with a mixture of trepidation, glee, and resentment. It has been suggested that C# is somehow a “rip-off” of Java. I won’t comment on the religious war between Microsoft and the “anyone but Microsoft” crowd, except to acknowledge that C# certainly learned a great deal from Java. But then Java learned a great deal from C++, which owed its syntax to C, which in turn was built on lessons learned in other languages. We all stand on the shoulders of giants.

不管怎么说,凭着自己的那一点小个性,还是会想在java上小冲一把。当天接到的任务,无视老刘给出的要求,晚上就直接开始编写有关图片压缩的代码。并且,很快的取得了成效:(以下为java批量压缩图片的核心代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package imageCompress;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
 
public class doCompress {
 
	private static String path;
 
	//获取压缩比例
	public float getRatio(int width, int height, int maxWidth , int maxHeight)
	{
		float ratio=0;
 
		float widthR=(float)maxWidth/width;
		float heightR=(float)maxHeight/height;
 
		if(widthR<1.0||heightR<1.0)
		{
			ratio=(widthR<=heightR?widthR:heightR);
		}
		return ratio;
	}
 
 
	public void imgCompress(String path,String maxW, String maxH)
	{
		try {
			imageSource iSource=new imageSource(path);
			String[] imgNames=iSource.getImageNames();
 
			for(int i=0;i<imgNames.length;i++)
			{
				Image img=javax.imageio.ImageIO.read(new File(path+"/PIC/"+imgNames[i]));;				
				int imgWidth=img.getWidth(null);
				int imgHeight=img.getHeight(null);
				float r=this.getRatio(imgWidth, imgHeight, Integer.parseInt(maxW), Integer.parseInt(maxH));
 
				imgWidth=(int)(imgWidth*r);
				imgHeight=(int)(imgHeight*r);
 
				BufferedImage bimg=new BufferedImage(imgWidth,imgHeight,BufferedImage.TYPE_INT_RGB);
 
				Graphics2D g2=bimg.createGraphics();
				g2.drawImage(img, 0, 0, imgWidth, imgHeight, Color.WHITE, null);
				g2.dispose();
 
				FileOutputStream outputStream=new FileOutputStream(path+"/PIC/"+imgNames[i]);
				JPEGImageEncoder encoder=JPEGCodec.createJPEGEncoder(outputStream);
				encoder.encode(bimg);
				outputStream.close();
			}
 
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}	
	}
}

用java写完图片模块之后,等到我再接触视频模块时才发现,如果弃用WMEncoder提供的Interface(全微软接口),几乎是不能完成任务的。原因在每一次对视频进行ReEncode的时候,视频中存在的header file全部会丢失,导致三分屏课件没有办法让视频与ppt同步,而脚本也无法对视频进行点播控制。百般无奈的开始研究WMEncoder提供的SDK,开始基础的c#学习。

encoder struct 150x150 六十岁的中国,二十岁的我(2)好在c#和java貌似同父异母的兄弟,语法套路很类似,再加上强大Visual Studio,用了不到3个小时,就可以上手了。呃,废话不多说,在这里想对视频压缩处理的相关核心部分做一下总结。首先是SDK,图中红线标出的即为需要用到的接口对象(点击可以查看大图)。其实对于视频操作的难点在于header file的处理。之前使用java的解决方案,我采用了一款名叫ffmpeg的视频开源项目代码。但处理后的视频没有办法再导入header,以至于让我的整个工作停滞了好几天。学习了sdk后,才发现WMEncBasicEdit Interface就能够较好的处理这一块的工作:

The WMEncBasicEdit interface provids an Automation interface that allows you to perform postprocessing on a Windows Media file, such as modifying the attributes, specifying mark-in and mark-out times to change the length of the file, adding scripts and markers, and indexing the file so that users can use seek functions. An XML configuration file can be used to save and load certain settings.

不过,这里不得不谈到接口中的一个BUG。对于config的处理,如果视频文件过长,indexing的数目过多,则无法正常的导入header file.微软自己推出的解决方案中(Encoder Utilities-Windows Media File Editor)直接删除了最后的2个indexing.对于MS封装整个接口的做法,我是深表遗憾的。哎,下面是视频操作的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
using System;
using System.Collections.Generic;
using System.Text;
using WMEncoderLib;
using System.Windows.Forms;
using System.IO;
 
 
namespace webAutoFinal
{
    class doVideoCompress
    {
 
 
        public void getVideoConfige(string sourceUrl)
        {
            // bool flag;
 
            string vPath = sourceUrl + "\\stream.wmv";
            string cPath = sourceUrl + "\\config.xml";
            try
            {
                //创建后期编辑器
                WMEncBasicEdit BasicEdit;
                BasicEdit = new WMEncBasicEdit();
 
                //设定输入视频
                BasicEdit.MediaFile = vPath;
                BasicEdit.SaveConfigFile(cPath);
                BasicEdit.Start();
 
            }
            catch (Exception e)
            {
                // Console.WriteLine(e.StackTrace);
                MessageBox.Show("  public  void getVideoConfige(string sourceUrl) method has something wrong!(视频文件没找到)" + e.Message);
            }
 
        }
 
        public bool compressVideo(string sourceUrl, string vParam)
        {
            string vPath = sourceUrl + @"\stream.wmv";
            string vPath2 = sourceUrl + @"\ostream.wmv";
            bool flag = false;
 
            try
            {
                //新建一个编码器
                WMEncoder encoder = new WMEncoder();
 
                IWMEncSourceGroupCollection srcGroupCollection = encoder.SourceGroupCollection;
                IWMEncSourceGroup srcGroup = srcGroupCollection.Add("SG_1");
 
 
                //输入选项
                //添加音频源
                IWMEncSource srcAudio = srcGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
                //添加视频源
                IWMEncVideoSource2 srcVideo = (IWMEncVideoSource2)srcGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_VIDEO);
                srcAudio.SetInput(vPath, "", "");
                srcVideo.SetInput(vPath, "", "");
 
                //输出选项
                IWMEncFile file = encoder.File;
                file.LocalFileName = vPath2;
 
                IWMEncProfileCollection ProColl = encoder.ProfileCollection;
                IWMEncProfile Pro;
                for (int i = 0; i < ProColl.Count; i++)
                {
                    Pro = ProColl.Item(i);
                    if (Pro.Name == vParam)
                    {
                        srcGroup.set_Profile(Pro);
                        break;
                    }
                }
 
                //开始压缩
                encoder.PrepareToEncode(true);
                encoder.Start();
 
                WMENC_ENCODER_STATE iEncoderState; //定义encoder的运行状态
                iEncoderState = encoder.RunState;
 
                IWMEncStatistics Stats;  //定义 统计对象
                decimal lEncTime;       //定义 已编码的时长
 
                //获取视频源的时长
                int lVidDuration = srcVideo.Duration / 1000;
 
                //    Console.WriteLine("****** Now ,Video-Compression Session Working ******");
                while (iEncoderState != WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED)
                {
                    //获得当前encoder的运行状态
                    iEncoderState = encoder.RunState;
                    // 获得encoder的统计对象
                    Stats = encoder.Statistics;
                    // 获得当前已编码的市场
                    lEncTime = Stats.EncodingTime * 10;
 
                    //  MessageBox.Show("Encoder starts..");
                    //  Console.Write("Encoder Status: {0} % of the video has benn encodered.", Math.Round(lEncTime / lVidDuration * 100, 0));
 
                    //   System.Threading.Thread.Sleep(100);
                    if ((Math.Round(lEncTime / lVidDuration * 100)) == 100)
                    {
                        flag = true;
                        break;
                    }
                    // encoder.AutoStop = true;
 
                }
 
                //释放编码器内存
                encoder.Stop();
                while (encoder.RunState != WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED)
                {
                    System.Threading.Thread.Sleep(1000);
                }
                encoder.Reset();
                encoder = null;
                return flag;
            }
 
            catch (Exception e)
            {
                //System.Console.Write(e.StackTrace);
                MessageBox.Show("public bool compressVideo(string sourceUrl, string vParam) method has something wrong!\n" + e.Message);
                return false;
            }
 
        }
 
        public void loadVideoConfig(string sourceUrl)
        {
            string vPath = sourceUrl + "\\ostream.wmv";
            string oPath = sourceUrl + "\\stream.wmv";
            string cPath = sourceUrl + "\\config.xml";
 
            //bool flag = false;
            if (System.IO.File.Exists(vPath)  && System.IO.File.Exists(cPath))
            {
                try
                {
 
                    // Create the WMEncBasicEdit object.
                    WMEncBasicEdit BasicEdit;
                    BasicEdit = new WMEncBasicEdit();
                    BasicEdit.MediaFile = vPath;
                    BasicEdit.OutputFile = oPath;
                    BasicEdit.ConfigFile = cPath;
 
                    // Start the basic edit process.
                    BasicEdit.Start();
 
                }
                catch (Exception e)
                {
                    // Console.WriteLine(e.StackTrace);
                    MessageBox.Show(" public void loadVideoConfig(string sourceUrl) method has something wrong!" + e.Message);
                }
            }
            else
                MessageBox.Show("some path can not find");
        }
 
    }
}

至于图片处理模块,我也用c#重写了两份批量压缩的代码。这一次的压缩不仅可以对分辨率控制,还可以对jpg压缩质量进行控制。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
 
namespace webAutoFinal
{
    class doImageCompress
    {
        //获取资源包中所有图片的URL
       public string[] loadDir(string fileFoldUrl)
        {
            string bg = fileFoldUrl + @"\skin\background.jpg";
            fileFoldUrl += @"\pic\";
 
            if (System.IO.File.Exists(bg))
                System.IO.File.Copy(bg, fileFoldUrl + @"\background.jpg",true);
            string[] pics = null;
            try
            {
                pics = Directory.GetFiles(fileFoldUrl, "*.jpg");
            }
            catch (Exception e)//未找到图片文件夹给出异常
            {
              //  Console.WriteLine("The source directory has something wrong!");
              //  Console.WriteLine(e.StackTrace);
                MessageBox.Show("doImageCompress.loadDir() Method got something wrong:"+e.Message);
            }
           // pics[pics.Length] = fileFoldUrl + @"\skin\background.jpg";
 
            return pics;
        }
 
        //将单个图片文件转换成byte
       public byte[] loadPicture(String filePath)
        {
            byte[] picData = null;
            FileInfo fi = new FileInfo(filePath);
 
            if (fi.Exists)
            {
                picData = new byte[fi.Length];
                FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);
                BinaryReader br = new BinaryReader(fs);
                br.Read(picData, 0, Convert.ToInt32(fi.Length));
                fs.Dispose();
            }
            else
            {
               // Console.WriteLine("can not find the picture!");
                MessageBox.Show("doImageCompress.loadPicture() Method got something wrong: can not find the picture!");
            }
            return picData;
        }
 
        //图片压缩
       public void compressPicuture(string filePath, int width, int height)
        {
            try
            {
                byte[] picByte = this.loadPicture(filePath);
                MemoryStream ms = new MemoryStream(picByte);
                System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
                Bitmap bmp = new Bitmap(img, width, height);
                bmp.Save(filePath);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
            }
        }
 
 
        //新压缩方法
 
        private ImageCodecInfo GetEncoder(string mimeType)
{
 
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
 
    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.MimeType == mimeType)
        {
            return codec;
        }
    }
    return null;
}
 
 
        //设置压缩级别   0--100L 
        public void jpgLevel(Bitmap bmp, string fileName)
        {
 
            EncoderParameters ps = new EncoderParameters(1);
            EncoderParameter p = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,100L);
            ps.Param[0] = p;
            bmp.Save(fileName, this.GetEncoder("image/jpeg"), ps);
 
        }
        //设置分辨率
        public void doCompressNEW(string fileName,int width,int height)
        {
           // Image img = Image.FromFile(fileName);
            byte[] picByte = this.loadPicture(fileName);
            MemoryStream ms = new MemoryStream(picByte);
            System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
            Bitmap bmp = new Bitmap(img, width, height);
            jpgLevel(bmp,fileName);
            GC.Collect();
           // bmp.Dispose();
        }
    }
}

其实,整个程序到这里基本上完成了,整体做下来还是比较有成就感的。接下来的工作,应该是对批量处理资源包的功能进行扩充,以及相关技术文档的整理了。
教育资源转换工具

3 Responses to 六十岁的中国,二十岁的我(2)

Avatar

xiangsir

十月 19th, 2009 at 12:55 下午

时常想,我跟你为什么有这么大的差距
我很懒吗?我不懒
我很笨吗?也不是很笨
我很缺乏深入挖掘的精神

Avatar

邵 明博

十月 23rd, 2009 at 12:23 下午

倒哦,一小小的笔记整理,xiangSir别这么大的感慨啊。我们共同努力啊。

Avatar

cSharp中异步委托的笔记 | Mingbo

十二月 19th, 2009 at 8:13 下午

[...] 刚入学那会,写了一个资源转换工具。那个时候还没意识到出现了这样的问题:当我执行转换的时候,UI 无法继续响应用户的操作,而只有当转换完毕之后才恢复正常。实际上,现在分析起来,就是主线程没空搭理你,它正忙,忙完之后才能执行你的更新的操作。这个处境和之前的那个跑3段路传递3个快件的境遇差不多。.net 为我们设计了这个异步委托来执行类似快递专员的工作。 [...]

Comment Form